题目描述
给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
示例 1:
输入:grid = [
[“1”,“1”,“1”,“1”,“0”],
[“1”,“1”,“0”,“1”,“0”],
[“1”,“1”,“0”,“0”,“0”],
[“0”,“0”,“0”,“0”,“0”]
]
输出:1
示例 2:
输入:grid = [
[“1”,“1”,“0”,“0”,“0”],
[“1”,“1”,“0”,“0”,“0”],
[“0”,“0”,“1”,“0”,“0”],
[“0”,“0”,“0”,“1”,“1”]
]
输出:3
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 300
grid[i][j] 的值为 ‘0’ 或 ‘1’
思考一:DFS
深度优先搜索(DFS)的核心思路是:遍历网格中的每个单元格,当遇到未访问的陆地(‘1’)时,启动一次深度优先搜索,递归探索所有与之相连的陆地并标记为已访问,每完成一次这样的搜索就代表发现了一个岛屿。通过这种方式,可以完整统计所有独立的岛屿数量。
算法过程
-
初始化:
- 计数器
ans记录岛屿数量(初始为0) - 二维数组
visited标记已访问的单元格(初始全为false)
- 计数器
-
遍历网格:
- 对每个单元格
(i,j),若未访问且为陆地(‘1’):- 启动DFS探索整个连通的岛屿
- 岛屿计数器
ans加1
- 对每个单元格
-
DFS探索:
- 递归访问当前单元格的上、下、左、右四个方向
- 遇到边界、水域(‘0’)或已访问的单元格则停止递归
- 访问时标记当前单元格为已访问
时空复杂度分析
- 时间复杂度:O(m×n),其中m和n分别为网格的行数和列数,每个单元格最多被访问一次
- 空间复杂度:O(m×n),最坏情况下(全为陆地),递归栈深度可达m×n,加上
visited数组的空间
代码
/**
* @param {character[][]} grid
* @return {number}
*/
var numIslands = function(grid) {
let ans = 0;
const [m, n] = [grid.length, grid[0].length];
const visited = Array.from({length: m}, () => Array(n).fill(false));
const dfs = function(i, j) {
if (i < 0 || i >= m || j < 0 || j >= n || grid[i][j] === '0' || visited[i][j]) return;
visited[i][j] = true;
dfs(i-1, j);
dfs(i+1, j);
dfs(i, j-1);
dfs(i, j+1);
};
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
if (visited[i][j] || grid[i][j] === '0') continue;
dfs(i, j);
ans++;
}
}
return ans;
};
思考二:BFS
广度优先搜索(BFS)的核心思路是:当发现未访问的陆地时,使用队列存储当前岛屿的所有单元格,逐层向外扩展探索所有相连的陆地并标记为已访问。每完成一次这样的层级遍历,就统计一个岛屿。BFS通过队列实现层级扩展,避免了递归可能带来的栈溢出问题。
算法过程
-
初始化:
- 计数器
ans记录岛屿数量(初始为0) - 二维数组
visited标记已访问的单元格
- 计数器
-
遍历网格:
- 对每个单元格
(i,j),若未访问且为陆地:- 初始化队列并将
(i,j)入队,标记为已访问 - 岛屿计数器
ans加1
- 初始化队列并将
- 对每个单元格
-
BFS探索:
- 从队列中取出单元格,将其上下左右四个方向的未访问陆地单元格入队
- 每个入队的单元格都标记为已访问
- 直到队列为空,当前岛屿探索完毕
时空复杂度分析
- 时间复杂度:O(m×n),每个单元格最多被访问一次
- 空间复杂度:O(min(m,n)),最坏情况下队列存储最多单元格数量为网格中较短边的长度(如满是陆地的网格中,队列最大长度为边界单元格数)
代码
/**
* @param {character[][]} grid
* @return {number}
*/
var numIslands = function(grid) {
let ans = 0;
const [m, n] = [grid.length, grid[0].length];
const visited = Array.from({length: m}, () => Array(n).fill(false));
// 定义四个方向的偏移量:上、下、左、右
const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
// BFS函数:探索从(i,j)开始的整个岛屿
const bfs = function(i, j) {
// 创建队列并将起始位置入队
const queue = [[i, j]];
// 标记为已访问
visited[i][j] = true;
// 当队列不为空时,继续探索
while (queue.length > 0) {
// 取出队首元素
const [x, y] = queue.shift();
// 探索四个方向
for (const [dx, dy] of directions) {
const nx = x + dx;
const ny = y + dy;
// 检查新位置是否有效且未访问且是陆地
if (nx >= 0 && nx < m && ny >= 0 && ny < n
&& !visited[nx][ny] && grid[nx][ny] === '1') {
// 标记为已访问并加入队列
visited[nx][ny] = true;
queue.push([nx, ny]);
}
}
}
};
// 遍历整个网格
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
// 发现未访问的陆地,启动BFS并计数
if (!visited[i][j] && grid[i][j] === '1') {
bfs(i, j);
ans++;
}
}
}
return ans;
};
3926

被折叠的 条评论
为什么被折叠?



