返回

LeetCode 題目下如何巧妙應用 Union-Find 算法?

见解分享

Union-Find 算法:图论中的连通性神器

Union-Find 算法概述

在图论中,Union-Find 算法是一种强大的工具,用于动态维护图中元素的连通性信息。它包含两个基本操作:

  • union(a, b) :合并两个元素 ab 所在的集合。
  • find(a) :返回元素 a 所在集合的代表元素。

应用场景

Union-Find 算法在解决以下问题时特别有用:

  • 检测两个元素是否属于同一集合。
  • 寻找某个集合的所有元素。
  • 计算图中连通分量的数量。
  • 构造最小生成树。

LeetCode 题目中的应用

岛屿数量

给定一个由 0(水)和 1(陆地)组成的网格,计算网格中岛屿的数量。

我们可以使用 Union-Find 算法轻松解决这个问题。首先,将每个陆地元素视为一个单独的集合。然后,检查每个陆地元素的上下左右相邻元素,如果相邻元素也为陆地,则将它们合并到同一个集合中。最后,统计集合的数量,即可得到岛屿的数量。

朋友圈

给定一个 n x n 的矩阵,其中 M[i][j] 表示人 i 和人 j 是否是朋友。计算朋友圈的数量。

同样,我们可以使用 Union-Find 算法解决这个问题。首先,将每个人视为一个单独的集合。然后,检查每个 M[i][j]1 的情况,如果人 i 和人 j 是朋友,则将他们合并到同一个集合中。最后,统计集合的数量,即可得到朋友圈的数量。

代码示例

岛屿数量

def num_islands(grid):
    """
    :type grid: List[List[str]]
    :rtype: int
    """
    if not grid:
        return 0

    m, n = len(grid), len(grid[0])
    uf = UnionFind(m * n)

    for i in range(m):
        for j in range(n):
            if grid[i][j] == '1':
                # 将当前陆地与上下左右相邻陆地合并
                if i > 0 and grid[i - 1][j] == '1':
                    uf.union(i * n + j, (i - 1) * n + j)
                if j > 0 and grid[i][j - 1] == '1':
                    uf.union(i * n + j, i * n + j - 1)
                if i < m - 1 and grid[i + 1][j] == '1':
                    uf.union(i * n + j, (i + 1) * n + j)
                if j < n - 1 and grid[i][j + 1] == '1':
                    uf.union(i * n + j, i * n + j + 1)

    return uf.count

class UnionFind:
    def __init__(self, n):
        self.parent = [i for i in range(n)]
        self.count = n

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

    def union(self, p, q):
        root_p = self.find(p)
        root_q = self.find(q)
        if root_p != root_q:
            self.parent[root_p] = root_q
            self.count -= 1

朋友圈

def find_circle_num(M):
    """
    :type M: List[List[int]]
    :rtype: int
    """
    if not M:
        return 0

    n = len(M)
    uf = UnionFind(n)

    for i in range(n):
        for j in range(i + 1, n):
            if M[i][j] == 1:
                uf.union(i, j)

    return uf.count

class UnionFind:
    def __init__(self, n):
        self.parent = [i for i in range(n)]
        self.count = n

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

    def union(self, p, q):
        root_p = self.find(p)
        root_q = self.find(q)
        if root_p != root_q:
            self.parent[root_p] = root_q
            self.count -= 1

常见问题解答

  1. Union-Find 算法的复杂度是多少?

    • find 和 union 操作的平均复杂度为 O(α(n)),其中 α(n) 是反阿克曼函数,增长极其缓慢。
  2. Union-Find 算法适用于稀疏图吗?

    • 适用于稀疏图,因为稀疏图中并不会频繁发生合并操作,可以将平均复杂度降至 O(1)。
  3. Union-Find 算法是如何优化存储空间的?

    • 通过路径压缩技术,将每个元素的父元素直接指向集合的根节点,从而优化存储空间。
  4. Union-Find 算法有哪些变种?

    • 加权并查集:根据元素的权重优化合并操作,以降低复杂度。
    • 路径分裂并查集:通过路径分裂技术进一步降低路径压缩后的树的高度。
  5. 在实际应用中,Union-Find 算法有什么优势?

    • 能够快速高效地处理动态连通性问题,例如社交网络中的好友分组、网络流量分析中的连通分量检测等。