并查集
并查集是一个完全二叉树,具体理解就看下面这个题吧:洛谷P1551
可以看到并查集每一个节点都存着其父亲的节点。可以支持查找一个元素所属的集合以及两个元素各自所属的集合的合并。可以设初始有n个元素分属不同的集合,通过给出其中元素之间的关系,要求统计元素间的关系(就像题里面是否是亲戚一样)。这时候只需判断元素是否属于同一集合即可。
要实现并查集,我们可以通过一个结构体和数组来实现。
要实现并查集,我们要支持以下这些操作。
1、初始化(unionFind)一个并查集
2、把元素a和b建立联系(uni(a,b))
3、查找a的祖先(find(x))
4、询问a与b是否有联系(ask(a,b))
代码(c++)实现如下:
//并查集模板
struct uf{
int bin[10005];
//记录父亲节点的数组
uf(){
for(int i=1;i<=sizeof(bin);i++){
bin[i]=i;
//初始化操作,所有元素都指向自己
}
}
int find(int x){
if(bin[x] != x) return find(bin[x]);
else return x;
//递归操作实现寻找 x的最终颜色
}
void uni(int x,int y){
//把 x的祖先染成 y的颜色
bin[find(x)]=find(y);
}
bool ask(int x,int y){
//询问操作
if(find(x) == find(y))return 1;
else return 0;
}
};
我们会发现,在find()函数中,每次查询都要递归,如果一个并查集只有根节点和左儿子,那么时间复杂度会极高,我们可以干脆把bin[x]直接指向他的祖先,这就是我们说的路径压缩,一般用并查集都要路径压缩,只需做一点小手脚就行了。
代码如下:
//路径压缩
int find(int x){
if(bin[x] != x) return bin[x]=find(bin[x]);
else return x;
}
通过路径压缩,可以有效地降低时间复杂度。