bfs广度优先搜索

目录

一、BFS的介绍

1.BFS的定义BFS(广度优先搜索,也可称宽度优先搜索)是连通图的一种遍历策略。因为它的基本思想是从一个顶点V0开始,辐射状地优先遍历其周围较广的区域。

2.BFS搜索的步骤1、首先创建一个visit[ ]数组和一个队列q,分别用来判断该位置是否已经访问过及让未访问过的点入队;

3.BFS的应用BFS算法一般应用于单源最短路径的搜索。

4.BFS的特点BFS的特点是先访问距离起始节点近的节点,然后逐渐向离起始节点更远的节点进行扩展。因此,当在图中搜索最短路径或寻找最近邻节点时,BFS是一种常用的算法。

二,BFS的实战应用

1.题目:迷宫

2.上题目:马的遍历

3.流星雨S

4.题目:玉米迷宫s

5.题目:机器人搬重物


一、BFS的介绍
1.BFS的定义
BFS(广度优先搜索,也可称宽度优先搜索)是连通图的一种遍历策略。因为它的基本思想是从一个顶点V0开始,辐射状地优先遍历其周围较广的区域。

广度优先搜索(BFS)类似于二叉树的层序遍历算法,它的基本思想是:首先访问起始顶点v,然后由v出发,依次访问v的各个未被访问过的邻接顶点w1,w2,w3….wn,然后再依次访问w1,w2,…,wi的所有未被访问过的邻接顶点,再从这些访问过的顶点出发,再访问它们所有未被访问过的邻接顶点….以此类推,直到途中所有的顶点都被访问过为止。类似的想法还将应用与Dijkstra单源最短路径算法和Prim最小生成树算法

广度优先搜索是一种分层的查找过程,每向前走一步可能访问一批顶点,不像深度优先搜索(DFS)那样有回退的情况,因此它不是一个递归的算法,为了实现逐层的访问,算法必须借助一个辅助队列并且以非递归的形式来实现。

为了更加直观的体现,请看如图,DFS先是一个地方搜到底,一个点一个点地搜索,而BFS是全方面搜索,一层一层的搜索,更为直观,全面。

2.BFS搜索的步骤
1、首先创建一个visit[ ]数组和一个队列q,分别用来判断该位置是否已经访问过及让未访问过的点入队;

2、初始化visit[ ]数组,清空q队列;

3、让起点start入队,并使该点的visit置1;

4、while(!q.empty()){......}执行搜索操作,

a、取出队头元素后使队头元素出队,判断该元素是否为目标到达点;

b、如果是目标点,就返回结果(一般是最短时间、最短路径);

c、如果不是目标点,就继续访问与其相邻的位置点,将可走的相邻的位置点入队,并更新visit[ ]数组;

3.BFS的应用
BFS算法一般应用于单源最短路径的搜索。

1、寻找非加权图(或者所有边权重相同)中任两点的最短路径。

2、寻找其中一个连通分支中的所有节点。(扩散性)

3、bfs染色法判断是否为二分图。

4.BFS的特点
BFS的特点是先访问距离起始节点近的节点,然后逐渐向离起始节点更远的节点进行扩展。因此,当在图中搜索最短路径或寻找最近邻节点时,BFS是一种常用的算法。

需要注意的是,BFS使用队列来存储待访问的节点,因此它是一种先进先出(First-In-First-Out,FIFO)的算法。此外,为了避免重复访问节点,需要使用一个标记数组或哈希表来记录节点的访问状态。

一般来说当图的边权都为1时使用BFS,边权为1表示从一个节点到相邻节点的距离或代价是相等的,这意味着每个相邻节点离起始节点的距离相差一个单位。在这种情况下,BFS可以确保首次到达目标节点时的路径长度最小。

如果图中存在边权不为1的情况,BFS可能无法得到最短路径。 对于带有不同边权的图,更适合使用Dijkstra算法或A*算法等其他路径搜索算法,它们可以考虑不同边权的影响,找到最优路径。


