图论6 并查集

主要操作

编辑 播报

初始化

把每个点所在集合初始化为其自身。

通常来说,这个步骤在每次使用该数据结构时只需要执行一次,无论何种实现方式,时间复杂度均为

查找

查找元素所在的集合,即根节点。

合并

将两个元素所在的集合合并为一个集合。

通常来说,合并之前,应先判断两个元素是否属于同一集合,这可用上面的“查找”操作实现。

例题

编辑 播报

题目描述

若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。

输入

第一行:三个整数

分别表示有

个人,

个亲戚关系,询问

对亲戚关系。

以下

行:每行两个数

,表示

具有亲戚关系。

接下来

行:每行两个数

,询问

是否具有亲戚关系。

输出

行,每行一个’Yes’或’No’。表示第

个询问的答案为“有”或“没有”亲戚关系。

分析问题实质

初步分析觉得本题是一个图论中判断两个点是否在同一个连通子图中的问题。对于题目中的样例,以人为点,关系为边,建立无向图如下:

比如判断3和4是否为亲戚时,我们检查3和4是否在同一个连通子图中,结果是在,于是他们是亲戚。又如7和10不在同一个连通子图中,所以他们不是亲戚。

用图的数据结构的最大问题是,我们无法存下多至(M=)2 000 000条边的图,后面关于算法时效等诸多问题就免谈了。

用图表示关系过于“奢侈”了。其实本题只是一个对分离集合(并查集)操作的问题。

例如样例:

9 7 1

2 4

5 7

1 3

8 9

1 2

5 6

2 3

1 9

我们可以给每个人建立一个集合,集合的元素值有他自己,表示最开始时他不知道任何人是它的亲戚。以后每次给出一个亲戚关系a, b,则a和他的亲戚与b和他的亲戚就互为亲戚了,将a所在集合与b所在集合合并。对于样例数据的操作全过程如下:

初始状态:{1} {2} {3} {4} {5} {6} {7} {8} {9}

输入关系 分离集合

(2,4) {2,4}{1} {3} {5} {6} {7} {8} {9}

(5,7) {2,4} {5,7} {1} {3} {6} {8} {9}

(1,3) {1,3} {2,4} {5,7}{6} {8} {9}

(8,9) {1,3} {2,4} {5,7} {8,9}{6}

(1,2) {1,2,3,4} {5,7} {8,9}{6}

(5,6) {1,2,3,4} {5,6,7} {8,9}

(2,3) {1,2,3,4} {5,6,7} {8,9}

判断亲戚关系

(1,9),因为1,9不在同一集合内,所以输出"NO"。

最后我们得到3个集合{1,2,3,4}、{5,6,7}、{8,9},于是判断两个人是否亲戚的问题就变成判断两个数是否在同一个集合中的问题。如此一来,需要的数据结构就没有图结构那样庞大了。

算法需要以下几个子过程:

(1) 开始时,为每个人建立一个集合FSY_ak_ioi(x);

(2) 得到一个关系a b,合并相应集合FSY_ak_noi(a,b);

(3) 此外我们还需要判断两个人是否在同一个集合中,这就涉及到如何标识集合的问题。我们可以在每个集合中选一个代表标识集合,因此我们需要一个子过程给出每个集合的代表元FSY_ak_csp(a)。于是判断两个人是否在同一个集合中,即两个人是否为亲戚,等价于判断FSY_ak_csp(a)=FSY_ak_csp(b)。

有了以上子过程的支持,我们就有如下算法。

PROBLEM-Relations(N, M, a1,…,aM, b1,…,bM, Q, c1,…,cQ, d1,…,dQ)

for i←1 to N

do FSY_ak_ioi(i)

for i←1 to M

do if FSY_ak_csp(ai) != FSY_ak_csp(bi)

then FSY_ak_noi(ai, bi)

for i←1 to Q

do if FSY_ak_csp(ci)=FSY_ak_csp(di)

then output “Yes”

else output “No”

解决问题的关键便为选择合适的数据结构实现并查集的操作,使算法的实现效率最高。

注意事项

本题的输入数据量很大,这使得我们的程序会在输入中花去不少时间。如果你用Pascal写程序,可以用库函数SetTextBuf为输入文件设置缓冲区,这可以使输入过程加快不少。如果你是用C语言的话,就不必为此操心了,系统会自动分配缓冲区。

单链表实现

编辑 播报

一个节点对应一个人&#

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值