图论基础|深度优先dfs、广度优先bfs

dfs 与 bfs 区别

提到深度优先搜索(dfs),就不得不说和广度优先搜索(bfs)有什么区别

先来了解dfs的过程,很多录友可能对dfs(深度优先搜索),bfs(广度优先搜索)分不清。

先给大家说一下两者大概的区别:

  • dfs是可一个方向去搜,不到黄河不回头,直到遇到绝境了,搜不下去了,再换方向(换方向的过程就涉及到了回溯)。
  • bfs是先把本节点所连接的所有节点遍历一遍,走到下一个节点的时候,再把连接节点的所有节点遍历一遍,搜索方向更像是广度,四面八方的搜索过程。

深度优先dfs

深度优先关键就两点:

  • 搜索方向,是认准一个方向搜,直到碰壁之后再换方向
  • 换方向是撤销原路径,改为节点链接的下一个路径,回溯的过程。

其实深搜和回溯是非常类似的,深搜三部曲如下:

 1.确认递归函数,参数

void dfs(参数)

通常我们递归的时候,我们递归搜索需要了解哪些参数,其实也可以在写递归函数的时候,发现需要什么参数,再去补充就可以。

一般情况,深搜需要 二维数组数组结构保存所有路径,需要一维数组保存单一路径,这种保存结果的数组,我们可以定义一个全局变量,避免让我们的函数参数过多。

vector<vector<int>> result; // 保存符合条件的所有路径
vector<int> path; // 起点到终点的路径
void dfs (图,目前搜索的节点)  

2.确认 终止条件

终止条件很重要,很多同学写dfs的时候,之所以容易死循环,栈溢出等等这些问题,都是因为终止条件没有想清楚。

if (终止条件) {
    存放结果;
    return;
}

终止添加不仅是结束本层递归,同时也是我们收获结果的时候。

另外,其实很多dfs写法,没有写终止条件,其实终止条件写在了, 下面dfs递归的逻辑里了,也就是不符合条件,直接不会向下递归。

 3.处理目前搜索节点出发的路径

一般这里就是一个for循环的操作,去遍历 目前搜索节点 所能到的所有节点。

for (选择:本节点所连接的其他节点) {
    处理节点;
    dfs(图,选择的节点); // 递归
    回溯,撤销处理结果
}

797.所有可能的路径

力扣题目链接(opens new window)

给你一个有 n 个节点的 有向无环图(DAG),请你找出所有从节点 0 到节点 n-1 的路径并输出(不要求按特定顺序)

graph[i] 是一个从节点 i 可以访问的所有节点的列表(即从节点 i 到节点 graph[i][j]存在一条有向边)。

提示:

  • n == graph.length
  • 2 <= n <= 15
  • 0 <= graph[i][j] < n
  • graph[i][j] != i(即不存在自环)
  • graph[i] 中的所有元素 互不相同
  • 保证输入为 有向无环图(DAG)

思路:深度优先基础题目

class Solution {
public:
    vector<vector<int>>result;
    vector<int>path;
    void dfs(vector<vector<int>>& graph, int x){
        if(x==graph.size()-1){//搜索到终点,停止搜索并把可行路径加入结果数组
            result.push_back(path);
            return;
        }

        for(int i=0; i<graph[x].size(); i++){//遍历节点所能访问的所有其他节点
            path.push_back(graph[x][i]);
            dfs(graph, graph[x][i]);//递归遍历
            path.pop_back();//回溯
        }
    }
    vector<vector<int>> allPathsSourceTarget(vector<vector<int>>& graph) {
        result.clear();
        path.clear();
        path.push_back(0);//从0节点出发
        dfs(graph,0);//从节点0开始搜索
        return result;
    }
};

广度优先搜索理论基础

广度优先类似于二叉树的层序遍历

广搜的搜索方式就适合于解决两个点之间的最短路径问题。因为广搜是从起点出发,以起始点为中心一圈一圈进行搜索,一旦遇到终点,记录之前走过的节点就是一条最短路。

上面我们提过,BFS是一圈一圈的搜索过程,但具体是怎么一圈一圈来搜呢。

我们用一个方格地图,假如每次搜索的方向为 上下左右(不包含斜上方),那么给出一个start起始位置,那么BFS就是从四个方向走出第一步。

图一

如果加上一个end终止位置,那么使用BFS的搜索过程如图所示:

图二

我们从图中可以看出,从start起点开始,是一圈一圈,向外搜索,方格编号1为第一步遍历的节点,方格编号2为第二步遍历的节点,第四步的时候我们找到终止点end。

正是因为BFS一圈一圈的遍历方式,所以一旦遇到终止点,那么一定是一条最短路径。

大家应该好奇,这一圈一圈的搜索过程是怎么做到的,是放在什么容器里,才能这样去遍历。

很多网上的资料都是直接说用队列来实现。

其实,我们仅仅需要一个容器,能保存我们要遍历过的元素就可以,那么用队列,还是用栈,甚至用数组,都是可以的

