DFS和BFS区别
bfs是广搜,一般是找最优解和最小路径,但是因为是一层一层的查找,所以就不适合分支太多,或者深度太深的
dfs是深搜,它解决了这个弱点,但是不能找出最优解,只能知道有解
走迷宫
描述
一个迷宫由R行C列格子组成,有的格子里有障碍物,不能走;有的格子是空地,可以走。
给定一个迷宫,求从左上角走到右下角最少需要走多少步(数据保证一定能走到)。只能在水平方向或垂直方向走,不能斜着走。
输入
第一行是两个整数,R和C,代表迷宫的长和宽。( 1<= R,C <= 40)
接下来是R行,每行C个字符,代表整个迷宫。
空地格子用’.‘表示,有障碍物的格子用’#‘表示。
迷宫左上角和右下角都是’.’。
输出
输出从左上角走到右下角至少要经过多少步(即至少要经过多少个空地格子)。计算步数要包括起点和终点。
样例输入
5 5
…##
#…#
#.#.#
#.#.#
#.#…
样例输出
9
BFS
未优化代码实现:(可跳过)
#include<stdio.h>
int vst[1000][1000]={0};
struct state{ //记录可走的那个位置的深度以及坐标
int x,y;
int step_counter;
}a[10000];
int dir[4][2]={{-1,0},{1,0},{0,-1},{0,1},};//左,右,下上
int bfs(int m,int n){
state now,next;
int head=0; //简易建一个队列
int tail=0;
int x=1,y=1;
a[head].x=1;//开始发展的地方,这里题目肯定是(1,1)
a[head].y=1;
a[head].step_counter=1;
tail++;
if(a[head].x==m&&a[head].y==n)//判断开始的点是不是直接就是终点
return a[head].step_counter;
while(head<tail)
{
now=a[head];//提取出队列第一个元素
head++;
int i;
for(i=0;i<4;i++)//这个循环算出这个点向左右上下移动后的那四个点
{
next.step_counter=now.step_counter+1;
next.x=now.x+dir[i][0];
next.y=now.y+dir[i][1];//给这四个点加状态
if(next.x==m&&next.y==n)//是终点直接返回
return next.step_counter;
if(vst[next.x][next.y]==0)//如果路是通的,并且没有被访问过(不走回头路,减少计算负担)
{
a[tail]=next;
tail++;
vst[next.x][next.y]=1; //让它是被计算
}
}
}
}
int main ()
{
int m ,n,i,z;
char str;
scanf("%d%d",&m,&n);
for(i=1;i<=m;i++)
for(z=1;z<=n;z++)
{
scanf(" ");
scanf("%c",&str);
if(str=='#')
vst[i][z]=1;
else if(str=='.')
vst[i][z]=0;
}
printf("%d",bfs(m,n));
return 0;
}
上面这个代码因为在思维凌乱的时候写的,所以有多个瑕疵
- 在bfs函数里面将开始的那个点设定,那么他就只能在一个点开始,但如果说是在同一时间的多个点,他就少变化,应该将结构体传进去,在主函数里面就把开始的点进行设定
int bfs(int m,int n,state s)//s为开始的那个点
- 当时并没有判断这个图的边界,这样就会超时,因为这个代码是向四周共同发展的,在加入队列的时候应该判断那个坐标的数是不是符合要求
if(!vst[next.x][next.y]&&next.x<=m&&next.x>=1&&next.y>=1&&next.y<=n)//判断条件因题目而异
q.push(next);//上面是用一个结构体变量数组来搭建一个简易的队列
//这里相当于a[tail]=next;
- bfs开始并没有将起始地的vst函数制动成1,这样下一步就会出现回跳,造成计算上的浪费
- bfs的主要动力就是靠循环来放同一层和下一层的(在队列中最多同时存在两层的数字)以及队列的 FIFO 的特点来进行最优解 的实现。而这里会发现在循环外面单独的判断了下起始点是不是终止点,这也让代码显得很长,可放在循环里面判断
优化后的代码:
#include<stdio.h>
#include<queue>
using namespace std;
struct state{
int x,y;
int step_counter;
};
int vst[1000][1000]={0};
int que[10000];
int dir[4][2]={{0,1},{0,-1},{-1,0},{1,0}};
int bfs(int m,int n,state s){
queue <state> q;
vst[s.x][s.y]=1;
int i;
q.push(s);
state now,next;
while(!q.empty())
{
now=q.front();
q.pop();
if(now.x==m&&now.y==n)
return now.step_counter;
for(i=0;i<4;i++)
{
next.step_counter=now.step_counter+1;
next.x=now.x+dir[i][0];
next.y=now.y+dir[i][1];
if(!vst[next.x][next.y]&&next.x<=m&&next.x>=1&&next.y>=1&&next.y<=n)
q.push(next);
vst[next.x][next.y]=1;
}
}
return 0;
}
int main ()
{
int m ,n,i,z;
char str;
scanf("%d%d",&m,&n);
for(i=1;i<=m;i++)
for(z=1;z<=n;z++)
{
scanf(" ");
scanf("%c",&str);
if(str=='#')
vst[i][z]=1;
else if(str=='.')
vst[i][z]=0;
}
state begin;
begin.step_counter = 1;
begin.x = 1;
begin.y = 1;
printf("%d",bfs(m,n,begin));
return 0;
}
DFS
dfs的关键和bfs不同,dfs是一条路走到死,撞南墙了,再回去一格,继续找。而bfs是一层一层找。因而dfs不需要用队列来存储要接下去找的那同一层的东西。
dfs的关键是通过自我的调用一次一次往下找
未成功代码 (可跳过)思路凌乱时候的错误代码,被bfs有点带偏
#include<stdio.h>
int vst[1000][1000]={0};//记录网格图每个点是否被访问,或者有障碍,1为有障碍或者已经访问,0反之
int dir[4][2]={{0,1},{0,-1},{-1,0},{1,0}};//向上下左右移动
int ans = 0; //用来记录最后的答案
struct state{ //记录这个网格图每一个的坐标和权值
int x, y ;
int step_counter;
};
int dfs(state s,int m,int n){
if(s.x<=m&&s.y<=n) //判断边界不完整
{
if(s.x==m&&s.y==n) //题目最后右下角为目的地
{
printf("找到了");
ans=s.step_counter;
return 1;
}
int i;
state now,next;
for(i=0;i<4;i++) //找这个点的接下去四周的点
{
next.step_counter=now.step_counter+1;
next.x=now.x+dir[i][0];
next.y=now.y+dir[i][1];
if(!vst[next.x][next.y]){
vst[next.x][next.y]=1;
if(dfs(next,m,n))
return 1; //找到了就直接跳出来给上一层传递消息
vst[next.x][next.y]=0;
}
//发现next这个点走下去的路线都不是,那就从now这个点的另一个下一个点重新开始
}
return 0;
}
}
int main ()
{
int m ,n,i,z;
char str;
scanf("%d%d",&m,&n);
for(i=1;i<=m;i++)
for(z=1;z<=n;z++)
{
scanf(" ");
scanf("%c",&str);
if(str=='#')
vst[i][z]=1;
else if(str=='.')
vst[i][z]=0;
}
state begin;
begin.step_counter=0; //记录起始地的坐标权值
begin.x=1;
begin.y=1;
dfs(begin,m,n);
printf("%d",ans);
return 0;
}
缺陷:
- 第一个致命的缺陷是判断的边界的时候,根本没判断完整,因而在这个图案 的负坐标系上无限度深搜,导致内存耗尽结束程序。
- 由于起始点肯定在边界内,所以其实这个判断可以浓缩到在是否为now结构体变量的接下一个变量next进行dfs操作判断上
if(!vst[next.x][next.y]&&next.x>=0&&next.x<=m&&next.y>=0&&next.y<=n){
if(dfs(next,m,n))
return 1; //找到了就直接跳出来给上一层传递消息
}
- 同样存在没有为起始点设置vst值的操作
- 上面那个代码没有给now赋值(
醉了)
实现代码
#include<stdio.h>
int vst[1000][1000]={0};//记录网格图每个点是否被访问,或者有障碍,1为有障碍或者已经访问,0反之
int dir[4][2]={{0,1},{0,-1},{-1,0},{1,0}};//向上下左右移动
int ans = 0; //用来记录最后的答案
struct state{ //记录这个网格图每一个的坐标和权值
int x, y ;
int step_counter;
};
int dfs(state s,int m,int n){
if(s.x==m&&s.y==n) //题目最后右下角为目的地
{
ans=s.step_counter;
return 1; //找到了就直接给上面那一层传递1信号,上面那层dfs就能用下面那个if(dfs)来持续给再上一层传递1信号,如此反复,最后跳出整个dfs函数
}
int i;
state now,next;
now = s;
vst[now.x][now.y]=1;//防止再次回跳
for(i=0;i<4;i++) //找这个点的接下去四周的点
{
next.step_counter=now.step_counter+1;//给接下去那个值设定初始值
next.x=now.x+dir[i][0];
next.y=now.y+dir[i][1];
if(!vst[next.x][next.y]&&next.x>=0&&next.x<=m&&next.y>=0&&next.y<=n){
if(dfs(next,m,n))//用来接收下一层dfs的return 1信号
return 1; //上一层的dfs传递消息
//vst[next.x][next.y]=0;会发现这里少了一行代码,是因为这里已经判断这个点的接下去所有路都走不通,那么就没必要再访问它
}
//发现next这个点走下去的路线都不是,那就从now这个点的另一个下一个点重新开始
}
return 0;//上面都没有return1的话,那就说明路走不通,那最后就返回0
}
int main ()
{
int m ,n,i,z;
char str;
scanf("%d%d",&m,&n);
for(i=1;i<=m;i++)
for(z=1;z<=n;z++)
{
scanf(" ");
scanf("%c",&str);
if(str=='#')
vst[i][z]=1;
else if(str=='.')
vst[i][z]=0;
}
state begin;
begin.step_counter=1; //记录起始地的坐标权值
begin.x=1;
begin.y=1;
dfs(begin,m,n);
printf("%d",ans);
return 0;
}
来源:CSDN
作者:斌斌翻水水
链接:https://blog.csdn.net/weixin_45924187/article/details/103446344