2-LeetCode剑指 Offer 10- I. 斐波那契数列详解

1,001 阅读1分钟

一、题目

写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:

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

斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1

样例1:

输入: n = 2
输出: 1

样例2:

输入: n = 5
输出: 5

提示:

  • 0 <= n <= 100

二、题解

斐波那契数列算是比较经典的问题了,要做出来其实很简单,但是我们这里多思考一下,讨论一下现在常用的几个方法。

1.递归法:就是设置几个出口值,然后一直递归。(时间长,容易超出时间限制)

2.多一个记忆数组递归法:在递归的基础上,设置一个记忆数组,用来存储已经算出来的值,在递归时,如果遇到已有的值直接在记忆数组里面取(相比于方法一时间更快一些,可以省去一些重复计算的浪费)。

3.动态规划思想(讨论几种不同的逻辑写法): 1.用三个变量a,b,sum不断向前计算,循环n之后得到最后的值.2.用三个变量a,b,sum不断向前计算,循环n-1之后得到最后的值 3.全部放到dp数组中计算(这个最省时间)。

4.最后一定别忘记要%1000000007,细心!

三、代码(js)

//1.直接递归做  一般会超出时间限制

/**

 * @param {numbern

 * @return {number}

 */

 let fib = function(n) {

    if(n==0){

        return 0;

    }

    if(n==1){

        return 1;

    }

    return (fib(n-1)+fib(n-2))%1000000007;

};




// 2.用一个数组存储f(0)到f(n)的值 然后遇到已有的值直接在数组里面取

// 这种不会超出时间限制 但消耗的内存和用时很多

/**

 * @param {numbern

 * @return {number}

 */

//用new Array(1000).fill(0)创建一个1000大小的零数组

 let a=new Array(1000).fill(0);

 a[1]=1;

 let fib = function(n) {

    if(n==0){

        return 0;

    }

    //如果a[n]存在 就直接返回a[n]

    if(a[n]!=0return a[n];

    //没存在就递归计算

    a[n] = (fib(n-1)+fib(n-2))%1000000007;

    return a[n];

};




// 3.动态规划1 

/**

 * @param {numbern

 * @return {number}

 */

 let fib = function(n) {

    let  a=0,b=1,sum;

    if(n==0)

      return a;

    if(n==1)

      return b;

    for(let i = 0 ; i<n-1 ; i++ )

    {

        sum=(a+b)%1000000007;

        a=b;

        b=sum

    }   

    return sum;

};




// // 4.动态规划2 

/**

 * @param {numbern

 * @return {number}

 */

 let fib = function(n) {

    let  a=0,b=1,sum;

    for(let i = 0 ; i<n ; i++ )

    {

        sum=(a+b)%1000000007;

        a=b;

        b=sum

    }   

    return a;

};




// 4.动态规划3

/**

 * @param {numbern

 * @return {number}

 */

 let fib = function(n) {

    //用new Array()来生成指定大小的数组

    let dp= new Array(n+1);

    dp[0]=0;

    dp[1]=1;

    for(let i = 2 ; i<=n ; i++ )

    {

        dp[i]=dp[i-1]+dp[i-2];

        dp[i]%=1000000007;

    }   

    return dp[n];

};

四、拓展思考

这里我们就来讨论下我给出的动态规划思想解题的1,2,3代码逻辑的的区别:

动态规划1 : 动态规划1的代码i循环n-1次,因为其实算fib(n)只需要n-1次计算,且n-1次计算之后答案就是sum(所以return sum),但是要注意的是,要讨论n==0和n==1的特殊情况,这就是i<n-1(循环n-1次)所带来的其他麻烦。

动态规划2 : 动态规划2的代码i循环n次,可以发现n次计算之后答案赋给了a(所以return a),相比于动态规划1更方便的是这里不要讨论n的值,缺点是多循环了一次。

动态规划3 : 动态规划3的代码其实才可以算作是动态规划,直接new一个大小为n+1(因为答案就为dp[n],所以数组大小为n+1)的dp数组,且dp[0]=0,dp[1]=1,然后i循环n次,直接通过dp[i]=dp[i-1]+dp[i-2]得到最后的答案dp[n]。(而且也不用考虑初值,因为dp[0]和dp[1]以及被提前赋值好了!)

最后一定记得要%1000000007,细节决定成败!

题目来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/fe…