文章目录
DFS和递归
前面博客中“一只老鼠走迷宫的问题”。设num是到达的钻块数量,算法过程描述如下:
- 在初始值令num=1,标记这个位置已经走过。
- 左,上,右,下4个方向,按顺时针顺序选一个能走的方向,走一步。
- 在新位置num++,标记这个位置已经走过。
- 继续前进,如果无路可走,回退到上一步,换个方向再走。
- 继续上面的过程,直到结束。
在以上过程中,能够访问到所有合法的钻块,并且每个钻块只访问一次,不会重复访问(回退不算重复),如下图所示
路线如图所示,从1到13,能一直走下去。在13这个位置,到了底不能再走了,按顺序回退到12,11;在11这个位置,换个方向到14,15。到达15后,发现不能再走下去,那么按顺序倒退,即14-11-10-9-8-7-6-5-4-3-2-1,在这个过程中发现全部都没有新路,最后退回到起点,结束。
为加深对递归的理解,这里再次给出递归返回的完整顺序,即13-12-15-14-11-10-9-8-7-6-5-4-3-2-1。
在这个过程中,最重要的特点是在一个位置只要有路,就一直走到最深处,直到无路可走,再退回一步,看在上一步的位置能不能换一个方向继续往下走。这样就遍历了所有可能走到的位置。
这个思路就是深度搜索。从初始状态出发,下一步可能有多种状态;选其中一个状态深入,到达新的状态;直到无法继续深入,回退到前一步,转移到其他状态,然后再深入下去。最后遍历完所有可以到达的状态,并得到最终的解。
上述过程用DFS实现最简单,代码比BFS短很多。
案例:用DFS实现红黑瓷钻
题目
有一个长方形的房间,铺着方形瓷砖,瓷砖为红色或黑色。一个人站在黑色瓷砖上,他可以按
上,下,左,右方向移动到相邻的瓷砖。但是他不能在红色瓷砖上移动,只能在黑色瓷砖上移
动。编程计算他可以达到的黑色瓷砖的数量。
输入:第一行包含两个正整数W和H,W和H分别表示x方向和y方向上的瓷砖数量。W和H均不超
过20.下面有H行,每行包含W个字符。每个字符表示一片瓷砖的颜色。用符号表示如下;“●”表示
黑瓷钻;“#”表示红色瓷砖;“@”代表黑色瓷钻上的人,在数据集中只出现一次。
输出:一个数字,这个人从初始瓷钻能到达的瓷钻总数量(包括起点)。
思路
阅读上面的例子,思路几乎相同
源码
#include<iostream>
using namespace std;
char room[23][23];
int dir[4][2] = {//利用二维表示法来表示左,上,右,下四个方向
{0,-1},
{-1,0},
{0,1},
{1,0}
};
int Wx, Hy, num=0; //Wx行,Hy列,用num表示统计可以走的位置有多少个。num=1表示算上起点,起点也可以走
#define CHECK(x,y) (x <Wx && x>=0 && y<Hy && y>=0) //判断是否在room中
void DFS(int wx, int hy) {
room[wx][hy] = '#';//标记这个位置,表示已经走过了
cout << "此点已经访问:" << wx << "," << hy << endl;
num++;//进一次递归,就表示可以走一个瓷钻
for (int i = 0; i < 4; i++) {//按左,上,右,下顺序走.
int newx = wx + dir[i][0];
int newy = hy + dir[i][1];
if (room[newx][newy] == '.' && CHECK(newx, newy)) {
DFS(newx, newy);//进行递归
cout << "递归回退 back:" << wx << " " << hy << endl;
}
}
}
int main() {
int x, y, wx=0, hy=0;//wx,hy用于标记起点
cout << "请输入您的行数和列数:";
while (cin >> Wx >> Hy) {//输入行数和列数
if (Wx == 0 && Hy == 0) {
break;
}
cout << "请输入您设计的地图:";
for (x = 0; x < Wx; x++) {
for (y = 0; y < Hy; y++) {
cin >> room[x][y];
if (room[x][y] == '@') {
wx = x;
hy = y;
}
}
}
DFS(wx, hy);
cout << "num:" << num << endl;
}
system("pause");
return 0;
}
运行结果
这里增加了回退情况的打印,方便理解。
分析
总结
分析这里的DFS和全面博客的BFS,显然可以看出DFS代码更加简短,而且很简单。
BFS:就像烟花,一颗爆炸后就消失,然后产生多个烟花,这多个烟花又爆炸,自己又消失(所以就是一个出,多个进的概念自然就是队列的思想)
DFS:DFS就像是迅捷斥候,被派往侦察敌情,把每一个地方都侦察一遍,然后返回军营(起点)汇报军情。(先往深处走再回退到原点,自然就是递归)。
所以思路的不同,根据实际情况运用的时机也不同。
下一篇博客我们分析 “回溯与剪枝”,有关8皇后的问题。