BFS 最短路径

目录

原理剖析:

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

2、 433. 最小基因变化

3、 127. 单词接龙

4、 675. 为高尔夫比赛砍树


原理剖析:

 为什么BFS能够解决最短路径问题?

对于无权图(边权为1)或所有边权重相等的情况,BFS是一种有效的最短路径解决方案。

BFS(广度优先搜索)之所以能够解决最短路径问题,主要是因为它按照从近到远的顺序探索所有的顶点。具体来说,BFS有以下几个特点使其适用于求解最短路径问题:

  1. 分层搜索:BFS会首先探索起点的所有邻接点,即距离为1的顶点。然后再探索这些顶点的邻接点,即距离为2的顶点,以此类推。这种分层的搜索保证了当我们第一次到达任何一个顶点时,我们找到的就是从起点到该顶点的最短路径。

  2. 无需权重:在无权图中,所有边的长度被视为相同(通常为1)。因此,BFS通过探索相邻的所有顶点,自然而然地找到了最短路径。

  3. 队列的使用:BFS使用队列来存储待探索的顶点,这保证了顶点是按照它们被发现的顺序进行探索的,即先入先出的顺序,这也是保持层级顺序和找到最短路径的关键。

由于BFS是逐层探索的,当你第一次到达任何顶点时,你肯定是通过最少数量的边到达的。换句话说,你找到的是从起点到该顶点的最短路径(在无权图中即最少步数)。

  1. 初始层(距离为0):首先探索起点本身。这可以被看作是第0层,因为它距离起点的距离为0。

  2. 第一层(距离为1):接下来,探索所有直接与起点相邻的顶点。这些顶点构成了第一层,因为你必须经过一条边才能从起点到达这些顶点。

  3. 第二层(距离为2):然后,从第一层的顶点出发,探索与它们相邻但尚未被探索的顶点。这些新探索到的顶点构成了第二层,因为从起点到这些顶点需要经过两条边。

  4. 以此类推:这个过程会继续进行,每次从最新发现的层的顶点出发,探索下一层的顶点。

这种分层的探索方法是BFS能够找到最短路径的关键。因为BFS从起点开始,先探索所有最近的顶点(一步可以到达的),然后是次近的(两步可以到达的),依此类推,所以当它第一次到达一个新顶点时,路径长度(或步数)必定是最小的。

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

 思路:我们采用层序遍历(也称为广度优先搜索,BFS)的方法。这种方法不仅适用于迷宫探索,还能帮助我们找到从入口到最近出口的最短路径。BFS使用队列逐层搜索,vis数组记录是否被访问过,step统计步数。

class Solution {
public:
    int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
    int nearestExit(vector<vector<char>>& maze, vector<int>& entrance) {
        int m = maze.size(), n = maze[0].size();
        bool vis[m][n];
        memset(vis, false, sizeof vis);
        queue<pair<int, int>> q;
        q.push(make_pair(entrance[0], entrance[1]));
        vis[entrance[0]][entrance[1]] = true;
        int step = 0;
        while (!q.empty()) {
            step++;
            int sz = q.size();
            for (int i = 0; i < sz; i++) {
                auto a = q.front();
                q.pop();
                for (int j = 0; j < 4; j++) {
                    int x = a.first + dx[j], y = a.second + 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;
                        q.push(make_pair(x, y));
                        vis[x][y] = true;
                    }
                }
            }
        }
        return -1;
    }
};
  1. 初始化:首先,我们需要一个访问标记数组 vis 来记录哪些位置已经被访问过,防止重复访问造成无限循环。同时,创建一个队列 q 用于存放待探索的迷宫格子。

  2. 开始探索:将入口位置入队,并标记为已访问。

  3. 步数增加:每次开始一轮队列中所有位置的探索,步数增加1,这代表从入口开始,每一步移动都在寻找可能的出口。

  4. 层序遍历:进行宽度优先搜索,每一轮循环对队列中的所有位置进行探索,对于每一个位置,尝试向上、下、左、右四个方向移动。

    • 如果移动后的新位置在迷宫内、是空格子('.'),并且之前未被访问过,则检查这个新位置是否是出口。出口的定义是位于迷宫边界上的空格子,但不包括入口位置。如果是出口,直接返回当前的步数(即从入口到这个出口的最短路径长度)。
    • 如果新位置不是出口,则将其加入队列中,以便进一步的探索,并标记为已访问。
  5. 无法到达出口:如果队列为空,即所有可达的位置都已被探索过,仍然没有找到出口,则说明无法从入口到达任何出口,返回 -1。

