leetcode.剑指 Offer 10- I. 斐波那契数列

125 阅读2分钟

剑指 Offer 10- I. 斐波那契数列(leetcode)

前言

斐波那契数列

斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardo Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:

0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,....

这个数列从第3项开始,每一项都等于前两项之和。

  1. F(0) = 0
  2. F(1) = 1
  3. F(n) = F(n - 1) + F(n - 2),其中n > 1

因为在计算的过程中 有很多重复计算的过程,所以可以进行缓存处理

动态规划

动态规划(Dynamic Programming,DP)是运筹学的一个分支,是求解决策过程最优化的过程。

可能有很多解,每个解对应一个值, 我们希望找到最优解。基本思想就是待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解

将一个大的问题拆分成一个个子问题,我们把它称之为子结构。

每个最优解,也就是最优值均由[这些小规模子问题]推到而来。

更重要的就是利用历史记录,来避免我们重复的计算。

  1. 定义状态 dp数组 ,明确dp[i]代表的含义
  2. 列出状态转移方程 dp元素之间的关系
  3. 初始化状态 确定dp元素中的最开始元素值

剑指offer 10-I. 斐波那契数列

解法1: 递归调用

function fib(n: number): number {
    const _fib=(n)=>{
        if(n<=1){
            return n 
        }
        if(n>1){
            return (_fib(n-1)+_fib(n-2))% 1000000007
        }

    }
   return  _fib(n)% 1000000007
};

为什么答案需要取模呢

该解法是最直接和暴力的想法 , 但是有一个问题,这里循环调用很多次容易引起栈溢出,优化方法采用动态规划和尾递归

优化后的递归实现

function fib(n: number): number {
    if(n<=1){
        return n 
    }
    let res1 = 0,res2 = 1;  
    for(let i=2;i<=n;i++){
        [res1,res2] = [res2% 1000000007,(res2+res1)% 1000000007]
    }
    return res2
};

解法2: 动态规划

function fib(n: number): number {
    let dp:Array<number> = new Array(n+1);
    if(n<1){
        return n 
    }
    dp[0]=0,dp[1] =1;
    for(let i=2;i<=n;i++){
        dp[i] = (dp[i-1]+dp[i-2])% 1000000007
    }
    return dp[n]
};

注意: 需要当心 第一个值多少,第二个值是多少,要根据题目确定