暴力递归
自顶向下:
f(total) = f(child1) + f(child2) + ... + f(childn) ? total是否能完整分割
1.首先对问题进行分而治之,将一个大问题转化为规模缩小了的同类子问题,如求f(n)是可以通过f(n-1)来求解
2.递归不能无休止进行,那么必须有明确的退出条件(base case)
3.问题有解时如何进行下一步的决策
4.暴力递归不会记录子问题的解
递归的一个非常重要的点就是:不去管函数的内部细节是如何处理的,我们只看其函数作用以及输入与输出。
114. 二叉树展开为链表
动态规划
自底向上,一般用于求次数或价值,比如斐波拉契和背包问题
dp[0] = ..
dp[1] = ..
...
dp[n] = dp[n - 1] ... 状态转移方程
1.类似于递归的过程,但是会保留每个子问题的解,并且下次直接使用,不用再次计算子问题
2.将暴力递归的每个过程都抽象成为一个状态表达,也就是每个问题的解
3.从小到大,依次求出每个问题的解
算法刻意练习之递归、分治、回溯、动态规划、贪心算法
大体思维导向:
1.先想递归
2.发现重复计算
3.通过记忆化等方法弄掉重复计算,状态转移方程类似于数学归纳法
4.最后看下能不能通过利用计算顺序来做到去掉递归用“刷表”方式直接顺序计算,能搞定最好搞不定拉倒。
回溯递归框架
自底向上,一般用于求各种排列组合
+/- f(1) +/- f(2) +/- f(3) ... +/-f(n) == f(total) ? 做出的选择能否构成完整total
public void backtrack(路径,选择列表){
if(满足结束条件){
result.add(结果);
}
for(选择:选择列表){
做出选择;
backtrack(路径,选择列表);
撤销选择;
}
}
复杂度分析
取最坏情况
常见思路
二叉树: 递归,迭代(借助队列或者栈)
二叉搜索树:
1.中序遍历
2.左子节点依次入栈
链表: 保存中间节点(虚拟节点,前置节点,后置节点),双指针