题目描述:
给定一个由 '1'(陆地)和 '0'(水域)组成的二维网格,计算岛屿的数量。岛屿由水平或垂直方向上相邻的陆地组成,四个方向连接在一起的 '1' 视为一个岛屿。
这是图遍历中非常典型的一道题,需要在遍历过程中对访问过的位置进行标记,以避免重复统计。
DFS
思路:
当我们遇到一个 '1' 时,就以它为起点,使用递归的方式向上下左右扩散,将与其相连的所有 '1' 都标记为 '0'(淹没处理),这样可以避免后续重复访问。每次成功触发一次 DFS,说明遇到了一个新的岛屿,计数器加一即可。
注释都比较清楚就是,主要要注意的就是
return的情况不能漏掉
var numIslands = function (grid) {
let res = 0
let row = grid.length, col = grid[0].length
var dfs = function (i, j) {
// 越界,直接return
if (i < 0 || i >= row || j < 0 || j >= col) return
// 遇到水 直接return
if (grid[i][j] === '0') return
// 符合条件的陆地,遍历到了之后,然后标记为水,避免下次遇到再计算
grid[i][j] = '0'
// 4个方向上都进行淹没的操作
dfs(i - 1, j)
dfs(i + 1, j)
dfs(i, j - 1)
dfs(i, j + 1)
}
for (let i = 0; i < row; ++i) {
for (let j = 0; j < col; ++j) {
if (grid[i][j] === '1') {
// 进入一次dfs递归,把上下左右的陆地进行淹没
dfs(i, j)
// 四个方向上都淹没完了之后,岛屿数量++
res++
}
}
}
return res
};
要点说明:
- 每次递归遇到陆地
'1',就将其置为'0'。 - 防止再次访问已处理的节点。
- 所有与之连通的陆地都会被递归“淹没”。
BFS
思路:
使用队列进行逐层搜索。每次遇到 '1',就将其加入队列,然后将所有与之相邻的 '1' 也加入队列并标记为 '0',直到整片岛屿被遍历完毕。
var numIslands = function (grid) {
let res = 0
let row = grid.length
let col = grid[0].length
let direction = [[1, 0], [-1, 0], [0, 1], [0, -1]]
var bfs = function (i, j) {
let queue = [[i, j]]
while (queue.length) {
let [x, y] = queue.shift()
for (let [dx, dy] of direction) {
let fx = dx + x
let fy = dy + y
if (fx >= 0 && fx < row && fy >= 0 && fy < col && grid[fx][fy] === '1') {
// 入队
queue.push([fx, fy])
// 重新标记
grid[fx][fy] = '0'
}
}
}
}
for (let i = 0; i < row; ++i) {
for (let j = 0; j < col; ++j) {
if (grid[i][j] === '1') {
res++
grid[i][j] = '0'
bfs(i, j)
}
}
}
return res
};
要点说明:
- 使用队列来处理每一层的节点,直到当前岛屿的所有
'1'都被“淹没”。 - 每遇到一个
'1',就触发一次新的 BFS。
🧮 时间 & 空间复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 | 特点与适用场景 |
|---|---|---|---|
| DFS | O(m × n) | O(m × n)(递归栈) | 实现简单,代码简洁,但递归深度大时易栈溢出 |
| BFS | O(m × n) | O(m × n)(队列) | 稳定性更好,适合大数据量或对栈深度敏感的环境 |
m × n 是网格中元素总数。
🎯 总结建议
| 使用场景 | 推荐方法 |
|---|---|
| 数据规模较小、对递归友好 | DFS |
| 输入规模较大、追求稳定性 | BFS |
| 后续扩展图路径、最短路径等 | BFS 更方便 |