2、 433. 最小基因变化

 思路:我们可以把每个基因序列看作是图中的一个顶点,而每次基因的变化则相当于图中两个顶点之间的一条边。这样,问题就转变成了寻找从起始顶点(start 基因序列)到目标顶点(end 基因序列)的最短路径问题,其中每次路径上的移动代表一个基因的变化。

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);
        int ret = 0;
        string ch = "ACGT";
        while (!q.empty()) {
            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] = ch[j];
                        if (hash.count(tmp) && !vis.count(tmp)) {
                            if (tmp == endGene)
                                return ret;
                            q.push(tmp);
                            vis.insert(tmp);
                        }
                    }
                }
            }
        }
        return -1;
    }
};
  1. 初始化:创建一个访问集合 vis 来记录已经访问过的基因序列,以及一个哈希集合 hash 来存储基因库中所有有效的基因序列。这样可以快速判断一个基因序列是否有效。

  2. 特殊情况处理:如果起始基因序列与目标基因序列相同,则不需要进行任何变化,直接返回 0。如果目标基因序列不在基因库中,说明无法通过变化达到目标,返回 -1。

  3. BFS 开始:从起始基因序列开始进行宽度优先搜索。每一次搜索的过程中,尝试改变当前基因序列的每一个位置为 'A'、'C'、'G'、'T' 中的任意一个,生成新的基因序列。

  4. 有效性和目标检查:对于每一个生成的新基因序列,如果它是有效的(即在基因库中)并且之前没有被访问过,则检查它是否为目标基因序列。如果是,那么当前的变化次数就是所求的最少变化次数;如果不是,就将其加入到队列中,以便进一步的搜索。

  5. 继续搜索直到找到解:重复步骤 3 和 4,直到找到目标基因序列或者搜索结束。每遍历完一层,变化次数加一。

  6. 返回结果:如果能找到目标基因序列,返回所需的最少变化次数;否则,说明无法通过变化达到目标,返回 -1。

3、 127. 单词接龙

思路:与第二题“433. 最小基因变化”方法等同。

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 (!hash.count(endWord))
            return 0;
        queue<string> q;
        q.push(beginWord);
        vis.insert(beginWord);
        int ret = 1;
        while (q.size()) {
            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) && !vis.count(tmp)) {
                            if (tmp == endWord)
                                return ret;
                            q.push(tmp);
                            vis.insert(tmp);
                        }
                    }
                }
            }
        }
        return 0;
    }
};

4、 675. 为高尔夫比赛砍树

 思路:首先需要找到砍树的顺序,然后按照砍树的顺序,⼀个⼀个的⽤ bfs 求出最短路即可。

class Solution {
public:
    int m, n;
    int cutOffTree(vector<vector<int>>& forest) {
        vector<pair<int, int>> trees;
        m = forest.size(), n = forest[0].size();
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (forest[i][j] > 1)
                    trees.push_back(make_pair(i, j));
            }
        }
        sort(trees.begin(), trees.end(),
             [&](const pair<int, int>& p1, const pair<int, int>& p2) {
                 return forest[p1.first][p1.second] <
                        forest[p2.first][p2.second];
             });
        int bx = 0, by = 0, ret = 0;
        for (auto& a : trees) {
            int step = bfs(forest, bx, by, a.first, a.second);
            if (step == -1)
                return -1;
            ret += step;
            bx = a.first, by = a.second;
        }
        return ret;
    }
    vector<vector<bool>> vis;
    int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
    int bfs(vector<vector<int>> forest, int bx, int by, int ex, int ey) {
        if (bx == ex && by == ey)
            return 0;
        vector<vector<bool>> vis(m, vector<bool>(n, false));
        queue<pair<int, int>> q;
        q.push(make_pair(bx, by));
        vis[bx][by] = true;
        int step = 0;
        while (q.size()) {
            step++;
            int sz = q.size();
            while (sz--) {
                auto a = q.front();
                q.pop();
                for (int i = 0; i < 4; i++) {
                    int x = a.first + dx[i], y = a.second + dy[i];
                    if (x >= 0 && x < m && y >= 0 && y < n && forest[x][y] &&
                        !vis[x][y]) {
                        if (x == ex && y == ey)
                            return step;
                        q.push(make_pair(x, y));
                        vis[x][y] = true;
                    }
                }
            }
        }
        return -1;
    }
};

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

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

