【算法-LeetCode】200. 岛屿数量(深度优先搜索/DFS)

200. 岛屿数量 - 力扣(LeetCode)

发布:2021年9月8日13:46:59

问题描述及示例

给你一个由 ‘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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/number-of-islands
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 300
  • grid[i][j] 的值为 ‘0’ 或 ‘1’

我的题解

更新:2021年9月8日17:03:34

参考:【算法-LeetCode】695. 岛屿的最大面积(深度优先搜索/DFS)_赖念安的博客-CSDN博客

上面这是我碰到的另一道岛屿问题。总体思路和本题差不多。

【更新结束】

这是我在LeetCode上碰到的第一道岛屿问题。一开始没想到用DFS,而是用动态规划和回溯之类的。后来感觉好像不大对劲,总感觉自己越走越偏了,由于有了前面做题的经验,我也不强求纯靠自己想出正确的方向了,如果已经经过了一番思考还是没有很好的思路,我就回去看看【相关标签】里的提示,看看这道题用到了什么方法。

我看到要用DFS,心里还是没有谱,因为在我的固有印象中,DFS是用于二叉树或是图的遍历的,运用在网格中的话我还是第一次见。但是慢慢地也有了相关的雏形,因为之前面试的时候碰到过一道找路径的题,也是放在网格中,当时题目有提示用递归之类的方法解答,于是我就开始了顺着这个方向开始思考。

一番研究后,我发现这类的岛屿问题其实都有差不多的套路。

我的题解1(深度优先搜索/DFS—递归)

①首先我们应该知道寻找岛屿的过程是基于对二维数组所形成的网格 grid 的遍历。在对这个网格 grid 进行遍历时,我们应该判断当前访问的网格点是否已经被访问过,如果是已经访问过的网格点,就可以立马跳过对它的后续访问。于是这就意味着我们需要一个与这个网格 grid 等长等宽的二维数组 visited 来记录我们对 grid 的访问情况。visited 数组中的每个元素都是布尔值, visited[i][j] === false 则说明网格点 grid[i][j] 没有被访问过,反之则为已被访问过。

②第二个关键点就是对当前遍历的网格点的相邻点(上下左右四个点)的递归遍历。如果当前遍历的网格点是未被访问过的且该网格点代表的是陆地,那么就要以该点为中心,对其上下左右四个点进行访问,而访问这些相邻点时也是同样的访问方法:即访问其上下左右四个相邻点,这就是深度优先搜索的精髓所在。而实现这种访问方法最直观的方法就是通过递归函数。

③在递归遍历网格点时,首先就应该判断当前网格点是否是有效的,也就是是否满足下面三个条件:

  • 当前访问的网格点在网格范围内(由专门的 inGrid() 函数完成判断并返回一个布尔值,如果为 false 则说明超出网格范围,反之则在范围内);
  • 当前访问的网格点未被访问过;
  • 当前访问的网格点代表的是陆地。

不满足其中任意一个条件都应该结束递归。

④当对一个网格点的DFS完成之后,说明已经完成了对一个以 grid[i][j] 为中心的岛屿的探索。这时应当立即让 result 值加一。

注意,gridDFS(grid, i, j) 函数是放在一个 if 判断中的,因为只有在当前网格点未被访问且代表陆地时才有探索的价值(或者说才需要我们用递归函数去探索其周围情况)。

⑤当网格中所有陆地点都被访问过后则将最终累加的 result 值返回。

/**
 * @param {character[][]} grid
 * @return {number}
 */
var numIslands = function (grid) {
  // result用于存储当前所探索到的岛屿数量,遍历完所有网格点后,result就是我们想要的结果
  let result = 0;
  // 二维数组visited用于记录程序对grid网格的访问情况,元素初始值均为false
  let visited = Array.from({ length: grid.length }).map(
    () => Array.from({ length: grid[0].length }).fill(false)
  );
  // 下面的双层for循环用于遍历grid网格中的每一个点
  for (let i = 0; i < grid.length; i++) {
    for (let j = 0; j < grid[0].length; j++) {
      // 如果当前遍历的点未被访问且代表的是陆地,则以其为中心进行探索
      if (!visited[i][j] && grid[i][j] === '1') {
        gridDFS(grid, i, j);
        // gridDFS返回后,说明完成了对一个以grid[i][j]为中心的岛屿的探索,结果值加1
        result++;
      }
    }
  }
  // 对grid中的所有网格点遍历完后,result中存储的就是最终的岛屿数量,将其返回
  return result;

  // gridDFS用于探索以grid[row][col]为中心的陆地
  function gridDFS(grid, row, col) {
  	// 递归访问之前应该先判断当前网格点的有效性
    if (!inGrid(grid, row, col) || visited[row][col] || grid[row][col] === '0') {
      return;
    }
    // 将当前访问的网格点的访问情况置为已访问
    visited[row][col] = true;
    // 分别对当前网格点的上下左右四个相邻点进行递归访问
    gridDFS(grid, row - 1, col);
    gridDFS(grid, row + 1, col);
    gridDFS(grid, row, col - 1);
    gridDFS(grid, row, col + 1);
  }

  // inGrid用于判断grid[row][col]是否在gird网格中
  function inGrid(grid, row, col) {
    return 0 <= row && row < grid.length && 0 <= col && col < grid[0].length;
  }
};


提交记录
49 / 49 个通过测试用例
状态:通过
执行用时:104 ms, 在所有 JavaScript 提交中击败了15.08%的用户
内存消耗:41.3 MB, 在所有 JavaScript 提交中击败了18.69%的用户
时间:2021/09/08 13:51

可以看到这种解法的时间表现和空间表现都不怎么好。我觉得主要还是因为递归的性能太差了,没办法。

其实这道题在空间消耗方面可以进行一定的优化,因为我们只需要访问代表陆地的网格点,所以只要当前网格点的值为 0 ,我们一律不进行访问。那既然这样,我们大可以将访问过的网格点的值由原来的 1 改为 0。这样的话就完全可以省去 visited 数组的空间开销,不过同时也需要对程序中相应的判断逻辑做一点修改就是了。

但是这种方法需要改变原始数据,我觉得还是最好不要那样,所以就没有用这种方法提交,但是空间表现一定比当前的好。

官方题解

更新:2021年7月29日18:43:21

因为我考虑到著作权归属问题,所以【官方题解】部分我不再粘贴具体的代码了,可到下方的链接中查看。

更新:2021年9月8日13:56:40

参考: 岛屿数量 - 岛屿数量 - 力扣(LeetCode)

【更新结束】

有关参考

更新:2021年9月8日13:57:03
参考:岛屿类问题的通用解法、DFS 遍历框架 - 岛屿数量 - 力扣(LeetCode)
参考:200. 岛屿数量(DFS / BFS) - 岛屿数量 - 力扣(LeetCode)
参考:岛屿类问题通用解法:DFS_Tim_Coder的博客-CSDN博客_岛屿问题dfs
参考:深度优先搜索dfs《岛屿问题》(共四题)_wuyouyin123的博客-CSDN博客_岛屿问题dfs
参考:基本算法——深度优先搜索(DFS)和广度优先搜索(BFS) - 简书
更新:2021年9月8日17:03:34
参考:【算法-LeetCode】695. 岛屿的最大面积(深度优先搜索/DFS)_赖念安的博客-CSDN博客

你可能感兴趣的:(LeetCode,leetcode,算法,dfs,岛屿问题)