背景

DFS 英文全称为(Depth First Search),中文简称深度优先搜索算法,其过程为沿着每一个可能的路径向下进行搜索,直到不能再深入为止,并且每一个节点只能访问一次。

算法的搜索遍历图的步骤

(1)首先找到初始节点A,

(2)依此从A未被访问的邻接点出发,对图进行深度优先遍历

(3)若有节点未被访问,则回溯到该节点,继续进行深度优先遍历

(4)直到所有与顶点A路径想通的节点都被访问过一次

举个例子,在下方的无向连通图中,假设我们要从起始点A出发,使用深度优先搜索算法进行搜索,首先访问A->B->E,走不通了,回溯到A起始点,走第二个分支节点B,路径为A->C->F->H->G->D,走不通了,再回溯到起始点A,发现所有的节点都已经走过一次了,因此本次搜索结束。

DFS的应用

深度优先搜索算法常常被用于解决迷宫问题。

首先我们定义一个5*5的迷宫矩阵 maze

0 1 0 0 0
0 1 1 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0

其中0代表迷宫走可以走的路,1代表迷宫中的墙壁

要求从左上角出发,走到左下角结束(0,0)出发,走到(4,4)

我们使用深度优先搜索算法进行求解

(1)从起始点(0,0)出发,第一步只能往下走(1,0),第二步走到交叉路口(2,0)

0 1 0 0 0
0 1 1 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0

(2)由于出口在右下角,设定优先顺序,下>右>左>上

(3)走到(2,0)处开始往下走,直到走到(4,2)发现走不通

0 1 0 0 0
0 1 1 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0

(4)此时回溯到上一节点(2,0),开始沿着另一个分支进行深度优先搜索

0 1 0 0 0
0 1 1 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0

(5)在节点(2,4)中再次遇到分支,优先往下走,最终走到(4,4)走出迷宫

0 1 0 0 0
0 1 1 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0

(6)因此最终走出迷宫的路径为:

(0,0)->(1,0)->(2,0)->(2,1)->(2,2)->(2,3)->(2,4)->(3,4)->(4,4)

(6)假设迷宫的出口在(2,0),则回溯到最近的未走过的分支顶点(2,4)往上走,最终走到终点

0 1 0 0 0
0 1 1 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0

代码

def DFS(x, y):if x <0 or x >= len(maze) or y < 0 or y >= len(maze[0]):#走出了迷宫墙外,不合法return Falseif maze_visit[x][y] == True:#防止走回头return Falseif maze[x][y] == 1:#标记为1的不能走return Falsemaze_visit[x][y] = True#标记本次递归路线,防止走回头if x == N and y == M:#走到终点停止递归myStack.append((x,y))return Truefor m in move:#四个方向尝试走next_x = x+m[0]next_y = y+m[1]if DFS(next_x, next_y):#判断能不能走通,能走通继续下一步递归myStack.append((x,y))#将走通的路径记录下来return Truereturn Falsemaze = [[0, 1, 0, 0, 0],[0, 1, 1, 1, 0],[0, 0, 0, 0, 0],[0, 1, 1, 1, 0],[0, 0, 0, 1, 0]]#定义迷宫
maze_visit = [[False, False, False, False, False],[False, False, False, False, False],[False, False, False, False, False],[False, False, False, False, False],[False, False, False, False, False]]#记录路线是否已经走过,防止走反move = [(0,1), (0,-1), (1,0), (-1,0)] #定义四个方向走的顺序
N, M = 4, 4 #定义出口的位置
myStack = []#记录走通的路径
DFS(0,0)#递归求解
myStack = myStack[::-1]#反转列表
for row in myStack:print('(' + str(row[0]) + ','+ str(row[1]) + ')')

输出迷宫路线

>>>move = [(0,1), (0,-1), (1,0), (-1,0)] #定义四个方向走的顺序
>>>N, M = 4, 4 #定义出口的位置
>>>myStack = []#记录走通的路径
>>>DFS(0,0)#递归求解
>>>myStack = myStack[::-1]#反转列表
>>>for row in myStack:
...    print('(' + str(row[0]) + ','+ str(row[1]) + ')')
(0,0)
(1,0)
(2,0)
(2,1)
(2,2)
(2,3)
(2,4)
(3,4)
(4,4)
0 1 0 0 0
0 1 1 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0

变更出口位置

