diff --git a/codes/c/chapter_graph/CMakeLists.txt b/codes/c/chapter_graph/CMakeLists.txt index 3a6b5188..28f8470f 100644 --- a/codes/c/chapter_graph/CMakeLists.txt +++ b/codes/c/chapter_graph/CMakeLists.txt @@ -1,4 +1,4 @@ add_executable(graph_adjacency_matrix graph_adjacency_matrix.c) -add_executable(graph_adjacency_list graph_adjacency_list.c) add_executable(graph_adjacency_list_test graph_adjacency_list_test.c) add_executable(graph_bfs graph_bfs.c) +add_executable(graph_dfs graph_dfs.c) diff --git a/codes/c/chapter_graph/graph_adjacency_list.c b/codes/c/chapter_graph/graph_adjacency_list.c index 756e7a7f..758ef587 100644 --- a/codes/c/chapter_graph/graph_adjacency_list.c +++ b/codes/c/chapter_graph/graph_adjacency_list.c @@ -41,11 +41,11 @@ struct Vertex { /* 顶点节点构造函数 */ Vertex *newVertex(int val) { - Vertex *v = (Vertex *)malloc(sizeof(Vertex)); + Vertex *vet = (Vertex *)malloc(sizeof(Vertex)); // 为新节点赋值并建立该节点的链表 - v->val = val; - v->linked = newLinklist(v); - return v; + vet->val = val; + vet->linked = newLinklist(vet); + return vet; } /* 顶点内存释放函数 */ @@ -103,25 +103,6 @@ void removeLink(linkList *l, Vertex *val) { } } -/* 根据索引查找链表中节点 */ -Node *findByindex(linkList *l, unsigned int index) { - unsigned int i = 0; - Node *temp = l->head->next; - while (temp != 0) { - if (i == index) { - return temp; - } - temp = temp->next; - i++; - } - - if (temp->next == 0) { - printf("vertex not found!\n"); - return 0; - } - return 0; -} - /* 根据顶点地址删除顶点 */ void removeNode(linkList *l, Vertex *val) { Node *temp = l->head->next; @@ -172,12 +153,9 @@ linkList *newLinklist(Vertex *val) { /* 基于邻接链表实现的无向图类结构 */ struct graphAdjList { - // 顶点列表 - Vertex **verticesList; - // 顶点数量 - unsigned int size; - // 当前容量 - unsigned int capacity; + Vertex **verticesList; // 邻接表 + unsigned int size; // 顶点数量 + unsigned int capacity; // 顶点容量 }; typedef struct graphAdjList graphAdjList; @@ -189,13 +167,13 @@ void addEdge(graphAdjList *t, int i, int j) { printf("Out of range in %s:%d\n", __FILE__, __LINE__); return; } - // 查找待连接的节点 - Vertex *v1 = t->verticesList[i]; - Vertex *v2 = t->verticesList[j]; + // 查找欲添加边的顶点 vet1 - vet2 + Vertex *vet1 = t->verticesList[i]; + Vertex *vet2 = t->verticesList[j]; - // 连接节点 - pushBack(v1->linked, v2); - pushBack(v2->linked, v1); + // 连接顶点 vet1 - vet2 + pushBack(vet1->linked, vet2); + pushBack(vet2->linked, vet1); } /* 删除边 */ @@ -206,13 +184,13 @@ void removeEdge(graphAdjList *t, int i, int j) { return; } - // 查找待删除边的相关节点 - Vertex *v1 = t->verticesList[i]; - Vertex *v2 = t->verticesList[j]; + // 查找欲删除边的顶点 vet1 - vet2 + Vertex *vet1 = t->verticesList[i]; + Vertex *vet2 = t->verticesList[j]; - // 移除待删除边 - removeLink(v1->linked, v2); - removeLink(v2->linked, v1); + // 移除待删除边 vet1 - vet2 + removeLink(vet1->linked, vet2); + removeLink(vet2->linked, vet1); } /* 添加顶点 */ @@ -221,16 +199,15 @@ void addVertex(graphAdjList *t, int val) { if (t->size >= t->capacity) { Vertex **tempList = (Vertex **)malloc(sizeof(Vertex *) * 2 * t->capacity); memcpy(tempList, t->verticesList, sizeof(Vertex *) * t->size); - free(t->verticesList); - // 指向新顶点表 - t->verticesList = tempList; - t->capacity = t->capacity * 2; + free(t->verticesList); // 释放原邻接表内存 + t->verticesList = tempList; // 指向新邻接表 + t->capacity = t->capacity * 2; // 容量扩大至2倍 } // 申请新顶点内存并将新顶点地址存入顶点列表 - Vertex *newV = newVertex(val); - newV->pos = t->size; - newV->linked = newLinklist(newV); - t->verticesList[t->size] = newV; + Vertex *newV = newVertex(val); // 建立新顶点 + newV->pos = t->size; // 为新顶点标记下标 + newV->linked = newLinklist(newV); // 为新顶点建立链表 + t->verticesList[t->size] = newV; // 将新顶点加入邻接表 t->size++; } @@ -242,31 +219,30 @@ void removeVertex(graphAdjList *t, unsigned int index) { exit(1); } - // 查找待删节点 - Vertex *v = t->verticesList[index]; - // 若不存在该节点,则返回 - if (v == 0) { + Vertex *vet = t->verticesList[index]; // 查找待删节点 + if (vet == 0) { // 若不存在该节点,则返回 printf("index is:%d\n", index); printf("Out of range in %s:%d\n", __FILE__, __LINE__); return; } - // 遍历待删除节点链表,将所有与待删除结点有关的边删除 - Node *temp = v->linked->head->next; + // 遍历待删除顶点的链表,将所有与待删除结点有关的边删除 + Node *temp = vet->linked->head->next; while (temp != 0) { - removeLink(temp->val->linked, v); - temp = temp->next; + removeLink(temp->val->linked, vet); // 删除与该顶点有关的边 + temp = temp->next; } - // 定点列表前移 + // 将顶点前移 for (int i = index; i < t->size - 1; i++) { - t->verticesList[i] = t->verticesList[i + 1]; + t->verticesList[i] = t->verticesList[i + 1]; // 顶点前移 + t->verticesList[i]->pos--; // 所有前移的顶点索引值减1 } - t->verticesList[t->size - 1] = 0; + t->verticesList[t->size - 1] = 0; // 将被删除顶点的位置置 0 t->size--; //释放被删除顶点的内存 - freeVertex(v); + freeVertex(vet); } /* 打印顶点与邻接矩阵 */ @@ -288,14 +264,14 @@ void printGraph(graphAdjList *t) { } /* 构造函数 */ -graphAdjList *newGraphic(unsigned int verticesNumber) { +graphAdjList *newGraphAdjList(unsigned int verticesCapacity) { // 申请内存 graphAdjList *newGraph = (graphAdjList *)malloc(sizeof(graphAdjList)); // 建立顶点表并分配内存 - newGraph->verticesList = (Vertex **)malloc(sizeof(Vertex *) * verticesNumber); - memset(newGraph->verticesList, 0, sizeof(Vertex *) * verticesNumber); - // 初始化大小和容量 - newGraph->size = 0; - newGraph->capacity = verticesNumber; - return newGraph; + newGraph->verticesList = (Vertex **)malloc(sizeof(Vertex *) * verticesCapacity); // 为顶点列表分配内存 + memset(newGraph->verticesList, 0, sizeof(Vertex *) * verticesCapacity); // 顶点列表置 0 + newGraph->size = 0; // 初始化顶点数量 + newGraph->capacity = verticesCapacity; // 初始化顶点容量 + // 返回图指针 + return newGraph; } diff --git a/codes/c/chapter_graph/graph_adjacency_list_test.c b/codes/c/chapter_graph/graph_adjacency_list_test.c index c881a44a..6b0384f8 100644 --- a/codes/c/chapter_graph/graph_adjacency_list_test.c +++ b/codes/c/chapter_graph/graph_adjacency_list_test.c @@ -10,7 +10,7 @@ int main() { /* 初始化无向图 */ - graphAdjList *graph = newGraphic(5); + graphAdjList *graph = newGraphAdjList(5); // 初始化顶点 addVertex(graph, 1); addVertex(graph, 3); diff --git a/codes/c/chapter_graph/graph_adjacency_matrix.c b/codes/c/chapter_graph/graph_adjacency_matrix.c index 90f97054..8667e001 100644 --- a/codes/c/chapter_graph/graph_adjacency_matrix.c +++ b/codes/c/chapter_graph/graph_adjacency_matrix.c @@ -8,32 +8,38 @@ /* 基于邻接矩阵实现的无向图类结构 */ struct graphAdjMat { - int *vertices; - unsigned int **adjMat; - unsigned int size; - unsigned int capacity; + int *vertices; // 顶点列表 + unsigned int **adjMat; // 邻接矩阵,元素代表“边”,索引代表“顶点索引” + unsigned int size; // 顶点数量 + unsigned int capacity; // 图容量 }; typedef struct graphAdjMat graphAdjMat; /* 添加边 */ +// 参数 i, j 对应 vertices 元素索引 void addEdge(graphAdjMat *t, int i, int j) { // 越界检查 if (i < 0 || j < 0 || i >= t->size || j >= t->size || i == j) { printf("Out of range in %s:%d\n", __FILE__, __LINE__); exit(1); } + // 添加边 + // 参数 i, j 对应 vertices 元素索引 t->adjMat[i][j] = 1; t->adjMat[j][i] = 1; } /* 删除边 */ +// 参数 i, j 对应 vertices 元素索引 void removeEdge(graphAdjMat *t, int i, int j) { // 越界检查 if (i < 0 || j < 0 || i >= t->size || j >= t->size || i == j) { printf("Out of range in %s:%d\n", __FILE__, __LINE__); exit(1); } + // 删除边 + // 参数 i, j 对应 vertices 元素索引 t->adjMat[i][j] = 0; t->adjMat[j][i] = 0; } @@ -42,13 +48,13 @@ void removeEdge(graphAdjMat *t, int i, int j) { void addVertex(graphAdjMat *t, int val) { // 如果实际使用不大于预设空间,则直接初始化新空间 if (t->size < t->capacity) { - t->vertices[t->size] = val; + t->vertices[t->size] = val; // 初始化新顶点值 - // 邻接矩新列阵置0 + for (int i = 0; i < t->size; i++) { - t->adjMat[i][t->size] = 0; + t->adjMat[i][t->size] = 0; // 邻接矩新列阵置0 } - memset(t->adjMat[t->size], 0, sizeof(unsigned int) * (t->size + 1)); + memset(t->adjMat[t->size], 0, sizeof(unsigned int) * (t->size + 1)); // 将新增行置 0 t->size++; return; } @@ -70,23 +76,21 @@ void addVertex(graphAdjMat *t, int val) { tempMat[k] = tempMatLine + k * (t->size * 2); } - // 原数据复制到新数组 for (int i = 0; i < t->size; i++) { - memcpy(tempMat[i], t->adjMat[i], sizeof(unsigned int) * t->size); + memcpy(tempMat[i], t->adjMat[i], sizeof(unsigned int) * t->size); // 原数据复制到新数组 } - // 新列置0 for (int i = 0; i < t->size; i++) { - tempMat[i][t->size] = 0; + tempMat[i][t->size] = 0; // 将新增列置 0 } - memset(tempMat[t->size], 0, sizeof(unsigned int) * (t->size + 1)); + memset(tempMat[t->size], 0, sizeof(unsigned int) * (t->size + 1)); // 将新增行置 0 // 释放原数组 free(t->adjMat[0]); free(t->adjMat); // 扩容后,指向新地址 - t->adjMat = tempMat; + t->adjMat = tempMat; // 指向新的邻接矩阵地址 t->capacity = t->size * 2; t->size++; } @@ -98,28 +102,21 @@ void removeVertex(graphAdjMat *t, unsigned int index) { printf("Out of range in %s:%d\n", __FILE__, __LINE__); exit(1); } - - // 清除删除的顶点,并将其后所有顶点前移 for (int i = index; i < t->size - 1; i++) { - t->vertices[i] = t->vertices[i + 1]; + t->vertices[i] = t->vertices[i + 1]; // 清除删除的顶点,并将其后所有顶点前移 } - - // 将被前移的最后一个顶点置0 - t->vertices[t->size - 1] = 0; + t->vertices[t->size - 1] = 0; // 将被前移的最后一个顶点置 0 // 清除邻接矩阵中删除的列 for (int i = 0; i < t->size - 1; i++) { if (i < index) { - // 被删除列后的所有列前移 for (int j = index; j < t->size - 1; j++) { - t->adjMat[i][j] = t->adjMat[i][j + 1]; + t->adjMat[i][j] = t->adjMat[i][j + 1]; // 被删除列后的所有列前移 } - } else { - // 被删除行的下方所有行上移 - memcpy(t->adjMat[i], t->adjMat[i + 1], sizeof(unsigned int) * t->size); - // 被删除列后的所有列前移 + } else { + memcpy(t->adjMat[i], t->adjMat[i + 1], sizeof(unsigned int) * t->size); // 被删除行的下方所有行上移 for (int j = index; j < t->size; j++) { - t->adjMat[i][j] = t->adjMat[i][j + 1]; + t->adjMat[i][j] = t->adjMat[i][j + 1]; // 被删除列后的所有列前移 } } } @@ -157,45 +154,41 @@ void printGraph(graphAdjMat *t) { } /* 构造函数 */ -graphAdjMat *newGraphic(unsigned int numberVertices, int *vertices, unsigned int **adjMat) { - // 函数指针 - graphAdjMat *newGraph = (graphAdjMat *)malloc(sizeof(graphAdjMat)); - +graphAdjMat *newGraphAjdMat(unsigned int numberVertices, int *vertices, unsigned int **adjMat) { // 申请内存 - newGraph->vertices = (int *)malloc(sizeof(int) * numberVertices * 2); - newGraph->adjMat = (unsigned int **)malloc(sizeof(unsigned int *) * numberVertices * 2); - unsigned int *temp = (unsigned int *)malloc(sizeof(unsigned int) * numberVertices * 2 * numberVertices * 2); - newGraph->size = numberVertices; - newGraph->capacity = numberVertices * 2; + graphAdjMat *newGraph = (graphAdjMat *)malloc(sizeof(graphAdjMat)); // 为图分配内存 + newGraph->vertices = (int *)malloc(sizeof(int) * numberVertices * 2); // 为顶点列表分配内存 + newGraph->adjMat = (unsigned int **)malloc(sizeof(unsigned int *) * numberVertices * 2); // 为邻接矩阵分配二维内存 + unsigned int *temp = (unsigned int *)malloc(sizeof(unsigned int) * numberVertices * 2 * numberVertices * 2); // 为邻接矩阵分配一维内存 + newGraph->size = numberVertices; // 初始化顶点数量 + newGraph->capacity = numberVertices * 2; // 初始化图容量 // 配置二维数组 for (int i = 0; i < numberVertices * 2; i++) { - newGraph->adjMat[i] = temp + i * numberVertices * 2; + newGraph->adjMat[i] = temp + i * numberVertices * 2; // 将二维指针指向一维数组 } // 赋值 memcpy(newGraph->vertices, vertices, sizeof(int) * numberVertices); for (int i = 0; i < numberVertices; i++) { - memcpy(newGraph->adjMat[i], adjMat[i], sizeof(unsigned int) * numberVertices); + memcpy(newGraph->adjMat[i], adjMat[i], sizeof(unsigned int) * numberVertices); // 将传入的邻接矩阵赋值给结构体内邻接矩阵 } + // 返回结构体指针 return newGraph; } /* Driver Code */ int main() { - /* 初始化无向图 */ int vertices[5] = {1, 3, 2, 5, 4}; unsigned int **edge = (unsigned int **)malloc(sizeof(unsigned int *) * 5); - // 用于构建二维数组的一维指针 unsigned int *temp = (unsigned int *)malloc(sizeof(unsigned int) * 25); memset(temp, 0, sizeof(unsigned int) * 25); for (int k = 0; k < 5; k++) { edge[k] = temp + k * 5; } - // 初始化边 edge[0][1] = edge[1][0] = 1; edge[0][3] = edge[3][0] = 1; @@ -203,9 +196,8 @@ int main() { edge[2][3] = edge[3][2] = 1; edge[2][4] = edge[4][2] = 1; edge[3][4] = edge[4][3] = 1; - // 建立无向图 - graphAdjMat *graph = newGraphic(5, vertices, edge); + graphAdjMat *graph = newGraphAjdMat(5, vertices, edge); free(edge); free(temp); printf("\n初始化后,图为:\n"); diff --git a/codes/c/chapter_graph/graph_bfs b/codes/c/chapter_graph/graph_bfs.c similarity index 60% rename from codes/c/chapter_graph/graph_bfs rename to codes/c/chapter_graph/graph_bfs.c index 99e11ea8..51a384f0 100644 --- a/codes/c/chapter_graph/graph_bfs +++ b/codes/c/chapter_graph/graph_bfs.c @@ -30,11 +30,11 @@ void hashMark(hashTable *h, int index) { /* 查询顶点是否已被标记 */ int hashQuery(hashTable *h, int index) { - // 若顶点已被标记,则返回 0 + // 若顶点已被标记,则返回 1 if (h->array[index % h->size] == 1) { - return 0; - } else { return 1; + } else { + return 0; } } @@ -66,8 +66,8 @@ queue *newQueue(unsigned int size) { } /* 入队 */ -void queuePush(queue *q, Vertex *v) { - q->list[q->tail] = v; +void queuePush(queue *q, Vertex *vet) { + q->list[q->tail] = vet; q->tail++; } @@ -88,46 +88,49 @@ void freeQueue(queue *q) { } /* 广度优先遍历 */ -void graphBFS(graphAdjList *t) { - // 初始化队列与哈希表 +// 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 +Vertex **graphBFS(graphAdjList *t, Vertex *startVet) { + // 顶点遍历序列 + Vertex **res = (Vertex **)malloc(sizeof(Vertex *) * t->size); + memset(res, 0, sizeof(Vertex *) * t->size); + // 队列用于实现 BFS queue *que = newQueue(t->size); + // 哈希表,用于记录已被访问过的顶点 hashTable *visited = newHash(t->size); - // 将第一个元素入队 - queuePush(que, t->verticesList[0]); - hashMark(visited, t->verticesList[0]->pos); - - printf("\n["); + int resIndex = 0; + queuePush(que, startVet); // 将第一个元素入队 + hashMark(visited, startVet->pos); // 标记第一个入队的顶点 + // 以顶点 vet 为起点,循环直至访问完所有顶点 while (que->head < que->tail) { // 遍历该顶点的边链表,将所有与该顶点有连接的,并且未被标记的顶点入队 Node *n = queueTop(que)->linked->head->next; while (n != 0) { // 查询哈希表,若该索引的顶点已入队,则跳过,否则入队并标记 - if (hashQuery(visited, n->val->pos) != 0) { - queuePush(que, n->val); - hashMark(visited, n->val->pos); + if (hashQuery(visited, n->val->pos) == 1) { + n = n->next; + continue; // 跳过已被访问过的顶点 } - n = n->next; + queuePush(que, n->val); // 只入队未访问的顶点 + hashMark(visited, n->val->pos); // 标记该顶点已被访问 } - // 打印队首元素 - if (que->head == que->tail - 1) { - printf("%d]\n", queueTop(que)->val); - } else { - printf("%d, ", queueTop(que)->val); - } - // 队首元素出队 - queuePop(que); + // 队首元素存入数组 + res[resIndex] = queueTop(que); // 队首顶点加入顶点遍历序列 + resIndex++; + queuePop(que); // 队首元素出队 } - printf("\n"); - // 释放队列与哈希表内存 + // 释放内存 freeQueue(que); freeHash(visited); + resIndex = 0; + // 返回顶点遍历序列 + return res; } +/* Driver Code */ int main() { - /* 初始化无向图 */ - graphAdjList *graph = newGraphic(3); + graphAdjList *graph = newGraphAdjList(3); // 初始化顶点 for (int i = 0; i < 10; i++) { addVertex(graph, i); @@ -145,13 +148,19 @@ int main() { addEdge(graph, 5, 8); addEdge(graph, 6, 7); addEdge(graph, 7, 8); - printf("\n初始化后,图为:\n"); printGraph(graph); - - printf("\n广度优先遍历(BFS)顶点序列为"); - graphBFS(graph); + printf("\n广度优先遍历(BFS)顶点序列为\n"); + Vertex **vets = graphBFS(graph, graph->verticesList[0]); + + // 打印广度优先遍历数组 + printf("["); + printf("%d", vets[0]->val); + for (int i = 1; i < graph->size && vets[i] != 0; i++) { + printf(", %d", vets[i]->val); + } + printf("]\n"); + free(vets); return 0; } - diff --git a/codes/c/chapter_graph/graph_dfs.c b/codes/c/chapter_graph/graph_dfs.c new file mode 100644 index 00000000..c201d18b --- /dev/null +++ b/codes/c/chapter_graph/graph_dfs.c @@ -0,0 +1,112 @@ +/** + * File: graph_dfs.c + * Created Time: 2023-07-13 + * Author: NI-SW (947743645@qq.com) + */ + +#include "graph_adjacency_list.c" + +/* 哈希表 */ +struct hashTable { + unsigned int size; + unsigned int *array; +}; + +typedef struct hashTable hashTable; + +/* 初始化哈希表 */ +hashTable *newHash(unsigned int size) { + hashTable *h = (hashTable *)malloc(sizeof(hashTable)); + h->array = (unsigned int *)malloc(sizeof(unsigned int) * size); + memset(h->array, 0, sizeof(unsigned int) * size); + h->size = size; + return h; +} + +/* 标记索引过的顶点 */ +void hashMark(hashTable *h, int index) { + h->array[index % h->size] = 1; +} + +/* 查询顶点是否已被标记 */ +int hashQuery(hashTable *h, int index) { + // 若顶点已被标记,则返回 1 + if (h->array[index % h->size] == 1) { + return 1; + } else { + return 0; + } +} + +/* 释放哈希表内存 */ +void freeHash(hashTable *h) { + free(h->array); + free(h); +} + +/* 深度优先遍历 DFS 辅助函数 */ +int resIndex = 0; +void dfs(graphAdjList *graph, hashTable *visited, Vertex *vet, Vertex **res) { + if (hashQuery(visited, vet->pos) == 1) { + return; // 跳过已被访问过的顶点 + } + hashMark(visited, vet->pos); // 标记顶点并将顶点存入数组 + res[resIndex] = vet; // 将顶点存入数组 + resIndex++; + // 遍历该顶点链表 + Node *n = vet->linked->head->next; + while (n != 0) { + // 递归访问邻接顶点 + dfs(graph, visited, n->val, res); + n = n->next; + } + return; +} + +/* 深度优先遍历 DFS */ +// 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 +Vertex **graphDFS(graphAdjList *graph, Vertex *startVet) { + // 顶点遍历序列 + Vertex **res = (Vertex **)malloc(sizeof(Vertex *) * graph->size); + memset(res, 0, sizeof(Vertex *) * graph->size); + // 哈希表,用于记录已被访问过的顶点 + hashTable *visited = newHash(graph->size); + dfs(graph, visited, startVet, res); + // 释放哈希表内存并将数组索引归零 + freeHash(visited); + resIndex = 0; + // 返回遍历数组 + return res; +} + +/* Driver Code */ +int main() { + graphAdjList *graph = newGraphAdjList(10); + for (int i = 0; i < 7; i++) { + addVertex(graph, i); + } + addEdge(graph, 0, 1); + addEdge(graph, 0, 3); + addEdge(graph, 1, 2); + addEdge(graph, 2, 5); + addEdge(graph, 5, 4); + addEdge(graph, 5, 6); + printf("\n初始化后,图为:\n"); + printGraph(graph); + + // 深度优先遍历 DFS + Vertex **vet = graphDFS(graph, graph->verticesList[0]); + + // 输出遍历结果 + printf("\n深度优先遍历(DFS)顶点序列为\n"); + printf("["); + printf("%d", vet[0]->val); + for (int i = 1; i < graph->size && vet[i] != 0; i++) { + printf(", %d", vet[i]->val); + } + printf("]\n"); + + // 释放内存 + free(vet); + return 0; +}