一起刷LeetCode——岛屿数量(图)

117 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第18天,点击查看活动详情

岛屿数量

给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。 岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。 此外,你可以假设该网格的四条边均被水包围。

来源:力扣(LeetCode) 链接:leetcode.cn/problems/nu…

分析

  • 把二维网格中根据给出的规则,即1的上下左右都是0或者边缘的情况下,算是一块陆地,求有多少块陆地
  • 这道题目与图相关,在解有多少块陆地的时候, 可以通过搜索二维网格来求解,搜索的时候可以使用一个和grid一样的二维数组来记录是否访问过,因为本题是要求有多少块陆地,因此与原数组的值不是直接相关,因此可以在原数组上标识

DFS

  • 从左上角开始,深度优先搜索
代码
var numIslands = function(grid){
    let res = 0
    const dfs = (x,y) => {
        grid[x][y]=0
        if(grid[x-1] && grid[x-1][y]) dfs(x-1,y)
        if(grid[x+1] && grid[x+1][y]) dfs(x+1,y)
        if(Number(grid[x][y-1]) dfs(x,y-1)
        if(Number(grid[x][y+1]) dfs(x,y+1)
    }
    for(let i=0;i<grid.length;i++){
        for(let j=0;j<grid[0].length;j++){
            const temp = grid[i][j]
            if(Number(temp)){
                res ++
                dfs(i,j)
            }
        }
    }
    return res
}

BFS

  • 搜索的方式也可以是广度优先
代码
var numIslands = function(grid) {
    let counter = 0;
    function bfs(x,y) {
        const queue = [[x,y]];
        grid[x][y] = 0;
        while(queue.length) {
            for (const [x,y] of queue) {
                // up
                if (grid[x-1] && Number(grid[x-1][y])) {
                    console.log("up")
                    queue.push([x-1,y]);
                    grid[x-1][y] = 0;
                }
                //down
                if (grid[x+1] && Number(grid[x+1][y])) {
                    console.log("down")
                    queue.push([x+1,y]);
                    grid[x+1][y] = 0;
                }
                // left
                if (Number(grid[x][y-1])) {
                    console.log("left")
                    queue.push([x,y-1]);
                    grid[x][y-1] = 0;
                }
                //right
                if (Number(grid[x][y+1])) {
                    console.log("right")
                    queue.push([x,y+1]);
                    grid[x][y+1] = 0;
                }
                queue.shift();
                break;
            }
        }
    }
    
    for (let i = 0; i < grid.length; i++) {
        for (let j = 0; j < grid[0].length; j++) {
            const cell = grid[i][j];
            if (Number(cell)) {
                counter++;
                bfs(i,j);
            }
        }
    }
    return counter;
};

计算联通分量的个数

  • 求所有的岛屿本身就是在计算图的联通分量个数
  • 联通分量是图的一个属性,根据这个题目,可以分析这个图是个无向图
  • 难点在于构造联通分量对应的并查集数据结构
代码
class UnionSet {
  constructor(n = 100) {
    this.father = new Array(n);
    this.n = n;
    this.init();
  }
  init() {
    for (let i = 0; i < this.n; i++) {
      this.father[i] = i;
    }
  }
  find(x) {
    return (this.father[x] =
      this.father[x] === x ? x : this.find(this.father[x]));
  }
  merge(a, b) {
    const fa = this.find(a);
    const fb = this.find(b);
    if (fa === fb) return false; 
    this.father[fb] = fa;
    return true;
  }
}

var numIslands = function(grid) {
    const row = grid.length, col = grid[0].length;
    const us = new UnionSet(row * col);
    let result = 0;
	
    for (let i = 0; i < row; i++) {
        for(let j = 0; j < col; j++) {
            if (grid[i][j] === "1") result++; 
            if (j + 1 < col && grid[i][j] === "1" && grid[i][j+1] === "1") {
                const valid = us.merge(i * col + j, i * col + j + 1);
                valid && result--;
            }
            if (i + 1 < row && grid[i][j] === "1" && grid[i+1][j] === "1") {
                const valid = us.merge(i * col + j, (i + 1) * col + j);
                valid && result--;
            }
        }
    }
    return result;
};

总结

  • 在解二维数组相关的问题时,在无法化简数组的情况下,一般都把二维数组看作图
  • 图分有向图和无向图,以及图有一些属性,比如本题的题干部分就是在讲形象的讲解什么是联通分量
  • 图在分析的时候一般会用DFS和BFS,同时辅助一个数组来记录元素是否被访问过
  • 今天也是有收获的一天