并查集

博客主要介绍了并查集,它是对树的一种操作,目的是找到节点的公共祖先。详细讲解了“并”操作,即将两个节点合并到一个树集合中;还介绍了“查”操作,用于查找节点的最老祖先,同时提到线性写法可压缩路径、加快查找速度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

并查集

并查集是对树的一种操作,旨在找到某个节点的公共祖先(最老公共祖先)。我们先讲一下并。

并就是讲两个节点合并到一个集合里面(这个集合必须是树),每个节点对应一个祖先,最老公共祖先的祖先就是自己,而每个节点在合并前的初始值也是自己,也就是:若有一个节点 a ,设它的祖先为 f[a] ,那么它的初始值就是 f[a]=a ,。这个合并操作并不难,只要判断一下这两个节点是否有祖先,没有就很好办,直接随便连,比如: a 和 b ,他们都没有祖先(也就是祖先是自己),那么就可以 f[a]=b,如果 f[a]≠a 那么就让 b 的最老祖先(可能是自己)再往上多一个祖先 f[a],此时 b 的最老祖先也就是 aa 的最老祖先了(不一定是 a )。具体算法如下:

void unionn(int p,int q,int bj)
{
  int t,p1,q1;
  p1=find(p);//搜索p的祖先
  q1=find(q);//搜索q的祖先
  if(p1==q1) return;//祖先都相同了,退出
  t=father[p1]+father[q1];//两个集合的总人数,因为初始化时father[]全部都等于-1,利用abs求的集合的人数
  if(father[p1]>father[q1]) {father[p1]=q1;father[q1]=t;}
  else{father[q1]=p1;father[p1]=t;}
  //哪边人数多就令另外一边的祖先指向人数多的一边
}

有时候我们需要查某个节点的最老祖先是谁,主要是为了下面判断某两个节点的最老祖先是否相同而准备的。假如有这样一组关系:

https://i-blog.csdnimg.cn/blog_migrate/c1cbeecb9a9c9a5d47c8db986a382dab.png

比如我们要找 11 的最老祖先,那么怎么找呢?那么就要通过 33 来找,再往上找,这样的话代码就是:

int find(int f[], int x) 
{
    if(f[x]>0)return x;//如果是祖先,就返回最老祖先的值
    return find(f[x]);//不是最老祖先,那么就往上继续找
}

但是,这样做的话,会有很多重复工作。比如我刚才找 11 ,现在找 22 ,那么你就重复搜索了 22 。此时我们就需要压缩路径,以便后面查找时更便捷。压缩路径的话,就是在查找的时候,顺带把经过的节点的祖先直接指向最老祖先,那么后面找最老祖先的时候,就一步到位了。代码如下:

int find(int x) 
{
    if(f[x]>0)return x;//如果是祖先,就返回最老祖先的值
    return f[x]=find(f[x]);//不是最老祖先,那么就往上继续找,并令当前的x直接指向祖先,压缩路径
}

线性写法:

int find(int p)
{
 int i,j;
 i=p;
 while(father[i]>=0) i=father[i];//不是最老祖先,那么就往上继续找
 while(i!=p)
 {
  j=father[p];
  father[p]=i;
  p=j;
 }//将路径上的所有点全部指向祖先
return i;
}

这种方法十分巧妙,压缩路径之后,查找的速度也会快得多。

总结完毕,谢谢

转载于:https://www.cnblogs.com/kurlisjoey/p/10880721.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值