用队列的话,就是保证每一圈都是一个方向去转,例如统一顺时针或者逆时针

因为队列是先进先出,加入元素和弹出元素的顺序是没有改变的。

如果用栈的话,就是第一圈顺时针遍历,第二圈逆时针遍历,第三圈有顺时针遍历

因为栈是先进后出,加入元素和弹出元素的顺序改变了。

那么广搜需要注意 转圈搜索的顺序吗? 不需要!

所以用队列,还是用栈都是可以的,但大家都习惯用队列了,所以下面的讲解用我也用队列来讲,只不过要给大家说清楚,并不是非要用队列,用栈也可以

广搜代码模板,该模板针对的就是,上面的四方格的地图: (详细注释)

int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 表示四个方向
// grid 是地图,也就是一个二维数组
// visited标记访问过的节点,不要重复访问
// x,y 表示开始搜索节点的下标
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]) { // 如果节点没被访问过
                que.push({nextx, nexty});  // 队列添加该节点为下一轮要遍历的节点
                visited[nextx][nexty] = true; // 只要加入队列立刻标记,避免重复访问
            }
        }
    }

}

200. 岛屿数量

题目链接(opens new window)

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

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

此外,你可以假设该网格的四条边均被水包围。

提示:

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

广度优先版本:

class Solution {
public:
    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});
        while(!que.empty()){
            pair<int, int> cur=que.front(); que.pop();//出队,访问
            int curx=cur.first;
            int cury= cur.second;
            visited[curx][cury]=true;//标记该节点访问过
            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;
                }
            }

        }
    }
    int numIslands(vector<vector<char>>& grid) {
        int result=0;
        int n=grid.size();
        int m=grid[0].size();
        vector<vector<bool>>visited(n, vector(m,false));
        

        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(!visited[i][j]&&grid[i][j]=='1'){
                    result++;
                    bfs(grid,visited,i,j );
                }
            }
        }
        return result;
    }
};

深度优先版本:

class Solution {
public:
    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);
            }
        }
    }
    int numIslands(vector<vector<char>>& grid) {
        int result=0;
        int n=grid.size();
        int m=grid[0].size();
        vector<vector<bool>>visited(n, vector(m,false));
        

        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(!visited[i][j]&&grid[i][j]=='1'){
                    result++;
                    dfs(grid,visited,i,j );
                }
            }
        }
        return result;
    }
};

   参考:代码随想录

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/483907.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

K8S之DaemonSet控制器

DaemonSet控制器 概念、原理解读、应用场景概述工作原理典型的应用场景介绍DaemonSet 与 Deployment 的区别 解读资源清单文件实践案例 概念、原理解读、应用场景 概述 DaemonSet控制器能够确保K8S集群所有的节点都分别运行一个相同的pod副本&#xff1b; 当集群中增加node节…

【嵌入式】Docker镜像构建指南:引领应用部署的革新之路

&#x1f9d1; 作者简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟。提供嵌入式方向的学习指导、简历面…

绘制韦恩图

主要源于跟着Cell学作图 | 12.韦恩图(Vennerable包)-CSDN博客&#xff0c;增加了相关数据转换的处理。 韦恩图与upset差异 upset图&#xff1a;多个集合交集可视_upset图r语言代码自定义交集顺序-CSDN博客 rm(list ls()) #构建模型数据 group1 <- rep(c("1",…

AcWing 796. 子矩阵的和

这个题的重点是仿照一维的数组&#xff0c;所以a[N][N]也是从1索引开始的。画个图举个例子就非常清晰了 之所以不好理解是因为没画格子&#xff0c;一个格子代表一个点&#xff0c;就很好理解了。 java代码&#xff1a; import java.io.*; public class Main{static int N 1…

Java为什么是值传递?

Java为什么是值传递&#xff1f; 在我们调用方法的时候&#xff0c;通常会传递参数&#xff0c;那我们到底传递的是对象本身&#xff0c;还是仅仅是对象的拷贝对象呢&#xff1f; 先搞懂两个概念&#xff0c;形参和实参 形参和实参 实参&#xff08;实际参数&#xff0c;Ar…

【热门话题】ECMAScript vs JavaScript:理解两者间的联系与区别

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 ECMAScript vs JavaScript&#xff1a;理解两者间的联系与区别1. ECMAScript&am…

Java设计模式之单例模式(多种实现方式)

虽然写了很多年代码&#xff0c;但是说真的对设计模式不是很熟练&#xff0c;虽然平时也会用到一些&#xff0c;但是都没有深入研究过&#xff0c;所以趁现在有空练下手 这章主要讲单例模式&#xff0c;也是最简单的一种模式&#xff0c;但是因为spring中bean的广泛应用&#…

车载测试项目实践 USD诊断 CANoe工具使用

本周末2天的时间&#xff0c;可以线下带大家对车载项目&#xff1a; uds诊断进行实操训练和CANoe工具的灵活使用 本博主从事新能源汽车的研发部&#xff0c;主要是嵌入式方面的&#xff0c;对车载测试的底层逻辑非常熟悉。 需要项目或者CANoe工具实操的可以关注并私信我

