Data Structure and Algorithms Guidebook

数据结构与算法完全知识体系

岛屿数量

给你一个由  '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

示例 1:

输入:
[
['1', '1', '1', '1', '0'],
['1', '1', '0', '1', '0'],
['1', '1', '0', '0', '0'],
['0', '0', '0', '0', '0'],
];
输出: 1;

示例  2:

输入:
[
['1','1','0','0','0'],
['1','1','0','0','0'],
['0','0','1','0','0'],
['0','0','0','1','1']
]
输出: 3
解释: 每座岛屿只能由水平和/或竖直方向上相邻的陆地连接而成。

解题思路

深度优先搜索

线性扫描整个二维网格,如果一个结点包含 1,则以其为根据点启动深度优先搜索。在深度优先搜索过程中,每个访问过的结点被标记为 0。计数启动深度优先搜索的根节点的数量,即为岛屿的数量。

为什么需要沉岛:

  • 遍历遇到 1 就是遇到土地,它肯定在一个岛上,统计计数 +1
  • 如果不把它和与它同岛的 1 变成 0,则后面重复遍历到它们时,会重复计数

怎么找到同处一岛的所有 1:

  • DFS,从当前 1 为入口
  • DFS 做的事情:沉岛
    • 将当前 1 变 0
    • 当前坐标的上下左右都递归 DFS,同处已到的 1 都变 0
  • DFS 出口:超出矩阵边界,或遇到 0,不用沉岛,直接返回
const numIslands = function(grid) {
let count = 0;
for (let i = 0; i < grid.length; i++) {
for (let j = 0; j < grid[0].length; j++) {
if (grid[i][j] === '1') {
// 若出现元素值为 1,则岛屿数量加 1
count++;
// 使用深度优先遍历将此岛屿所有元素变为 0
dfs(i, j, grid);
}
}
}
function dfs(i, j, grid) {
if (i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || grid[i][j] === '0') {
return;
}
// 将当前格的值设为 0,表示已经遍历过
grid[i][j] = '0';
// 遍历当前格的上下左右四个
dfs(i, j + 1, grid);
dfs(i, j - 1, grid);
dfs(i + 1, j, grid);
dfs(i - 1, j, grid);
}
return count;
};

复杂度分析:

  • 时间复杂度 ,其中 分别为行数和列数
  • 空间复杂度 ,此时整个网格均为陆地,深度优先搜索的深度达到

广度优先搜索

线性扫描整个二维网格,如果一个节点包含 1,则以其为根节点启动广度优先搜索。将其放入队列中,并将值设为 0 以标记访问过该节点。迭代地搜索队列中的每个节点,直到队列为空。

  • 遇到 1 就计数 +1
  • 维护一个队列,遇到 1 就让它的坐标入列
  • 节点出列,并考察四个方向,如果是 1,将它转为 0,并将节点入列
  • 如果越界了或遇到 0,则跳过不用入列
  • 出列...入列,直到没有可以入列的节点,则当前岛屿的所有 1 都转 0 了
const numIslands = function(grid) {
let count = 0;
let queue = [];
for (let i = 0; i < grid.length; i++) {
for (let j = 0; j < grid[0].length; j++) {
if (grid[i][j] === '1') {
count++;
// 标记为已访问
grid[i][j] = '0';
queue.push([i, j]);
bfs(queue, grid);
}
}
}
function bfs(queue, grid) {
const dirs = [
[0, 1],
[1, 0],
[0, -1],
[-1, 0],
];
while (queue.length) {
const cur = queue.shift();
for (const dir of dirs) {
const x = cur[0] + dir[0];
const y = cur[1] + dir[1];
if (x < 0 || x >= grid.length || y < 0 || y >= grid[0].length || grid[x][y] !== '1') {
continue;
}
grid[x][y] = '0';
queue.push([x, y]);
}
}
}
return count;
};

复杂度分析:

  • 时间复杂度 ,其中 分别为行数和列数
  • 空间复杂度 ,在最坏的情况下(全部为陆地),队列大小可以达到