相关文章

ASP.NET Mvc+FFmpeg+Video实现视频转码

目录 首先&#xff0c;做了视频上传的页面&#xff1a; FFmpeg&#xff1a;视频转码 FFmpegHelper工作类&#xff1a; 后台控制器代码&#xff1a; 前端视图代码&#xff1a; 参考文章&#xff1a; 首先&#xff0c;做了视频上传的页面&#xff1a; 借鉴了这篇文章 ASP.…

【pycharm】如何将pacharm设置成中文

【pycharm】汉化教程——如何将pacharm设置成中文 1、打开pycharm 2、点击file 3、点击setting——Plugins——搜索Chinese——点击如下图图标进行下载 汉化后界面情况&#xff1a;

【数据结构与算法】(13):交换排序之冒泡排序和快速排序

&#x1f921;博客主页&#xff1a;Code_文晓 &#x1f970;本文专栏&#xff1a;数据结构与算法 &#x1f63b;欢迎关注&#xff1a;感谢大家的点赞评论关注&#xff0c;祝您学有所成&#xff01; ✨✨&#x1f49c;&#x1f49b;想要学习更多数据结构与算法点击专栏链接查看&…

生成器模式(软考uml C++版)

按照软考中级软件设计师中指定的生成器模式uml图&#xff0c;可编写对应的C&#xff0b;&#xff0b;代码&#xff1a; #include<iostream> #include<vector> #include<string> using namespace std;/*创建者模式&#xff0c;又名生成器模式意图&#xff1a…

每日五道java面试题之springMVC篇(四)

目录&#xff1a; 第一题. Spring MVC怎么样设定重定向和转发的&#xff1f;第二题.Spring MVC怎么和AJAX相互调用的&#xff1f;第三题. 如何解决POST请求中文乱码问题&#xff0c;GET的又如何处理呢&#xff1f;第四题. Spring MVC的异常处理&#xff1f;第五题. 如果在拦截请…

【JWT】入门 *JWT*,并封装一个实用的 *JWT* 工具类

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ 【JWT】入门 *JWT*&#xff0c;并封装一个实用…

SQLiteC/C++接口详细介绍之sqlite3类(八)

返回目录&#xff1a;SQLite—免费开源数据库系列文章目录 上一篇&#xff1a;SQLiteC/C接口详细介绍之sqlite3类&#xff08;七&#xff09; 下一篇&#xff1a; SQLiteC/C接口详细介绍之sqlite3类&#xff08;八&#xff09;&#xff08;暂未发表&#xff09; 24.sqlite3_cr…

网络安全msf学习1

工具&#xff1a;netcat 用途 &#xff1a;端口连接、数据提交 工具nmap 用途&#xff1a;端口扫描、服务识别、操作系统指纹识别 工具 httprint 用途&#xff1a;通过远程http指纹判断http服务类型 工具&#xff1a; tamper ie 用途&#xff1a; http数据包修改、转发工…

SpringMVC基础之工作流程

文章目录 SpringMVC 的工作流程1. 总图2. DispatcherServlet3. 必需的配置4. 加载配置文件的两个时机5. 定义控制器6. 创建 JSP 视图 SpringMVC 的工作流程 1. 总图 如上图&#xff0c;Spring MVC 程序的完整执行流程如下&#xff1a; 用户通过浏览器发送请求&#xff0c;请求…

怎样提升小程序日活?签到抽奖可行吗?

