BFS-程序员宅基地

技术标签: 算法  

BFS

BFS 与 DFS 的时间复杂度相同,都是 O ( n + m ) O(n + m) O(n+m),其中 n 表示图的节点数,m 表示图的边数。

当树或者图的所有边的权重都是 1 时,才可以使用 BFS 求最短距离,否则应该使用 DFS 考虑所有情况。

当权重不是 1 的时候,应该使用 Dijkstra 等算法来求最短距离。

BFS 原理

队列 —> 先进先出 —> BFS

栈 —> 后进先出 —> DFS

所有的 BFS、DFS 都可以对应一颗树。

bfs 遍历顺序:image-20201014201015283

bfs 实现遍历主要是依靠 队列 来做。每次取出队头元素,然后再将队头元素的所有子节点加入队尾,一开始将根节点放入空队列中,这样就实现 bfs 遍历了。

image-20201014201617028

通过上图,你会惊奇地发现:队头元素出队的顺序 与 元素加入队列的顺序 竟然一样!

BFS 代码

实现 bfs 需要两种数组: 判重数组(定义为布尔数组 st)队列(用数组实现)

绝大部分 bfs 都是在节点入队时判重,但是特殊的算法(比如 Dijkstra)会在出队时判重。

为什么需要判重数组?

没有判重数组的话,bfs 搜索树可能会出现 ,这样就会导致死循环的发生。

队列在 BFS 中的操作

对于 队列操作,bfs 框架为:

queue <-- 初始状态
while (queue 非空) {
    
	t <-- 队头元素
   for (拓展 t) {
    
       ver <-- 新节点
       if (!st[ver]) {
    
           在队尾插入 ver
       }
   }
}

BFS 解决的问题

**迷宫问题是 BFS 最重要的一个问题。我们常常用 BFS 搜索从起点到终点的最短路步长,最短步长就是 BFS 递归的层数。**这是因为 bfs 搜索树的每一层的节点都代表了一种选择,它是按照距离来遍历点的,一开始遍历距离为 1 的点,再从距离为 1 的点开始,遍历离起点 距离 为 2 的点,以此类推…… 因此从起点到终点的最小层数就是短步长。

比如这个迷宫的 bfs 搜索树(S 表示起点,E 表示终点)

image-20201014204537343

树与图的 BFS

image-20201119162614003

错误

利用 BFS 求从节点 1 到节点 n 的最短距离。

错误代码:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <queue>

using namespace std;

const int N = 100010, M = 2 * N;
int n, m;
int h[N], e[M], ne[M], idx;
bool st[N];
int d[N]; // d[i]存储从1号点到i号店的最短路径,可以将 d 数组初始化为 -1,表示当前节点还没有被遍历过。
//! 注意,对于树来说,你可以使用布尔数组st[i]来表示当前节点是否已经被遍历过。这是因为在树结构中,i号节点只可能被它的父节点遍历
//! 注意,对于图来说,你不能用一个布尔数组st[i]来表示当前节点是否已经被遍历过。这是因为在图结构中,可能有多个节点指向i号节点

void init() {
    
    memset(h, -1, sizeof h);
    memset(d, -1, sizeof d);
}

void add(int a, int b) {
    
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}

void bfs() {
    
    queue<int> q;
    q.push(1);
    // st[1] = true;

    while(q.size()) {
    
        int now = q.front();
        q.pop();

        if (!st[now]) {
    
            st[now] = true;
            for (int i = h[now]; ~i; i = ne[i]) {
    
                int j = e[i];
                
                q.push(j);
                d[j] = d[now] + 1;
                if (j == n) return;
            }
        }
    }
}

int main()
{
    
    init();
    scanf("%d%d", &n, &m);

    int a, b;
    for (int i = 1; i <= m; ++i) {
    
        scanf("%d%d", &a, &b);
        add(a, b);
    }

    bfs();
    printf("%d\n", d[n]);
}

