LeetCode 题解——岛屿问题

简介: 前端西瓜哥

大家好,我是前端西瓜哥,今天我们做一道经典算法题——岛屿数量。

image.png

这题要我们求岛屿的数量,"1" 代表岛屿的一部分,"0" 则代表海水。

其实这道题归属于岛屿问题,是有固定套路的,那就是 Flood fill 算法。

Flood fill 算法

Flood fill 算法是一种特殊的算法,它在一个区域内,从某个点开始往外扩散找到与其联通的所有点,最终获得一个区域块。

从一个点开始覆盖整个区域的过程,因很像洪水淹没一个岛屿的过程,所以被命名为 Flood fill,直译为 “洪水填充”。

得到的这个区域往往会用来填充新的颜色。绘图工具的油漆桶、魔法棒工具,其实就是通过 Flood fill 算法实现的。

实现上通常为 DFS (深度优先遍历)或 BFS(广度优先遍历)。

思路和实现

那我们看看如何用 Flood fill 算法来解这道题。

首先我们遍历 grid 二维数组,当发现数组元素为 '1',代表我们访问到了岛屿的一部分,换句话说,就是我们找到了一座岛屿,计数器 count 加一。

然后,我们用洪水淹没掉这个岛屿,这么做其实是对这座岛屿的所有部分标记为 “已访问”。你也可以额外用一个大小和 grid 一样的 visited 数组来标记,但没必要,除非不允许修改原数组。

淹没后,我们继续从原来的地方继续往下遍历,直到结束。

![image-20220208225632064](/Users/fstar/Library/Application Support/typora-user-images/image-20220208225632064.png)

DFS 的具体代码实现为:

function numIslands(grid: string[][]): number {
  const m = grid.length;
  const n = grid[0].length;
  let count = 0;
  for (let i = 0; i < m; i++) {
    for (let j = 0; j < n; j++) {
      if (grid[i][j] === '1') { // 发现岛屿的一部分
        count++;
        floodFill(grid, i, j);
      }
    }
  }
  return count;
};
function floodFill(grid: string[][], i: number, j: number) {
  const m = grid.length; // 共 m 行
  const n = grid[0].length; // 共 n 列
  if (i < 0 || i >= m || j < 0 || j >= n) return; // 跑到地图外了
  if (grid[i][j] === '0') return; // 到达岛屿边缘了
  grid[i][j] = '0'; // 淹没
  floodFill(grid, i - 1, j);
  floodFill(grid, i + 1, j);
  floodFill(grid, i, j - 1);
  floodFill(grid, i, j + 1);
}

DFS 有一个比较致命的问题:当二维数组很大的时候,会有栈溢出的风险

所以我们不妨实现一个 BFS 的解法:

function numIslands(grid: string[][]): number {
  const m = grid.length;
  const n = grid[0].length;
  let count = 0;
  for (let i = 0; i < m; i++) {
    for (let j = 0; j < n; j++) {
      if (grid[i][j] === '1') { // 发现岛屿的一部分
        count++;
        floodFill(grid, i, j);
      }
    }
  }
  return count;
};
function floodFill(grid: string[][], i: number, j: number) {
  grid[i][j] = '0';
  const queue: number[][] = [[i, j]];
  while (queue.length) {
    const size = queue.length;
    for (let k = 0; k < size; k++) {
      const [i, j] = queue.shift();
      fillIfInArea(grid, i - 1, j, queue);
      fillIfInArea(grid, i + 1, j, queue);
      fillIfInArea(grid, i, j - 1, queue);
      fillIfInArea(grid, i, j + 1, queue);
    }
  }
}
function fillIfInArea(grid: string[][], i: number, j: number, queue: number[][]) {
  if (i < 0 || i >= grid.length || j < 0 || j >= grid[0].length) return;
  if (grid[i][j] === '0') return;
  grid[i][j] = '0';
  queue.push([i, j]);
}

因为 JavaScript 原生并没有提供队列,我图省事,直接用动态数组实现队列,出队效率很差。

用链表实现队列,性能会更好些。如果你有兴趣先实现个双链表,然后再实现队列的话。

结尾

面试中看到岛屿相关的算法题,一定要想起本文介绍的 “被水淹没,不知所措” 的 Flood fill 算法。

相关文章
【力扣每日一题/30】463. 岛屿的周长
【力扣每日一题/30】463. 岛屿的周长
【力扣每日一题/30】463. 岛屿的周长
【Leetcode -463.岛屿的周长 - 476.数字的补码】
【Leetcode -463.岛屿的周长 - 476.数字的补码】
44 0
|
6月前
|
算法
【经典LeetCode算法题目专栏分类】【第9期】深度优先搜索DFS与并查集:括号生成、岛屿问题、扫雷游戏
【经典LeetCode算法题目专栏分类】【第9期】深度优先搜索DFS与并查集:括号生成、岛屿问题、扫雷游戏
|
7月前
|
算法 定位技术
【leetcode】剑指 Offer II 105. 岛屿的最大面积-【深度优先DFS】
【leetcode】剑指 Offer II 105. 岛屿的最大面积-【深度优先DFS】
77 0
|
7月前
|
分布式计算 算法 vr&ar
☆打卡算法☆LeetCode 200. 岛屿数量 算法解析
☆打卡算法☆LeetCode 200. 岛屿数量 算法解析
|
7月前
leetcode-695:岛屿的最大面积
leetcode-695:岛屿的最大面积
56 0
|
7月前
leetcode-463:岛屿的周长
leetcode-463:岛屿的周长
40 0
|
7月前
|
Go
golang力扣leetcode 200.岛屿数量
golang力扣leetcode 200.岛屿数量
33 0
|
7月前
|
C++ 索引 Python
leetcode-200:岛屿数量
leetcode-200:岛屿数量
48 0
图解LeetCode——200. 岛屿数量
图解LeetCode——200. 岛屿数量
11484 1
图解LeetCode——200. 岛屿数量