数据结构之并查集(Union/Find)

1、概念

并查集是一种用于处理一些不相交集合(Disjoint Sets)的合并及查询等问题的树形数据结构

下面我们举个例子来引入对并查集的理解。

大家都看过古惑仔吧,在香港呢分布着很多的帮派,而每个帮派都有一个老大,比如什么洪兴老大蒋天生,铜锣湾陈浩南。各个帮派人数众多,小弟们通过大哥们相连,而大哥的大哥的最终大哥就是共同的老大,所以拐了很多个弯最终还是同个帮派的人,由共同的老大连接着,这些众多的帮派的原理也就是我们所说的并查集。而某两个古惑仔有了矛盾,而不知道对面是哪个帮派的不敢下手,毕竟别打了自己人,所以就需要确认一下, 报出自己直接上级的大哥肯定不稳妥,毕竟一个帮派的大哥有那么多,而帮派老大只有一个,只要看看老大是不是一样就知道是不是属于同一个帮派啦,这里一层层地上溯寻找最终老大的过程就是查找。若某天,一个帮派的老大很佩服另外一个帮派的老大, 自愿做了另外一个老大的小弟(虽然几率很小),这样两个帮派就成了一家人,无论小弟有多少,之后都共同信奉一个老大,这就是合并的过程。

2、基本操作

顾名思义,并查集包含两种基本操作:

(1)查找(Find)。查找元素所在的集合即根节点。

(2)合并(Combine)。先判断两个元素是否在同一集合,若否,则将 两个集合合并。

下面给出代码帮助理解:

int  pre[1050];//pre数组用于记录根节点

void Init(int n)   //初始化结点,将根节点的pre[i]置为它本身,n为节点数
{
    for(int i=0;i<=n;i++)
        pre[i]=i;
}

int Find(int x)
{
    int r=x;
    while(r!=pre[r])//判断是否为根节点
        r=pre[r];
    return r;//返回根节点
}

void Combine(int x,int y)
{
    int fx=Find(x),fy=Find(y);
    if(fx!=fy)//若根节点不同
    {
        pre[fy]=fx;//将其中一个置为另一个的根节点,实现合并
    }
}

3、优化

再用刚才的例子,如果小弟们都很喜欢做大哥,小弟又收了小弟,这样一层一层收下去,很容易造成关系复杂,最底层的小弟需要上溯很多层才能找到最后的老大,因此,我们优化一下帮派的结构,将除了老大的所有人都置为老大的小弟,这样就 实现了优化。

4、例题及代码

HDU-1213-How Many Tables

题目里的桌子数其实就是我们介绍的帮派数,判断多少个根节点就得出了所需的桌子数。下面给出代码及注释

#include <iostream>
#include <stdio.h>
#include <cstring>
using namespace std;

int  pre[1050];//pre数组用于记录根节点
int t[1050]={0};//用于判断是否为根节点
void Init(int n)   //初始化结点
{
    for(int i=0;i<=n;i++)
        pre[i]=i;
}

int Find(int x)
{
    int r=x;
    while(r!=pre[r])//判断是否为根节点
        r=pre[r];

    int i=x,j;
    while(pre[i]!=r)//优化过程,最终pre[i]都指向根节点
    {
        j=pre[i];
        pre[i]=r;
        i=j;
    }
    return r;//返回根节点
}

void Combine(int x,int y)
{
    int fx=Find(x),fy=Find(y);
    if(fx!=fy)//若根节点不同
    {
        pre[fy]=fx;//将其中一个置为另一个的根节点,实现合并
    }
}

int main()
{
    int n,N,M,i,a,b,ans;
    scanf("%d",&n);
    while(n--)
    {
        scanf("%d%d",&N,&M);
        Init(N);

        while(M--)          //吸收并整理数据
        {
            scanf("%d%d",&a,&b);
            Combine(a,b);
        }


        memset(t,0,sizeof(t));
        for(i=1;i<=N;i++)          //标记根结点
        {
            t[Find(i)]=1;//同时实现优化
        }
        for(ans=0,i=1;i<=N;i++)//ans记录所需桌子数
            if(t[i])
                ans++;

        printf("%d\n",ans);

    }
    return 0;
}

同类例题:

HDU-1232-畅通工程

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值