宽度优先搜索算法(又称广度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。
光用语言描述起来可能不太理解,下面直接看一道关于bfs类似的题
1 int data[5][5]={ 2 {0,1,0,0,0}, 3 {0,1,0,1,0}, 4 {0,0,0,0,0}, 5 {0,1,1,1,0}, 6 {0,0,0,1,0} 7 };
迷宫问题,首先0代表可以顺利通过,但是1代表墙壁,即无法通过
起始点是在(0 0),终点是在(4 4),请找出一条通路,并打印出其路径
由于这个题是需要打印出其路径,所以我们首先要定义一个pre是用来指向此刻路径的父亲路径,只有这样当我们找到(4,4)的时候,我们才可以凭借着其父亲路径将从起点到终点的路径输出出来
1、首先我们要定义一个结构体,结构体里面可以存储我们通过的路径,代码如下:
1 typedef struct sss 2 { 3 int x,y; 4 int pre; //记录其父亲路径的位置 5 }qq; 6 qq queue[100];
2、其次由于在迷宫中摸索的时候是需要有方向的,所以我们还需要定义一个二维数组去指示下一步的方向,代码如下:
1 int dxy[4][2]={ 2 {-1,0},{0,1},{1,0},{0,-1} 3 }; //(-1,0)就相当于是此刻的x坐标-1,y坐标不动,即是向上一个单位,(0,1),(1.0),(0,-1)也是同理
3、我们还要定义一个数组用来表示当前路径是否被访问过,代码如下:
1 int visit[5][5]={0}; //0代表未访问,1代表已访问
如果这几个都定义好了,其实也就没啥了,接下来就是整体代码环节了:
1 #include <stdio.h> 2 int data[5][5]={ 3 {0,1,0,0,0}, 4 {0,1,0,1,0}, 5 {0,0,0,0,0}, 6 {0,1,1,1,0}, 7 {0,0,0,1,0} 8 }; 9 10 typedef struct sss 11 { 12 int x,y; 13 int pre; 14 }qq; 15 qq queue[100]; 16 int dxy[4][2]={ 17 {-1,0},{0,1},{1,0},{0,-1} 18 }; 19 int visit[5][5]={0}; //0代表未访问,1代表已访问 20 21 void bfs(int s,int d); 22 void print_data(int rear); 23 bool cmp(int next_x,int next_y); 24 int main(int argc, char *argv[]) 25 { 26 27 bfs(0,0); 28 return 0; 29 } 30 31 void bfs(int s,int d) 32 { 33 int front=0,rear=1; 34 int next_x,next_y; 35 int i; 36 queue[front].x=s; 37 queue[front].y=d; 38 queue[front].pre=-1; 39 visit[s][d]=1; 40 41 while(front<rear) 42 { 43 for(i=0;i<4;i++) 44 { 45 next_x=queue[front].x+dxy[i][0]; 46 next_y=queue[front].y+dxy[i][1]; 47 if(cmp(next_x,next_y)) 48 { 49 queue[rear].x=next_x; 50 queue[rear].y=next_y; 51 queue[rear].pre=front; 52 visit[next_x][next_y]=1; 53 rear++; 54 } 55 else 56 continue; 57 58 if(next_x==4&&next_y==4) 59 { 60 print_data(rear-1); 61 } 62 } 63 front++; 64 } 65 66 } 67 68 void print_data(int rear) 69 { 70 if(rear!=-1) 71 { 72 print_data(queue[rear].pre); 73 printf("(%d %d)\n",queue[rear].x,queue[rear].y); 74 } 75 } 76 77 bool cmp(int next_x,int next_y) 78 { 79 if(next_x>=0&&next_x<5&&next_y>=0&&next_y<5&&visit[next_x][next_y]==0&&data[next_x][next_y]==0) 80 return true; 81 else 82 return false; 83 }
下面来分析一下代码的执行过程
0 | 1 | 0 | 0 | 0 |
0 | 1 | 0 | 1 | 0 |
0 | 0 | 0 | 0 | 0 |
0 | 1 | 1 | 1 | 0 |
0 | 0 | 0 | 1 | 0 |
首先,这就是那个迷宫,起点在(0,0),终点在(4,4),现在让找到一条通路输出,首先由于(0,0)
是起点,所以我们应该将(0,0)放入到结构体数组queue[0]中,因为我们还需要找下一个邻接的next_x和next_y,所以我们应该定义一个指向当前的位置的front和指向下一个位置的rear,在初始时front=0,rear=1,,所以我们将(0,0)放入到结构体数组queue[0]中并设置为已经访问后,
数组下标 | x | y | pre(父亲位置) |
0 | 0 | 0 | -1 |
然后找寻下一个节点,当我们执行for语句的时候即是在遍历当前位置的四个方向,找出满足条件的将其放入到结构体数组中,通过四个方向找完后发现只有next_x=1,next_y=0是满足条件的,在将其存入到数组中的时候要注意他的父亲位置是0也就是front,故将其存入数组并设置为已经访问后
数组下标 | x | y | pre(父亲位置) |
0 | 0 | 0 | -1 |
1 | 1 | 0 | 0 |
0 | 1 | 0 | 0 | 0 |
0 | 1 | 0 | 1 | 0 |
0 | 0 | 0 | 0 | 0 |
0 | 1 | 1 | 1 | 0 |
0 | 0 | 0 | 1 | 0 |
此刻判断当前的位置是否到终点了,明显是没有到的,故rear++,front++,此时front=1,rear=2,此时要从(1,0)开始找邻接点了,同理当next_x=2,next_y=0时是符合条件的,将其装入数组并设置为已经访问后,
数组下标 | x | y | pre(父亲位置) |
0 | 0 | 0 | -1 |
1 | 1 | 0 | 0 |
2 | 2 | 0 | 1 |
0 | 1 | 0 | 0 | 0 |
0 | 1 | 0 | 1 | 0 |
0 | 0 | 0 | 0 | 0 |
0 | 1 | 1 | 1 | 0 |
0 | 0 | 0 | 1 | 0 |
此时通过判断是没有到达终点的,所以front++,rear++,rear=3,front=2,此时通过找寻发现(2,1)和(3,0)都是符合的,所以都加入到数组中
数组下标 | x | y | pre(父亲位置) |
0 | 0 | 0 | -1 |
1 | 1 | 0 | 0 |
2 | 2 | 0 | 1 |
3 | 2 | 1 | 2 |
4 | 3 | 0 | 2 |
0 | 1 | 0 | 0 | 0 |
0 | 1 | 0 | 1 | 0 |
0 | 0 | 0 | 0 | 0 |
0 | 1 | 1 | 1 | 0 |
0 | 0 | 0 | 1 | 0 |
此时判断依旧没有到达终点,则继续往下找,此时的front=3,rear=5,则开始从(2,1)继续往下找,则(2,2)是符合的,所以同理
数组下标 | x | y | pre(父亲位置) |
0 | 0 | 0 | -1 |
1 | 1 | 0 | 0 |
2 | 2 | 0 | 1 |
3 | 2 | 1 | 2 |
4 | 3 | 0 | 2 |
5 | 2 | 2 | 3 |
0 | 1 | 0 | 0 | 0 |
0 | 1 | 0 | 1 | 0 |
0 | 0 | 0 | 0 | 0 |
0 | 1 | 1 | 1 | 0 |
0 | 0 | 0 | 1 | 0 |
此时的front=4,rear=6,则开始从(3,0)开始找起,(4,0)是符合条件的,所以同理
数组下标 | x | y | pre(父亲位置) |
0 | 0 | 0 | -1 |
1 | 1 | 0 | 0 |
2 | 2 | 0 | 1 |
3 | 2 | 1 | 2 |
4 | 3 | 0 | 2 |
5 | 2 | 2 | 3 |
6 | 4 | 0 | 4 |
依次找下去即可,这里就不一步一步了,相信看到这里,您应该都懂了,希望着篇文章能对您有帮助,谢谢