最小生成树之克鲁斯卡尔(kruskal)算法

 Kruskal算法

前面讲了最小生成树的Prim算法的实现思路是,通过顶点的扩展不断地寻找最小权重的生成树,而Kruskal算法是查找最小权值的边,然后逐渐把连通分量变为一个联结全部顶点的最小生成树。

不同于 Prim算法 ,这次用边集数组结构来实现 Kruskal算法 

结构很简单,包括权值,边的弧起点和终点的下标

将前面Prim例子中的图转化为边集数组,并且按照权值升序排列储存为 edges[] 这样一个数组,那么这样做的意义就在于后面按照权值的顺序来安排边,代码如下:

 1 void MiniSpanTree_Kruskal(MGraph G)
 2 {
 3     int i,n,m;
 4     Edge edges[MAXVEX];
 5     int parent[MAXVEX];
 6     //这里省略构造edges数组时的排序步骤代码
 7     for(i = 0;i < G.numVertexes;i++)
 8     {
 9         parent[i] = 0;
10     }
11     for(i = 0;i < G.numEdges;i++)
12     {
13         n = Find(parent,edges[i].begin);
14         m = Find(parent,edgws[i].end);
15         if(n != m)
16         {
17             parent[n] = m;
18             //将这一条边的结尾顶点下标存在数组起点下标的位置
19             printf("(%d %d) %d",edges[i].begin,edges[i].end,edges[i].weight);
20         }
21     }
22 }
23 
24 int Find(int *parent,int f)
25 {
26     while(parent[f] > 0)
27         f = parent[f];
28     return f;
29 }

我们来读一哈这个代码:

3~10行定义边集数组以及初始化 parent 数组,后面详细讲解这个数组的用处

代码结构较为简单,故讲解11~22行循环,夹杂 Find 函数实现

第一次循环: n = 4 ,m = 7, parent = {0,0,0,0,7,0,0,0,0,0,0,0,0,0,0} ,然后打印边的起点终点和权值,此时 parent 数组以数字下标位置和内容表示(v4 v7)这条边已经加入豪华最小生成树

第二次循环:n = 2 ,m = 8, parent = {0,0,8,0,7,0,0,0,0,0,0,0,0,0,0} ,然后打印边的起点终点和权值,此时 parent 数组以数字下标位置和内容表示(v2 v8)这条边已经加入豪华最小生成树

 

 第三次循环:n = 0 ,m = 1, parent = {0,1,8,0,7,0,0,0,0,0,0,0,0,0,0} ,然后打印边的起点终点和权值,此时 parent 数组以数字下标位置和内容表示(v0 v1)这条边已经加入豪华最小生成树

 第三、四、五、六次循环:因为循环中的判断条件和之前一样, 故第六次循环后parent = {1,5,8,7,7,8,0,0,6,0,0,0,0,0,0} ,然后不断打印边(v0,v5)(v1,v8)(v3,v7)(v1,v6)的起点终点和权值

当 i = 7 时,我们会发现对应的是 (v5 v6)这条边,如果打印这条边,则会生成闭环,就不符合我们最小生成树的结构要求,那么我们来跑一下 11行的循环, 调用Find函数,会传入参数 edges[7].begin = 5,此时 Find函数内部,parent[5] = 8,大于0, m = f = parent[8] = 6,而 edges[7].end = 6,传入Find 得 n = 6,此时m = n,不打印结点信息,(否则将形成闭环)

后面的 i = 8、 i = 9均为闭环所以均不打印

生成树为

Kruskal算法总结:

假设 N= (V,{E})是连通网,则令最小生成树的初始状态为只有 n 个顶点而无边的非连通图 T={V,{}},图中每个顶点自成一个连通分量。在 E 中选择代价最小的边,若该边依附的顶点落在 T 中不同的连通分量上,则将此边加入到 T 中,否则舍去此边而选择下一条代价最小的边。依次类推,直至 T 中所有顶点都在同一连通分量上为止。
此算法的 Find 函数由边数 e 决定,时间复杂度为 O(loge)而外面有一个 for 循环 e 次。 所以克鲁斯卡尔算法的时间复杂度为 O(eloge).
对比两个算法,Kruskal算法主要是针对边来展开,边数少时效率会非常高,所以对于稀疏图有很大的优势,而Prim算法对于稠密图,即边数非常多的情况会更好一些。

 

posted @ 2019-02-11 16:52  compassion‘s  阅读(2268)  评论(0编辑  收藏  举报