数据结构01-并查集

并查集(Union-Find)

基本定义:

并查集是指:对集合有“并”和“查”两种操作的一种数据结构;(这里的集合可以理解为无向图)

image-20221105213338279

并操作:将两个节点连通;

查操作:两个节点是否是连通的;

连通性:

自反性:p 与 q 连通 即 p 被与 q 连通

对称性:p 与 q 连通,则 q 与 p 连通

传递性:如果 p 连通 q,q 连通r,则 p 连通 r

连通子图:

相互连通的最大集合,例:

image-20221105214300593

实现操作:

对于一个有 N 个节点的并查集,可能有 M 个“并”或“查”的操作混合使用,实现操作的目的是当 N 和 M 非常大的时候仍让保持”并“和”查“高效,

思路1:快速查找

思路:使用大小为 N 的数组存储连通关系,当且仅当数组中的两个值相等时,其下标所指的节点是连通的;初始化时,每个节点的原始值即为本身;

例:

image-20221105215053780

image-20221105215039652

这种思路下,很容易进行查找操作,对于节点 i 和节点 j,只需要比较 id[i] 和 id[j] 的值,相等则连通,不相等则不连通,时间复杂度为 O(1)。

但在进行“并”操作时代价较高,如果要连接 p 节点和 q 节点,不仅需要将其将 q 点的值修改为 p 点的值,还需将所有与 q点 的值相等的节点的值都修改为 p 点的值,这需要对整个数组进行一次遍历,时间复杂度为 O(N)。

image-20221105215732878

思路2:快速合并

思路:这一思路采用树形结构来存储,同样使用大小为 N 的数组,但存储的是节点的父节点,初始化时,每个节点的原始值即为本身;

例:

image-20221105220558868

image-20221105220611012

这种思路下,很容易进行连接操作,对于节点 p 和节点 q,只需要将 q 的值修改为 p 的值(q的父节点为q)即可,时间复杂度为 O(1);

但在进行“查找”操作时代价较高,要判断两节点是否连通,需要判断两节点是否有相同的“根”

如要比较 2 号 和 3 号节点,需要先找到 2 的根节点:2 的父节点是 9, 9 的父节点是 9 本身,所以 2 的根节点为 9;再寻找3的根节点:3 的父节点是 4, 4 的父节点是 9, 9 的父节点是 9 本身,所以 3 的根节点也为 9;因此两者是连通的。

在寻找时,需要逐层向上寻找父节点,最坏情况下,如果整个并查集是一个深度为 N 的树,则时间复杂度达到 O(N)

改进思路1:加权快速合并

思路:快速合并的问题在于,树的深度过高会影响查找速度,那么可以通过优化树的高度来改进:

快速合并在进行连接操作时,如要将 p 和 q 连接,由于总是将 p 连接到 q,可能将更高的树父节点设置为更矮的树,这使得树的高度被进一步增高;

image-20221105222000514

如果能够记录树的深度,每次连接时都能将更矮的树父节点设置为更高的树,就能缩短树的高度。

image-20221105222514690

在实现时,需要额外增加一个数组,用于记录根为 i 的树中的节点数目,增加了一定的空间开销(空间换时间);

改进思路2:路径压缩

思路:仍然通过优化树的高度来改进:

路径压缩优化不在连接的时候进行,在查找操作时进行:当查询到某节点的根节点时,直接将节点的父节点设置为根节点,缩短了字树的高度,在下次查找时就能节省时间了;

例:查询 9 的根节点时,需要逐层查询 9、6、3、1 的父节点,其根节点都为 0,则将这些点的,父节点都设置为 0

image-20221105223637199

image-20221105223800468

这一操作使得树变得“扁平”,且不增加时间复杂度(只需要一个额外的赋值操作)

总结:时间复杂度分析

image-20221105224224396


数据结构01-并查集
https://username.github.io/2022/11/08/数据结构01-并查集/
作者
ZhuoRan-Takuzen
发布于
2022年11月8日
许可协议