diff --git a/codes/c/CMakeLists.txt b/codes/c/CMakeLists.txt index 915e7073..8b74fec3 100644 --- a/codes/c/CMakeLists.txt +++ b/codes/c/CMakeLists.txt @@ -13,3 +13,4 @@ add_subdirectory(chapter_hashing) add_subdirectory(chapter_tree) add_subdirectory(chapter_searching) add_subdirectory(chapter_sorting) +add_subdirectory(chapter_graph) diff --git a/codes/c/chapter_graph/CMakeLists.txt b/codes/c/chapter_graph/CMakeLists.txt new file mode 100644 index 00000000..3a6b5188 --- /dev/null +++ b/codes/c/chapter_graph/CMakeLists.txt @@ -0,0 +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) diff --git a/codes/c/chapter_graph/graph_adjacency_list.c b/codes/c/chapter_graph/graph_adjacency_list.c new file mode 100644 index 00000000..756e7a7f --- /dev/null +++ b/codes/c/chapter_graph/graph_adjacency_list.c @@ -0,0 +1,301 @@ +/** + * File: graph_adjacency_list.c + * Created Time: 2023-07-07 + * Author: NI-SW (947743645@qq.com) + */ + +#include "../utils/common.h" + +typedef struct Vertex Vertex; +typedef struct Node Node; +typedef struct linkList linkList; + +void freeVertex(Vertex *); +void freeLinklist(linkList *); +linkList *newLinklist(Vertex *); + +/* 链表节点 */ +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; + // 与其它节点相连接的边的链表 + linkList *linked; + // 索引位,标记该顶点在顶点列表中的索引 + unsigned int pos; +}; + +/* 顶点节点构造函数 */ +Vertex *newVertex(int val) { + Vertex *v = (Vertex *)malloc(sizeof(Vertex)); + // 为新节点赋值并建立该节点的链表 + v->val = val; + v->linked = newLinklist(v); + return v; +} + +/* 顶点内存释放函数 */ +void freeVertex(Vertex *val) { + // 释放该顶点和该顶点的链表的内存 + freeLinklist(val->linked); + free(val); +} + +/* 链表 */ +struct linkList { + Node *head; + Node *tail; +}; + +/* 链表头插法 */ +void pushFront(linkList *l, Vertex *val) { + Node *temp = newNode(); + temp->val = val; + temp->next = l->head->next; + l->head->next = temp; + if (l->tail == l->head) { + l->tail = temp; + } +} + +/* 链表尾插法 */ +void pushBack(linkList *l, Vertex *val) { + Node *temp = newNode(); + temp->val = val; + temp->next = 0; + l->tail->next = temp; + l->tail = temp; +} + +/* 根据顶点地址与该顶点连接的删除边 */ +void removeLink(linkList *l, Vertex *val) { + Node *temp = l->head->next; + Node *front = l->head; + while (temp != 0) { + if (temp->val == val) { + front->next = temp->next; + if (l->tail == temp) { + l->tail = front; + } + free(temp); + return; + } + front = temp; + temp = temp->next; + } + + if (temp->next == 0) { + printf("vertex not found!\n"); + } +} + +/* 根据索引查找链表中节点 */ +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; + Node *front = l->head; + while (temp != 0) { + if (temp->val == val) { + front->next = temp->next; + if (l->tail == temp) { + l->tail = front; + } + freeVertex(val); + free(temp); + return; + } + front = temp; + temp = temp->next; + } + + if (temp->next == 0) { + printf("vertex not found!\n"); + } +} + +/* 释放链表内存 */ +void freeLinklist(linkList *l) { + Node *temp = l->head->next; + while (temp != 0) { + free(l->head); + l->head = temp; + temp = temp->next; + } + free(l->head); + l->head = 0; + free(l); +} + +/* 链表构造函数 */ +linkList *newLinklist(Vertex *val) { + linkList *newLinklist = (linkList *)malloc(sizeof(linkList)); + + newLinklist->head = newNode(); + newLinklist->head->val = val; + newLinklist->tail = newLinklist->head; + newLinklist->head->next = 0; + + return newLinklist; +} + +/* 基于邻接链表实现的无向图类结构 */ +struct graphAdjList { + // 顶点列表 + Vertex **verticesList; + // 顶点数量 + unsigned int size; + // 当前容量 + unsigned int capacity; +}; + +typedef struct graphAdjList graphAdjList; + +/* 添加边 */ +void addEdge(graphAdjList *t, int i, int j) { + // 越界检查 + if (i < 0 || j < 0 || i == j || i >= t->size || j >= t->size) { + printf("Out of range in %s:%d\n", __FILE__, __LINE__); + return; + } + // 查找待连接的节点 + Vertex *v1 = t->verticesList[i]; + Vertex *v2 = t->verticesList[j]; + + // 连接节点 + pushBack(v1->linked, v2); + pushBack(v2->linked, v1); +} + +/* 删除边 */ +void removeEdge(graphAdjList *t, int i, int j) { + // 越界检查 + if (i < 0 || j < 0 || i == j || i >= t->size || j >= t->size) { + printf("Out of range in %s:%d\n", __FILE__, __LINE__); + return; + } + + // 查找待删除边的相关节点 + Vertex *v1 = t->verticesList[i]; + Vertex *v2 = t->verticesList[j]; + + // 移除待删除边 + removeLink(v1->linked, v2); + removeLink(v2->linked, v1); +} + +/* 添加顶点 */ +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; + } + // 申请新顶点内存并将新顶点地址存入顶点列表 + Vertex *newV = newVertex(val); + newV->pos = t->size; + newV->linked = newLinklist(newV); + t->verticesList[t->size] = newV; + t->size++; +} + +/* 删除顶点 */ +void removeVertex(graphAdjList *t, unsigned int index) { + // 越界检查 + if (index < 0 || index >= t->size) { + printf("Out of range in %s:%d\n", __FILE__, __LINE__); + exit(1); + } + + // 查找待删节点 + Vertex *v = t->verticesList[index]; + // 若不存在该节点,则返回 + if (v == 0) { + printf("index is:%d\n", index); + printf("Out of range in %s:%d\n", __FILE__, __LINE__); + return; + } + + // 遍历待删除节点链表,将所有与待删除结点有关的边删除 + Node *temp = v->linked->head->next; + while (temp != 0) { + removeLink(temp->val->linked, v); + temp = temp->next; + } + + // 定点列表前移 + for (int i = index; i < t->size - 1; i++) { + t->verticesList[i] = t->verticesList[i + 1]; + } + t->verticesList[t->size - 1] = 0; + t->size--; + + //释放被删除顶点的内存 + freeVertex(v); +} + +/* 打印顶点与邻接矩阵 */ +void printGraph(graphAdjList *t) { + printf("邻接表 =\n"); + for (int i = 0; i < t->size; i++) { + Node *n = t->verticesList[i]->linked->head->next; + printf("%d: [", t->verticesList[i]->val); + while (n != 0) { + if (n->next != 0) { + printf("%d, ", n->val->val); + } else { + printf("%d", n->val->val); + } + n = n->next; + } + printf("]\n"); + } +} + +/* 构造函数 */ +graphAdjList *newGraphic(unsigned int verticesNumber) { + // 申请内存 + 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; +} diff --git a/codes/c/chapter_graph/graph_adjacency_list_test.c b/codes/c/chapter_graph/graph_adjacency_list_test.c new file mode 100644 index 00000000..c881a44a --- /dev/null +++ b/codes/c/chapter_graph/graph_adjacency_list_test.c @@ -0,0 +1,54 @@ +/** + * File: graph_adjacency_list_test.c + * Created Time: 2023-07-11 + * Author: NI-SW (947743645@qq.com) + */ + +#include "graph_adjacency_list.c" + +/* Driver Code */ +int main() { + + /* 初始化无向图 */ + graphAdjList *graph = newGraphic(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"); + printGraph(graph); + + /* 添加边 */ + // 顶点 1, 2 的索引分别为 0, 2 + addEdge(graph, 0, 2); + printf("\n添加边 1-2 后图为\n"); + printGraph(graph); + + /* 删除边 */ + // 顶点 1, 3 的索引分别为 0, 1 + removeEdge(graph, 0, 1); + printf("\n删除边 1-3 后,图为\n"); + printGraph(graph); + + /* 添加顶点 */ + addVertex(graph, 6); + printf("\n添加顶点 6 后,图为\n"); + printGraph(graph); + + /* 删除顶点 */ + // 顶点 3 的索引为 1 + removeVertex(graph, 1); + printf("\n删除顶点 3 后,图为\n"); + printGraph(graph); + + return 0; +} diff --git a/codes/c/chapter_graph/graph_adjacency_matrix.c b/codes/c/chapter_graph/graph_adjacency_matrix.c new file mode 100644 index 00000000..90f97054 --- /dev/null +++ b/codes/c/chapter_graph/graph_adjacency_matrix.c @@ -0,0 +1,238 @@ +/** + * File: graph_adjacency_matrix.c + * Created Time: 2023-07-06 + * Author: NI-SW (947743645@qq.com) + */ + +#include "../utils/common.h" + +/* 基于邻接矩阵实现的无向图类结构 */ +struct graphAdjMat { + int *vertices; + unsigned int **adjMat; + unsigned int size; + unsigned int capacity; +}; + +typedef struct graphAdjMat graphAdjMat; + +/* 添加边 */ +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); + } + t->adjMat[i][j] = 1; + t->adjMat[j][i] = 1; +} + +/* 删除边 */ +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); + } + t->adjMat[i][j] = 0; + t->adjMat[j][i] = 0; +} + +/* 添加顶点 */ +void addVertex(graphAdjMat *t, int val) { + // 如果实际使用不大于预设空间,则直接初始化新空间 + if (t->size < t->capacity) { + t->vertices[t->size] = val; + + // 邻接矩新列阵置0 + for (int i = 0; i < t->size; i++) { + t->adjMat[i][t->size] = 0; + } + memset(t->adjMat[t->size], 0, sizeof(unsigned int) * (t->size + 1)); + t->size++; + return; + } + + // 扩容,申请新的顶点数组 + int *temp = (int *)malloc(sizeof(int) * (t->size * 2)); + memcpy(temp, t->vertices, sizeof(int) * t->size); + temp[t->size] = val; + + // 释放原数组 + free(t->vertices); + t->vertices = temp; + + // 扩容,申请新的二维数组 + unsigned int **tempMat = (unsigned int **)malloc(sizeof(unsigned int *) * t->size * 2); + unsigned int *tempMatLine = (unsigned int *)malloc(sizeof(unsigned int) * (t->size * 2) * (t->size * 2)); + memset(tempMatLine, 0, sizeof(unsigned int) * (t->size * 2) * (t->size * 2)); + for (int k = 0; k < t->size * 2; k++) { + 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); + } + + // 新列置0 + for (int i = 0; i < t->size; i++) { + tempMat[i][t->size] = 0; + } + memset(tempMat[t->size], 0, sizeof(unsigned int) * (t->size + 1)); + + // 释放原数组 + free(t->adjMat[0]); + free(t->adjMat); + + // 扩容后,指向新地址 + t->adjMat = tempMat; + t->capacity = t->size * 2; + t->size++; +} + +/* 删除顶点 */ +void removeVertex(graphAdjMat *t, unsigned int index) { + // 越界检查 + if (index < 0 || index >= t->size) { + 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]; + } + + // 将被前移的最后一个顶点置0 + t->vertices[t->size - 1] = 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]; + } + } 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->size--; +} + +/* 打印顶点与邻接矩阵 */ +void printGraph(graphAdjMat *t) { + if (t->size == 0) { + printf("graph is empty\n"); + return; + } + printf("顶点列表 = ["); + for (int i = 0; i < t->size; i++) { + if (i != t->size - 1) { + printf("%d, ", t->vertices[i]); + } else { + printf("%d", t->vertices[i]); + } + } + printf("]\n"); + printf("邻接矩阵 =\n[\n"); + for (int i = 0; i < t->size; i++) { + printf(" ["); + for (int j = 0; j < t->size; j++) { + if (j != t->size - 1) { + printf("%u, ", t->adjMat[i][j]); + } else { + printf("%u", t->adjMat[i][j]); + } + } + printf("],\n"); + } + printf("]\n"); +} + +/* 构造函数 */ +graphAdjMat *newGraphic(unsigned int numberVertices, int *vertices, unsigned int **adjMat) { + // 函数指针 + 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; + } + + // 赋值 + memcpy(newGraph->vertices, vertices, sizeof(int) * numberVertices); + for (int i = 0; i < numberVertices; i++) { + 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; + 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 = newGraphic(5, vertices, edge); + free(edge); + free(temp); + printf("\n初始化后,图为:\n"); + printGraph(graph); + + /* 添加边 */ + // 顶点 1, 2 的索引分别为 0, 2 + addEdge(graph, 0, 2); + printf("\n添加边 1-2 后图为\n"); + printGraph(graph); + + /* 删除边 */ + // 顶点 1, 3 的索引分别为 0, 1 + removeEdge(graph, 0, 1); + printf("\n删除边 1-3 后,图为\n"); + printGraph(graph); + + /* 添加顶点 */ + addVertex(graph, 6); + printf("\n添加顶点 6 后,图为\n"); + printGraph(graph); + + /* 删除顶点 */ + // 顶点 3 的索引为 1 + removeVertex(graph, 1); + printf("\n删除顶点 3 后,图为\n"); + printGraph(graph); + + return 0; +} diff --git a/codes/c/chapter_graph/graph_bfs b/codes/c/chapter_graph/graph_bfs new file mode 100644 index 00000000..99e11ea8 --- /dev/null +++ b/codes/c/chapter_graph/graph_bfs @@ -0,0 +1,157 @@ +/** + * File: graph_bfs.c + * Created Time: 2023-07-11 + * 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) { + // 若顶点已被标记,则返回 0 + if (h->array[index % h->size] == 1) { + return 0; + } else { + return 1; + } +} + +/* 释放哈希表内存 */ +void freeHash(hashTable *h) { + free(h->array); + free(h); +} + +/* 队列 */ +struct queue { + Vertex **list; + unsigned int size; + int head; + int tail; +}; + +typedef struct queue queue; + +/* 初始化队列 */ +queue *newQueue(unsigned int size) { + queue *q = (queue *)malloc(sizeof(queue)); + q->size = size; + q->list = (Vertex **)malloc(sizeof(Vertex *) * size); + q->head = 0; + q->tail = 0; + + return q; +} + +/* 入队 */ +void queuePush(queue *q, Vertex *v) { + q->list[q->tail] = v; + q->tail++; +} + +/* 出队 */ +void queuePop(queue *q) { + q->head++; +} + +/* 队首元素 */ +Vertex *queueTop(queue *q) { + return q->list[q->head]; +} + +/* 释放队列内存 */ +void freeQueue(queue *q) { + free(q->list); + free(q); +} + +/* 广度优先遍历 */ +void graphBFS(graphAdjList *t) { + // 初始化队列与哈希表 + queue *que = newQueue(t->size); + hashTable *visited = newHash(t->size); + // 将第一个元素入队 + queuePush(que, t->verticesList[0]); + hashMark(visited, t->verticesList[0]->pos); + + printf("\n["); + 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); + } + n = n->next; + } + // 打印队首元素 + if (que->head == que->tail - 1) { + printf("%d]\n", queueTop(que)->val); + } else { + printf("%d, ", queueTop(que)->val); + } + // 队首元素出队 + queuePop(que); + } + printf("\n"); + + // 释放队列与哈希表内存 + freeQueue(que); + freeHash(visited); +} + +int main() { + + /* 初始化无向图 */ + graphAdjList *graph = newGraphic(3); + // 初始化顶点 + for (int i = 0; i < 10; i++) { + addVertex(graph, i); + } + // 初始化边 + 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"); + printGraph(graph); + + printf("\n广度优先遍历(BFS)顶点序列为"); + graphBFS(graph); + + return 0; +} +