LeetCode 200.岛屿数量 C++

本文探讨了如何使用宽度优先搜索(BFS)和深度优先搜索(DFS)算法解决LeetCode上的'岛屿计数'问题,通过实例演示了两种方法的实现细节,帮助理解在给定陆地和水域网格中计算独立岛屿数量的技巧。

题目描述

给你一个由 '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)
链接:https://leetcode-cn.com/problems/number-of-islands

解题思路1

bfs解法
1.首先特判,若数组为空,直接return 0。
2.数组非空的话,就bfs宽搜,搜一遍。

bfs函数:
1.首先row和column分别代表数组的行数和列数。
2.遍历数组中的每一个元素,如果是1的话,就入队并且置为0,计数器res++,对其进行bfs搜索。并且在bfs过程中,如果它相邻的点也是1的话,置0入队,注意此时res不用加,因为当前结点时与我们之前的结点相连接的,在该题目中属于同一个岛屿,继续搜,直至搜到相邻点没有1的情况,那么双重循环中的该元素bfs搜索完毕,双重for循环继续往下走,因为我们有置0的操作,所以访问过的结点不在被重复访问。
3.双重for循环结束,即对全部结点搜索结束。
4.返回计数器res。

代码1

class Solution {
public:
    void bfs(vector<vector<char>> &grid, int &res){
        int row=grid.size();    //行数
        int column=grid[0].size();  //列数
        queue<pair<int, int>> q;    //存数据的队列,用pari<int,int>方便后面取当前行号与宽号
        for(int i=0;i<row;i++){ //遍历二维数组
            for(int j=0;j<column;j++){
                if(grid[i][j]=='1'){    //如果当前结点为11
                    grid[i][j]='0'; //置0,表示后面不在访问
                    res++;  //结果+1
                    q.push({i,j});  //入队
                    while(!q.empty()){  
                        auto p=q.front();   //用p记录队头元素
                        q.pop();    //弹出
                        int r=p.first;  //当前元素的行号
                        int c=p.second; //当前元素的列号
                        if(r-1>=0&&grid[r-1][c]=='1'){  //当前上面的元素是否符合条件
                            grid[r-1][c]='0';   //若符合,则置0
                            q.push({r-1,c});    //入队
                        }
                        if(r+1<row&&grid[r+1][c]=='1'){ //同理,这是当前结点下面的元素的判断
                            grid[r+1][c]='0';
                            q.push({r+1,c});
                        }
                        if(c-1>=0&&grid[r][c-1]=='1'){  //同理,这是当前结点左面的元素的判断
                            grid[r][c-1]='0';
                            q.push({r,c-1});
                        }
                        if(c+1<column&&grid[r][c+1]=='1'){  //同理,这是当前结点右面的元素的判断
                            grid[r][c+1]='0';
                            q.push({r,c+1});
                        }
                    }
                }
            }
        }
    }
    int numIslands(vector<vector<char>>& grid) {
        int res=0;  //计数器
        int row=grid.size();    //行数
        if(row==0){ //特判,空数组直接return 0
            return 0;
        }
        bfs(grid, res); //非空则bfs搜索一遍,结果保存在res中带回
        return res; //返回结果
    }
};

解题思路2

dfs解法
dfs函数的作用就是去扫描,在dfs的情况下扫到1,就置0,并且判断其相邻结点是否符合dfs要求。
主函数:
遍历二维数组,如果右元素等于1,就将结果res++,并且对其dfs扫一遍,dfs的作用是将其所有相邻的符合条件满足等于1的结点全部都置为0。

代码2

class Solution {
public:
    void dfs(vector<vector<char>> &grid,int i,int j){
        int row=grid.size();    //行数
        int col=grid[0].size(); //列数
        grid[i][j]='0'; //能进入dfs的都是符合条件的,所以将当前结点置0
        if(i-1>=0&&grid[i-1][j]=='1'){  如果当前结点的上面的结点符合要求
            dfs(grid, i-1,  j); //对当前结点上面的结点进行dfs搜索
        }   
        if(i+1<=row-1&&grid[i+1][j]=='1'){  //同理这是当前结点的下面的结点的判断
            dfs(grid, i+1,  j);
        }
        if(j-1>=0&&grid[i][j-1]=='1'){  //同理这是当前结点的左面的结点的判断
            dfs(grid, i,  j-1);
        }
        if(j+1<=col-1&&grid[i][j+1]=='1'){  //同理这是当前结点的右面的结点的判断
            dfs(grid, i,  j+1);
        }
    }
    int numIslands(vector<vector<char>>& grid) {
        int res=0;  //计数器
        int row=grid.size();    //行数
        if(row==0){ //如果二维数组为空
            return res; //直接返回0
        }
        int col=grid[0].size(); //列数
        for(int i=0;i<row;i++){ //遍历二维数组
            for(int j=0;j<col;j++){
                if(grid[i][j]=='1'){    //如果当前结点为1
                    res++;  //计数器+1
                    dfs(grid,i,j);  
                //对当前结点dfs扫一遍,作用是把与其相邻的所有等于1的结点都置0
                }
            }
        }
        return res;
    }
};