二,BFS的实战应用

1.BFS的大致模板如下:

1 BFS算法:
 2 
 3 通常用队列(先进先出,FIFO)实现
 4 
 5 初始化队列Q;
 6 Q = {起点s};
 7 标记s为已访问;
 8 while(Q非空)
 9 {
10     取Q队首元素u;
11     u出队;
12     if(u==目标状态)
13     {
14         ……
15     }
16     else
17     {
18         所有与u相邻且未被访问的点进入队列;
19         标记u为已访问;
20     }
21 }
1.题目:迷宫

输入n,m表示迷宫大小,在输入1或2表示能不能走,1表示能走,2表示不能走,在输入起点和终点,最后输出起点到终点要走多少步。

输入:

5 4

	1 1 2 1

	1 1 1 1

	1 1 2 1

	1 2 1 1

	1 1 1 2

1 1 4 3

输出:

7

方法是bfs,然后加了一点点队列和结构体

#include<bits/stdc++.h>

using namespace std;

int a[100][100];//a【i】【j】=1表示是空地可以走,为0表示不是空地

int v[100][100];//v【i】【j】=1表示已经被访问了,为0表示未被访问

struct point {

	int x;

	int y;

	int step;

};

queue<point> r;//定义队列


int dx[4]= {0,1,0,-1}; //四个方向

int dy[4]= {1,0,-1,0};


int main() {

	//input

	int n,m,startx,starty,p,q;

	scanf("%d%d",&n,&m);     //迷宫大小

	for(int i=1; i<=n; i++) {

		for(int j=1; j<=m; j++) {

			scanf("%d",&a[i][j]);   //输入1或2,如果是1表示可以走,2表示不能走

		}

	}

	scanf("%d%d%d%d",&startx,&starty,&p,&q);         //输入起点和终点


	//BFS

	point start;

	//初始化开始

	start.x = startx;

	start.y = starty;

	start.step = 0;

	//初始化结束

	r.push(start);//将起点入队

	v[startx][starty] = 1 ;


	int flag = 0;

	while(!r.empty()) {           //判断是不是空栈,是则值为1,不是则值为0

		int x=r.front().x,y=r.front() .y;

		if(x==p && y==q) { // arrive destinationa

			flag = 1;

			cout<<r.front().step;

			break;

		}


		for(int k = 0 ; k < 3 ; k++) {

			int tx,ty;

			tx = x + dx[k];

			ty = y + dy[k];

			if (a[tx][ty]==1 && v[tx][ty]==0) { //如果是空地 并且 未被访问过

				//入队

				point temp;//把拓展的点放到temp里

				temp.x = tx;

				temp.y = ty;

				temp.step = r.front().step + 1 ;

				r.push(temp);

				v[tx][ty] = 1; //设置为已访问

			}

		}

		r.pop();//拓展完了需要将队首元素出队


	}

	if(flag==0)//没找到

		cout<<"no answer";

	return 0;

}
2.上题目:马的遍历

请读者思考一下应该怎么写,如果思路跟我一样,我记得可以照搬我的

输入:

3 3 1 1

输出:

0    3    2    
3    -1   1    
2    1    4    

