版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/admintan/article/details/78523900
Algorithm
BFS & DFS
本篇文章讨论一个最基础寻路场景 迷宫 在DFS BFS 中怎样求解
基础数据:
0#000000#*#########
0###0#00#0000000000
000#0#00##########0
#0000#00#0000000000
######00#0000######
00000000#0000000000
0000#00##########00
0000#00000000000000
0000#00000000000000
0000#00000000000000
0000#00000000000000
0000#0#############
0000000000000000000
0000000000000000000
0000000000000000000
0000000000#########
0000000000##0000000
00000#######0000000
0000000000000000000
0000000000000000000
此图为一个
开始 直到寻找到*为结束 最后将路径输出为符号 (.)
基础框架
节点类
class node
{
public:
int x,y;
int count;
node():x(0),y(0),count(0){};
node(int a,int b,int c){x=a;y=b;count=c;}
};
访问标记
如果图为二维数组的话 一般需要设置一个同样大小的访问标记图 以判断该点是否被走过了从而避免向回扫描 或者重复走
以一个布尔数组来定义
bool visited[mapsize][mapsize]
扫描方向
在一般的题目中可能出现的有两种形式即四方向 上下左右
int dir[4][2]={0,1,0,-1,1,0,-1,0}; //方向
和八方向 上下左右 以及偏方向 上左…下右
int dir[8][2]={0,1 ,0,-1 ,1,0, -1,0
-1,-1 ,-1,1 ,1,1, 1,-1};
或者
int dx[8]={0,0,1,1,1,-1,-1,-1};
int dy[8]={1,-1,1,0,-1,1,0,-1};
约束条件
此部分是为了防止访问超出边界或者图中的阻隔元素。
bool check(int x,int y)
{
if(x<0||x>=mapsize||y<0||y>=mapsize||map[x][y]=='#'||vis[x][y]==true||map[x][y]==0)
return true;
else return false;
}
DFS 递归 与 栈
使用栈来保存未被检测的点 节点按照深度优先次序被访问并依次入栈 并以相反的次序出栈进行新的检测
void DFS2(node s)//非递归DFS
{
cout<<"______________DFS___________"<<endl;
stack <node> p;
p.push(s);
vis[s.x][s.y]=true;
while(!p.empty())
{
node now = p.top();
//现在的位置是刚刚被推进栈顶的位置 由于先进后出所以向更深处搜索
p.pop();//出队一个元素
if(map[now.x][now.y]=='*')//结束条件
{
cout<<" path count is "<< now.count<<endl;
exit(0);}
for(int i=0;i<8;++i)
{
if(check(now.x+dx[i],now.y+dy[i]))
continue;//如果路径不合法跳过之后循环内的代码执行下一次循环
p.push(node(now.x+dx[i],now.y+dy[i],++now.count));
vis[now.x+dx[i]][now.y+dy[i]]=true;//标记走过
path[now.x+dx[i]][now.y+dy[i]]='.';//路径
//输出显示
showpath();
}
}
}
void DFS1(node s)//递归DFS
{
cout<<"____________________DFS____________"<<endl;
vis[s.x][s.y]=true;//标记初始点走过
if(map[s.x][s.y]=='*')
{
cout <<"Find *& End count is"<<s.count<<endl;
system("pause");
//exit(0);
}
for(int i=0;i<8;++i)
{
if(check(s.x+dx[i],s.y+dy[i]))
continue;
vis[s.x+dx[i]][s.y+dy[i]]=true;//标记走过
path[s.x+dx[i]][s.y+dy[i]]='@';//路径
showpath();
cout<< " i= "<<i<<" path count "<<s.count
<<" x "<<s.x+dx[i]<<" y "<<s.y+dy[i]<<endl;
DFS1(node(s.x+dx[i],s.y+dy[i],s.count+1));
}
}
我们来看一看DFS的搜索过程
0#00..00#*######### path
.###.#.0#0000000000 path
...#.#.0##########0 path
#....#.0#0000000000 path
######.0#0000###### path
000000.0#0000000000 path
0000#0.##########00 path
0000#00000000000000 path
0000#00000000000000 path
0000#00000000000000 path
0000#00000000000000 path
0000#0############# path
0000000000000000000 path
0000000000000000000 path
0000000000000000000 path
0000000000######### path
0000000000##0000000 path
00000#######0000000 path
0000000000000000000 path
0000000000000000000 path
0#00..00#*######### path
.###.#.0#0000000000 path
...#.#.0##########0 path
#....#.0#0000000000 path
######.0#0000###### path
000000.0#0000000000 path
0000#0.##########00 path
0000#0..0.000000000 path
0000#0......0000000 path
0000#0......0000000 path
0000#0......0000000 path
0000#0############# path
0000000000000000000 path
0000000000000000000 path
0000000000000000000 path
0000000000######### path
0000000000##0000000 path
00000#######0000000 path
0000000000000000000 path
0000000000000000000 path
0#00..00#.######### path
.###.#.0#.......... path
...#.#.0##########. path
#....#.0#000....... path
######.0#000.###### path
000000.0#000....... path
0000#0.##########.. path
0000#0..0.0.0.0.0.. path
0000#0............. path
0000#0............. path
0000#0............. path
0000#0############# path
0000000000000000000 path
0000000000000000000 path
0000000000000000000 path
0000000000######### path
0000000000##0000000 path
00000#######0000000 path
0000000000000000000 path
0000000000000000000 path
i= 1 path count 87 x 9 y 0
____________________DFS____________
Find *& End count is88
BFS
使用队列保存未被检测的节点 节点按照宽度优先的次序被访问和进出队列
void BFS(node s)
{
cout<<"______________BFS___________"<<endl;
queue <node> q;
q.push(s);
vis[s.x][s.y]=true;
while(!q.empty())
{
node now = q.front(); //现在的位置是刚刚被推进队列顶的位置
q.pop();//出队一个元素
if(map[now.x][now.y]=='*')//结束条件
{cout<<" path count is "<< now.count<<endl;}
for(int i=0;i<8;++i)
{
if(check(now.x+dx[i],now.y+dy[i]))
continue;//如果路径不合法跳过之后循环内的代码执行下一次循环
q.push(node(now.x+dx[i],now.y+dy[i],++now.count));
vis[now.x+dx[i]][now.y+dy[i]]=true;//标记走过
path[now.x+dx[i]][now.y+dy[i]]='.';//路径
//输出显示
showpath();
}
}
}
在这个执行过程中我们来看看搜索的变化范围:
0#.....0#*######### path
.###.#..#0000000000 path
...#.#..##########0 path
#....#00#0000000000 path
######00#0000###### path
00000000#0000000000 path
0000#00##########00 path
0000#00000000000000 path
0000#00000000000000 path
0000#00000000000000 path
0000#00000000000000 path
0000#0############# path
0000000000000000000 path
0000000000000000000 path
0000000000000000000 path
0000000000######### path
0000000000##0000000 path
00000#######0000000 path
0000000000000000000 path
0000000000000000000 path
0#......#*######### path
.###.#..#0000000000 path
...#.#..##########0 path
#....#..#0000000000 path
######..#0000###### path
0.......#0000000000 path
0...#..##########00 path
0...#......00000000 path
0...#......00000000 path
0...#......00000000 path
0000#......00000000 path
0000#0############# path
0000000000000000000 path
0000000000000000000 path
0000000000000000000 path
0000000000######### path
0000000000##0000000 path
00000#######0000000 path
0000000000000000000 path
0000000000000000000 path
0#......#*######### path
.###.#..#0000000000 path
...#.#..##########. path
#....#..#.......... path
######..#....###### path
........#.......... path
....#..##########.. path
....#.............. path
....#.............. path
....#.............. path
....#.............. path
....#.############# path
................... path
................... path
................... path
..........######### path
..........##....000 path
.....#######....000 path
.................00 path
.................00 path
可以看得出
DFS着重对深度的探索如果目标深度较高 那DFS有大概率会更高效
BFS着重对广度的探索如果目标深度较浅 或者分布分散 那BFS大概率会更高效
最终整体代码如下:
#include <iostream>
#include <cmath>
#include <fstream>
#include <string>
#include <queue>
#include <stack>
#include <string>
#include <memory.h>
#include <algorithm>
using namespace std;
#define mapsize 20
char map[mapsize][mapsize];
int dis[mapsize][mapsize];
bool vis[mapsize][mapsize];
char path[mapsize][mapsize];
int dx[8]={0,0,1,1,1,-1,-1,-1};
int dy[8]={1,-1,1,0,-1,1,0,-1};
void showmap();
void showpath();
class node
{
public:
int x,y;
int count;
node():x(0),y(0),count(0){};
node(int a,int b,int c){x=a;y=b;count=c;}
};
bool check(int x,int y)
{
if(x<0||x>=mapsize||y<0||y>=mapsize||map[x][y]=='#'||vis[x][y]==true||map[x][y]==0)
return true;
else return false;
}
bool check2(int x,int y)
{
if(x>=0&&x<mapsize&&y>=0&&y<mapsize&&map[x][y]!='#'&&vis[x][y]==false)
return false;
else return true;
}
// check和check2等价
void BFS(node s)
{
cout<<"______________BFS___________"<<endl;
queue <node> q;
q.push(s);
vis[s.x][s.y]=true;
while(!q.empty())
{
node now = q.front(); //现在的位置是刚刚被推进队列顶的位置
q.pop();//出队一个元素
if(map[now.x][now.y]=='*')//结束条件
{cout<<" path count is "<< now.count<<endl;}
for(int i=0;i<8;++i)
{
if(check(now.x+dx[i],now.y+dy[i]))
continue;//如果路径不合法跳过之后循环内的代码执行下一次循环
q.push(node(now.x+dx[i],now.y+dy[i],++now.count));
vis[now.x+dx[i]][now.y+dy[i]]=true;//标记走过
path[now.x+dx[i]][now.y+dy[i]]='.';//路径
//输出显示
showpath();
}
}
}
void DFS1(node s)//递归DFS
{
cout<<"____________________DFS____________"<<endl;
vis[s.x][s.y]=true;//标记初始点走过
if(map[s.x][s.y]=='*')
{
cout <<"Find *& End count is"<<s.count<<endl;
system("pause");
//exit(0);
}
for(int i=0;i<8;++i)
{
if(check(s.x+dx[i],s.y+dy[i]))
continue;
vis[s.x+dx[i]][s.y+dy[i]]=true;//标记走过
path[s.x+dx[i]][s.y+dy[i]]='@';//路径
showpath();
cout<< " i= "<<i<<" path count "<<s.count
<<" x "<<s.x+dx[i]<<" y "<<s.y+dy[i]<<endl;
DFS1(node(s.x+dx[i],s.y+dy[i],s.count+1));
}
}
void DFS2(node s)//非递归DFS
{
cout<<"______________DFS___________"<<endl;
stack <node> p;
p.push(s);
vis[s.x][s.y]=true;
while(!p.empty())
{
node now = p.top();
//现在的位置是刚刚被推进栈顶的位置 由于先进后出所以向更深处搜索
p.pop();//出队一个元素
if(map[now.x][now.y]=='*')//结束条件
{
cout<<" path count is "<< now.count<<endl;
exit(0);}
for(int i=0;i<8;++i)
{
if(check(now.x+dx[i],now.y+dy[i]))
continue;//如果路径不合法跳过之后循环内的代码执行下一次循环
p.push(node(now.x+dx[i],now.y+dy[i],++now.count));
vis[now.x+dx[i]][now.y+dy[i]]=true;//标记走过
path[now.x+dx[i]][now.y+dy[i]]='.';//路径
//输出显示
showpath();
}
}
}
void showmap()
{
//display
for(int x=0;x<mapsize;x++)
{
for (int y=0;y<mapsize;y++)
cout << map[y][x] ;
cout<< " map "<<endl;
}
std::cout << "\n";
}
void showpath()
{
for(int x=0;x<mapsize;x++)
{
for (int y=0;y<mapsize;y++)
cout << path[y][x] ;
cout<< " path "<<endl;
}
cout<<endl;
}
int main(int argc, char* argv[])
{
ifstream file;
file.open("map.in");
char ch[mapsize];
int i=0,j=0,line=0;
while(!file.eof()&&line<mapsize)//读图一到二十行
{
file>>ch;
line++;
for(i=0;i<mapsize;i++)
map[i][j]=ch[i];
j++;
i=0;
}
//读图完毕
showmap();
node start;
// 队列BFS
memcpy(path,map,sizeof(map));//重新复制路径图
memset(vis,false,sizeof(vis));//清空
BFS(start);
cout<< "Press any key to process DFS1"
system("pause");
// 递归DFS
memset(path,0,sizeof(path));
memcpy(path,map,sizeof(map));//重新复制路径图
memset(vis,false,sizeof(vis));//清空
DFS1(start);
//栈DFS
memset(path,0,sizeof(path));
memcpy(path,map,sizeof(map));//重新复制路径图
memset(vis,false,sizeof(vis));//清空
DFS2(start);
file.close();
system("pause");
return 0;
}