并查集是一种数据结构,他的作用有两个:
1,合并:将两个子集合并成一个集合
2,查找:确定某个元素处在哪个集合
fa[x]存节点x的父节点
查找:
就是为了找到某个点的根节点,如果找到了就返回,如果没找到就继续递归查找
int find(int x){if(fa[x]==x)return x;return find(fa[x]);
}
这个过程我们优化一下,优化为带路径压缩的查找
在返回的路上,顺带修改各节点的父节点为根,这样的好处就是,我遍历过一次的节点,那么下次再查找时就可以直接找到不用再来一次遍历,
int find(int x){if(fa[x]==x)return x;return fa[x]=find(fa[x]);
}
合并:
将两个集合合并在一起,拥有一个共同的根
void unionm(int x,int y){fa[find(x)]=find(y);
}
就是让x的根节点指向y的根节点
但是我们通常都是让小集合指向大集合,所以其实还可以再优化一下(但其实这步优化没必要,有上面路径压缩的优化足够了)
//按秩合并
vector<int>siz(N,1);//记录并初始化子树的大小为1
void unionm(int x,int y){x=find(x),y=find(y);if(x==y)return;if(siz[x]>siz[y])swap(x,y);fa[x]=y;siz[y]+=siz[x];
}
来道例题练习一下
P3367 【模板】并查集
#include<bits/stdc++.h>
using namespace std;
const int N=200010;
int fa[N];
int find(int x){if(fa[x]==x)return x;return fa[x]=find(fa[x]);
}
void unionn(int x,int y){//别写成union关键字fa[find(x)]=find(y);
}
int main(){int n,m;cin>>n>>m;for(int i=1;i<=n;i++){fa[i]=i;}while(m--){int z,x,y;cin>>z>>x>>y;if(z==1){unionn(x,y);}else{x=find(x),y=find(y);if(x==y)cout<<"Y"<<endl;else{cout<<"N"<<endl;}}}return 0;
}