bfs和dfs(第一次尝试)

99封情书 提交于 2019-12-22 15:17:19

DFS和BFS的各种例题

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;
}

上面这个代码因为在思维凌乱的时候写的,所以有多个瑕疵

  1. 在bfs函数里面将开始的那个点设定,那么他就只能在一个点开始,但如果说是在同一时间的多个点,他就少变化,应该将结构体传进去,在主函数里面就把开始的点进行设定
int  bfs(int m,int n,state s)//s为开始的那个点
  1. 当时并没有判断这个图的边界,这样就会超时,因为这个代码是向四周共同发展的,在加入队列的时候应该判断那个坐标的数是不是符合要求
if(!vst[next.x][next.y]&&next.x<=m&&next.x>=1&&next.y>=1&&next.y<=n)//判断条件因题目而异
	q.push(next);//上面是用一个结构体变量数组来搭建一个简易的队列
	//这里相当于a[tail]=next;	
  1. bfs开始并没有将起始地的vst函数制动成1,这样下一步就会出现回跳,造成计算上的浪费
  2. 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;
}

缺陷

  1. 第一个致命的缺陷是判断的边界的时候,根本没判断完整,因而在这个图案 的负坐标系上无限度深搜,导致内存耗尽结束程序。
  2. 由于起始点肯定在边界内,所以其实这个判断可以浓缩到在是否为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;		//找到了就直接跳出来给上一层传递消息 
			}
  1. 同样存在没有为起始点设置vst值的操作
  2. 上面那个代码没有给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;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!