首页 > 其他分享 >【力扣】岛屿数量(体会一下dfs和bfs思路的实质)

【力扣】岛屿数量(体会一下dfs和bfs思路的实质)

时间:2024-03-20 21:26:00浏览次数:21  
标签:力扣 int dfs bfs grid visited nextx nexty

题目描述

image
注意,需要求的是岛屿的数量,而不是岛屿的总面积,
这道题很考验对dfs思路的理解,而不是简单地套用模版。
可以用dfs和bfs两种方法做。

深度优先搜索版本

首先看代码:

class Solution {
private:
    int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 四个方向
    void dfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y) {
        for (int i = 0; i < 4; i++) {
            int nextx = x + dir[i][0];
            int nexty = y + dir[i][1];
            if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue;  // 越界了,直接跳过
            if (!visited[nextx][nexty] && grid[nextx][nexty] == '1') { // 没有访问过的 同时 是陆地的

                visited[nextx][nexty] = true; 
                dfs(grid, visited, nextx, nexty);
            } 
        }
    }
public:
    int numIslands(vector<vector<char>>& grid) {
        int n = grid.size(), m = grid[0].size();
        vector<vector<bool>> visited = vector<vector<bool>>(n, vector<bool>(m, false)); 

        int result = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (!visited[i][j] && grid[i][j] == '1') { 
                    visited[i][j] = true;
                    result++; // 遇到没访问过的陆地,+1
                    dfs(grid, visited, i, j); // 将与其链接的陆地都标记上 true
                }
            }
        }
        return result;
    }
};

这个代码的思路是:在numIslands函数里,通过循环每次找到一个没有被访问过的岛屿。在新岛屿上找到一个起始点后,用dfs对这个岛屿上所有的陆地点标记为visited,然后再回到numIslands函数里寻找下一个新岛屿(这里就能看出dfs操作的作用)。
但是看代码后会发现,这个dfs和一般的dfs代码很不一样,因为在开头没有终止条件。理解这里就需要对dfs或者回溯算法的执行过程细节熟悉。
在前面学习的回溯算法里,需要注意,return语句并不是为了“停止遍历”,而是为了“在合适的位置停止遍历”,其实就算不加return语句,许多回溯函数在遍历所有可能结果后也会自动停止并回溯,因为运行到边界后,程序不会进入for循环而是直接运行到函数结尾,这个过程其实和return ;的效果是一样的,只有在:遍历已经到达尽头,但是仍然会进入下一层递归的情况下,才有可能造成死循环。所以控制递归调用的位置才是避免死循环的关键。
所以在这个代码里,由于dfs函数只会遍历陆地方块,所以不需要return语句,它自然地就会遍历一个岛屿的所有陆地然后结束。

广度优先搜索版本

根据上面的算法,广度优先版本的思路就很容易想到,也是在numIslands里寻找新的岛屿,在bfs函数里遍历整个岛屿的所有陆地。
代码如下:

class Solution {
private:
int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 四个方向
void bfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y) {
    queue<pair<int, int>> que;
    que.push({x, y});
    visited[x][y] = true; // 只要加入队列,立刻标记
    while(!que.empty()) {
        pair<int ,int> cur = que.front(); que.pop();
        int curx = cur.first;
        int cury = cur.second;
        for (int i = 0; i < 4; i++) {
            int nextx = curx + dir[i][0];
            int nexty = cury + dir[i][1];
            if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue;  // 越界了,直接跳过
            if (!visited[nextx][nexty] && grid[nextx][nexty] == '1') {
                que.push({nextx, nexty});
                visited[nextx][nexty] = true; // 只要加入队列立刻标记
            }
        }
    }
}
public:
    int numIslands(vector<vector<char>>& grid) {
        int n = grid.size(), m = grid[0].size();
        vector<vector<bool>> visited = vector<vector<bool>>(n, vector<bool>(m, false));

        int result = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (!visited[i][j] && grid[i][j] == '1') {
                    result++; // 遇到没访问过的陆地,+1
                    bfs(grid, visited, i, j); // 将与其链接的陆地都标记上 true
                }
            }
        }
        return result;
    }
};

可以看出,bfs中没有用到递归,而是通过循环实现的。
通过que.push({nextx, nexty});语句,每次将后续结点入队,直到que.empty(),即没有任何后继结点,也就是所有结点都被遍历过,这时结束循环。

