【算法】最小生成树-Kruskal算法

: 连通且不含环的图

生成树:给定无向图G=(V,E),连接G中所有点,且边集是E的子集的树

最小生成树(MST):权值最小的生成树

Kruskal算法

①将所有边按从小到大排列

②依次考察每条边((u,v))。若(u)(v)在同一个连通分量中,加入((u,v))会成环,因此不能选择;否则,加入((u,v))一定是最优的(假设不加入这条边能得最优解T,则(T+(u,v))一定有且只有一个环,且这个环中至少有一条边((u',v'))的权值(≥(u,v))

关于连通分量的判断与合并,使用并查集。

并查集将每个连通分量看成一个包含该分量中所有点的集合,这些点之间两两连通(强连通?),只需要选其中一个点作为集合的代表即可(也就是缩点)。

//并查集
//p[x]保存结点x的祖先
int find(int x) {return p[x] == x ? x : p[x] = find(p[x]);}
//最小生成树Kruskal算法
int u[maxn], v[maxn];//第i条边的两个端点的序号
int w[maxn];//第i条边的权值
int cmp(const int i, const int j) { return w[i] < w[j]; }
int find(int x) {return p[x] == x ? x : p[x] = find(p[x]);}
int Kruskal() {
	int ans = 0;
	for (int i = 0; i < n; i++) p[i] = i;
	for (int i = 0; i < m; i++) r[i] = i;
	sort(r, r + m, cmp);
	//实际上是对w数组排序,但将结果顺序的下标保存在r数组中
    //如w[4]={4,2,1,0},排序后r数组储存的是{3,2,1,0}
	for (int i = 0; i < m; i++) {
		int e = r[i];//取边(u,v),记为e
		int x = find(u[e]);//找出e的一个端点u的所在集合编号
		int y = find(v[e]);//找出e的另一个端点v的所在集合编号
		if (x != y) {//如果不在同一个集合
			ans += w[e];//最小生成树的总权值更新
			p[x] = y;//合并
		}
	}
	return ans;
}
原文地址:https://www.cnblogs.com/streamazure/p/12911636.html