news 2026/4/3 3:04:04

无向图邻接矩阵实现与遍历实战:从理论到C语言代码解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
无向图邻接矩阵实现与遍历实战:从理论到C语言代码解析

1. 无向图与邻接矩阵基础

第一次接触图结构时,我盯着那些密密麻麻的线条和数字看了半天。后来才发现,无向图其实就是用圆圈(顶点)和连线(边)组成的结构,就像地铁线路图那样简单。比如北京地铁4号线和10号线在海淀黄庄站相交,这两个站点就是顶点,线路就是边。

邻接矩阵则是把这种关系转换成二维表格的存储方式。假设我们有4个地铁站(A、B、C、D),用矩阵表示就是:

A B C D A [0 1 1 0] B [1 0 1 1] C [1 1 0 0] D [0 1 0 0]

这里的1表示两站连通(比如A-B有连线),0表示不连通。因为是无向图,矩阵总是对称的——A到B和B到A是同一件事。

实际项目中我遇到过内存浪费的问题。当有1000个站点但只有2000条线路时,矩阵里98%的空间都是0。这时候用邻接表更划算,但邻接矩阵在查找两个站点是否直达时更快,直接查表就行。

2. C语言实现邻接矩阵

先看核心数据结构设计。我习惯用结构体封装,这样代码更清晰:

#define MAX_SIZE 100 // 最大顶点数 typedef struct { int nodes[MAX_SIZE]; // 顶点集合 int edges[MAX_SIZE][MAX_SIZE]; // 邻接矩阵 int nodeCount; // 顶点数 int edgeCount; // 边数 } Graph;

初始化矩阵时有个坑要注意:必须显式把所有边初始化为0。有次我忘记初始化,遍历时出现了随机值导致程序崩溃:

