【搜索回溯算法】:BFS的魔力--如何使用广度优先搜索找到最短路径

✨感谢您阅读本篇文章,文章内容是个人学习笔记的整理,如果哪里有误的话还请您指正噢✨
✨ 个人主页:余辉zmh–CSDN博客
✨ 文章所属专栏:搜索回溯算法篇–CSDN博客

在这里插入图片描述

文章目录

  • 一.广度优先搜索(BFS)解决最短路问题
    • 1.基本思想
    • 2.算法步骤
  • 二.例题
    • 1.迷宫中离入口最近的出口
    • 2.为高尔夫比赛砍树
    • 3.最小基因变化
    • 4.单词接龙

一.广度优先搜索(BFS)解决最短路问题

1.基本思想

  • 广度优先搜索是一种图(或树)的遍历算法。在解决边权相等的最短问题时,他从起始节点开始,逐层的向外扩展搜索。
  • 因为边权相等,所以先被搜索到的节点一定是通过最少的边到达的。

2.算法步骤

  • 初始化
    • 将起始节点标记为已访问,并将其加入到队列中。
    • 记录起始节点到自身的距离为0。
  • 搜索过程
    • 取出队列头部节点
    • 遍历该节点所有未访问的相邻节点
    • 对于相邻节点,标记为已访问,并将其加入到队列中,然后距离加一。
    • 重复上述操作,直到队列为空,或者找到目标节点。

在这里插入图片描述

二.例题

1.迷宫中离入口最近的出口

题目

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

算法原理

广度搜索,最短路的经典例题,给定迷宫中的起始位置,找到距离出口的最近距离,也就是离开迷宫的最短路径长度。

本道题就是棋盘格式,在二维数组中搜索,所以每一个位置都有上下左右四个方向可以搜索,但是不能越界,具体的搜索方式和上面图中的样例相同。

代码实现

//重命名
typedef pair<int, int> PII;
//两个数组用来四个方向搜索
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0};int nearestExit(vector<vector<char>>& maze, vector<int>& entrance){//获取行数和列数int m = maze.size(), n = maze[0].size();//创建一个布尔类型的二维数组vector<vector<bool>> check(m, vector<bool>(n));//创建一个队列,存放的是数组的下标queue<PII> q;//将起始位置入队,并标记为已遍历q.push({entrance[0], entrance[1]});check[entrance[0]][entrance[1]] = true;int step = 0;while(!q.empty()){//每一次外层循环,表示向外扩展一层,层数就是步数,所以步数加一step++;//获取当前层有多少个下标,依次出队int sz = q.size();while(sz--){auto [a,b]=q.front();q.pop();//遍历当前位置的四个方向for (int k = 0; k < 4; k++){int x = a + dx[k], y = b + dy[k];if(x>=0&&x<m&&y>=0&y<n&&maze[x][y]=='.'&&check[x][y]==false){//如果符合入队条件并且是出口时,直接返回当前步数if(x==0||x==m-1||y==0||y==n-1){return step;}//如果符合入队条件但不是出口时,入队并标记为已遍历else{q.push({x, y});check[x][y] = true;}}}}}//如果循环结束也没找到出口,说明不存在出口return -1;
}

2.为高尔夫比赛砍树

题目

!在这里插入图片描述

算法原理

因为力扣官方给的示例不是很好,没有完整的描述题目中的规则,所以这里没有展示官方示例,在下面图片中,我会先写一个示例来讲解本道题的题意,然后再写算法原理。本道题可以理解为上面的那道题的扩展,不再局限于一个起始位置和终点位置搜索

在这里插入图片描述

以图中的为例,既然要找到砍树所有树的最少步数,而砍树的顺序是固定的,所以只能限制每一次移动到下一个树时,必须是最短的路径,这不就转换成迷宫问题吗,以上面的砍树顺序为例,先从1到2,最短路径就是1->8->2,最少步数是2,再从2到4,最短路径就是2->8->4或者2->10->4,最少步数就是2,依次类推,将每一次的最少步数累加到一起,就是整个的最少步数。如果出现某个位置到某个位置不能到达时,比如有障碍不能通过,直接返回-1,因为出现某个树不能被砍掉,即使前面的步数再少,也不能砍完所有的树。

代码实现

