斐波那契数列不超时的写法(备忘录写法)
原理,在原先的写法中是直接return f(n-1)+f(n-2)然后一直形成递归的。
但是如果我们用一个数组,来把递归算出来的数存放进来,并且在递归的时候进行判断,
如果这个数组里面有这个数,就将他拿出来直接使用,节省了再一次递归所用的时间。
可能我说的不是很好理解看图:
首先假如n = 10 ,为了方便我不会画成f(10)=f(9)+f(8),而是直接 画成 10= 9+8
可以很明显的看出,如果递归正常操作的话,画成树状图,会有无数的支点,绿色部分即是那无数的支点,不过我将他省略。
因为我们的目的就是算出红色部分的支点,将绿色部分给减掉。并将红色部分的支点存放起来。
因为递归是按顺序递归的,比如10=9+8。这个时候就会调用9 ,然后9= 8+7,以此类推下去,最后到了2=1+0;数据值用一个数组存放。得出数组[2]=数据值。然后我们就知道了f(10)-f(0) 的数据值,并将他们全部存放进数组。这样子一次递归的时候直接使用。
为什么说省时间呢?因为上面的那一次递归只是10=9+8.其中的9引起的递归。98765432。这些只是他的一个支点,他还有另一个支点。
而算完他那部分递归下去的所有支点,别忘了10=9+8;的8还没有递归。上面的8是9下面的支点。这个8是10下面的支点。所以我们要递归无数次才能得出最后的f(10)=多少(数据值)
package 数据结构.递归;
public class 斐波那契备忘录递归 {
public static void main(String[] args) {
Solution03 s = new Solution03();
System.out.println(s.fib(10));
}
}
class Solution03 {
int fib(int N) {
// 备忘录全初始化为 0
int[] memo = new int[N + 1];
// 进行带备忘录的递归
return helper(memo, N);
}
int helper(int[] memo, int n) {
// base case
if (n == 0 || n == 1) return n;
// 已经将已知的f(n)存放进去这个数组了。不了解可以看下上面的解析。我说的不好。比较推荐你们debug一下
if (memo[n] != 0) return memo[n];
memo[n] = helper(memo, n - 1) + helper(memo, n - 2); //这个就是将数据值存放进数组。因为没有直接return数据值,而是将他存放进数组。这样我们只需要存放他需要递归的无数个节点的10个或者说n个。等需要用的时候直接代入即可。
return memo[n];
}
}