并查集
Posted subject
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并查集相关的知识,希望对你有一定的参考价值。
并查集
并查集运用树的结构,通过判断每个点的最终双亲是否相同来判断是不是在一个集合中
一般步骤:
1.查找:查找一个点的最终双亲---递归
2.合并:如果两个相通点的双亲相同,说明已经在一个集合中,不需要合并,不同则将两个集合合并
例题:村村通
给定n个村庄和m条已经通的路,问还需要在休几条路
思路:将通的路算作一个集合,求出有几个独立的集合
将村庄比较树的结点,与它相通则表示为它的孩子;
int par[N] 记录每个村庄的最终双亲
首先将每个村庄的双亲记为自己:
void init(int n) { for (int i = 0; i <= n; i++) { par[i] = i; Rank[i] = 1; } }
然后开始输入相通的村庄,并进行合并---其中Rank[] 的作用记录每科树的深度,使得在合并时让大树作为小树的双亲(减小树的深度)
int find(int x) {//找最终双亲 if(par[x] == x) return x; else return par[x] = find(par[x]); } void unite(int x, int y) { x = find(x); y = find(y); if(x == y) return ; if(Rank[x] <= Rank[y]) par[x] = y; else par[y] = x; if(Rank[x] == Rank[y]) Rank[x]++; }
此时所有集合都已经找到了,我们只需要遍历每一个村庄,如果每个村庄的双亲就是自己说明它是集合的根节点 ans++;
最后ans就是集合的个数,要想让所有集合相通 需要添加 ans-1 条路。
全部代码
#include<bits/stdc++.h> using namespace std; const int N = 1010; int par[N], Rank[N]; //par 祖先,Rank秩优化 void init(int n) { for (int i = 0; i <= n; i++) { par[i] = i; Rank[i] = 1; } } int find(int x) { if(par[x] == x) return x; else return par[x] = find(par[x]); } void unite(int x, int y) { x = find(x); y = find(y); if(x == y) return ; if(Rank[x] <= Rank[y]) par[x] = y; else par[y] = x; if(Rank[x] == Rank[y]) Rank[x]++; } bool same(int x, int y) { return find(x) == find(y); } //init(int) 初始化,unite(x,y) 合并 same 是否相同 // find() 找祖先 int main() { int n,m; while(cin>>n) { if(n==0) break; cin>>m; init(n); for(int i=1;i<=m;i++) { int x,y; cin>>x>>y; unite(x,y); } int ans = 0; for(int i=1;i<=n;i++) { if(i==find(i)) ans++; } cout<<ans-1<<endl; } return 0; }
以上是关于并查集的主要内容,如果未能解决你的问题,请参考以下文章