并查集(Union-Find)

990. 等式方程的可满足性

130. 被围绕的区域

128. 最长连续序列

 

顾名思义,并查集是来解决图的连通性问题

所以,并查集主要就是实现以下接口:

存储数据结构

如何表示节点与节点之间的连通性关系呢??

用数组parent[]来表示这种关系

Union 方法

介绍了存储的数据结构,那如何把两个节点连接起来呢??

很简单,只需将其中任一一个节点的根节点指向另一个节点的根节点即可

23

现在的问题就变成了如何快速找到某一个节点的根节点!!

刚刚介绍数据结构的时候,强调了根节点的特点,即自己指向自己

如何,是不是很简单,哈哈哈哈哈哈

connected() && count()

这两个方法的实现很简单

count()需要维护一个全局变量,来记录图的连通分量的数量

另外,我们需要明确的是:只有在调用union()方法时,才可能改变连通分量的数量

瓶颈分析

行文至此,已经把并查集的所有接口实现。但这远远不够,因为此时的代码还不完美,时间复杂度可能会很高

分析上述实现的方法,find()是决定并查集时间复杂度的重要因素。抛开find()因素,其他方法的时间复杂度均可视为O(1)。所以如果要优化算法的时间复杂度,需要从find()入手

对于有 n 个节点 1 个连通分量的并查集来说,最坏的时间复杂度为O(n),最好的时间复杂度为O(1)

优化角度 1:平衡性优化

思路:当我们每次连接两个节点的时候,不希望出现头重脚轻的情况,而希望到达一种平衡的状态

使用额外的一个数组size[]记录每个连通分量中的节点数,每次均把节点数少的分量接到节点数多的分量上,如图

25

注意:只有每个连通分量的根节点的 size[] 才可以代表该连通分量中的节点数

优化角度 2:路径压缩

思路:使树高始终保持为常数

上面是用迭代实现的「路径压缩」,下面给出一种用递归实现的「路径压缩」,其效率更高!

递归直接一次性把一棵树拉平了!!(强力推荐使用这种方法!!!✨✨✨)

注意:

完整模版

实战题目

题目 1: 被围绕的区域

题目详情可见 被围绕的区域

这个题目可以用「DFS」,也可以使用「并查集」去解决,这篇文章给出并查集的解决方法。想要了解「DFS」的方法,可见 秒杀所有岛屿题目(DFS)

1

题目 2: 最长连续序列

题目详情可见 最长连续序列

注意:size 别写反了!!!!🩸的教训

亮点