一、什么是并查集
并查集是一种用来管理元素分组情况的数据结构 ,可以查询元素a与元素b是否在同一个组中,也可以将两个不同的组进行合并操作(无法进行分割)。
并查集是使用树形结构实现的,但不是二叉树。在树种每个元素对应着一个节点,同一组元素对应同一颗树上。
二、并查集的构造
1、初始化并查集
我们准备n个节点来表示n个元素,最开始时只有单独节点,不设置边,同时设置两个数组pre[]与rank[]分别用于记录前驱节点与树的高度。
int pre[MAX_N];
int rank[MAX_N];
void init(int n){
for(int i=0; i<n; i++){
pre[i] = i;
rank[i] = 0;
}
}
2、查询树的根
在查询树的根时,我们会从一个指定元素x,利用pre[]找到它的前驱节点,继而逐步向上寻找直到找到元素x所在树的根。
然而在向上搜索根的过程中我们发现,若是像左图中的情况,查询元素5的根时,会不断向上,直至找到根节点。当我们再次寻找元素5时,它仍旧会从底部不断向上寻找根,这会使得复杂度变高。因此我们不妨在第一次寻找根节点时,将指定的元素x的前驱节点直接更改为根节点,便于之后的查询,这种方法被称之为路径压缩。
int find(int x){
//若指定元素本身就是根节点,返回指定元素
if(pre[x]==x){//递归边界
return x;
}else{
//递归,不断向上寻找前驱节点
return pre[x] = find(pre[x]);
}
}
3、合并
因为并查集是树结构,我们可以将最顶点看作根节点。从一个组的根向另一个组的根连边,这样两棵树就合并为一棵树,这样操作结束后,就可以将两组元素合并到同一个组。为方便此操作,我们会比较树的高度,将树高较矮的树的根节点当作另一棵树的子节点连接上去(前提为合并的元素不在同一棵树上)。
void unite(int x,int y){
x = find(x);//查找并记录x的根
y = find(y);//查找并记录y的根
if(x==y) return ;//判断是否是同一个根
if(rank[x]>rank[y]){
pre[y] = x;
}else{
//若树高相同,则让x所在的树的根附属到y的根下
if(rank[x]==rank[y]) rank[y]++;
pre[x] = y;
}
}
4、判断
为查询两个节点是否属于同一个组,我们需要延树向上走,来查询包含这个元素的树的根是谁,若两个节点走到了同一个根那么就可以知道他们属于同一组。
bool same(int x,int y){//判断元素x与元素y是否在同一个组中
return find(x)==find(y);
}