深度优先DFS
DFS要有子问题的思路:子问题是什么?子问题如何状态转移到整体问题。主函数用于遍历所有的搜索位置,判断是否可以开始搜索。辅函数则负责具体的深度优先搜索的递归调用
搜索类
树、图等节点
序列等位于前面的关系
一维 or 二维(二叉树) dfs
记录状态的dfs (图)
基于图的DFS: 和BFS一样一般需要一个set来记录访问过的节点,避免重复访问造成死循环; Word XXX 系列面试中非常常见,例如word break,word ladder,word pattern,word search
基于排列组合的DFS: 其实与图类DFS方法一致,但是排列组合的特征更明显
记忆化搜索(DFS + Memoization Search):算是用递归的方式实现动态规划,递归每次返回时同时记录下已访问过的节点特征,避免重复访问同一个节点,可以有效的把指数级别的DFS时间复杂度降为多项式级别; 注意这一类的DFS必须在最后有返回值(分治法),不可以用回溯法; for循环的dp题目都可以用记忆化搜索的方式写,但是不是所有的记忆化搜索题目都可以用for循环的dp方式写。
二叉树
注意一定有递归终止条件,如果是叶子节点下面的空,可以直接返回;如果是中间几点,可以中途根据不同条件判断来return。如果有返回值的递归函数,可能是需要对节点返回值进行处理,也就是由子问题状态转移到整体问题上
注意如何在递归函数中不同阶段进行返回
遍历
preorder/ inorder/ postorder/ level
递归转迭代: preorder 直接用 stack; inorder 用 stack + cur; postorder 用 stack + cur + prev;
递归写法与非递归/栈写法
[了解]空间优化 Morris traversal: iterative approach to leverage preorder traversal that the entire left subtree would be placed between the node and its right subtree
属性
检查子树结构的题都需要一个 helper 函数,输入带两个 root
构造
二叉搜索树
中序遍历是从小到大
一些问题可以从排序列表的角度先得到思路,再转化到BST中
除了中序遍历,常用还有分治法(搜索一半树或搜索整个树)
divide & conquer
几种方式
回溯
回溯的本质仍然是穷举所有可能,需要记录节点状态的深度优先搜索,可以返回所有解。
N叉树
树的深度为总的选项个数
集合中递归查找子集,候选集合的大小就构成了树的宽度; 递归构成的树的深度
无返回
for循环横向遍历,递归纵向遍历,回溯不断调整结果集
回溯中递归函数的项:
paths 路径,已经做过的选择
选择列表,当期可以做出的选择
res 结果列表
每个节点既有路径也有选择。
知道回溯算法中如果什么都不设置会输出什么结果
去重
如果一个元素不能重复使用,需要startIndex,调整下一层递归的起始位置
如果不同位置的相同元素结果相同,那么排序,在横向for循环时判断
如果子序列不同排序,则采用额外的数组记录是否使用过
图搜索
常见的DFS用来解决什么问题?
1 图中(有向无向皆可)的符合某种特征(比如最长)的路径以及长度
2 排列组合
3 遍历一个图(或者树)
4 找出图或者树中符合题目要求的全部方案
DFS基本模板(需要记录路径,不需要返回值 and 不需要记录路径,但需要记录某些特征的返回值) 除了遍历之外多数情况下时间复杂度是指数级别,一般是O(方案数×找到每个方案的时间复杂度) 递归题目都可以用非递归迭代的方法写,但一般实现起来非常麻烦 基于树的DFS:需要记住递归写前序中序后序遍历二叉树的模板
不记录visited的话,有两个思路
递归的时候直接改原矩阵,递归返回的时候在改回去就行了。 要求输入能够被更改,但是程序跑完之后输入还是一样的, 不会被破环。
直接用UnionFind,检查连通性即可
Dijkstra 是一个最短路径算法,他的核心就是边的松弛
记忆化搜索
DFS + Memo就是DP,
代码
DFS
四个方向初始化和遍历
DFS辅助函数
Last updated