简单易懂的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):

一棵树共有nn个点,kk条边,接下来的kk行每行输入xxyy,表示从节点xx和节点yy有一条单向边。请利用 BFS ,从节点1开始将遍历到的情况输出。

输入

第一行输入一个整数nn和一个整数kk,分别表示一共的点数和边数, 接下来的kk行,每行输入两个整数xxyy,分别表示从点xx到点yy有一条单向边。

输出

输出遍历到的情况。

样例输入

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%的数据:0<n,m<10000 < n,m < 1000 ,多种方案请按字典序第一位输出,数据保证不会有重复的边。

我们来思考一下:根据 BFS ,头指针指向哪里就把从1n1 - 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 (基础篇) 的所有内容。

posted @   uhw177po  阅读(43)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具

阅读目录(Content)

此页目录为空

点击右上角即可分享
微信分享提示