每日一题 LeetCode

本文介绍两种高效算法用于计算二维网格中的岛屿数量。深度优先搜索(DFS)遍历网格,标记并计数岛屿;并查集(Union-Find)则通过合并相邻陆地来统计连通分量,最终得出岛屿总数。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/number-of-islands

岛屿数量

题目描述

给定一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,假设该网格的四条边均被水包围
在这里插入图片描述

题解

方法一:深度优先搜索(DFS)

将二维网格看成一个无向图,竖直或水平相邻的 1 之间有边相连。
扫描整个二维网格,若一个位置为 1,则以其为起始节点开始进行深度优先搜索。在深度优先搜索的过程中,每个搜索到的 1 都会被重新标记为 0。
最终岛屿的数量即深度优先搜索的次数。

复杂度分析
时间复杂度:O(MN)O(MN)O(MN),其中 M 和 N 分别为行数和列数。
空间复杂度:O(MN)O(MN)O(MN),在最坏情况下,整个网格均为陆地,深度优先搜索的深度达到 MN。

方法二:并查集

扫描整个二维网格。如果一个位置为 1,则将其与相邻四个方向上的 1 在并查集中进行合并union。
最终岛屿的数量就是并查集中连通分量的数目。
复杂度分析
时间复杂度:O(MN∗α(MN))O(MN * \alpha(MN))O(MNα(MN)),其中 M 和 N 分别为行数和列数。注意当使用路径压缩(见 find 函数)和按秩合并(见数组 rank)实现并查集时,单次操作的时间复杂度为 α(MN)\alpha(MN)α(MN),其中 α(x)\alpha(x)α(x) 为反阿克曼函数,当自变量 x 的值在人类可观测的范围内(宇宙中粒子的数量)时,函数 α(x)\alpha(x)α(x) 的值不会超过 5,因此也可以看成是常数时间复杂度。
空间复杂度:O(MN)O(MN)O(MN),即并查集需要的空间

代码

//方法二:并查集Union
class UnionFind{
public:
    UnionFind(vector<vector<char>>& grid){
        count = 0;
        int rows = grid.size();
        int cols = grid[0].size();
        for(int row = 0; row < rows; row++){
            for(int col = 0; col < cols; col++){
                if(grid[row][col] == '1'){
                    parent.push_back(row*cols+col);
                    ++count;
                }
                else{
                    parent.push_back(-1);
                }
                rank.push_back(0);
            }
        }
    }

    int find(int row){
        if(parent[row] != row){
            parent[row] = find(parent[row]);
        }
        return parent[row];
    }

    void unite(int x, int y){
        int rootx = find(x);
        int rooty = find(y);
        if(rootx != rooty){
            if(rank[rootx] < rank[rooty]){
                swap(rootx, rooty);
            }
            parent[rooty] = rootx;
            if(rank[rootx] == rank[rooty])
                rank[rootx]++;
            --count;
        }
    }

    int getCount() const{
        return count;
    }

private:
    vector<int> parent;
    vector<int> rank;
    int count;
};

class Solution {
public:
    int numIslands(vector<vector<char>>& grid) {
        /*
        //方法一:深度优先搜索DFS
        int rows = grid.size();
        if(!rows)
            return 0;
        int cols = grid[0].size();//这个放在判断前,输入[]测试就会报错?

        int num_islands = 0;
        for(int row = 0; row < rows; row++){
            for(int col = 0; col < cols; col++){
                if(grid[row][col] == '1'){
                    ++num_islands;
                    DFS(grid, row, col);
                }
            }
        }
        return num_islands;
    }

private:
    void DFS(vector<vector<char>>& grid, int row, int col){
        int rows = grid.size();
        int cols = grid[0].size();

        grid[row][col] = '0';
        if(row-1>=0 && grid[row-1][col] == '1')
            DFS(grid, row-1, col);
        if(row+1<rows && grid[row+1][col] == '1')
            DFS(grid, row+1, col);
        if(col-1>=0 && grid[row][col-1] == '1')
            DFS(grid, row, col-1);
        if(col+1<cols && grid[row][col+1] == '1')
            DFS(grid, row, col+1);
    }*/

        //方法二
        int rows = grid.size();
        if(!rows)
            return 0;
        int cols = grid[0].size();

        int num_islands = 0;
        UnionFind uf(grid);
        for(int row = 0; row < rows; row++){
            for(int col = 0; col < cols; col++){
                if(grid[row][col] == '1'){
                    grid[row][col] == '0';
                    if(row-1>=0 && grid[row-1][col] == '1')
                        uf.unite(row*cols+col, (row-1)*cols+col);
                    if(row+1<rows && grid[row+1][col] == '1')
                        uf.unite(row*cols+col, (row+1)*cols+col);
                    if(col-1>=0 && grid[row][col-1] == '1')
                        uf.unite(row*cols+col, row*cols+col-1);
                    if(col+1<cols && grid[row][col+1] == '1')
                        uf.unite(row*cols+col, row*cols+col+1);
                }
            }
        }
        return uf.getCount();
    }
};

小结

DSF, BSF,union

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值