>>>move = [(0,1), (0,-1), (1,0), (-1,0)] #定义四个方向走的顺序
>>>N, M = 0, 2 #定义出口的位置
>>>myStack = []#记录走通的路径
>>>DFS(0,0)#递归求解
>>>myStack = myStack[::-1]#反转列表
>>>for row in myStack:
...    print('(' + str(row[0]) + ','+ str(row[1]) + ')')
(0,0)
(1,0)
(2,0)
(2,1)
(2,2)
(2,3)
(2,4)
(1,4)
(0,4)
(0,3)
(0,2)
0 1 0 0 0
0 1 1 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0

递归的回溯过程

以入口为(0,0),出口为(0,2)为例,详细说一下递归的回溯过程

0 1 0 0 0
0 1 1 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0

在代码中添加增加埋点,使每次发生递归时,对参数(x,y)进行输出

def DFS(x, y):print("end:", str((x, y)))if x <0 or x >= len(maze) or y < 0 or y >= len(maze[0]):#走出了迷宫墙外,不合法print("False: 走出了迷宫墙外,不合法")return Falseif maze_visit[x][y] == True:#防止走回头print("False: 往回走,不合法")return Falseif maze[x][y] == 1:#标记为1的不能走print("False: 穿墙,不合法")return Falsemaze_visit[x][y] = True#标记本次递归路线,防止走回头if x == N and y == M:#走到终点停止递归print("True: 到达终点")myStack.append((x,y))return Truefor m in move:#四个方向尝试走print("strat:", str((x,y)), "move:", str(m))next_x = x+m[0]next_y = y+m[1]if DFS(next_x, next_y):#判断能不能走通,能走通继续下一步递归myStack.append((x,y))#将走通的路径记录下来return Trueprint("回溯上一节点")return False

创建好埋点后执行代码,输出如下:

>>>maze = [[0, 1, 0, 0, 0],
...        [0, 1, 1, 1, 0],
...        [0, 0, 0, 0, 0],
...        [0, 1, 1, 1, 0],
...        [0, 0, 0, 1, 0]]#定义迷宫
>>>maze_visit = [[False, False, False, False, False],
...              [False, False, False, False, False],
...              [False, False, False, False, False],
...              [False, False, False, False, False],
...              [False, False, False, False, False]]#记录路线是否已经走过,防止走反
>>>move = [(0,1), (0,-1), (1,0), (-1,0)] #定义四个方向走的顺序
>>>N, M = 0, 2 #定义出口的位置
>>>myStack = []#记录走通的路径
>>>DFS(0,0)#递归求解
end: (0, 0)
strat: (0, 0) move: (0, 1)
end: (0, 1)
False: 穿墙,不合法
回溯上一节点
strat: (0, 0) move: (0, -1)
end: (0, -1)
False: 走出了迷宫墙外,不合法
回溯上一节点
strat: (0, 0) move: (1, 0)
end: (1, 0)
strat: (1, 0) move: (0, 1)
end: (1, 1)
False: 穿墙,不合法
回溯上一节点
strat: (1, 0) move: (0, -1)
end: (1, -1)
False: 走出了迷宫墙外,不合法
回溯上一节点
strat: (1, 0) move: (1, 0)
end: (2, 0)
strat: (2, 0) move: (0, 1)
end: (2, 1)
strat: (2, 1) move: (0, 1)
end: (2, 2)
strat: (2, 2) move: (0, 1)
end: (2, 3)
strat: (2, 3) move: (0, 1)
end: (2, 4)
strat: (2, 4) move: (0, 1)
end: (2, 5)
False: 走出了迷宫墙外,不合法
回溯上一节点
strat: (2, 4) move: (0, -1)
end: (2, 3)
False: 往回走,不合法
回溯上一节点
strat: (2, 4) move: (1, 0)
end: (3, 4)
strat: (3, 4) move: (0, 1)
end: (3, 5)
False: 走出了迷宫墙外,不合法
回溯上一节点
strat: (3, 4) move: (0, -1)
end: (3, 3)
False: 穿墙,不合法
回溯上一节点
strat: (3, 4) move: (1, 0)
end: (4, 4)
strat: (4, 4) move: (0, 1)
end: (4, 5)
False: 走出了迷宫墙外,不合法
回溯上一节点
strat: (4, 4) move: (0, -1)
end: (4, 3)
False: 穿墙,不合法
回溯上一节点
strat: (4, 4) move: (1, 0)
end: (5, 4)
False: 走出了迷宫墙外,不合法
回溯上一节点
strat: (4, 4) move: (-1, 0)
end: (3, 4)
False: 往回走,不合法
回溯上一节点
回溯上一节点
strat: (3, 4) move: (-1, 0)
end: (2, 4)
False: 往回走,不合法
回溯上一节点
回溯上一节点
strat: (2, 4) move: (-1, 0)
end: (1, 4)
strat: (1, 4) move: (0, 1)
end: (1, 5)
False: 走出了迷宫墙外,不合法
回溯上一节点
strat: (1, 4) move: (0, -1)
end: (1, 3)
False: 穿墙,不合法
回溯上一节点
strat: (1, 4) move: (1, 0)
end: (2, 4)
False: 往回走,不合法
回溯上一节点
strat: (1, 4) move: (-1, 0)
end: (0, 4)
strat: (0, 4) move: (0, 1)
end: (0, 5)
False: 走出了迷宫墙外,不合法
回溯上一节点
strat: (0, 4) move: (0, -1)
end: (0, 3)
strat: (0, 3) move: (0, 1)
end: (0, 4)
False: 往回走,不合法
回溯上一节点
strat: (0, 3) move: (0, -1)
end: (0, 2)
True: 到达终点

