算法思想
并查集(Union-Find)本质上是解决动态连通性(Dynamic-Connectivity)问题
一般包括以下操作:
- 合并(Union):将两个节点连通
- 查询(Find):询问两个节点间是否存在通路
数据结构
采用树形结构,但以下部分用数组实现
并查集类(UnionFind)类设计
- 数组存储
id[i]
表示父节点,id[i]
若为i
本身则为根节点 - 权值存储
sz[i]
表示树的规模,初始化为1
路径压缩
每次找根节点时将路径上的父节点向根节点上提,可以保证树的高度≤3(几乎展平)
代码实现
/*
Starting from an empty data structure,
any sequence of M union-find ops on N objects
makes ≤c(N + M lg*N) array accesses
·Analysis can be improved to N + M α(M,N)
·lg* stands for iterate log function, ≈5
*/
class UnionFind{
private:
int *id, *sz;//sz[i]:以i为根的树高
int root(int i){
while(i!=id[i]){
id[i]=id[id[i]];//将树展平
i=id[i];
}
return i;
}
public:
UnionFind(int N){
id=new int[N],sz=new int[N];
for(int i=0; i<N; i++){
id[i]=i;
sz[i]=1;
}
}
bool isConnected(int p,int q){
return root(p)==root(q);
}
void quickUnion(int p,int q){
int i=root(p);
int j=root(q);
if (i==j) return;
if (sz[i]<sz[j]) { id[i]=j; sz[j]+=sz[i]; }
else { id[j]=i; sz[i]+=sz[j]; }
//小树根节点连接到大树根节点上
}
};
测试代码
可以用一下主程序测试 (或者打模板题)
int main(){
int n,m,p,x,y;
cin>>n>>m>>p;
UnionFind uf=UnionFind(n);
for(int i=0; i<m; i++){
cin>>x>>y;
uf.quickUnion(x-1,y-1);
}
for(int i=0; i<p; i++){
cin>>x>>y;
cout<<(uf.isConnected(x-1,y-1)?"Yes":"No")<<endl;
}
return 0;
}
PS:数组是从0开始编号的,但测试数据是从1开始编号,请根据实际情况改动
在有的题目中,还可以将*sz
改为public
,比如:洛谷P2078朋友-传送门
为了凑篇幅, 也放在下面仅供参考:
#include <iostream>
using namespace std;
class UnionFind{
private:
int *id;
int root(int i){
while(i!=id[i]){
id[i]=id[id[i]];
i=id[i];
}
return i;
}
public:
int *sz;
UnionFind(int N){
id=new int[N],sz=new int[N];
for(int i=0; i<N; i++){
id[i]=i;
sz[i]=1;
}
}
bool isConnected(int p,int q){
return root(p)==root(q);
}
void quickUnion(int p,int q){
int i=root(p);
int j=root(q);
if (i==j) return;
if (i>j) { id[i]=j; sz[j]+=sz[i]; }
else { id[j]=i; sz[i]+=sz[j]; }
}
};
int main(){
int n,m,p,q,x,y;
cin>>n>>m>>p>>q;
UnionFind a=UnionFind(n),b=UnionFind(m);
for(int i=0; i<p; i++){
cin>>x>>y;
a.quickUnion(x-1,y-1);
}
for(int i=0; i<q; i++){
cin>>x>>y;
b.quickUnion(-x-1,-y-1);
}
cout<<min(a.sz[0],b.sz[0]);
return 0;
}
最后! 【算法重修】这个系列虽然很早就开坑了,但是不知道下一次更是什么时候,周更的flag已成月更flag,就随缘吧……