图的深度优先搜索(DFS)类似于树的先序遍历,尽可能“深”地搜索一个图。
DFS 算法
基本思想
首先访问某一起始顶点v,再由v出发,访问与v邻接且未访问过的顶点w,在以w为起始顶点出发,访问与w邻接且未访问过的顶点……
1 | void DFS (Graph G, int v) { //从顶点v出发,深度优先遍历图G |
以下图为例,采用深度优先搜索,且以字母顺序优先,遍历顺序如下:
graph LR a((a)) b((b)) c((c)) d((d)) e((e)) f((f)) g((g)) a --- b & c b --- c & d c --- e d --- f & g
性能分析
空间复杂度
DPS利用递归,需要一个以每个顶点为起点的DFS函数递归工作栈,空间复杂度为 $O(|V|)$
($|V|$ 表示顶点的个数)
时间复杂度
遍历图实际上是对每一个顶点查找其邻接点的过程,时间复杂度取决于图的存储结构。
邻接矩阵
查找每个顶点的邻接点的时间为 $O(|V|)$,总时间复杂度 $O(|V^2|)$
邻接表
查找所有顶点的邻接点的时间为 $O(|E|)$,(即有多少条边,查找多少次邻接点),访问顶点所需时间为 $O(|V|)$,总时间复杂度 $O(|V|+|E|)$
DFS 例题
迷宫问题
描述
定义一个二维数组 N*M ,如 5 × 5 数组下所示:
它表示一个迷宫,其中的$1$表示墙壁,$0$表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的路线。入口点为$[0,0]$,既第一格是可以走的路。
数据范围:$2\leq n,m \leq 10$,输入的内容只包含 $0、1$
输入描述:
输入两个整数,分别表示二维数组的行数,列数。再输入相应的数组,其中的1表示墙壁,0表示可以走的路。数据保证有唯一解,不考虑有多解的情况,即迷宫只有一条通道。
输出描述:
左上角到右下角的最短路径
示例一
输入:
5 5
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)
(1,0)
(2,0)
(2,1)
(2,2)
(2,3)
(2,4)
(3,4)
(4,4)
分析
从迷宫左上角的起点开始,对迷宫进行深度优先遍历,把$1$当作已访问过的点,$0$当作未访问过的点。先尽量“深”地遍历未访问的点,若周围的点都被访问过,则则证明此路不通,回溯到上一个还有未访问的邻接点的点,重新开始深度优先遍历。
对于深度优先遍历的当前点,
如果该点未访问过,(如果是回溯到该点,则已访问过,不需要进行这两步)
将该点加入临时路径,该点标记为已访问
如果该点为终点,则当前路径为最终路径
如果存在未访问过的邻接点,则按(下、右、上、左)的优先顺序,访问邻接点,即对该邻接点进行递归的深度优先遍历
如果邻接点都已访问,则进行回溯
重新修改该点为未访问,将该点弹出临时路径
(利用递归自动实现)通过函数栈回到上一个点的遍历访问,查看剩下的优先顺序的邻接点是否未访问,如不存在未访问的,则继续向上一个点回溯,直到回溯到的点还剩有未访问的邻接点,重新开始深度遍历
代码实现
1 |
|
数独游戏
描述
玩家需要根据9X9盘面上的已知数字,推算出所有剩余空格的数字,并且满足每一行、每一列、每一个3X3粗线宫内的数字均含1-9,并且不重复。
例如:(0代表空缺位)
数据范围:输入一个 9*9 的矩阵
输入描述:
包含已知数字的9X9盘面数组(空缺位以数字0表示)
输出描述:
完整的9X9盘面数组
示例一
输入:
0 9 2 4 8 1 7 6 3
4 1 3 7 6 2 9 8 5
8 6 7 3 5 9 4 1 2
6 2 4 1 9 5 3 7 8
7 5 9 8 4 3 1 2 6
1 3 8 6 2 7 5 9 4
2 7 1 5 3 8 6 4 9
3 8 6 9 1 4 2 5 7
0 4 5 2 7 6 8 3 1
输出:
5 9 2 4 8 1 7 6 3
4 1 3 7 6 2 9 8 5
8 6 7 3 5 9 4 1 2
6 2 4 1 9 5 3 7 8
7 5 9 8 4 3 1 2 6
1 3 8 6 2 7 5 9 4
2 7 1 5 3 8 6 4 9
3 8 6 9 1 4 2 5 7
9 4 5 2 7 6 8 3 1
示例二
输入:
0 0 8 7 1 9 2 4 5
9 0 5 2 3 4 0 8 6
0 7 4 8 0 6 1 0 3
7 0 3 0 9 2 0 0 0
5 0 0 0 0 0 0 0 0
8 6 1 4 0 3 5 2 9
4 0 0 0 2 0 0 0 8
0 0 0 0 0 0 0 7 0
1 0 7 0 6 8 0 5 0
输出:
6 3 8 7 1 9 2 4 5
9 1 5 2 3 4 7 8 6
2 7 4 8 5 6 1 9 3
7 4 3 5 9 2 8 6 1
5 9 2 6 8 1 4 3 7
8 6 1 4 7 3 5 2 9
4 5 6 3 2 7 9 1 8
3 8 9 1 4 5 6 7 2
1 2 7 9 6 8 3 5 4
分析
深度优先遍历所有的空缺位,对于每个空缺位,
- 找出目前该空缺位所有可能的取值
- 如果存在可能的取值
- 将可能的取值之一填入
- 如果当前为最后一个空缺点,且有可能的取值,则求得最终结果
- 深度优先搜索下一个空缺位
- 如果不存在可能的取值
- 该位恢复成空缺位
- 回溯回上一个空缺位,换一个可能的取值
代码实现
1 |
|