島嶼問題 (Number of Islands)#

經典題目:島嶼數量 #

題目:給定二維網格,'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 實作
fun numIslands(grid: Array<CharArray>): Int {
    if (grid.isEmpty()) return 0

    val rows = grid.size
    val cols = grid[0].size
    var count = 0

    fun dfs(r: Int, c: Int) {
        // 邊界檢查 + 是否為陸地
        if (r < 0 || r >= rows || c < 0 || c >= cols || 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 0 until rows) {
        for (c in 0 until cols) {
            if (grid[r][c] == '1') {
                count++
                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(n: Int) {
    private val parent = IntArray(n) { it }
    var count = 0

    fun find(x: Int): Int {
        if (parent[x] != x) {
            parent[x] = find(parent[x])  // 路徑壓縮
        }
        return parent[x]
    }

    fun union(x: Int, y: Int) {
        val rootX = find(x)
        val rootY = find(y)
        if (rootX != rootY) {
            parent[rootX] = rootY
            count--  // 合併時計數減 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

變體:朋友圈 #

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

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

差異

  • 島嶼問題:網格上的空間相鄰
  • 朋友圈:節點間的邏輯連接
fun findCircleNum(m: Array<IntArray>): Int {
    val n = m.size
    val uf = UnionFind(n)
    uf.count = n  // 初始每人獨立

    for (i in 0 until n) {
        for (j in i + 1 until n) {
            if (m[i][j] == 1) {
                uf.union(i, j)
            }
        }
    }

    return uf.count
}