0
点赞
收藏
分享

微信扫一扫

并查集模板+带权并查集

并查集由一个整数型的数组和两个函数构成。数组pre[ ]记录了每个点的前导点是什么,函数find是查找,join是合并。

int pre[1005];
int find(int x)//查找x的前一个节点
{
int r=x;
while (pre[r]!=r){
r=pre[r];//一直寻找上一个节点,直到根节点
}//最终r为根节点

//路径压缩 :把所有子节点的上一个节点都改为根节点
int i=x, j;
while (i!=r){
j=pre[i];//暂时存储
pre[i]=r;
i=j;
}

return r;//返回每个点的直接根节点
}

void join(int x, int y)//合并两个附属于不同根节点的子节点
{
int fx=find(x), fy=find(y);
if (fx!=fy)//改变fx的上一个节点后,fx的都指着fx,fx指着fy,根节点数量不会增加
pre[fx]=fy;//把fx归到fy下,使得fy成为最后的根节点
}

求互相没有联系的组的个数

bool t[1005]={0};
for (int i=1; i<=n; i++)//因为join的合并实在find函数后面,最后直接合并两个根节点,
t[find(i)]=1;//每次调用find函数,n个点后,所有的点都直属于它的根节点。
int num=0;
for (int i=1; i<=n; i++)
if (t[i])
num++;
//num的个数为互相没有联系的组数

并查集的优化
并查集有两种优化方式,第一种是路径压缩,第二种是按秩合并。

路径压缩又分为非递归实现的和递归实现
非递归实现的就是上面的代码,它不适用于求解带权并查集的问题,而递归实现的路径压缩虽然空间消耗相比较大,但是如果求解带权问题就要用它了。
下面是递归实现的路径压缩

int find(int x)
{
if (x==father[x])
return x;
int tmp=father[x];
father[x]=find(father[x]);//通过递归让子结点直接连到根节点上
//维护带权边操作
return father[x];
}

按秩合并
一般情况下合并两个节点时,谁做谁的父亲都无所谓,这时就出现了按秩优化。假设有两个根节点,第一个根节点的深度是100个,第二个根节点的深度是10个,如果让第一个根节点合并到第二个下面,那么在下一次find的过程中,最坏的情况下第一个根节点下面的最后一个节点,就要find100次才能到根节点,这样就浪费了时间。如果让深度小的根节点合并到深度大的根节点下面,就会缩短很大的时间。

void join(int x, int y)
{
//在初始化pre数组的同时要初始化rank都为1
//rank指的是树的深度
int fx=find(x), fy=find(y);
if (fx!=fy){
if (rank[fx]>rank[fy]){
pre[fy]=fx;
}else if (rank[fy]>rank[fx]){
pre[fx]=fy;
}else{
rank[fx]++;
pre[fy]=fx;
}
}
}

带权并查集
普通的并查集就是描述两个点之间的关系,比如说a的父亲节点是b,c的父亲节点是a,b是a和c的根节点。而如果现在告诉了你a和b两个节点之间的关系,a和c之间的关系,也就是所谓的加上了边权,那么解题的时候就必须加上边权信息,这就成了带权并查集。
带权并查集只是在普通的并查集find函数和join函数中加了一些操作。

比如说初始每个结点的根节点的距离deep[i]都是0(每个点都是自己的老大),每个结点所在的集合有sum[i]=1个(初始只有自己一个集合),通过合并一些点求它到根节点的距离。
那么在find的时候就要更新deep值

int find(int x)
{
if (x==father[x])
return x;
int tmp=father[x];
father[x]=find(tmp);
deep[x]+=deep[tmp];
//它到根节点的距离等于它到它爸的距离加上他爸到它爸的爸的距离加上.....
return father[x];
}

只是这样的操作deep每次都是0,根本不会更新,所以还需要一个sum数组,这样当两个节点合并的时候,某一个集合的数量就会增加,而被合并的那个节点到跟节点的距离就是合并后作为根节点的那个结合的数量,最后再把这个集合的数量加上被合并的节点所在的集合的数量。
虽然只是改变了根节点的信息,其他的子节点并没有改变,不要着急,在下次用find查询某一个节点的信息的时候,deep[i]就会在find函数里被自动改变了。

void join(int x, int y)
{
int fx=find(x), fy=find(y);
if (fx!=fy){
father[fy]=fx;
deep[fy]=sum[fx];
sum[fx]+=sum[fy];
}
}


举报

相关推荐

0 条评论