LeetCode 题解——岛屿数量

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

1644557623-number-of-islands-desc.jpg

LeetCode 上对应题目为 200 题:https://leetcode-cn.com/problems/number-of-islands/

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

Flood fill 算法

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

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

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

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

思路和实现

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

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

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

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

1644557775-number-of-island-solve.jpeg

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 算法。

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值