简单易懂的BFS
最近学习BFS,看到许多教程都用C++内置的queue,但其实还有更简单的方法。
先了解概念:BFS与DFS不同,举个例子:
我们来用DFS遍历这棵树:A、B、D、E、G、C、F
但我们用BFS遍历这棵树:A、B、C、D、E、F、G
发现了什么:DFS就是一直往下搜索,没有孩子时返回上一步,看有没有其他孩子,假如没有继续往上返回一步,有就继续往下搜索;而BFS却一层一层地搜索,很有规律。
DFS
这是为什么呢?DFS使用的是一个栈,我们想象一下这是一个关系表:
把总裁A入栈后,先把自己的下属经理B入栈,然后B再把自己的下属主管D节点入栈,让Ta来清点Ta自己D手下的工人,然后节点D发现自己没工人了(默哀一秒),便入栈、报告给自己的领导B。B又找到下属主管E,让Ta清点Ta自己E手下的工人,E只有一个工人G,Ta让G入栈,接着自己手下就没人了,于是自己也入栈了。现在B没有其他手下了,只能自己入栈报告给A。A又瞄准C来清点人数,C只有一个工人F,便先把F入栈再自己入栈。这下好了,只有A自己一人只能,自己入栈了,公司人数终于清点完了。
我还自己做了个简易的动图:
BFS
而BFS却得益于用了队列,我们再来想象一下这幅关系表:
把总裁A入队,头指针和尾指针都指向A,A把Ta所有的下属经理入队,接着尾指针往后移把经理B、C入队,然后A发现没有自己直接的上司关系了,便头指针往后移进行出队。然后到了B这边,B有手下D、E俩人,便继续尾指针往后移进行入队,自己则头指针往后移来出队。
现在轮到C了,C只有一个手下F,就让F入队尾指针往后移。
最后轮到D,D没有,头指针往后移出队。E有一个,让G入队,自己出队。轮到F,F没有,出队。最后的最后轮到G,G没有,出队。现在公司没有人了,
因为我们发现是不头指针和尾指针相交成一个“×”形,头尾相连了。
也可以看一看我的动图:
BFS的优点
BFS用的是队列,相比于DFS会更快。但是,肯定有人说:那不可以记忆化+DFS呢?DFS当数据大的时候会爆栈,也就是MLE,而BFS则不会。
BFS有个显著的优点:就是先进先出,就是说先遍历的那个点一定是步数最少的一个,所以遇到走迷宫问题,DFS要一个位置遍历许多次,而BFS只需1次。但遇到图遍历,就差不多了。
BFS模板题
先看一到模板题(洛谷上没有只能自己出qwq):
一棵树共有nnn个点,kkk条边,接下来的kkk行每行输入xxx、yyy,表示从节点xxx和节点yyy有一条单向边。请利用 BFS ,从节点1开始将遍历到的情况输出。
输入
第一行输入一个整数nnn和一个整数kkk,分别表示一共的点数和边数,
接下来的kkk行,每行输入两个整数xxx和yyy,分别表示从点xxx到点yyy有一条单向边。
输出
输出遍历到的情况。
样例输入
9 8
1 2
1 3
1 4
2 5
3 6
3 7
4 8
6 9
样例输出
1 2 3 4 5 6 7 8 9
提示
对于100100%100的数据:0<n,m<10000 < n,m < 10000<n,m<1000 ,多种方案请按字典序第一位输出,数据保证不会有重复的边。
我们来思考一下:根据 BFS ,头指针指向哪里就把从1−n1 - n1−n遍历一遍,假如邻接矩阵为True且没被做过,就说明可以保存,把这个节点保存下来。每次一个节点遍历完,便头指针往后移。
思考完毕,附上AC代码:
#include <bits/stdc++.h>
using namespace std ;
const int Maxn = 1010 ;
int n , k , x , y , b [Maxn] ;
bool g [Maxn] [Maxn] , d [Maxn] ;
void bfs (int x) ;
int main () {
cin >> n >> k ;
for (int i = 1 ; i <= k ; i ++) {
cin >> x >> y ;
g [x] [y] = true ; //邻接矩阵
}
bfs (1) ; //进行BFS
return 0 ;
}
void bfs (int x) {
int t , w , fx , sx ;
t = w = 1 ;
b [t] = x ; d [x] = true ; //初始化
while (t <= w) { //判断是否交叉
fx = b [t] ; printf ("%d " , fx) ; //取出父节点
for (int i = 1 ; i <= n ; i ++) { //枚举所有子节点
sx = i ; //取出子节点
if (g [fx] [sx] == false) {
continue ;
} //不是边
if (d [sx] == true) {
continue ;
} //做过了
//不满足条件
b [++ w] = sx ; d [sx] = true ; //保存到队列
}
t ++ ; //遍历下一个节点
}
}
BFS的实际应用
洛谷B3625迷宫寻路:B3625迷宫寻路
这是一道非常经典的 BFS 题, 我们知道机器猫只能向上、下、左、右移动,我们便可以每次遍历方向,方向为w[4][2]={-1,0,1,0,0,-1,0,1},每一次新的位置都要判断是否做过、越界、碰墙,假如都没有便可以保存结果了。
AC代码:
#include<bits/stdc++.h>
const int N=10010;
using namespace std;
int b[N],c[N],f[110][110],t,w,fx,fy,sx,sy,n,m;
char a[110][110];
const int e[4][2]={{-1,0},{1,0},{0,-1},{0,1}}; //四个方向
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i]+1;
//下面进行BFS
t=w=1;
b[t]=c[t]=1;f[1][1]=1; //初始化
while(t<=w)
{
fx=b[t];fy=c[t]; //取出父位置
for(int i=0;i<=3;i++) //遍历查找子位置
{
sx=fx+e[i][0];sy=fy+e[i][1];//取出子位置
if(sx<1||sx>n||sy<1||sy>m) continue; //越界
if(f[sx][sy]==1||a[sx][sy]=='#') continue; //做过或碰到墙
if(sx==n&&sy==m)
{
cout<<"Yes";
return 0;
}//假若找到
w++;
b[w]=sx;c[w]=sy;f[sx][sy]=1;//保存到队列
}
t++;//查找下一个
}
cout<<"No";
//注:这是我早期代码风格
}
BFS的提高
这只是最基础的 BFS ,当你走进洛谷真正想做 BFS 的题目时,往往更难,更多样化:或许是求步数、或许是方向改变、或许是加上前缀和、或许是加上二分图、图论。但这些题都脱不了本质: BFS
我们如果刷到 BFS 的题,就务必记起BFS的框架:
void bfs (???)
{
t = w = 1 ;
初始化;
while(t <= w){
取出根节点;
遍历查找子节点{
取出子节点;
if (不满足条件) continue ;
保存到队列;
}
t ++ ;//出队
}
}
这就是 BFS (基础篇) 的所有内容。
简单易懂的BFS的更多相关文章
- 图的遍历(搜索)算法(深度优先算法DFS和广度优先算法BFS)
图的遍历的定义: 从图的某个顶点出发访问遍图中所有顶点,且每个顶点仅被访问一次.(连通图与非连通图) 深度优先遍历(DFS): 1.访问指定的起始顶点: 2.若当前访问的顶点的邻接顶点有未被访问的,则 ...
- 【BZOJ-1656】The Grove 树木 BFS + 射线法
1656: [Usaco2006 Jan] The Grove 树木 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 186 Solved: 118[Su ...
- POJ 3278 Catch That Cow(bfs)
传送门 Catch That Cow Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 80273 Accepted: 25 ...
- POJ 2251 Dungeon Master(3D迷宫 bfs)
传送门 Dungeon Master Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 28416 Accepted: 11 ...
- Sicily 1215: 脱离地牢(BFS)
这道题按照题意直接BFS即可,主要要注意题意中的相遇是指两种情况:一种是同时到达同一格子,另一种是在移动时相遇,如Paris在(1,2),而Helen在(1,2),若下一步Paris到达(1,1),而 ...
- Sicily 1048: Inverso(BFS)
题意是给出一个3*3的黑白网格,每点击其中一格就会使某些格子的颜色发生转变,求达到目标状态网格的操作.可用BFS搜索解答,用vector储存每次的操作 #include<bits/stdc++. ...
- Sicily 1444: Prime Path(BFS)
题意为给出两个四位素数A.B,每次只能对A的某一位数字进行修改,使它成为另一个四位的素数,问最少经过多少操作,能使A变到B.可以直接进行BFS搜索 #include<bits/stdc++.h& ...
- Sicily 1051: 魔板(BFS+排重)
相对1150题来说,这道题的N可能超过10,所以需要进行排重,即相同状态的魔板不要重复压倒队列里,这里我用map储存操作过的状态,也可以用康托编码来储存状态,这样时间缩短为0.03秒.关于康托展开可以 ...
- Sicily 1150: 简单魔板(BFS)
此题可以使用BFS进行解答,使用8位的十进制数来储存魔板的状态,用BFS进行搜索即可 #include <bits/stdc++.h> using namespace std; int o ...
- ACM/ICPC 之 靠墙走-DFS+BFS(POJ3083)
//POJ3083 //DFS求靠左墙(右墙)走的路径长+BFS求最短路 //Time:0Ms Memory:716K #include<iostream> #include<cst ...
随机推荐
- es创建索引及别名更新mapping方法 elasticsearch [nested] nested object under path [XXX] is not of nested type
[nested] nested object under path [XXX] is not of nested type这是因为在创建索引时没有指定类型为数组,这就是一个大坑,ES官方说可以不用指定 ...
- 彻底解决IDEA Jrebel 错误找不到口令文件的问题
本来我的Jrebel也是好用的,突然就变成了这个样子,网上针对这个问题有很多帖子,但是每次新建项目后我都需要去解决一下这个问题,这我不能忍,经过一整天的百度和测试终于找到了原因并解决这个问题,伸手党直 ...
- 测试网络的小工具WinMTR
ping网络的小工具 搜集了两个版本中文版和英文版 中文版---- WinMTR中文版.rarhttps://www.aliyundrive.com/s/bZqmokL5dTt提取码: k6v7 英文 ...
- Jenkins项目构建成功后,配置邮件
6.1 配置信息 6.1.1 发给多个收件人 邮件要发送给多个人,要使用[,]分割 6.1.2. 项目构建引用Editable Email Notification,设置tigger 在项目构建后,引 ...
- C# Linq.FirstOrDefault、Linq.Where、Linq.AsParallel、List.Exists、List.Find、Dictionar.TryGetValue、HashSet.Contains 性能的比较
今天我们来比较一下集合检索方法性能更优问题,测试代码 public class Entity { public int Id { get; set; } public int No { get; se ...
- 最新扣子(Coze)实战案例:扣子卡片的制作及使用,完全免费教程
♂️ 大家好,我是斜杠君,手把手教你搭建扣子AI应用. ☘️ 本文是<AI应用开发系列教程之扣子(Coze)实战教程>,完全免费学习. 关注斜杠君,可获取完整版教程. 如果想学习AI应用 ...
- python学习_PIL的Image模块初步使用
基本介绍: Pillow 是 Python 中较为基础的图像处理库,主要用于图像的基本处理,比如裁剪图像.调整图像大小和图像颜色处理等.与 Pillow 相比,OpenCV 和 Scikit-imag ...
- WEB前端项目开发流程
项目需求分析 这个环节是由项目经理完成,项目经理首先和客户进行交流,了解客户的需求,然后分析项目的可行性,如果项目可以被实现,项目经理写出项目需求文档交给设计师完成后续的开发. 页面设计 这个环节主要 ...
- JDK1.8新特性Lambda表达式简化if-else里都有for循环的优化方式
在日常开发过程当中,能把代码写出来,不一定就意味着能把代码写好,说不准,所写的代码在他人看来,其实就是一坨乱七八糟的翔,因此,代码简化尤其重要,我曾经遇到过这样一个类型的代码,即if-else里都有相 ...
- node sass
registry=https://registry.npmmirror.com/ sass_binary_site=https://cdn.npmmirror.com/mirrors/node-sas ...