//重命名
typedef pair<int, int> PII;
//两个数组用来四个方向搜索
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0};
int m = 0;
int n = 0;int bfs(vector<vector<int>>& forest,int bx,int by,int ex,int ey){//如果起始位置就等于终点位置返回0if(bx==ex&&by==ey){return 0;}//初始化布尔类型的二维数组,用来标记已遍历的位置vector<vector<bool>> check(m, vector<bool>(n));//创建一个队列,将起始位置入队queue<PII> q;q.push({bx, by});check[bx][by] = true;int ret = 0;while(!q.empty()){ret++;int sz = q.size();while(sz--){//先获取队头元素auto [i, j] = q.front();q.pop();for (int k = 0; k < 4; k++){int x = i + dx[k], y = j + dy[k];if(x>=0&&x<m&&y>=0&&y<n&&forest[x][y]!=0&&check[x][y]==false){if(x==ex&&y==ey){return ret;}else{q.push({x, y});check[x][y] = true;}}}}}return -1;
}
int cutOffTree(vector<vector<int>>& forest){//获取行数和列数m = forest.size();n = forest[0].size();//新建立一个数组,用来存放每个位置的下标vector<PII> trees;//遍历整个原始数组,将大于等于1的位置存放到新数组中for (int i = 0; i < m; i++){for (int j = 0; j < n; j++){if(forest[i][j]>1){trees.push_back({i, j});}}}//按照原数组位置对应的值进行从小到大排序//使用Lambda表达式自定义排序规则,[&]是捕获列表,可以访问forest变量,()内是参数列表,{}内是函数体定义比较规则sort(trees.begin(), trees.end(), [&](const PII &p1, const PII &p2){return forest[p1.first][p1.second] < forest[p2.first][p2.second]; });int bx = 0, by = 0;int ret = 0;for(auto& [a,b] : trees){//根据起始位置和终点位置找到最短步数int step = bfs(forest, bx, by, a, b);//如果返回的最短步数是-1,说明无法从当前起始位置到终点位置,直接输出-1if(step==-1){return -1;}//如果返回的不是-1,说明找到最短步数,累加ret += step;//将当前的终点位置作为下一个的起始位置bx = a, by = b;}return ret;
}

3.最小基因变化

题目

在这里插入图片描述

算法原理

本道题刚开始看可能觉得没有什么思路,但这也是最短路最经典的一个例题,虽然不是棋盘格式的搜索,但是根据题意我们可以自己写出搜索展开图,画出搜索展开图后就能明白为什么是最短路问题以及为什么可以用广度搜索来解。

在这里插入图片描述

本道题有两个细节要处理,一个是如何判断变化后的基因是否重复出现,一个是变化后的基因是否存在与基因库中;

因为基因序列是用字符串的形式表示,所以对于这两个细节的处理都可以借助哈希表来实现。

一个哈希表check用来存放每次变化后的基因,如果存放前发现哈希表中已经存在该变化后的基因,说明重复出现,不再添加到哈希表中。

另一个哈希表hash用来存放基因库中的基因序列,先遍历整个基因库,将所有基因序列存放到哈希表中,题中要求变化后的基因必须存在于基因库中才是有效的基因序列,所以变化后的基因在存放到另一个哈希表前,先判断是否存在基因库中,如果不存在该基因库中就不再添加,存在就继续存放,同时要判断是否重复出现。

搜索的实现方式还是和上面的格式一样,借助队列来实现,循环逐层搜索,直到找到目标基因序列。

代码实现

int minMutation(string startGene, string endGene, vector<string>& bank){//建立两个哈希表,一个用来存放变化后的基因,防止相同的基因重复查找//一个用来存放基因库中的基因unordered_set<string> check;unordered_set<string> hash(bank.begin(), bank.end());//判断特殊情况,变化前基因等于变化后要查找的基因,直接返回0次if(startGene==endGene){return 0;}//如果变化后要查找的基因不在基因库中,直接返回-1if(hash.count(endGene)==0){return -1;}//建立一个队列,将变化前的基因入队,并存放到检查哈希表中queue<string> q;q.push(startGene);check.insert(startGene);string change = "ACGT";int ret = 0;while(!q.empty()){//每循环一次,相当与向外扩展一层,变化次数加一ret++;int sz = q.size();while(sz--){string t=q.front();q.pop();//i循环表示一个基因8个位置的变化,j循环表示一个位置有4种情况变化for (int i = 0; i < 8; i++){//这里内层每次循环要拷贝一下原字符串,在拷贝的基础上变化,防止原字符串变化string tmp = t;for (int j = 0; j < 4; j++){tmp[i] = change[j];//变化后的字符串要存在基因库中才能存放到检查哈希表中,并且不能重复存放if(hash.count(tmp)!=0&&check.count(tmp)==0){//如果满足上面两个条件并且就是结束字符串,直接返回当前变化次数if(tmp==endGene){return ret;}//如果不是结束字符串,入队,并且存放到检查哈希表中else{q.push(tmp);check.insert(tmp);}}}}}}//循环结束后还没有返回,说明没有找到结束字符串,返回-1return -1;
}

4.单词接龙

题目

在这里插入图片描述

算法原理

