【算法】最小生成树-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;
}
posted @ 2020-05-18 17:01  StreamAzure  阅读(189)  评论(0)    收藏  举报