BFS与DFS

迷宫问题:

定义一个二维数组: 
int maze[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,
};
它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到 
右下角的最短路线。

Input           一个5 × 5的二维数组,表示一个迷宫。数据保证有唯一解。

Output         左上角到右下角的最短路径,格式如样例所示。

Sample Input
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

Sample Output
(0, 0) (1, 0) (2, 0) (2, 1) (2, 2) (2, 3) (2, 4) (3, 4) (4, 4)


广度优先搜索(BFS)

简要思想:

1、从初始状态s(起点)开始,利用规则(直线走:4个方向,加入斜线八个方向),生成下一层的状态(相邻点)。

2、顺序检查下一层的所有状态,看是否出现目标状态des,否则就对该层所有状态点,分别利用规则,再生成再下一层的所有状态点。

3、继续按照上面的思想生成再下一层的所有状态节点,这样层层往下展开,直到出现目标状态节点为止。

其中特别注意的是若要记录经过点的路径,要创建一个数组pre[N*N],节点ID对应的数组值pre[ID]==节点ID的前一个节点id,最后用递归的方法打印出路径节点。


程序用队列实现(queue)

#include <iostream>
#include <queue>
using namespace std;

void print(int des,int pre[30])   //递归实现逆推路线的打印
{
	if(pre[des]!=-1)
		print(pre[des],pre);
	cout<<'('<<des/5<<','<<des%5<<')'<<endl;

}
void BFS(int maze[5][5])
{
	int pre[30];//存储前一个节点
	queue<int> que;//定义que队列存储节点ID
	int vis[30];//是否被访问
	memset(vis,0,sizeof(vis));

	pre[0]=-1;
	que.push(0);//将起始点存入队列,并涂黑
	vis[0]=1;

	int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
	int now,row,col,nr,nc,next;
	while(!que.empty())
	{
		now=que.front();
		que.pop();//取出队列头的节点
		row=now/5;//得到节点的坐标位置
		col=now%5;
		for(int i=0;i<4;i++)//分别检测所取的节点的上下左右节点
		{
			nr=row+dir[i][0];
			nc=col+dir[i][1];
			next=nr*5+nc;//坐标换算成节点ID
			if(nr>=0&&nr<=5&&nc>=0&&nc<=5&&maze[nr][nc]==0&&!vis[next])
			{
				pre[next]=now;//记录next节点的上一节点
				//if(next==24) return;//到达终点
				vis[next]=1;
				que.push(next);
			}
		}
	}
	print(24,pre);//24为(4,4)的节点ID
}

void main()
{
	int maze[5][5];
	for(int i=0;i<5;i++)
		for(int j=0;j<5;j++)
			cin>>maze[i][j];
	BFS(maze);
}


核心表述:

void BFS()
{
	初始化队列Q;
	Q={起始点s};
	标记s为已访问;
	while(Q非空)
	{
		取出队列Q的首元素n;
		n出列;
		//if(n==目标点){……}
		所有与n相邻且未被访问的点进入队列;
		标记n为已访问
	}
	//完成遍历所有的点
}
广度优先算法详解:http://blog.csdn.net/raphealguo/article/details/7523411

以上实例步骤:

1.起始节点Vs变成灰色,加入队列QQ={(0,0)}

2.取出队列Q的头一个节点Vn,Vn={0,0}Q={}

3.把Vn={0,0}染成黑色,取出Vn所有相邻的白色节点{(1,0)}

4.不包含终点(4,4),染成灰色,加入队列QQ={(1,0)}

5.取出队列Q的头一个节点Vn,Vn={1,0}Q={}

6.把Vn={1,0}染成黑色,取出Vn所有相邻的白色节点{(2,0)}

7.不包含终点(4,4),染成灰色,加入队列QQ={(2,0)}

8.取出队列Q的头一个节点VnVn={2,0}Q={}

9.把Vn={2,0}染成黑色,取出Vn所有相邻的白色节点{(2,1), (3,0)}

10.不包含终点(4,4),染成灰色,加入队列QQ={(2,1), (3,0)}

11.取出队列Q的头一个节点VnVn={2,1}Q={(3,0)}

12. 把Vn={2,1}染成黑色,取出Vn所有相邻的白色节点{(2,2)}

13.不包含终点(4,4),染成灰色,加入队列QQ={(3,0), (2,2)}

14.持续下去,知道Vn的所有相邻的白色节点中包含了(4,4)……

15.此时获得了答案




深度优先搜索(DFS)

基本思想:

1、从初始状态S开始,利用规则生成搜索树下一层任一个状态节点;

2、看是否是目标状态节点des,若否,依此状态节点利用规则生成再下一层任一个节点,继续检查是否为目标状态节点,若否继续以上操作,一直进行到叶节点(即不能再生成新的状态节点,最底部)。

