层层递进——广度优先搜索
广度优先搜索,也称宽度优先搜索。它与深度优先搜索相类似,从某个状态出发探索所有可以到达的状态。
与深度优先搜索的不同之处在于搜索的顺序,宽度优先搜索总是先搜索距离初始状态近的状态。也就是说,它是按照开始状态→只需1次转移就可以到达的所有状态→只需2次转移就可以到达的所有状态→······这样的顺序进行搜索。对于同一个状态,宽度优先搜索只经过一次,因此复杂度为O(状态数×转移的方式)。
深度优先搜索(隐式地)利用了栈进行计算,而宽度优先搜索则利用了队列。搜索时,首先将初始状态添加到队列里,此后从队列的最前端不断取出状态,把从该状态可以转移到的状态中尚未访问过的部分加入队列,如此往复,直至队列被取空或者找到了问题的解。通过观察这个队列,我们就可以知道所有的状态都是按照距初始状态由近及远的顺序被遍历的。
下面给出一道经典的迷宫例题:
给定一个大小为N×M的迷宫。迷宫由通道和墙壁组成,每一步可以向邻接的上下左右四个的通道移动。请求出从起点到终点所需的最小步数。请注意,本题假定从起点一定可以移动到终点。(N,M≤100)('#', '.' , 'S', 'G'分别表示墙壁、通道、起点和终点)
输入:
10 10
#S######.#
......#..#
.#.##.##.#
.#........
##.##.####
....#....#
.#######.#
....#.....
.####.###.
....#...G#
输出:
22
代码:
1 #include<iostream> 2 #include<queue> 3 using namespace std; 4 const int INF = 100000000, maxn = 105; 5 typedef pair<int, int> P;//可以使用结构体 6 char maze[maxn][maxn]; 7 int n, m, sx, sy, gx, gy,d[maxn][maxn];//到各个位置的最短距离的数组 8 int dx[4] = { 1,0,-1,0 }, dy[4]= { 0,1,0,-1 };//4个方向移动的向量 9 int bfs()//求从(sx,sy)到(gx,gy)的最短距离,若无法到达则是INF 10 { 11 queue<P> que; 12 for (int i = 0; i < n; i++) 13 for (int j = 0; j < m; j++) 14 d[i][j] = INF;//所有的位置都初始化为INF 15 que.push(P(sx, sy));//将起点加入队列中 16 d[sx][sy] = 0;//并把这一地点的距离设置为0 17 while (que.size())//不断循环直到队列的长度为0 18 { 19 P p = que.front();// 从队列的最前段取出元素 20 que.pop();//取出后从队列中删除该元素 21 if (p.first == gx&&p.second == gy) 22 break; 23 for (int i = 0; i < 4; i++)//四个方向的循环 24 { 25 int nx = p.first + dx[i],ny = p.second + dy[i];//移动后的位置标记为(nx,ny) 26 if (0 <= nx&&nx < n && 0 <= ny&&ny < m&&maze[nx][ny] != '#'&&d[nx][ny] == INF)//判断是否可以移动以及是否访问过(即d[nx][ny]!=INF) 27 { 28 que.push(P(nx, ny));//可以移动,添加到队列 29 d[nx][ny] = d[p.first][p.second] + 1;//到该位置的距离为到p的距离+1 30 } 31 } 32 } 33 return d[gx][gy]; 34 } 35 int main() 36 { 37 cin >> n >> m; 38 sx = 0, sy = 1, gx = 9, gy = 8;//起点和终点坐标 39 for (int i = 0; i < n; i++) 40 for (int j = 0; j < m; j++) 41 cin >> maze[i][j]; 42 cout << bfs() << endl; 43 return 0; 44 }
另一道相似的例题:
有一天,小哈一个去玩迷宫。但是方向感很不好的小哈很快就迷路了。小哼得知后便立即去解救无助的小哈。小哼当然是有备而来,已经弄清楚了迷宫地图,现在小哼要以最快速度去解救小哈。问题就此开始了……
迷宫由n行m列的单元格组成,每个单元格要么是空地,要么是障碍物。你的任务是帮助小哼找到一条从迷宫的起点到小哈所在位置的最短路径,注意障碍物是不能走的,当然也不能走到迷宫之外。n和m都小于等于100。
输入格式:
第一行有两个数N M。N表示迷宫的行,M表示迷宫的列。接来下来N行M列为迷宫,0表示空地,1表示障碍物。最后一行4个数,前两个数为迷宫入口的x和y坐标。后两个为小哈的x和y坐标。
输出格式:
一个整数表示小哼到小哈的最短步数。如果不能解救小哈则输出No Way!
样例 1 :
输入: 5 4 0 0 1 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1 1 1 4 3
输出: 7
代码:
#include <bits/stdc++.h> using namespace std; struct note{//创建一个结构 int x;//存放想x坐标 int y;//存放y坐标 int s;//步数 }; int main() { struct note p[10001];//创建一个队列 int a[101][101]={0},book[101][101]={0};//a存放,book标记已走过的点 int n,m,i,j,flag=0,x0,y0,endx,endy,tx,ty; //x0,y0存放初始位置坐标,flag标记变量,endx,endy存放小哈位置坐标 int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}};//有一个二维数组表示方向 int tail=1,head=1;//初始化队列 cin>>n>>m; for(i=1;i<=n;i++) for(j=1;j<=m;j++) cin>>a[i][j]; cin>>x0>>y0>>endx>>endy; //初始化 p[tail].x=x0; p[tail].y=y0; p[tail].s=0; tail++; book[x0][y0]=1; while(head<tail) { for(i=0;i<4;i++)//分别探索每个方向 { tx=p[head].x+next[i][0]; ty=p[head].y+next[i][1]; if(tx<1||tx>n||ty<1||ty>m)//判断边界 continue; if(book[tx][ty]==0&&a[tx][ty]==0)//未走过,没有障碍物则探索 { book[tx][ty]=1;//标记 p[tail].x=tx;//存入队列 p[tail].y=ty; p[tail].s=p[head].s+1;//记录步数 tail++; } if(tx==endx&&ty==endy)//如果到达目标点则标记并退出循环 { flag=1; break; } } if(flag==1) break; head++;//当一个点扩展结束后,head++才能对后面的点再进行扩展 } if(flag==1)//判断是否解救成功,并输出数据 { cout<<p[tail-1].s; } else cout<<"No Way!"<<endl; return 0; }
测题网址:https://www.acoj.com/problems/12032
宽搜和深搜一样,都会生成能够所有遍历到的状态,因此需要对所有状态处理时使用宽度优先搜索也是可以的。但是递归函数可以很简短地编写,而且状态的管理也更简单,所以大多数情况下都是用深搜实现。反之,在求取最短路时深度优先搜索需要反复经历同样的状态,所以此时还是用宽度优先搜索为好。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了