聊聊斐波那契数列中递归的重复计算
聊聊斐波那契数列中递归的重复计算
所谓的斐波纳契数列是指:
前2个数是 0 和 1 。
第 i 个数是第 i-1 个数和第i-2 个数的和。
斐波纳契数列的前10个数字是:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34 ...
1 递归实现
根据定义,很快就能写出递归实现:
public int fibonacci(int n) { if (n < 2) {return 0;} if (2 == n) {return 1;} return fibonacci(n - 1) + fibonacci(n - 2); }
但是,这个函数去执行的时候,会发现非常的慢。那是什么原因呢?
我们仔细观察一下,每一次递归的时候,我们都会调用两个,所以,计算时,从n、n-1、… 0会是一个金字塔,二叉树的分布,也就是靠近0的底层,会有2^n次运算,就是一个指数级运算,计算时间复杂度为O(2^n),运算速度是非常慢的。
原因是什么呢?假如,我们计算的是10,那么它相应的要计算 9 + 8,而9要计算 8 + 7, 8又要计算 7 + 6.相当于越往底层去,计算的次数越多。
10 / \ 9 8 / \ / \ 8 7 7 6 ...
2 递归改进
那么避免重复计算的方法就是,每计算得到一个值,便用一个数组保存这个值,下次计算该值的时候直接引用就可以了。
public int fibonacci(int n) { int[] array = new int[n + 1]; if (n < 2) {return 0;} if (2 == n) {return 1;} array[1] = 0; array[2] = 1; return helper(array, n); } private int helper(int[] array, int n) { if (n < 2) { return 0; } if (2 == n) { return 1; } array[n] = helper(array, n - 1) + array[n - 2]; // 保存每次计算的结果 return array[n]; }
但这种方法不是尾递归的,所以,调用栈内存会比较多。
3 迭代法
用迭代法来计算是最简单,速度也最快的。
public int fibonacci(int n) { int[] array = new int[n + 1]; if (n < 2) {return 0;} if (2 == n) {return 1;} array[1] = 0; array[2] = 1; for (int i = 3; i <= n; i++) { array[i] = array[i - 1] + array[i - 2]; } return array[n]; }