并查集和集合

引入

在修建道路时,为了让尽可能多的点连通,需要修建连通两个点的公路,这就需要随时询问两个点是否已经连通。

若将已经连通的点看作一个集合,那么修建一条公路的意义,就是合并两个集合,所以如何快速查询两个点是否属于同一个集合,以及快速地合并两个集合,十分重要。

集合

集合与并查集的概念

集合是由一个或多个确定的元素所构成的整体。集合中的元素有如下 三个特征:

  1. 确定性:一个元素要么属于集合,要么不属于集合。

  2. 互异性:集合中的元素互不相同。

  3. 无序性:集合中的元素没有先后顺序。

并查集是一个可以维护集合的数据结构,它能高效支持集合的基本操作:

  1. 合并两个集合。
  2. 查询两个指定元素是否属于同一个集合。

需要注意的是,由于计算机存储结构的限制,并查集维护的集合是离散意义下的集合,而不是广义的集合。集合中的元素是有限的。

集合的存储

  1. 数组存储
    存储一个集合最简单的方式就是直接用数组。我们将一个集合的所有元素按某种特定顺序存储在数组里。使用数组存储集合,可以支持较丰富的集合操作,但是维护集合的时间复杂度较高,对于几乎所有操作,单次操作的时间复杂度都是和集合大小成正比的。

    在C++语言中, 我们可以使用STL中的vector来实现数组存储集合。

  2. 链表存储
    可以模仿数组存储方式,将集合中的元素存储在一个链表里。链表一大好处是可以避免元素的复制,这对于合并操作是比较有帮助的,在定位元素所在的集合的两端后,直接将两个集合的端点相接即可合并完成。

    然而,使用链表维护集合的最坏情况时间复杂度仍然是与集合大小成正比的,在时间效率上还不够优秀。

    在 C++ 语言中,我们可以使用 STL 中的 list 来实现链表存储集合。如果题目对时间效率的要求较高,也可以选择自行实现链表。

    值得一提的是,假设元素总数是 nn,且仅需要支持合并和查询操作,那 么上述两种方式可以采用启发式合并的技术(即每次将较小集合合并入较大集合并修改较小集合所有元素的信息)做到总时间复杂度 O(n\log_2 n)O(nlog2​n)。虽然 单次操作的时间复杂度较高,但是可以证明总时间复杂度是可以接受的。

  3. 森林存储
    这就是并查集。

并查集

因为一个元素只可能属于一个集合,所以我们可以为每一个集合选取一个代表元。于是查询两个元素是否属于同一个集合实际上就是询问两个元素所在集合的代表元是否相同。这个询问的时间复杂度可以利用数组标记优化为 O(1)O(1)。

但是合并两个集合时需要改变其中一个集合中所有元素的代表元,时间复杂度仍然非常高,如何优化呢?

注意到,合并操作的时间复杂度远高于查询操作的复杂度,这启发我们通过一定的方式,提高查询操作的复杂度,降低合并操作的复杂度。

我们并不需要 O(1)O(1) 知道每个元素所属集合的代表元,这启发我们用森林来维护代表元。用森林中的一棵树代表一个集合,树根为对应集合的代表元。

这样,对于每棵树上的元素,查询其代表元时,时间复杂度与树的高度成正比。

对两个集合进行合并操作时,只需将其中一个集合的代表元(树根)指向另一个集合(树)的代表元即可。时间复杂度也与树的高度成正比。

这就是并查集。

并查集是一种树形的数据结构,顾名思义,它用于处理一些不交集的 合并 及 查询 问题。它支持两种操作:

  • 查找(Find):确定某个元素处于哪个子集;
  • 合并(Union):将两个子集合并成一个集合。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10247D

我会继续努力,信息技术会越好

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值