1.问题分析
在上一篇博客中A同学前往迷宫解救B同学的行动中,我们用了深度优先搜索方法,这里介绍另外一种可以使用的方法——广度优先搜索(Breadth First Search,BFS),也称为宽度优先搜索。
依旧是用一个为数组存储这个迷宫。最开始的时候A同学在迷宫(1,1)处,他可以往右走或者往下走。上一篇博客使用DFS方法我们是先让A同学先向右走,然后一直尝试下去,知道走不通的时候回到原点。
现在介绍另一种方法——通过“一层一层”扩展的方法来找大B同学。扩展时每发现一个点就将这个点加入到队列中,直至走到B同学的位置为止。
2.算法设计
(1):最开始A同学在(1,1)处,可以到达的坐标有(1,2)和(2,1)。
此时并没有找到B同学,所以继续往下走,假设可以走到(1,2)点,下一个点可以走到(2,2),假设走到(2,1)点,下一个点可以走到(2,2)和(3,1)。此时两个点都可以到达(2,2),为了防止一个点多次被走到,我们需要一个数组来记录书否这个点已经被走到过。
现在我们到达了(2,2)或者(3,1),也并没有找到B同学的为止,所以我们要重复上述的方法。假设现在我们到达(2,2),我们可以往下到达(2,3)和(3,2);假设我们现在到达(3,1),我们可以往下到达(3,2)和(4,1)。
(2):回顾上面的过程,我们确定数据结构:我们可以用一个队列来模拟上述的过程,这里我们还是用一个结构体还实现队列(C++也可以直接用STL的队列头文件)
struct note { int x;//横坐标 int y;//纵坐标 int step;//步数 }; struct note que[N];//队列 int head; int tail; int maze[N][N]={0};//初始化地图 int marked[N][N]={0};//标记数组,判断是否被访问 head=1; tail=1; //指向队列头 //第一步将(1,1)加入队列,因为此处是起点,默认已经在队列中 que[tail].x=1; que[tail].y=1; que[tail].step=0; tail++;//往下走 marked[1][1]=1;//此时(1,1)已经访问过。
(3):从(1,1)开始,到达(1,2)。
tx=que[head].x; ty=que[head].y+1;
(4):我们需要判断(1,2)是否越界。
if(tx<1 || tx>n || ty<1 || ty>m) { continue; }
(5):再判断(1,2)是否为障碍物或者已经在路径中。
if(maze[tx][ty]==0 && marked[tx][ty]==0) { }
(6):如果满足上述条件,就将(1,2)放入队列。
marked[tx][ty]=1; que[tail].x=tx; que[tail].y=ty; que[tail].step=que[head].step+1; tail++;
(7):接下来还要继续往别的方向走。我们规定一个顺序:右,下,左,上。当(1,1)扩展完成后,(1,1)现在对我们来说已经没有用了,我们就将(1,1)出列。出列之后,现在队列的head正好指向(1,2)这个点,通过这个点我们继续扩展,最后将(2,2)也加入队列。(1,2)操作完毕,也将(1,2)出列。以此类推,接下来都是这样的操作。
(8):为了方便扩展,我们还需要一个方向函数:
int next[4][3]={{0,1},{1,0},{0,-1},{-1,0}};
3.源代码
#include <iostream> #include <cstdio> #include <cstdlib> #include <algorithm> #include <cstring> using namespace std; const int N=111; struct note { int x;//横坐标 int y;//纵坐标 int step;//步数 }; int next[4][3]= {{0,1},{1,0},{0,-1},{-1,0}}; int main() { struct note que[N*N];//队列 int head; int tail; int maze[N][N]= {0}; //初始化地图 int marked[N][N]= {0}; //标记数组,判断是否被访问 int next[4][3]= {{0,1},{1,0},{0,-1},{-1,0}};//方向数组。 int n; int m; int startx; int starty; int p; int q; int tx; int ty; int flag; cout << "请输入迷宫的行数n和列数m" << endl; cin >> n>> m; cout << "请输入迷宫maze" << endl; for(int i=1; i<=n; i++) { for(int j=1; j<=m; j++) { cin >> maze[i][j]; } } cout << "请输入起点的坐标和目标的坐标:"<< endl; cin >> startx >> starty >> p >> q; head=1; tail=1; //指向队列头 //往队列插入迷宫入口坐标: //第一步将(1,1)加入队列,因为此处是起点,默认已经在队列中 que[tail].x=1; que[tail].y=1; que[tail].step=0; tail++;//往下走 marked[1][1]=1;//此时(1,1)已经访问过。 flag=0;//用来标记是否已经到达目标点。 while(head<tail) { //枚举四个方向 for(int 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(maze[tx][ty]==0 && marked[tx][ty]==0) { marked[tx][ty]=1; //此时已经访问过 que[tail].x=tx; que[tail].y=ty; que[tail].step=que[head].step+1; tail++; } if(tx==p && ty==q) { flag=1; break; } } if(flag==1) { break; } head++;//将已经扩展过的点出列。 } cout << "A同学找到B同学的最短距离是:"<< que[tail-1].step << endl; return 0; }