并查集
普通并查集
基本操作
①初始化
void init(){
for(int i=1;i<=50000;i++)
fa[i]=i;
}
②查询操作
int Find(int x){
return fa[x]==x?x:Find(f[x]);
}
③集合合并
void merge(int x,int y){
int fx=Find(x);
int fy=Find(y);
if(fx!=fy)
fa[fx]=fy;
}
优化
①路径压缩
/*递归形式*/(可能会溢出栈)
int Find(int x){
return fa[x]==x?fa[x]:Find(fa[x]);
}
/*迭代形式*/
int Find(int x) {
int fx = x, t;
while (fa[fx] != fx) fx = fa[fx];//找到祖先fx
while (x != fx) { t = fa[x]; fa[x] = fx; x = t; } //路径压缩
return fx;
}
②按秩合并(可以保留树的结构)
rk[i]秩可以理解为树高,按秩合并即在进行合并操作时让秩小的树的树根依附到秩大的树的树根上
/*rk初始化为0*/
void merge(int x,int y){
int fx=Find(x);
int fy=Find(y);
if(fx!=fy){
if(rk[fx]<rk[fy]) fa[fx]=fy;
else if(rk[fy]<rk[fx]) fa[fy]=fx;
else fa[fx]=fy,rk[fy]++;//相等则随便合并,但树高会增加
}
}
rk[i]秩可以理解为节点,按秩合并即在进行合并操作时让秩小的树的树到秩大的树上
void merge(int x,int y)
{
int fx=Find(x),fy=Find(y);
if(fx==fy)
return;
if(rk[fx]>rk[fy])
swap(fx,fy);
fa[fx]=fy;
rk[fy]+=rk[fx];
}
带权并查集
在普通并查集的基础上加上一个数组w来记录点与父节点之间的关系。
因此要改变查询和合并函数的写法。(常用取模的方式来更新权值)
如图中,如果进行合并操作,那么w[fx]+w[x]=r+w[y],即w[fx]=r+w[y]+~w[x];
查询操作
int Find(int x){
if(fa[x]==x)
return x;
int fx=Find(f[x]);
//更新w[x]的值
return fa[x]=fx;
}
集合合并
void merge(int x,int y){
int fx=Find(x);
int fy=Find(y);
if(fx!=fy)
{ fa[fx]=fy;
//更新w[fx]的值
}
}