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

转载自C++ 最小生成树之kruskal(克鲁斯卡尔)算法

kruskal算法:同样解决最小生成树的问题,和prim算法不同,kruskal算法采用了边贪心的策略,思想要比prim算法简单。

算法基本思想

  • 在初始状态时隐去图中的所有边,这样图中每个顶点都自成一个连通块。之后执行下面的步骤:
    (1)对所有的边 按边权从小到大 进行排序;
    (2)按边权从小到大测试所有边,如果当前测试边所连接的两个顶点不在同一个连通块中,则把这条测试边加入当前最小生成树中;否则,将边舍弃;
    (3)执行步骤(2),知道最小生成树中的边数等于总顶点数减1或者测试完所有的边时结束。当结束时,如果最小生成树的边数小于总顶点数减1,则说明该图不连通。

算法的重点 并查集的使用!

在于如何判断两个点是否在不同的连通块中.
  • 把每个连通块当做一个集合,判断两个端点是否在同一个连通块中就可以转换为判断两个端点是否在同一个集合中,解决这个问题的方法是使用并查集。
  • 并查集可以通过查询两个结点所在集合的根结点是否相同来判断它们是否在同一个集合中
如何将测试边加入最小生成树
  • 加测试边合并就能解决这个问题
  • 即只要把测试边的两个端点所在集合合并

具体代码实现

  • kruskal算法需要对边排序.对边的定义:
struct edge {
	int u, v; // 边的两个端点
	int cost; // 权值
	edge(int x, int y, int val) :u(x), v(y), cost(val){}
};

// 比较函数,排序时使用
bool cmp(edge a, edge b) {
	return a.cost < b.cost;
}
  • 具体代码
// 如何判断测试边的两个端点是否在不同的连通块中
int findFather(vector<int> father, int x) {
	int a = x;
	while (x != father[x]) {
		x = father[x]; // 找到最远的父亲
	}
	// 把所有儿子全部挂到指向x上.
	while (a != father[a]) {
		int z = a;
		a = father[a];
		father[z] = x;
	}
	return x;
}

// n顶点个数 m边数
int kruskal(int n,int m,vector<edge>& E) {
	// 需要用到并查集
	int ans = 0; // 所求边的权值之和
	int NumEdge = 0; // 记录最小生成树的边数

	// 初始化并查集
	vector<int> father(n);
	for (int i = 0; i < n; i++) {
		father[i] = i; // 每个点自己一个组
	}

	// 将权值从小到大排序
	sort(E.begin(), E.end(), cmp);
	
	// 遍历所有的边
	for (int i = 0; i < m; ++i) {
		int father_U = findFather(father, E[i].u);
		int father_V = findFather(father, E[i].v);
		if (father_U != father_V) {
			// 不在同一个集合中,就可以把边加入最小生成树中
			father[father_U] = father_V;
			ans += E[i].cost;
			NumEdge++;
			if (NumEdge == n - 1) {
				break;
			}
		}
	}
	if (NumEdge != n - 1) {
		return -1;
	}
	return ans;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值