4.2 bfs 广度优先搜索
广度优先算法 Breadth First Search BFS
深度优先算法的兄弟,包含另外一种搜索思维
也叫宽度优先算法
概述
与深度优先算法不同,广度优先算法注重于"对所有情况的分析搜索"
如果是深度优先算法是刨根问底地分析每种情况,
广度优先就是在在层层扩展中找到题解
例
还是之前的问题,我们想在n*m的迷宫中找到起点到终点的最短路径
分析
我们的核心思想是分析扩展时每发现一个点,就将这个点加入到队列中,直到到达终点
另外,为防止一个点被多次走到,我们还要一个数组来记录一个点是否被走到
队列与搜索路径
我们决定使用一个队列来模拟搜索过程,我们需要:
1.两个参数x,y代表点坐标
2.一个参数s代表我们走过的步数
3.某一点之前的父,用于计算路径 (本题不需要计算路径,就省去这部分)
实现如下:
struct note {
int x ; //横坐标
int y ; //纵坐标
int s ; //步数
};
初始化
我们设定相关数据完成队列,并且对各个条件进行初始化
struct node que[2501] ; //地图为50*50,设置为2501就不会越界了
int head,tail ;
int a[51][51] = {0} ; //用于存储地图
int book[51][51] = {0} ; //记录拿一些点已经在队列中了,防止点的重复扩展
//0代表还没走过
//设置队列
head = 1 ;
tail = 1 ;
//先将默认起点(1,1)加入
que[tail].x = 1 ;
que[tail].y = 1 ;
que[tail].s = 0 ;
tail++;
book[1][1] = 1 ;
开始搜索
我们以(1,1) -> (1,2)为例解释怎么进行点的判断
1.尝试走到(1,2)
tx = que[head].x + 0;
ty = que[head].y + 1;
2.判断
主要是两点,一是是否越界,二是是否为障碍,三是是否已经走过
if(tx<1 || tx>n || ty<1 || ty >m){
continue ;
}
if(a[tx][ty] == 0 && book[tx][ty]){
//入队
}
入队
if(a[tx][ty] == 0 && book[tx][ty] == 0 ){
//入队
book[tx][ty] = 1 ; //顺带一提,宽度优先中只会入队
//和深度优先不同,宽度优先没有把book还原的操作
//插入新的点到队列中
que[tail].x = tx ;
que[tail].y = ty ;
qye[tail].s = que[head].s + 1;
tail++ ;
}
更新
我们再用相同的做法判断(2,1),至此我们已经搜索了(1,1)周围的所有点
现在我们需要更新(1,1),这个点已经没用了,我们将它出队
head++;
之后,我们新的出发点就变成了(1,2),对它进行搜索,然后再更新,再搜索...直到到达终点
另外,这里的按四个方向搜索的办法和深度是一样的
int next[4][2] = { {0,1},{1,0},{0,-1},{-1,0}};
完整代码
#include<stdio.h>
struct note {
int x ; //横坐标
int y ; //纵坐标
int s ; //步数
int f ; //父编号,暂时还用不到,之后的代码请先无视f
};
int main(){
struct note que[2501] ;
int a[51][51] = {0} , book[51][51] = {0};
//定义一个用于表示方向的数组
int next[4][2] = { {0,1}, //向右
{1,0}, //向下
{0,-1}, //向左
{-1,0} //向上
};
int head,tail ;
int i,j,k,n,m,startx,starty,p,q,tx,ty,flag ;
//读入参数
scanf("%d %d",&n,&m) ;
for(i=1;i<=m;i++)
for(j=1;j<=m;j++)
scanf("%d",&a[i][j]);
scanf("%d %d %d %d",&startx,,&starty,&p,&q);
//初始化队列
head = 1 ;
tali = 1 ;
//往队列插入迷宫入口坐标
que[tail].x = startx ;
que[tail].y = statty ;
que[tail].f = 0 ;
que[tail].s = 0 ;
tail++;
book[startx][starty] = 1 ;
flag = 0 ; //0表示还没到达,1表示到达终点
//当队列不为空的时候执行循环
//为什么是“不为空”时执行,请看后面的代码
while(head < tail){
for(k=0;k<=3;k++){
//计算下一个点
tx = que[head].x + next[k][0] ;
ty = que[head].y + next[k][1] ;
//对应判断,与深度优先相同
if(tx<1 || tx>n || ty<1 || ty>m) continue ;
if(a[tx][ty] == 0 && book[tx][ty] == 0){
book[tx][ty] =1 ; //标记已经走过
que[tail].x = tx ;
que[tail].y = ty ;
que[tail].f = head ;
que[tail].s = que[head].s +1;
tail ++ ;
}
if(tx == p && ty == q){
//如果到达目标点了,停止扩展,结束,退出循环
flag = 1 ;
break ;
}
}
if(flag ==1)
break ;
head++ ; //一个点拓展结束后,对后面的点再拓展
}
//打印步数
//值得注意的是,tail是指向“最后一个位置的下一个位置”,因此我们要-1
printf("%d",que[tail-1].s);
getchar();getchar();
return 0 ;
}