【经典算法】BFS_最短路问题

news2024/9/21 10:40:07

1. 最短路问题介绍

最短路径问题是图论中的一类十分重要的问题。本篇文章只介绍边权为1(或边权相同)的最简单的最短路径问题所谓边权,就是两点之间的距离

这类问题通俗的说就是告诉你起点和终点,要你找出最短的路径或是最短路径是多少
在这里插入图片描述

解决方法:从起点开始,来一次bfs即可
A出队列后,向外扩展一层,B,C入队列,注意,此时出队列要B,C同时出(其实是写一个for循环,先B后C)

那如何计算出最短路径是多少呢?
扩展的层数,就是最短路的长度

2. 算法原理和代码实现(含题目链接)

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

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

算法原理:
我们可以从起点开始层序遍历,并且在遍历的过程中记录当前遍历的层数。这样就能在找到出⼝的时候,得到起点到出⼝的最短距离

把题目抽象为:从当前位置出发,到离边界上的那个点的最短路程是多少
在这里插入图片描述

细节/技巧问题:

(1) 人当前所在位置不能当做出口
(2) 出口:与边界相邻的空格就是出口
(3) 人在移动时仅需走到出口位置即可,不需要走出迷宫

代码实现:

class Solution 
{
    int dx[4] = {0,0,1,-1};
    int dy[4] = {1,-1,0,0};

public:
    int nearestExit(vector<vector<char>>& maze, vector<int>& e) 
    {
        int m = maze.size(), n = maze[0].size();
        bool vis[m][n];
        memset(vis, 0, sizeof(vis));

        queue<pair<int, int>> q;
        q.push({e[0], e[1]});
        vis[e[0]][e[1]] = true;

        int step = 0; // 记录步数
        while(q.size())
        {
            step++;
            int sz = q.size();
            // 假设进去sz个,要同时出
            for(int i = 0; i < sz; i++)
            {
                auto[a,b] = q.front();
                q.pop();
                for(int j = 0; j < 4; j++)
                {
                    int x = a+dx[j], y = b+dy[j];
                    if(x >= 0 && x < m && y >= 0 && y < n && maze[x][y] == '.' && !vis[x][y])
                    {
                        // 判断是否到达出口了
                        if(x == 0 || x == m-1 || y == 0 || y == n-1)
                            return step;
                        else
                        {
                            q.push({x, y});
                            vis[x][y] = true;
                        }
                    }
                }
            }
        }
        return -1;
    }
};

433.最小基因变化

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

算法原理:

如果将每次字符串的变换抽象成图中的两个点和⼀条边的话,问题就变成了边权为1的最短路题

![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/36d6a38e830a46e5a5c0fe31fa7f8fc5.png

其他细节/技巧问题:

(1) 原字符串每个字符变化后一定存在相同的,此时要用哈希表来标记搜索的状态,每次变化后都扔进哈希表中
(2) 如何枚举出所有的变化情况呢?直接两层for循环
(3) 变化成哪种字符串才能入队列呢?变化后的字符串在基因库中存在时,才队列。所以把基因库里的字符串扔进哈希表中,每次变化后都要判断是否在基因库中

代码实现:

class Solution 
{
public:
    int minMutation(string startGene, string endGene, vector<string>& bank) 
    {
        unordered_set<string> vis; // 记录字符串是否搜索过 
        unordered_set<string> hash(bank.begin(), bank.end()); // 判断变化后的字符串是否在库中

        //处理特殊情况
        if(startGene == endGene) return 0;
        if(!hash.count(endGene)) return -1;

        queue<string> q;
        q.push(startGene);
        vis.insert(startGene);

        string change = "AGCT";

        int ret = 0; //记录变化次数
        while(q.size())
        {
            ret++; // 就是往外扩展了一层
            int sz = q.size();
            // 每次都要同时出队列
            while(sz--)
            {
                string t = q.front();
                q.pop();
                // 变化过程
                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) && !vis.count(tmp))
                        {
                            // 判断是否已经结束
                            if(tmp == endGene) return ret;
                            q.push(tmp);
                            vis.insert(tmp);
                        }
                    }
                }
            }
        }
        return -1;
    }
};

