【C++算法】dfs深度优先搜索(上) ——【全面深度剖析+经典例题展示】

这篇具有很好参考价值的文章主要介绍了【C++算法】dfs深度优先搜索(上) ——【全面深度剖析+经典例题展示】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

💃🏼 本人简介:男
👶🏼 年龄:18
📕 ps:七八天没更新了欸,这几天刚搞完元宇宙,上午一直练🚗,下午背四级单词和刷题来着,还在忙一些学弟学妹录制视频和准备开学一些事,一直没空出时间来,等 20号练完车,也马上开学了QAQ。不过今天倒是空出来一些时间,恰好这几天学到了dfs,原理和例题都很棒,谨以此篇作为学后的回顾总结!

1. dfs算法原理

1.1 dfs思想

  • 深度优先搜索,简称dfs,简单讲就是一个搜索算法。
  • 深搜是按照深度优先的方式进行搜索,通俗来讲就是一条路走到黑不撞南墙不回头
  • 注意:这里的搜索并不是我们平时在文件上或网络上查找信息,而是通过一种穷举的方式,把所有可行的方案都列举出来,不断去尝试,直到找到问题的解。
  • 具体来讲,dfs可以将“问题状态空间”看做一棵搜索树,深度优先就是从树根一直往下搜,遇到不可解就回溯,往其它方向继续向下扩展,像子集和和全排列问题,还有N皇后问题都可以深度优先搜索算法解决,它是一种暴力解决NP问题的非常直观的方法。
  • 总的来说:DFS 用于找所有解的问题,它的空间效率高,而且找到的不一定是最优解,必须记录并完成整个搜索,故一般情况下,深搜需要非常高效的剪枝(剪枝的概念请百度)。

1.2 与递归区别

  • 深搜是一种算法,注重的是思想;而递归是一种基于编程语言的实现方式。
  • 深搜可以用递归实现,也就是说递归是我们用计算机编程语言实现深搜算法的手段。

1.3 举例说明

如下图,灰色代表墙壁,绿色代表起点,红色代表终点,规定每次只能走一步,且只能往下或右走。求一条绿色到红色的最短路径。例子来源于这里

深度优先算法案例演示详解,# 算法,深度优先,算法,c++,dfs,深度优先遍历

用dfs来讲就是,先从绿点开始找准一个方向,并沿这个方向一直遍历,如果遇到障碍物不能继续遍历就回溯,返回上一个节点,直到找到终点为止。深度优先算法案例演示详解,# 算法,深度优先,算法,c++,dfs,深度优先遍历

2. 经典例题——迷宫游戏

都学了这么多了。我们不妨来玩一个迷宫游戏巩固一下所学的算法!【迷宫如图下所示】
深度优先算法案例演示详解,# 算法,深度优先,算法,c++,dfs,深度优先遍历

最短的解法如下图所示【大家答对了嘛】 深度优先算法案例演示详解,# 算法,深度优先,算法,c++,dfs,深度优先遍历

2.1 题干信息

  • 我们用一个二维字符数组来表示图画的迷宫。
S**.
....
***T
  • 其中S表示起点,T表示终点,*表示墙壁,.表示平地。你需要从S出发走到T,每次只能向上下左右相邻的位置移动一位,不能走出地图,也不能穿过墙壁,每个点只能通过一次。用x表示你所要走的路线。

2.2 整体思路

  • 我们从起点S开始,每走一步需要对上下左右一个方向一个方向地尝试,如果沿着某个方向不能走到终点,我们就要原路返回,继续尝试其他方向,直到走出迷宫。这是一种最朴素的走迷宫方式,虽然效率也许比较低,但如果迷宫有解,就一定能走出终点。
  • 上面说的这种走法,就对应着今天学习的dfs算法。首先找到起点s,走到每个点时,按照左、下、右、上的顺序尝试。每走到下一个点以后,我们把这个点当做起点S,继续按顺序尝试。如果某个点上下左右四个方向都尝试过,便回到走到这个点之前的点,这一步我们称之为回溯。继续尝试其他方向。直到所有点都尝试过上下左右四个方向。
  • 这就好比你自己去走这个迷宫,你也要一个方向一个方向的尝试着走,如果这条路不行就回头,尝试下一条路,dfs 的思想和我们直观的想法很类似。只不过,接下来我们需要用程序来完成这个过程。

