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

目录

  • 1. 最短路问题介绍
  • 2. 算法原理和代码实现(含题目链接)
    • 1926.迷宫中离入口最近的出口
    • 433.最小基因变化
    • 127.单词接龙
    • 675.为高尔夫比赛砍树
  • 3. 算法总结

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循环),而最短路程就是向外扩展的层数

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

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

相关文章

ant design pro 中用户的表单如何控制多个角色

ant design pro 如何去保存颜色ant design pro v6 如何做好角色管理ant design 的 tree 如何作为角色中的权限选择之一ant design 的 tree 如何作为角色中的权限选择之二ant design pro access.ts 是如何控制多角色的权限的 看上面的图片 当创建或编辑一个用户时&#xff0c;…

自带灭火电池?深蓝SL03托底事故揭秘

近日&#xff0c;网络上的一段热传视频&#xff0c;让不少网友看得先是惊心动魄&#xff0c;然后却又啧啧称奇。 该视频显示&#xff0c;8月18日晚上19点28分&#xff0c;一辆深蓝SL03在行驶中意外遭遇严重托底事故&#xff0c;车辆瞬间腾空跳跃&#xff0c;紧接着底盘出现明火…

禁止浏览器默认填充密码 vue

禁止浏览器默认填充密码会和我的样式冲突 所以禁止 第一种&#xff1a; 通过给表单元素添加 autocomplete"off" 属性&#xff0c; 可以防止浏览器自动填充表单中的账号和密码。可以在 input 标签或整个 form 标签上使用&#xff1a; <template><a-form&g…

向量数据库中的PQ(Procduct Quantization)

为了加快向量之间距离计算和比较速度&#xff0c;有人发明的Product Quantization方法&#xff0c;这个方法并不是一种索引&#xff0c;所以它并不能减少目标向量&#xff08;要查找的向量&#xff09;&#xff0c;与数据库中向量的比较次数&#xff0c;但是它可以加快与每个数…

(第三十三天)

1. 设置主从从 mysql57 服务器 &#xff08; 1 &#xff09;配置主数据库 [rootmsater_5 ~] # systemctl stop filewalld [rootmsater_5 ~] # setenforce 0 [rootmsater_5 ~] # systemctl disable filewalld [rootmsater_5 ~] # ls anaconda-ks.cfg mysql-5.7.44-linux-g…

uniapp 页面跳转传参:父页面监听子页面传过来的数据

