BFS 最短路径

news2025/1/13 3:10:39

目录

原理剖析:

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;
    }
};

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

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

相关文章

PTA题解 --- N个数求和(C语言)

今天是PTA题库解法讲解的第二天&#xff0c;今天我们要讲解N个数求和&#xff0c;题目如下&#xff1a; 要解决这个问题&#xff0c;我们可以用C语言编写一个程序来处理和简化分数。程序的基本思路如下&#xff1a; 1. 定义一个函数来计算两个数的最大公约数&#xff08;GCD&a…

【Python爬虫基础教程 | 第一篇】URL、HTTP基础必知必会

前言 该专栏开设的目的在于给初学者提供一个学习爬虫的成长平台&#xff0c;文章涉及内容均为必备知识。 可订阅专栏&#xff1a;【Python爬虫教程】 | CSDN秋说 文章目录 前言URL概念及组成结构HTTP概念简述浏览器接收资源HTTP协议的结构请求结构请求行请求头请求体请求差异及…

win10虚拟机安装驱动教程

在虚拟机菜单栏中选择安装VMware Tools&#xff1a; 安装好后&#xff0c;在虚拟机中打开此电脑&#xff0c;双击DVD驱动器进行安装&#xff1a; 一直点击下一步&#xff1a; 安装完成&#xff1a; 此时重启虚拟机&#xff0c;发面小屏幕页面的虚拟机自动占满了全部屏幕&#x…

Java Web开发从0到1

文章目录 总纲第1章 Java Web应用开发概述1.1 程序开发体系结构1.1.1 C/S体系结构介绍1.1.2 B/S体系结构介绍1.1.3 两种体系结构的比较1.2 Web应用程序的工作原理1.3 Web应用技术1.3.1 客服端应用技术1.3.2 服务端应用技术1.4 Java Web应用的开发环境变量1.5 Tomcat的安装与配置…

OpenCV(八)——基本线条操作

基本线条操作 OpenCV中提供了基本的线条的操作&#xff0c;包括画直线、画矩形、画圆形等。 &#xff08;1&#xff09;画直线&#xff0c;在OpenCV中利用line()画直线&#xff0c;形式为image_with_line cv2.line(image, start_point, end_point, color, thickness)。line(…

使用 Docker Compose 快速搭建监控网站 uptime-kuma

有时候需要监控自己搭建的一些网站、服务是否正常运行&#xff0c; 这时候可以考虑使用一个监控网站&#xff0c; 定时的进行检测&#xff0c; 记录网站、服务的运行状态&#xff0c; 在这推荐使用 uptime-kuma。 博主博客 https://blog.uso6.comhttps://blog.csdn.net/dxk539…

学生打架支小蜜AI校园防欺凌系统可以识别到吗?

随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;已经渗透到我们生活的方方面面&#xff0c;为我们的生活带来了极大的便利。在校园安全领域&#xff0c;AI技术的应用也日益广泛&#xff0c;其中&#xff0c;AI校园防欺凌系统更是受到了广泛关注。那么&#…

【SystemVerilog】结构体真是太好用了~

前言 Verilog最大的缺陷之一是没有数据结构。在SystemVerilog中可以使用struct创建结构&#xff0c;struct只是把数据组织到一起&#xff0c;是数据的集合&#xff0c;所以是可综合的。 结构体是可以通过模块接口进行传递的&#xff0c;这就是本文想描述的内容。 一、结构体的…

进程及进程状态

1.PCB及task_struct 进程信息被放在一个叫做进程控制块的数据结构中&#xff0c;可以理解为进程属性的集合。 书上称之为 PCB &#xff08; process control block &#xff09;&#xff0c; Linux 操作系统下的 PCB 是 : task_struct。 task_struct是PCB的一种。 task_struc…

MySQL教程-安装与卸载

MySQL官网 https://www.mysql.com MySQL 官方提供了两种不同的版本&#xff1a; 社区版 MySQL Community Server&#xff0c;免费&#xff0c;但不提供任何技术支持商业版 MySQL Enterprise Server&#xff0c;收费&#xff0c;官方可提供技术支持 本教程采用MySQL的社区版作…

看!Chat4.0如何看待AI与光纤资源管理软件的应用结合点及价值

问&#xff1a;你好&#xff0c;AI在光纤资源管理软件中有那些应用结合点&#xff0c;请详细描述应用结合点及价值? 答&#xff1a;AI在光纤资源管理软件中的应用结合点涉及多个方面&#xff0c;它们通过智能化的手段提高资源管理的效率和准确性。以下是一些关键的应用结合点及…

阿里云服务器centos安装msf教程

msf官方命令行一键安装 curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > msfinstall && chmod 755 msfinstall && ./msfinstall 稍微等待几分钟即可安装成功&am…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:Progress)

进度条组件&#xff0c;用于显示内容加载或操作处理等进度。 说明&#xff1a; 该组件从API version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 无 接口 Progress(options: ProgressOptions<Type>) 创建进度组件&a…

VGG网络的代码实现

VGG网络的程序实现完全根据配置表来实现。 全连接层之前的部分属于特征提取部分&#xff0c;后三部分全连接层用来分类。 1、模型 import torch.nn as nn import torch# official pretrain weights #预训练的权重下载地址 model_urls {vgg11: https://download.pytorch.org/…

使用opencv进行图片分析

opencv学习 一、配置环境并打开编译器 配置opencv在你的任意一个盘里创建一个专属于opencv的文件夹便于学习与整理 打开控制台winr输入cmd&#xff0c;进入后输入conda activate opencv&#xff0c;进入环境以后进入你所设置的opencv文件的盘&#xff0c;我的是D盘&#xff0…

fastreport循环数据表

1.创建数据源 2.将数据源关联到数据区 3.配置控件及属性 拖拽文本控件&#xff0c;设置文本控件的style属性为Data 4.比对效果 比对数据库和报表的数据一致。且循环显示。 数据库数据 报表展示数据

【Qt】从QMainWindow到UI框架

目录 简介UI布局元素Central WidgetMenu BarToolbarsStatus BarDock Widgets 参考文档 简介 如下图所示&#xff0c;我们常见的一些desktop软件&#xff0c;比如VS Code、Smart VCI等&#xff0c;一般都会包含顶部的菜单栏&#xff0c;底部的状态栏&#xff0c;以及一些其他UI…

工业制造领域系统:SCADA、PLC、DCS、MES、HMI、ERP等,一文秒懂

工业制造控制系统在工业制造领域起到了关键的作用&#xff0c;帮助企业提高生产效率、降低成本、提高产品质量和安全性。不同的企业根据自身需求和规模&#xff0c;可能会选择使用其中的一种或多种系统。 SCADA系统&#xff08;Supervisory Control and Data Acquisition&…

第2章 信息技术基础

本章学习要点 全面了解医院信息系统建设所涉及的主要信息技术以及这些技术的应用情况。 计算机与网络、信息技术与信息系统、数字媒体与数据存储技术、条形码(二维码)、RFID技术、云计算、APP技术 1.XML 可扩展标记语言与Access&#xff0c;Oracle和SQL Server等数据库不同…

什么软件可以剪辑录音?录音剪辑推荐3款工具

随着数字技术的发展&#xff0c;录音已经成为我们日常生活和工作中不可或缺的一部分。无论是会议记录、课堂笔记&#xff0c;还是音乐创作、语音聊天&#xff0c;我们都需要用到录音功能。然而&#xff0c;单纯的录音往往不能满足我们的需求&#xff0c;我们还需要对录音进行剪…