承接上文圖表示法,本文聚焦鄰接表的工程級建立流程:從資料結構定義、節點/邊建立、圖初始化、查找操作到記憶體釋放,提供完整可編譯的 C 語言實作。
typedef struct ArcNode {
int dest;
int weight;
struct ArcNode *next;
} ArcNode;
typedef struct VertexNode {
int id;
ArcNode *first_arc;
struct VertexNode *next; // 頂點鏈表,支援動態增刪頂點
} VertexNode;
typedef struct Graph {
int vex_count;
VertexNode *vertices; // 頂點鏈表頭
} Graph;
VertexNode* create_vertex(int id) {
VertexNode *v = (VertexNode*)malloc(sizeof(VertexNode));
if (!v) return NULL;
v->id = id;
v->first_arc = NULL;
v->next = NULL;
return v;
}
ArcNode* create_arc(int dest, int weight) {
ArcNode *a = (ArcNode*)malloc(sizeof(ArcNode));
if (!a) return NULL;
a->dest = dest;
a->weight = weight;
a->next = NULL;
return a;
}
VertexNode* create_vertex_with_arc(int id, int dest, int weight) {
VertexNode *v = create_vertex(id);
if (!v) return NULL;
v->first_arc = create_arc(dest, weight);
return v;
}
Graph* create_graph(int start_id, int dest_id, int weight) {
Graph *g = (Graph*)malloc(sizeof(Graph));
if (!g) return NULL;
g->vex_count = 1;
g->vertices = create_vertex_with_arc(start_id, dest_id, weight);
return g;
}
VertexNode* find_vertex(Graph *g, int id) {
VertexNode *cur = g->vertices;
while (cur) {
if (cur->id == id) return cur;
cur = cur->next;
}
return NULL;
}
ArcNode* find_arc(VertexNode *v, int dest_id) {
ArcNode *cur = v->first_arc;
while (cur) {
if (cur->dest == dest_id) return cur;
cur = cur->next;
}
return NULL;
}
int insert_vertex(Graph *g, int id) {
if (find_vertex(g, id)) return -1; // 已存在
VertexNode *v = create_vertex(id);
if (!v) return -2;
v->next = g->vertices; // 頭插法
g->vertices = v;
g->vex_count++;
return 0;
}
int insert_edge(Graph *g, int from, int to, int weight) {
VertexNode *v_from = find_vertex(g, from);
VertexNode *v_to = find_vertex(g, to);
if (!v_from || !v_to) return -1;
// from -> to
ArcNode *a1 = create_arc(to, weight);
if (!a1) return -2;
a1->next = v_from->first_arc;
v_from->first_arc = a1;
// to -> from (無向圖)
ArcNode *a2 = create_arc(from, weight);
if (!a2) { /* 需回滾 a1,略 */ return -3; }
a2->next = v_to->first_arc;
v_to->first_arc = a2;
return 0;
}
void destroy_graph(Graph *g) {
VertexNode *v = g->vertices;
while (v) {
ArcNode *a = v->first_arc;
while (a) {
ArcNode *tmp = a;
a = a->next;
free(tmp);
}
VertexNode *tmp_v = v;
v = v->next;
free(tmp_v);
}
free(g);
}
int main() {
Graph *g = create_graph(1, 2, 10);
insert_vertex(g, 3);
insert_edge(g, 2, 3, 5);
insert_edge(g, 1, 3, 7);
// 驗證
VertexNode *v1 = find_vertex(g, 1);
for (ArcNode *a = v1->first_arc; a; a = a->next)
printf("1 -> %d (w=%d)\n", a->dest, a->weight);
destroy_graph(g);
return 0;
}
unordered_map<int, VertexNode*> 或 ID 映射陣列鄰接表建立雖涉及多層指標,但遵循「小函式組合大功能」原則,將建立、查找、插入、銷毀拆解為獨立單元,即可構建健壯、可維護的圖庫。掌握這套流程,是實作 Dijkstra、Tarjan、最大流等複雜圖演算法的堅實基礎。