当前位置: 首页 > ai >正文

【优选算法】BFS解决最短路问题(单源)

在这里插入图片描述

目录

  • 最短路问题简介
  • 一、[迷宫中离入口最近的出口](https://leetcode.cn/problems/nearest-exit-from-entrance-in-maze/description/)
  • 二、[最小基因变化](https://leetcode.cn/problems/minimum-genetic-mutation/description/)
  • 三、[单词接龙](https://leetcode.cn/problems/word-ladder/description/)
  • 四、[为高尔夫比赛砍树](https://leetcode.cn/problems/cut-off-trees-for-golf-event/description/)
  • 结尾

最短路问题简介

单源最短路问题指:从图中一个特定的起点(称为 “源点”)出发,求解到图中所有其他顶点的最短路径。

边权为 1 的单源最短路
当图中所有边的权重均为 1 时,图可视为 “等权图”(每条边的 “代价” 相同)。此时,求解单源最短路的核心思路是利用 BFS 的层次遍历特性。


一、迷宫中离入口最近的出口

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

思路讲解
本道题需要我们找到从迷宫入口到最近出口的最短路径。以下是具体思路:

  1. 创建一个队列;创建一个与原二维数组同等规模的二维数组,用于标记该点是否被访问,并将这个二维数组初始化为false,表示未被访问;创建一个变量记录当前步数
  2. 将入口位置加入队列,并标记为 true ,表示已访问(避免重复处理)。
  3. BFS遍历过程:
    • 从队列中取出当前位置,遍历四个方向的相邻位置,若合法(在范围内、非墙、未访问),检查是否为出口(不能是入口),若为出口,立即返回当前步数
    • 若合法并且不是出口,则标记位 true ,表示已访问并加入队列。
  4. 没有找到出口,返回 - 1。

编写代码

class Solution {int dx[4] = {0,0,-1,1};int dy[4] = {1,-1,0,0};int rows , cols;
public:int _bfs(vector<vector<char>>& maze,vector<vector<bool>>& vis,int row,int col){queue<pair<int,int>> qu;vis[row][col] = true;qu.push({row,col});int ret = 0;while(!qu.empty()){int cnt = qu.size();ret++;while(cnt--){int qu_x = qu.front().first , qu_y = qu.front().second;qu.pop();for(int k = 0 ; k < 4 ; k++){int x = qu_x + dx[k] , y = qu_y + dy[k];if(x >= 0 && x < rows && y >= 0 && y < cols && maze[x][y] == '.' && vis[x][y] == false){if(x == 0 || x == rows - 1 || y == 0 || y == cols - 1)  return ret;vis[x][y] = true;qu.push({x,y});}}}} return -1;}int nearestExit(vector<vector<char>>& maze, vector<int>& entrance) {rows = maze.size() , cols = maze[0].size();vector<vector<bool>> vis(rows,vector<bool>(cols,false));int ans = _bfs(maze,vis,entrance[0],entrance[1]);return ans;}
};

二、最小基因变化

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

思路讲解
本道题需要我们找到从起始基因序列 start 到目标基因序列 end 的最小变化次数,每次变化必须生成基因库 bank 中的有效序列。下面是具体思路:

  1. 将基因库bank存入哈希集合(unordered_set),方便快速判断某个基因序列是否有效(存在于基因库中)。若end不在基因库中,直接返回-1(无法完成变化)
  2. 队列存储 “当前基因序列” ,起始时将start加入队列。用另一个哈希集合us_have 记录已访问的基因序列,避免重复处理(防止循环)
  3. 定义一个变量记录当前步数
  4. BFS逐层扩展搜索
    • 记录当前队列中元素的个数为 cnt,并将步数 +1
    • 将以下步骤循环执行 cnt 次
      • 从队列中取出当前基因序列,遍历序列的每个位置
      • 对每个位置,尝试替换为A、C、G、T中与当前字符不同的字符,生成新基因序列
      • 若新序列是end,直接返回当前步数
      • 若新序列在基因库中且未被访问,将其加入队列,再添加到 us_have 表示已访问
  5. 没有找到,返回-1。

编写代码

class Solution {char d_char[4] = {'A','C','G','T'};
public:int minMutation(string startGene, string endGene, vector<string>& bank) {unordered_set<string> us_have;  // 已经遍历过的unordered_set<string> us_bank(bank.begin(),bank.end());  // 基因库queue<string> qu;qu.push(startGene);us_bank.insert(startGene);int ans = 0;while(!qu.empty()){ans++;int cnt = qu.size();while(cnt--){string q_s = qu.front();qu.pop();for(int i = 0 ; i < 8 ; i++){for(int j = 0 ; j < 4 ; j++){string tmp = q_s;tmp[i] = d_char[j];if(us_bank.find(tmp) != us_bank.end() && us_have.find(tmp) == us_have.end()){if(tmp == endGene)  return ans;us_have.insert(tmp);qu.push(tmp);}}}}}return -1;}
};

三、单词接龙

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

思路讲解
本道题需要我们找到从起始单词 beginWord 到目标单词 endWord 的最短转换序列,每次转换只能改变一个字母,且转换后的单词必须存在于字典中。本道题与上一道题的思路基本一致,以下是具体思路:

  1. 将字典 wordList 转换为哈希集合(unordered_set),以便快速检查某个单词是否在字典中存在。若 endWord 不在字典中,直接返回 0。
  2. 将起始单词 beginWord 加入队列。使用哈希集合 us_have 记录已访问的单词,避免重复处理。
  3. 定义一个变量记录当前步数(beginWord也算1步)
  4. BFS逐层扩展搜索:
    • 记录当前队列中元素的个数为 cnt,并将步数 +1
    • 将以下步骤循环执行 cnt 次
      • 从队列中取出当前单词
      • 遍历当前单词的每个位置,尝试将该位置的字符替换为 a 到 z 中的任意一个
      • 若新单词等于 endWord,直接返回当前步数。
      • 若生成的新单词有效(在字典中)且未被访问过,将其加入队列,再添加到 us_have 表示已访问
  5. 没有找到目标,返回 0。

编写代码

class Solution {
public:int ladderLength(string beginWord, string endWord, vector<string>& wordList) {if (find(wordList.begin(), wordList.end(), endWord) == wordList.end())return 0;unordered_set<string> us_have; // 已经遍历过的字符串unordered_set<string> us_words(wordList.begin(), wordList.end()); // 字典queue<string> qu;qu.push(beginWord);us_have.insert(beginWord);int len = beginWord.size(); // 单词的长度int ans = 1;while (!qu.empty()) {ans++;int cnt = qu.size();while (cnt--) {string q_s = qu.front();qu.pop();for (int i = 0; i < len; i++) {for (int j = 0; j < 26; j++) {string tmp = q_s;tmp[i] = 'a' + j;if (us_words.find(tmp) != us_words.end() &&us_have.find(tmp) == us_have.end()) {if (tmp == endWord)return ans;qu.push(tmp);us_have.insert(tmp);}}}}}return 0;}
};

四、为高尔夫比赛砍树

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

思路讲解
本道题需要我们按照树的高度从低到高砍树,并计算最小步数。可以使用多次BFS解决,以下是具体思路:

  1. 遍历矩阵,将所有树的位置和高度存入数组,并按高度排序。
  2. 遍历数组,依次通过BFS计算从当前位置到下一棵树的最短路径,累加步数。
  3. 若任意两棵树之间无法到达,返回 - 1。

编写代码

class Solution {typedef pair<int,int> PII;int dx[4] = {0,0,-1,1};int dy[4] = {1,-1,0,0};int rows , cols;
public:int bfs(vector<vector<int>>& forest , int row , int col , int erow , int ecol){if(row == erow && col == ecol) return 0;    vector<vector<bool>> vis(rows,vector<bool>(cols,false));queue<PII> qu;qu.push({row,col});vis[row][col] = true;int ret = 0;while(!qu.empty()){ret++;int cnt = qu.size();while(cnt--){int qu_x = qu.front().first , qu_y = qu.front().second;qu.pop();for(int i = 0 ; i < 4 ; i++){int x = qu_x + dx[i] , y = qu_y + dy[i];if(x >= 0 && x < rows && y >= 0 && y < cols && forest[x][y] > 0 && vis[x][y] == false){if(x == erow && y == ecol)return ret;qu.push({x,y});vis[x][y] = true;}}}}return -1;}int cutOffTree(vector<vector<int>>& forest) {vector<PII> vpos;int ans = 0;rows = forest.size() , cols = forest[0].size();if(forest[0][0] == 0)   return -1;for(int i = 0 ; i < rows ; i++){for(int j = 0 ; j < cols ; j++){if(forest[i][j] > 1){vpos.push_back({i,j});}}}sort(vpos.begin(),vpos.end(),[&](PII& p1 , PII& p2){return forest[p1.first][p1.second] < forest[p2.first][p2.second];});int size = vpos.size();int row = 0 , col = 0;for(int i = 0 ; i < size ; i++){int a = vpos[i].first , b = vpos[i].second;int n = bfs(forest,row,col,a,b);if(n == -1) return -1;else ans += n;row = a , col = b;}return ans;}
};

结尾

如果有什么建议和疑问,或是有什么错误,大家可以在评论区中提出。
希望大家以后也能和我一起进步!!🌹🌹
如果这篇文章对你有用的话,希望大家给一个三连支持一下!!🌹🌹

在这里插入图片描述

http://www.xdnf.cn/news/16325.html

相关文章:

  • LeetCode 1577.数的平方等于两数乘积的方法数
  • Spring 解析 XML 配置文件的过程(从读取 XML 到生成 BeanDefinition)
  • HarmonyOS NEXT 系列之规范开发三方共享包
  • Java学习-------序列化与反序列化
  • MGRE综合实验
  • 【Linux | 网络】传输层(UDP和TCP)
  • 笔记本键盘的启用和禁用
  • Rust实战:高效开发技巧
  • 强连通分量:Kosaraju算法
  • 使用Python绘制动态樱花
  • CentOS 镜像源配置与 EOL 后的应对策略
  • 【C++篇】STL的关联容器:unordered_map和unordered_set(上篇):哈希表的模拟实现
  • Triton Shared编译
  • Linux网络-------2.应⽤层⾃定义协议与序列化
  • 大模型算法面试笔记——常用优化器SGD,Momentum,Adagrad,RMSProp,Adam
  • Spring MVC设计精粹:源码级架构解析与实践指南
  • AI Coding IDE 介绍:Cursor 的入门指南
  • 深度学习计算(深度学习-李沐-学习笔记)
  • Python 程序设计讲义(23):循环结构——循环控制语句 break 与 continue
  • 【笔记】Einstein关系式 D = ukBT 的推导与应用研究
  • 【自动化运维神器Ansible】Ansible常用模块之hostname模块详解
  • Java面试实战:企业级性能优化与JVM调优全解析
  • 【编号444】雅鲁藏布江(上中下)游8级水系湖泊数据合集
  • cacti漏洞CVE-2022-46169的复现
  • Java:采用mybatis+pagehealper优雅的实现分页功能
  • 如何筛选适合自己阅读的文献?高效文献调研流程?
  • 【C++高效编程】STL queue深度剖析:从底层原理到高级应用
  • FastAPI入门:安装、Pydantic、并发和并行
  • 嵌入式硬件篇---有线串口通信问题解决
  • 使用Clion开发STM32(Dap调试)