接下来详细对每一步进行解析,先看方向依次为:右>左>下>上

move = [(0,1), (0,-1), (1,0), (-1,0)]

第一步:(0,0)往右走到(0,1)穿墙,不合法,回溯到(0,0)

第二步:(0,0)往左走到(0,-1)走出了迷宫墙外,不合法,回溯到(0,0)

第三步:(0,0)往下走到(1,0)合法,将(1,0)作为起点,进入DFS(1,0)

end: (0, 0)
strat: (0, 0) move: (0, 1)
end: (0, 1)
False: 穿墙,不合法
回溯上一节点
strat: (0, 0) move: (0, -1)
end: (0, -1)
False: 走出了迷宫墙外,不合法
回溯上一节点
strat: (0, 0) move: (1, 0)
end: (1, 0)
0 1 0 0 0
0 1 1 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0

第四步:(1,0)往右走到(1,1)穿墙,不合法,回溯到(1,0)

第五步:(1,0)往左走到(1,-1)走出了迷宫墙外,不合法,回溯到(1,0)

第六步:(1,0)往下走到(1,0)合法,将(2,0)作为起点,进入DFS(2,0)

strat: (1, 0) move: (0, 1)
end: (1, 1)
False: 穿墙,不合法
回溯上一节点
strat: (1, 0) move: (0, -1)
end: (1, -1)
False: 走出了迷宫墙外,不合法
回溯上一节点
strat: (1, 0) move: (1, 0)
end: (2, 0)
0 1 0 0 0
0 1 1 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0

第七步:(2,0)往右走到(2,1)合法,将(2,1)作为起点,进入DFS(2,1)

第八步:(2,1)往右走到(2,2)合法,将(2,2)作为起点,进入DFS(2,2)

第九步:(2,2)往右走到(2,3)合法,将(2,3)作为起点,进入DFS(2,3)

第十步:(2,3)往右走到(2,4)合法,将(2,4)作为起点,进入DFS(2,4)

第十一步:(2,4)往右走到(2,5)走出了迷宫墙外,不合法,回溯到(2,4)

第十二步:(2,4)往左走到(2,3)往回走,不合法,回溯到(2,4)

第十三步:(2,4)往下走到(3,4)合法,将(3,4)作为起点,进入DFS(3,4)

strat: (2, 0) move: (0, 1)
end: (2, 1)
strat: (2, 1) move: (0, 1)
end: (2, 2)
strat: (2, 2) move: (0, 1)
end: (2, 3)
strat: (2, 3) move: (0, 1)
end: (2, 4)
strat: (2, 4) move: (0, 1)
end: (2, 5)
False: 走出了迷宫墙外,不合法
回溯上一节点
strat: (2, 4) move: (0, -1)
end: (2, 3)
False: 往回走,不合法
回溯上一节点
strat: (2, 4) move: (1, 0)
end: (3, 4)
0 1 0 0 0
0 1 1 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0

第十四步:(3,4)往右走到(3,5)走出了迷宫墙外,不合法,回溯到(3,4)

