最小生成树——Kruskal(克鲁斯卡尔)算法及应用

克鲁斯卡尔算法简单来说,就是把图的边大小从小到大排个序,遍历的时候逐个访问,如果加入这条边不会形成回路,那么就加入这条边。
在这里使用并查集来判断边两个端点所在集合的根节点是否相同(相同的话就是形成回路,舍弃),不在一个集合,就加入最小生成树(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

*/


发布了212 篇原创文章 · 获赞 6 · 访问量 6392

猜你喜欢

转载自blog.csdn.net/weixin_42377217/article/details/104320441