leetcode:200. 岛屿数量

题目来源

题目描述

在这里插入图片描述

在这里插入图片描述

题目解析

深度优先遍历DFS

目标是找到矩阵中 “岛屿的数量” ,上下左右相连的 1 都被认为是连续岛屿。

dfs方法: 设目前指针指向一个岛屿中的某一点 (i, j),寻找包括此点的岛屿边界。

  • 从 (i, j) 向此点的上下左右 (i+1,j),(i-1,j),(i,j+1),(i,j-1) 做深度搜索。
  • 终止条件:
    • (i, j) 越过矩阵边界;
    • grid[i][j] == 0,代表此分支已越过岛屿边界。
  • 搜索岛屿的同时,执行 grid[i][j] = ‘0’,即将岛屿所有节点删除,以免之后重复搜索相同岛屿。
  • 主循环:
    • 遍历整个矩阵,当遇到 grid[i][j] == ‘1’ 时,从此点开始做深度优先搜索 dfs,岛屿数 count + 1 且在深度优先搜索中删除此岛屿。
  • 复杂度:所有的数遍历一遍,所以复杂度是O(N*M)
class Solution {
    void infect(vector<vector<char>>& grid, int x, int y){
       // 越界,或者不为1,那么就不需要感染
        if(x < 0  || x >= grid.size() || y < 0 || y >= grid[0].size() || grid[x][y] != '1'){
            return;
        }
        
        grid[x][y] = '0';  //grid[x][y] = '2'
        infect(grid, x - 1, y);
        infect(grid, x + 1, y);
        infect(grid, x, y - 1);
        infect(grid, x, y + 1);
    }
public:
    int numIslands(vector<vector<char>>& grid) {
        if(grid.empty() || grid[0].empty()){
            return 0;
        }
        int ans = 0, m = grid.size(), n = grid[0].size();
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if(grid[i][j] == '1'){
                    ans++;
                    infect(grid, i, j);
                }
            }
        }
        
        return ans;
    }
};

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

广度优先遍历 BFS

  • 主循环和思路一类似,不同点是在于搜索某岛屿边界的方法不同。
  • bfs 方法:
    • 借助一个队列queue,判断队列首部节点(i, j)是否未越界而且为1
      • 如果是则置(删除此岛屿),并将此节点上下左右节点(i+1,j),(i-1,j),(i,j+1),(i,j-1)加入队列
      • 如果不是则跳过此节点
    • 循环pop队列首节点,直到整个队列为空,此时已经遍历完此岛屿
class Solution {
    void bfs(vector<vector<char>>& grid, int i, int j){
        std::queue<pair<int, int>> list;
        list.push({i, j});
        while (!list.empty()){
            auto curr = list.front();list.pop();
            i = curr.first, j = curr.second;
            if(i >= 0 && i < grid.size() && j >= 0 && j < grid[0].size() && grid[i][j] == '1'){
                grid[i][j] = '0';
                list.push({i + 1, j});
                list.push({i - 1, j});
                list.push({i , j + 1});
                list.push({i , j - 1});
            }
        }
    }
public:
    int numIslands(vector<vector<char>>& grid) {
        if(grid.empty() || grid[0].empty()){
            return 0;
        }
        int ans = 0, m = grid.size(), n = grid[0].size();
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if(grid[i][j] == '1'){
                    ans++;
                    bfs(grid, i, j);
                }
            }
        }

        return ans;
    }
};

在这里插入图片描述

并查集

并查集是一种树型的数据结构,用于处理一些不相交的合并以及查询问题。

步骤:

  • 遍历这个二维数组,将所有的1都认为是一个单独的集合
  • 遍历这个二维数组,对于每一个1,只看它的左边和上边,如果发现有1,就做union操作(为甚只看左边和上边,因为右边和下边是对称的,而我们从左到右,从上到下遍历,所以不会重复和遗落)
class Solution {

    
    class UnionFind{
    private:
        std::vector<int> parent;
        std::vector<int> size;
        std::vector<int> help;
        int cnt{};
        int wid;

        int index(int i, int j) const{
            return i * wid + j;         //x、y 表示二维坐标值,w 表示矩阵宽度。
        }

        int findRoot(int i){
            int hi = 0;
            while (parent[i] != i){
                help[hi++] = i;
                i = parent[i];
            }
            for (hi--;  hi >= 0; --hi) {
                parent[help[hi]] = i;
            }
            return i;
        }
    public:
        explicit UnionFind(vector<vector<char>>& board){
            int m = board.size();         // 矩阵行数
            int n = board[0].size();      // 矩阵列数(宽度),即第一行元素数
            parent.resize(m * n);
            size.resize(m * n);
            help.resize(m * n);
            wid = n;
            for (int i = 0; i < m; ++i) {
                for (int j = 0; j < n; ++j) {
                    if(board[i][j] == '1'){
                        int idx = index(i, j);
                        parent[idx] = idx;
                        size[idx] = 1;
                        ++cnt;
                    }
                }
            }
        }

