前面一篇文章讲解了DFS的基本概念和基础的使用方法,但不够深入,DFS的应用是很广泛的,不论是枚举状态或者路径,还是递归,其本质上都是DFS。今天就来好好的理解一下DFS的内在本质,并学会在树,在图以及在回溯中的应用。
回顾DFS
深度优先搜索,是指沿着某一路径方向,一直遍历到叶子节点为止,然后再回到初始顶点,换个方向继续。
这里就不过多的重复了,因为在前一篇文章里面已经讲过了,看那篇文章就好。
注意理解DFS的本质,DFS的本质就是递归,因此用递归式的DFS效率是最高的,如果是迭代式则要借助栈,伪码参见前一篇文章。
DFS树的遍历
树的常规遍历,涉及路径的问题,如查找 某一个路径,或者查找所有的路径都非常适合用DFS,效率也非常的高。
对于涉及树的层序的时候,如果是寻找层级内的某种状态,如层和,层最大值层最小值等,也是可以用DFS的。这方面可以参考前面的文章,以及关于二叉树的文章。
典型题目
题目 | 题解 | 说明 |
---|---|---|
110. 平衡二叉树 | 题解 | |
1448. 统计二叉树中好节点的数目 | 题解 | |
题解 | ||
题解 | ||
题解 |
路径问题
寻找特定的路径,或者枚举所有可能的路径就非常适合用DFS来求解。这其实是回溯算法,回溯其实就是用递归来枚举所有状态,这也是DFS的本质。
典型题目
题目 | 题解 | 说明 |
---|---|---|
797. 所有可能的路径 | 题解 | |
题解 | ||
题解 | ||
题解 |
图的遍历
典型题目
题目 | 题解 | 说明 |
---|---|---|
733. 图像渲染 | 题解 | |
200. 岛屿数量 | 题解 | |
695. 岛屿的最大面积 | 题解 |
有返回值的DFS
有返回值的情况略为复杂,常规的DFS,特别是递归式,只以标记当成返回结果的,函数本身并没有返回值,但有时候光做标记还不够,还需要额外的信息作为是否有解的判断,这时就需要额外的返回值,通常用dfs函数的返回值作为判断。
写返回值时就要小心一些,当超过边界了,或者确定无解的情况下时返回无解状态(如false),DFS过程中已标记过了的地方直接返回有解(如true),然后递归 调用,并把递归 的所有结果合并起来当作 返回值。这里特别要注意的是要把下一步都递归了,再合并结果,因为DFS除了有返回值外,它还会做标记,如果简单的进行与,会因为布尔操作符的short-circuit原因导致某些分支没走下去,最后的标记状态肯定就不对。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
典型题目
题目 | 题解 | 说明 |
---|---|---|
1254. 统计封闭岛屿的数目 | 题解 | |
1020. 飞地的数量 | 题解 | |
1905. 统计子岛屿 | 题解 |
着色法DFS
典型题目
题目 | 题解 | 说明 |
---|---|---|
802. 找到最终的安全状态 | 题解 | |
题解 | ||
题解 |
枚举+DFS(回溯)
如前所述,DFS的本质就是枚举所有状态,这其实也是回溯算法的核心所在,关于回溯可以参考另外的文章。
这类题目通常涉及矩阵或者可转化为图,用DFS进行暴力搜索(暴力穷举所有可能的路径),再辅以其他方法进行剪枝优化。
典型题目
题目 | 题解 | 说明 |
---|---|---|
79. 单词搜索 | 题解 | |
212. 单词搜索 II | 题解 | |
329. 矩阵中的最长递增路径 | 题解 | |
365. 水壶问题 | 题解 | 建模是难点,如何定义状态 |
386. 字典序排数 | 题解 | |
529. 扫雷游戏 | 题解 | |
题解 | ||
题解 | ||
题解 |