本道题可以说和上面的那道题最小基因变化一模一样,连代码过程几乎都是一样的,唯一有点不同的就是,上面那道题中一个位置只有四个字符可以变化,而本道题中一个位置有26个字符可以变化,所以在循环时对这点的处理不同,其他地方都是一模一样,所以上面那道题理解之后,本道题就会变得非常简单。

代码实现

int ladderLength(string beginWord, string endWord, vector<string>& wordList){//和上一道题基因变化一样unordered_set<string> check;unordered_set<string> hash(wordList.begin(), wordList.end());if(beginWord==endWord){return 2;}if(hash.count(endWord)==0){return 0;}queue<string> q;q.push(beginWord);check.insert(beginWord);int ret = 1;while(!q.empty()){ret++;int sz = q.size();while(sz--){string t=q.front();q.pop();for (int i = 0; i < t.size(); i++){string tmp = t;for (char ch = 'a'; ch <= 'z'; ch++){tmp[i] = ch;if(hash.count(tmp)!=0&&check.count(tmp)==0){if(tmp==endWord){return ret;}else{q.push(tmp);check.insert(tmp);}}}}}}return 0;
}

以上就是关于使用广度优先搜索解决最短路问题的讲解,如果哪里有错的话,可以在评论区指正,也欢迎大家一起讨论学习,如果对你的学习有帮助的话,点点赞关注支持一下吧!!!
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Dest1ny漏洞库:用友 U8-CRM 系统 ajaxgetborrowdata.php 存在 SQL 注入漏洞

用友U8-CRM系统ajaxgetborrowdata.php存在SQL注入漏洞&#xff0c;文件多个方法存在SQL注入漏洞&#xff0c;未经身份验证的攻击者通过漏洞执行任意SQL语句&#xff0c;调用xp_cmdshell写入后门文件&#xff0c;执行任意代码&#xff0c;从而获取到服务器权限。 hunter app.n…

C# 添加、替换、提取、或删除Excel中的图片

在Excel中插入与数据相关的图片&#xff0c;能将关键数据或信息以更直观的方式呈现出来&#xff0c;使文档更加美观。此外&#xff0c;对于已有图片&#xff0c;你有事可能需要更新图片以确保信息的准确性&#xff0c;或者将Excel 中的图片单独保存&#xff0c;用于资料归档、备…

接口 V2 完善:分布式环境下的 WebSocket 实现与 Token 校验

&#x1f3af; 本文档详细介绍了如何使用WebSocket协议优化客户端与服务端之间的通信&#xff0c;特别是在处理异步订单创建通知的场景中。通过引入WebSocket代替传统的HTTP请求-响应模式&#xff0c;实现了服务器主动向客户端推送数据的功能&#xff0c;极大地提高了实时性和效…

【ProtoBuf 安装】ProtoBuf在window/Linux下的安装 创建/删除swap分区

文章目录 1.ProtoBuf在window下的安装2.ProtoBuf在Linux下的安装创建swap分区命令解析关闭swap分区删除swap分区的影响 1.ProtoBuf在window下的安装 1、下载ProtoBuf编译器 下载地址&#xff1a;https://github.com/protocolbuffers/protobuf/releases 如果要在 C 下使用 Pro…

day7手机拍照装备

对焦对不上&#xff1a;1、光太暗&#xff1b;2、离太近&#xff1b;3、颜色太单一没有区分点 滤镜可以后期P 渐变灰滤镜&#xff1a;均衡色彩&#xff0c;暗的地方亮一些&#xff0c;亮的地方暗一些 中灰滤镜&#xff1a;减少光差 手机支架&#xff1a;最基本70cm即可 手…

vue事件总线(原理、优缺点)

目录 一、原理二、使用方法三、优缺点优点缺点 四、使用注意事项具体代码参考&#xff1a; 一、原理 在Vue中&#xff0c;事件总线&#xff08;Event Bus&#xff09;是一种可实现任意组件间通信的通信方式。 要实现这个功能必须满足两点要求&#xff1a; &#xff08;1&#…

分享|instructionfine-tuning 指令微调是提高LLM性能和泛化能力的通用方法

《生成式AI导论》课程中&#xff0c;李宏毅老师提到一篇关于“ instruction fine-tuning” 指令微调的论文&#xff1a; 《Scaling Instruction-Finetuned Language Models》 摘要分享&#xff1a; 事实证明&#xff0c; 在一组以指令形式表达的数据集上微调语言模型可以提…

拟合损失函数

文章目录 拟合损失函数一、线性拟合1.1 介绍1.2 代码可视化1.2.1 生成示例数据1.2.2 损失函数1.2.3 绘制三维图像1.2.4 绘制等高线1.2.5 损失函数关于斜率的函数 二、 多变量拟合2.1 介绍2.2 代码可视化2.2.1 生成示例数据2.2.2 损失函数2.2.3 绘制等高线 三、 多项式拟合3.1 介…

