克鲁斯卡尔算法简单来说,就是把图的边大小从小到大排个序,遍历的时候逐个访问,如果加入这条边不会形成回路,那么就加入这条边。
在这里使用并查集来判断边两个端点所在集合的根节点是否相同(相同的话就是形成回路,舍弃),不在一个集合,就加入最小生成树(father[fau] = fav;),同时把当前最小生成树的边数+1
这里同时利用了一个性质:最小生成树的边数 = 总顶点数 -1(若不满足,说明不连通,则算法失效)
不写伪代码了,直接上例子:
《算法笔记》P412
#include <stdio.h>
#include <algorithm>
using namespace std;
const int maxn = 110;
const int maxe = 10010;
struct edge{
int u,v;
int cost;
}E[maxe];
bool cmp(edge a,edge b){ //从小到大排序
return a.cost <b.cost;
}
int father[maxn];
int findfather(int x){
int a = x;
while(x != father[x]){
x = father[x];
}
//路径压缩
while(a != father[a]){
int z = a;
a = father[a];
father[z] = x;
}
return x;
}
int kruskal(int n,int m){
int ans = 0,Numedge = 0;
for(int i = 0;i <n;i++){
father[i] = i;
}
sort(E,E+m,cmp);
for(int i = 0;i <m;i++){
int fau = findfather(E[i].u);
int fav = findfather(E[i].v);
if(fau != fav){ //不在一个集合中
father[fau] = fav; //合并集合(把测试边加入最小生成树中)
ans += E[i].cost; //边权之和增加测试边的边权
Numedge++; //当前生成树的边数+1
if(Numedge == n-1) break;
}
}
if(Numedge != n-1) return -1;
else return ans;
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i = 0;i <m;i++){
scanf("%d%d%d",&E[i].u,&E[i].v,&E[i].cost);
}
int ans = kruskal(n,m);
printf("%d\n",ans);
return 0;
}
/*
6 10
0 1 4
0 4 1
0 5 2
1 2 1
1 5 3
2 3 6
2 5 5
3 4 5
3 5 4
4 5 3
*/