        void merge(int i1,  int j1, int i2, int j2){
            int i = index(i1, j1);
            int j = index(i2, j2);
            int ri = findRoot(i);
            int rj = findRoot(j);
            if(ri != rj){
                if(size[ri] >= size[rj]){
                    parent[rj] = ri;
                    size[ri] += size[rj];
                }else{
                    parent[ri] = rj;
                    size[rj] += size[ri];
                }
                --cnt;
            }
        }

        int count() const {
            return cnt;
        }

        bool isConnected(int i, int j){
            return findRoot(i) == findRoot(j);
        }
    };

    
public:
    int numIslands(vector<vector<char>>& board) {
        if(board.empty()){
            return 0;
        }

        int m = board.size();         // 矩阵行数
        int n = board[0].size();      // 矩阵列数(宽度),即第一行元素数

        UnionFind unionFind(board);

        // 第一列(除了(0, 0)外)
        for (int i = 1; i < m; ++i) {
            if(board[i - 1][0] == '1' && board[i][0] == '1'){
                unionFind.merge(i - 1, 0, i, 0);
            }
        }

        // 第一行(除了(0, 0)外)
        for (int j = 1; j < n; ++j) {
            if(board[0][j - 1] == '1' && board[0][j] == '1'){
                unionFind.merge(0, j - 1, 0, j);
            }
        }

        //
        for (int i = 1; i < m; ++i) {
            for (int j = 1; j < n; ++j) {
                if(board[i][j] == '1' ){  //  只看左&上
                    if(board[i - 1][j] == '1'){
                        unionFind.merge(i, j, i - 1, j);
                    }
                    if(board[i][j - 1] == '1'){
                        unionFind.merge(i, j, i, j - 1);
                    }
                }
            }
        }

        return unionFind.count();
    }
};

类似题目

题目思路
leetcode:130. 被围绕的区域 Surrounded Regions
leetcode:695. 岛屿的最大面积Max Area of Island
leetcode:463. 岛屿的周长 Island Perimeter岛屿的周长就是岛屿方格和非岛屿方格相邻的边的数量。
leetcode:200. 岛屿数量 Number of Islandsdfs、并查集
leetcode:305. 岛屿的数量 Number of Islands II 动态生成并查集
leetcode:694. 不同岛屿的数量 Number of Distinct Islands
leetcode:711.不同岛屿的数量 II Number of Distinct Islands II
leetcode:1020. 飞地的数量 number-of-enclaves
leetcode:1254. 统计封闭岛屿的数目 number-of-closed-islands只要提前把靠边的陆地都淹掉,然后算出来的就是封闭岛屿了。
leetcode:1905. 统计子岛屿 count-sub-islands
leetcode:286.给每个空房间位上填上该房间到 最近 门的距离 Walls and Gates从门开始扩散
leetcode:489. 扫地机器人 Robot Room Cleaner难点:怎么建立位置坐标?怎么回溯?
leetcode:317. 离建筑物最近的距离 Shortest Distance from All Buildings (1) 从每一个建筑物开始进行广度优先搜索 (2) 在搜索的同时计算每一个空格到这个建筑物的距离 (3) 在搜索的同时将每一个空格到每一个建筑物的距离进行累加,得到每个空格到所有建筑物的距离(4) 取空格到所有建筑物的最小距离
leetcode:323. 无向连通图中的连通分量个数Number of Connected Components in an Undirected Graph 连通图
Rotting Oranges
  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
给定一个整数数组 nums 和一个目标值 target,要求在数组中找出两个数的和等于目标值,并返回这两个数的索引。 思路1:暴力法 最简单的思路是使用两层循环遍历数组的所有组合,判断两个数的和是否等于目标值。如果等于目标值,则返回这两个数的索引。 此方法的时间复杂度为O(n^2),空间复杂度为O(1)。 思路2:哈希表 为了优化时间复杂度,可以使用哈希表来存储数组中的元素和对应的索引。遍历数组,对于每个元素nums[i],我们可以通过计算target - nums[i]的值,查找哈希表中是否存在这个差值。 如果存在,则说明找到了两个数的和等于目标值,返回它们的索引。如果不存在,将当前元素nums[i]和它的索引存入哈希表中。 此方法的时间复杂度为O(n),空间复杂度为O(n)。 思路3:双指针 如果数组已经排序,可以使用双指针的方法来求解。假设数组从小到大排序,定义左指针left指向数组的第一个元素,右指针right指向数组的最后一个元素。 如果当前两个指针指向的数的和等于目标值,则返回它们的索引。如果和小于目标值,则将左指针右移一位,使得和增大;如果和大于目标值,则将右指针左移一位,使得和减小。 继续移动指针,直到找到两个数的和等于目标值或者左指针超过了右指针。 此方法的时间复杂度为O(nlogn),空间复杂度为O(1)。 以上三种方法都可以解决问题,选择合适的方法取决于具体的应用场景和要求。如果数组规模较小并且不需要考虑额外的空间使用,则暴力法是最简单的方法。如果数组较大或者需要优化时间复杂度,则哈希表或双指针方法更合适。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值