[路飞]_LeetCode_岛屿数量

197 阅读2分钟

「这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战

题目

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

提示:

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

来源:力扣(LeetCode)leetcode-cn.com/problems/nu…

解题思路

  • 上下左右相邻的为1格子组成陆地,那么这也是个连通性问题,可以用并查集来解决。

  • 因为并查集内部存父节点的数据结构是一维数组,所以我们处理时要把二维的网格转成一维数组,公式为:ind = (i, j) => i * m + j

  • 合并格子的时候只要合并上和左,因为右和下会在后面的循环中覆盖到。并且只合并值为1的格子。

  • 合并完后,格子的值为1,并且并查集中这个格子的父节点是自己时,则是一个岛屿。循环找出所有这种情况去节点总数就是岛屿数。

代码实现

var numIslands = function (grid) {
    const n = grid.length
    const m = grid[0].length
    //初始化一个数量为n*m的并查集
    const u = new UnionSet(n * m)

    //把二维数组转换为一维数组
    const ind = (i, j) => i * m + j

    for (let i = 0; i < n; i++) {
        for (let j = 0; j < m; j++) {
            //如果当前格子不等于1则不处理
            if (grid[i][j] !== '1') continue

            //如果上面的格子是1,则和当前格子合并
            //当i等于0时说明是最上面一条边上,跳过
            if (i > 0 && grid[i - 1][j] === '1') u.merge(ind(i, j), ind(i - 1, j))
            
            //如果左边的格子是1,则和当前格子合并
            //当j等于0时说明是最左边一条边上,跳过
            if (j > 0 && grid[i][j - 1] === '1') u.merge(ind(i, j), ind(i, j - 1))
        }
    }

    let ans = 0

    //统计所有格子当中值为1,并且并查集中父节点等于自己的格子的个数,就是岛屿个数
    for (let i = 0; i < n; i++) {
        for (let j = 0; j < m; j++) {
            if (grid[i][j] === '1' && u.get(ind(i, j)) === ind(i, j)) ans++
        }
    }

    return ans
};

class UnionSet {
    constructor(n) {
        //初始化父节点数组,每个节点的父节点默认为自己
        this.pa = new Array(n + 1).fill(0).map((item, index) => index)

        //初始化每棵树的节点数
        this.size = new Array(n + 1).fill(1)
    }

    get(x) {
        //查找x的父节点,并且完成路径优化
        //优化后,x的父节点指向所在树的根节点
        return this.pa[x] = this.pa[x] === x ? x : this.get(this.pa[x])
    }

    merge(a, b) {
        //找到a的根节点
        const ra = this.get(a)
        //找到b的根节点
        const rb = this.get(b)

        //如果a和b在一个集合中则不需要合并
        if (ra === rb) return

        //把节点总数小的集合合并到节点总数多的集合里
        //更新节点总数多的集合为 a和b之和
        if (this.size[ra] < this.size[rb]) {
            this.pa[ra] = rb
            this.size[rb] += this.size[ra]
        } else {
            this.pa[rb] = ra
            this.size[ra] += this.size[rb]
        }
    }
}

如有错误欢迎指出,欢迎一起讨论!