程序员必备的基本算法:递归详解( 四 )
- 一个子问题时间 = f(n-1)+f(n-2) , 也就是一个加法的操作 , 所以复杂度是 「O(1)」;
- 问题个数 = 递归树节点的总数 , 递归树的总结点 = 2^n-1 , 所以是复杂度「O(2^n)」 。
回过头来 , 你仔细观察这颗递归树 , 你会发现存在「大量重复计算」 , 比如f(8)被计算了两次 , f(7)被重复计算了3次...所以这个递归算法低效的原因 , 就是存在大量的重复计算!
「那么 , 怎么解决这个问题呢?」
既然存在大量重复计算 , 那么我们可以先把计算好的答案存下来 , 即造一个备忘录 , 等到下次需要的话 , 先去「备忘录」查一下 , 如果有 , 就直接取就好了 , 备忘录没有才再计算 , 那就可以省去重新重复计算的耗时啦!这就是「带备忘录的解法」
我们来看一下「带备忘录的递归解法」吧~
一般使用一个数组或者一个哈希map充当这个「备忘录」 。
假设f(10)求解加上「备忘录」 , 我们再来画一下递归树:
「第一步」 , f(10)= f(9) + f(8) , f(9) 和f(8)都需要计算出来 , 然后再加到备忘录中 , 如下:
文章插图
「第二步 , 」 f(9) = f(8)+ f(7) , f(8)= f(7)+ f(6), 因为 f(8) 已经在备忘录中啦 , 所以可以省掉 , f(7),f(6)都需要计算出来 , 加到备忘录中~
文章插图
「第三步 , 」 f(8) = f(7)+ f(6),发现f(8) , f(7),f(6)全部都在备忘录上了 , 所以都可以剪掉 。
文章插图
所以呢 , 用了备忘录递归算法 , 递归树变成光秃秃的树干咯 , 如下:
文章插图
带「备忘录」的递归算法 , 子问题个数=树节点数=n , 解决一个子问题还是O(1),所以「带「备忘录」的递归算法的时间复杂度是O(n)」 。 接下来呢 , 我们用带「备忘录」的递归算法去撸代码 , 解决这个青蛙跳阶问题的超时问题咯~ , 代码如下:
public class Solution {//使用哈希map , 充当备忘录的作用Map tempMap = new HashMap();public int numWays(int n) {// n = 0 也算1种if (n == 0) {return 1;}if (n <= 2) {return n;}//先判断有没计算过 , 即看看备忘录有没有if (tempMap.containsKey(n)) {//备忘录有 , 即计算过 , 直接返回return tempMap.get(n);} else {// 备忘录没有 , 即没有计算过 , 执行递归计算,并且把结果保存到备忘录map中 , 对1000000007取余(这个是leetcode题目规定的)tempMap.put(n, (numWays(n - 1) + numWays(n - 2)) % 1000000007);return tempMap.get(n);}}}
去leetcode提交一下 , 如图 , 稳了:文章插图
来自:
- 28岁程序员狂赚上亿,宣布退休:有钱一时爽,一直有钱一直爽
- 云主机必备的10个开源安全工具
- To B 产品与 To C 产品的区别
- 用尽全身力气不想加班的机器人,这大概是程序员最后的倔强,哈哈
- Java函数式编码结构-好程序员
- 又是一年1024,程序员:我写的算法不足以控制人类
- 阿里员工哀叹:公务员真好,每一样都完爆程序员,网友:想得真美
- 阿里内部Java应届生就业宝典,打摆子统统必备,内容太全面
- 一个普通本科的安卓程序员如何才能进腾讯,阿里,字节这些大厂?
- Windows必备5款软件,良心好用堪称黑科技,还请低调使用