最小生成树-Kruskal算法

Prim算法贪心选择不一样,Kruskal算法采起每次选择权值最小的边的方法,这样,在不构成环且最后可以链接完全部边它们的权重和必定是最小的。html

和以前Prim算法的图同样,便于区别两者。node

Kruskal既然是选择最小的边,那么就先找一个最小的出来,是1-6(10)ios

而后继续找出剩下的边中最小一条边,是3-4(12)算法

继续找一条最小的出来,2-7(14)数组

在来,为了区别已经选择的点,我把点的颜色也作个标记,有颜色的表示为已经加入生成树的点ide

喔,忘了把最小的边2-3(16)选了spa

如今图变成了这样,(依旧难看).......3d

还没找完,继续找最小的边..是7-4(18)code

你会发现我把这条边用蓝色标记了,是否是我为了好玩??固然不是了,这条边是有问题的。htm

这条边虽然是上一次选择完后剩下的边中最短的,可是,它的左右两个点已是最小生成树的结点了,而把这条边加入后,构成了右边的一个大大的环。这样显然不是最小生成树了.

再复习下,生成树:一个连通图的生成树,是一个极小连通子图,其中包含图的全部结点,和构成一棵数的(n-1)条边。若是在一棵生成树的两个结点上添加任意一边,一定构成一个环。

最小生成树:图的全部生成树中全部边的权值和最小的那个生成树

因此,有环的连生成树都不是了,怎么会是最小生成树。。

因此,这条边7-4(18)丢掉丢掉,从新选择。选择5-4(22)

好像图中全部的点都有颜色了,可是还没彻底好,由于它们还不是一条绳上的蚂蚱,

继续选,最短的是6-5(25)

继续选 ,唉,不对,选了24发现又有环了,选28也构成环了。。。。在仔细一看,最小生成树已经生成了

这个就是Kruskal算法的流程。那么接下来讲说代码。

 1 struct node  2 {  3     int u;  4     int v;  5     int w;//边的权重
 6 }Edge;  7 void Kruskal(Graph g)//无向图g采用邻接矩阵
 8 {  9     Edge E[MAXN];//存放图中全部的边
10     int vest[MAXN];//辅助数组,存放连通的点的编号
11     k = 0; 12     for(int i = 0;i < g.n;i++) 13  { 14         for(int j = 0; j <= i ;j++) 15  { 16             if(g.egdes[i][j] != 0 &&g.egdes[i][j]!= INF) 17             {//说明i到j边
18                 E.[k].u = i; 19                 E.[k].v = j; 20                 E.[k].w = g.edges[i][j]; 21                 k++; 22  } 23  } 24     }//以上将全部的边都加入到E数组中,确定是不重复的
25     Sort(E);//对边集数组进行排序。
26     Init(vest);//将辅助数组初始化,即每一个结点一开始都没有加入到最小生成树中,因此它们各自为一个阵营
27     j = 0; 28     Count = 0 ; 29     while(Count < g.n)//已经加入生成树的结点数小于图的结点数,代表生成树没生成完,
30  { 31         u1 = E[j].u; 32         v1 = E[j].v;//选出最短的边
33         s1 = vest[u1]; 34         s2 = vest[v1];//获得两个所在集合的编号,
35         if(s1 != s2)//若是它们编号不等,,说明它们还每加入到同一个生成树中,
36  { 37             //这里能够打印它们的编号 。。。
38             k++; 39             for(int i = 0; i< g.n;i++) 40  { 41                 if(vest[i]==s2) 42  { 43                     vest[i] = s1;//把属于s2的那个点加入到s1所在点集合中
44                 }//即合并到一块儿去,代表它们在一个生成树中了,这样就和 45                 //其余没有加入到的点区分开了
46  } 47  } 48         j++;//继续找下一条边
49  } 50 }
View Code

这个代码没有写全,由于这样用的次数少的可怜了。都用它的升级版了

首先,针对排序的问题,排序随你选,接下来就是判断是否构成环的问题了。这里用并查集轻易的实现

 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<vector>
 6 #include<stack>
 7 #include<map>
 8 #include<set>
 9 #include<list>
10 #include<queue>
11 #include<string>
12 #include<algorithm>
13 #include<iomanip>
14 using namespace std; 15 const int maxn = 100; 16 int par[maxn]; 17 int rank[maxn]; 18 int n; 19 int m; 20 
21 struct node 22 { 23     int Start; 24     int End; 25     int weight; 26 }edges[maxn]; 27 
28 int cmp(node a,node b)//按权值从小到大排序
29 { 30     return a.weight < b.weight; 31 } 32 
33 void Init(int n)//par初始化为本身
34 { 35     for(int i = 1;i <= n;i++) 36  { 37         par[i] = i; 38  } 39 } 40 int  Find(int x)//找出父亲结点
41 { 42     if(x != par[x])return x =Find(par[x]); 43     return x; 44 } 45 
46 int Kruskal() 47 { 48     int sum = 0; 49     for(int i = 1;i <= m ;i++) 50  { 51         int a = Find(edges[i].Start); 52         int b = Find(edges[i].End); 53         if(a != b)//父亲结点不一样
54  { 55             sum += edges[i].weight;//通常求最小生成树的长度,这里就没去掉
56             par[a] = b;//合并集合
57  } 58  } 59     return sum; 60 } 61 
62 int main() 63 { 64     
65     cin>>n>>m; 66  Init(n); 67     for(int i = 1;i <= m;i++) 68  { 69         cin>>edges[i].Start>>edges[i].End>>edges[i].weight; 70  } 71     sort(edges+1,edges+1+n,cmp); 72     cout<<Kruskal(); 73     return 0; 74 }
View Code

传送门:NetWork  不会的点这里