1 題目描述
一個二維數組,把 1 看做陸地,把 0 看做大海,陸地相連組成一個島嶼。把陣列以外的區域也看做是大海,問總共有多少個島嶼。
Constraints:
m == grid.length
n == grid[i].length
1 <= m, n <= 300
grid[i][j] is '0' or '1'
2 解法
這個解法主要有兩個:
- DFS:就是深入檢查每個node是否有相連,如果有就把相連的部分都變成0,直到沒有相連的部分為止。
- 併查集 Disjoint-set data structure: 利用尋找root的方式,如果發現有共同的祖先,那就表示這兩個land是相連的,如果是不同的root就代表不同祖先,可能要建立bridge或是union合併。
DFS的方法比較簡單明瞭,併查集的方法比較複雜,但是很有趣,所以我兩個都會來說明如何實現。
2.1 DFS
基本上在上下左右探索的時候,要確保三個原則:
- 不要超出rows邊界
- 不要超出cols邊界
- 不要走到海洋
1 2 3 4 5 6 7 8 9 10 11
| def dfs(i, j): if (i < 0 or i >= len(grid)) or (j < 0 or j >= len(grid[0])) or grid[i][j] == '0': return grid[i][j] = '0' dfs(i - 1, j) dfs(i + 1, j) dfs(i, j - 1) dfs(i, j + 1)
|
開始檢查陸地,如果發現陸地,先num_islands += 1
,然後開始DFS探索。
1 2 3 4 5 6 7 8 9 10 11 12
| def numIslands(self, grid: List[List[str]]) -> int: if not grid: return 0
num_islands = 0 for i in range(len(grid)): for j in range(len(grid[0])): if grid[i][j] == '1': num_islands += 1 dfs(i, j)
return num_islands
|
完整的程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| """ Time: O(M * N) - M 與 N 分別是 rows 與 cols - 因為每個元素都會被訪問一次,所以時間複雜度是 O(M * N)
Space: O(M * N) - 空間複雜度來自遞歸的深度,最壞情況下是整個地圖都是陸地,所以是 O(M * N) """ def numIslands(self, grid: List[List[str]]) -> int: def dfs(i, j): if (i < 0 or i >= len(grid)) or (j < 0 or j >= len(grid[0])) or grid[i][j] == '0': return grid[i][j] = '0' dfs(i - 1, j) dfs(i + 1, j) dfs(i, j - 1) dfs(i, j + 1)
if not grid: return 0
num_islands = 0 for i in range(len(grid)): for j in range(len(grid[0])): if grid[i][j] == '1': num_islands += 1 dfs(i, j)
return num_islands
|
2.2 併查集
在開始之前我先來說個故事,目前這個城市有三個幫派,分別是BossA, BossB, BossC,老大底下有一些小弟用P來表示。
當兩個小弟遇見的時候,如果發現他們的共同老大都是相同的,例如BossA,那就不會打架,畢竟是同一個幫派的。
但是當兩個小弟遇見的時候,發現他們的老大不一樣,例如BossA和BossB,那就會打架,因為他們是不同幫派的。
你會發現,我們在找Boss的時候,每次一層層往上找太麻煩了,如果我們在找的過程中,就慢慢把組織扁平化,變成兩層不就很好找嗎?
然後打架打完,總會有輸贏,輸的一方要合併進去另一個幫派。
在上面的故事中,主要牽涉兩個動作,我們可以利用這個特性去找到有多少個land。
Find
尋找老大是誰
Union
老大不同的時候,要合併兩個幫派
Step1. 先假設每個小弟node都獨自一個幫派,他們的parent(老大)都是自己。
- 島嶼的數量就是所有boss的數量。
Step2. 我們發現小弟0號是島嶼,因此開始檢查周邊是不是有島嶼相連,如果有就合併。
- 我們發現小弟5號是島嶼,因此小弟5號的parent指定為小弟0號。
- lands 這時候 -1 代表有一個島嶼被合併了。
Step3. 繼續檢查周邊是不是有島嶼相連,如果有就合併。
- 小弟0號的左邊也是島嶼,因此小弟1號的parent指定為小弟0號。
- lands 這時候 -1 代表有一個島嶼被合併了。
Step4. 這時候小弟0號都檢查完畢,換下一個小弟1號,繼續檢查周邊是不是有島嶼相連,如果有就合併。
- 發現小弟6號也是島嶼,而小弟0號的老大是1號,因此小弟6號的parent指定為小弟0號。這就是我們說的階級兩層化。
- lands 這時候 -1 代表有一個島嶼被合併了。
Step5. 繼續尋找下一個島嶼,找到小弟5號,但是因為小弟5號下面是海洋,所以不用合併。
Step6. 但是小弟5號的左邊是島嶼,所以進行合併
- 但是發現小弟5號的老大是小弟0號,而小弟6號的老大也是小弟0號,所以不用做任何事情。
Step7. 繼續尋找下一個島嶼,找到小弟12號,但是下面跟右邊都是海洋,所以不用處理,他獨自當老大。
Step8. 繼續尋找下一個島嶼,找到小弟18號,因為下面是海洋,所以我們只要檢查左邊即可。
- 左邊的小弟19號是島嶼,並且他與小弟18的parent不同,因此進行合併。
- lands 這時候 -1 代表有一個島嶼被合併了。
寫成程式碼的邏輯就是如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| class UnionFind: def __init__(self, grid, rows, cols): self.nums = 0 for i in range(rows): for j in range(cols): if grid[i][j] == '1': self.nums += 1
total_nodes = rows * cols self.parents = [i for i in range(total_nodes)]
def union(self, node1, node2): root1 = self.find(node1) root2 = self.find(node2) if root1 != root2: self.parents[root2] = root1 self.nums -= 1 def find(self, node): while self.parents[node] != node: self.parents[node] = self.parents[self.parents[node]] node = self.parents[node] return node
def get_nums(self): return self.nums
|
以下是呼叫的程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| """ Time: O(M * N * a(M * N)) - M 與 N 分別是 rows 與 cols - a(M * N) 是 Ackermann 函數的反函數,是一個很慢的函數,隨著unionFind的路徑壓縮優化,他的值增長極慢,因此可以近似為 O(1) 常數
Space: O(M * N) - parents 陣列,大小與grid的數量呈正比 """ class Solution: def numIslands(self, grid: List[List[str]]) -> int: if not grid: return 0
rows = len(grid) cols = len(grid[0]) uf = UnionFind(grid, rows, cols)
for row in range(rows): for col in range(cols): if grid[row][col] == '1': if row < rows - 1 and grid[row + 1][col] == '1': uf.union(self.node(row, col, cols), self.node(row + 1, col, cols)) if col < cols - 1 and grid[row][col + 1] == '1': uf.union(self.node(row, col, cols), self.node(row, col + 1, cols))
return uf.get_nums()
def node(self, i, j, cols): return i * cols + j
|
Ref