定义
并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。(By百度百科)
查找
假设有2个集合,集合1 = {0,1,2},集合2 = {3,4,5}。
如果判断1和5是否在一个集合,如果直接枚举一个集合是非常慢的。
而我们把任何一个集合中的每一个元素都用同一个数字来表示,那么我们只要比较1和5各自的数字是否相同就可以得知他们是否在一个集合中。
讲的更通俗一点,这里有6个人,0是1的爸爸,1是2的爸爸,3是4的爸爸,4是5的爸爸,而且他们都特别不孝顺,都不知道自己的爷爷是谁。
现在他们想知道自己是不是一家人,2问自己的爸爸,即1,1问0,0发现自己没有爸爸,就告诉1自己是这一家家主,1又告诉2,另一边也是这样。2和5一交流,发现一边的家主是0,一边是3,他们就知道自己不是一家人了。
代码:
我们用father[x]数组来表示x的爸爸是谁。
inline int find(int x){
int r = x;
while(r != father[r]) r = fathrt[r];//让自己的爸爸去问
return r;//r就是x一家的家主
}
合并
还是2个集合,{0,1,2}和{3,4,5}
还是那群不孝顺的人,还是那2个小孩子。现在他们现在恋爱了,结婚了,那么这6个人就是一家人了,那么再次进行询问的话怎么办呢。我们告诉3,你现在是0的人啦,以后你儿子问你的时候你和他说家主是0就OK啦。
代码:
inline void join(int x,int y){
int r = find(x);
father[r] = find(y);//x的家主变成y的家主
}
路径压缩
依旧是那群人。。。
在问过以后,2和5都觉的如果这样子每次都去问一边太麻烦了,于是决定以后直接问0,跳过剩下的其他人。
代码:
inline int find(int x){
if(x != father[x]) return father[x] = find(father[x]);else
return x;
}
例题
1.模版:
题目来自洛谷p3367
AC代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,kkk[11000];
int find(int x){
if(kkk[x] != x) return kkk[x] = find(kkk[x]);else
return x;
}
void join(int a,int b){
int x = find(b),y = find(a);
kkk[x] = y;
}
int main(){
//freopen("kkk.txt","r",stdin);
cin>>n>>m;
for(int i = 1;i <= n;i++){
kkk[i] = i;
}
for(int i = 1;i <= m;i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
if(x == 1) join(y,z);
if(x == 2){
if(find(y) == find(z)) cout<<"Y"<<endl;else
cout<<"N"<<endl;
}
}
return 0;
}
2.简单应用:
题目来自洛谷
AC代码:
其实和模版没什么区别。。。
#include<bits/stdc++.h>
using namespace std;
int n,m,p,f[110000];
int find(int k){
if(f[k] == k) return k;else
return f[k] = find(f[k]);
}
void init(){
cin>>n>>m>>p;
for(int i = 1;i <= n;i++){
f[i] = i;
}
int x,y;
for(int i = 1;i <= m;i++){
cin>>x>>y;
f[find(x)] = find(y);
}
}
int main(){
init();
int x,y;
for(int i = 1;i <= p;i++){
cin>>x>>y;
if(find(x) == find(y)) cout<<"Yes"<<endl;else
cout<<"No"<<endl;
}
return 0;
}