并查集
1、并查集的定义
并查集用数组实现
int father[N];
father[2]=1表示元素2的父亲结点是元素1
2、并查集的基本操作
并查集的使用需要先初始化father数组,然后再根据需要进行查找或合并的操作。
(1)初始化
for(int i=1;i<=N;i++)
{
father[i]=i; //令father[i]=-1也可
}
(2)查找
查找操作:对给定的结点寻找其根结点的过程。实现的方式是递推或是递归
递推:
int findFather(int x){
while(x != father[x]) //如果不是根结点,继续循环
{
x = father[x]; //获得自己的父亲结点
}
return x;
}
递归:
int findFather(int x)
{
if(x==father[x]) return x;
else return findFather(father[x]);
}
(3)合并
把两个集合并成一个集合。获得两个元素的根节点faA与faB,只需要把其中一个的父亲结点指向另一个结点。
void Union(int a,int b)
{
int faA = findFather(a);//查找a的根节点,记为faA
int faB = findFather(b);//查找b的根节点,记为faB
if(faA != faB){ //如果不属于同一个集合
father[faA] = faB; //合并他们
}
}
3、路径压缩
优化查询操作
把当前查询结点的路径上的所有结点的父亲都指向根结点,查找的时候就不需要一直回溯去找父亲了。查询复杂度降为O(1)。
递推写法:
int findFather(int x){
//由于x在下面的while中会变成根结点,因此先把原先的x保存一下
int a=x;
while(x!=father[x]) //寻找根结点
{
x=father[x];
}
while(a!=father[a])
{
int z=a; //因为a要被father[a]覆盖
a = father[a]; //a回溯父亲节点
father[z]=x; //将原先的结点a的父亲改为根结点x
}
return x; //返回根结点
}
递归写法: