并查集(Disjoint-set)详解(附图片例子)

并查集基本思路

学习任何一个数据结构之前,都应该先定义其有哪些操作。并查集也是一种数据结构,因此,俺准备定义一下它有哪些操作。(P.S. 俺假设看这篇文章的童鞋至少高中毕业,都知道数学中的集合是个什么概念,嘿嘿)

1、初始化(initialize)操作把集合中的每个元素看作是一个子集合

2、合并(Union)操作:把2个子集合,合并成一个集合

3、查找(Find)操作:判断2个元素是否在同一个子集合中

俺举例说明上面的3个操作。假设现在有10个人,他们构成一个集合,每个人代表集合中的一个元素。俺用0表示第1个人,1表示第2个人,以此类推。因此,俺就可以用一个大小为10的数组,Arr,表示这10个人。如下图所示。黑体数字是数组的下标。

并查集

初始化操作就是将这10个人中的每个人看成是一个子集合。

人类是初会性动物,我们每个人都需要与他人建立关系,来分享自己的喜怒哀乐,寻求幸福。经过初始化以后,每个人之间是没有任何关系的。当我们需要建立关系时,我们就用到了合并操作。比如,1号小朋友想和2号小朋友一起玩耍,我们用 Union(2, 1) 操作建立关系。过了一段时间以后,一些小朋友和另一些小朋友就建立起了关系(这种关系有可能是间接的,比如A认识B,B认识C,我们也可以认为A与C有关系)。

实际上,你可以把它看成一种无向图结构。起初,每个结点是孤立的。然后,结点与结点之间开始建立关系。假设建立起了以下几种关系:

Union(2, 1),Union(4, 3),Union(8, 4),Union(9, 3),Union(6, 5)

如下图所示:

并查集

数组就变成了下图的样子。下面数组的表示正是并查集数据结构用到的技巧。俺来解释一下。经过了上面的合并操作,我们得到了下面5个子集合:

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

集合中的每个元素都已经建立了关系,无论是直接的还是间接的。从下面的数组可以看到,每个集合中元素对应的数组值是相同的,这表示它们属于一个集合。比如,集合 {3, 4, 8, 9},它们每个对应的数组值为3.

并查集

查找操作就是来判断2个元素之间是否属于同一个子集合,这样就可以知道2个元素之间是否存在关系了。

时间复杂度

有了上面的数组结构,查找操作是很快的。假设给定2个元素A与B,只需判断 Arr[A] == Arr[B] 是否相等,就知道它们是否有关系。因此时间是 O(1)。但对于合并操作来说,时间复杂度就是 O(N^2) 了。因此,我们需要稍微改进一下数据结构的组织方式。

并查集改进

改进的并查集结构与上面的类似,只不过它引入了一个 root 结点的概念,转换了一下存储方式。

举个例子。假设一个集合包含5个元素,初始化后的数组结构如下图所示。与没改进前的方式是一样的。

并查集

假设我们经历了几个合并操作,得到了3个子集合,{0, 1, 2},{3, 4},{5}。结果如下图所示。没改进之前,每个子集合中的元素,在数组中对应的值都是一样的。但现在,有了一种递进的层级关系,与树结构一样。比如 {0, 1, 2} 这个子集合。通过元素1(数组下标1),你会找到元素0(数组下标1中所存储的值),以此类推,最终你会找到 root 元素。这个结构与操作系统中的文件系统的 File Allocation Table(FAT) 实现是类似的。

判断一个元素是否为 root 元素非常简单。假设给定一个元素E, 只需判断 Arr[E] == E 是否相等就可以。

并查集

时间复杂度

假设俺想对上图中的元素1和元素4执行合并操作,俺首先需要“顺藤摸瓜”地找到元素1和元素4分别对应的 root 元素。找到以后,可以在 O(1) 时间内完成合并。合并操作实际上就是把元素(下标)2对应的值改成4就可以了,如下图所示:

并查集

查找操作只需要判断2个元素是否有同一个 root 元素。因此,查找过程也需要先找到给定元素的 root 元素。

因此,在最坏情况下,合并与查找是接近线性时间的。

从上图可以看到,线性时间正是由于我们没有追踪每个集合中元素的数量,导致合并到最后产生一个极度不平衡的树结构。

进一步改进

为了得到一个相对平衡的树结构,我们还需要一个额外的数组,size,来追踪每个子集合中元素的数量,然后把元素少的子集合合并到元素多的集合中。假设一个包含6个元素的集合,它的 size 数组的初始化结构如下所示。由于刚初始化后的集合,每个子集合只有1个元素,所以 size 数组的值都是1.

并查集

下面举个例子。假设合并元素0和元素1,合并结果如下图,注意 size 数组的变化。

并查集

然后,我们合并元素1和2. 结果如下图所示。以这种方法合并,最终会得到一个平衡的树。因此,无论查找还是合并操作,时间复杂度将从 O(N) 降低到 O(log N).

并查集

 

参考资料

Basics of Disjoint Data Structures

Disjoint-set data structure

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值