# [LeetCode] 200. Number of Islands (Medium) ###### tags: `LeetCode` ## 題目 給定一個 $m \times n$ 的二維矩陣`grid`,裡面只包含 `"0"`(代表水)和 `"1"`(代表陸地),計算這個矩陣上面島的數量。 所謂的島指的是四面環水,由水平或垂直的相鄰陸地連接而成的區域。 在這裡我們假設`grid`的四個邊緣都被水包圍。 ## 限制條件 * `m == grid.length` * `n == grid[i].length` * `1 <= m, n <= 300` * `grid[i][j]` is `"0"` or `"1"`. ## 範例 * Example 1: ```python 輸入: grid = [ ["1","1","1","1","0"], ["1","1","0","1","0"], ["1","1","0","0","0"], ["0","0","0","0","0"] ] 輸出: 1 ``` * Example 2: ```python 輸入: grid = [ ["1","1","0","0","0"], ["1","1","0","0","0"], ["0","0","1","0","0"], ["0","0","0","1","1"] ] 輸出: 3 ``` ## 解答 ### 基於DFS的解法 這題我們可以基於DFS來做。DFS的概念,是從一個起點 $v$ 開始,盡可能的遠離 $v$,直到無法再走下去時再回溯去找另一條尚未尋訪過的路徑。因此在尋訪時我們必須將已經尋訪過的頂點標記為visited(已尋訪),直到沒有任何未尋訪的頂點為止。 對於這道題目,我們必須分別尋訪每個島,並把尋訪過的陸地(`"1"`)標記為水(`"0"`)。 實作DFS的方法有兩種:遞迴和stack。 * 使用遞迴的解法 在這邊因為我們不確定要從哪邊開始尋訪,所以我們選擇迭代所有地圖上的點,找出合適的起點(也就是陸地),再從該點開始做DFS。用遞迴來實作DFS和樹的尋訪是很類似的,演算法的步驟如下: 1. 標記目前所在頂點為visited並對該節點進行操作(如print出節點的value) 2. 尋訪相鄰的頂點,在那些頂點上依序呼叫遞迴函式 使用遞迴的方式來解本題,其Python code如下: ```Python= class Solution: def numIslands(self, grid: List[List[str]]) -> int: # 檢查整片地圖的 size,如果橫排(row)數量是0則沒有島,直接回傳0 rows = len(grid) cols = len(grid[0]) if rows == cols == 1: return 1 if grid[0][0] == "1" else 0 def dfs(r, c): # 相當於標記為 visited grid[r][c] = "0" # 依序在相鄰的頂點呼叫遞迴函式 if r - 1 >= 0 and grid[r - 1][c] == "1": dfs(r - 1, c) if r + 1 < rows and grid[r + 1][c] == "1": dfs(r + 1, c) if c - 1 >= 0 and grid[r][c - 1] == "1": dfs(r, c - 1) if c + 1 < cols and grid[r][c + 1] == "1": dfs(r, c + 1) # 島的總數先初始化為 0 n = 0 for r in range(rows): for c in range(cols): if grid[r][c] == "1": # 發現新的島,把整個島尋訪一次 n += 1 dfs(r, c) return n ``` * 使用stack的解法 用stack來實作DFS的概念其實就是把前面的遞迴寫成迴圈。我們知道遞迴的背後就是一個function call的stack,這邊我們將相鄰的頂點放入stack裡面,再按照stack的後進先出順序進行尋訪。 ```python= class Solution: def numIslands(self, grid: List[List[str]]) -> int: if not grid: return 0 rows = len(grid) if rows == 0: return 0 cols = len(grid[0]) n = 0 for i in range(rows): for j in range(cols): if grid[i][j] == "1": n += 1 grid[i][j] = "0" stack = [(i, j)] while stack: r, c = stack.pop(-1) if r - 1 >= 0 and grid[r - 1][c] == "1": stack.append((r - 1, c)) grid[r - 1][c] = "0" if r + 1 < rows and grid[r + 1][c] == "1": stack.append((r + 1, c)) grid[r + 1][c] = "0" if c - 1 >= 0 and grid[r][c - 1] == "1": stack.append((r, c - 1)) grid[r][c - 1] = "0" if c + 1 < cols and grid[r][c + 1] == "1": stack.append((r, c + 1)) grid[r][c + 1] = "0" return n ``` ### 基於BFS的解法 我們當然也可以用BFS來解這題,只要把前面DFS的stack版本改成使用queue即可,其他的概念都和基於DFS的解法類似,不再多做說明: ```python= class Solution: def numIslands(self, grid: List[List[str]]) -> int: if not grid: return 0 rows = len(grid) if rows == 0: return 0 cols = len(grid[0]) n = 0 for i in range(rows): for j in range(cols): if grid[i][j] == "1": n += 1 grid[i][j] = "0" queue = [(i, j)] while queue: r, c = queue.pop(0) if r - 1 >= 0 and grid[r - 1][c] == "1": queue.append((r - 1, c)) grid[r - 1][c] = "0" if r + 1 < rows and grid[r + 1][c] == "1": queue.append((r + 1, c)) grid[r + 1][c] = "0" if c - 1 >= 0 and grid[r][c - 1] == "1": queue.append((r, c - 1)) grid[r][c - 1] = "0" if c + 1 < cols and grid[r][c + 1] == "1": queue.append((r, c + 1)) grid[r][c + 1] = "0" return n ```