数据结构: 并查集

915 字
5 分钟
数据结构: 并查集

引入#

并查集 (Disjoint Sets) 是一种用于高效管理不相交集合的数据结构,常用于处理动态连通性问题,例如判断两个元素是否属于同一集合、合并集合等。

并查集支持以下三种基本操作:

  1. 初始化 (MakeSet): 为每个元素创建一个独立集合。
  2. 查找 (Find): 查找某个元素所属集合的代表 (根节点)。
  3. 合并 (Union): 将两个元素所在的集合合并为一个。
Warning

并查集无法以较低复杂度实现集合的分离。

接下来我们介绍并查集的基础实现进阶实现

基础实现#

基础实现只需维护一个parent数组。

初始化#

初始时, 每个元素都位于一个单独的集合, 表示为一棵只有根节点的树。如果一开始有n个元素, 那我们就创建长度为nparent数组, 并且parent[i] = i

Note

在并查集的实现中为了方便一般将根节点的父亲设为自己。

def __init__(self, n):
self.parent = list(range(n))

查找#

查找某个元素属于哪个集合, 这个问题放到树中就是从这个节点就是不断向上, 直到找到根节点。 为了减少后续查找时向上寻找的开销, 可以在查找过程中进行路径压缩, 降低树的高度。 具体来说, 首先沿着节点不断向上, 找到根节点, 然后再将路径上的所有节点的父亲设为根节点。

查询时进行路径压缩
查询时进行路径压缩

def find(self, x):
old = x
ancestor = parent[x]
while ancestor != x:
x = ancestor
ancestor = parent[x]
x = parent[old]
while ancestor != x:
parent[old] = ancestor
old = x
x = parent[x]
return ancestor

合并#

合并两个不相交的集合, 统一选择将左边树的根节点连到右边树的根节点。

def union(self, x, y):
rootX = self.find(x)
rootY = self.find(y)
if rootX != rootY:
self.parent[rootX] = rootY # 合并集合

进阶实现#

在基础版合并的实现中, 我们固定将右侧的树的根节点作为新树的根节点, 这并不合理。 如果左边的树更大, 那么就会有更大概率会多向上访问一次, 在上面的例子中, 左树有 5 个节点, 而右树有 3 个节点, 将右树的根作为新树的根节点会导致我们有 58\frac{5}{8} 的节点在查找时需要多向上访问一次。 那么看来如果某一边的树更大 (节点数更多), 更适合选择其根节点作为新树的根节点。

一般的做法是追踪树的深度, 而不是追踪树的节点数, 我们要引入rank数组, 表示树的高度, 这个方法叫按秩分配。

初始化#

初始时, 每个树的高度为 0。

def __init__(self, n):
self.parent = list(range(n))
self.rank = [0] * n

查找#

依然是带路径压缩的查找, 但是使用的是更加优雅的递归式写法。

def find(self, x):
if self.parent[x] != x:
self.parent[x] = self.find(self.parent[x])
return self.parent[x]

合并#

根据rank绝对选择哪个树的根节点作为新树的根节点, 当两棵树的树高一致的时候, 合并时会改变树高。

def union(self, x, y):
rootX = self.find(x)
rootY = self.find(y)
if rootX == rootY:
return
# 按秩合并
if self.rank[rootX] < self.rank[rootY]:
self.parent[rootX] = rootY
else:
self.parent[rootY] = rootX
if self.rank[rootX] == self.rank[rootY]:
self.rank[rootX] += 1

TIPs#

  1. 如果获得集合的个数?
    • 方法一是初始化集合数为n, 当每次union的时候减一;
    • 方法二是数根节点的数目
    def get_count(self, first, last):
    count = 0
    while first != last:
    if self.parent[first] == first:
    count += 1
    first += 1
    return count

参考#

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!

赞助
数据结构: 并查集
https://llm-tech.com.cn/posts/disjoint-sets/
作者
Ming
发布于
2026-03-20
许可协议
CC BY-NC-SA 4.0
随机文章 随机推荐
Profile Image of the Author
Ming
你是来找 Ming 学习的吗
🎉 欢迎来到 Ming 的博客
这里是我的个人博客,分享 AI Infra、LLM 等技术内容。欢迎关注交流!
分类
标签
站点统计
文章
1
分类
1
标签
2
总字数
1,017
运行时长
0
最后活动
0 天前

目录