正确代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <queue>

using namespace std;

const int N = 100010, M = 2 * N;
int n, m;
int h[N], e[M], ne[M], idx;
bool st[N];
int d[N]; // d[i]存储从1号点到i号店的最短路径,可以将 d 数组初始化为 -1,表示当前节点还没有被遍历过。
//! 注意,对于树来说,你可以使用布尔数组st[i]来表示当前节点是否已经被遍历过。这是因为在树结构中,i号节点只可能被它的父节点遍历
//! 注意,对于图来说,你不能用一个布尔数组st[i]来表示当前节点是否已经被遍历过。这是因为在图结构中,可能有多个节点指向i号节点

void init() {
    
    memset(h, -1, sizeof h);
    memset(d, -1, sizeof d);
}

void add(int a, int b) {
    
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}

void bfs() {
    
    queue<int> q;
    q.push(1);
    // st[1] = true;
    d[1] = 0; //* 这一步初始化容易忘记

    while(q.size()) {
    
        int now = q.front();
        q.pop();


        for (int i = h[now]; ~i; i = ne[i]) {
    
            int j = e[i];
            
            if (d[j] == -1) {
    
                q.push(j);
                d[j] = d[now] + 1;
                if (j == n) return;
            }

        }
    }
}

int main()
{
    
    init();
    scanf("%d%d", &n, &m);

    int a, b;
    for (int i = 1; i <= m; ++i) {
    
        scanf("%d%d", &a, &b);
        add(a, b);
    }

    bfs();
    printf("%d\n", d[n]);
}

AcWing

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_44960253/article/details/110245727

智能推荐

解决win10/win8/8.1 64位操作系统MT65xx preloader线刷驱动无法安装_mt65驱动-程序员宅基地

文章浏览阅读1.3w次。转载自 http://www.miui.com/thread-2003672-1-1.html 当手机在刷错包或者误修改删除系统文件后会出现无法开机或者是移动定制(联通合约机)版想刷标准版,这时就会用到线刷,首先就是安装线刷驱动。 在XP和win7上线刷是比较方便的,用那个驱动自动安装版,直接就可以安装好,完成线刷。不过现在也有好多机友换成了win8/8.1系统,再使用这个_mt65驱动

SonarQube简介及客户端集成_sonar的客户端区别-程序员宅基地

文章浏览阅读1k次。SonarQube是一个代码质量管理平台,可以扫描监测代码并给出质量评价及修改建议,通过插件机制支持25+中开发语言,可以很容易与gradle\maven\jenkins等工具进行集成,是非常流行的代码质量管控平台。通CheckStyle、findbugs等工具定位不同,SonarQube定位于平台,有完善的管理机制及强大的管理页面,并通过插件支持checkstyle及findbugs等既有的流..._sonar的客户端区别

元学习系列(六):神经图灵机详细分析_神经图灵机方法改进-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏27次。神经图灵机是LSTM、GRU的改进版本,本质上依然包含一个外部记忆结构、可对记忆进行读写操作,主要针对读写操作进行了改进,或者说提出了一种新的读写操作思路。神经图灵机之所以叫这个名字是因为它通过深度学习模型模拟了图灵机,但是我觉得如果先去介绍图灵机的概念,就会搞得很混乱,所以这里主要从神经图灵机改进了LSTM的哪些方面入手进行讲解,同时,由于模型的结构比较复杂,为了让思路更清晰,这次也会分开几..._神经图灵机方法改进

【机器学习】机器学习模型迭代方法(Python)-程序员宅基地

文章浏览阅读2.8k次。一、模型迭代方法机器学习模型在实际应用的场景,通常要根据新增的数据下进行模型的迭代,常见的模型迭代方法有以下几种:1、全量数据重新训练一个模型,直接合并历史训练数据与新增的数据,模型直接离线学习全量数据,学习得到一个全新的模型。优缺点:这也是实际最为常见的模型迭代方式,通常模型效果也是最好的,但这样模型迭代比较耗时,资源耗费比较多,实时性较差,特别是在大数据场景更为困难;2、模型融合的方法,将旧模..._模型迭代