3、若叶节点仍不是目标状态节点,回溯到上一层结果,取另一可能扩展搜索的分支,依次生成新的状态节点,继续往下扩展,直到该支叶节点,若此过程仍未遇到目标节点,那么再次采用相同的回溯方法推导上层节点,扩展可能的分支生成新状态,一直继续下去直到找到目标状态点为止。 实例表述:

http://wenku.baidu.com/view/5e81f56152ea551811a6870a?fr=prin


深度和广度相比较

一般用广度来求解最优值(最短路径),但当N*N矩阵N很大时随着每一层的元素加入,则队列需要很大的存储空间,内存消耗很大;但对于深度来说,每次搜的过程,每一层只需要维护一个节点,但DFS只能寻找有解,而无法寻找最优解。

上题需改为:

定义一个二维数组:

int maze[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,
};

它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的一条路线。

用堆栈来实现深度搜索过程

(类比)

#include <iostream>
using namespace std;

int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
int maze[10][10]={
  0,1,0,0,0,0,0,0,0,0,  
  0,1,1,0,1,1,1,0,0,0,  
  0,0,0,0,0,0,0,0,0,0,  
  1,1,1,0,1,0,0,0,0,0,  
  0,1,0,0,1,0,1,1,1,0,  
  0,1,0,0,1,1,1,1,1,0,  
  0,0,0,1,1,0,0,0,1,0,  
  0,1,0,0,0,0,1,0,1,0,  
  0,1,1,1,0,1,1,0,1,1,  
  0,0,0,0,0,0,1,0,0,0,  
};
int table[10][10]={0};
int step=-1;
int stepcomplete=0;
int allcomplete=0;
int Act[1000]={0};//每一步节点的四个方向值为1-4
int x=0,y=0;

void test()//测试是否已达到目标 
{
	if(x==9&&y==9)
	{
		stepcomplete=allcomplete=1;
		cout << "Get to destination Success" << endl; 
	}
}

int actOK()
{
	int nextX=x+dir[Act[step]-1][0];
	int nextY=y+dir[Act[step]-1][1];
  
  if (Act[step] >4)//方向是否错误  
    return 0;  
  if(nextX >= 10 || nextX < 0)//x坐标是否越界  
    return 0;  
  if(nextY >= 10|| nextY < 0)//y坐标是否越界  
    return 0;  
  if(table[nextX][nextY] == 1)//是否已达到过  
    return 0;  
  if(maze[nextX][nextY] == 1)//是否有障碍  
    return 0; 

  x=nextX;
  y=nextY;
  table[x][y]=1;
  return 1;
}

void back()//到达死胡同时返回上一节点
{
	table[x][y]=0;
	x-=dir[Act[step-1]-1][0];
	y-=dir[Act[step-1]-1][1];
	Act[step]=0;//方向重新归位
	step--;//返回上一步
}

void DFS()
{
	table[x][y]=1;//起点标记已访问
	while(!allcomplete)
	{
		step++;
		stepcomplete=0;
		while(!stepcomplete)//对该点的四个相邻点进行判断,如果检测到有效点直接执行外循环,
		{  //若无改变方向,继续检测,直到四个都检测完,若检测为死胡同则返回上一节点,从没有检测的方向继续检测,依次进行
			
			Act[step]++;//改变方向
			if(actOK())//检测到有效点,点替换,重新外循环,重新检测新点的邻近有效点
			{
				test();//检测新点是否为终点
				stepcomplete=1;//进入下一个外循环
			}
			else
			{
				if(Act[step]>4)
					back();//已经搜寻完4个方向无有效点,回退到上个节点,重新内循环
				if(step<0)//全部路径搜寻完,没有找到目标点,退出
					stepcomplete=allcomplete=1;
			}

		}
	}
}

void print()
{
	cout<<"The path is:"<<endl;
	for(int i=0;i<10;i++)
	{
		for(int j=0;j<10;j++)
			cout<<table[i][j]<<' ';
		cout<<endl;
	}
	for(int i=0;i<10;i++)
	{
		for(int j=0;j<10;j++)
		{
			if(table[i][j]==1)
				cout<<'('<<i<<','<<j<<')'<<"->";
		}
	}
}

void main()
{
	DFS();
	print();
}
注:其主要思想是,对一个点的四个邻接点依次进行检测当检测到有效点(点的坐标在图内且未被访问过),直接对这个有效点进行新一轮的邻接有效点检测,直到遇到终点;若遇到死胡同,则返回上一点,继续从未检测的邻接点开始寻找有效点(又开辟了一条新路径),若无有效点继续返回上一点,依次进行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值