200. 岛屿数量#
👋 今天我们要聊的这道题,在谷歌、微软、字节的面试中经常出现。它不只是考察代码能力,更是在测试你对搜索算法的理解深度。
🎯 从一张旧地图说起#
想象一下,你正在研究一张古老的藏宝图:
- 蓝色的海水中散落着若干座岛屿
- 每座岛屿都由若干块相连的陆地组成
- 你需要数清楚到底有多少座独立的岛屿
这不就是今天要解决的算法题吗?
问题描述#
题目目标#
给你一个由字符 '1'(陆地)和 '0'(水)组成的二维网格 grid ,请你计算网格中岛屿的数量。岛屿总是被水包围,并且每座岛屿只能由水平或垂直方向上相邻的陆地连接形成。
示例 1#
输入:
grid = [["1","1","1","1","0"],["1","1","0","1","0"],["1","1","0","0","0"],["0","0","0","0","0"]]text输出: 1
网格示意:
1 1 1 1 0
1 1 0 1 0
1 1 0 0 0
0 0 0 0 0text说明: 图示把输入结构和输出结果并排展开后,会更容易直接看清题目的变化过程。
💡 问题的本质#
LeetCode 200题”岛屿数量”是这样描述的:
输入一个由 '1'(陆地)和 '0'(水)组成的二维网格
要求计算网格中岛屿的数量(岛屿是由相邻陆地单元组成的区域)
示例:
输入:
[
['1','1','0','0','0'],
['1','1','0','0','0'],
['0','0','1','0','0'],
['0','0','0','1','1']
]
输出:3plaintext🤔 看起来简单?等等,这里有几个关键点:
- 什么算”相邻”?(上下左右四个方向)
- 如何避免重复计算同一个岛屿?
- 如何高效地探索整个地图?
🎨 绘画给我们的启发#
想象你在给这张地图上色:
- 发现一块陆地,就用红色笔把它涂掉
- 顺着这块陆地,把所有相连的陆地都涂红
- 完成后,又发现新的未涂色的陆地,换个颜色继续…
每换一次颜色,就代表发现了一座新岛屿!这就是深度优先搜索(DFS)的思想。
⚡ 代码实现:深度优先搜索#
class Solution {
public int numIslands(char[][] grid) {
// 边界条件:如果网格为空,说明不存在任何岛屿
if (grid == null || grid.length == 0) {
// 直接返回 0,表示岛屿数量为 0
return 0;
}
// islandCount:统计总共发现了多少座岛屿
int islandCount = 0;
// rows:网格总行数,后续用于遍历和边界判断
int rows = grid.length;
// cols:网格总列数,后续用于遍历和边界判断
int cols = grid[0].length;
// 双层循环扫描整个网格中的每一个位置
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
// 如果当前位置是陆地 '1',说明发现了一座“尚未被访问”的新岛屿
if (grid[i][j] == '1') {
islandCount++; // 岛屿数量 +1(先记数)
// 从当前陆地出发做 DFS,把整座连通岛屿都标记为“已访问”
exploreIsland(grid, i, j);
}
}
}
// 全部扫描完成后,返回最终岛屿数量
return islandCount;
}
// DFS:从 (row, col) 出发,递归探索并标记整座岛屿
private void exploreIsland(char[][] grid, int row, int col) {
// 递归终止条件(任一满足都直接返回):
// 1) 行越界;2) 列越界;3) 当前位置不是陆地 '1'(可能是水 '0' 或已访问 '2')
if (row < 0 || row >= grid.length ||
col < 0 || col >= grid[0].length ||
grid[row][col] != '1') {
return;
}
// 把当前陆地标记为已访问,避免后续重复统计同一座岛屿
// 这里用 '2' 仅用于区分:'1' 未访问陆地,'2' 已访问陆地
grid[row][col] = '2';
// 分别向上、下、左、右四个方向继续递归扩展
// 这四次递归会把所有与当前格子连通的陆地都访问到
exploreIsland(grid, row - 1, col); // 上
exploreIsland(grid, row + 1, col); // 下
exploreIsland(grid, row, col - 1); // 左
exploreIsland(grid, row, col + 1); // 右
}
}java🔍 解法要点解析#
就像探索一座未知的岛屿:
- 发现陆地就开始探索
- 把探索过的地方标记一下(防止迷路)
- 向四个方向继续探索
- 直到这座岛屿探索完毕
- 寻找下一座未探索的岛屿
📊 复杂度分析#
时间复杂度:O(M × N)
- M 和 N 是地图的行数和列数
- 每个格子最多被访问一次
空间复杂度:O(M × N)
- 最坏情况:整个地图都是陆地
- 递归调用栈的深度可能达到 M × N
🎯 面试官最爱追问#
-
Q:如何处理超大地图? A:可以考虑分块处理,或使用广度优先搜索(BFS)减少栈空间
-
Q:如果不能修改输入数组呢? A:可以用额外的visited数组记录访问状态
-
Q:如何计算最大岛屿面积? A:只需在DFS时统计每个岛屿的面积即可
💡 举一反三#
这个解题思路还可以用在:
- 封闭岛屿的数量
- 最大岛屿面积
- 岛屿的周长
- 不同岛屿的数量
🎁 思考题#
如果要求每座岛屿必须是正方形才计数,怎么修改代码?
示例:
1 1 1 这个算一座岛
1 1 1 (3×3的正方形)
1 1 1
1 1 1 这个不算
1 1 1 (非正方形)
1 1 0plaintext如果你知道答案?请在评论区留言~