base64图片打成Zip包上传,以及服务端解压的简单实现_base64可以装换zip吗-程序员宅基地

文章浏览阅读2.3k次。1、前言上传图片一般采用异步上传的方式,但是异步上传带来不好的地方,就如果图片有改变或者删除,图片服务器端就会造成浪费。所以有时候就会和参数同步提交。笔者喜欢base64图片一起上传,但是图片过多时就会出现数据丢失等异常。因为tomcat的post请求默认是2M的长度限制。2、解决办法有两种:① 修改tomcat的servel.xml的配置文件,设置 maxPostSize=..._base64可以装换zip吗

Opencv自然场景文本识别系统(源码&教程)_opencv自然场景实时识别文字-程序员宅基地

文章浏览阅读1k次,点赞17次,收藏22次。Opencv自然场景文本识别系统(源码&教程)_opencv自然场景实时识别文字

随便推点

ESXi 快速复制虚拟机脚本_exsi6.7快速克隆centos-程序员宅基地

文章浏览阅读1.3k次。拷贝虚拟机文件时间比较长,因为虚拟机 flat 文件很大,所以要等。脚本完成后,以复制虚拟机文件夹。将以下脚本内容写入文件。_exsi6.7快速克隆centos

好友推荐—基于关系的java和spark代码实现_本关任务:使用 spark core 知识完成 " 好友推荐 " 的程序。-程序员宅基地

文章浏览阅读2k次。本文主要实现基于二度好友的推荐。数学公式参考于:http://blog.csdn.net/qq_14950717/article/details/52197565测试数据为自己随手画的关系图把图片整理成文本信息如下:a b c d e f yb c a f gc a b dd c a e h q re f h d af e a b gg h f bh e g i di j m n ..._本关任务:使用 spark core 知识完成 " 好友推荐 " 的程序。

南京大学-高级程序设计复习总结_南京大学高级程序设计-程序员宅基地

文章浏览阅读367次。南京大学高级程序设计期末复习总结,c++面向对象编程_南京大学高级程序设计

4.朴素贝叶斯分类器实现-matlab_朴素贝叶斯 matlab训练和测试输出-程序员宅基地

文章浏览阅读3.1k次,点赞2次,收藏12次。实现朴素贝叶斯分类器,并且根据李航《统计机器学习》第四章提供的数据训练与测试,结果与书中一致分别实现了朴素贝叶斯以及带有laplace平滑的朴素贝叶斯%书中例题实现朴素贝叶斯%特征1的取值集合A1=[1;2;3];%特征2的取值集合A2=[4;5;6];%S M LAValues={A1;A2};%Y的取值集合YValue=[-1;1];%数据集和T=[ 1,4,-1;..._朴素贝叶斯 matlab训练和测试输出

Markdown 文本换行_markdowntext 换行-程序员宅基地

文章浏览阅读1.6k次。Markdown 文本换行_markdowntext 换行

错误:0xC0000022 在运行 Microsoft Windows 非核心版本的计算机上,运行”slui.exe 0x2a 0xC0000022″以显示错误文本_错误: 0xc0000022 在运行 microsoft windows 非核心版本的计算机上,运行-程序员宅基地

文章浏览阅读6.7w次,点赞2次,收藏37次。win10 2016长期服务版激活错误解决方法:打开“注册表编辑器”;(Windows + R然后输入Regedit)修改SkipRearm的值为1:(在HKEY_LOCAL_MACHINE–》SOFTWARE–》Microsoft–》Windows NT–》CurrentVersion–》SoftwareProtectionPlatform里面,将SkipRearm的值修改为1)重..._错误: 0xc0000022 在运行 microsoft windows 非核心版本的计算机上,运行“slui.ex