一、 日活运营策略 小程序应该是即用即走的&#xff0c;每个小程序都在用户中有自己的独特定位&#xff0c;可能是生活日常必备&#xff08;美食、团购、商城&#xff09;&#xff0c;也可能是工作办公必备&#xff08;文档、打卡、工具&#xff09;。 如果你想要让自己的小程…

sqllab第十九关通关笔记

知识点&#xff1a; 错误注入 最大长度为32位&#xff1b;如果目标长度>32时&#xff0c;需要利用截取函数进行分段读取referer注入 insert语句update语句 通过admin admin进行登录发现页面打印除了referer字段的信息 这应该是一个referer注入 首先进行测试一下 构造payl…

实现elasticsearch和数据库的数据同步

1. 数据同步 elasticsearch中的酒店数据来自于mysql数据库&#xff0c;因此mysql数据发生改变时&#xff0c;elasticsearch也必须跟着改变&#xff0c;这个就是elasticsearch与mysql之间的数据同步。 1.1. 思路分析 常见的数据同步方案有三种&#xff1a; 同步调用 异步通知…

在macOS上安装Homebrew教程

1.打开终端&#xff1a; 打开Finder&#xff0c;转到应用程序 > 实用工具文件夹&#xff0c;然后双击终端.app。 或者&#xff0c;使用Spotlight搜索&#xff08;按下 Command(⌘) Spacebar&#xff09;并输入“终端”&#xff0c;然后回车以打开。 也可以像我一样把终端…

【SQL Server】实验五 视图

1 实验目的 掌握SQL视图语句的基本使用方法&#xff0c;如CREATE VIEW、DROP VIEW。掌握视图更新、WITH CHECK OPTION等高级功能的使用。 2 实验内容 2.1 掌握SQL视图语句的基本使用方法 创建视图&#xff08;省略视图列名&#xff09;。创建视图&#xff08;不能省略列名的…

(三)丶RabbitMQ的四种类型交换机

前言&#xff1a;四大交换机工作原理及实战应用 1.交换机的概念 交换机可以理解成具有路由表的路由程序&#xff0c;仅此而已。每个消息都有一个称为路由键&#xff08;routing key&#xff09;的属性&#xff0c;就是一个简单的字符串。最新版本的RabbitMQ有四种交换机类型&a…

专业无网设备如何远程运维?向日葵远程控制能源场景案例解析

清洁能源领域&#xff0c;拥有庞大的上下游产业链&#xff0c;涉及的相关工业设备门类多、技术覆盖全、行业应用广。在这一领域内&#xff0c;相关专业设备的供应商的核心竞争力除了本身产品的技术能力之外&#xff0c;服务也是重要的一环。 某企业作为致力于节能环保方向的气…

XML语言的学习记录1

学习笔记&#xff1a; xml&#xff08;可扩展标记语言&#xff09;语言没有预定义的标签&#xff0c;都是使用者自定义&#xff1b;xml是纯文本&#xff0c;是不作为的&#xff1b;语法 每个标签必须有关闭标签&#xff1b;对大小写敏感&#xff1b;最外层必须有根元素&#x…

使用FFmpeg源码配置程序configure查看所有支持的编码器/解码器/封装/解封装及网络协议

查看支持编码器: configure --list-encoders 查看支持编码器: configure --list-decoders 查看所有支持的封装: configure --list-muxers 查看所有支持的解封装: configure --list-demuxers 查看所有支持的网络通信协议: configure --list-protocols

微服务学习day02 -- nacos配置管理 -- Feign远程调用 -- Gateway服务网关

0.学习目标 1.Nacos配置管理 Nacos除了可以做注册中心&#xff0c;同样可以做配置管理来使用。 1.1.统一配置管理 当微服务部署的实例越来越多&#xff0c;达到数十、数百时&#xff0c;逐个修改微服务配置就会让人抓狂&#xff0c;而且很容易出错。我们需要一种统一配置管理…

K8S CNI

OCI概念 OCI&#xff0c;Open Container Initiative&#xff0c;开放容器标准&#xff0c;是一个轻量级&#xff0c;开放的治理结构&#xff08;项目&#xff09;&#xff0c;在 Linux 基金会的支持下成立&#xff0c;致力于围绕容器格式和运行时创建开放的行业标准。 OCI 项目…