斐波那契数,通常用 F(n) 表示,形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
给你 n ,请计算 F(n) 。
示例 1:
输入:2
输出:1
解释:F(2) = F(1) + F(0) = 1 + 0 = 1
示例 2:
输入:3
输出:2
解释:F(3) = F(2) + F(1) = 1 + 1 = 2
示例 3:
0 <= n <= 30来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/fibonacci-number
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解法一:直接“傻递归”
为什么叫傻递归,最大原因是存在大量大量的重复计算。详细请自己画画递归树,自己推一推。
自顶向上,存在大量重复的计算
分析:
1. 时间复杂度: O(2^n)
/**
* @param {number} n
* @return {number}
*/
var fib = function(n) {
if (n === 0 || n === 1) return n;
if (n === 2) return 1;
return fib(n-1) + fib(n-2);
};
解法二:聪明递归
针对以上的傻递归,设置一个数组memo或者哈希表来存储中间结果,计算的时候看是否在memo中含有结果,如果有的话就直接返回
事件复杂度: 0(n)
空降复杂度:0(n) 空间换时间的一个案例
/**
* @param {number} n
* @return {number}
*/
var fib = function(n) {
const memo = [];
return helper(memo,n);
};
function helper(memo,n) {
if (n === 1 || n === 0) return n;
if (n === 2) return 1;
// 先查表
if (memo[n]) return memo[n];
memo[n] = helper(memo,n-1) + helper(memo,n-2);
return memo[n];
}
方法三:使用动态规划
f(0) = 0 f(1) = 1 f(2) = 1 f(n) = f(n-1) + f(n-1);
时间复杂度: 0(n)
空间复杂度: 0(n)
/**
* @param {number} n
* @return {number}
*/
var fib = function(n) {
const memo = [];
memo[0] = 0;
memo[1] = 1;
memo[2] = 1;
for(let i = 3; i <= n; ++i) {
memo[i] = memo[i-1] + memo[i-2];
}
return memo[n];
};
方法四:压缩状态
由状态方程可以知道,结果只有两个状态有关,即 n-1和n-2
时间复杂度: 0(n)
空间复杂度:0(n)
/**
* @param {number} n
* @return {number}
*/
var fib = function(n) {
let first = 0,second = 1,result;
if (n === 0 || n === 1) return n;
for(let i = 2; i <= n; ++i) {
result = first + second;
first = second;
second = result;
}
return result;
};
总结
斐波那契问题是动态规划的一个很好的切入点,但是它还不是动态规划的问题,因为它没有求最值的要求。
但是我们可以学习的是,第一找到状态转义方程,然后进行傻递归,然后设置备忘录去进行一个优化递归,然后进行一个动态规划的思路,最后去进行动态规划的优化。方法一到方法四十完全是沿着这条路一直走下来的。一步一步实现降为打击。官方题解中还有两种较为高级的解法,对数学要求相当的高,可以去看看!
参考
-
《labuladong的算法小抄》
-
leetcode官方题解