diff --git a/.gitignore b/.gitignore index bf1bb21..026730a 100644 --- a/.gitignore +++ b/.gitignore @@ -25,5 +25,5 @@ node_modules dist dist-ssr *.local -.vscode/ +# .vscode/ built/ \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..9f67e65 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,23 @@ +{ + "configurations": [ + { + "name": "Win32", + "includePath": [ + "D:\\MinGW\\include", // Include system path, stdio.h ... + "D:\\MinGW\\lib\\gcc\\mingw32\\6.3.0\\include", // Include system path, stddef.h ... + "${workspaceFolder}/**" + ], + "defines": [ + "_DEBUG", + "UNICODE", + "_UNICODE" + ], + "windowsSdkVersion": "8.1", + "compilerPath": "D:\\MinGW\\bin\\gdb.exe", // MinGW gdb path + "cStandard": "c17", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-x64" // gcc-x64, gcc-arm64, msvc-x64, clang-x64, clang-arm64 + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..d3db956 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,29 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "gcc.exe build and debug active file", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}\\built\\app.exe", // executable file + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": true, // printf + "MIMode": "gdb", // gcc-x64, gcc-arm64, msvc-x64, clang-x64, clang-arm64 + "miDebuggerPath": "D:\\MinGW\\bin\\gdb.exe", // MinGW gdb path + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "preLaunchTask": "C/C++: gcc.exe build active file" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..adddc71 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "init.h": "c" + } +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..4ad2313 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,30 @@ +{ + "tasks": [ + { + "type": "cppbuild", + "label": "C/C++: gcc.exe build active file", + // "command": "D:\\MinGW\\bin\\gcc.exe", + "command": "make", // use makefile + "args": [ + // "-fdiagnostics-color=always", + // "-g", + // "${file}", + // "-o", + // "${fileDirname}\\${fileBasenameNoExtension}.exe" + "CFLAG=-g" + ], + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "detail": "Task generated by Debugger." + } + ], + "version": "2.0.0" +} \ No newline at end of file diff --git a/README.en.md b/README.en.md index e81b550..3fb2c37 100644 --- a/README.en.md +++ b/README.en.md @@ -13,7 +13,7 @@ It has the characteristics of **simplicity, universality, and efficiency**, with | module | version | usage | path | describe | |:-------------|:---------|:-----------------------------|:--------------------------------------|:--------------------------------------| -| overall | 00.01.01 | [link](README.md) | ./ | Overall +| overall | 00.02.00 | [link](README.md) | ./ | Overall | init | 01.00.00 | [link](/doc/init.md) | ./source/00_application | Initialize export module | console | 01.00.00 | [link](/doc/console.md) | ./source/00_application/console | Console command input, combined with the 'command' module, parsing commands entered in the console | arg | 01.00.00 | [link](/doc/arg.md) | ./source/01_general | Indefinite parameters, obtain the number of indefinite parameters and specified parameters @@ -47,6 +47,7 @@ It has the characteristics of **simplicity, universality, and efficiency**, with | str | 01.00.00 | [link](/doc/str.md) | ./source/03_container | String class | tree | 01.00.00 | [link](/doc/tree.md) | ./source/03_container | Universal tree container | vector | 01.00.00 | [link](/doc/vector.md) | ./source/03_container | Universal vector(array) container +| graph | 01.00.00 | [link](/doc/graph.md) | ./source/03_container | Universal graph container | check | 01.00.00 | [link](/doc/check.md) | ./source/04_algorithm | Verification algorithm, sum check, parity check, XOR check, LRC check | crc | 01.00.00 | [link](/doc/check.md) | ./source/04_algorithm | Universal and standard CRC algorithms | encrypt | 01.00.00 | [link](/doc/encrypt.md) | ./source/04_algorithm | Encryption and decryption algorithms diff --git a/README.md b/README.md index baef264..b050d96 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ varch(we-architecture,意为我们的框架库)是嵌入式C语言常用 | module | version | usage | path | describe | |:-------------|:---------|:-----------------------------|:--------------------------------------|:--------------------------------------| -| overall | 00.01.01 | [link](README.md) | ./ | 整体 +| overall | 00.02.00 | [link](README.md) | ./ | 整体 | init | 01.00.00 | [link](/doc/init.md) | ./source/00_application | 初始化导出模块 | console | 01.00.00 | [link](/doc/console.md) | ./source/00_application/console | 控制台命令输入,结合 `command` 模块,解析在控制台中输入的命令 | arg | 01.00.00 | [link](/doc/arg.md) | ./source/01_general | 不定参数,获取不定参数和指定参数的个数 @@ -40,13 +40,14 @@ varch(we-architecture,意为我们的框架库)是嵌入式C语言常用 | dict | 01.00.00 | [link](/doc/dict.md) | ./source/03_container | 通用字典容器,基于哈希表实现 | heap | 01.00.00 | [link](/doc/heap.md) | ./source/03_container | 通用堆容器 | list | 01.00.00 | [link](/doc/list.md) | ./source/03_container | 通用列表容器,单链接和支持内部迭代器 -| map | 01.00.00 | [link](/doc/map.md) | ./source/03_container | 通用地图容器,基于RB-tree实现 +| map | 01.00.00 | [link](/doc/map.md) | ./source/03_container | 通用映射容器,基于RB-tree实现 | queue | 01.00.00 | [link](/doc/queue.md) | ./source/03_container | 通用队列容器 | set | 01.00.00 | [link](/doc/set.md) | ./source/03_container | 通用集合容器,基于RB-tree实现 | stack | 01.00.00 | [link](/doc/stack.md) | ./source/03_container | 通用栈式容器 | str | 01.00.00 | [link](/doc/str.md) | ./source/03_container | 字符串类 | tree | 01.00.00 | [link](/doc/tree.md) | ./source/03_container | 通用树容器 | vector | 01.00.00 | [link](/doc/vector.md) | ./source/03_container | 通用向量(数组)容器 +| graph | 01.00.00 | [link](/doc/graph.md) | ./source/03_container | 通用图容器 | check | 01.00.00 | [link](/doc/check.md) | ./source/04_algorithm | 校验算法,求和校验,奇偶校验,异或校验,LRC校验 | crc | 01.00.00 | [link](/doc/check.md) | ./source/04_algorithm | 通用标准CRC算法 | encrypt | 01.00.00 | [link](/doc/encrypt.md) | ./source/04_algorithm | 加密解密算法 diff --git a/built.sh b/built.sh new file mode 100644 index 0000000..410e537 --- /dev/null +++ b/built.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +exe_file="built/bin/app" + +echo "cleaning..." +if [ -f $exe_file ]; then + rm $exe_file +fi + +echo "compilling..." + +if [ "$1" == "debug" ]; then + make CFLAG=-g +else + make +fi + +if [ -f $exe_file ]; then + echo "#################################" + echo "# Compile success !!!..." + echo "#################################" +else + echo "#################################" + echo "*** Compile fail ***" + echo "#################################" +fi + +# find . -name *.h -o -name *.c | xargs wc -l + +# enter key to continue +read -p "Press enter to continue" diff --git a/clean.sh b/clean.sh new file mode 100644 index 0000000..e5be84d --- /dev/null +++ b/clean.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +echo "cleaning all ..." + +rm built/* -rf + + diff --git a/doc/graph.md b/doc/graph.md new file mode 100644 index 0000000..a9e5416 --- /dev/null +++ b/doc/graph.md @@ -0,0 +1,764 @@ +## 介绍 + +图(Graph)是一种由节点(或称顶点)和边组成的数据结构,通常用于表示对象之间的关系。以下是图的基本概念及其特点: + +### 1. 基本组成 +- **节点(Vertex)**:图中的基本单位,表示对象。 +- **边(Edge)**:连接两个节点的线,表示节点之间的关系。 + +### 2. 类型 +- **有向图(Directed Graph)**:边有方向,表示从一个节点到另一个节点的单向关系。 +- **无向图(Undirected Graph)**:边没有方向,表示节点之间的双向关系。 +- **加权图(Weighted Graph)**:边带有权重,表示连接两个节点的代价或距离。 + +### 3. 表示方法 +- **邻接矩阵(Adjacency Matrix)**:用一个二维数组表示节点之间的连接关系,数组中的值表示边的存在(1)或不存在(0),如果是加权图,则用边的权重表示。 + +- **邻接表(Adjacency List)**:用一个数组或链表表示每个节点的邻接节点,适用于稀疏图。 + +### 4. 应用 +- **社交网络**:节点表示用户,边表示用户之间的关系。 +- **网络路由**:节点表示路由器,边表示连接。 +- **路径查找**:如地图应用中查找最短路径。 + +该模块实现了一个图的数据结构,包括基本的图操作,如添加和删除顶点与边,以及深度优先搜索(DFS)和广度优先搜索(BFS)等算法。该图支持有向图和无向图,适合于各种图论应用。 + +## 接口 + +### 创建和删除graph对象 + +```c +graph_t graph_create(int max, int directed); +``` + +创建并初始化一个图。 + +**参数**: +- `max`: 图中最大顶点数。 +- `directed`: 标志位,指示图是否为有向图(非零)或无向图(零)。 + +**返回**: 返回指向新创建图的指针,如果创建失败则返回NULL。 + +```c +void graph_delete(graph_t graph); +``` + +销毁图并释放相关资源。 + +**参数**: +- `graph`: 指向要销毁的图的指针。 + +**返回**: 无 + +例子 +```c +graph_t graph = graph_create(10, 1); +graph_delete(graph); +``` + +### 添加顶点 + +```c +int graph_add_vertex(graph_t graph, void *data, int size); +``` + +向图中添加一个顶点。 + +**参数**: +- `graph`: 指向要添加顶点的图的指针。 +- `data`: 与顶点相关联的数据的指针。 +- `size`: 要复制的数据的大小。 + +**返回**: 返回添加的顶点的索引,如果添加失败则返回-1。 + +例子 +```c +void graph_traverse_int(int index, void *data, int size) +{ + printf("graph[%d] %d\r\n", index, *(int *)data); +} + +static void test_add_vertex(void) +{ + graph_t graph = NULL; + + graph = graph_create(10, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 12), sizeof(int)); + graph_add_vertex(graph, la(int, 13), sizeof(int)); + graph_add_vertex(graph, la(int, 15), sizeof(int)); + graph_add_vertex(graph, la(int, 23), sizeof(int)); + + graph_ls(graph, graph_traverse_int); + + graph_delete(graph); +} +``` + +其中 `la()` 为获取字面量地址的宏定义,具体可以参考 [test_graph.c](../test/test_graph.c) 文件 +`graph_ls()` 线性搜索方法,下文介绍。 + +结果 +``` +graph[0] 12 +graph[1] 13 +graph[2] 15 +graph[3] 23 + +``` + +### 添加边 + +```c +int graph_add_edge(graph_t graph, int start, int end, int weight); +``` + +向图中添加一条边。 + +**参数**: +- `graph`: 指向要添加边的图的指针。 +- `start`: 起始顶点的索引。 +- `end`: 结束顶点的索引。 +- `weight`: 边的权重。 + +**返回**: 如果边成功添加返回1,失败返回0。 + +例子 +```c +static void test_add_edge(void) +{ + graph_t graph = NULL; + + graph = graph_create(10, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 12), sizeof(int)); + graph_add_vertex(graph, la(int, 13), sizeof(int)); + graph_add_vertex(graph, la(int, 15), sizeof(int)); + graph_add_vertex(graph, la(int, 23), sizeof(int)); + + printf("---------------\r\n"); + + graph_dfs(graph, 0, graph_traverse_int); + + printf("---------------\r\n"); + + graph_add_edge(graph, 0, 1, 0); + graph_add_edge(graph, 0, 2, 0); + graph_add_edge(graph, 0, 3, 0); + graph_add_edge(graph, 1, 0, 0); + graph_add_edge(graph, 1, 2, 0); + graph_add_edge(graph, 1, 3, 0); + graph_add_edge(graph, 2, 3, 0); + + graph_dfs(graph, 0, graph_traverse_int); + + printf("---------------\r\n"); + + graph_delete(graph); +} +``` + +`graph_dfs()` 为深度优先搜索方法,下文介绍 + +对比添加边前后搜索的情况,没有边连接的图搜不到下一个顶点 + +结果 +``` +--------------- +graph[0] 12 +--------------- +graph[0] 12 +graph[3] 23 +graph[2] 15 +graph[1] 13 +--------------- +``` + +### 移除顶点和边 + +```c +int graph_remove_vertex(graph_t graph, int index); +``` + +从图中移除指定的顶点及其关联的边。 + +**参数**: +- `graph`: 指向要移除顶点的图的指针。 +- `index`: 要移除的顶点的索引。 + +**返回**: 如果顶点成功移除返回1,失败返回0。 + +```c +int graph_remove_edge(graph_t graph, int start, int end); +``` + +从图中移除指定的边。 + +**参数**: +- `graph`: 指向要移除边的图的指针。 +- `start`: 边的起始顶点索引。 +- `end`: 边的结束顶点索引。 + +**返回**: 如果边成功移除返回1,失败返回0。 + +例子 +```c +static void test_remove(void) +{ + graph_t graph = NULL; + + graph = graph_create(100, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 100), sizeof(int)); + graph_add_vertex(graph, la(int, 200), sizeof(int)); + graph_add_vertex(graph, la(int, 300), sizeof(int)); + graph_add_vertex(graph, la(int, 500), sizeof(int)); + + graph_add_edge(graph, 0, 1, 0); + graph_add_edge(graph, 0, 2, 0); + graph_add_edge(graph, 0, 3, 0); + graph_add_edge(graph, 1, 0, 0); + graph_add_edge(graph, 1, 2, 0); + graph_add_edge(graph, 1, 3, 0); + graph_add_edge(graph, 2, 3, 0); + + graph_remove_vertex(graph, 1); + graph_remove_edge(graph, 0, 2); + + graph_dfs(graph, 0, graph_traverse_int); + + graph_delete(graph); +} +``` + +结果 +``` +graph[0] 100 +graph[3] 500 +``` + +### 遍历 + +```c +void graph_ls(graph_t graph, graph_traverse_t func); +``` + +从图开始位置逐个线性遍历。 + +**参数**: +- `graph`: 指向要遍历的图的指针。 +- `func`: 对每个访问到的顶点执行的回调函数。 + +**返回**: 无 + +```c +void graph_ls(graph_t graph, graph_traverse_t func); +``` + +从指定起始顶点开始执行深度优先遍历。 + +**参数**: +- `graph`: 指向要遍历的图的指针。 +- `start`: 起始顶点的索引。 +- `func`: 对每个访问到的顶点执行的回调函数。 + +**返回**: 无 + +```c +void graph_dfs(graph_t graph, int start, graph_traverse_t func); +``` + +从指定起始顶点开始执行广度优先遍历。 + +**参数**: +- `graph`: 指向要遍历的图的指针。 +- `start`: 起始顶点的索引。 +- `func`: 对每个访问到的顶点执行的回调函数。 + +**返回**: 无 + +例子 +```c +static void test_search(void) +{ + graph_t graph = NULL; + + graph = graph_create(100, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 100), sizeof(int)); + graph_add_vertex(graph, la(int, 200), sizeof(int)); + graph_add_vertex(graph, la(int, 300), sizeof(int)); + graph_add_vertex(graph, la(int, 500), sizeof(int)); + + graph_add_edge(graph, 0, 1, 0); + graph_add_edge(graph, 0, 2, 0); + graph_add_edge(graph, 0, 3, 0); + graph_add_edge(graph, 1, 0, 0); + graph_add_edge(graph, 1, 2, 0); + graph_add_edge(graph, 1, 3, 0); + graph_add_edge(graph, 2, 3, 0); + + graph_remove_vertex(graph, 1); + graph_remove_edge(graph, 0, 2); + + printf("graph_ls ----------------------\r\n"); + graph_ls(graph, graph_traverse_int); + printf("graph_dfs ----------------------\r\n"); + graph_dfs(graph, 0, graph_traverse_int); + printf("graph_bfs ----------------------\r\n"); + graph_bfs(graph, 0, graph_traverse_int); + + graph_delete(graph); +} +``` + +结果 +``` +graph_ls ---------------------- +graph[0] 100 +graph[2] 300 +graph[3] 500 +graph_dfs ---------------------- +graph[0] 100 +graph[3] 500 +graph_bfs ---------------------- +graph[0] 100 +graph[3] 500 +``` + +### 设置和获取数据 + +```c +int graph_vertex_set_data(graph_t graph, int index, void *data, int size); +``` + +为图中的指定顶点设置数据。 + +**参数**: +- `graph`: 指向图的指针。 +- `index`: 顶点的索引。 +- `data`: 要设置的数据的指针。 +- `size`: 数据的大小。 + +**返回**: 如果成功设置数据返回1,失败返回0。 + +```c +int graph_vertex_get_data(graph_t graph, int index, void *data, int size); +``` + +从图中的指定顶点获取数据。 + +**参数**: +- `graph`: 指向图的指针。 +- `index`: 顶点的索引。 +- `data`: 指向将要复制数据的位置的指针。 +- `size`: 数据缓冲区的大小。 + +**返回**: 如果成功获取数据返回1,失败返回0。 + +```c +void *graph_vertex_data(graph_t graph, int index, int *size); +``` + +获取图中指定顶点的数据。 + +**参数**: +- `graph`: 指向图的指针。 +- `index`: 顶点的索引。 +- `size`: 指向存储顶点数据大小的指针(如果不为NULL)。 + +**返回**: 返回顶点数据的指针,如果失败则返回NULL。 + +例子 +```c +static void test_data(void) +{ + graph_t graph = NULL; + + graph = graph_create(100, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 100), sizeof(int)); + graph_add_vertex(graph, la(int, 200), sizeof(int)); + graph_add_vertex(graph, la(int, 300), sizeof(int)); + graph_add_vertex(graph, la(int, 500), sizeof(int)); + + graph_vertex_set_data(graph, 2, la(int, 1024), sizeof(int)); + + int size = 0; + printf("graph_vertex_data[3].data = %d\r\n", *(int *)graph_vertex_data(graph, 3, &size)); + printf("graph_vertex_data[3].size = %d\r\n", size); + + graph_ls(graph, graph_traverse_int); + + graph_delete(graph); +} +``` + +结果 +``` +graph_vertex_data[3].data = 500 +graph_vertex_data[3].size = 4 +graph[0] 100 +graph[1] 200 +graph[2] 1024 +graph[3] 500 +``` + +### 出度和入度 + +```c +int graph_out_degree(graph_t graph, int index); +``` + +获取指定顶点的出度。 + +**参数**: +- `graph`: 指向图的指针。 +- `index`: 顶点的索引。 + +**返回**: 顶点的出度,如果失败返回-1。 + +```c +int graph_in_degree(graph_t graph, int index); +``` + +获取指定顶点的入度。 + +**参数**: +- `graph`: 指向图的指针。 +- `index`: 顶点的索引。 + +**返回**: 顶点的入度,如果失败返回-1。 + +例子 +```c +static void test_degree(void) +{ + graph_t graph = NULL; + + graph = graph_create(10, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 100), sizeof(int)); + graph_add_vertex(graph, la(int, 200), sizeof(int)); + graph_add_vertex(graph, la(int, 300), sizeof(int)); + graph_add_vertex(graph, la(int, 500), sizeof(int)); + + graph_add_edge(graph, 0, 1, 0); + graph_add_edge(graph, 0, 2, 0); + graph_add_edge(graph, 0, 3, 0); + graph_add_edge(graph, 1, 0, 0); + graph_add_edge(graph, 1, 2, 0); + graph_add_edge(graph, 1, 3, 0); + graph_add_edge(graph, 2, 3, 0); + + printf("graph_out_degree %d\r\n", graph_out_degree(graph, 3)); + printf("graph_out_degree %d\r\n", graph_out_degree(graph, 2)); + + printf("graph_in_degree %d\r\n", graph_in_degree(graph, 0)); + printf("graph_in_degree %d\r\n", graph_in_degree(graph, 2)); + + graph_delete(graph); +} +``` + +结果 +``` +graph_out_degree 0 +graph_out_degree 1 +graph_in_degree 1 +graph_in_degree 2 +``` + +### 相邻 + +```c +int graph_is_adjacent(graph_t graph, int start, int end); +``` + +检查两个顶点是否相邻。 + +**参数**: +- `graph`: 指向图的指针。 +- `start`: 起始顶点的索引。 +- `end`: 结束顶点的索引。 + +**返回**: 。如果顶点相邻返回1,否则返回0或失败 + + +### 权重 + +```c +int graph_get_edge_weight(graph_t graph, int start, int end); +``` + +获取两个顶点之间边的权重(如果存在)。 + +**参数**: +- `graph`: 指向图的指针。 +- `start`: 起始顶点的索引。 +- `end`: 结束顶点的索引。 + +**返回**: 边的权重,如果失败或边不存在返回INT_MAX。 + +```c +int graph_set_edge_weight(graph_t graph, int start, int end, int weight); +``` + +设置两个顶点之间边的权重(如果存在)。 + +**参数**: +- `graph`: 指向图的指针。 +- `start`: 起始顶点的索引。 +- `end`: 结束顶点的索引。 +- `weight`: 边的权重。 + +**返回**: 如果存在该边返回1,否则返回0或失败。 + +例子 +```c +static void test_weight(void) +{ + graph_t graph = NULL; + + graph = graph_create(100, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 100), sizeof(int)); + graph_add_vertex(graph, la(int, 200), sizeof(int)); + graph_add_vertex(graph, la(int, 300), sizeof(int)); + graph_add_vertex(graph, la(int, 500), sizeof(int)); + + graph_add_edge(graph, 0, 1, 0); + graph_add_edge(graph, 0, 2, 0); + graph_add_edge(graph, 0, 3, 10); + graph_add_edge(graph, 1, 0, 0); + graph_add_edge(graph, 1, 2, 0); + graph_add_edge(graph, 1, 3, 0); + graph_add_edge(graph, 2, 3, 0); + + printf("graph_get_edge_weight = %d\r\n", graph_get_edge_weight(graph, 0, 3)); + graph_set_edge_weight(graph, 0, 3, 1024); + printf("graph_get_edge_weight = %d\r\n", graph_get_edge_weight(graph, 0, 3)); + + graph_delete(graph); +} +``` + +结果 +``` +graph_get_edge_weight = 10 +graph_get_edge_weight = 1024 + +``` + + +### 检查顶点 + +```c +int graph_contains_vertex(graph_t graph, int index); +``` + +检查顶点是否存在于图中。 + +**参数**: +- `graph`: 指向图的指针。 +- `index`: 顶点的索引。 + +**返回**: 如果顶点存在返回1,否则返回0或失败。 + + +### 拓扑排序 + +```c +int graph_contains_vertex(graph_t graph, int index); +``` + +对图进行拓扑排序。 + +**参数**: +- `graph`: 指向图的指针。 + +**返回**: 无 + + +### 最短路径 + +```c +void graph_shortest_path(graph_t graph, int start); +``` + +查找从起始顶点到其它顶点的最短路径。 + +**参数**: +- `graph`: 指向图的指针。 +- `start`: 起始顶点的索引。 + +**返回**: 无 + +例子 +```c +static void test_shortest_path(void) +{ + graph_t graph = NULL; + + graph = graph_create(100, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 100), sizeof(int)); + graph_add_vertex(graph, la(int, 200), sizeof(int)); + graph_add_vertex(graph, la(int, 300), sizeof(int)); + graph_add_vertex(graph, la(int, 500), sizeof(int)); + + graph_add_edge(graph, 0, 1, 3); + graph_add_edge(graph, 0, 2, 2); + graph_add_edge(graph, 0, 3, 5); + graph_add_edge(graph, 1, 0, 6); + graph_add_edge(graph, 1, 2, 3); + graph_add_edge(graph, 1, 3, 2); + graph_add_edge(graph, 2, 3, 1); + + graph_shortest_path(graph, 0); + + graph_delete(graph); +} +``` + +结果 +``` +Shortest path from 0 to 0: 0 (0) +Shortest path from 0 to 1: 3 (0 -> 1) +Shortest path from 0 to 2: 2 (0 -> 2) +Shortest path from 0 to 3: 3 (0 -> 2 -> 3) +``` + +### 连通图 + +```c +int graph_is_connected(graph_t graph); +``` + +检查图是否连通。 + +**参数**: +- `graph`: 指向图的指针。 + +**返回**: 如果图连通返回1,否则返回0。 + + +### 完全图 + +```c +int graph_is_complete(graph_t graph); +``` + +检查图是否为完全图。 + +**参数**: +- `graph`: 指向图的指针。 + +**返回**: 如果图为完全图返回1,否则返回0。 + +### 二部图 + +```c +int graph_is_bipartite(graph_t graph); +``` + +检查图是否为二部图。 + +**参数**: +- `graph`: 指向图的指针。 + +**返回**: 如果图为二部图返回1,否则返回0。 + +### 欧拉图 + +```c +int graph_is_eulerian(graph_t graph); +``` + +检查图是否为欧拉图(能恰好遍历每条边一次)。 + +**参数**: +- `graph`: 指向图的指针。 + +**返回**: 如果图为欧拉图返回1,否则返回0。 + +### 最小顶点覆盖 + +```c +void graph_min_vertex_cover(graph_t graph); +``` + +计算图的最小顶点覆盖。 + +**参数**: +- `graph`: 指向图的指针。 + +**返回**: 无 + +例子 +```c +static void test_min_cover(void) +{ + graph_t graph = NULL; + + graph = graph_create(100, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 100), sizeof(int)); + graph_add_vertex(graph, la(int, 200), sizeof(int)); + graph_add_vertex(graph, la(int, 300), sizeof(int)); + graph_add_vertex(graph, la(int, 500), sizeof(int)); + + graph_add_edge(graph, 0, 1, 3); + graph_add_edge(graph, 0, 2, 2); + graph_add_edge(graph, 0, 3, 5); + graph_add_edge(graph, 1, 0, 6); + graph_add_edge(graph, 1, 2, 3); + graph_add_edge(graph, 1, 3, 2); + graph_add_edge(graph, 2, 3, 1); + + graph_min_vertex_cover(graph); + + graph_delete(graph); +} +``` + +结果 +``` +0 3 1 2 +``` diff --git a/makefile b/makefile index 4b37af1..6de2e4d 100644 --- a/makefile +++ b/makefile @@ -20,6 +20,8 @@ PARSER_PATH = $(WORKSPACE)/05_parser ################################################################################## ### sources and head path ################################################################################## +include $(TESTSPACE)/test.mk + INCLUDE += -I $(APPLICATION_PATH) INCLUDE += -I $(GENDATA_PATH) INCLUDE += -I $(VSTD_PATH) @@ -35,66 +37,27 @@ SOURCES += $(wildcard $(VSTD_PATH)/*.c) SOURCES += $(wildcard $(CONTAINER_PATH)/*.c) SOURCES += $(wildcard $(ALGORITHM_PATH)/*.c) SOURCES += $(wildcard $(PARSER_PATH)/*.c) - -#---------------------------------------- -# test sources -#---------------------------------------- -# SOURCES += $(TESTSPACE)/test_init.c -# SOURCES += $(TESTSPACE)/test_kern.c -# SOURCES += $(TESTSPACE)/test_valloc.c -# SOURCES += $(TESTSPACE)/test_arg.c -# SOURCES += $(TESTSPACE)/test_vstd.c -# SOURCES += $(TESTSPACE)/test_vlog.c -# SOURCES += $(TESTSPACE)/test_ini.c -# SOURCES += $(TESTSPACE)/test_txls.c -# SOURCES += $(TESTSPACE)/test_xml.c -# SOURCES += $(TESTSPACE)/test_csv.c -# SOURCES += $(TESTSPACE)/test_json.c -# SOURCES += $(TESTSPACE)/test_vector.c -# SOURCES += $(TESTSPACE)/test_list.c -# SOURCES += $(TESTSPACE)/test_str.c -# SOURCES += $(TESTSPACE)/test_queue.c -# SOURCES += $(TESTSPACE)/test_stack.c -# SOURCES += $(TESTSPACE)/test_deque.c -# SOURCES += $(TESTSPACE)/test_set.c -# SOURCES += $(TESTSPACE)/test_dict.c -# SOURCES += $(TESTSPACE)/test_map.c -# SOURCES += $(TESTSPACE)/test_heap.c -# SOURCES += $(TESTSPACE)/test_tree.c -# SOURCES += $(TESTSPACE)/test_rbtree.c -# SOURCES += $(TESTSPACE)/test_pthread.c -# SOURCES += $(TESTSPACE)/test_encrypt.c -# SOURCES += $(TESTSPACE)/test_calculate.c -# SOURCES += $(TESTSPACE)/test_command.c -# SOURCES += $(TESTSPACE)/test_check.c -# SOURCES += $(TESTSPACE)/test_crc.c -# SOURCES += $(TESTSPACE)/test_sort.c -# SOURCES += $(TESTSPACE)/test_hash.c -# SOURCES += $(TESTSPACE)/test_pid.c -# SOURCES += $(TESTSPACE)/test_search.c -# SOURCES += $(TESTSPACE)/test_filter.c -# SOURCES += $(TESTSPACE)/test_oscp.c -# SOURCES += $(TESTSPACE)/test_tool.c -SOURCES += $(TESTSPACE)/test_sList.c -# SOURCES += $(TESTSPACE)/test_dList.c -# SOURCES += $(TESTSPACE)/test_cQueue.c +SOURCES += $(TEST_SRC) ################################################################################## ### targets and recipes ################################################################################## -OBJS = $(patsubst %.c, $(BUILT_DIR)/%.o, $(SOURCES)) -TAR_PATH = $(BUILT_DIR)/$(TARGET) +OBJ_PATH = $(BUILT_DIR)/obj +BIN_PATH = $(BUILT_DIR)/bin +OBJS = $(patsubst %.c, $(OBJ_PATH)/%.o, $(SOURCES)) +TAR_PATH = $(BIN_PATH)/$(TARGET) # link ${TAR_PATH}:$(OBJS) + $(shell mkdir -p $(dir $@)) # @ $(CC) $(OBJS) -o $(TAR_PATH) -lm -lX11 -lpthread - @ $(CC) $(OBJS) -o $(TAR_PATH) -lm -lpthread + @ $(CC) $(CFLAG) $(OBJS) -o $(TAR_PATH) -lm -lpthread # compile -$(BUILT_DIR)/%.o:%.c +$(OBJ_PATH)/%.o:%.c $(shell mkdir -p $(dir $@)) @ echo "compiling $(notdir $<)" - @ $(CC) $(INCLUDE) -c $< -o $@ + @ $(CC) $(CFLAG) $(INCLUDE) -c $< -o $@ .PHONY:clean clean: diff --git a/run.sh b/run.sh index 3618b4e..f7ab983 100644 --- a/run.sh +++ b/run.sh @@ -1,6 +1,6 @@ #!/bin/bash -exe_file="built/app" +exe_file="built/bin/app" echo "cleaning..." if [ -f $exe_file ]; then diff --git a/source/03_container/graph.c b/source/03_container/graph.c new file mode 100644 index 0000000..441b800 --- /dev/null +++ b/source/03_container/graph.c @@ -0,0 +1,1327 @@ +/********************************************************************************************************* + * ------------------------------------------------------------------------------------------------------ + * file description + * ------------------------------------------------------------------------------------------------------ + * \file graph.c + * \unit graph + * \brief This is a C language graph + * \author Lamdonn + * \version v1.0.0 + * \license GPL-2.0 + * \copyright Copyright (C) 2023 Lamdonn. + ********************************************************************************************************/ +#include "graph.h" +#include +#include +#include +#include + +/* vertex data flags */ +#define FLAG_VISITED 0x01 + +/* The macro definition method for traversal */ +#define for_each(graph, i) for (int i = 0, f = 0; i < (graph)->max && f < (graph)->cvertex; i++) if ((graph)->vertices[i] && ++f > 0) + +typedef struct EDGE { + struct EDGE* next; /**< The pointer to the next neighbor */ + int index; /**< The indices of the adjacent vertices */ + int weight; /**< Edge weight */ +} EDGE, *edge_t; + +typedef struct { + edge_t firstedge; /**< The list that stores vertex edges */ + int flag; /**< Vertex data flags */ + void *data; /**< Address of data */ + int size; /**< Size of data */ +} VERTEX, *vertex_t; + +typedef struct GRAPH { + vertex_t *vertices; + int max; /**< Maximum number of vertices can be stored */ + int cvertex; /**< The count of vertices in the graph */ + int cedge; /**< The count of edges in the graph */ + int directed; /**< Mark whether it is a directed graph */ +} GRAPH; + +static int alloc_vertex_index(graph_t graph) +{ + int i; + + for (i = 0; i < graph->max; i++) + { + if (!graph->vertices[i]) + { + return i; + } + } + + return -1; +} + +static int vertex_set(vertex_t vectex, void* data, int size) +{ + void* d = NULL; + + /* Input value validity check */ + if (!vectex) return 0; + if (size < 0) return 0; + + /* If the incoming data size is 0, set the air sensitive data directly */ + if (size == 0) + { + if (vectex->data) free(vectex->data); + vectex->data = NULL; + vectex->size = 0; + return 1; + } + + /* If the data size is inconsistent, update the data storage space */ + if (size != vectex->size) + { + d = realloc(vectex->data, size); + if (!d) return 0; + vectex->data = d; + } + + /* Data assignment */ + if (data) memcpy(vectex->data, data, size); + + /* Update data size */ + vectex->size = size; + + return 1; +} + +/** + * \brief Creates and initializes a vertex + * \param[in] data: pointer to the data to be associated with the vertex + * \param[in] size: size of the data to be copied + * \return A pointer to the newly created vertex, or NULL if the creation fails + */ +static vertex_t vertex_create(void *data, int size) +{ + vertex_t vertex; + + // Allocate memory for the vertex structure + vertex = (vertex_t)malloc(sizeof(VERTEX)); + if (!vertex) + { + return NULL; // Return NULL if memory allocation fails + } + + // Initialize the vertex memory to zero + memset(vertex, 0, sizeof(VERTEX)); + + // Set the data for the vertex, return NULL if it fails + if (!vertex_set(vertex, data, size)) + { + free(vertex); // Free the allocated memory if setting data fails + return NULL; + } + + return vertex; // Return the created vertex +} + +/** + * \brief Destroys a vertex and frees associated resources + * \param[in] vertex: pointer to the vertex to be destroyed + * \return none + */ +static void vertex_destroy(vertex_t vertex) +{ + // Check if the vertex has associated data and free it + if (vertex->data) + { + free(vertex->data); // Free the allocated memory for the data + vertex->data = NULL; // Set the pointer to NULL to avoid dangling reference + } + + // Free the vertex structure itself + free(vertex); +} + +/** + * \brief Creates and initializes an edge + * \param[in] index: index of the edge + * \param[in] weight: weight of the edge + * \return A pointer to the newly created edge, or NULL if the creation fails + */ +static edge_t edge_create(int index, int weight) +{ + edge_t edge; + + // Allocate memory for the edge structure + edge = (edge_t)malloc(sizeof(EDGE)); + if (!edge) + { + return NULL; // Return NULL if memory allocation fails + } + + // Initialize the edge properties + edge->index = index; // Set the index of the edge + edge->weight = weight; // Set the weight of the edge + edge->next = NULL; // Initialize the next pointer to NULL + + return edge; // Return the created edge +} + +/** + * \brief Destroys an edge and frees associated resources + * \param[in] edge: pointer to the edge to be destroyed + * \return none + */ +static void edge_destroy(edge_t edge) +{ + // Free the memory allocated for the edge structure + free(edge); +} + +graph_t graph_create(int max, int directed) +{ + graph_t graph; + + // Check if the maximum number of vertices is valid + if (max <= 0) + { + return NULL; // Return NULL if max is non-positive + } + + // Allocate memory for the graph structure + graph = (graph_t)malloc(sizeof(GRAPH)); + if (!graph) + { + return NULL; // Return NULL if memory allocation fails + } + + // Allocate memory for the vertex array + graph->vertices = (vertex_t *)malloc(sizeof(vertex_t) * max); + if (!graph->vertices) + { + free(graph); // Free the graph memory if vertex allocation fails + return NULL; + } + + // Initialize graph parameters + graph->max = max; // Set the maximum number of vertices + graph->cvertex = 0; // Initialize current vertex count + graph->cedge = 0; // Initialize current edge count + graph->directed = directed; // Set the directed flag + memset(graph->vertices, 0, sizeof(vertex_t) * max); // Initialize vertex pointers to NULL + + return graph; // Return the created graph +} + +void graph_delete(graph_t graph) +{ + edge_t edge, temp; + + // Check if the graph is NULL + if (!graph) return; + + // Iterate through each vertex in the graph + for_each(graph, i) + { + edge = graph->vertices[i]->firstedge; // Get the first edge of the vertex + + // Free all edges associated with the vertex + while (edge) + { + temp = edge; // Store the current edge + edge = edge->next; // Move to the next edge + edge_destroy(temp); // Free the current edge + } + + // Destroy the vertex and set its pointer to NULL + vertex_destroy(graph->vertices[i]); + graph->vertices[i] = NULL; + } + + // Free the vertex array if it was allocated + if (graph->vertices) free(graph->vertices); + + // Free the graph structure itself + free(graph); +} + +int graph_add_vertex(graph_t graph, void *data, int size) +{ + vertex_t vertex; + int index = -1; + + // Check if the graph is NULL + if (!graph) return -1; + + // Allocate an index for the new vertex + index = alloc_vertex_index(graph); + if (index < 0) return -1; // Return -1 if no available index + + // Create and initialize the new vertex + vertex = vertex_create(data, size); + if (!vertex) + { + return -1; // Return -1 if vertex creation fails + } + + // Add the vertex to the graph and update vertex count + graph->vertices[index] = vertex; + graph->cvertex++; + + return index; // Return the index of the newly added vertex +} + +/** + * \brief Adds an edge to the graph + * \param[in] graph: pointer to the graph where the edge will be added + * \param[in] start: index of the starting vertex + * \param[in] end: index of the ending vertex + * \param[in] weight: weight of the edge + * \return 1 if the edge was added successfully, 0 if the addition fails + */ +int graph_add_edge(graph_t graph, int start, int end, int weight) +{ + edge_t edge; + + // Check if the graph is NULL + if (!graph) return 0; + // Validate indices for the vertices + if (start < 0 || start >= graph->max || end < 0 || end >= graph->max) return 0; + // Ensure both vertices exist + if (!graph->vertices[start] || !graph->vertices[end]) return 0; + + // Create a new edge from start to end + edge = edge_create(end, weight); + if (!edge) + { + return 0; // Return 0 if edge creation fails + } + + /* Head insert: insert the edge at the beginning of the adjacency list */ + edge->next = graph->vertices[start]->firstedge; + graph->vertices[start]->firstedge = edge; + graph->cedge++; + + // If the graph is undirected, add a reverse edge + if (!graph->directed) + { + edge = edge_create(start, weight); + if (!edge) + { + // If reverse edge creation fails, remove the original edge + edge = graph->vertices[start]->firstedge; + graph->vertices[start]->firstedge = edge->next; // Remove the edge + edge_destroy(edge); // Free the edge + graph->cedge--; // Decrement edge count + return 0; // Return 0 on failure + } + + // Insert the reverse edge at the beginning of the end vertex's list + edge->next = graph->vertices[end]->firstedge; + graph->vertices[end]->firstedge = edge; + graph->cedge++; + } + + return 1; // Return 1 to indicate successful addition of the edge +} + +int graph_remove_vertex(graph_t graph, int index) +{ + edge_t prev, curr, temp; + + // Check if the graph is NULL + if (!graph) return 0; + // Validate the vertex index + if (index < 0 || index >= graph->max) return 0; + // Ensure the vertex exists + if (!graph->vertices[index]) return 0; + + // Delete edges that are incident to the vertex from neighboring vertices + for_each(graph, i) + { + prev = NULL; // Previous edge pointer + curr = graph->vertices[i]->firstedge; + + // Traverse the adjacency list of the neighboring vertex + while (curr) + { + if (curr->index == index) + { + // If the edge to be removed is the first edge + if (!prev) + { + graph->vertices[i]->firstedge = curr->next; // Remove from head + } + else + { + prev->next = curr->next; // Bypass the current edge + } + + temp = curr; // Save the edge to be deleted + curr = curr->next; // Move to the next edge + + edge_destroy(temp); // Free the edge + graph->cedge--; // Decrement edge count + } + else + { + prev = curr; // Move previous pointer forward + curr = curr->next; // Move to next edge + } + } + } + + // Delete all edges associated with the vertex being removed + curr = graph->vertices[index]->firstedge; + while (curr) + { + temp = curr; // Save current edge + curr = curr->next; // Move to next edge + edge_destroy(temp); // Free the edge + graph->cedge--; // Decrement edge count + } + + // Delete the vertex itself + vertex_destroy(graph->vertices[index]); + graph->vertices[index] = NULL; // Nullify the vertex pointer + + // Update the count of vertices + graph->cvertex--; + + return 1; // Return 1 to indicate successful removal +} + +int graph_remove_edge(graph_t graph, int start, int end) +{ + edge_t prev = NULL, curr; + + // Check if the graph is NULL + if (!graph) return 0; + // Validate vertex indices + if (start < 0 || start >= graph->max || end < 0 || end >= graph->max) return 0; + // Ensure both vertices exist + if (!graph->vertices[start] || !graph->vertices[end]) return 0; + + // Traverse the adjacency list of the starting vertex + curr = graph->vertices[start]->firstedge; + + while (curr) + { + // Check if the current edge points to the ending vertex + if (curr->index == end) + { + // Remove the edge from the adjacency list + if (!prev) + { + graph->vertices[start]->firstedge = curr->next; // Remove from head + } + else + { + prev->next = curr->next; // Bypass the current edge + } + + edge_destroy(curr); // Free the edge + graph->cedge--; // Decrement edge count + + break; // Exit loop after removing the edge + } + else + { + prev = curr; // Move previous pointer forward + curr = curr->next; // Move to next edge + } + } + + // If the graph is undirected, also remove the reverse edge + if (!graph->directed) + { + prev = NULL; + curr = graph->vertices[end]->firstedge; + + while (curr) + { + // Check if the current edge points to the starting vertex + if (curr->index == start) + { + // Remove the edge from the adjacency list + if (!prev) + { + graph->vertices[end]->firstedge = curr->next; // Remove from head + } + else + { + prev->next = curr->next; // Bypass the current edge + } + + edge_destroy(curr); // Free the edge + graph->cedge--; // Decrement edge count + + break; // Exit loop after removing the edge + } + else + { + prev = curr; // Move previous pointer forward + curr = curr->next; // Move to next edge + } + } + } + + return 1; // Return 1 to indicate successful removal +} + +void graph_ls(graph_t graph, graph_traverse_t func) +{ + vertex_t vertex; + + // Validate input + if (!graph) return; // Check if the graph is valid + if (!func) return; // Check if the function is valid + + // Iterate over all vertices in the graph + for_each(graph, i) + { + vertex = graph->vertices[i]; // Get the current vertex + // Apply the provided function to the vertex + func(i, vertex->data, vertex->size); + } +} + +/** + * \brief Depth-First Search (DFS) traversal of a graph + * \param[in] graph: pointer to the graph to be traversed + * \param[in] index: index of the current vertex being visited + * \param[in] func: callback function to be executed for each visited vertex + * \return none + */ +static void dfs(graph_t graph, int index, graph_traverse_t func) +{ + vertex_t vertex; + edge_t curr; + + // Check if the vertex at the given index exists + if (graph->vertices[index]) + { + vertex = graph->vertices[index]; + // Execute the callback function with the vertex data + if (func) func(index, vertex->data, vertex->size); + // Mark the vertex as visited + vertex->flag |= FLAG_VISITED; + curr = vertex->firstedge; + + // Traverse all adjacent edges + while (curr) + { + // If the adjacent vertex has not been visited, continue DFS on it + if (!(graph->vertices[curr->index]->flag & FLAG_VISITED)) + { + dfs(graph, curr->index, func); + } + + curr = curr->next; // Move to the next edge + } + } +} + +void graph_dfs(graph_t graph, int start, graph_traverse_t func) +{ + // Check if the graph exists + if (!graph) return; + // Validate the starting vertex index + if (start < 0 || start >= graph->max) return; + // Ensure the starting vertex exists + if (!graph->vertices[start]) return; + + // Reset the visited flag for all vertices + for_each(graph, i) + { + graph->vertices[i]->flag &= ~FLAG_VISITED; + } + + // Start the DFS from the specified starting vertex + dfs(graph, start, func); +} + +/** + * \brief Breadth-First Search (BFS) traversal of a graph + * \param[in] graph: pointer to the graph to be traversed + * \param[in] index: index of the current vertex being visited + * \param[in] func: callback function to be executed for each visited vertex + * \param[in] queue: array used for queueing vertices during traversal + * \return none + */ +static void bfs(graph_t graph, int index, graph_traverse_t func, int *queue) +{ + int front = 0, rear = 0, nindex; + vertex_t vertex, nvertex; + edge_t curr; + + // Get the starting vertex + vertex = graph->vertices[index]; + + // Execute the callback function for the starting vertex + func(index, vertex->data, vertex->size); + vertex->flag |= FLAG_VISITED; // Mark the starting vertex as visited + + // Enqueue the starting vertex + queue[rear++] = index; + + // Process the queue until it's empty + while (front != rear) + { + index = queue[front++]; // Dequeue the front vertex + curr = graph->vertices[index]->firstedge; // Get its adjacency list + + // Traverse all adjacent edges + while (curr) + { + nindex = curr->index; // Index of the adjacent vertex + + // If the adjacent vertex has not been visited + if (!(graph->vertices[nindex]->flag & FLAG_VISITED)) + { + nvertex = graph->vertices[nindex]; // Get the adjacent vertex + + // Execute the callback function for the adjacent vertex + func(nindex, nvertex->data, nvertex->size); + nvertex->flag |= FLAG_VISITED; // Mark it as visited + + // Enqueue the adjacent vertex + queue[rear++] = nindex; + } + + curr = curr->next; // Move to the next edge + } + } +} + +/** + * \brief Initializes BFS traversal with a queue of a given maximum size + * \param[in] graph: pointer to the graph to be traversed + * \param[in] start: index of the starting vertex + * \param[in] func: callback function to be executed for each visited vertex + * \param[in] max: maximum size of the queue + * \return none + */ +static void t_bfs(graph_t graph, int start, graph_traverse_t func, const int max) +{ + int queue[max]; // Create a queue array + + memset(queue, 0, sizeof(int) * max); // Initialize the queue + + bfs(graph, start, func, queue); // Start BFS traversal +} + +void graph_bfs(graph_t graph, int start, graph_traverse_t func) +{ + // Check if the graph exists + if (!graph) return; + // Validate the starting vertex index + if (start < 0 || start >= graph->max) return; + // Ensure the starting vertex exists + if (!graph->vertices[start]) return; + // Ensure the callback function is not NULL + if (!func) return; + + // Reset the visited flag for all vertices + for_each(graph, i) + { + graph->vertices[i]->flag &= ~FLAG_VISITED; + } + + // Start BFS from the specified starting vertex + t_bfs(graph, start, func, graph->max); +} + + +/** + * \brief Retrieves the number of vertices in the graph + * \param[in] graph: pointer to the graph + * \return The number of vertices in the graph, or 0 if the graph is NULL + */ +int graph_vertex_count(graph_t graph) +{ + // Check if the graph is NULL + if (!graph) return 0; + + return graph->cvertex; // Return the count of vertices +} + +/** + * \brief Retrieves the number of edges in the graph + * \param[in] graph: pointer to the graph + * \return The number of edges in the graph, or 0 if the graph is NULL + */ +int graph_edge_count(graph_t graph) +{ + // Check if the graph is NULL + if (!graph) return 0; + + return graph->cedge; // Return the count of edges +} + +int graph_vertex_set_data(graph_t graph, int index, void *data, int size) +{ + vertex_t vertex; + + // Check if the graph is NULL + if (!graph) return 0; + // Validate the vertex index + if (index < 0 || index >= graph->max) return 0; + // Ensure the vertex exists + if (!graph->vertices[index]) return 0; + // Validate the data size + if (size < 0) return 0; + + vertex = graph->vertices[index]; + + // Set the vertex data; return 0 if it fails + if (!vertex_set(vertex, data, size)) return 0; + + return 1; // Return 1 to indicate success +} + +int graph_vertex_get_data(graph_t graph, int index, void *data, int size) +{ + vertex_t vertex; + + // Check if the graph is NULL + if (!graph) return 0; + // Validate the vertex index + if (index < 0 || index >= graph->max) return 0; + // Ensure the vertex exists + if (!graph->vertices[index]) return 0; + // Ensure the data pointer is not NULL + if (!data) return 0; + + vertex = graph->vertices[index]; + + // Validate the size of the buffer + if (size < vertex->size) return 0; + + // Data assignment using memcpy + memcpy(data, vertex->data, vertex->size); + + return 1; // Return 1 to indicate success +} + +void *graph_vertex_data(graph_t graph, int index, int *size) +{ + vertex_t vertex; + + // Check if the graph is NULL + if (!graph) return NULL; + // Validate the vertex index + if (index < 0 || index >= graph->max) return NULL; + // Ensure the vertex exists + if (!graph->vertices[index]) return NULL; + + vertex = graph->vertices[index]; + + // If size pointer is provided, store the size of the vertex data + if (size) *size = vertex->size; + + return vertex->data; // Return the vertex data +} + +int graph_out_degree(graph_t graph, int index) +{ + edge_t edge; + vertex_t vertex; + int degree = 0; + + // Check if the graph is NULL + if (!graph) return -1; + // Validate the vertex index + if (index < 0 || index >= graph->max) return -1; + // Ensure the vertex exists + if (!graph->vertices[index]) return -1; + + vertex = graph->vertices[index]; + edge = vertex->firstedge; + + // Count the edges originating from the vertex + while (edge) + { + degree++; + edge = edge->next; // Move to the next edge + } + + return degree; // Return the out-degree +} + +int graph_in_degree(graph_t graph, int index) +{ + edge_t edge; + vertex_t vertex; + int degree = 0; + + // Check if the graph is NULL + if (!graph) return -1; + // Validate the vertex index + if (index < 0 || index >= graph->max) return -1; + // Ensure the vertex exists + if (!graph->vertices[index]) return -1; + + // Iterate over all vertices to count edges pointing to the specified vertex + for_each(graph, i) + { + vertex = graph->vertices[i]; + edge = vertex->firstedge; + + while (edge) + { + // Check if the edge points to the specified vertex + if (edge->index == index) + { + degree++; // Increment in-degree + } + + edge = edge->next; // Move to the next edge + } + } + + return degree; // Return the in-degree +} + +int graph_is_adjacent(graph_t graph, int start, int end) +{ + edge_t edge; + + // Check if the graph is NULL + if (!graph) return 0; + // Validate the vertex indices + if (start < 0 || start >= graph->max || end < 0 || end >= graph->max) return 0; + // Ensure both vertices exist + if (!graph->vertices[start] || !graph->vertices[end]) return 0; + + // Iterate through the edges of the starting vertex + edge = graph->vertices[start]->firstedge; + while (edge) + { + // Check if the current edge points to the ending vertex + if (edge->index == end) + { + return 1; // Vertices are adjacent + } + edge = edge->next; // Move to the next edge + } + + return 0; // Vertices are not adjacent +} + +int graph_get_edge_weight(graph_t graph, int start, int end) +{ + edge_t edge; + + // Check if the graph is NULL + if (!graph) return INT_MAX; + // Validate the vertex indices + if (start < 0 || start >= graph->max || end < 0 || end >= graph->max) return INT_MAX; + // Ensure both vertices exist + if (!graph->vertices[start] || !graph->vertices[end]) return INT_MAX; + + // Iterate through the edges of the starting vertex + edge = graph->vertices[start]->firstedge; + while (edge) + { + // Check if the current edge points to the ending vertex + if (edge->index == end) + { + return edge->weight; // Return the edge weight + } + + edge = edge->next; // Move to the next edge + } + + return INT_MAX; // Return INT_MAX if no edge exists between the vertices +} + +int graph_set_edge_weight(graph_t graph, int start, int end, int weight) +{ + edge_t edge; + + // Check if the graph is NULL + if (!graph) return 0; + // Validate the vertex indices + if (start < 0 || start >= graph->max || end < 0 || end >= graph->max) return 0; + // Ensure both vertices exist + if (!graph->vertices[start] || !graph->vertices[end]) return 0; + + // Iterate through the edges of the starting vertex + edge = graph->vertices[start]->firstedge; + while (edge) + { + // Check if the current edge points to the ending vertex + if (edge->index == end) + { + edge->weight = weight; // Set the new weight + return 1; // Return 1 to indicate success + } + + edge = edge->next; // Move to the next edge + } + + return 0; // Return 0 if no edge exists between the vertices +} + +int graph_contains_vertex(graph_t graph, int index) +{ + // Check if the graph is NULL + if (!graph) return 0; + // Validate the vertex index + if (index < 0 || index >= graph->max) return 0; + // Ensure the vertex exists + if (!graph->vertices[index]) return 0; + + return 1; // Return 1 if the vertex exists +} + +/** + * \brief Performs a topological sort on a directed acyclic graph (DAG) + * \param[in] graph: pointer to the graph + * \param[in] indegree: array to store the in-degrees of vertices + * \param[in] queue: array to use as a queue for vertices with zero in-degree + */ +static void topological_sort(graph_t graph, int *indegree, int *queue) +{ + int i, f = 0; + int front = 0, rear = 0; + int index; + edge_t curr; + + // Calculate the in-degree for each vertex + for_each(graph, i) + { + curr = graph->vertices[i]->firstedge; + + while (curr) + { + indegree[curr->index]++; + curr = curr->next; + } + } + + // Add vertices with zero in-degree to the queue + for_each(graph, i) + { + if (indegree[i] == 0) + { + queue[rear++] = i; + } + } + + // Process the queue + while (front < rear) + { + index = queue[front++]; + printf("%d ", index); // Print the vertex in sorted order + curr = graph->vertices[index]->firstedge; + + while (curr) + { + // Decrease the in-degree of the neighboring vertex + if (--indegree[curr->index] == 0) + { + queue[rear++] = curr->index; // Add to queue if in-degree becomes zero + } + curr = curr->next; + } + } + printf("\n"); +} + +/** + * \brief Initializes the queue and in-degree array, then calls the topological sort function + * \param[in] graph: pointer to the graph + * \param[in] max: maximum number of vertices + */ +static void t_topological_sort(graph_t graph, const int max) +{ + int queue[max]; // Queue for vertices with zero in-degree + int indegree[max]; // Array to hold in-degrees of vertices + + memset(queue, 0, sizeof(int) * max); // Initialize queue + memset(indegree, 0, sizeof(int) * max); // Initialize in-degree counts + + topological_sort(graph, indegree, queue); // Perform the topological sort +} + +void graph_topological_sort(graph_t graph) +{ + if (!graph) return; // Check if the graph is NULL + + t_topological_sort(graph, graph->max); // Call the helper function +} + +/** + * \brief Computes the shortest path from a starting vertex to all other vertices in a weighted graph + * \param[in] graph: pointer to the graph + * \param[in] start: index of the starting vertex + * \param[out] dist: array to store the shortest distances from the start vertex + * \param[out] prev: array to store the previous vertex in the shortest path + * \param[out] path: array to store the vertices in the shortest path for printing + */ +static void shortest_path(graph_t graph, int start, int *dist, int *prev, int *path) +{ + int j; + edge_t curr; + int min_dist, min_index; + int alt; + int path_count; + int prev_index; + + // Initialize distances and previous vertices + for_each(graph, i) + { + dist[i] = INT_MAX; // Set all distances to infinity + prev[i] = -1; // Set previous vertices to -1 + } + + dist[start] = 0; // Distance from start to itself is zero + + while (1) + { + min_dist = INT_MAX; + min_index = -1; + + // Find the unvisited vertex with the smallest distance + for_each(graph, i) + { + if (!(graph->vertices[i]->flag & FLAG_VISITED) && dist[i] < min_dist) + { + min_dist = dist[i]; + min_index = i; + } + } + + // If no unvisited vertex is found, exit the loop + if (min_index == -1) + { + break; + } + + // Mark the vertex as visited + graph->vertices[min_index]->flag |= FLAG_VISITED; + curr = graph->vertices[min_index]->firstedge; + + // Relaxation step + while (curr) + { + alt = dist[min_index] + graph_get_edge_weight(graph, min_index, curr->index); + + // Update distance and previous vertex if a shorter path is found + if (alt < dist[curr->index]) + { + dist[curr->index] = alt; + prev[curr->index] = min_index; + } + + curr = curr->next; + } + } + + // Print the shortest paths from the start vertex to all other vertices + for_each(graph, i) + { + printf("Shortest path from %d to %d: ", start, i); + if (dist[i] == INT_MAX) + { + printf("not reachable\n"); + } + else + { + printf("%d (", dist[i]); + + // Build the path from end to start + path_count = 0; + prev_index = i; + + while (prev_index != -1) + { + path[path_count++] = prev_index; + prev_index = prev[prev_index]; + } + + // Print the path in the correct order + for (j = path_count - 1; j >= 0; j--) + { + printf("%d", path[j]); + + if (j != 0) + { + printf(" -> "); + } + } + + printf(")\n"); + } + } +} + +/** + * \brief Initializes arrays for distances, previous vertices, and paths, then calls the shortest_path function + * \param[in] graph: pointer to the graph + * \param[in] start: index of the starting vertex + * \param[in] max: maximum number of vertices + */ +static void t_shortest_path(graph_t graph, int start, int max) +{ + int dist[max], prev[max], path[max]; + + memset(dist, 0, sizeof(int) * max); // Initialize distance array + memset(prev, 0, sizeof(int) * max); // Initialize previous array + memset(path, 0, sizeof(int) * max); // Initialize path array + + shortest_path(graph, start, dist, prev, path); // Call the shortest path function +} + +void graph_shortest_path(graph_t graph, int start) +{ + // Validate input + if (!graph) return; + if (start < 0 || start >= graph->max) return; + if (!graph->vertices[start]) return; + + t_shortest_path(graph, start, graph->max); // Call the helper function +} + +int graph_is_connected(graph_t graph) +{ + // Check if the graph is NULL + if (!graph) return 0; + + // Perform a depth-first search starting from the first vertex + graph_dfs(graph, 0, NULL); + + // Check if all vertices were visited + for_each(graph, i) + { + if (!(graph->vertices[i]->flag & FLAG_VISITED)) + { + return 0; // If any vertex is not visited, the graph is not connected + } + } + + return 1; // All vertices were visited, the graph is connected +} + +/** + * \brief Checks if the graph is a complete graph + * \param[in] graph: pointer to the graph + * \param[out] degree: array to hold the degree of each vertex + * \return 1 if the graph is complete, 0 if it is not + */ +static int is_complete(graph_t graph, int *degree) +{ + // For undirected graphs, each vertex's degree should be n-1 (where n is the number of vertices) + // For directed graphs, the sum of in-degree and out-degree of each vertex should be n-1 + for_each(graph, i) + { + if (graph->directed) + { + // Calculate degree for directed graph + degree[i] = graph_in_degree(graph, i) + graph_out_degree(graph, i); + } + else + { + // Calculate degree for undirected graph + degree[i] = graph_out_degree(graph, i); + } + } + + // Check if all vertices have the required degree for a complete graph + for_each(graph, i) + { + if (degree[i] != graph->cvertex - 1) + { + return 0; // Not a complete graph + } + } + + return 1; // It is a complete graph +} + +/** + * \brief Helper function to check if the graph is complete, initializes the degree array + * \param[in] graph: pointer to the graph + * \param[in] max: maximum number of vertices + * \return 1 if the graph is complete, 0 if it is not + */ +static int t_is_complete(graph_t graph, const int max) +{ + int degree[max]; // Array to hold degrees of vertices + + memset(degree, 0, sizeof(int) * max); // Initialize degree array + + return is_complete(graph, degree); // Call the complete check function +} + +int graph_is_complete(graph_t graph) +{ + // Validate input + if (!graph) return 0; + + return t_is_complete(graph, graph->max); // Call the helper function +} + +/** + * \brief Checks if the graph is bipartite using BFS + * \param[in] graph: pointer to the graph + * \param[out] color: array to hold colors of vertices (1 or 0) + * \param[out] queue: array to use as a queue for BFS + * \return 1 if the graph is bipartite, 0 if it is not + */ +static int is_bipartite(graph_t graph, int *color, int *queue) +{ + vertex_t vertex; + edge_t curr; + int index, next_index; + int front = 0, rear = 0; + + // Start coloring the first vertex + color[0] = 1; // Color the first vertex with color 1 + queue[rear++] = 0; // Enqueue the first vertex + + // BFS loop + while (front < rear) + { + index = queue[front++]; // Dequeue a vertex + vertex = graph->vertices[index]; + curr = vertex->firstedge; + + // Explore all adjacent vertices + while (curr) + { + next_index = curr->index; + + // If the adjacent vertex has not been colored + if (color[next_index] == -1) + { + color[next_index] = 1 - color[index]; // Assign opposite color + queue[rear++] = next_index; // Enqueue the adjacent vertex + } + // If the adjacent vertex has the same color, the graph is not bipartite + else if (color[next_index] == color[index]) + { + return 0; // Not bipartite + } + + curr = curr->next; // Move to the next edge + } + } + + return 1; // Graph is bipartite +} + +/** + * \brief Helper function to initialize color and queue arrays and check bipartiteness + * \param[in] graph: pointer to the graph + * \param[in] max: maximum number of vertices + * \return 1 if the graph is bipartite, 0 if it is not + */ +static int t_is_bipartite(graph_t graph, const int max) +{ + int color[max]; // Array to store colors of vertices + int queue[max]; // Queue for BFS + + memset(color, -1, sizeof(int) * max); // Initialize colors to -1 (uncolored) + memset(queue, 0, sizeof(int) * max); // Initialize queue + + return is_bipartite(graph, color, queue); // Call bipartite check function +} + +int graph_is_bipartite(graph_t graph) +{ + // Validate input + if (!graph) return 0; + + return t_is_bipartite(graph, graph->max); // Call the helper function +} + +int graph_is_eulerian(graph_t graph) +{ + // Validate input + if (!graph) return 0; + + // For undirected graphs, each vertex's degree must be even + // For directed graphs, in-degree must equal out-degree for each vertex + for_each(graph, i) + { + if ((graph->directed && graph_in_degree(graph, i) != graph_out_degree(graph, i)) || + (!graph->directed && graph_out_degree(graph, i) % 2 != 0)) + { + return 0; // Not Eulerian + } + } + + return 1; // The graph is Eulerian +} + +/** + * \brief Finds a minimum vertex cover for the graph + * \param[in] graph: pointer to the graph + * \param[out] flag: array to track whether a vertex is included in the cover + */ +static void min_vertex_cover(graph_t graph, int *flag) +{ + edge_t curr; + int v; + + // Iterate over all vertices + for_each(graph, u) + { + // If the vertex is not already included in the cover + if (!flag[u]) + { + // Include the vertex in the cover + flag[u] = 1; + curr = graph->vertices[u]->firstedge; + + // Traverse the edges of the vertex + while (curr) + { + v = curr->index; + // If the adjacent vertex is not included in the cover + if (!flag[v]) + { + // Include the adjacent vertex as well + flag[v] = 1; + // Print the edge in the vertex cover + printf("%d ", u); + printf("%d ", v); + break; // Stop after adding one edge + } + curr = curr->next; // Move to the next edge + } + } + } + printf("\n"); // Print a newline after all edges are processed +} + +/** + * \brief Helper function to initialize the flag array and call the min_vertex_cover function + * \param[in] graph: pointer to the graph + * \param[in] max: maximum number of vertices + */ +static int t_min_vertex_cover(graph_t graph, const int max) +{ + int flag[max]; // Array to indicate if a vertex is included in the cover + + memset(flag, 0, sizeof(int) * max); // Initialize all flags to 0 + + min_vertex_cover(graph, flag); // Call the function to find the vertex cover +} + +void graph_min_vertex_cover(graph_t graph) +{ + // Check if the graph is valid + if (!graph) return; + + t_min_vertex_cover(graph, graph->max); // Call the helper function +} + +// TODO: +// void graph_minimum_spanning_tree(graph_t graph); +// int graph_max_flow(graph_t graph, int source, int sink); +// void graph_articulation_points(graph_t graph); +// void graph_bridges(graph_t graph); +// void graph_minimum_spanning_tree(graph_t graph); +// void graph_topological_sort(graph_t graph); diff --git a/source/03_container/graph.h b/source/03_container/graph.h new file mode 100644 index 0000000..cf0e4f1 --- /dev/null +++ b/source/03_container/graph.h @@ -0,0 +1,238 @@ +/********************************************************************************************************* + * ------------------------------------------------------------------------------------------------------ + * file description + * ------------------------------------------------------------------------------------------------------ + * \file graph.h + * \unit graph + * \brief This is a C language graph + * \author Lamdonn + * \version v1.0.0 + * \license GPL-2.0 + * \copyright Copyright (C) 2023 Lamdonn. + ********************************************************************************************************/ +#ifndef __graph_H +#define __graph_H + +/* Version infomation */ +#define GRAPH_V_MAJOR 1 +#define GRAPH_V_MINOR 0 +#define GRAPH_V_PATCH 0 + +/* graph type definition, hiding structural members, not for external use */ + +typedef struct GRAPH *graph_t; + +/** + * \brief traverse callback function type + * \param[in] index: vertex index + * \param[in] data: address of data + * \param[in] size: size of data + * \return none + */ +typedef void (*graph_traverse_t)(int index, void *data, int size); + +/** + * \brief Creates and initializes a graph + * \param[in] max: maximum number of vertices in the graph + * \param[in] directed: flag indicating if the graph is directed (non-zero) or undirected (zero) + * \return A pointer to the newly created graph, or NULL if creation fails + */ +graph_t graph_create(int max, int directed); + +/** + * \brief Destroys a graph and frees associated resources + * \param[in] graph: pointer to the graph to be destroyed + * \return none + */ +void graph_delete(graph_t graph); + +/** + * \brief Adds a vertex to the graph + * \param[in] graph: pointer to the graph to which the vertex will be added + * \param[in] data: pointer to the data associated with the vertex + * \param[in] size: size of the data to be copied + * \return The index of the added vertex, or -1 if the addition fails + */ +int graph_add_vertex(graph_t graph, void *data, int size); + +/** + * \brief Adds an edge to the graph + * \param[in] graph: pointer to the graph where the edge will be added + * \param[in] start: index of the starting vertex + * \param[in] end: index of the ending vertex + * \param[in] weight: weight of the edge + * \return 1 if the edge was added successfully, 0 if the addition fails + */ +int graph_add_edge(graph_t graph, int start, int end, int weight); + +/** + * \brief Removes a specified vertex and its associated edges from the graph + * \param[in] graph: pointer to the graph from which the vertex will be removed + * \param[in] index: index of the vertex to be removed + * \return 1 if the vertex was removed successfully, 0 if the removal fails + */ +int graph_remove_vertex(graph_t graph, int index); + +/** + * \brief Removes a specified edge from the graph + * \param[in] graph: pointer to the graph from which the edge will be removed + * \param[in] start: index of the starting vertex of the edge + * \param[in] end: index of the ending vertex of the edge + * \return 1 if the edge was removed successfully, 0 if the removal fails + */ +int graph_remove_edge(graph_t graph, int start, int end); + +/** + * \brief Performs a linear search on the graph and applies the provided function to each vertex + * \param[in] graph: pointer to the graph + * \param[in] func: function to apply to each vertex (takes vertex index, data, and size) + */ +void graph_ls(graph_t graph, graph_traverse_t func); + +/** + * \brief Initiates a DFS traversal from a specified starting vertex + * \param[in] graph: pointer to the graph to be traversed + * \param[in] start: index of the starting vertex + * \param[in] func: callback function to be executed for each visited vertex + * \return none + */ +void graph_dfs(graph_t graph, int start, graph_traverse_t func); + +/** + * \brief Initiates a BFS traversal from a specified starting vertex + * \param[in] graph: pointer to the graph to be traversed + * \param[in] start: index of the starting vertex + * \param[in] func: callback function to be executed for each visited vertex + * \return none + */ +void graph_bfs(graph_t graph, int start, graph_traverse_t func); + +/** + * \brief Sets data for a specified vertex in the graph + * \param[in] graph: pointer to the graph + * \param[in] index: index of the vertex + * \param[in] data: pointer to the data to be set + * \param[in] size: size of the data + * \return 1 if the data was set successfully, 0 if it fails + */ +int graph_vertex_set_data(graph_t graph, int index, void *data, int size); + +/** + * \brief Retrieves data from a specified vertex in the graph + * \param[in] graph: pointer to the graph + * \param[in] index: index of the vertex + * \param[out] data: pointer to where the data will be copied + * \param[in] size: size of the buffer for data + * \return 1 if the data was retrieved successfully, 0 if it fails + */ +int graph_vertex_get_data(graph_t graph, int index, void *data, int size); + +/** + * \brief Retrieves the data of a specified vertex in the graph + * \param[in] graph: pointer to the graph + * \param[in] index: index of the vertex + * \param[out] size: pointer to store the size of the vertex data (if not NULL) + * \return Pointer to the vertex data, or NULL if it fails + */ +void *graph_vertex_data(graph_t graph, int index, int *size); + +/** + * \brief Retrieves the out-degree of a specified vertex in a directed graph + * \param[in] graph: pointer to the graph + * \param[in] index: index of the vertex + * \return The out-degree of the vertex, or -1 if it fails + */ +int graph_out_degree(graph_t graph, int index); + +/** + * \brief Retrieves the in-degree of a specified vertex in a directed graph + * \param[in] graph: pointer to the graph + * \param[in] index: index of the vertex + * \return The in-degree of the vertex, or -1 if it fails + */ +int graph_in_degree(graph_t graph, int index); + +/** + * \brief Checks if two vertices are adjacent in the graph + * \param[in] graph: pointer to the graph + * \param[in] start: index of the starting vertex + * \param[in] end: index of the ending vertex + * \return 1 if the vertices are adjacent, 0 if they are not or if it fails + */ +int graph_is_adjacent(graph_t graph, int start, int end); + +/** + * \brief Retrieves the weight of the edge between two vertices in a weighted graph + * \param[in] graph: pointer to the graph + * \param[in] start: index of the starting vertex + * \param[in] end: index of the ending vertex + * \return The weight of the edge if it exists, or INT_MAX if it fails or the edge does not exist + */ +int graph_get_edge_weight(graph_t graph, int start, int end); + +/** + * \brief Sets the weight of the edge between two vertices in a weighted graph + * \param[in] graph: pointer to the graph + * \param[in] start: index of the starting vertex + * \param[in] end: index of the ending vertex + * \param[in] weight: new weight for the edge + * \return 1 if the weight was set successfully, 0 if it fails + */ +int graph_set_edge_weight(graph_t graph, int start, int end, int weight); + +/** + * \brief Checks if a vertex exists in the graph + * \param[in] graph: pointer to the graph + * \param[in] index: index of the vertex + * \return 1 if the vertex exists, 0 if it does not or if it fails + */ +int graph_contains_vertex(graph_t graph, int index); + +/** + * \brief Public function to perform topological sort on the graph + * \param[in] graph: pointer to the graph + */ +void graph_topological_sort(graph_t graph); + +/** + * \brief Public function to find shortest paths from a starting vertex in the graph + * \param[in] graph: pointer to the graph + * \param[in] start: index of the starting vertex + */ +void graph_shortest_path(graph_t graph, int start); + +/** + * \brief Checks if the graph is connected + * \param[in] graph: pointer to the graph + * \return 1 if the graph is connected, 0 if it is not + */ +int graph_is_connected(graph_t graph); + +/** + * \brief Public function to determine if the graph is a complete graph + * \param[in] graph: pointer to the graph + * \return 1 if the graph is complete, 0 if it is not + */ +int graph_is_complete(graph_t graph); + +/** + * \brief Public function to determine if the graph is bipartite + * \param[in] graph: pointer to the graph + * \return 1 if the graph is bipartite, 0 if it is not + */ +int graph_is_bipartite(graph_t graph); + +/** + * \brief Checks if the graph is Eulerian (can traverse each edge exactly once) + * \param[in] graph: pointer to the graph + * \return 1 if the graph is Eulerian, 0 if it is not + */ +int graph_is_eulerian(graph_t graph); + +/** + * \brief Public function to compute the minimum vertex cover for the graph + * \param[in] graph: pointer to the graph + */ +void graph_min_vertex_cover(graph_t graph); + +#endif diff --git a/test/test.mk b/test/test.mk new file mode 100644 index 0000000..1e3600a --- /dev/null +++ b/test/test.mk @@ -0,0 +1,49 @@ + +TEST_SRC = +TEST_INC = + +#---------------------------------------- +# test sources +#---------------------------------------- +# TEST_SRC += $(TESTSPACE)/test_init.c +# TEST_SRC += $(TESTSPACE)/test_kern.c +# TEST_SRC += $(TESTSPACE)/test_valloc.c +# TEST_SRC += $(TESTSPACE)/test_arg.c +# TEST_SRC += $(TESTSPACE)/test_vstd.c +# TEST_SRC += $(TESTSPACE)/test_vlog.c +# TEST_SRC += $(TESTSPACE)/test_ini.c +# TEST_SRC += $(TESTSPACE)/test_txls.c +# TEST_SRC += $(TESTSPACE)/test_xml.c +# TEST_SRC += $(TESTSPACE)/test_csv.c +# TEST_SRC += $(TESTSPACE)/test_json.c +# TEST_SRC += $(TESTSPACE)/test_vector.c +# TEST_SRC += $(TESTSPACE)/test_list.c +# TEST_SRC += $(TESTSPACE)/test_str.c +# TEST_SRC += $(TESTSPACE)/test_queue.c +# TEST_SRC += $(TESTSPACE)/test_stack.c +# TEST_SRC += $(TESTSPACE)/test_deque.c +# TEST_SRC += $(TESTSPACE)/test_set.c +# TEST_SRC += $(TESTSPACE)/test_dict.c +# TEST_SRC += $(TESTSPACE)/test_map.c +# TEST_SRC += $(TESTSPACE)/test_heap.c +# TEST_SRC += $(TESTSPACE)/test_tree.c +TEST_SRC += $(TESTSPACE)/test_graph.c +# TEST_SRC += $(TESTSPACE)/test_rbtree.c +# TEST_SRC += $(TESTSPACE)/test_pthread.c +# TEST_SRC += $(TESTSPACE)/test_encrypt.c +# TEST_SRC += $(TESTSPACE)/test_calculate.c +# TEST_SRC += $(TESTSPACE)/test_command.c +# TEST_SRC += $(TESTSPACE)/test_check.c +# TEST_SRC += $(TESTSPACE)/test_crc.c +# TEST_SRC += $(TESTSPACE)/test_sort.c +# TEST_SRC += $(TESTSPACE)/test_hash.c +# TEST_SRC += $(TESTSPACE)/test_pid.c +# TEST_SRC += $(TESTSPACE)/test_search.c +# TEST_SRC += $(TESTSPACE)/test_filter.c +# TEST_SRC += $(TESTSPACE)/test_oscp.c +# TEST_SRC += $(TESTSPACE)/test_tool.c +# TEST_SRC += $(TESTSPACE)/test_sList.c +# TEST_SRC += $(TESTSPACE)/test_dList.c +# TEST_SRC += $(TESTSPACE)/test_cQueue.c + +export TEST_SRC TEST_INC diff --git a/test/test_graph.c b/test/test_graph.c new file mode 100644 index 0000000..ce68b24 --- /dev/null +++ b/test/test_graph.c @@ -0,0 +1,384 @@ +#include +#include +#include +#include "init.h" +#include "tool.h" +#include "valloc.h" +#include "graph.h" +#include "arg.h" + +#define la(type, value) ((type[1]){value}) + +void graph_traverse_int(int index, void *data, int size) +{ + printf("graph[%d] %d\r\n", index, *(int *)data); +} + +static void test_create(void) +{ + graph_t graph = NULL; + int i = 25; + int index = -1; + + graph = graph_create(100, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + + index = graph_add_vertex(graph, la(int, 12), sizeof(int)); + printf("index %d\r\n", index); + + index = graph_add_vertex(graph, la(int, 13), sizeof(int)); + printf("index %d\r\n", index); + + index = graph_add_vertex(graph, la(int, 15), sizeof(int)); + printf("index %d\r\n", index); + + index = graph_add_vertex(graph, la(int, 23), sizeof(int)); + printf("index %d\r\n", index); + + printf("graph_add_edge %d\r\n", graph_add_edge(graph, 0, 1, 0)); + printf("graph_add_edge %d\r\n", graph_add_edge(graph, 0, 2, 0)); + printf("graph_add_edge %d\r\n", graph_add_edge(graph, 2, 3, 0)); + + // graph_remove_edge(graph, 0, 2); + // graph_remove_vertex(graph, 0); + + printf("ret %d\r\n", graph_vertex_set_data(graph, 1, la(int, 1024), sizeof(int))); + + printf("graph_vertex_data %d\r\n", *(int *)graph_vertex_data(graph, 1, NULL)); + + graph_dfs(graph, 0, graph_traverse_int); + printf("----------------------\r\n"); + graph_bfs(graph, 0, graph_traverse_int); + + graph_delete(graph); +} + +static void test_add_vertex(void) +{ + graph_t graph = NULL; + + graph = graph_create(10, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 12), sizeof(int)); + graph_add_vertex(graph, la(int, 13), sizeof(int)); + graph_add_vertex(graph, la(int, 15), sizeof(int)); + graph_add_vertex(graph, la(int, 23), sizeof(int)); + + graph_ls(graph, graph_traverse_int); + + graph_delete(graph); +} + +static void test_add_edge(void) +{ + graph_t graph = NULL; + + graph = graph_create(10, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 12), sizeof(int)); + graph_add_vertex(graph, la(int, 13), sizeof(int)); + graph_add_vertex(graph, la(int, 15), sizeof(int)); + graph_add_vertex(graph, la(int, 23), sizeof(int)); + + printf("---------------\r\n"); + + graph_dfs(graph, 0, graph_traverse_int); + + printf("---------------\r\n"); + + graph_add_edge(graph, 0, 1, 0); + graph_add_edge(graph, 0, 2, 0); + graph_add_edge(graph, 0, 3, 0); + graph_add_edge(graph, 1, 0, 0); + graph_add_edge(graph, 1, 2, 0); + graph_add_edge(graph, 1, 3, 0); + graph_add_edge(graph, 2, 3, 0); + + graph_dfs(graph, 0, graph_traverse_int); + + printf("---------------\r\n"); + + graph_delete(graph); +} + +static void test_degree(void) +{ + graph_t graph = NULL; + + graph = graph_create(10, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 100), sizeof(int)); + graph_add_vertex(graph, la(int, 200), sizeof(int)); + graph_add_vertex(graph, la(int, 300), sizeof(int)); + graph_add_vertex(graph, la(int, 500), sizeof(int)); + + graph_add_edge(graph, 0, 1, 0); + graph_add_edge(graph, 0, 2, 0); + graph_add_edge(graph, 0, 3, 0); + graph_add_edge(graph, 1, 0, 0); + graph_add_edge(graph, 1, 2, 0); + graph_add_edge(graph, 1, 3, 0); + graph_add_edge(graph, 2, 3, 0); + + printf("graph_out_degree %d\r\n", graph_out_degree(graph, 3)); + printf("graph_out_degree %d\r\n", graph_out_degree(graph, 2)); + + printf("graph_in_degree %d\r\n", graph_in_degree(graph, 0)); + printf("graph_in_degree %d\r\n", graph_in_degree(graph, 2)); + + graph_delete(graph); +} + +static void test_remove(void) +{ + graph_t graph = NULL; + + graph = graph_create(100, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 100), sizeof(int)); + graph_add_vertex(graph, la(int, 200), sizeof(int)); + graph_add_vertex(graph, la(int, 300), sizeof(int)); + graph_add_vertex(graph, la(int, 500), sizeof(int)); + + graph_add_edge(graph, 0, 1, 0); + graph_add_edge(graph, 0, 2, 0); + graph_add_edge(graph, 0, 3, 0); + graph_add_edge(graph, 1, 0, 0); + graph_add_edge(graph, 1, 2, 0); + graph_add_edge(graph, 1, 3, 0); + graph_add_edge(graph, 2, 3, 0); + + graph_remove_vertex(graph, 1); + graph_remove_edge(graph, 0, 2); + + graph_dfs(graph, 0, graph_traverse_int); + + graph_delete(graph); +} + +static void test_search(void) +{ + graph_t graph = NULL; + + graph = graph_create(100, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 100), sizeof(int)); + graph_add_vertex(graph, la(int, 200), sizeof(int)); + graph_add_vertex(graph, la(int, 300), sizeof(int)); + graph_add_vertex(graph, la(int, 500), sizeof(int)); + + graph_add_edge(graph, 0, 1, 0); + graph_add_edge(graph, 0, 2, 0); + graph_add_edge(graph, 0, 3, 0); + graph_add_edge(graph, 1, 0, 0); + graph_add_edge(graph, 1, 2, 0); + graph_add_edge(graph, 1, 3, 0); + graph_add_edge(graph, 2, 3, 0); + + graph_remove_vertex(graph, 1); + graph_remove_edge(graph, 0, 2); + + printf("graph_ls ----------------------\r\n"); + graph_ls(graph, graph_traverse_int); + printf("graph_dfs ----------------------\r\n"); + graph_dfs(graph, 0, graph_traverse_int); + printf("graph_bfs ----------------------\r\n"); + graph_bfs(graph, 0, graph_traverse_int); + + graph_delete(graph); +} + +static void test_data(void) +{ + graph_t graph = NULL; + + graph = graph_create(100, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 100), sizeof(int)); + graph_add_vertex(graph, la(int, 200), sizeof(int)); + graph_add_vertex(graph, la(int, 300), sizeof(int)); + graph_add_vertex(graph, la(int, 500), sizeof(int)); + + graph_vertex_set_data(graph, 2, la(int, 1024), sizeof(int)); + + int size = 0; + printf("graph_vertex_data[3].data = %d\r\n", *(int *)graph_vertex_data(graph, 3, &size)); + printf("graph_vertex_data[3].size = %d\r\n", size); + + graph_ls(graph, graph_traverse_int); + + graph_delete(graph); +} + +static void test_weight(void) +{ + graph_t graph = NULL; + + graph = graph_create(100, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 100), sizeof(int)); + graph_add_vertex(graph, la(int, 200), sizeof(int)); + graph_add_vertex(graph, la(int, 300), sizeof(int)); + graph_add_vertex(graph, la(int, 500), sizeof(int)); + + graph_add_edge(graph, 0, 1, 0); + graph_add_edge(graph, 0, 2, 0); + graph_add_edge(graph, 0, 3, 10); + graph_add_edge(graph, 1, 0, 0); + graph_add_edge(graph, 1, 2, 0); + graph_add_edge(graph, 1, 3, 0); + graph_add_edge(graph, 2, 3, 0); + + printf("graph_get_edge_weight = %d\r\n", graph_get_edge_weight(graph, 0, 3)); + graph_set_edge_weight(graph, 0, 3, 1024); + printf("graph_get_edge_weight = %d\r\n", graph_get_edge_weight(graph, 0, 3)); + + graph_delete(graph); +} + +static void test_shortest_path(void) +{ + graph_t graph = NULL; + + graph = graph_create(100, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 100), sizeof(int)); + graph_add_vertex(graph, la(int, 200), sizeof(int)); + graph_add_vertex(graph, la(int, 300), sizeof(int)); + graph_add_vertex(graph, la(int, 500), sizeof(int)); + + graph_add_edge(graph, 0, 1, 3); + graph_add_edge(graph, 0, 2, 2); + graph_add_edge(graph, 0, 3, 5); + graph_add_edge(graph, 1, 0, 6); + graph_add_edge(graph, 1, 2, 3); + graph_add_edge(graph, 1, 3, 2); + graph_add_edge(graph, 2, 3, 1); + + graph_shortest_path(graph, 0); + + graph_delete(graph); +} + +static void test_min_cover(void) +{ + graph_t graph = NULL; + + graph = graph_create(100, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 100), sizeof(int)); + graph_add_vertex(graph, la(int, 200), sizeof(int)); + graph_add_vertex(graph, la(int, 300), sizeof(int)); + graph_add_vertex(graph, la(int, 500), sizeof(int)); + + graph_add_edge(graph, 0, 1, 3); + graph_add_edge(graph, 0, 2, 2); + graph_add_edge(graph, 0, 3, 5); + graph_add_edge(graph, 1, 0, 6); + graph_add_edge(graph, 1, 2, 3); + graph_add_edge(graph, 1, 3, 2); + graph_add_edge(graph, 2, 3, 1); + + graph_min_vertex_cover(graph); + + graph_delete(graph); +} + +static void test_others(void) +{ + graph_t graph = NULL; + + graph = graph_create(100, 1); + if (!graph) + { + printf("graph_create fail!\r\n"); + } + + graph_add_vertex(graph, la(int, 100), sizeof(int)); + graph_add_vertex(graph, la(int, 200), sizeof(int)); + graph_add_vertex(graph, la(int, 300), sizeof(int)); + graph_add_vertex(graph, la(int, 500), sizeof(int)); + + graph_add_edge(graph, 0, 1, 3); + graph_add_edge(graph, 0, 2, 2); + graph_add_edge(graph, 0, 3, 5); + graph_add_edge(graph, 1, 0, 6); + graph_add_edge(graph, 1, 2, 3); + graph_add_edge(graph, 1, 3, 2); + graph_add_edge(graph, 2, 3, 1); + + // graph_topological_sort(graph); + // graph_shortest_path(graph, 0); + // graph_minimum_spanning_tree(graph); + // printf("graph_is_connected %d\r\n", graph_is_connected(graph)); + // printf("graph_is_complete %d\r\n", graph_is_complete(graph)); + // printf("graph_is_bipartite %d\r\n", graph_is_bipartite(graph)); + // printf("graph_is_eulerian %d\r\n", graph_is_eulerian(graph)); + // printf("graph_is_eulerian %d\r\n", graph_max_flow(graph, 0, 3)); + // graph_articulation_points(graph); + // graph_bridges(graph); + graph_min_vertex_cover(graph); + + graph_dfs(graph, 0, graph_traverse_int); + + graph_delete(graph); +} + +static void test(void) +{ + printf("graph test!\r\n"); + + // test_create(); + // test_add_vertex(); + // test_add_edge(); + // test_remove(); + // test_search(); + // test_data(); + // test_degree(); + // test_weight(); + // test_shortest_path(); + test_min_cover(); + + v_check_unfree(); +} +init_export_app(test);