# 刷題筆記 - LeetCode 200. Number of Islands
https://leetcode.com/problems/number-of-islands
## Thoughts
這題是要找出總共有多少個島嶼,如果是島嶼的話會是 `"1"` 表示,而 `"1"` 上下左右如果也有 `"1"` 的話,代表它們是同一個島嶼。
Example 2:
```
Input: grid = [
["1","1","0","0","0"],
["1","1","0","0","0"],
["0","0","1","0","0"],
["0","0","0","1","1"]
]
Output: 3
```
題目中的例子可以看出 `(2, 2)` 為 `"1"`,但 `(1,2), (3,2), (2, 1), (2, 3)` 皆是 `"0"`,這個 `"1"` 不與其他 `"1"` 相連,構成一個獨立島嶼。
問題在我們必須遍歷所有的元素,如果遇到 `"1"` 就代表我們遇上了島。
但不能單純遇到 `"1"` 就當成 1 個島,必須確認這個 `"1"` 是否與其他的 `"1"` 相連。
所以問題是 -
1. 可以雙層迴圈遍歷,但要怎麼確認上下左右也是 `"1"`
2. 如果確認上下左右是 `"1"`,勢必會有重疊的部分,要怎麼記錄
## Solution 1 - DFS, Recursion
### Logic
我們可以寫一個遞迴的 function,訪問每個點後,再去訪問這個點的上下左右四個點。
但遇到邊界、或是遇到 `"0"` 的時候,就該停止訪問。
而如果我們遍歷每個點,然後訪問島的上下左右,極有可能我們會重複訪問到同一個點。但我們已經檢查過上下左右的點,我們不會想再檢查一次,因此必須將訪問過的點做標記。而前述停止訪問的條件,也要考慮現在這個點是不是我們已經走過的點。
遞迴的 function 寫好後,我們要怎麼計算島嶼數量?
從第一個點開始,判斷 `(0, 0)` 是否為 `1`,先假設`(0, 0)` 是單獨一個島,附近都是水
→ 判斷是否為 `"1"`,執行 `dfs(0, 0)`
→ 檢查 `(0, 0)` 的上下左右,也就是 `(-1, 0), (1, 0), (0, -1), (0, 1)`
→ 執行 `dfs(-1, 0)` → 超過邊界,返回
→ 執行 `dfs(1, 0)` → `(1, 0) == "0"`,返回
→ 執行 `dfs(0, -1)` → 超過邊界,返回
→ 執行 `dfs(0, 1)` → `(0, 1) == "0"`,返回
→ 在這個 function 中確認從這個點開始,可以繼續往上下左右訪問的話,就先做標記,
這邊可以令 `(0, 0)` 等於 `"2"` (or `"0"`, 看個人喜好)
全部執行完後返回,這時島嶼數量就可以 +1,再去走下一個點 `(0, 1)`
### Code
```python=
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
if not grid:
return 0
rows = len(grid)
cols = len(grid[0])
islands = 0
def dfs(i, j):
if (i < 0 or i > rows - 1 or j < 0 or j > cols - 1
or grid[i][j] != "1"):
return
grid[i][j] = "2"
dfs(i-1, j)
dfs(i+1, j)
dfs(i, j-1)
dfs(i, j+1)
for i in range(rows):
for j in range(cols):
if grid[i][j] == "1":
dfs(i, j)
islands += 1
return islands
```
- 首先取得島嶼的列數與行數 (`rows` & `cols`)
- 然後定義遞迴的 function
- function 內先確認現在這個點是不是要繼續往下訪問,所以如果
1. x 座標超過邊界 `0 ~ (rows-1)`
2. y 座標超過邊界 `0 ~ (cols-1)`
3. 是水或者是訪問過的點 `"0" or "2" --> grid[i][j] != "1"` 就返回
- 假設可以繼續訪問,先把 `grid[i][j]` 標記為 `"2"`
- 再來繼續確認這個點的上下左右
- 最後全部都走完,返回到迴圈內,`islands` 就能加 1
**加上 `print(...)` 版本的程式碼**
```python!=
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
if not grid:
return 0
rows = len(grid)
cols = len(grid[0])
islands = 0
def dfs(i, j, depth = 0):
print(f"{' '*depth}DFS visiting: ({i}, {j}), value: {grid[i][j] if 0 <= i < rows and 0 <= j < cols else 'out of bounds'}")
if (i < 0 or i > rows - 1 or j < 0 or j > cols - 1 or grid[i][j] != "1"):
print(f"{' '*depth}Stopping at ({i}, {j}) - out of bounds or not '1'")
return
grid[i][j] = "2"
print(f"{' '*depth}Marked ({i}, {j}) as visited (changed to '2')")
dfs(i-1, j, depth + 1)
dfs(i+1, j, depth + 1)
dfs(i, j-1, depth + 1)
dfs(i, j+1, depth + 1)
for i in range(rows):
for j in range(cols):
if grid[i][j] == "1":
print(f"\nStarting new island search at ({i}, {j})")
dfs(i, j)
islands += 1
print(f"Finished island, current count: {islands}")
print("Current grid state:")
for row in grid:
print(row)
return islands
```
可以跑 Testcase 觀察 Output:
Input
```
[["1","0","0","0"],
["0","1","1","1"],
["1","0","0","1"]]
```
Output
```
Starting new island search at (0, 0)
DFS visiting: (0, 0), value: 1
Marked (0, 0) as visited (changed to '2')
DFS visiting: (-1, 0), value: out of bounds
Stopping at (-1, 0) - out of bounds or not '1'
DFS visiting: (1, 0), value: 0
Stopping at (1, 0) - out of bounds or not '1'
DFS visiting: (0, -1), value: out of bounds
Stopping at (0, -1) - out of bounds or not '1'
DFS visiting: (0, 1), value: 0
Stopping at (0, 1) - out of bounds or not '1'
Finished island, current count: 1
Current grid state:
['2', '0', '0', '0']
['0', '1', '1', '1']
['1', '0', '0', '1']
Starting new island search at (1, 1)
DFS visiting: (1, 1), value: 1
Marked (1, 1) as visited (changed to '2')
DFS visiting: (0, 1), value: 0
Stopping at (0, 1) - out of bounds or not '1'
DFS visiting: (2, 1), value: 0
Stopping at (2, 1) - out of bounds or not '1'
DFS visiting: (1, 0), value: 0
Stopping at (1, 0) - out of bounds or not '1'
DFS visiting: (1, 2), value: 1
Marked (1, 2) as visited (changed to '2')
DFS visiting: (0, 2), value: 0
Stopping at (0, 2) - out of bounds or not '1'
DFS visiting: (2, 2), value: 0
Stopping at (2, 2) - out of bounds or not '1'
DFS visiting: (1, 1), value: 2
Stopping at (1, 1) - out of bounds or not '1'
DFS visiting: (1, 3), value: 1
Marked (1, 3) as visited (changed to '2')
DFS visiting: (0, 3), value: 0
Stopping at (0, 3) - out of bounds or not '1'
DFS visiting: (2, 3), value: 1
Marked (2, 3) as visited (changed to '2')
DFS visiting: (1, 3), value: 2
Stopping at (1, 3) - out of bounds or not '1'
DFS visiting: (3, 3), value: out of bounds
Stopping at (3, 3) - out of bounds or not '1'
DFS visiting: (2, 2), value: 0
Stopping at (2, 2) - out of bounds or not '1'
DFS visiting: (2, 4), value: out of bounds
Stopping at (2, 4) - out of bounds or not '1'
DFS visiting: (1, 2), value: 2
Stopping at (1, 2) - out of bounds or not '1'
DFS visiting: (1, 4), value: out of bounds
Stopping at (1, 4) - out of bounds or not '1'
Finished island, current count: 2
Current grid state:
['2', '0', '0', '0']
['0', '2', '2', '2']
['1', '0', '0', '2']
Starting new island search at (2, 0)
DFS visiting: (2, 0), value: 1
Marked (2, 0) as visited (changed to '2')
DFS visiting: (1, 0), value: 0
Stopping at (1, 0) - out of bounds or not '1'
DFS visiting: (3, 0), value: out of bounds
Stopping at (3, 0) - out of bounds or not '1'
DFS visiting: (2, -1), value: out of bounds
Stopping at (2, -1) - out of bounds or not '1'
DFS visiting: (2, 1), value: 0
Stopping at (2, 1) - out of bounds or not '1'
Finished island, current count: 3
Current grid state:
['2', '0', '0', '0']
['0', '2', '2', '2']
['2', '0', '0', '2']
```
最後輸出為 3
### Time & Space Complexity
- Time: O(m * n)
- 2 層迴圈,每個點最多只會被拜訪一次,所以最多拜訪所有點一次
- m: number of rows in the grid
- n: number of columns in the grid
- Space: O(m * n)
- 此解法沒有運用到額外的 visited matrix 來儲存訪問過的點,我們直接修改原本的 `gird` 將 `"1"` 標記為 `"2"`
- 但 call stack 最壞情況下仍是 O(m * n)。假設整個 grid 都是 `"1"`,遞迴會持續呼叫,最多會呼叫到 `m * n` 層,也就是每個點都進入一次遞迴,因此空間複雜度會是 O(m * n)
## Solution 2 - DFS, Stack
(working)
### Logic
### Code
### Time & Space Complexity
## Solution 3 - BFS, Queue
(working)
### Logic
### Code
### Time & Space Complexity