隣接リストとリンクリストは違います。C言語にはガベージコレクタがないので何かを追加したり削除したりするにはmalloc, freeを用います。隣接リストはリンクリストを用いて表現することができます。貴殿のコードには動的生成の考えがないのでそれを平易に導入して、回答としたいと思います。
隣接リストというのはリンクリストよりも複雑な構造であり、隣接リストよりもまずリンクリストのコードをしっかりと構築する必要があると感じました。大きな問題を小さな問題に分けて考える、という考え方は重要です。そのため、私が書いたコードは関数の数が多いです。このことによりテスタビリティ(テストのしやすさ)が向上し、コーディングで詰まることは少なくなると思います。
ただし、大きな問題を小さな問題に分けて考えるとき、プログラミングの多くの概念を知っていなければなりません。わたしが書いたコードはLisp風のオブジェクトシステムが主体になっていますが、オブジェクトシステムという大仰なシステムを用意しなくとも、よりシンプルに書くことができたんだろうと思います。それはわーっと書き出してからリファクタリングをするとよいと思うのですが。
書いててつくづく思ったのは、複雑なデータ構造を取り扱うのにC言語では貧弱で、C++言語がどうしても欲しくなるということでしょうか。例えば、今回のコードではオブジェクトシステムという大仰なシステムを組んでいますがC++のテンプレートを使えば同じようなコードを2回かかなくて済むわけですね。また、単方向リストについてのライブラリはC++では充実しているので単方向リストのコードをわざわざ書きに行く必要はなかったのかなと。
最後に、コードを書く際はテスタビリティを意識していただきたいと思います。テスタビリティを上げるには、関数やクラスを小分けにする、プログラミング業界で一般的に使われている語彙を意識してできるだけ使うようにする、といった工夫が必要になると思います。
私が書いたコードは恐らく自己満足なのでしょうが、正しいコードを書くには概念を注意深く小分けにして少しずつデバッグを行っていくのが肝心なのだと思います。私からは以上です。
C
1#include <stdio.h>
2#include <stdlib.h>
3#include <stdbool.h>
4
5/*
6 * リンクリストによるオブジェクトシステム
7 */
8
9typedef enum tag_ObjectType {
10 OBJECT_TYPE_INTEGER,
11 OBJECT_TYPE_LIST
12} ObjectType;
13
14struct tag_Object; // オブジェクト型の前方宣言
15
16typedef struct tag_ListObjectComponent {
17 struct tag_Object *car; // carはリストの要素(伝統的な概念)
18 struct tag_Object *cdr; // cdrはリストの後続部(伝統的な概念)
19} ListObjectComponent;
20
21typedef struct tag_Object {
22 ObjectType type;
23 union {
24 int i;
25 ListObjectComponent list;
26 } d;
27} Object;
28
29void object_print(Object *obj) {
30 // 引数objはconst型でないので関数内部で書き換え可能であることに注意
31 if (obj == NULL) {
32 printf("[]");
33 return;
34 }
35 if (obj->type == OBJECT_TYPE_INTEGER) {
36 printf("%d", obj->d.i);
37 return;
38 }
39 printf("[");
40 object_print(obj->d.list.car);
41 obj = obj->d.list.cdr;
42 while (obj != NULL) {
43 if (obj->type != OBJECT_TYPE_LIST) {
44 printf(" . "); // .はドット対であることを表す記号
45 object_print(obj);
46 printf("]");
47 return;
48 }
49 printf(", ");
50 object_print(obj->d.list.car);
51 obj = obj->d.list.cdr;
52 }
53 printf("]");
54}
55
56void object_println(Object *obj) {
57 object_print(obj);
58 printf("\n");
59}
60
61Object *object_allocateInteger(int n) {
62 Object *obj = malloc(sizeof(Object));
63 obj->type = OBJECT_TYPE_INTEGER;
64 obj->d.i = n;
65 return obj;
66}
67#define Int object_allocateInteger
68
69Object *object_allocateList(Object *car, Object *cdr) {
70 Object *obj = malloc(sizeof(Object));
71 obj->type = OBJECT_TYPE_LIST;
72 obj->d.list.car = car;
73 obj->d.list.cdr = cdr;
74 return obj;
75}
76#define List object_allocateList
77
78void object_free(Object *obj) {
79 if (obj == NULL) return;
80 if (obj->type == OBJECT_TYPE_LIST) {
81 object_free(obj->d.list.car);
82 object_free(obj->d.list.cdr);
83 }
84 free(obj);
85}
86
87/*
88 * 標準リストについての関数
89 *
90 * ここでは、次のものを標準リストとする。
91 * 1. NULL
92 * 2. nを任意のint型として、List(Int(n), 標準リスト)
93 *
94 * すなわち、標準リストとは要素が全て整数のリンクリストのこと
95 */
96
97Object *list_add(Object *list, int n) {
98 return List(Int(n), list);
99}
100
101Object *list_remove(Object *list, int n) {
102 if (list == NULL) return NULL;
103 if (list->d.list.car->d.i == n) {
104 Object *cdr = list->d.list.cdr;
105 free(list->d.list.car);
106 free(list);
107 return list_remove(cdr, n);
108 }
109 list->d.list.cdr = list_remove(list->d.list.cdr, n);
110 return list;
111}
112
113// Predicate型は、オブジェクト型とvoidポインタ型(環境を与えるために使用する)を
114// 与えてbool型を返す関数のポインタ型
115typedef bool (*Predicate)(Object *, void *);
116
117// 述語(boolを返す関数)を与えて、trueならば要素を削除する関数
118// (要素が整数でないリストにも適用することができる)
119Object *list_remove_if(Object *list, Predicate pred, void *pEnv) {
120 if (list == NULL) return NULL;
121 if (pred(list->d.list.car, pEnv)) {
122 Object *cdr = list->d.list.cdr;
123 object_free(list->d.list.car);
124 free(list);
125 return list_remove_if(cdr, pred, pEnv);
126 }
127 list->d.list.cdr = list_remove_if(list->d.list.cdr, pred, pEnv);
128 return list;
129}
130
131bool car_equals_n(Object *obj, void *pNum) {
132 return (obj->d.list.car->d.i == *((int*)pNum)) ? true : false;
133}
134
135// MapFunc型はオブジェクト型とvoidポインタ型(環境を与えるために使用する)を
136// 与えてオブジェクト型を返す関数のポインタ型
137typedef Object *(*MapFunc)(Object *, void *);
138
139// リストとMapFunc型の関数を与えてリストの各要素を破壊的に変換する関数
140// (要素が整数でないリストにも適用することができる)
141Object *list_map_into(Object *list, MapFunc fn, void *pEnv) {
142 Object *p = list;
143 while (p != NULL) {
144 p->d.list.car = fn(p->d.list.car, pEnv);
145 p = p->d.list.cdr;
146 }
147 return list;
148}
149
150Object *remove_n_for_cdr(Object *obj, void *pNum) {
151 obj->d.list.cdr = list_remove(obj->d.list.cdr, *(int*)pNum);
152 return obj;
153}
154
155// list_find関数は指定した整数nを見つけるとそれ以降の部分リストを返します。
156// 整数nがリストにない場合はNULLを返します。
157// (list_find関数は整数nがリスト内に存在するかどうかを判定するために使います)
158Object *list_find(Object *list, int n) {
159 while (list != NULL) {
160 if (list->d.list.car->d.i == n) {
161 return list;
162 }
163 list = list->d.list.cdr;
164 }
165 return NULL;
166}
167
168/*
169 * 隣接リスト(無向グラフ)
170 */
171
172typedef struct tag_AdjacencyList {
173 // 隣接リストの構造
174 // [ [ 頂点1 . [隣接する頂点11, ..., 隣接する頂点1n] ],
175 // ...
176 // [ 頂点m . [隣接する頂点m1, ..., 隣接する頂点ml] ] ]
177 Object *d;
178} AdjacencyList;
179#define Adj AdjacencyList
180
181// ヘルパー関数: 頂点uに隣接する頂点のリストを取得する
182static Object *adjacencyList_getEdges(Adj *adj, int u) {
183 Object *d = adj->d;
184 while (d != NULL) {
185 if (d->d.list.car->d.list.car->d.i == u) {
186 return d->d.list.car->d.list.cdr;
187 }
188 d = d->d.list.cdr;
189 }
190 return NULL;
191}
192
193// ヘルパー関数: 頂点uに隣接する頂点のリストをobjに設定する
194static void adjacencyList_setEdges(Adj *adj, int u, Object *obj) {
195 Object *d = adj->d;
196 while (d != NULL) {
197 if (d->d.list.car->d.list.car->d.i == u) {
198 d->d.list.car->d.list.cdr = obj;
199 }
200 d = d->d.list.cdr;
201 }
202}
203
204// 隣接リストadjが頂点nを持っているならばtrue, そうでなければfalse
205bool hasNode(Adj *adj, int n) {
206 Object *d = adj->d;
207 while (d != NULL) {
208 if (d->d.list.car->d.list.car->d.i == n) return true;
209 d = d->d.list.cdr;
210 }
211 return false;
212}
213
214// 隣接リストadjに頂点nを追加
215void addNode(Adj *adj, int n) {
216 if (hasNode(adj, n)) {
217 fprintf(stderr, "addNode: 隣接リストはすでに頂点%dを持っています.\n", n);
218 return;
219 }
220 adj->d = List(List(Int(n), NULL), adj->d);
221}
222
223// 隣接リストadjに枝(u, v)を追加
224void addEdge(Adj *adj, int u, int v) {
225 if (!hasNode(adj, u)) {
226 fprintf(stderr, "addEdge: 隣接リストは始点%dを持っていません.\n", u);
227 return;
228 }
229 if (!hasNode(adj, v)) {
230 fprintf(stderr, "addEdge: 隣接リストは終点%dを持っていません.\n", v);
231 return;
232 }
233 Object *ue = adjacencyList_getEdges(adj, u);
234 Object *ve = adjacencyList_getEdges(adj, v);
235 if (list_find(ue, v)) {
236 fprintf(stderr, "addEdge: 始点%dの隣接ノードとして終点%dがすでに登録されています.\n", u, v);
237 return;
238 }
239 if (list_find(ve, u)) {
240 fprintf(stderr, "addEdge: 終点%dの隣接ノードとして始点%dがすでに登録されています.\n", v, u);
241 }
242 adjacencyList_setEdges(adj, u, list_add(ue, v));
243 adjacencyList_setEdges(adj, v, list_add(ve, u));
244}
245
246// 頂点uを削除(それに付随する枝も削除)
247void removeNode(Adj *adj, int u) {
248 adj->d = list_remove_if(adj->d, &car_equals_n, (void*)(&u));
249 adj->d = list_map_into(adj->d, &remove_n_for_cdr, (void*)(&u));
250}
251
252int main() {
253 Adj adj = { .d = NULL };
254 addNode(&adj, 1);
255 addNode(&adj, 2);
256 addNode(&adj, 3);
257 addEdge(&adj, 1, 2);
258 addEdge(&adj, 1, 3);
259 addEdge(&adj, 2, 3);
260 object_println(adj.d);
261 removeNode(&adj, 2);
262 object_println(adj.d);
263 return 0;
264}