第十五步:(3,4)往左走到(3,3)往回走,不合法,回溯到(3,4)

第十六步:(3,4)往下走到(4,4)合法,将(4,4)作为起点,进入DFS(4,4)

strat: (3, 4) move: (0, 1)
end: (3, 5)
False: 走出了迷宫墙外,不合法
回溯上一节点
strat: (3, 4) move: (0, -1)
end: (3, 3)
False: 穿墙,不合法
回溯上一节点
strat: (3, 4) move: (1, 0)
end: (4, 4)
0 1 0 0 0
0 1 1 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0

第十七步:(4,4)往右走到(4,5)走出了迷宫墙外,不合法,回溯到(4,4)

第十八步:(4,4)往左走到(4,3)穿墙,不合法,回溯到(4,4)

第十九步:(4,4)往下走到(4,4)走出了迷宫墙外,不合法,回溯到(4,4)

第十九步:(4,4)往上走到(3,4)往回走,不合法,回溯到(4,4)

第二十步:此时四个方向都走不通,因此回溯到上一个交叉路口(3,4),在第十四到第十八步(3,4)节点已经往右,左,下三个方向走过一次了(move循环到了第三位),因此只剩往上走一种选择,(3,4)往上走到(2,4)往回走,不合法,回溯到(2,4)

第二十一步:同理(2,4)在第十一到第十三步已经往右,左,下三个方向走过一次了,因此只剩往上走一种选择,(3,4)往上走到(1,4),合法,将(1,4)作为起点,进入DFS(1,4)

strat: (4, 4) move: (0, 1)
end: (4, 5)
False: 走出了迷宫墙外,不合法
回溯上一节点
strat: (4, 4) move: (0, -1)
end: (4, 3)
False: 穿墙,不合法
回溯上一节点
strat: (4, 4) move: (1, 0)
end: (5, 4)
False: 走出了迷宫墙外,不合法
回溯上一节点
strat: (4, 4) move: (-1, 0)
end: (3, 4)
False: 往回走,不合法
回溯上一节点
回溯上一节点
strat: (3, 4) move: (-1, 0)
end: (2, 4)
False: 往回走,不合法
回溯上一节点
回溯上一节点
strat: (2, 4) move: (-1, 0)
end: (1, 4)
0 1 0 0 0
0 1 1 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0

从(1,4)开始到达终点的步骤再次就不进行详细解析了~~~(同理)最终到达终点(0,2)

strat: (1, 4) move: (0, 1)
end: (1, 5)
False: 走出了迷宫墙外,不合法
回溯上一节点
strat: (1, 4) move: (0, -1)
end: (1, 3)
False: 穿墙,不合法
回溯上一节点
strat: (1, 4) move: (1, 0)
end: (2, 4)
False: 往回走,不合法
回溯上一节点
strat: (1, 4) move: (-1, 0)
end: (0, 4)
~~~~~~~~~~~~~~~~~~~~
strat: (0, 4) move: (0, 1)
end: (0, 5)
False: 走出了迷宫墙外,不合法
回溯上一节点
strat: (0, 4) move: (0, -1)
end: (0, 3)
~~~~~~~~~~~~~~~~~~~~
strat: (0, 3) move: (0, 1)
end: (0, 4)
False: 往回走,不合法
回溯上一节点
strat: (0, 3) move: (0, -1)
end: (0, 2)
True: 到达终点

!!!!!!!!!!!!!!!!!!递归结束!!!!!!!!!!!!!!!!!!!!

使用递归求解的案例

匈牙利算法寻找最大匹配_202xxx的博客-CSDN博客

【牛客网华为机试】HJ28 素数伴侣_202xxx的博客-CSDN博客

【牛客网华为机试】HJ43 迷宫问题_202xxx的博客-CSDN博客