解决长尾问题,BEV-CLIP:自动驾驶中复杂场景的多模态BEV检索方法

解决长尾问题&#xff0c;BEV-CLIP&#xff1a;自动驾驶中复杂场景的多模态BEV检索方法 理想汽车的工作&#xff0c;原文&#xff0c;BEV-CLIP: Multi-modal BEV Retrieval Methodology for Complex Scene in Autonomous Driving 链接&#xff1a;https://arxiv.org/pdf/2401.…

算法思想总结:位运算

创作不易&#xff0c;感谢三连支持&#xff01;&#xff01; 一、常见的位运算总结 标题 二、位1的个数 . - 力扣&#xff08;LeetCode&#xff09; 利用第七条特性&#xff1a;n&&#xff08;n-1&#xff09;干掉最后一个1&#xff0c;然后每次都用count去统计&#xff…

MySQL数据库的下载和安装以及命令行语法学习

MySQL数据库的下载和安装以及命令行语法学习 学习MYSQL&#xff0c;掌握住基础的SQL句型&#xff08;创建数据库、查看数据库列表、数据增、删、改、查等操作类型&#xff09; 首先要知道MySQL下载和安装方法&#xff1a; 提示&#xff1a;别嫌啰嗦&#xff0c;对于一个初识MY…

vue-cli3中拉取vue-cli2

vue-cli3中拉取vue-cli2 拉取 2.x 模板 (旧版本) Vue CLI > 3 和旧版使用了相同的 vue 命令&#xff0c;所以 Vue CLI 2 (vue-cli) 被覆盖了。如果你仍然需要使用旧版本的 vue init 功能&#xff0c;你可以全局安装一个桥接工具&#xff1a; npm install -g vue/cli-init…

linux源配置:ubuntu、centos;lspci与lsmod命令区别

1、ubuntu源配置 1&#xff09;先查电脑版本型号: lsb_release -c2&#xff09;再编辑源更新&#xff0c;源要与上面型号对应 参考&#xff1a;https://midoq.github.io/2022/05/30/Ubuntu20-04%E6%9B%B4%E6%8D%A2%E5%9B%BD%E5%86%85%E9%95%9C%E5%83%8F%E6%BA%90/ /etc/apt/…

MUNIK第二届功能安全及自动驾驶研讨会将在沪召开

2024年4月26日,由上海秒尼科技术服务有限公司(以下简称“Munik”)联合Parosoft主办的“第二届功能安全及自动驾驶研讨会”将在上海虹桥隆重开幕。 据了解,本次功能与自动驾驶安全研讨会,将聚焦在ISO 26262标准体系下,自动驾驶新形势下各个零部件供应商如何满足功能安全等相关重…

移除和替换任何内容:AI 驱动的图像修复工具 | 开源日报 No.204

Sanster/IOPaint Stars: 15.1k License: Apache-2.0 IOPaint 是一款由 SOTA AI 模型驱动的图像修复工具。 该项目解决了从图片中移除任何不需要的对象、瑕疵或人物&#xff0c;以及擦除和替换图片上任何内容&#xff08;由稳定扩散技术支持&#xff09;的问题。 完全免费且开…

赋能数据收集:从机票网站提取特价优惠的JavaScript技巧

背景介绍 在这个信息时代&#xff0c;数据的收集和分析对于旅游行业至关重要。在竞争激烈的市场中&#xff0c;实时获取最新的机票特价信息能够为旅行者和旅游企业带来巨大的优势。 随着机票价格的频繁波动&#xff0c;以及航空公司和旅行网站不断推出的限时特价优惠&#xff…

【Java - 框架 - HttpClient 5】(01) HttpClient 5 使用详细教程,代码示例 - 快速上手

HttpClient 5 使用详细教程&#xff0c;代码示例 - 快速上手 依赖 【Maven依赖】 <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents.client5/httpclient5 --> <dependency><groupId>org.apache.httpcomponents.client5</groupId>…

网络分析(蓝桥杯,acwing,并查集)

题目描述&#xff1a; 小明正在做一个网络实验。 他设置了 n 台电脑&#xff0c;称为节点&#xff0c;用于收发和存储数据。 初始时&#xff0c;所有节点都是独立的&#xff0c;不存在任何连接。 小明可以通过网线将两个节点连接起来&#xff0c;连接后两个节点就可以互相通…

JAVAEE——多线程的设计模式,生产消费模型,阻塞队列

文章目录 多线程设计模式什么是设计模式单例模式饿汉模式懒汉模式线程安全问题懒汉模式就一定安全吗&#xff1f;锁引发的效率问题jvm的优化引起的安全问题 阻塞队列阻塞队列是什么&#xff1f;生产消费者模型阻塞队列实现消费生产者模型可能遇到的异常 多线程设计模式 什么是…

【PHP + 代码审计】数组函数

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java、PHP】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收…