unity商店插件A* Pathfinding Project如何判断一个点是否在导航网格上?

需要使用NavGraph.IsPointOnNavmesh(Vector3 point) 如果点位于导航网的可步行部分&#xff0c;则为真。 如果一个点在可步行导航网表面之上或之下&#xff0c;在任何距离&#xff0c;如果它不在更近的不可步行节点之上 / 之下&#xff0c;则认为它在导航网上。 使用方法 Ast…

2025美国大学生数学建模竞赛美赛E题成品参考论文(48页)(含模型,可运行代码,求解结果)

2025美国大学生数学建模竞赛E题成品参考论文 目录 一、问题重述 二、问题分析 三、模型假设 四、模型建立与求解 4.1问题1 4.1.1问题1思路分析 4.1.2问题1模型建立 4.1.3问题1代码&#xff08;仅供参考&#xff09; 4.1.4问题1求解结果&#xff08;仅供参考&…

开源音乐管理软件Melody

本文软件由网友 heqiusheng 推荐。不过好像已经是一年前了 &#x1f602; 简介 什么是 Melody &#xff1f; Melody 是你的音乐精灵&#xff0c;旨在帮助你更好地管理音乐。目前的主要能力是帮助你将喜欢的歌曲或者音频上传到音乐平台的云盘。 主要功能包括&#xff1a; 歌曲…

PCIE模式配置

对于VU系列FPGA&#xff0c;当DMA/Bridge Subsystem for PCI Express IP配置为Bridge模式时&#xff0c;等同于K7系列中的AXI Memory Mapped To PCI Express IP。

maven的打包插件如何使用

默认的情况下&#xff0c;当直接执行maven项目的编译命令时&#xff0c;对于结果来说是不打第三方包的&#xff0c;只有一个单独的代码jar&#xff0c;想要打一个包含其他资源的完整包就需要用到maven编译插件&#xff0c;使用时分以下几种情况 第一种&#xff1a;当只是想单纯…

反向代理模块

1 概念 1.1 反向代理概念 反向代理是指以代理服务器来接收客户端的请求&#xff0c;然后将请求转发给内部网络上的服务器&#xff0c;将从服务器上得到的结果返回给客户端&#xff0c;此时代理服务器对外表现为一个反向代理服务器。 对于客户端来说&#xff0c;反向代理就相当…

Java 大视界 -- Java 大数据与碳中和:能源数据管理与碳排放分析(66)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

《企业应用架构模式》笔记

领域逻辑 表模块和数据集一起工作-> 先查询出一个记录集&#xff0c;再根据数据集生成一个&#xff08;如合同&#xff09;对象&#xff0c;然后调用合同对象的方法。 这看起来很想service查询出一个对象&#xff0c;但调用的是对象的方法&#xff0c;这看起来像是充血模型…

《剪映5.9官方安装包》免费自动生成字幕

&#xff08;避免失效建议存自己网盘后下载&#xff09;剪映5.9官方Win.Mac 链接&#xff1a;https://pan.xunlei.com/s/VOHc-Fg2XRlD50MueEaOOeW1A1?pwdawtt# 官方唯一的免费版&#xff0c;Win和Mac都有&#xff0c;此版本官方已下架&#xff0c;觉得有用可转存收藏&#xf…

基于RIP的MGRE VPN综合实验

实验拓扑 实验需求 1、R5为ISP&#xff0c;只能进行IP地址配置&#xff0c;其所有地址均配为公有IP地址&#xff1b; 2、R1和R5间使用PPP的PAP认证&#xff0c;R5为主认证方&#xff1b; R2与R5之间使用ppp的CHAP认证&#xff0c;R5为主认证方&#xff1b; R3与R5之间使用HDLC封…

006 mybatis关联查询(一对一、一对多)

文章目录 一对一查询SQL语句方法一&#xff1a;resultType方法二&#xff1a;resultMap创建扩展po类Mapper映射文件Mapper接口测试代码小结 一对多查询SQL语句修改po类Mapper映射文件Mapper接口测试代码 注意&#xff1a;因为一个订单信息只会是一个人下的订单&#xff0c;所以…

RKNN_C++版本-YOLOV5

1.背景 为了实现低延时&#xff0c;所以开始看看C版本的rknn的使用&#xff0c;确实有不足的地方&#xff0c;请指正&#xff08;代码借鉴了rk官方的仓库文件&#xff09;。 2.基本的操作流程 1.读取模型初始化 // 设置基本信息 // 在postprocess.h文件中定义&#xff0c;详见…