宽度有限搜索算法(又称广度优先搜索)是最简便的图的搜索算法之一。
Dijkstra单源最短路径算法和Prim最小生成树算法都采用了宽度优先搜索类似的思想。
BFS(Breadth First Search)属于一种盲目搜寻法,目的是系统的展开并检查图中的所有节点,以找寻结果。
dfs=栈,压栈,出栈;(栈可以保存路径)
bfs=队列,入队列,出队列;(队列可以计算路径长度)
bfs是按一层一层来访问,适合求最短路径的步数,
深度优先搜索用栈(stack)来实现,整个过程可以想象成一个倒立的树形:
1、把根节点压入栈中。
2、每次从栈中弹出一个元素,搜索所有在它下一级的元素,把这些元素压入栈中。并把这个元素记为它下一级元素的前驱。
3、找到所要找的元素时结束程序。
4、如果遍历整个树还没有找到,结束程序。
int check(参数)
{
if(满足条件)
return 1;
return 0;
}
void dfs(int step)
{
判断边界
{
相应操作
}
尝试每一种可能
{
满足check条件
标记
继续下一步dfs(step+1)
恢复初始状态(回溯的时候要用到)
}
}
例题 迷宫问题:
输入
5 5
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)
(1,0)
(2,0)
(2,1)
(2,2)
(2,3)
(2,4)
(3,4)
(4,4)
#include <iostream>
#include <algorithm>
#include <stack>
#include <vector>
using namespace std;
struct point{
int x,y;
}p;
int main(){
int N, M;
while(cin >> N >> M){
vector<vector<int> > maze(N,vector<int>(M));
vector<vector<int> > vis(N,vector<int>(M));
for(int i =0; i<N; i++){
for(int j =0; j<M; j++){
cin >> maze[i][j];
vis[i][j]=0;
}
}
int dir[4][2] = { 1,0,-1,0,0,1,0,-1};//点的四个方向
bool flag =0; //是否走完
stack<point> stk;
point s={0,0};
stk.push(s);
vis[0][0] =1;
while(!flag){ //深度优先搜索思想,往该点四个方向探查,若有则从这点继续探寻,若没有则回溯到上一个点
p = stk.top(); //回退路径的点
stk.pop();
int i = p.x, j= p.y;
for(int k =0; k <4;k++){ //对四个方向进行探查
int dx = i + dir[k][0];
int dy = j + dir[k][1];
point temp={dx,dy};
if(dx == N-1&&dy == M-1){ //到达终点
stk.push(temp);
flag =true;
break;
}
if(dx>=0&& dx<=N-1&& dy>=0&& dy<=M-1&& maze[dx][dy] ==0&& vis[dx][dy] ==0)
{ //未被访问过且有路径
stk.push(temp);
vis[dx][dy] =1;
i = dx; //从该点继续探查
j = dy;
k=-1;
}
}
}
stack<point> outputStk; //把栈的先后顺序换一下输出
while(!stk.empty()){
outputStk.push(stk.top());
stk.pop();
}
outputStk.push(s);
while(!outputStk.empty()){
point p = outputStk.top();
outputStk.pop();
cout <<"("<<p.x<<","<<p.y<<")"<< endl;
}
}
return 0;
}
广度优先搜索使用队列(queue)来实现,整个过程也可以看做一个倒立的树形:
1、把根节点放到队列的末尾。
2、每次从队列的头部取出一个元素,查看这个元素所有的下一级元素,把它们放到队列的末尾。并把这个元素记为它下一级元素的前驱。
3、找到所要找的元素时结束程序。
4、如果遍历整个树还没有找到,结束程序。
#include<iostream>
#include<string>
#include<queue>
using namespace std;
char a[1010][1010];//记录整个图形的
int vis[1010][1010];//用来标记的走没走过
struct Point{
int x, y;
}tmp1, tmp2;
int mt[4][2] = {1,0,-1,0,0,1,0,-1};//移动的四个方向
queue<Point>q;
int main(){
int n, i, j;
while(~scanf("%d", &n))
{
for(i = 0; i < n; i++)
{
scanf("%s", a[i]);
}
int sx, sy;
int ex, ey;
for(i = 0; i < n; i++){
for(j = 0; j < n; j++){
vis[i][j] = 0;//初始化标记
if(a[i][j] == 'S'){//找到初始位置
sx = i;
sy = j;
}
else if(a[i][j] == 'E'){//终点位置
ex = i;
ey = j;
}
}
}
while(!q.empty()){//队列初始化
q.pop();
}
tmp1.x = sx;
tmp1.y = sy;
vis[tmp1.x][tmp1.y] = 1;//首先压入起始位置
q.push(tmp1);
int flag = 0;
while(!q.empty()){
tmp1 = q.front();
q.pop();
for(i = 0; i < 4; i++)
{
tmp2.x = tmp1.x + mt[i][0];//引动的方向:横坐标向右移一位 x+1
tmp2.y = tmp1.y + mt[i][1];
if(tmp2.x >=0 && tmp2.x < n && tmp2.y >= 0 && tmp2.y < n && !vis[tmp2.x][tmp2.y] && a[tmp2.x][tmp2.y] != '#')
{
//判断条件
vis[tmp2.x][tmp2.y] = vis[tmp1.x][tmp1.y] + 1;//原来的位置加一
q.push(tmp2);
}
}
if(vis[ex][ey]){//如果到达终点就标记一下,表示能到达终点
flag = 1;
break;
}
}
if(flag)printf("%d\n", vis[ex][ey]-1);//终点位置也包括在内,所以减1
else printf("-1\n");
}
return 0;
}