并查集基础

昨天用了一天时间学了并查集,在网上找了好多资料,也看了好多人写的,还在poj上刷了几道水题,才终于搞懂了并查集的一些基础内容,所以就做一些总结。在学习过程中刷了几道题,也做一点总结。

其实之前的数据结构课就讲过union-find算法,当时竟然没有察觉这就是并查集o(╯□╰)o!

先上定义: 并查集是一种树型数据结构,用于处理一些不相交集合(Disjoint Sets)的合并以及查询问题。常常在使用在以森林来表示。(Wiki)

(其实算法导论上就用了名为不相交集合数据结构的名字作为并查集那一部分的标题,当时看着隐隐有些蛋疼)

并查集的重点主要在于2部分:

1.查找元素所在森林的根节点(Find)。分布查找两元素,然后就可以判断它们是否属于一个集合。

在查找中,有个优化的技术点是路径压缩。这个等会结合代码讲。

2.合并两个不相交集合(Union)。

在合并中,也有个优化的技术点是按秩合并。这个也等会讲。

下面结合例子和代码来了解并查集。

例 犯罪团伙

• 警察抓到了n个罪犯,警察根据经验知道他们属于不同的犯罪团伙,却不能判断有多少个团伙,但通过警察的审讯,知道其中的一些罪犯之间相互认识,已知同一犯罪团伙的成员之间直接或间接认识。有可能一个犯罪团伙只有一个人。
•    请你根据已知罪犯之间的关系,确定犯罪团伙的数量。已知罪犯的编号从1至n。
•输入:
•第一行:n(<=10000,罪犯数量),
•第二行:m(<=100000,关系数量)
•以下若干行:每行两个数:I 和j,中间一个空格隔开,表示罪犯i和罪犯j相互认识。
•输出:一个整数,犯罪团伙的数量。
如:3
      2
      1 2
      1 3     输出的结果是1
 
这是一个很简单的例子,直觉上来做,就是先每个罪犯都是一个单独的团体,然后如果两个罪犯认识就把他们连起来,最后看形成了几片(森林),就是几个团伙了。
而在具体求解过程中,需要反复的用到查询一个元素所属的集合,并且将不同集合合并。这就是并查集的典型特征,适合用并查集来求解。
并查集一般可以用树型结构去存储,每棵树构成一个集合,多棵树构成森林。并查集还有一个典型特征就是每个集合都会有一个代表元,判断所属集合时作为关键特征,就是这棵树的根。
下面就是并查集的主要结构的代码:
上面也提到过并查集的主要部分就是union & find, 这里再加上一个初始化。
#Define father[x]: x的父节点
初始化函数  Make_Set(int x): 初始化时就是将每个元素的父节点设为自己,则其根节点也是自己(每个元素一个树)
void Make_Set(int x)
{
    father[x]=x;
}

查找函数: Find_Set(int x): 查找一个元素所属集合,找出它的根节点即可,这是这个集合的代表元

int Find_Set(int x)
{
    if(x!=father[x])
        return Find_Set(father[x]);
    else return x

合并函数: Union(int x,int y) 合并x,y所在的两个集合

void Union(int x,int y)
{
    x=Find_Set(x);
    y=Find_Set(y);
    if( x==y ) return;
    else father[x]=y;
}

示意图:

以上并查集的基本部分就大概说完了,然后就可以讨论一下并查集的两个优化

1. 在查找的时候用的路径压缩

从上面Find_Set代码中,我们知道查找使用的是递归,当元素排成一条链这样的极端情况时,查找可能就会很费时(O(n)). 所以我们可以再递归时就将所有这条路径上的节点全部直接指向跟节点,这样就可以压缩路径长度。

int Find_Set(int x)
{
    if(x!=father[x]) 
        return father[x]=Find_Set(father[x]);
    else return x;
}

2. 在合并时用的按秩合并
上面在合并的时候,我们直接就将一个节点的父节点给接到了另一个的下面,这样可能会带来树过高,这里可以采用按秩合并的方法,这里的秩实际上可以理解为一个树的高度。

使用秩合并还需要定义一个rank[x]

在Make_Set(x)中增加一句 rank[x]=0;//所有初始高度都是0。

然后修改Union函数

void Union(int x, int y)
{
    x=Find_Set(x);
    y=Find_Set(y);
    if(x==y) return;
    else{         
    if(rank[x]>rank[y])
        father[y]=x;
    else{
        father[x]=y;
        if(rank[x]==rank[y]) //当两个高度相同时,合并会使得y树高度增加1
            rank[y]++;
          }
    }
}

到此,并查集的基本内容就是那么多了。昨天做的题目是:3道普通并查集,1道种类并查集
poj 1611 poj 2524 poj2236; poj1182


POJ-1611

POJ-2524

 

POJ-2236

 

 前两道都是裸的并查集,比较简单、第3道加了一点计算几何。第4道是著名的食物链,很有难度。

 

转载于:https://www.cnblogs.com/soyscut/p/3250709.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值