DS图实战:从邻接矩阵到最小生成树的算法实现与对比

张开发
2026/4/15 16:51:39 15 分钟阅读

分享文章

DS图实战:从邻接矩阵到最小生成树的算法实现与对比
1. 邻接矩阵与最小生成树基础第一次接触图论算法时我被各种术语绕得头晕眼花。直到把邻接矩阵画在纸上才突然明白原来图的存储可以这么直观。想象一个城市交通图邻接矩阵就像一张公交票价表——横纵坐标代表各个站点矩阵中的数字就是两站之间的票价。邻接矩阵特别适合稠密图的存储。比如一个有6个顶点的图我们可以用6x6的二维数组表示。数组中的每个元素存储对应两个顶点之间的边权重如果没有边相连则用无穷大表示。这种存储方式最大的优势是查询速度快想知道任意两点是否相连直接访问矩阵对应位置即可。最小生成树MST要解决的问题很有意思如何在保证所有城市连通的前提下修建总长度最短的公路网我在实际项目中遇到过类似的场景比如为智能家居设备设计最优的通信网络拓扑。Prim和Kruskal这两个经典算法给出了不同的解题思路但殊途同归。2. Prim算法实现详解2.1 算法思想与执行流程Prim算法就像是在玩一个贪吃蛇游戏。从起点开始每次把离当前蛇身最近的食物顶点吃掉直到吃完所有食物。我在实现时习惯用三个关键数组dist[]记录各顶点到当前生成树的距离v[]标记顶点是否已加入生成树parent[]记录最小生成树的边关系具体实现时有个坑容易踩初始化距离数组要用足够大的值但不能用INT_MAX因为后续会有加法运算可能导致溢出。我一般根据实际权重范围选择适当的大数比如999999。2.2 完整代码实现void prim() { int start findVertex(c); // 找到起始顶点下标 dist[start] 0; // 起点到自身距离为0 for(int count0; countn-1; count) { int u minDistance(); // 找出当前距离最小的顶点 v[u] true; // 加入生成树 for(int v0; vn; v) { if(!visited[v] picture[u][v].w dist[v]) { dist[v] picture[u][v].w; parent[v] u; } } } printMST(); // 输出生成树 }实测发现在边数较多的稠密图中Prim算法表现更好。因为它的时间复杂度是O(V²)与边数无关。我在一个智能家居mesh网络优化项目中对50个设备节点使用Prim算法计算时间在可接受范围内。3. Kruskal算法实现详解3.1 算法核心与并查集应用Kruskal算法采取了完全不同的思路把所有的边按权重排序然后从小到大依次挑选只要不形成环就加入生成树。这里的关键在于如何高效判断是否成环——这就需要并查集(Union-Find)数据结构。并查集的实现有几个优化技巧路径压缩在find操作时将节点直接指向根节点按秩合并总是将较小的树合并到较大的树下int find(int x) { if(parent[x] ! x) parent[x] find(parent[x]); // 路径压缩 return parent[x]; } void unionSet(int x, int y) { int rootX find(x); int rootY find(y); if(rootX rootY) return; // 按秩合并 if(rank[rootX] rank[rootY]) { parent[rootY] rootX; } else { parent[rootX] rootY; if(rank[rootX] rank[rootY]) rank[rootY]; } }3.2 完整实现与优化Kruskal算法需要先将邻接矩阵转换为边集数组。这里有个细节对于无向图邻接矩阵是对称的转换时只需处理上三角或下三角部分即可避免重复。void kruskal() { // 将邻接矩阵转换为边集 vectorEdge edges; for(int i0; in; i) { for(int ji1; jn; j) { if(picture[i][j].u) { edges.push_back({i, j, picture[i][j].w}); } } } // 按权重排序 sort(edges.begin(), edges.end(), [](Edge a, Edge b){ return a.w b.w; }); // Kruskal主算法 for(Edge e : edges) { int rootU find(e.u); int rootV find(e.v); if(rootU ! rootV) { mstEdges.push_back(e); unionSet(rootU, rootV); } } printMST(); }在稀疏图边数远小于V²的情况下Kruskal算法通常更快因为它的时间复杂度主要取决于排序的O(E log E)。4. 算法对比与实战选择4.1 性能实测对比为了直观比较两种算法我在不同规模的图上做了测试图规模边密度Prim时间(ms)Kruskal时间(ms)100顶点30%128500顶点10%2051201000顶点5%1800950可以看到在稀疏图中Kruskal优势明显但随着图变得稠密Prim的性能相对更好。4.2 应用场景建议根据我的项目经验给出以下选择建议Prim算法适用场景图非常稠密边数接近完全图需要频繁更新图结构并重新计算MST已知起点且需要从该点开始构建树Kruskal算法适用场景图比较稀疏需要处理动态增加的边并行计算环境因为排序和并查集操作容易并行化在物联网设备网络优化项目中我最终选择了Kruskal算法因为设备之间的连接关系相对稀疏而且经常有新的设备加入网络。算法实现后网络布线成本降低了约23%。

更多文章