2.3 细分拆解

第一步前的输入地图和变量设置,就不详细讲了,直接看代码即可

#include<iostream>
#include<stdio.h>
using namespace std;
int n, m;
char pos[150][150];	   //判断走没走过
bool trace[150][150];  //显示路径

int main() {
	//输入地图
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			cin >> pos[i][j];
		}
	}
	return 0;
}

①判断迷宫终点,记录所走路径

  • 首先确定边界条件,当走到字符T时,我们找到了终点,从而结束搜索。所以边界条件判断为pos[x][y] == 'T'
  • 其次,为了防止走回头路,我们需要标记当前这个路径已走过,即当前这个点已走过,所以我们需要用trace[x][y]数组来做标记,为了显示出路径,走过的点我们用字符x表示。
bool dfs(int x, int y) {
	if (pos[x][y] == 'T') { //找到终点,返回true
		return true;
	}
	trace[x][y] = 1;		//若找不到,则trace数组标记为1表示已走过
	pos[x][y] = 'x';		//用pos显示最后的路径
}

②完善搜索与回溯,处理数组边界

  • 结束操作处理好后,就要开始真正的搜索了。假设现在我们坐标为(x, y),分别遍历该坐标的上下左右位置,选择好依次进行方向的顺序后,一个方向一个方向进行访问,如果某一方向能走到终点,则返回true
  • 在上下左右遍历时,我们要考虑数组元素是否越界,此时我们就需要一个bool类型的check_in()函数进行判断。
  • 注意:判断移动后位置能走的3个条件【缺一不可】
    • ①没越界,在地图内;
    • ②这个位置不是障碍物*,可以走到;
    • ③该位置之前没走过
bool check_in(int x, int y) {	//	判断数组是否越界
	return (x > 0 && x <= n && y > 0 && y <= m); //这里表示的是如果()里为真,则返回true,否则返回false
}

bool dfs(int x, int y) {
	if (pos[x][y] == 'T') { //找到终点,返回true
		return true;
	}
	trace[x][y] = 1;		//若上下左右都找不到,则trace数组标记为1表示已走过
	pos[x][y] = 'x';		//用pos显示最后的路径

	int tx = x - 1, ty = y; //假设先往上走
	if (check_in(tx, ty) && pos[tx][ty] != '*' && !trace[tx][ty]) { //能移动到该位置的条件有三个:①没越界,在地图内; ②这个位置不是障碍物*,可以走到; ③该位置之前没走过
		if (dfs(tx, ty)) {	//对移动后位置进行判断是不是终点,如果是,返回true,如果不是,在对其上下左右判断。
			return true;
		}
	}

	tx = x, ty = y - 1; //如果往上走行不通,则选择向左走
	if (check_in(tx, ty) && pos[tx][ty] != '*' && !trace[tx][ty]) {
		if (dfs(tx, ty)) {
			return true;
		}
	}

	tx = x + 1, ty = y; //如果往左走行不通,则选择向下走
	if (check_in(tx, ty) && pos[tx][ty] != '*' && !trace[tx][ty]) {
		if (dfs(tx, ty)) {
			return true;
		}
	}

	tx = x , ty = y + 1; //如果往下走行不通,则选择向右走
	if (check_in(tx, ty) && pos[tx][ty] != '*' && !trace[tx][ty]) {
		if (dfs(tx, ty)) {
			return true;
		}
	}

	pos[x][y] = '.';	//	如果一个位置的上下左右都走不了,则取消该位置的路径进行回溯,回溯过程把之前已标记的'x'改回'.'
	trace[x][y] = 0;//只找可行路线的话,trace可改可不改。但如果找全部解,则需要恢复
	return false;
}

大家有没有发现判断上下左右是否可行时,主题代码是不是都是一样的欸?所以大家可以想一下,能怎么去优化一下嘛,在这里我卖个关子,咱们后面马上讲!