<think>好的,用户需要的是用C++实现LeetCode1254题,而且要求不使用类。首先,我得先理解题目要求。题目是统计封闭岛屿的数目,封闭岛屿指的是完全被水域包围的陆地,也就是说,岛屿不能位于矩阵的边缘,否则就可能接触到边界而无法被水域包围。接下来,我需要参考用户提供的引用内容。用户引用了几个不同的解决方案,包括Java和C++的代码,其中有的使用类,有的用DFSBFS。用户特别提到要C++实现且不用类,所以需要将类的方法转换为函数形式。首先看引用中的C++代码,比如引用[2]中的Solution类,里面有closedIsland方法和dfs方法。为了不使用类,可以将这些成员函数改为全局函数或者静态函数,或者直接在main函数外定义。不过通常,在C++中,可以将函数放在全局作用域,或者使用命名空间,但用户可能只需要一个函数式的实现,不需要类的封装。然后,分析算法思路。封闭岛屿的条件是岛屿的所有陆地都不在矩阵的边界上。因此,遍历矩阵中的每个点,当遇到陆地(0)时,进行DFSBFS遍历,将相连的陆地标记为已访问,同时检查是否触及边界。如果整个岛屿都没有触及边界,则计数加一。需要注意的点是,如何标记已访问的陆地。通常的做法是修改原矩阵,将访问过的陆地标记为1(水)或者其他值,比如2,避免重复访问。这样在遍历时就不会重复处理同一块陆地。 接下来,考虑如何将类的方法转换为独立函数。例如,引用[2]中的closedIsland函数作为类的成员函数,可以改为一个全局函数,接受grid的引用作为参数,并返回整数结果。同样,dfs函数也需要调整,可能通过传递引用来修改grid和判断是否触及边界。 然后,处理边界条件。在DFS过程中,如果发现当前坐标在矩阵的边界上(i==0或 i== rows-1或j ==0或j ==cols-1),则这个岛屿不是封闭的,需要标记为无效。例如,在引用[2]中,使用一个val变量,初始为1,如果遇到边界,则设置为0。这样在遍历完整个岛屿后,如果val仍为1,则计数增加。 现在,将这些思路整合成一个不使用类的C++实现。具体步骤如下: 1.编写一个DFS函数,参数包括grid的引用,当前坐标i,j,以及一个引用变量is_closed(或者指针)来记录是否触及边界。2.DFS过程中,检查当前坐标是否在边界,如果是,则将is_closed设为false。 3.遍历四个方向,递归处理相邻的陆地。 4.主函数中遍历整个矩阵,对每个未访问的陆地(值为0)启动DFS,并根据is_closed的值决定是否计数。需要注意的是,修改grid来标记已访问的陆地,比如将0改为1或其他值。同时,要正确处理矩阵的行列数,避免越界访问。现在,编写代码的大纲:-函数closedIsland接受二维vector的引用,返回整数。 -DFS辅助函数,参数包括i,j, grid的引用,以及一个bool引用或指针来标记是否封闭。 -遍历每个点,如果是陆地,则调用DFS,并统计结果。检查引用中的例子,比如引用[3]中的代码,他们使用了一个flag数组来标记是否访问过,但用户可能需要更节省空间的方法,直接修改原grid。例如,将访问过的陆地标记为1,这样就不需要额外的存储空间。例如,在引用[2]的代码中,DFS函数里将访问过的陆地标记为1,这样在后续遍历中就不会重复处理。所以,在编写代码时,可以在DFS中将grid[i][j]=1,以避免重复访问。然后,处理封闭判断。例如,在DFS开始时,假设该岛屿是封闭的(is_closed= true),如果在DFS过程中发现任何边界点,则将is_closed设为false。最后,如果is_closed为true,则计数加一。现在,将这些转化为代码:主函数遍历每个单元格,当遇到0时,调用DFS,并传递一个变量来记录是否封闭。例如: intclosedIsland(vector<vector<int>>& grid) {introws= grid.size();if(rows==0)return0;intcols =grid[0].size();intcount =0;for(inti =0;i <rows;++i) {for(intj =0;j <cols;++j) {if(grid[i][j]==0){bool is_closed= true;dfs(grid, i, j, is_closed);if (is_closed){++count;}}}}return count; }然后,DFS函数的实现:void dfs(vector<vector<int>>&grid,int i, intj,bool&is_closed){int rows= grid.size();intcols =grid[0].size();//检查是否在边界if(i==0 ||i ==rows-1 ||j ==0|| j== cols-1) {is_closed= false;//这里不能直接返回,因为还要继续标记所有相连的陆地}//标记为已访问grid[i][j] =1;//四个方向vector<pair<int,int>>dirs= {{-1,0}, {1,0},{0,-1}, {0,1}};for (auto dir: dirs){int x= i+ dir.first;inty =j +dir.second;if (x >=0&& x< rows&& y>=0&& y< cols&& grid[x][y]==0){dfs(grid, x, y, is_closed);}} }这样,每次DFS遍历一个岛屿,并在过程中检查是否有边界点,从而判断是否封闭。需要注意的是,即使某个点位于边界,仍然需要继续遍历整个岛屿,以确保所有相连的陆地都被标记为已访问,避免重复处理。因此,在DFS中,即使发现边界点,也需要继续处理相邻的节点,而不是立即返回。 现在,测试这个代码是否正确。例如,引用[4]中的示例输入,输出应该是2。用这个代码处理的话,应该正确统计封闭岛屿数量。另外,需要考虑矩阵为空的情况,所以在主函数中先检查rows是否为0,避免越界错误。 这样,代码应该可以正确运行,并且没有使用类,符合用户的需求。</think>### LeetCode 1254 C++ 实现(不使用类) **问题分析** 封闭岛屿的定义是:被水域完全包围的陆地区域,即岛屿的所有陆地格子均不位于矩阵边界上。因此,解题思路为: 1. 遍历所有格子,遇到陆地(0)时启动深度优先搜索(DFS) 2.DFS过程中标记已访问的陆地,并检查是否触及矩阵边界 3. 若整个岛屿未触及边界,则计数加一 **C++代码实现** ```cpp #include <vector> using namespace std; void dfs(vector<vector<int>>& grid, int i, int j, bool& is_closed) { int rows = grid.size(); int cols = grid[0].size(); // 检查当前点是否在边界 if (i == 0 || i == rows - 1 || j == 0 || j == cols - 1) { is_closed = false; // 标记为非封闭岛屿 } // 标记当前点为已访问(改为水域) grid[i][j] = 1; // 定义四个方向 int dx[] = {-1, 1, 0, 0}; int dy[] = {0, 0, -1, 1}; // 遍历相邻格子 for (int k = 0; k < 4; ++k) { int x = i + dx[k]; int y = j + dy[k]; if (x >= 0 && x < rows && y >= 0 && y < cols && grid[x][y] == 0) { dfs(grid, x, y, is_closed); } } } int closedIsland(vector<vector<int>>& grid) { if (grid.empty()) return 0; int rows = grid.size(); int cols = grid[0].size(); int count = 0; for (int i = 0; i < rows; ++i) { for (int j = 0; j < cols; ++j) { if (grid[i][j] == 0) { bool is_closed = true; dfs(grid, i, j, is_closed); if (is_closed) { ++count; } } } } return count; } ``` **代码说明** 1. **DFS函数**:通过递归标记相连的陆地,并检查是否接触边界。参数 `is_closed` 作为引用传递,用于记录岛屿的封闭状态[^2] 2. **主函数**:遍历矩阵,对每个未访问的陆地启动DFS,并根据 `is_closed` 统计封闭岛屿数量 3. **边界处理**:DFS过程中若发现陆地接触矩阵边界,则标记该岛屿为非封闭[^4] **复杂度分析** - 时间复杂度:$O(mn)$,每个格子最多访问一次 - 空间复杂度:$O(mn)$,递归栈深度最坏情况下为矩阵大小
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值