127.单词接龙

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

算法原理:

这道题和第二题基本一模一样,都是把一个字符串变化成目标字符串,唯一不同的是本题统计的是整个过程中单词的个数,其实就是上一题的最小次数+1

细节/技巧问题:

参考前两题

代码实现:

class Solution 
{
public:
    int ladderLength(string beginWord, string endWord, vector<string>& wordList) 
    {
        unordered_set<string> hash(wordList.begin(), wordList.end());
        unordered_set<string> vis; // 标记已经搜索过的

        //if(beginWord == endWord) return 1;
        if(!hash.count(endWord)) return 0;

        queue<string> q;
        q.push(beginWord);
        vis.insert(beginWord);

        int ret = 0;
        while(q.size())
        {
            ret++;
            int sz = q.size();
            while(sz--)
            {
                string t = q.front();
                q.pop();
                for(int i = 0; i < 10; i++)
                {
                    string tmp = t;
                    for(char ch = 'a'; ch <= 'z'; ch++)
                    {
                        tmp[i] = ch; // 每次只修改一个字符
                        // 存在列表中并且没有被访问过
                        if(!vis.count(tmp) && hash.count(tmp))
                        {
                            if(tmp == endWord) 
                                return ret+1; 

                            q.push(tmp);
                            vis.insert(tmp);
                        }
                    }
                }
            }
        }
        return 0;
    }
};

675.为高尔夫比赛砍树

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

算法原理:

这道题目确实很难
这道题可以抽象成若干个迷宫问题我们只要计算出从这一棵树到下一棵树的最少步数,再把所有的步数相加,就可以求出砍完所有树的最少步数所以这里要多次使用bfs算法,写成函数,它的作用是统计两棵树之间的最少步数
在这里插入图片描述

难点/细节/技巧问题:

(1) 树的坐标如何存储。使用vector容器
(2) 由于要按照高度从低到高开始砍,所以先要把树的高度排升序
(3) bfs的参数是传起点和终点

代码实现:

class Solution 
{
    int m, n;
    int dx[4] = {0,0,1,-1};
    int dy[4] = {1,-1,0,0};
    
public:
    int cutOffTree(vector<vector<int>>& f) 
    {
        m = f.size(), n = f[0].size();
        // 记录树的位置
        vector<pair<int, int>> trees;
        for(int i = 0; i < m; i++)
            for(int j = 0; j < n; j++)
                if(f[i][j] > 1) trees.push_back({i, j}); // 是树,才记录坐标

        // 从低到高开始砍
        sort(trees.begin(), trees.end(), [&](const pair<int, int>& p1,const pair<int, int>& p2){
            return f[p1.first][p1.second] < f[p2.first][p2.second];
        });

        int bx = 0, by = 0; // 起始位置
        int step = 0;
        for(auto& [a,b] : trees)
        {
            // 使用bfs计算两树之间的最短路
            int ret = bfs(f, bx, by, a, b);
            if(ret == -1) return -1;
            step += ret;
            bx = a, by = b; // 更新下一个位置的坐标
        }
        return step;
    }

    bool vis[51][51];
    int bfs(vector<vector<int>>& f, int bx, int by, int ex, int ey)
    {
        if(bx == ex && by == ey) return 0;
        memset(vis, 0, sizeof(vis)); // 每次计算都要把标记还原

        queue<pair<int ,int>> q;
        q.push({bx, by});
        vis[bx][by] = true;

        int ret = 0; // 记录每两颗树之间的最短步数
        while(q.size())
        {
            ret++;
            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 && !vis[x][y] && f[x][y])
                    {
                        // 判断是否走到终点
                        if(x == ex && y == ey) return ret;
                        q.push({x, y});
                        vis[x][y] = true;
                    }
                }
            }
        }
        return -1;
    }
};

3. 算法总结

bfs算法是解决最短路问题的经典方法。我感觉解决最短路问题核心的关键是每一次出队列时都要把上一次入队列的数据全部出完(就要写for循环),而最短路程就是向外扩展的层数

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

每日OJ_牛客_DP3跳台阶扩展问题