③找寻迷宫起点,打印结束路径

  • 我们费心费力地把迷宫主体的dfs的函数写完了,但我们该从哪开始呢?结束条件如何设置呢?
int main() {
	//输入地图
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			cin >> pos[i][j];
		}
	}
	cout << "----------------------------------------" << endl;

	//找寻起点
	int x , y ; //定义x,y用于保存迷宫起点时的位置
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			if (pos[i][j] == 'S') {
				x = i, y = j;
			}
		}
	}
	if (dfs(x, y)) { //如果能找到终点,则打印迷宫显示其路径
		cout << "鼠鼠我欸,已到达终点啦,路线如下: " << endl;
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				cout << pos[i][j];
			} cout << endl;
		}
	}
	else {
		cout << "鼠鼠我欸,走不出去啦awa" << endl;
	}
	return 0;
}

2.4 总体代码展示

#include<iostream>
#include<stdio.h>
using namespace std;

int n, m;
char pos[150][150];	   //判断走没走过
bool trace[150][150];  //显示路径

bool check_in(int x, int y) {	//	判断数组是否越界
	return (x > 0 && x <= n && y > 0 && y <= m); //这里表示的是如果()里为真,则返回true,否则返回false
}

bool dfs(int x, int y) {
	if (pos[x][y] == 'T') { //找到终点,返回true
		return true;
	}
	trace[x][y] = 1;		//若上下左右都找不到,则trace数组标记为1表示已走过
	pos[x][y] = 'x';		//用pos显示最后的路径

	int tx = x - 1, ty = y; //假设先往上走
	if (check_in(tx, ty) && pos[tx][ty] != '*' && !trace[tx][ty]) { //能移动到该位置的条件有三个:①没越界,在地图内; ②这个位置不是障碍物*,可以走到; ③该位置之前没走过
		if (dfs(tx, ty)) {	//对移动后位置进行判断是不是终点,如果是,返回true,如果不是,在对其上下左右判断。
			return true;
		}
	}

	tx = x, ty = y - 1; //如果往上走行不通,则选择向左走
	if (check_in(tx, ty) && pos[tx][ty] != '*' && !trace[tx][ty]) {
		if (dfs(tx, ty)) {
			return true;
		}
	}

	tx = x + 1, ty = y; //如果往左走行不通,则选择向下走
	if (check_in(tx, ty) && pos[tx][ty] != '*' && !trace[tx][ty]) {
		if (dfs(tx, ty)) {
			return true;
		}
	}

	tx = x , ty = y + 1; //如果往下走行不通,则选择向右走
	if (check_in(tx, ty) && pos[tx][ty] != '*' && !trace[tx][ty]) {
		if (dfs(tx, ty)) {
			return true;
		}
	}

	pos[x][y] = '.';	//	如果一个位置的上下左右都走不了,则取消该位置的路径进行回溯,回溯过程把之前已标记的'x'改回'.'
	trace[x][y] = 0;//只找可行路线的话,trace可改可不改。但如果找全部解,则需要恢复
	return false;
}
int main() {
	//输入地图
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			cin >> pos[i][j];
		}
	}
	cout << "----------------------------------------" << endl;
	//找寻起点
	int x , y ; //定义x,y用于保存迷宫起点时的位置
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			if (pos[i][j] == 'S') {
				x = i, y = j;
			}
		}
	}
	if (dfs(x, y)) { //如果能找到终点,则打印迷宫显示其路径
		cout << "鼠鼠我欸,已到达终点啦,路线如下: " << endl;
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				cout << pos[i][j];
			} cout << endl;
		}
	}
	else {
		cout << "鼠鼠我欸,走不出去啦awa" << endl;
	}
	return 0;
}

2.5 测试样例

深度优先算法案例演示详解,# 算法,深度优先,算法,c++,dfs,深度优先遍历

2.6 代码优化

  • 前面提到,我们在对下一位置进行上下左右判断时,需要写四个主体相同的代码。为了减少代码量,我们不妨写一个循环,用一个二维数组依次表示四个方向,进而进行判断。