void initGraph(Graph *g, int n) { g->nodeCount = n; g->edgeCount = 0; memset(g->edges, 0, sizeof(g->edges)); // 关键步骤! }

添加边的函数需要处理无向图的对称性。比如添加边A-B:

void addEdge(Graph *g, int v1, int v2) { if (v1 == v2) return; // 防止自环 g->edges[v1][v2] = 1; g->edges[v2][v1] = 1; // 无向图要设置对称位置 g->edgeCount++; }

测试时可以打印矩阵验证:

void printMatrix(Graph *g) { for (int i = 0; i < g->nodeCount; i++) { for (int j = 0; j < g->nodeCount; j++) { printf("%d ", g->edges[i][j]); } printf("\n"); } }

3. 深度优先遍历(DFS)实战

DFS就像走迷宫时右手扶墙策略。从起点出发,沿着一条路走到尽头,然后回退到最近的分叉点。用递归实现最直观:

int visited[MAX_SIZE]; // 访问标记数组 void dfs(Graph *g, int v) { visited[v] = 1; printf("%d ", g->nodes[v]); // 访问当前顶点 for (int i = 0; i < g->nodeCount; i++) { if (g->edges[v][i] && !visited[i]) { dfs(g, i); // 递归访问邻接点 } } }

非递归版本需要用栈模拟。我更喜欢用数组实现栈,比链表更省内存:

void dfsNonRecursive(Graph *g, int start) { int stack[MAX_SIZE], top = -1; memset(visited, 0, sizeof(visited)); stack[++top] = start; visited[start] = 1; while (top != -1) { int v = stack[top--]; printf("%d ", g->nodes[v]); for (int i = g->nodeCount-1; i >= 0; i--) { if (g->edges[v][i] && !visited[i]) { visited[i] = 1; stack[++top] = i; } } } }

注意邻接点要逆序入栈,这样才能保持与递归相同的访问顺序。

4. 广度优先遍历(BFS)精解

BFS就像水面波纹扩散,一层层向外探索。必须用队列实现,C语言中可以用数组模拟循环队列:

#define QUEUE_SIZE 100 typedef struct { int data[QUEUE_SIZE]; int front, rear; } Queue; void bfs(Graph *g, int start) { Queue q = { .front = 0, .rear = 0 }; memset(visited, 0, sizeof(visited)); q.data[q.rear++] = start; visited[start] = 1; while (q.front != q.rear) { int v = q.data[q.front++]; printf("%d ", g->nodes[v]); for (int i = 0; i < g->nodeCount; i++) { if (g->edges[v][i] && !visited[i]) { visited[i] = 1; q.data[q.rear++] = i; } } } }

在社交网络分析中,BFS可以用来查找三度人脉。我曾用这个算法实现了"可能认识的人"推荐功能。

5. 完整应用案例

结合地铁换乘查询的实例。先构建图:

Graph g; initGraph(&g, 6); // 6个站点 addEdge(&g, 0, 1); // A-B addEdge(&g, 0, 2); // A-C addEdge(&g, 1, 3); // B-D addEdge(&g, 2, 3); // C-D addEdge(&g, 3, 4); // D-E addEdge(&g, 4, 5); // E-F

DFS和BFS的遍历结果会不同:

DFS: A B D C E F BFS: A B C D E F

实际调试时遇到过队列溢出问题。当站点超过100时,需要动态调整队列大小或改用链表队列。

6. 性能优化技巧

空间优化:对于稀疏图,可以用位压缩。比如用1个int表示32个边状态,空间减少32倍。

时间优化:并行化BFS。在多层遍历时,可以同时处理当前层的所有邻接点。用OpenMP实现:

#pragma omp parallel for for (int i = 0; i < g->nodeCount; i++) { if (g->edges[v][i] && !visited[i]) { #pragma omp critical { visited[i] = 1; q.data[q.rear++] = i; } } }

算法选择:在路径规划中,DFS适合找所有可能路线,BFS适合找最短路径。我曾用DFS生成迷宫所有解法,用BFS实现最短逃生路径计算。

最后提醒:遍历时要记得重置visited数组。有次我在连续调用时没清空,导致第二次遍历直接跳过所有节点,排查了半天才发现这个问题。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/15 10:31:55

Qwen3-32B GPU显存优化:Clawdbot网关层PagedAttention内存复用配置

Qwen3-32B GPU显存优化&#xff1a;Clawdbot网关层PagedAttention内存复用配置 1. 为什么需要在Clawdbot网关层做显存优化 你可能已经试过直接用Ollama跑Qwen3-32B&#xff0c;也搭好了Clawdbot前端界面&#xff0c;但一开多轮对话、并发稍高&#xff0c;GPU显存就爆了——明…

作者头像 李华
网站建设 2026/3/22 15:15:59

深入解析SGP30传感器:I2C通信协议与低功耗设计的奥秘

深入解析SGP30传感器&#xff1a;I2C通信协议与低功耗设计的奥秘 1. SGP30传感器核心架构解析 SGP30作为一款金属氧化物气体传感器&#xff0c;其核心价值在于单芯片集成多传感元件的创新设计。不同于传统分立式气体传感器&#xff0c;SGP30通过四个独立的传感元件协同工作&a…

作者头像 李华
网站建设 2026/3/31 4:14:11

手把手教你用OFA VQA模型:图片问答实战教程

手把手教你用OFA VQA模型&#xff1a;图片问答实战教程 1. 为什么你需要一个“会看图说话”的AI&#xff1f; 你有没有过这样的时刻&#xff1a; 看到一张陌生的医学影像&#xff0c;想快速知道它显示的是什么结构&#xff1f;给孩子辅导作业时&#xff0c;面对一张复杂的物…

作者头像 李华
网站建设 2026/4/2 8:46:38

GPEN助力公安侦查:监控画面嫌疑人面部增强实战

GPEN助力公安侦查&#xff1a;监控画面嫌疑人面部增强实战 1. 为什么监控里的人脸总是“看不清”&#xff1f; 在真实案件侦办中&#xff0c;你是否也遇到过这样的场景&#xff1a; 监控录像里那个关键嫌疑人&#xff0c;只留下一个模糊的侧脸、一段晃动的背影&#xff0c;或…

作者头像 李华
网站建设 2026/3/26 23:41:29

无需训练数据!IndexTTS 2.0实现即插即用音色克隆

无需训练数据&#xff01;IndexTTS 2.0实现即插即用音色克隆 你有没有过这样的经历&#xff1a;剪好一段30秒的短视频&#xff0c;反复试了七八种AI配音&#xff0c;不是语速太快赶不上画面动作&#xff0c;就是情绪太平像机器人念稿&#xff0c;再不然就是“欢迎来到”三个字…

作者头像 李华