目录 DP3跳台阶扩展问题 题解代码1&#xff08;dp&#xff09; 题解代码2&#xff08;找规律&#xff09; DP3跳台阶扩展问题 跳台阶扩展问题_牛客题霸_牛客网 题解代码1&#xff08;dp&#xff09; 假定第一次跳的是一阶&#xff0c;那么剩下的是n-1个台阶&#xff0c;跳法…

汇编语言:jcxz 指令

‌ 在汇编语言中&#xff0c;jcxz 标号 指令是一个有条件转移指令&#xff0c;用于检测CX寄存器的值&#xff0c;如果CX寄存器的值为0&#xff0c;则跳转到指令中标号所指定的地址处执行&#xff1b;否则&#xff0c;指令将继续顺序执行。‌ 1. jcxz 标号 &#xff0…

超市收银出入库进销存库存管理系统java数据库SpringBoot前端VUE

系统设计是一个把软件需求转换成用软件系统表示的过程[9]。通过对目标系统的分析和研究&#xff0c;做出了对师范学院实习管理系统的总体规划&#xff0c;这是全面开发系统的重要基础。在对实习管理系统全面分析调查的基础上&#xff0c;制定出商品销售系统的总体规划。系统设计…

KEEPALIVED的高可用实现

目录 一.高可用集群 1.1 集群类型 Keepalived 环境准备 1.2Keepalived 相关文件 1.3实验步骤 1.3.1HTTP配置 1.3.2安装KA 1.3.3实现master/slave的 Keepalived 单主架构 1.3.4抢占模式和非抢占模式 非抢占模式 抢占模式 抢占延迟模式 preempt_delay 1.3.5开启KA…

习题2.38-2.39