//马  队列
#include<bits/stdc++.h>      //非本人写的,并且本人不是用这种方法写的
using namespace std;
const int dx[8]= {-1,-2,-2,-1,1,2,2,1};
const int dy[8]= {2,1,-1,-2,2,1,-1,-2}; //8个方向
queue<pair<int,int> >q;    //queue指队列,pair的使用是为了让队列里有两个数据,正常情况下队列里只能有一个,现在也可以是 <string,int>q  <char,double>q
int f[500][500];//存步数
bool vis[500][500];//走没走过
int main() {
	int n,m,x,y;
	memset(f,-1,sizeof(f));
	memset(vis,false,sizeof(vis));
	cin>>n>>m>>x>>y;
	f[x][y]=0;
	vis[x][y]=true;
	q.push(make_pair(x,y));
	while(!q.empty()) {           //empty判断是不是空栈,说的话值为1,不是则为0
		int xx=q.front().first,yy=q.front().second;    //英语,first指第一个,second指第二个,
		q.pop();//取队首并出队
		for(int i=0; i<8; i++) {
			int u=xx+dx[i],v=yy+dy[i];
			if(u<1||u>n||v<1||v>m||vis[u][v])continue;//出界或走过就不走
			vis[u][v]=true;
			q.push(make_pair(u,v));
			f[u][v]=f[xx][yy]+1;
		}
	}
	for(int i=1; i<=n; i++) {
		for(int j=1; j<=m; j++)
			printf("%-5d",f[i][j]);
		printf("\n");
	}              //注意场宽!!
	return 0;
}
3.流星雨S

输入:

4
0 0 2
2 1 2
1 1 2
0 3 5

输出:

5
#include<bits/stdc++.h> //万能头文件
using namespace std;
int n,ma[305][305],v[305][305],sx,sy,st,ans[305][305];//分别为陨石数量,陨石砸落地图,记录是否走过地图,陨石x,y坐标及砸落时间,每个点的最少时间图。
int dx[5]= {0,0,1,-1};
int dy[5]= {1,-1,0,0}; //方便移动和处理陨石砸落
int main() {
	cin>>n;
	for (int i=0; i<305; i++) {
		for (int j=0; j<305; j++) {
			ma[i][j]=-1;
		}
	}//陨石坠落地初始化为-1
	for (int i=1; i<=n; i++) {
		cin>>sx>>sy>>st;//输入陨石
		for (int j=0; j<5; j++) { //上下左右中标记陨石
			if (sx+dx[j]>=0&&sy+dy[j]>=0&&(ma[sx+dx[j]][sy+dy[j]]==-1||ma[sx+dx[j]][sy+dy[j]]>st))
				ma[sx+dx[j]][sy+dy[j]]=st;             //如果该标记x,y坐标大于0且该点没有被陨石砸落或已标记时间没有该时间早,标记陨石
		}
	}
	queue<pair<int,int> >q;           //构造队列,存储将处理点x,y坐标
	v[0][0]=1;                       //初始点设为已走过
	q.push(make_pair(0,0));          //初始点放入队列
	while (!q.empty()) {             //只要队列不为空
		int x=q.front().first,y=q.front().second;    //提取将处理点x,y坐标
		q.pop();                                     //删除已处理点,出栈
		int s=ans[x][y]+1;                          //即将标记的点时间是现在点的下一个单位
		if (ma[x][y]==-1) {                         //如果该点安全,输出即将标记的点的时间-1
			printf("%d",s-1);
			return 0;
		}
		for (int i=0; i<4; i++) {
			int xx=x+dx[i],yy=y+dy[i];//提取将处理点的坐标
			if (xx>=0&&yy>=0&&(ma[xx][yy]==-1||s<ma[xx][yy])&&v[xx][yy]==0) { //将处理点需要x,y坐标大于等于0,且该点没有走过,并且陨石降落时间小于现时间
				q.push(make_pair(xx,yy));//放入将处理队列
				v[xx][yy]=1;//标记已走过
				ans[xx][yy]=s;//将该点时间放入数组
			}
		}
	}
	printf("-1");//如果出不了陨石区,输出-1
	return 0;
}
4.题目:玉米迷宫s

输入:

5 6
###=##
#.W.##
#.####
#.@W##
######

输出:

3

认真思考!

#include<bits/stdc++.h>
using namespace std;
const int N=350;
struct point {
	int x;
	int y;
	int t;
};
queue<point> que;

char a[N][N];
bool vis[N][N];
int n,m;
int dx[4]= {1,0,-1,0};
int dy[4]= {0,1,0,-1};
int sx;
int sy;

