JS-斐波那契数

107 阅读2分钟

斐波那契数是一个非常常见的算法题,可以使用暴力递归、记忆化递归、动态规划等解法去得出结果。

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 动态规划法

对于动态规划的问题,可以拆解城五步曲。

  1. 确定dp数组和下标的含义
  2. 确定推导公式
  3. 初始化dp数组
  4. 确定递归顺序
  5. 举例推导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); 
};

力扣链接:leetcode.cn/problems/fe…