并查集(Union-Find)算法介绍

                  并查集

1.并查集是一种树型的数据结构,用于处理一些不相交集合的合并问题。

并查集的主要操作有

1.合并两个不相交集合(Union(int x,int y))

2.判断两个元素是否属于同一个集合(Find(int x))

3.路径压缩

定义一个数组father,father[i]表示元素i的父亲结点.

初始化每个集合只有其本身(编号从1-N):

  for (int i=1; i<=n; i++)
        {
            father[i]=i;
        }
即对于节点 i ,它的组号也是 i。
合并操作(Union(int x,int y)),father[i]=j,表示j是i的父亲(即i,j属于同一集合)





查找操作(Find(int x))

查找两个元素是否属于同一集合(father[x]=x表示该元素没有合并过,自身为一个集合)

int Find(int x)   //查找x所属集合
  {
    while (x!=Father[x]) 
    {
        x=Father[x];
    }
    return Father[x];
  }

根据上面就可以写成合并操作了

void Union(int x,int y)
{
    x=Find(x);   //查找所属集合
    y=Find(y);
    if(x!=y)   //不在同一个集合
    {
        Father[x]=y;  //合并
    }
}

又如:


上面所有元素最终形成三个集合。


合并操作的改进

最上面的Union的执行是相当任意的,它通过使得第二课棵树的子树而完成合并,对其进行简单的改进是借助任意的方法打破现有的关系,使得总让较小的树成为较大的树的子树,这种方法叫做按大小求并。

进行一次任意的并的结果(分别Union(5,6),Union(7,8),Union(5,7)  后Union(4,5)):


按大小求并的结果:



即总是size小的树作为子树和size大的树进行合并。这样就能够尽量的保持整棵树的平衡。

在初始情况下,每个组的大小都是1,因为只含有一个节点,所以我们可以使用额外的一个数组来维护每个组的大小,对该数组的初始化也很直观:

for (int i = 0; i < N; i++)
{
    sz[i] = 1;    // 初始情况下,每个组的大小都是1
}
而在进行合并的时候,会首先判断待合并的两棵树的大小,然后按照上面图中的思想进行合并,实现代码:
void Union(int x, int y)
{
    int i = find(x);
    int j = find(y);
    if (i == j) return;
    // 将小树作为大树的子树
    if (sz[i] < sz[j])
    {
        Father[i] = j;
        sz[j] += sz[i];
    }
    else
    {
        Father[j] = i;
        sz[i] += sz[j];
    }
}

形象化(上面的文字及此图来自 这里):



另一种实现方法为按高度求并,我们跟踪每棵树的高度而不是大小并执行那些Union使得浅点的树成为高点的树的子树,因为只有两棵相同深度的的树求并时树的高度才增加(此时树的高度增加1)。

void Union(int x,int y)
{
    if(height[x]==height[y])
    {
        height[x]=height[x]+1;
        Father[y]=x;
    }
    else if(height[x]<height[y])
    {
        Father[y]=x;
    }
    else
    {
        Father[x]=y;
    }
}

路径压缩

通过 路径压缩使得结点离根更加的近,一个未采用路径压缩的例子:



路径压缩后:


这样采用路径压缩后使得Find更加的高效。

实现代码:

int Find(int x)
{
   if(x!=Father[x])
   {
       Father[x]=Find(Father[x]);   //递归找根进行更新
   }
    return Father[x];
}

点这里并查集应用

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值