并查集及其应用——连通数、最小生成树(Kruskal)
代码将很多数组用优先队列替代,利用优先队列(小根堆)自排列的特性,实现每次输出值最小的边。
/*
-------------------------------------------------
Author: wry
date: 2022/2/26 20:35
Description: FindAndUnion
-------------------------------------------------
*/
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100+10;
struct Edge{
int from;
int to;
int length;
bool operator< (Edge e) const {
return length<e.length;
}
};
priority_queue<Edge,vector<Edge>,greater<Edge>> edge; //小根堆,相当于sort(),升序
int father[MAXN];
int height[MAXN]; //根节点height最高,最下面的为0
void Initial (int n) {
for (int i=0;i<n;i++) {
father[i] = i;
height[i] = 0;
}
}
//找根节点
int Find(int x) {
if (x!=father[x]) { //如果不是独立节点
father[x] = Find(father[x]); //用递归的方法将其直系父节点变成其父节点的根节点
}
return father[x];
}
//合并(小树合并到大树)
int Union(int x,int y) {
x = Find(x);
y = Find(y);
if (x!=y) {
if (height[x] < height[y]) {
father[x] = y;
}
else if (height[y] < height[x]) {
father[y] = x;
}
else { //如果节点不同但高度相同
father[y] = x;
height[x]++;
}
}
}
int Kruskal(int n,int m) { //n个顶点,m条边,返回最小生成树的权值
Initial(n);
int sum = 0;
for (int i=0;i<m;i++) {
Edge t = edge.top();
if (Find(t.from)!= Find(t.to)) { //如果起点和终点不在同一个树
Union(t.from,t.to);
sum += t.length;
}
edge.pop();
}
return sum;
}
int main() {
//求连通图数量
int n,m; //n个顶点,m条边
while (cin>>n>>m) {
if (n==0 && m==0) {
break;
}
Initial(n);
while (m--) {
int x,y;
cin>>x>>y; //输入边的两个顶点
Union(x,y); //将这两个顶点合并在一起(如果是两个新顶点,则走else;如果有已经输入过的点,则走if或者else if)
}
//求连通分量数
int component = 0;
for (int i=0;i<=n;i++) {
if (i == Find(i)) { //自己就是自己的根节点
component++;
}
}
}
/* //求最小生成树
int n;
while (cin>>n) {
if (n==0) {
break;
}
int m = n*(n-1)/2;
for (int i=0;i<m;i++) {
Edge e;
cin >> e.from >> e.to >> e.length;
edge.push(e);
}
int answer = Kruskal(n,m);
}
return 0;*/
}

浙公网安备 33010602011771号