C++,Kruskal克鲁斯卡尔算法求最小生成树

Kruskal算法:

一、将图中所有边去掉,保留下所有点,变成了n个顶点的森林

二、贪婪准则:从所有边中依次选出权值最小的边,加入图中,还必须保证新加入的边不会产生环路,若产生环路,则抛弃

三、加到n-1条边,则结束循环,就会生成该图的最小生成树

 

伪代码如下:

 

现在的问题就是,如何判断是否有环路呢?

我们使用并查集的方法来判断,用二叉树实现并查集。查 即: 就是 find() 函数,对于每一个节点,使用find函数,则返回该节点所在二叉树的根节点的值。通过find 函数来判断 两个节点是否有同一个根节点,即是否在同一颗二叉树内。如果插入的边的两端节点都在同一颗树内,则插入该树,肯定会产生环路。反之,如果插入边的两个节点不在同一颗树内,则不会产生环路。

并 即: 就是 union_tree() 函数,先通过find 函数找到两个节点的根节点,如果不相同,则需要合并,合并就是把一个节点的根节点的值改成两一个节点的根节点,就是相当于,把一颗二叉树的根,连在了另一颗二叉树上,实现合并。

使用并查集解决了环路问题,那我们就用算法生成 最小树吧。

 

#include <iostream>
#include<algorithm>
using namespace std;
struct Edge {
	int x, y;
	int w;

}edge[100];
int fa[100]; //存放每个点所属的类父节点

int cmp(Edge &e1,Edge &e2) {  //按权值从小到大排序,权值一样则按起始点排序
	if (e1.w != e2.w) {
		return e1.w < e2.w;
	}
	else {
		return e1.x < e2.x;
	}
}
int find(int x) {     //寻找根节点
	return fa[x] == x ? x : fa[x]=find(fa[x]);
}


void union_tree(int x, int y) { //将两个边集合并
	int fx = fa[x];
	int fy = fa[y];
	if (fx != fy) {
		fa[fx] = fy;
	}
}
int main()
{
	int n, m,x,y,w; //顶点个数和边的个数
	int sum = 0;
	int cnt = 0;//统计树的边
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; i++) {
		scanf("%d%d%d", &x, &y, &w);
		edge[i].x = x;
		edge[i].y = y;
		edge[i].w = w;
	}
	sort(edge + 1, edge + m + 1,cmp);

	for (int i = 1; i <= n; i++) {  //初始化 根节点全部为自己,然后合并
		fa[i] = i;
	}

	for (int i = 1; i <= m; i++) {
		int x1 = find(edge[i].x);
		int y1 = find(edge[i].y);
		if (x1 != y1) {
			sum += edge[i].w;
			union_tree(x1, y1);
			cnt++;
		}

		if (cnt == m - 1) {
			break;
		}
	}
	cout << sum << endl;  //权值之和

	return 0;
}

样例:

8
10
1 2 2
1 3 8
2 3 7
2 4 9
3 4 4
3 5 10
3 6 12
5 6 6
5 7 14
7 8 3

输出:

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值