
1.题目回顾
-
输入: 一个由字符
'1'(陆地)和'0'(水域)组成的m x n二维网格grid。 -
输出: 计算网格中岛屿的数量。
-
岛屿定义: 岛屿总是被水包围,并且只能由水平或垂直方向相邻的陆地连接形成。
-
示例:
输入:文本grid = [ ["1","1","0","0","0"], ["1","1","0","0","0"], ["0","0","1","0","0"], ["0","0","0","1","1"] ]输出:
3(左上角1个,中间1个,右下角1个)
2.核心思路:洪水填充(Flood Fill)
这道题的本质是求连通分量的数量 。最直观的解法就是使用深度优先搜索(DFS) 或广度优先搜索(BFS)。
我们可以把这道题想象成"陆地沉没"的过程:
- 遍历网格: 我们挨个扫描网格中的每一个格子。
- 发现陆地: 当我们遇到一个
'1'时,说明我们发现了一座新岛屿,此时岛屿计数器加 1。 - 洪水填充(沉没岛屿): 为了不重复计算这座岛屿的其他部分,我们需要立刻以当前格子为起点,通过 DFS 或 BFS 把它上下左右 所有相连的陆地
'1'全部"淹没"(标记为'0')。 - 重复: 继续扫描,直到整个网格都被遍历完。
3.算法详细步骤(以 DFS 为例)
初始化计数器
创建一个变量 count = 0,用来记录岛屿的数量。
双重循环遍历
使用两层 for 循环遍历 grid 中的每一个坐标 (i, j)。
触发 DFS
如果当前坐标 grid[i][j] == '1':
count加 1。- 调用 DFS 函数,传入当前坐标
(i, j)。
DFS 递归逻辑
在 DFS 函数中:
- 边界检查: 如果坐标越界,或者当前格子已经是水域
'0',直接返回。 - 沉没当前陆地: 将当前格子
grid[i][j]设为'0'(标记为已访问)。 - 向四个方向扩散: 递归调用 DFS 处理上、下、左、右四个相邻的格子。
4. 代码实现 (Java & Kotlin)
Java 版本
class Solution {
public int numIslands(char[][] grid) {
if (grid == null || grid.length == 0) return 0;
int count = 0;
int rows = grid.length;
int cols = grid[0].length;
// 1. 双重循环遍历整个网格
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
// 2. 发现陆地,岛屿数量+1,并触发DFS将其淹没
if (grid[i][j] == '1') {
count++;
dfs(grid, i, j);
}
}
}
return count;
}
// 深度优先搜索(沉没岛屿)
private void dfs(char[][] grid, int i, int j) {
int rows = grid.length;
int cols = grid[0].length;
// 边界检查:越界或已经是水域则返回
if (i < 0 || i >= rows || j < 0 || j >= cols || grid[i][j] == '0') {
return;
}
// 将当前陆地沉没(标记为已访问)
grid[i][j] = '0';
// 向上下左右四个方向继续扩散
dfs(grid, i - 1, j); // 上
dfs(grid, i + 1, j); // 下
dfs(grid, i, j - 1); // 左
dfs(grid, i, j + 1); // 右
}
}
Kotlin版本
class Solution {
fun numIslands(grid: Array<CharArray>): Int {
if (grid.isEmpty()) return 0
var count = 0
val rows = grid.size
val cols = grid[0].size
for (i in 0 until rows) {
for (j in 0 until cols) {
if (grid[i][j] == '1') {
count++
dfs(grid, i, j)
}
}
}
return count
}
private fun dfs(grid: Array<CharArray>, i: Int, j: Int) {
val rows = grid.size
val cols = grid[0].size
// 边界检查
if (i < 0 || i >= rows || j < 0 || j >= cols || grid[i][j] == '0') {
return
}
// 沉没当前陆地
grid[i][j] = '0'
// 向四个方向扩散
dfs(grid, i - 1, j)
dfs(grid, i + 1, j)
dfs(grid, i, j - 1)
dfs(grid, i, j + 1)
}
}
复杂度分析
假设网格的行数为 M ,列数为 N。
时间复杂度: O(M×N)
在最坏的情况下(比如整个网格全是陆地),我们需要访问每一个格子一次。虽然 DFS 会递归,但每个格子最多只会被访问和修改一次(从 '1' 变成 '0'),因此总的时间复杂度是线性的。
空间复杂度: O(M×N)
空间复杂度主要取决于递归调用栈的深度。
- 最坏情况: 网格全是陆地,DFS 会递归 M×N层,此时空间复杂度为 O(M×N)。
- 最好情况: 网格非常平坦,递归深度较小。