三行并查集

问题的提出

给定一个大小为n的集合{a, b, c, f, ...},其中存在k <= n个连通块,将该集合分成k个小的子集。
给定两个元素ij,如何在尽可能高效地进行以下两个操作

  • 判断两个元素是否在同一个连通块(即两元素连同)
  • 将两个元素所在的连通块连载一起,形成一个连通块

问题的解决

并查集,能在均摊O(1)的时间复杂度内解决上面的问题。
使用C++最简易的实现如下:

Init(n) { for(int i = 0; i < n; ++i) root[i] = i;}
Find(x) { return x == root[x] ? x : root[x] = find(root[x]); }
Union(x, y) {root[find(x)] = find(y); }

技巧一:路径压缩

如下的代码实现了路径压缩操作。

Find(x) { return x == root[x] ? x : root[x] = find(root[x]); }

[!question] 怎么理解路径压缩?
针对集合{1,2,3,4,5,6},若存在两个连通块:{1,2,3,4}{5,6}。其中{1,2,3,4}的存储形式是

graph RL 4 --> 3 --> 2 --> 1

当调用Find(4)时,调用过程如下,

1<-2<-3<-4
F(4) := root[4] = F(3) = 1
F(3) := root[3] = F(2) = 1
F(2) := root[2] = F(1) = 1
F(1) := 1

最终,连通块会变成下面这个样子

graph RL 4 --> 1 3 --> 1 2 --> 1

技巧二:按秩归并

秩,是指连通块的大小。
TODO

完整代码

class UnionFind:
    def __init__(self, n: int):
        # 一开始有 n 个集合 {0}, {1}, ..., {n-1}
        # 集合 i 的代表元是自己,大小为 1
        self._fa = list(range(n))  # 代表元
        self._size = [1] * n  # 集合大小
        self.cc = n  # 连通块个数

    # 返回 x 所在集合的代表元
    # 同时做路径压缩,也就是把 x 所在集合中的所有元素的 fa 都改成代表元
    def find(self, x: int) -> int:
        # 如果 fa[x] == x,则表示 x 是代表元
        if self._fa[x] != x:
            self._fa[x] = self.find(self._fa[x])  # fa 改成代表元
        return self._fa[x]

    # 判断 x 和 y 是否在同一个集合
    def is_same(self, x: int, y: int) -> bool:
        # 如果 x 的代表元和 y 的代表元相同,那么 x 和 y 就在同一个集合
        # 这就是代表元的作用:用来快速判断两个元素是否在同一个集合
        return self.find(x) == self.find(y)

    # 把 from 所在集合合并到 to 所在集合中
    # 返回是否合并成功
    def merge(self, from_: int, to: int) -> bool:
        x, y = self.find(from_), self.find(to)
        if x == y:  # from 和 to 在同一个集合,不做合并
            return False
        self._fa[x] = y  # 合并集合。修改后就可以认为 from 和 to 在同一个集合了
        self._size[y] += self._size[x]  # 更新集合大小(注意集合大小保存在代表元上)
        # 无需更新 _size[x],因为我们不用 _size[x] 而是用 _size[find(x)] 获取集合大小,但 find(x) == y,我们不会再访问 _size[x]
        self.cc -= 1  # 成功合并,连通块个数减一
        return True

    # 返回 x 所在集合的大小
    def get_size(self, x: int) -> int:
        return self._size[self.find(x)]  # 集合大小保存在代表元上

# 作者:灵茶山艾府
# 链接:https://leetcode.cn/discuss/post/mOr1u6/
# 来源:力扣(LeetCode)
# 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
posted @ 2025-08-20 00:24  0xxd  阅读(6)  评论(0)    收藏  举报