int dir[4][2] = { { -1 , 0 } , { 0 , -1 } , { 1 ,  0 } , { 0 , 1 } };
//	按逆时针依次表示     向上	      向左		 向下	         向右	
//第一个数表示x(行)变化,第二个表示y(列)变化
bool dfs(int x, int y) {
	if (dfs(x, y) == 'T') {
		return true;
	}
	trace[x][y] = 1;
	pos[x][y] = 'x';
	for (int i = 1; i <= 4; i++) {  //1、2、3、4依次表示上、左、下、右
		int tx = x + dir[i][0]; //表示x加上第几个方向的第1个数,即行变化,
		int ty = y + dir[i][1];  //表示x加上第几个方向的第2个数,即列变化,
		if (check_in(tx, ty) && pos[tx][ty] != '*' && !trace[tx][ty]) {
			if (dfs(tx, ty)) {
				return true;
			}
		}
	}
	pos[x][y] = '.';	//	如果一个位置的上下左右都走不了,则取消该位置的路径进行回溯,回溯过程把之前已标记的'x'改回'.'
	trace[x][y] = 0;//只找可行路线的话,trace可改可不改。但如果找全部解,则需要恢复
}

优化后代码

#include<iostream>
#include<stdio.h>
using namespace std;

int n, m;
char pos[150][150];	   //判断走没走过
bool trace[150][150];  //显示路径
		int dir[4][2] = { { -1 , 0 } , { 0 , -1 } , { 1 ,  0 } , { 0 , 1 } };
//	按逆时针依次表示     向上	      向左		 向下	         向右	
//第一个数表示x(行)变化,第二个表示y(列)变化

bool check_in(int x, int y) {	//	判断数组是否越界
	return x > 0 && x <= n && y > 0 && y <= m; //这里表示的是如果()里为真,则返回true,否则返回false
}

bool dfs(int x, int y) {
	if (dfs(x, y) == 'T') {
		return true;
	}
	trace[x][y] = 1;
	pos[x][y] = 'x';
	for (int i = 1; i <= 4; i++) {  //1、2、3、4依次表示上、左、下、右
		int tx = x + dir[i][0]; //表示x加上第几个方向的第1个数,即行变化,
		int ty = y + dir[i][1];  //表示x加上第几个方向的第2个数,即列变化,
		if (check_in(tx, ty) && pos[tx][ty] != '*' && !trace[tx][ty]) {
			if (dfs(tx, ty)) {
				return true;
			}
		}
	}
	pos[x][y] = '.';	//	如果一个位置的上下左右都走不了,则取消该位置的路径进行回溯,回溯过程把之前已标记的'x'改回'.'
	trace[x][y] = 0;//只找可行路线的话,trace可改可不改。但如果找全部解,则需要恢复

}


int main() {
	//输入地图
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			cin >> pos[i][j];
		}
	}
	cout << "----------------------------------------" << endl;
	//找寻起点
	int x , y ; //定义x,y用于保存迷宫起点时的位置
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			if (pos[i][j] == 'S') {
				x = i, y = j;
			}
		}
	}
	if (dfs(x, y)) { //如果能找到终点,则打印迷宫显示其路径
		cout << "鼠鼠我欸,已到达终点啦,路线如下: " << endl;
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				cout << pos[i][j];
			} cout << endl;
		}
	}
	else {
		cout << "鼠鼠我欸,走不出去啦awa" << endl;
	}
	return 0;
}

最后,感谢大家支持u (^ _ ^)

如果感觉这篇文章对你有帮助的话,不妨三连支持下,十分感谢(✪ω✪)。文章来源地址https://www.toymoban.com/news/detail-783260.html

printf("点个赞吧*^*");
cout << "收藏一下叭o_o";
System.out.println("评论一下吧^_^");
print("关注一下叭0-0")

