![4ed240710a28e41b8c8d28944007edff.gif](https://i-blog.csdnimg.cn/blog_migrate/6bf44e542d32db302da646fb1e9fb167.gif)
「今天是学习C语言第 163 天」
纸上学来终觉浅,绝知此事要躬行。—— 陆游「冬夜读书示子聿」# 最小生成树
对于一个n个顶点的连通图,可以选择n-1条边构成生成树,树中的代价等于n-1条边的和,因此最小生成树是图生成的代价最小的生成树。
求解最小生成树算法主要有两种,分别是Prim算法和Kruskal算法。下一篇学习Prim算法。
# Kruskal算法基本思想
对n个顶点构成的连通图中所有的边从小到大排序,每次选择代价最小的边,且该边不会和已选的边形成环路,直到选择n-1条边为止,即可获得最小生成树。
Kruskal算法时间复杂度是O(eloge),其中e表示边的条数,由此可见Kruskal算法适合顶点较多、边较少的稀疏图。
# 实现要点
1.图中的所有边需要从小到大排序,可以使用各种排序方法;
2.使用并查集算法检查环路,判断当前选择的边上两端顶点是否已经在一个连通分量的顶点集合中。
备注:并查集算法可以参见前一篇文章。
C语言实现常用数据结构:并查集(第19篇)
# 运行结果
求以下图的最小生成树,将最小生成树的边依次输出。
edge : 2 ---- 3 , cost : 1edge : 1 ---- 3 , cost : 2edge : 0 ---- 1 , cost : 3edge : 1 ---- 4 , cost : 4edge : 4 ---- 5 , cost : 5--------------------------------Process exited after 0.6048 seconds with return value 0请按任意键继续. . .
# 代码
/*========================================== 名称 :C语言实现常用数据结构 功能 :Kruskal最小生成树算法 环境 :Windows 10 + Dev-C++编译 作者 :一只会C的猫 公众号 :C语言大全(coderpointer) 时间 :2020.9.25==========================================*/#include #include #include #include struct edge { // 边上顶点编号、代价 int src, dst, cost;};struct graph { // 顶点、边个数 int v, e; // 使用动态分配保持所有边的信息 struct edge *edge;}; // 创建并查集 int *new_set(int n){ int *set = (int *)malloc(n * sizeof(int)); assert(set); // 初始化,每个顶点单独一个集合 int i; for(i = 0; i < n; i++) set[i] = i; return set;}// 并查集中查找顶点x的根节点 int find_set(int set[], int x){ assert(set); int root = x; while(set[root] != root) root = set[root]; // 压缩路径 set[x] = root; return root;}// 合并集合void union_set(int set[], int x, int y){ assert(set); int rx, ry; rx = find_set(set, x); ry = find_set(set, y); if(rx != ry) set[rx] = ry; } // 检测是否有环int is_circle(int set[], int x, int y){ return find_set(set, x) == find_set(set, y);} // 比较大小函数int edge_cmp(const void *x, const void *y){ struct edge *e1 = (struct edge *)x; struct edge *e2 = (struct edge *)y; return e1->cost - e2->cost;} void kruskal_mst(struct graph *graph){ assert(graph); // 保持最小生成树的边,边的个数是顶点数-1 struct edge *edge_mst = (struct edge *)malloc \ ((graph->v - 1) * sizeof(struct edge)); // 图的边按照代价从小到大排序 qsort(graph->edge, graph->e, sizeof(graph->edge[0]), \ edge_cmp); // 每次选择代价最小的边,且不形成环 // 创建并查集,用于检测环路 int *set = new_set(graph->v); int i; struct edge *p = graph->edge; for(i = 0; i < graph->v - 1; i++) { // 如果有环,则选取下一条边 while(is_circle(set, p->src, p->dst)) p++; edge_mst[i] = *p; union_set(set, p->src, p->dst); p++; } // 输出最小生成树的边 for(i = 0; i < graph->v - 1; i++) printf("edge : %d ---- %d , cost : %d\n", i, edge_mst[i].src, \ edge_mst[i].dst, edge_mst[i].cost); } // 创建图数据结构 struct graph *create_graph(int v, int e){ struct graph *graph = (struct graph *)malloc( \ sizeof(struct graph)); assert(graph); graph->v = v; graph->e = e; struct edge *edge = (struct edge *)malloc( \ e * sizeof(struct edge)); assert(edge); graph->edge = edge; return graph;}int main(void){ struct graph *graph = create_graph(6, 8); // 顶点0和1之间的边 graph->edge[0].src = 0; graph->edge[0].dst = 1; graph->edge[0].cost = 3; // 顶点0和2之间的边 graph->edge[1].src = 0; graph->edge[1].dst = 2; graph->edge[1].cost = 8; // 顶点1和3之间的边 graph->edge[2].src = 1; graph->edge[2].dst = 3; graph->edge[2].cost = 2; // 顶点1和4之间的边 graph->edge[3].src = 1; graph->edge[3].dst = 4; graph->edge[3].cost = 4; // 顶点2和3之间的边 graph->edge[4].src = 2; graph->edge[4].dst = 3; graph->edge[4].cost = 1; // 顶点2和4之间的边 graph->edge[5].src = 2; graph->edge[5].dst = 4; graph->edge[5].cost = 6; // 顶点3和5之间的边 graph->edge[6].src = 3; graph->edge[6].dst = 5; graph->edge[6].cost = 8; // 顶点4和5之间的边 graph->edge[7].src = 4; graph->edge[7].dst = 5; graph->edge[7].cost = 5; kruskal_mst(graph); return 0;}
---------- End ----------
往期精彩推荐:
一万分钟C语言学习计划:2020开篇
C语言内存管理的两种方式
C89标准库功能简介
C语言链接与存储类型
C语言标准输入输出
C语言入门基本语法
更多请点击公众号历史文章...
「喜欢C请赏个 赞 点击右下角 在看」