一种可能出现的错误:
image

标签:力扣,int,dfs,bfs,grid,visited,nextx,nexty
From: https://www.cnblogs.com/satsuki26681534/p/18086103

相关文章

  • Flume - [03] HDFS Sink
      一、概述  将事件写入Hadoop分布式文件系统(HDFS)。目前支持创建文本和序列文件。支持两种文件类型的压缩。可以根据经过的时间、数据大小或事件数周期性地滚动文件(关闭当前文件并创建文件)。根据事件起源的时间戳或机器等属性对数据进行存储/分区。HDFS目录路径可能包好......
  • 【力扣刷题日记】512.游戏玩法分析II
    前言练习sql语句,所有题目来自于力扣(https://leetcode.cn/problemset/database/)的免费数据库练习题。今日题目:512.游戏玩法分析II表:Activity列名类型player_idintdevice_idintevent_datedategames_playedint(player_id,event_date)是这个表的两个主键(具有唯一值的列......
  • 《牛客》-E魔法之森的蘑菇(经典BFS变种)
    思路:由于某些固定方向的情况,我们将到达该点的粒度划分成从那个方向的到达该点,及基础bfs为每个点可以到达一次,变成没个点可以到达四次(四个方向)用一个三维数组进行标记vis[N][N][4],其余细节看下方ACcodeACcode:#include<bits/stdc++.h>usingnamespacestd;#defineendl......
  • mongo GridFSBucket
    1、描述:mongo  单机使用 GridFSBucket2、pox中添加jar<dependency><groupId>org.mongodb</groupId><artifactId>mongo-java-driver</artifactId><version>3.12.9</version></dependency>3、module中使用,直接上代码 3.1......
  • 1793.好子数组的最大分数(力扣每日一题)
    1793.好子数组的最大分数给你一个整数数组nums(下标从0开始)和一个整数k。一个子数组(i,j)的分数定义为min(nums[i],nums[i+1],...,nums[j])*(j-i+1)。一个好子数组的两个端点下标需要满足i<=k<=j。请你返回好子数组的最大可能分数。 示例......
  • HDFSRPC安全认证Kerberos篇推广
    本文主要阐述HDFSRPC安全认证相关的实现。主要介绍Kerberos相关的实现。写在前面相关blog可以先看一下https://segmentfault.com/a/1190000039085046?sort=newesthttps://blog.csdn.net/qq_35995514/article/details/106348765https://blog.csdn.net/S1124654/article/detail......
  • BFS记忆化搜索---标记
    迷宫(洛谷)题目描述给定一个\(N\timesM\)方格的迷宫,迷宫里有\(T\)处障碍,障碍处不可通过。在迷宫中移动有上下左右四种方式,每次只能移动一个方格。数据保证起点上没有障碍。给定起点坐标和终点坐标,每个方格最多经过一次,问有多少种从起点坐标到终点坐标的方案。输入格式第......
  • 【力扣hot100】49. 字母异位词分组
    题目给你一个字符串数组,请你将字母异位词组合在一起。可以按任意顺序返回结果列表。字母异位词是由重新排列源单词的所有字母得到的一个新单词。示例1:输入:strs=[“eat”,“tea”,“tan”,“ate”,“nat”,“bat”]输出:[[“bat”],[“nat”,“tan”],[......
  • 刷题日记——干碎那个BFS!(含国科大机试2021)
    例题小引——迷宫问题问题描述:迷宫由n行m列的单元格组成(n,m都小于等于50),每个单元格要么是空地,要么是障碍物。现请你找到一条从起点到终点的最短路径长度。分析——(迷宫问题BFS解法)使用BFS算法,进行广度优先遍历,总体思路是访问一个结点,就把相邻的结点入队,然后下一个访......
  • 力扣---验证二叉搜索树---前根/中根/后根遍历
     题目解析参考:验证二叉搜索树_哔哩哔哩_bilibili一开始做呢,就跟这位老兄一样:因为没有考虑到5和3的比较接下来走入整体:先根遍历解法:首先 每个点其实都有范围,比如根节点的范围在(-INF,INF),就拿上面图片举例子,4这个节点的范围应该是(-INF,5),所以先序遍历,我们要先比较根......