题目描述
网格里 1 是陆地、0 是水。上下左右相连的 1 算同一座岛,问共几座岛?
思路解析
地图:绿色是陆地,蓝色是水。肉眼看:左上角连成一片是一座岛,右下角单独一格是另一座。
从左到右、从上到下扫网格。每碰到一个还没访问过的陆地,岛屿数就 +1,然后用 DFS 把和它相连的陆地全部标记为已访问,这样同一座岛就不会被重复数。
扫描 (0,0):扫到 (0,0) 是陆地,而且没访问过。按代码的顺序:先对它 DFS,把整座岛淹掉,等 DFS 结束再给岛屿数 +1。
DFS · 访问 (0,0):把 (0,0) 标记为已访问(变灰)。然后检查它的上下左右,看哪些也是陆地。
DFS · 右边 (0,1):(0,0) 右边的 (0,1) 是陆地,走过去,继续对它做 DFS。
DFS · 访问 (0,1):标记 (0,1)。它右边是水、下边是水,没有新陆地可走,退回 (0,0) 继续看其它方向。
DFS · 下边 (1,0):(0,0) 下边的 (1,0) 也是陆地,走过去。
DFS · 访问 (1,0):标记 (1,0)。它周围也没有新陆地了。这座岛的 3 格全部访问完,DFS 结束。
第一座岛完成 · count+1:整座岛(3 格)都标记完了,DFS 返回。这时岛屿数才 +1 = 1(对应代码里 dfs(...) 之后的 ans += 1)。继续往后扫描。
继续扫描 · 全是水:接着扫 (0,2)、(1,1)、(1,2)、(2,0)、(2,1)——它们要么是水、要么已访问,全部跳过。
扫到 (2,2):扫到 (2,2) 是陆地,又没访问过。同样:先对它 DFS。
DFS · 访问 (2,2) · count+1:标记 (2,2)。上边是水、左边是水、右和下都越界,是孤零零一座岛,DFS 立刻结束。岛屿数 +1 = 2。
扫描结束:整个网格扫完,一共触发了 2 次「发现新岛」,答案就是 2。
DFS 沿着一个方向一直深入,直到走不动(碰到水或边界)才回头换方向。把一座岛「淹没」靠的就是它。
岛屿最大面积、被围绕的区域、单词搜索……所有「网格里找连通区域」的题,都是这套网格 DFS/BFS。
参考代码(Python)
1def numIslands(self, grid):
2 ans = 0
3 for i in range(rows):
4 for j in range(cols): # 扫描每一格
5 if grid[i][j] == class="cl-str">"1" and not visited[i][j]:
6 self.dfs(grid, i, j) # 淹没整座岛
7 ans += 1 # 岛屿数 +1
8 return ans复杂度分析
- 时间复杂度:O(m×n) —— 每个格子最多访问一次
- 空间复杂度:O(m×n) —— 递归栈最坏铺满整张网格
套路模板
主循环找未访问的陆地 → DFS 淹掉一整片 → 计数 +1。换 BFS 就把递归改成队列。
1for i in range(m):
2 for j in range(n):
3 if grid[i][j]==class="cl-str">"1" and not visited[i][j]:
4 dfs(i, j); ans += 1
5def dfs(i, j):
6 visited[i][j] = True
7 for di,dj in [(0,1),(1,0),(0,-1),(-1,0)]:
8 ni,nj = i+di, j+dj
9 if 不越界 and not visited and grid==class="cl-str">"1": dfs(ni,nj)易错点
- ❌ 递归前不判越界 → ✅ 先判 0<=ni<m and 0<=nj<n(边缘格子的邻居会出界)
- ❌ 忘了标记 visited → ✅ 进 dfs 第一件事就标记(否则无限递归 / 重复数同一座岛)
以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握。