到了这里,关于【C++算法】dfs深度优先搜索(上) ——【全面深度剖析+经典例题展示】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 深度优先搜索(DFS)算法

    目录 算法思想 时间复杂度和空间复杂度 算法实现 算法优缺点 应用领域 深度优先搜索(DFS)算法的思想是从图的某个起始顶点开始,沿着一条路径尽可能深入地访问图中的所有顶点,直到不能继续为止,然后返回并探索其他路径。具体而言,DFS算法使用栈数据结构来实现,

    2024年02月05日
    浏览(47)
  • 深度优先搜索(DFS)(算法笔记)

    本文内容基于《算法笔记》和官方配套练题网站“晴问算法”,是我作为小白的学习记录,如有错误还请体谅,可以留下您的宝贵意见,不胜感激。 深度优先搜索是一种枚举所有完整路径以遍历所有情况的搜索方法,总是以“深度”作为前进的。实现方式是有很多,最

    2024年02月08日
    浏览(53)
  • C++ 更多的DFS深度优先搜索...

    目录 DFS模版 剪枝 DFS的两种状态 使用全局变量存储 使用函数参数存储传递 众所周知,DFS是一种省督有限搜索,可以想象成一棵树根节点K开始递归到最深层的节点,通常用来枚举符合题目的所有可能情况或者个数。 话说,这个代码和全排列有什么不同吗哈哈哈哈哈 剪枝时

    2024年02月13日
    浏览(67)
  • [算法日志]图论: 深度优先搜索(DFS)

    ​ 深度优先搜索算法是一种遍历图这种数据结构的算法策略,其中心思想是朝图节点的一个方向不断跳转,当该节点无下一个节点或所有方向都遍历完时,便回溯朝上一个节点的另一个方向继续遍历。这种搜索策略与回溯法有异曲同工之妙。 正因为和回溯法有相似之处,所

    2024年02月03日
    浏览(63)
  • 【算法详解 | DFS算法】深度优先搜索解走迷宫问题 | 深度优先图遍历

    by.Qin3Yu 本文需要读者掌握 结构体 和 栈 的操作基础,完整代码将在文章末尾展示。 特别声明:本文为了尽可能使用简单描述,以求简单明了,可能部分专有名词定义不准确。 栈相关操作可以参考我的往期博文: 【C++数据结构 | 栈速通】使用栈完成十进制数转二四八进制数

    2024年02月03日
    浏览(51)
  • 第一周算法训练(dfs)(深度优先搜索算法)

    dfs: 深度优先搜索算法 ,是一种用于遍历或 搜索树或图的算法 .沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被

    2024年02月20日
    浏览(52)
  • Python 算法基础篇:深度优先搜索( DFS )和广度优先搜索( BFS )

    深度优先搜索( DFS )和广度优先搜索( BFS )是两种常用的图遍历算法,用于在图中搜索目标节点或遍历图的所有节点。本篇博客将介绍 DFS 和 BFS 算法的基本概念,并通过实例代码演示它们的应用。 😃😄 ❤️ ❤️ ❤️ 深度优先搜索( DFS )是一种用于遍历或搜索图或树

    2024年02月07日
    浏览(67)
  • 图的遍历(搜索)算法(深度优先算法DFS和广度优先算法BFS)

    从图的某个顶点出发访问遍图中所有顶点,且每个顶点仅被访问一次。(连通图与非连通图) 1、访问指定的起始顶点; 2、若当前访问的顶点的邻接顶点有未被访问的,则任选一个访问之;反之,退回到最近访问过的顶点;直到与起始顶点相通的全部顶点都访问完毕; 3、若

    2024年01月17日
    浏览(49)
  • 【数据结构与算法】搜索算法(深度优先搜索 DFS和广度优先搜索 BFS)以及典型算法例题

    【数据结构与算法】系列文章链接: 【数据结构与算法】递推法和递归法解题(递归递推算法典型例题) 【数据结构与算法】系列文章链接: 【数据结构与算法】C++的STL模板(迭代器iterator、容器vector、队列queue、集合set、映射map)以及算法例题 【数据结构与算法】系列文章链

    2024年04月13日
    浏览(78)
  • 如何实现一个简单的深度优先搜索(DFS)算法?

    前端入门之旅:探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅!这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发者,这里都将为你提供一

    2024年02月07日
    浏览(54)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包