(DFS)深度优先搜索算法详解相关推荐

  1. C语言 DFS(深度优先搜索算法) 详解

    基本概念 深度优先搜索算法(Depth First Search,简称DFS):一种用于遍历或搜索树或图的算法. 沿着树的深度遍历树的节点,尽可能深的搜索树的分支.当节点v的所在边都己被探寻过或者在搜 ...

  2. C++算法之深度优先搜索算法详解

    1.深度优先搜索算法 深度优先搜索是一种在开发爬虫早期使用较多的方法.它的目的是要达到被搜索结构的叶结点(即那些不包含任何超链的HTML文件) .在一个HTML文件中,当一个超链被选择后,被链接的HT ...

  3. A*启发式搜索算法详解 人工智能

    A*启发式搜索算法详解 人工智能 我们尝试解决的问题是把一个游戏对象(game object)从出发点移动到目的地.路径搜索(Pathfinding)的目标是找到一条好的路径--避免障碍物.敌人,并把 ...

  4. 1-Wire搜索算法详解(1)

    最近一直在proteus上仿真单总线搜索算法,虽然参考了美信公司的应用笔记以及其他的一些资源,仍然没有成功,估计应该是protues问题.这里先转载一篇不错的博文,以供参考. 原文连接 http:// ...

  5. 1-Wire搜索算法详解(2)

    原文连接:http://blog.sina.com.cn/s/blog_57ad1bd20102uxxv.html 1-Wire搜索算法详解(2) 4 实例及算法分析 要理解算法,或制定算法,我们需要 ...

  6. DS18B20 1-WIRE ROM搜索算法详解

    转自:http://blog.sina.com.cn/s/blog_57ad1bd20102uxxw.html 1-WIRE搜索算法详解(1) 0前言 美信公司(http://www.maximint ...

  7. 算法导论:dfs深度优先搜索算法及基于dfs的拓扑排序以及宽度优先搜索算法bfs

    1.dfs深度优先搜索算法 算法导论中是通过三种标记颜色来介绍dfs的,white代表还没被搜过,grey代表被搜了一些,还没结束,white表示已经搜索完成的状态. c/c++复现dfs代码 #in ...

  8. opencv制作微信小游戏 最强连一连 辅助(2)--dfs深度优先搜索算法

    深度优先搜索算法还是大二上数据结构的时候学的,工作以后都忘得差不多了.赶紧回来温习一下吧. 深度优先搜索的算法的 入参是一个地图(一般可以用二维数组表示)和一个起始点. 比如 这个就是一个5*5的二维 ...

  9. c++深度优先搜索详解

    目录 哈喽 dfs乃何物? DFS算法过程: dfs基本框架 题目1 代码 题目2 n-皇后问题 代码 寻路 代码 最后 哈喽 各位好 晚上从沙发上起来,端坐在电脑前 我大抵是无聊了 思前想后,我还是 ...

最新文章

  1. Redis中7种集合类型应用场景
  2. Oracle 游标的练习
  3. Flink学习笔记02:Flink三种运行模式
  4. extjs JsonStore加载数据,Combobox只显示最后一项值问题
  5. exec 直接赋值_了解 JavaScript 解构赋值
  6. HTTP缓存-http强制缓存与协商缓存
  7. 产品经理职责技能和所需证书
  8. wifi服务器延迟高,网络延时高(网络延迟高怎么办(家里WIFI延迟高,教你几招搞定网络延迟))...
  9. 逍遥安卓模拟器提示此设备未经Play保护机制认证解决方法步骤
  10. 我最近在看什么——《蛤蟆先生去看心理医生》
  11. SpringBoot日记本系统全程直播09:项目一期完结
  12. 如何用深度学习进行语音识别
  13. 敏捷开发系列学习总结(8)——创业公司研发团队怎么建设
  14. 两个均匀分布相加、两个正态分布相加、由均匀分布生成正态分布
  15. 软件模块化定制将造成传统软件消失?
  16. 航模电池基本使用及保养
  17. vs发布exe文件给别人打开闪退问题
  18. 微信小程序 自动对对联
  19. PSEN ma1.3p-22PSEN ma1.3-12皮尔兹安全开关
  20. Java 数据结构与算法

热门文章

  1. 生态版图 | 10月份YashanDB获信创产品认证,并与3款产品完成互认证
  2. UG区域拉伸和零件透明在装配中不显示
  3. LearnOpenGL->立方体贴图
  4. java 抽奖_简单实现java抽奖系统
  5. 集成支付宝,跳转到支付宝后显示的不是支付页面
  6. Unified Functional Testing(UFT)15.0.2入门保姆级教程(二),图文详解。QTP
  7. 2015中国智能硬件蛋年创新大会手记
  8. 【Unity入门计划】基本概念(8)-瓦片地图 TileMap 01
  9. Android 百度地图导航引擎初始化失败
  10. JS模块化-实现一个简单的CommonJS