void goto_another(int &nx,int &ny,int k) { //goto_another函数用于寻找另一个传送门,nx、ny代表当前点的坐标,记得要加上取地址符'&',因为每当贝西踏入一个传送门,它就会立即被传送至另一个传送门,不能在原地停留
	for(int i=1; i<=n; i++) {
		for(int j=1; j<=m; j++) {
			if(a[i][j]==a[nx][ny]&&(i!=nx||j!=ny)) { //如果a[i][j]这个点的是一个与a[nx][ny]相同的传送门,并且a[i][j]与a[nx][ny]不是同一个点
				nx=i;//改变当前坐标,将贝西强行移动至另一个传送门处
				ny=j;
				return ;//告辞
			}
		}
	}
}

int main() {

	cin>>n>>m;
	for(int i=1; i<=n; i++) {
		for(int j=1; j<=m; j++) {
			cin>>a[i][j];
			if(a[i][j]=='@') { //获取起点坐标
				sx=i;
				sy=j;
			}
		}
	}
	que.push((point) {  //入列
		sx,sy,0
	});

	while(!que.empty()) {  //不是非空就继续循环
		point f=que.front();//重新定义一个结构体的通用字f
		que.pop();           //出列
		if(a[f.x][f.y]=='=') { //终点,结束
			cout<<f.t;
			return 0;
		}
		if(a[f.x][f.y]>='A'&&a[f.x][f.y]<='Z') { //题目说范围是A到Z都可以用,如果当前点是一个传送门,那么就传送至另一个传送门
			goto_another(f.x,f.y,f.t);               //到达另一个传送门的地方
		}
		for(int i=0; i<=3; i++) {                //四个方向
			int nx=f.x+dx[i];
			int ny=f.y+dy[i];
			if(nx>=1&&nx<=n&&ny>=1&&ny<=m&&a[nx][ny]!='#'&&!vis[nx][ny]) {  //入列条件
				vis[nx][ny]=true;
				que.push((point) {     //入列
					nx,ny,f.t+1        //t用来统计步数
				});
			}
		}
	}
	return 0;
}
5.题目:机器人搬重物

输入;

9 10
0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 1 0
0 0 0 1 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 1 0 0 0 0
0 0 0 1 1 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 1 0
7 2 2 7 S

输出:

12

挺难的说。

#include<bits/stdc++.h>
using namespace std;
int sd[55][55];
int a[55][55];//a为读入的方格地图 
int n,m;
int x11,y11;//起点 
int x2,y2;//终点 
int f[55][55];//f为格点地图 

int fx[5]={0,-1,1,0,0};//fx[i]表方向i(编号)的x情况  
int fy[5]={0,0,0,-1,1};//fy[i]表方向i(编号)的y情况 
int ft[5]={0,1,4,2,3};//ft[i]表示顺时针排列各个方向的编号(上1 右4 下2 左3) 上加下=5,左加右=5
int fft[5]={0,1,3,4,2};//fft[i]表示数字i在ft[]数组中的下标 ,即不是数组ft[]此时的方向

int abc[5]={0,1,2,1,0};//abc[5]表示转到[顺时针转i次到达的那个方向]的最短次数 
struct node
{
	int x,y;//当前点的坐标 
	int t;//1=>N 2=>S 3=>W 4=>E 方向编号 
	int time;//从起点到当前点的最短时间 
};
queue<node> q;//队列q 
char ch;//读入起点的方向 
int cto;//起点的方向 

void change()
{
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=m;++j)
		{
			if(sd[i][j]==1)//如果当前格为障碍物,则它的四个顶点都不能走 
			{
				a[i-1][j]=1;
				a[i][j-1]=1;
				a[i-1][j-1]=1;
				a[i][j]=1;
			}
		}
	}
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=m;++j)
		{
			scanf("%d",&sd[i][j]);
		}
	}
	cin>>x11>>y11>>x2>>y2>>ch;
		switch(ch)
	{
		case 'N': cto=1;break;
		case 'S': cto=2;break;
		case 'W': cto=3;break;
		case 'E': cto=4;break;
	}//判断ch代表的方向 
	change();//把方格地图转化为机器人可以走的格点地图 
	node first;//起点 