父页面 监听events事件 uni.navigateTo({url: "/components/watermark-camera",events: { // 重点重点重点重点重点重点重点重点getImages(data) { // 接收子页面抛出的 getImages 事件console.log("水印相机的照片&#xff1a;", data)}}})子页面 const …

<数据集>航拍牧场奶牛识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;1805张 标注数量(xml文件个数)&#xff1a;1805 标注数量(txt文件个数)&#xff1a;1805 标注类别数&#xff1a;1 标注类别名称&#xff1a;[cow] 序号类别名称图片数框数1cow1805141337 使用标注工具&#xff…

World of Warcraft [CLASSIC] the Eye of Eternity [EOE] P1-P2

World of Warcraft [CLASSIC] the Eye of Eternity [EOE] 永恒之眼&#xff08;蓝龙&#xff09; 第一阶段 第二阶段 第三阶段 载具1-6技能介绍 World of Warcraft [CLASSIC] the Eye of Eternity [EOE]_永恒之眼 eoe-CSDN博客 永恒之眼怎么出副本呢&#xff0c;战斗结束&am…

makefile文件基本语法

一、makefile文件基本介绍 Makefile 文件是 make 工具使用的配置文件&#xff0c;它定义了如何自动化构建项目的规则和命令。Makefile 文件的主要作用是指定如何编译和链接程序&#xff0c;以及管理文件之间的依赖关系&#xff0c;从而实现高效的构建过程。 1.1 Makefile 的基…

uniapp微信小程序 分享功能

uniapp https://zh.uniapp.dcloud.io/api/plugins/share.html#onshareappmessage export default {onShareAppMessage(res) {if (res.from button) {// 来自页面内分享按钮console.log(res.target)}return {title: 自定义分享标题,path: /pages/test/test?id123}} }需要再真机…

Appium定位元素

使用工具&#xff1a; 报错: Error while obtaining UI hierarchy XML file: com.android.ddmlib.SyncException: Remote object doesn‘t 参考链接&#xff1a;Error while obtaining UI hierarchy XML file: com.android.ddmlib.SyncException: Remote object doesn‘t-CSD…

Centos安装Jenkins教程详解版(JDK8+Jenkins2.346.1)

本教程基于 JDK8 和 Jenkins2.346.1 JDK安装 下载OpenJDK8文件 wget https://mirrors.tuna.tsinghua.edu.cn/Adoptium/8/jdk/x64/linux/OpenJDK8U-jdk_x64_linux_hotspot_8u422b05.tar.gz解压到指定目录 # 创建目录 mkdir -p /usr/local/software# 解压文件到指定目录&#…

Eclipse部署一个项目到Tomcat和部署多个项目到Tomcat

Eclipse部署一个项目到Tomcat&#xff1a; https://blog.csdn.net/weixin_42334396/article/details/105902994 Eclipse部署多个项目到Tomcat&#xff1a; https://blog.csdn.net/zhanglin1220/article/details/82056185 使用cmd方法强制关闭端口&#xff0c;解除端口占用方法&…

多元统计分析——基于R语言的单车使用情况可视化分析

注&#xff1a;基于R语言的单车使用情况可视化分析为实验记录&#xff0c;存在不足&#xff0c;自行改进。 一、提出问题&#xff08;要解决或分析的问题&#xff09; 1 、用户对共享单车的使用习惯&#xff0c;环境对共享单车运营带来的影响&#xff1f; 2 、共享单车的租赁…

stripe Element 如何使用

这里要准备好几个东西&#xff1a; 一个支付成功过后的回调 还有一个下单的接口 一旦进入这个下单界面&#xff0c;就要去调下单的接口的&#xff0c;用 post, 这个 接口你自己写&#xff0c;可以写在后端中&#xff0c;也可以放到 nextjs 的 api 中。 首先说的是这个下单…

聚星文社——绘唐科技Ai推文软件

聚星文社——绘唐科技Ai推文软件 聚星文社--绘唐科技Ai推文软件https://iimenvrieak.feishu.cn/docx/ZhRNdEWT6oGdCwxdhOPcdds7nof AI推文软件是一种利用人工智能技术帮助用户自动生成推文内容的工具。 该软件会分析用户提供的相关信息和目标群体&#xff0c; 然后使用机器学习…

闲置物品交易平台网站商城-计算机毕设Java|springboot实战项目

&#x1f393; 作者&#xff1a;计算机毕设小月哥 | 软件开发专家 &#x1f5a5;️ 简介&#xff1a;8年计算机软件程序开发经验。精通Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等技术栈。 &#x1f6e0;️ 专业服务 &#x1f6e0;️ 需求定制化开发源码提…

【运维高级内容--MySQL】

目录 一、mysql安装 二、MySQL主从复制 一、mysql安装 yum install cmake gcc-c openssl-devel ncurses-devel.x86_64 rpcgen.x86_64 #安装依赖性 #在root路径下下载mysql-boost-5.7.44、libtirpc-devel-1.3.3-8.el9_4.x86_64.rpm安装包 yum install libtirpc-devel…

MFC之word操作

MFC对word操作 背景说明 当对程序的内容进行输出时&#xff0c;比如自定义对象属性描述或者注释&#xff08;详细设计&#xff09;生成文档时&#xff0c;如果采用手动输入会比较麻烦&#xff0c;并且当程序变动时&#xff0c;需要再一次修改对应文档&#xff0c;作为程序员做…

ASP.NET Core 入门教程一 创建最小 API

构建最小 API&#xff0c;以创建具有最小依赖项的 HTTP API。 它们非常适合需要在 ASP.NET Core 中仅包括最少文件、功能和依赖项的微服务和应用。 本教程介绍使用 ASP.NET Core 生成最小 API 的基础知识。 启动 Visual Studio 2022 并选择“创建新项目”。 在“创建新项目”…