0
点赞
收藏
分享

微信扫一扫

算法入门到进阶(三)——搜索技术(DFS和递归)

Java架构领域 2022-02-04 阅读 23

文章目录


DFS和递归

前面博客中“一只老鼠走迷宫的问题”。设num是到达的钻块数量,算法过程描述如下:

  1. 在初始值令num=1,标记这个位置已经走过。
  2. 左,上,右,下4个方向,按顺时针顺序选一个能走的方向,走一步。
  3. 在新位置num++,标记这个位置已经走过。
  4. 继续前进,如果无路可走,回退到上一步,换个方向再走。
  5. 继续上面的过程,直到结束。

在以上过程中,能够访问到所有合法的钻块,并且每个钻块只访问一次,不会重复访问(回退不算重复),如下图所示
在这里插入图片描述
路线如图所示,从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皇后的问题。

举报

相关推荐

0 条评论