//	first.x=x11;
//	first.y=y11;
//	first.t=cto;
//	first.time=0;
//	q.push(first);//起点入队 ,这样也行
	q.push((node) { //但是这里得用结构体名字
		x11,y11,cto,0
	});
	node u,d;
	while(!q.empty())
	{
		u=q.front();
		q.pop();
		for(int i=1;i<=4;++i)
		{
			int zhuan=abc[i];//[顺时针转i下的那个方向]的最短旋转次数 
			
			//求出旋转完了以后方向的编号fangx(为了方便讨论,全部当做顺时针旋转) 
			int fangx=fft[u.t]+i;//此时fangx为下标 
			if(fangx>=5) fangx-=4;

			fangx=ft[fangx];//此时fangx为方向编号 
			//此时fangx存的是由当前点顺时针转了i次后到达的方向的编号
			 
			for(int j=1;j<=3;++j)//走1~3步 
			{
				int lsx=u.x+fx[fangx]*j;//计算按当前旋转方向走j步的坐标 
				int lsy=u.y+fy[fangx]*j;
				if(lsx>=n || lsx<=0 || lsy>=m || lsy<=0 || (lsx==x11&&lsy==y11) || a[lsx][lsy]==1)
				{
					//判断边界和障碍物 (特判:是否为起点)
					break;
				}
				if((u.time+zhuan+1<f[u.x+fx[fangx]*j][u.y+fy[fangx]*j] || f[u.x+fx[fangx]*j][u.y+fy[fangx]*j]==0) && a[u.x+fx[fangx]*j][u.y+fy[fangx]*j]==0)
				{//如果当前点可以刷新距离,就入队 
					d.x=u.x+fx[fangx]*j;
					d.y=u.y+fy[fangx]*j; 
					d.t=fangx;
					d.time=u.time+zhuan+1;
					f[u.x+fx[fangx]*j][u.y+fy[fangx]*j]=d.time;
					q.push(d);
				}
			}
		}
	}
	if(f[x2][y2]==0 && (x2!=x11 || y2!=y11))//如果为0,代表不能走到 
	{
		cout<<"-1";
	}
	else//否则输出终点的距离 
	cout<<f[x2][y2];
	return 0;
}

小结:兽人永不为奴!

BFS广度优先搜索算法(Breadth-First Search)是一种图的搜索算法,也是最简便的图搜索算法之一。它的目的是系统地展开并检查图中的所有节点,以找寻结果。这个算法不考虑结果的可能位置,而是彻底地搜索整张图,直到找到结果为止。广度优先搜索算法通常用于找出两个节点之间的最短路径。它可以帮助我们解决类似于寻找最短路径的问题。 BFS算法的基本思想是从起始节点开始,逐层地向外扩展,先访问离起始节点最近的节点,再逐渐向外层节点扩展,直到达到目标节点。它使用队列来存储待访问的节点,先进先出(FIFO)的特点保证了广度优先。通过不断地将节点加入队列并访问,直到队列为空,我们可以遍历整张图并找到最短路径。 BFS广度优先搜索算法的运行时间为O(V+E),其中V为顶点数,E为边数。它可以指出是否存在从起始节点到目标节点的路径,并且可以找到最短路径。当面临需要寻找最短路径的问题时,我们可以使用图来建立模型,并使用广度优先搜索算法解决问题。 参考资料: https://www.cnblogs.com/tianqizhi/p/9914539.html 文中引用的参考资料<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [代码 基于BFS广度优先搜索算法代码](https://download.csdn.net/download/s13166803785/85545057)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [广度优先搜索算法BFS)详解](https://blog.csdn.net/lemonxiaoxiao/article/details/105730735)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值