斐波那契数是一个非常常见的算法题,可以使用暴力递归、记忆化递归、动态规划等解法去得出结果。
1. 题目
斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1 给定 n ,请计算 F(n) 。
示例 1: 输入:n = 2 输出:1 解释:F(2) = F(1) + F(0) = 1 + 0 = 1
示例 2: 输入:n = 3 输出:2 解释:F(3) = F(2) + F(1) = 1 + 1 = 2 来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/fi…
2.题解
2.1 暴力递归法
function fib (n) {
if (n <= 1) return n;
return fib(n - 1) + fib(n - 2);
}
2.2 记忆化递归
在暴力递归法中, fib(5) = fib(3) + fib(4) 、fib(4) = fib(2) + fib(3),fib(3)被计算了两次,假设基数很大的情况下,这个计算量是特别惊人的,所以需要对计算结果进行存储,确保不重复计算。
function fib (n) {
if (n <= 1) return n;
let memo = {};
return helper(memo, n);
}
function helper (memo = {}, n) {
if (n <= 1) return n;
if (memo[n] !== 0) return memo[n];
memo[n] = helper(memo, n - 1) + helper(memo, n - 2);
return memo[n];
}
2.3 动态规划法
对于动态规划的问题,可以拆解城五步曲。
- 确定dp数组和下标的含义
- 确定推导公式
- 初始化dp数组
- 确定递归顺序
- 举例推导dp数组
这道题我们可以按以下拆分
2.3.1. 确定dp数组和下标的含义
第i个数的斐波那契数值是dp[i]
2.3.2. 确定推导公式
题目已给方程,dp[i] = dp[i - 1] + dp[i - 2]
2.3.3. 初始化dp数组
dp[0] = 0; dp[1] = 1;
2.3.4. 确定遍历顺序
因为dp[i] = dp[i - 1] + dp[i - 2],dp[i]都是依赖前面的值,所以遍历顺序是从前往后遍历
2.3.5. 举例推导dp数组
当N=10时,dp数组应该是如下的数列;
0 1 1 2 3 5 8 13 21 34 55
既然推导五步曲已经分析完了,那么我们来编写代码。
function fib (n) {
if (n <= 1) return n;
let dp = new Array(n + 1).fill(0);
dp[1] = 1;
for (let i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
参考 代码随想录programmercarl.com/0509.%E6%96…
2.4 尾调用
尾调用就是一个函数最后一步是调用另一个函数。函数调用的过程会形成一个调用栈,如果函数嵌套很多层,则会导致堆栈溢出,所以ES6在严格模式下会进行尾调用优化,只保留内部函数的调用记录。
const MOD = 1000000007;
var fib = function(n, n1 = 1, n2 = 1) {
if (n === 0) return 0;
if (n <= 2) {
return n2;
}
return fib(n - 1, n2, (n1 + n2) % MOD);
};