并查集及其应用——连通数、最小生成树(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;*/ }