From efbbfd8203a03856eec0a3c47e4280c11a8b2fbf Mon Sep 17 00:00:00 2001 From: krahets Date: Sun, 29 Oct 2023 00:08:28 +0800 Subject: [PATCH] Reimplement the graph code for C --- codes/c/chapter_graph/graph_adjacency_list.c | 339 +++++++----------- .../chapter_graph/graph_adjacency_list_test.c | 52 +-- .../c/chapter_graph/graph_adjacency_matrix.c | 231 +++++------- codes/c/chapter_graph/graph_bfs.c | 194 ++++------ codes/c/chapter_graph/graph_dfs.c | 122 +++---- codes/c/utils/common.h | 1 + codes/c/utils/vertex.h | 49 +++ 7 files changed, 397 insertions(+), 591 deletions(-) create mode 100644 codes/c/utils/vertex.h diff --git a/codes/c/chapter_graph/graph_adjacency_list.c b/codes/c/chapter_graph/graph_adjacency_list.c index ebdb74477..98584544d 100644 --- a/codes/c/chapter_graph/graph_adjacency_list.c +++ b/codes/c/chapter_graph/graph_adjacency_list.c @@ -6,259 +6,166 @@ #include "../utils/common.h" -typedef struct Vertex Vertex; -typedef struct Node Node; -typedef struct LinkedList LinkedList; +// 假设节点最大数量为 100 +#define MAX_SIZE 100 -void freeVertex(Vertex *); -void freeLinklist(LinkedList *); -LinkedList *newLinklist(Vertex *); +/* 节点结构体 */ +typedef struct AdjListNode { + Vertex *vertex; // 顶点 + struct AdjListNode *next; // 后继节点 +} AdjListNode; -/* 链表节点 */ -struct Node { - // 链表节点内包含顶点类和下一个节点地址 - Vertex *val; - Node *next; -}; - -/* 链表节点构造函数 */ -Node *newNode() { - Node *n = (Node *)malloc(sizeof(Node)); - n->next = 0; - n->val = 0; - return n; -} - -/* 顶点节点类 */ -struct Vertex { - // 节点值 - int val; - // 与其它节点相连接的边的链表 - LinkedList *list; - // 索引位,标记该顶点在顶点列表中的索引 - unsigned int pos; -}; - -/* 顶点节点构造函数 */ -Vertex *newVertex(int val) { - Vertex *vet = (Vertex *)malloc(sizeof(Vertex)); - // 为新节点赋值并建立该节点的链表 - vet->val = val; - vet->list = newLinklist(vet); - return vet; -} - -/* 顶点内存释放函数 */ -void freeVertex(Vertex *val) { - // 释放该顶点和该顶点的链表的内存 - freeLinklist(val->list); - free(val); -} - -/* 链表 */ -struct LinkedList { - Node *head; - Node *tail; -}; +/* 基于邻接表实现的无向图类 */ +typedef struct { + AdjListNode *heads[MAX_SIZE]; // 节点数组 + int size; // 节点数量 +} GraphAdjList; -/* 链表头插法 */ -void pushFront(LinkedList *list, Vertex *val) { - Node *temp = newNode(); - temp->val = val; - temp->next = list->head->next; - list->head->next = temp; - if (list->tail == list->head) { - list->tail = temp; +/* 构造函数 */ +GraphAdjList *newGraphAdjList() { + GraphAdjList *graph = (GraphAdjList *)malloc(sizeof(GraphAdjList)); + if (!graph) { + return NULL; } + graph->size = 0; + for (int i = 0; i < MAX_SIZE; i++) { + graph->heads[i] = NULL; + } + return graph; } -/* 链表尾插法 */ -void pushBack(LinkedList *list, Vertex *val) { - Node *temp = newNode(); - temp->val = val; - temp->next = 0; - list->tail->next = temp; - list->tail = temp; -} - -/* 根据顶点地址与该顶点连接的删除边 */ -void removeLink(LinkedList *list, Vertex *val) { - Node *temp = list->head->next; - Node *front = list->head; - while (temp != 0) { - if (temp->val == val) { - front->next = temp->next; - if (list->tail == temp) { - list->tail = front; +/* 析构函数 */ +void delGraphAdjList(GraphAdjList *graph) { + for (int i = 0; i < graph->size; i++) { + AdjListNode *cur = graph->heads[i]; + while (cur != NULL) { + AdjListNode *next = cur->next; + if (cur != graph->heads[i]) { + free(cur); } - free(temp); - return; + cur = next; } - front = temp; - temp = temp->next; - } - if (temp->next == 0) { - printf("vertex not found!\n"); + free(graph->heads[i]->vertex); + free(graph->heads[i]); } + free(graph); } -/* 根据顶点地址删除顶点 */ -void removeItem(LinkedList *list, Vertex *val) { - Node *temp = list->head->next; - Node *front = list->head; - while (temp != 0) { - if (temp->val == val) { - front->next = temp->next; - if (list->tail == temp) { - list->tail = front; - } - freeVertex(val); - free(temp); - return; +/* 查找顶点对应的节点 */ +AdjListNode *findNode(GraphAdjList *graph, Vertex *vet) { + for (int i = 0; i < graph->size; i++) { + if (graph->heads[i]->vertex == vet) { + return graph->heads[i]; } - front = temp; - temp = temp->next; - } - if (temp->next == 0) { - printf("vertex not found!\n"); } + return NULL; } -/* 释放链表内存 */ -void freeLinklist(LinkedList *list) { - Node *temp = list->head->next; - while (temp != 0) { - free(list->head); - list->head = temp; - temp = temp->next; - } - free(list->head); - list->head = 0; - free(list); +/* 添加边辅助函数 */ +void addEdgeHelper(AdjListNode *head, Vertex *vet) { + AdjListNode *node = (AdjListNode *)malloc(sizeof(AdjListNode)); + node->vertex = vet; + // 头插法 + node->next = head->next; + head->next = node; } -/* 链表构造函数 */ -LinkedList *newLinklist(Vertex *val) { - LinkedList *newLinklist = (LinkedList *)malloc(sizeof(LinkedList)); - newLinklist->head = newNode(); - newLinklist->head->val = val; - newLinklist->tail = newLinklist->head; - newLinklist->head->next = 0; - return newLinklist; +/* 添加边 */ +void addEdge(GraphAdjList *graph, Vertex *vet1, Vertex *vet2) { + AdjListNode *head1 = findNode(graph, vet1); + AdjListNode *head2 = findNode(graph, vet2); + assert(head1 != NULL && head2 != NULL && head1 != head2); + // 添加边 vet1 - vet2 + addEdgeHelper(head1, vet2); + addEdgeHelper(head2, vet1); } -/* 基于邻接链表实现的无向图类结构 */ -typedef struct { - Vertex **vertices; // 邻接表 - unsigned int size; // 顶点数量 - unsigned int capacity; // 顶点容量 -} GraphAdjList; - -/* 添加边 */ -void addEdge(GraphAdjList *graph, int i, int j) { - // 越界检查 - if (i < 0 || j < 0 || i == j || i >= graph->size || j >= graph->size) { - printf("Out of range in %s:%d\n", __FILE__, __LINE__); - return; +/* 删除边辅助函数 */ +void removeEdgeHelper(AdjListNode *head, Vertex *vet) { + AdjListNode *pre = head; + AdjListNode *cur = head->next; + // 在链表中搜索 vet 对应节点 + while (cur != NULL && cur->vertex != vet) { + pre = cur; + cur = cur->next; } - // 查找欲添加边的顶点 vet1 - vet2 - Vertex *vet1 = graph->vertices[i]; - Vertex *vet2 = graph->vertices[j]; - // 连接顶点 vet1 - vet2 - pushBack(vet1->list, vet2); - pushBack(vet2->list, vet1); + if (cur == NULL) + return; + // 将 vet 对应节点从链表中删除 + pre->next = cur->next; + // 释放内存 + free(cur); } /* 删除边 */ -void removeEdge(GraphAdjList *graph, int i, int j) { - // 越界检查 - if (i < 0 || j < 0 || i == j || i >= graph->size || j >= graph->size) { - printf("Out of range in %s:%d\n", __FILE__, __LINE__); - return; - } - // 查找欲删除边的顶点 vet1 - vet2 - Vertex *vet1 = graph->vertices[i]; - Vertex *vet2 = graph->vertices[j]; - // 移除待删除边 vet1 - vet2 - removeLink(vet1->list, vet2); - removeLink(vet2->list, vet1); +void removeEdge(GraphAdjList *graph, Vertex *vet1, Vertex *vet2) { + AdjListNode *head1 = findNode(graph, vet1); + AdjListNode *head2 = findNode(graph, vet2); + assert(head1 != NULL && head2 != NULL); + // 删除边 vet1 - vet2 + removeEdgeHelper(head1, head2->vertex); + removeEdgeHelper(head2, head1->vertex); } /* 添加顶点 */ -void addVertex(GraphAdjList *graph, int val) { - // 若大小超过容量,则扩容 - if (graph->size >= graph->capacity) { - Vertex **tempList = (Vertex **)malloc(sizeof(Vertex *) * 2 * graph->capacity); - memcpy(tempList, graph->vertices, sizeof(Vertex *) * graph->size); - free(graph->vertices); // 释放原邻接表内存 - graph->vertices = tempList; // 指向新邻接表 - graph->capacity = graph->capacity * 2; // 容量扩大至2倍 - } - // 申请新顶点内存并将新顶点地址存入顶点列表 - Vertex *newV = newVertex(val); // 建立新顶点 - newV->pos = graph->size; // 为新顶点标记下标 - newV->list = newLinklist(newV); // 为新顶点建立链表 - graph->vertices[graph->size] = newV; // 将新顶点加入邻接表 - graph->size++; +void addVertex(GraphAdjList *graph, Vertex *vet) { + assert(graph != NULL && graph->size < MAX_SIZE); + AdjListNode *head = (AdjListNode *)malloc(sizeof(AdjListNode)); + head->vertex = vet; + head->next = NULL; + // 在邻接表中添加一个新链表 + graph->heads[graph->size++] = head; } /* 删除顶点 */ -void removeVertex(GraphAdjList *graph, unsigned int index) { - // 越界检查 - if (index < 0 || index >= graph->size) { - printf("Out of range in %s:%d\n", __FILE__, __LINE__); - exit(1); +void removeVertex(GraphAdjList *graph, Vertex *vet) { + AdjListNode *node = findNode(graph, vet); + assert(node != NULL); + // 在邻接表中删除顶点 vet 对应的链表 + AdjListNode *cur = node, *pre = NULL; + while (cur) { + pre = cur; + cur = cur->next; + free(pre); } - Vertex *vet = graph->vertices[index]; // 查找待删节点 - if (vet == 0) { // 若不存在该节点,则返回 - printf("index is:%d\n", index); - printf("Out of range in %s:%d\n", __FILE__, __LINE__); - return; + // 遍历其他顶点的链表,删除所有包含 vet 的边 + for (int i = 0; i < graph->size; i++) { + cur = graph->heads[i]; + pre = NULL; + while (cur) { + pre = cur; + cur = cur->next; + if (cur && cur->vertex == vet) { + pre->next = cur->next; + free(cur); + break; + } + } } - // 遍历待删除顶点的链表,将所有与待删除节点有关的边删除 - Node *temp = vet->list->head->next; - while (temp != 0) { - removeLink(temp->val->list, vet); // 删除与该顶点有关的边 - temp = temp->next; + // 将该顶点之后的顶点向前移动,以填补空缺 + int i; + for (i = 0; i < graph->size; i++) { + if (graph->heads[i] == node) + break; } - // 将顶点前移 - for (int i = index; i < graph->size - 1; i++) { - graph->vertices[i] = graph->vertices[i + 1]; // 顶点前移 - graph->vertices[i]->pos--; // 所有前移的顶点索引值减 1 + for (int j = i; j < graph->size - 1; j++) { + graph->heads[j] = graph->heads[j + 1]; } - graph->vertices[graph->size - 1] = 0; graph->size--; - // 释放内存 - freeVertex(vet); + free(vet); } -/* 打印顶点与邻接矩阵 */ -void printGraph(GraphAdjList *graph) { - printf("邻接表 =\n"); - for (int i = 0; i < graph->size; i++) { - Node *n = graph->vertices[i]->list->head->next; - printf("%d: [", graph->vertices[i]->val); - while (n != 0) { - if (n->next != 0) { - printf("%d, ", n->val->val); - } else { - printf("%d", n->val->val); - } - n = n->next; +/* 打印邻接表 */ +void printGraph(const GraphAdjList *graph) { + printf("邻接表 =\n"); + for (int i = 0; i < graph->size; ++i) { + AdjListNode *node = graph->heads[i]; + printf("%d: [", node->vertex->val); + node = node->next; + while (node) { + printf("%d, ", node->vertex->val); + node = node->next; } printf("]\n"); } } - -/* 构造函数 */ -GraphAdjList *newGraphAdjList(unsigned int verticesCapacity) { - // 申请内存 - GraphAdjList *newGraph = (GraphAdjList *)malloc(sizeof(GraphAdjList)); - // 建立顶点表并分配内存 - newGraph->vertices = (Vertex **)malloc(sizeof(Vertex *) * verticesCapacity); // 为顶点列表分配内存 - memset(newGraph->vertices, 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 5e9f9713b..665cc4e6e 100644 --- a/codes/c/chapter_graph/graph_adjacency_list_test.c +++ b/codes/c/chapter_graph/graph_adjacency_list_test.c @@ -8,46 +8,48 @@ /* Driver Code */ int main() { - /* 初始化无向图 */ - GraphAdjList *graph = newGraphAdjList(5); - // 初始化顶点 - addVertex(graph, 1); - addVertex(graph, 3); - addVertex(graph, 2); - addVertex(graph, 5); - addVertex(graph, 4); - // 初始化边 - addEdge(graph, 0, 1); - addEdge(graph, 0, 3); - addEdge(graph, 1, 2); - addEdge(graph, 2, 3); - addEdge(graph, 2, 4); - addEdge(graph, 3, 4); - printf("\n初始化后,图为:\n"); + int vals[] = {1, 3, 2, 5, 4}; + int size = sizeof(vals) / sizeof(vals[0]); + Vertex **v = valsToVets(vals, size); + Vertex *edges[][2] = {{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]}, + {v[2], v[3]}, {v[2], v[4]}, {v[3], v[4]}}; + int egdeSize = sizeof(edges) / sizeof(edges[0]); + GraphAdjList *graph = newGraphAdjList(); + // 添加所有顶点和边 + for (int i = 0; i < size; i++) { + addVertex(graph, v[i]); + } + for (int i = 0; i < egdeSize; i++) { + addEdge(graph, edges[i][0], edges[i][1]); + } + printf("\n初始化后,图为\n"); printGraph(graph); /* 添加边 */ - // 顶点 1, 2 的索引分别为 0, 2 - addEdge(graph, 0, 2); - printf("\n添加边 1-2 后图为\n"); + // 顶点 1, 2 即 v[0], v[2] + addEdge(graph, v[0], v[2]); + printf("\n添加边 1-2 后,图为\n"); printGraph(graph); /* 删除边 */ - // 顶点 1, 3 的索引分别为 0, 1 - removeEdge(graph, 0, 1); + // 顶点 1, 3 即 v[0], v[1] + removeEdge(graph, v[0], v[1]); printf("\n删除边 1-3 后,图为\n"); printGraph(graph); /* 添加顶点 */ - addVertex(graph, 6); + Vertex *v5 = newVertex(6); + addVertex(graph, v5); printf("\n添加顶点 6 后,图为\n"); printGraph(graph); /* 删除顶点 */ - // 顶点 3 的索引为 1 - removeVertex(graph, 1); - printf("\n删除顶点 3 后,图为\n"); + // 顶点 3 即 v[1] + removeVertex(graph, v[1]); + printf("\n删除顶点 3 后,图为:\n"); printGraph(graph); + // 释放内存 + delGraphAdjList(graph); return 0; } diff --git a/codes/c/chapter_graph/graph_adjacency_matrix.c b/codes/c/chapter_graph/graph_adjacency_matrix.c index 5e83b0849..a69bb797b 100644 --- a/codes/c/chapter_graph/graph_adjacency_matrix.c +++ b/codes/c/chapter_graph/graph_adjacency_matrix.c @@ -6,211 +6,140 @@ #include "../utils/common.h" -/* 基于邻接矩阵实现的无向图类结构 */ +// 假设顶点数量最大为 100 +#define MAX_SIZE 100 + +/* 基于邻接矩阵实现的无向图结构体 */ typedef struct { - int *vertices; // 顶点列表 - int **adjMat; // 邻接矩阵,元素代表“边”,索引代表“顶点索引” - int size; // 顶点数量 - int capacity; // 图容量 + int vertices[MAX_SIZE]; + int adjMat[MAX_SIZE][MAX_SIZE]; + int size; } GraphAdjMat; -/* 添加边 */ -// 参数 i, j 对应 vertices 元素索引 -void addEdge(GraphAdjMat *graph, int i, int j) { - // 越界检查 - if (i < 0 || j < 0 || i >= graph->size || j >= graph->size || i == j) { - printf("Out of range in %s:%d\n", __FILE__, __LINE__); - exit(1); - } - // 添加边 - // 参数 i, j 对应 vertices 元素索引 - graph->adjMat[i][j] = 1; - graph->adjMat[j][i] = 1; -} - -/* 删除边 */ -// 参数 i, j 对应 vertices 元素索引 -void removeEdge(GraphAdjMat *graph, int i, int j) { - // 越界检查 - if (i < 0 || j < 0 || i >= graph->size || j >= graph->size || i == j) { - printf("Out of range in %s:%d\n", __FILE__, __LINE__); - exit(1); +/* 构造函数 */ +GraphAdjMat *newGraphAdjMat() { + GraphAdjMat *graph = (GraphAdjMat *)malloc(sizeof(GraphAdjMat)); + graph->size = 0; + for (int i = 0; i < MAX_SIZE; i++) { + for (int j = 0; j < MAX_SIZE; j++) { + graph->adjMat[i][j] = 0; + } } - // 删除边 - // 参数 i, j 对应 vertices 元素索引 - graph->adjMat[i][j] = 0; - graph->adjMat[j][i] = 0; + return graph; } /* 添加顶点 */ void addVertex(GraphAdjMat *graph, int val) { - // 如果实际使用不大于预设空间,则直接初始化新空间 - if (graph->size < graph->capacity) { - graph->vertices[graph->size] = val; // 初始化新顶点值 - for (int i = 0; i < graph->size; i++) { - graph->adjMat[i][graph->size] = 0; // 邻接矩新列阵置0 - } - memset(graph->adjMat[graph->size], 0, sizeof(int) * (graph->size + 1)); // 将新增行置 0 - graph->size++; + if (graph->size == MAX_SIZE) { + fprintf(stderr, "图的顶点数量已达最大值\n"); return; } - // 扩容,申请新的顶点数组 - int *temp = (int *)malloc(sizeof(int) * (graph->size * 2)); - memcpy(temp, graph->vertices, sizeof(int) * graph->size); - temp[graph->size] = val; - // 释放原数组 - free(graph->vertices); - graph->vertices = temp; - // 扩容,申请新的二维数组 - int **tempMat = (int **)malloc(sizeof(int *) * graph->size * 2); - int *tempMatLine = (int *)malloc(sizeof(int) * (graph->size * 2) * (graph->size * 2)); - memset(tempMatLine, 0, sizeof(int) * (graph->size * 2) * (graph->size * 2)); - for (int k = 0; k < graph->size * 2; k++) { - tempMat[k] = tempMatLine + k * (graph->size * 2); - } - for (int i = 0; i < graph->size; i++) { - memcpy(tempMat[i], graph->adjMat[i], sizeof(int) * graph->size); // 原数据复制到新数组 - } - for (int i = 0; i < graph->size; i++) { - tempMat[i][graph->size] = 0; // 将新增列置 0 + // 添加第 n 个顶点,并将第 n 行和列置零 + int n = graph->size; + graph->vertices[n] = val; + for (int i = 0; i <= n; i++) { + graph->adjMat[n][i] = graph->adjMat[i][n] = 0; } - memset(tempMat[graph->size], 0, sizeof(int) * (graph->size + 1)); // 将新增行置 0 - // 释放原数组 - free(graph->adjMat[0]); - free(graph->adjMat); - // 扩容后,指向新地址 - graph->adjMat = tempMat; // 指向新的邻接矩阵地址 - graph->capacity = graph->size * 2; graph->size++; } /* 删除顶点 */ void removeVertex(GraphAdjMat *graph, int index) { - // 越界检查 if (index < 0 || index >= graph->size) { - printf("Out of range in %s:%d\n", __FILE__, __LINE__); - exit(1); + fprintf(stderr, "顶点索引越界\n"); + return; } + // 在顶点列表中移除索引 index 的顶点 for (int i = index; i < graph->size - 1; i++) { - graph->vertices[i] = graph->vertices[i + 1]; // 清除删除的顶点,并将其后所有顶点前移 + graph->vertices[i] = graph->vertices[i + 1]; } - graph->vertices[graph->size - 1] = 0; // 将被前移的最后一个顶点置 0 - // 清除邻接矩阵中删除的列 - for (int i = 0; i < graph->size - 1; i++) { - if (i < index) { - for (int j = index; j < graph->size - 1; j++) { - graph->adjMat[i][j] = graph->adjMat[i][j + 1]; // 被删除列后的所有列前移 - } - } else { - memcpy(graph->adjMat[i], graph->adjMat[i + 1], sizeof(int) * graph->size); // 被删除行的下方所有行上移 - for (int j = index; j < graph->size; j++) { - graph->adjMat[i][j] = graph->adjMat[i][j + 1]; // 被删除列后的所有列前移 - } + // 在邻接矩阵中删除索引 index 的行 + for (int i = index; i < graph->size - 1; i++) { + for (int j = 0; j < graph->size; j++) { + graph->adjMat[i][j] = graph->adjMat[i + 1][j]; + } + } + // 在邻接矩阵中删除索引 index 的列 + for (int i = 0; i < graph->size; i++) { + for (int j = index; j < graph->size - 1; j++) { + graph->adjMat[i][j] = graph->adjMat[i][j + 1]; } } graph->size--; } -/* 打印顶点与邻接矩阵 */ -void printGraph(GraphAdjMat *graph) { - if (graph->size == 0) { - printf("graph is empty\n"); +/* 添加边 */ +// 参数 i, j 对应 vertices 元素索引 +void addEdge(GraphAdjMat *graph, int i, int j) { + if (i < 0 || j < 0 || i >= graph->size || j >= graph->size || i == j) { + fprintf(stderr, "边索引越界或相等\n"); return; } - printf("顶点列表 = ["); - for (int i = 0; i < graph->size; i++) { - if (i != graph->size - 1) { - printf("%d, ", graph->vertices[i]); - } else { - printf("%d", graph->vertices[i]); - } - } - printf("]\n"); - printf("邻接矩阵 =\n[\n"); - for (int i = 0; i < graph->size; i++) { - printf(" ["); - for (int j = 0; j < graph->size; j++) { - if (j != graph->size - 1) { - printf("%u, ", graph->adjMat[i][j]); - } else { - printf("%u", graph->adjMat[i][j]); - } - } - printf("],\n"); - } - printf("]\n"); + graph->adjMat[i][j] = 1; + graph->adjMat[j][i] = 1; } -/* 构造函数 */ -GraphAdjMat *newGraphAjdMat(int numberVertices, int *vertices, int **adjMat) { - // 申请内存 - GraphAdjMat *newGraph = (GraphAdjMat *)malloc(sizeof(GraphAdjMat)); // 为图分配内存 - newGraph->vertices = (int *)malloc(sizeof(int) * numberVertices * 2); // 为顶点列表分配内存 - newGraph->adjMat = (int **)malloc(sizeof(int *) * numberVertices * 2); // 为邻接矩阵分配二维内存 - int *temp = (int *)malloc(sizeof(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; // 将二维指针指向一维数组 +/* 删除边 */ +// 参数 i, j 对应 vertices 元素索引 +void removeEdge(GraphAdjMat *graph, int i, int j) { + if (i < 0 || j < 0 || i >= graph->size || j >= graph->size || i == j) { + fprintf(stderr, "边索引越界或相等\n"); + return; } - // 赋值 - memcpy(newGraph->vertices, vertices, sizeof(int) * numberVertices); - for (int i = 0; i < numberVertices; i++) { - memcpy(newGraph->adjMat[i], adjMat[i], - sizeof(int) * numberVertices); // 将传入的邻接矩阵赋值给结构体内邻接矩阵 + graph->adjMat[i][j] = 0; + graph->adjMat[j][i] = 0; +} + +/* 打印邻接矩阵 */ +void printGraphAdjMat(GraphAdjMat *graph) { + printf("顶点列表 = "); + printArray(graph->vertices, graph->size); + printf("邻接矩阵 =\n"); + for (int i = 0; i < graph->size; i++) { + printArray(graph->adjMat[i], graph->size); } - // 返回结构体指针 - return newGraph; } /* Driver Code */ int main() { - /* 初始化无向图 */ - int vertices[5] = {1, 3, 2, 5, 4}; - int **edge = (int **)malloc(sizeof(int *) * 5); - // 用于构建二维数组的一维指针 - int *temp = (int *)malloc(sizeof(int) * 25); - memset(temp, 0, sizeof(int) * 25); - for (int k = 0; k < 5; k++) { - edge[k] = temp + k * 5; + // 初始化无向图 + GraphAdjMat *graph = newGraphAdjMat(); + int vertices[] = {1, 3, 2, 5, 4}; + for (int i = 0; i < 5; i++) { + addVertex(graph, vertices[i]); + } + int edges[][2] = {{0, 1}, {0, 3}, {1, 2}, {2, 3}, {2, 4}, {3, 4}}; + for (int i = 0; i < 6; i++) { + addEdge(graph, edges[i][0], edges[i][1]); } - // 初始化边 - edge[0][1] = edge[1][0] = 1; - edge[0][3] = edge[3][0] = 1; - edge[1][2] = edge[2][1] = 1; - edge[2][3] = edge[3][2] = 1; - edge[2][4] = edge[4][2] = 1; - edge[3][4] = edge[4][3] = 1; - // 建立无向图 - GraphAdjMat *graph = newGraphAjdMat(5, vertices, edge); - free(edge); - free(temp); - printf("\n初始化后,图为:\n"); - printGraph(graph); + printf("\n初始化后,图为\n"); + printGraphAdjMat(graph); /* 添加边 */ // 顶点 1, 2 的索引分别为 0, 2 addEdge(graph, 0, 2); - printf("\n添加边 1-2 后图为\n"); - printGraph(graph); + printf("\n添加边 1-2 后,图为\n"); + printGraphAdjMat(graph); /* 删除边 */ // 顶点 1, 3 的索引分别为 0, 1 removeEdge(graph, 0, 1); printf("\n删除边 1-3 后,图为\n"); - printGraph(graph); + printGraphAdjMat(graph); /* 添加顶点 */ addVertex(graph, 6); printf("\n添加顶点 6 后,图为\n"); - printGraph(graph); + printGraphAdjMat(graph); /* 删除顶点 */ // 顶点 3 的索引为 1 removeVertex(graph, 1); printf("\n删除顶点 3 后,图为\n"); - printGraph(graph); + printGraphAdjMat(graph); + + // 释放内存 + free(graph); return 0; } diff --git a/codes/c/chapter_graph/graph_bfs.c b/codes/c/chapter_graph/graph_bfs.c index a6522c6ed..537fc2b14 100644 --- a/codes/c/chapter_graph/graph_bfs.c +++ b/codes/c/chapter_graph/graph_bfs.c @@ -6,156 +6,110 @@ #include "graph_adjacency_list.c" -/* 哈希表 */ -typedef struct { - unsigned int size; - unsigned int *array; -} 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); -} +// 假设节点最大数量为 100 +#define MAX_SIZE 100 -/* 队列 */ +/* 节点队列结构体 */ typedef struct { - Vertex **list; - unsigned int size; - int head; - int tail; + Vertex *vertices[MAX_SIZE]; + int front, rear, size; } Queue; -/* 初始化队列 */ -Queue *newQueue(unsigned int size) { +/* 构造函数 */ +Queue *newQueue() { Queue *q = (Queue *)malloc(sizeof(Queue)); - q->size = size; - q->list = (Vertex **)malloc(sizeof(Vertex *) * size); - q->head = 0; - q->tail = 0; - + q->front = q->rear = q->size = 0; return q; } -/* 入队 */ -void queuePush(Queue *q, Vertex *vet) { - q->list[q->tail] = vet; - q->tail++; +/* 判断队列是否为空 */ +int isEmpty(Queue *q) { + return q->size == 0; } -/* 出队 */ -void queuePop(Queue *q) { - q->head++; +/* 入队操作 */ +void enqueue(Queue *q, Vertex *vet) { + q->vertices[q->rear] = vet; + q->rear = (q->rear + 1) % MAX_SIZE; + q->size++; } -/* 队首元素 */ -Vertex *queueTop(Queue *q) { - return q->list[q->head]; +/* 出队操作 */ +Vertex *dequeue(Queue *q) { + Vertex *vet = q->vertices[q->front]; + q->front = (q->front + 1) % MAX_SIZE; + q->size--; + return vet; } -/* 释放队列内存 */ -void freeQueue(Queue *q) { - free(q->list); - free(q); +/* 检查顶点是否已被访问 */ +int isVisited(Vertex **visited, int size, Vertex *vet) { + // 遍历查找节点,使用 O(n) 时间 + for (int i = 0; i < size; i++) { + if (visited[i] == vet) + return 1; + } + return 0; } -/* 广度优先遍历 */ +/* 广度优先遍历 BFS */ // 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点 -Vertex **graphBFS(GraphAdjList *t, Vertex *startVet) { - // 顶点遍历序列 - Vertex **res = (Vertex **)malloc(sizeof(Vertex *) * t->size); - memset(res, 0, sizeof(Vertex *) * t->size); +void graphBFS(GraphAdjList *graph, Vertex *startVet, Vertex **res, int *resSize, Vertex **visited, int *visitedSize) { // 队列用于实现 BFS - Queue *que = newQueue(t->size); - // 哈希表,用于记录已被访问过的顶点 - HashTable *visited = newHash(t->size); - int resIndex = 0; - queuePush(que, startVet); // 将第一个元素入队 - hashMark(visited, startVet->pos); // 标记第一个入队的顶点 + Queue *queue = newQueue(); + enqueue(queue, startVet); + visited[(*visitedSize)++] = startVet; // 以顶点 vet 为起点,循环直至访问完所有顶点 - while (que->head < que->tail) { - // 遍历该顶点的边链表,将所有与该顶点有连接的,并且未被标记的顶点入队 - Node *n = queueTop(que)->list->head->next; - while (n != 0) { - // 查询哈希表,若该索引的顶点已入队,则跳过,否则入队并标记 - if (hashQuery(visited, n->val->pos) == 1) { - n = n->next; - continue; // 跳过已被访问过的顶点 + while (!isEmpty(queue)) { + Vertex *vet = dequeue(queue); // 队首顶点出队 + res[(*resSize)++] = vet; // 记录访问顶点 + // 遍历该顶点的所有邻接顶点 + AdjListNode *node = findNode(graph, vet); + while (node != NULL) { + // 跳过已被访问过的顶点 + if (!isVisited(visited, *visitedSize, node->vertex)) { + enqueue(queue, node->vertex); // 只入队未访问的顶点 + visited[(*visitedSize)++] = node->vertex; // 标记该顶点已被访问 } - queuePush(que, n->val); // 只入队未访问的顶点 - hashMark(visited, n->val->pos); // 标记该顶点已被访问 + node = node->next; } - // 队首元素存入数组 - res[resIndex] = queueTop(que); // 队首顶点加入顶点遍历序列 - resIndex++; - queuePop(que); // 队首元素出队 } // 释放内存 - freeQueue(que); - freeHash(visited); - resIndex = 0; - // 返回顶点遍历序列 - return res; + free(queue); } /* Driver Code */ int main() { - /* 初始化无向图 */ - GraphAdjList *graph = newGraphAdjList(3); - // 初始化顶点 - for (int i = 0; i < 10; i++) { - addVertex(graph, i); + // 初始化无向图 + int vals[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + int size = sizeof(vals) / sizeof(vals[0]); + Vertex **v = valsToVets(vals, size); + Vertex *edges[][2] = {{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]}, {v[1], v[4]}, {v[2], v[5]}, {v[3], v[4]}, + {v[3], v[6]}, {v[4], v[5]}, {v[4], v[7]}, {v[5], v[8]}, {v[6], v[7]}, {v[7], v[8]}}; + int egdeSize = sizeof(edges) / sizeof(edges[0]); + GraphAdjList *graph = newGraphAdjList(); + // 添加所有顶点和边 + for (int i = 0; i < size; i++) { + addVertex(graph, v[i]); + } + for (int i = 0; i < egdeSize; i++) { + addEdge(graph, edges[i][0], edges[i][1]); } - // 初始化边 - addEdge(graph, 0, 1); - addEdge(graph, 0, 3); - addEdge(graph, 1, 2); - addEdge(graph, 1, 4); - addEdge(graph, 2, 5); - addEdge(graph, 3, 4); - addEdge(graph, 3, 6); - addEdge(graph, 4, 5); - addEdge(graph, 4, 7); - addEdge(graph, 5, 8); - addEdge(graph, 6, 7); - addEdge(graph, 7, 8); - printf("\n初始化后,图为:\n"); + printf("\n初始化后,图为\n"); printGraph(graph); - printf("\n广度优先遍历(BFS)顶点序列为\n"); - Vertex **vets = graphBFS(graph, graph->vertices[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); + // 广度优先遍历 BFS + // 顶点遍历序列 + Vertex *res[MAX_SIZE]; + int resSize = 0; + // 用于记录已被访问过的顶点 + Vertex *visited[MAX_SIZE]; + int visitedSize = 0; + graphBFS(graph, v[0], res, &resSize, visited, &visitedSize); + printf("\n广度优先遍历(BFS)顶点序列为\n"); + printArray(vetsToVals(res, resSize), resSize); + // 释放内存 + delGraphAdjList(graph); return 0; } diff --git a/codes/c/chapter_graph/graph_dfs.c b/codes/c/chapter_graph/graph_dfs.c index 91a4e8b71..6fde398c4 100644 --- a/codes/c/chapter_graph/graph_dfs.c +++ b/codes/c/chapter_graph/graph_dfs.c @@ -6,105 +6,69 @@ #include "graph_adjacency_list.c" -/* 哈希表 */ -typedef struct { - unsigned int size; - unsigned int *array; -} HashTable; +// 假设节点最大数量为 100 +#define MAX_SIZE 100 -/* 初始化哈希表 */ -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; +/* 检查顶点是否已被访问 */ +int isVisited(Vertex **res, int size, Vertex *vet) { + // 遍历查找节点,使用 O(n) 时间 + for (int i = 0; i < size; i++) { + if (res[i] == vet) { + return 1; + } } -} - -/* 释放哈希表内存 */ -void freeHash(HashTable *h) { - free(h->array); - free(h); + return 0; } /* 深度优先遍历 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->list->head->next; - while (n != 0) { - // 递归访问邻接顶点 - dfs(graph, visited, n->val, res); - n = n->next; +void dfs(GraphAdjList *graph, Vertex **res, int *resSize, Vertex *vet) { + // 记录访问顶点 + res[(*resSize)++] = vet; + // 遍历该顶点的所有邻接顶点 + AdjListNode *node = findNode(graph, vet); + while (node != NULL) { + // 跳过已被访问过的顶点 + if (!isVisited(res, *resSize, node->vertex)) { + // 递归访问邻接顶点 + dfs(graph, res, resSize, node->vertex); + } + node = node->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; +void graphDFS(GraphAdjList *graph, Vertex *startVet, Vertex **res, int *resSize) { + dfs(graph, res, resSize, startVet); } /* Driver Code */ int main() { - GraphAdjList *graph = newGraphAdjList(10); - for (int i = 0; i < 7; i++) { - addVertex(graph, i); + // 初始化无向图 + int vals[] = {0, 1, 2, 3, 4, 5, 6}; + int size = sizeof(vals) / sizeof(vals[0]); + Vertex **v = valsToVets(vals, size); + Vertex *edges[][2] = {{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]}, {v[2], v[5]}, {v[4], v[5]}, {v[5], v[6]}}; + int egdeSize = sizeof(edges) / sizeof(edges[0]); + GraphAdjList *graph = newGraphAdjList(); + // 添加所有顶点和边 + for (int i = 0; i < size; i++) { + addVertex(graph, v[i]); + } + for (int i = 0; i < egdeSize; i++) { + addEdge(graph, edges[i][0], edges[i][1]); } - 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"); + printf("\n初始化后,图为\n"); printGraph(graph); // 深度优先遍历 DFS - Vertex **vet = graphDFS(graph, graph->vertices[0]); - - // 输出遍历结果 + Vertex *res[MAX_SIZE]; + int resSize = 0; + graphDFS(graph, v[0], res, &resSize); 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"); + printArray(vetsToVals(res, resSize), resSize); // 释放内存 - free(vet); + delGraphAdjList(graph); return 0; } diff --git a/codes/c/utils/common.h b/codes/c/utils/common.h index 7014cbb93..0b1ca7af8 100644 --- a/codes/c/utils/common.h +++ b/codes/c/utils/common.h @@ -18,6 +18,7 @@ #include "list_node.h" #include "print_util.h" #include "tree_node.h" +#include "vertex.h" // hash table lib #include "uthash.h" diff --git a/codes/c/utils/vertex.h b/codes/c/utils/vertex.h new file mode 100644 index 000000000..e4886942e --- /dev/null +++ b/codes/c/utils/vertex.h @@ -0,0 +1,49 @@ +/** + * File: vertex.h + * Created Time: 2023-10-28 + * Author: Krahets (krahets@163.com) + */ + +#ifndef VERTEX_H +#define VERTEX_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* 顶点结构体 */ +typedef struct { + int val; +} Vertex; + +/* 构造函数,初始化一个新节点 */ +Vertex *newVertex(int val) { + Vertex *vet; + vet = (Vertex *)malloc(sizeof(Vertex)); + vet->val = val; + return vet; +} + +/* 将值数组转换为顶点数组 */ +Vertex **valsToVets(int *vals, int size) { + Vertex **vertices = (Vertex **)malloc(size * sizeof(Vertex *)); + for (int i = 0; i < size; ++i) { + vertices[i] = newVertex(vals[i]); + } + return vertices; +} + +/* 将顶点数组转换为值数组 */ +int *vetsToVals(Vertex **vertices, int size) { + int *vals = (int *)malloc(size * sizeof(int)); + for (int i = 0; i < size; ++i) { + vals[i] = vertices[i]->val; + } + return vals; +} + +#ifdef __cplusplus +} +#endif + +#endif // VERTEX_H