2.38这个题目特别有意思,能比较深的理解什么是左侧优先右侧优先。先看看实现代码 ;;满足交换律,即操作函数的参数可相互交换,其结果不变 (defn fold-right[op initial sequences](if (empty? sequences)initial(op (first sequences)(accumulate op initial (rest sequence…

快讯 | 28页知网侵权函难挡秘塔AI:多家数据库抢着合作!

在数字化浪潮的推动下&#xff0c;人工智能&#xff08;AI&#xff09;正成为塑造未来的关键力量。硅纪元视角栏目紧跟AI科技的最新发展&#xff0c;捕捉行业动态&#xff1b;提供深入的新闻解读&#xff0c;助您洞悉技术背后的逻辑&#xff1b;汇聚行业专家的见解&#xff0c;…

使用 ESP32 和 TFT 屏幕显示实时天气信息 —— 基于 OpenWeatherMap API

实时监测环境数据是一个非常常见的应用场景&#xff0c;例如气象站、智能家居等。这篇博客将带你使用 ESP32 微控制器和一个 TFT 屏幕&#xff0c;实时显示当前城市的天气信息。通过 OpenWeatherMap API&#xff0c;我们能够获取诸如温度、天气情况以及经纬度等详细的天气数据&…

【书生大模型实战营(暑假场)闯关材料】基础岛:第1关 书生大模型全链路开源体系

【书生大模型实战营&#xff08;暑假场&#xff09;闯关材料】基础岛&#xff1a;第1关 书生大模型全链路开源体系 简介一、背景介绍1.1 背景介绍1.2 全链路开源开放体系的优势 二、全链路开源开放体系的主要特点2.1 模型组件的公开和共享2.2 数据集的公开和共享2.3 模型的互操…

2024年【汽车驾驶员(技师)】报名考试及汽车驾驶员(技师)模拟考试题库

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 汽车驾驶员&#xff08;技师&#xff09;报名考试是安全生产模拟考试一点通生成的&#xff0c;汽车驾驶员&#xff08;技师&#xff09;证模拟考试题库是根据汽车驾驶员&#xff08;技师&#xff09;最新版教材汇编出…

Paimon实战 -- paimon原理解析

一.简介 Apache Paimon 原名 Flink Table Store&#xff0c;2022年1月在 Apache Flink 社区从零开始研发&#xff0c;Flink 社区希望能够将 Flink 的 Streaming 实时计算能力和 Lakehouse 新架构优势进一步结合&#xff0c;促进数据在数据湖上真正实时流动起来&#xff0c;并为…

打靶记录12——Fawkes

靶机&#xff1a; https://download.vulnhub.com/harrypotter/Fawkes.ova这是个哈利波特系列的靶机&#xff0c;作者和本人都非常喜欢这个系列&#xff0c;因为它的漏洞和利用点都设计得很巧妙。 难度&#xff1a; 高 目标&#xff1a; 取得2个root权限 3 个flag 涉及攻…

学习二叉树遍历

class Node{ V value; Node left; Node reight; } 用递归和非递归两种方式实现二叉树的先序&#xff0c;中序&#xff0c;后续遍历 如何直观的打印一颗二叉树 如何完成二叉树的宽度优先遍历 递归序列 先序是出现的第一个数字&#xff0c;中序第二个&#xff0c;后续第三…

兼容并蓄,高效集成:EasyCVR视频综合接入能力助力多元化项目需求

随着视频技术的不断进步&#xff0c;视频监控、视频直播、执法记录仪、语音可视对讲、无人机等视频资源的应用场景日益丰富。这些视频资源不仅在数量上快速增长&#xff0c;而且在质量、格式、编码标准等方面也呈现出多样化的特点。因此&#xff0c;为了有效整合这些资源&#…

5.4 视图的创建与管理

欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;欢迎订阅相关专栏&#xff1a; 工&#x1f497;重&#x1f497;hao&#x1f497;&#xff1a;野老杂谈 ⭐️ 全网最全IT互联网公司面试宝典&#xff1a;收集整理全网各大IT互联网公司技术、项目、HR面试真题.…

【Datawhale X 魔搭 】AI夏令营第四期大模型方向,Task3:源大模型RAG实战(持续更新)

什么是RAG&#xff1f; RAG&#xff1a;检索增强生成&#xff08;Retrieval Augmented Generation&#xff09;。 RAG的提出背景&#xff1a; 在实际业务场景中&#xff0c;通用的基础大模型可能存在无法满足我们需求的情况&#xff0c;主要有以下几方面原因&#xff1a; 知识局…

AtCoder Beginner Contest 367 A~D

本期封面原图 画师えびら A - Shout Everyday 题意 b点睡觉c点起床&#xff0c;问你a点是不是醒着的 思路 直接判断即可&#xff0c;注意处理跨天的情况 代码 #include <bits/stdc.h> #define mod 998244353 using namespace std; typedef long long ll; typedef …

推荐 4 款知名免费的思维导图软件,强大跨平台好用,效率大大提升

TreeMind TreeMind树图是一款基于AI人工智能的在线思维导图工具&#xff0c;旨在通过简单的操作快速生成专业详尽的思维导图。它具有多种创新功能和丰富的资源库&#xff0c;能够极大地提高用户的学习和工作效率。 TreeMind树图的主要功能包括&#xff1a; AI一句话生成思维导…

汇编语言:div指令和mul指令

1. div指令 div 是除法指令&#xff0c;在8086CPU中&#xff0c;使用div做除法时&#xff0c;遵循以下规则&#xff1a; &#xff08;1&#xff09;除数&#xff1a;有8位&#xff08;1字节&#xff09;和16位&#xff08;2字节&#xff09;两种&#xff0c;放在一个寄存器&am…

移情别恋c++ ദ്ദി˶ー̀֊ー́ ) ——5.string(模拟实现)

1.存储结构 namespace zone {class string{public:private: //设置私有&#xff0c;不允许随便访问底层数据char* _str; //字符串存储空间首地址指针size_t _size; //当前字符数量size_t _capaicty; //可用容量static const size_t npos;}const size_t string::nops -1;//在类…

MATLAB中ichol函数用法

目录 语法 说明 示例 不完全 Cholesky 分解 使用 ichol 作为预条件子 使用 diagcomp 选项 ichol函数的功能是对矩阵进行不完全 Cholesky 分解。 语法 L ichol(A) L ichol(A,options) 说明 L ichol(A) 通过零填充执行 A 的不完全 Cholesky 分解。A 必须为稀疏方阵。…