島嶼問題#

經典題目:島嶼數量 (LeetCode 200)#

題目:給定二維網格,'1' 為陸地,'0' 為水域。計算島嶼數量。

規則:四連通(上下左右),對角線不算相連。

輸入:
1 1 0 0 0
1 1 0 0 0
0 0 1 0 0
0 0 0 1 1

輸出:3(三個島嶼)

解法一:染色法 (Flood Fill)#

核心思想#

發現一座島嶼時,將其「沉沒」(標記為已訪問),避免重複計算。

演算法步驟#

  1. 走訪整個網格
  2. 遇到 '1' 時,島嶼數量 +1
  3. 用 DFS/BFS 將整座島嶼標記為 '0'
DFS 實作
def numIslands(grid):
    if not grid:
        return 0

    rows, cols = len(grid), len(grid[0])
    count = 0

    def dfs(r, c):
        # 邊界檢查 + 是否為陸地
        if r < 0 or r >= rows or c < 0 or c >= cols or grid[r][c] != '1':
            return

        grid[r][c] = '0'  # 沉島

        # 四個方向擴散
        dfs(r - 1, c)
        dfs(r + 1, c)
        dfs(r, c - 1)
        dfs(r, c + 1)

    for r in range(rows):
        for c in range(cols):
            if grid[r][c] == '1':
                count += 1
                dfs(r, c)

    return count

方向陣列技巧:使用 dx = [-1, 1, 0, 0], dy = [0, 0, -1, 1] 簡化四方向走訪。

副作用:直接修改 grid 會破壞原始資料。若不允許修改,需使用額外的 visited 陣列。


解法二:並查集 (Union-Find)#

核心思想#

將相鄰的陸地合併到同一集合,最終集合數量即為島嶼數量。

演算法步驟#

  1. 初始化:每個 '1' 單獨成集
  2. 走訪網格:相鄰的 '1' 執行 Union
  3. 統計最終集合數量

關鍵實作#

class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))
        self.count = 0

    def find(self, x):
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x])  # 路徑壓縮
        return self.parent[x]

    def union(self, x, y):
        root_x, root_y = self.find(x), self.find(y)
        if root_x != root_y:
            self.parent[root_x] = root_y
            self.count -= 1  # 合併時計數減 1

座標轉換:2D 座標 (i, j) 轉為 1D 索引:i * cols + j


兩種解法比較#

特性Flood FillUnion-Find
時間複雜度O(M × N)O(M × N × α)
空間複雜度O(M × N) 遞迴堆疊O(M × N)
是否修改原圖是(可避免)
適用場景一次性查詢動態連通性

選擇建議

  • 簡單題目 → Flood Fill(程式碼簡潔)
  • 需要多次查詢連通性 → Union-Find

變體:朋友圈 (LeetCode 547)#

題目:N×N 鄰接矩陣表示 N 人的好友關係,求朋友圈數量。

本質:與島嶼問題相同,都是求連通分量數量。

差異

  • 島嶼問題:網格上的空間相鄰
  • 朋友圈:節點間的邏輯連接
def findCircleNum(M):
    n = len(M)
    uf = UnionFind(n)
    uf.count = 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