1. 程式人生 > >劍指offer面試題10:斐波拉契數列

劍指offer面試題10:斐波拉契數列

 題目描述:

大家都知道斐波那契數列,現在要求輸入一個整數n,請你輸出斐波那契數列的第n項(從0開始,第0項為0)。n<=39

思路一:遞迴

(通常來說編譯器對尾遞迴是會有化,所以實際效果還是可以的,不過最好是自己實現優化。不能依賴編譯器)

參考部落格:尾遞迴與編譯器優化 https://www.cnblogs.com/kiwibird/p/4967206.html

long long Fibonacci1(unsigned int n){
    if(n<=0)return 0;
    if(n==1)return 1;
    return Fibonacci1(n-1)+Fibonacci1(n-2);
}

思路二 :模擬遞迴,動態思想自底向上

long long Fibonacci2(unsigned n){//劍指offer
    int result[2]={0,1};
    if(n<2)
        return result[n];
    long long fibNMinusOne=1;
    long long fibNMinusTwo=0;
    long long fibN=0;
    for(unsigned int i=2;i<=n;++i){
        fibN=fibNMinusOne+fibNMinusTwo;
        fibNMinusTwo=fibNMinusOne;
        fibNMinusOne=fibN;
    }
    return fibN;
}

連結:https://www.nowcoder.com/questionTerminal/c6c7742f5ba7442aada113136ddea0c3
來源:牛客網

class Solution {//牛客高贊
public:
    int Fibonacci(int n) {
        int f = 0, g = 1;
        while(n-->0) {//負值也true的
            g += f;
            f = g - f;
        }
        return f;
    }
};

class Solution {//牛客AC
public:
    int Fibonacci(int n) {
        int ans[2]={0,1};
        if(n<2)return ans[n];
        int fib1=0;
        int fib2=1;
        int fibn;
        for(int i=2;i<=n;i++){
            fibn=fib1+fib2;
            fib1=fib2;
            fib2=fibn;
        }
        return fibn;
    }
};

思路三:矩陣快速冪(O(logn))

* O(logN)解法:由f(n) = f(n-1) + f(n-2),可以知道

* [f(n),f(n-1)] = [f(n-1),f(n-2)] * {[1,1],[1,0]}

* 所以最後化簡為:[f(n),f(n-1)] = [1,1] * {[1,1],[1,0]}^(n-2)

* 所以這裡的核心是:

* 1.矩陣的乘法

* 2.矩陣快速冪(因為如果不用快速冪的演算法,時間複雜度也只能達到O(N))

*/連結:https://www.nowcoder.com/questionTerminal/c6c7742f5ba7442aada113136ddea0c3

struct Matrix2By2{
    Matrix2By2(
        long long m00 = 0,
        long long m01 = 0,
        long long m10 = 0,
        long long m11 = 0
    )
    :m_00(m00), m_01(m01), m_10(m10), m_11(m11){}

    long long m_00;
    long long m_01;
    long long m_10;
    long long m_11;
};

Matrix2By2 MatrixMultiply(const Matrix2By2& matrix1,const Matrix2By2& matrix2){
    return Matrix2By2(
        matrix1.m_00 * matrix2.m_00 + matrix1.m_01 * matrix2.m_10,
        matrix1.m_00 * matrix2.m_01 + matrix1.m_01 * matrix2.m_11,
        matrix1.m_10 * matrix2.m_00 + matrix1.m_11 * matrix2.m_10,
        matrix1.m_10 * matrix2.m_01 + matrix1.m_11 * matrix2.m_11);
}

Matrix2By2 MatrixPower(unsigned int n){
//    assert(n > 0);
    Matrix2By2 matrix;
    if(n == 1){
        matrix = Matrix2By2(1, 1, 1, 0);
    }
    else
    if(n % 2 == 0){
        matrix = MatrixPower(n / 2);
        matrix = MatrixMultiply(matrix, matrix);
    }
    else
    if(n % 2 == 1){
        matrix = MatrixPower((n - 1) / 2);
        matrix = MatrixMultiply(matrix, matrix);
        matrix = MatrixMultiply(matrix, Matrix2By2(1, 1, 1, 0));
    }
    return matrix;
}

long long Fibonacci_Solution3(unsigned int n){
    int result[2] = {0, 1};
    if(n < 2)
        return result[n];
    Matrix2By2 PowerNMinus2 = MatrixPower(n - 1);
    return PowerNMinus2.m_00;
}

c++ assert() 使用方法

https://blog.csdn.net/yunzhongguwu005/article/details/9178911


題目二:青蛙跳臺階(就是斐波拉契數列)

這裡考察了建模能力,通過題目可以分析出青蛙的跳到第n級臺階時的跳法是和前面臺階有關的因為青蛙有兩種跳法一步和兩步,因此可以考慮動態規劃的思想。

那麼當現在所在位置的前面臺階數大於2時,到達現在所在的位置有兩種,即從前前面的臺階跳兩步到達和前面的臺階跳一步到達(前面指已經當前位置前已經跳過的臺階),於是可以得到遞推公式,跳到當前臺階的總跳法dp[n]=dp[n-1]+dp[n-2]。(編寫程式時注意遞推邊界n==2)

擴充套件(青蛙可能可以跳一步,兩步,三步...n)具體分析類似。

補充公式(假設青蛙跳的步數可以是1~n種的任意數),那跳到第n臺階的總跳法f(n)=2^(n-1).

劍指offer上說用數學歸納證明,其實想想完全不用,

假設:從第0臺階條跳到第n臺極有兩種大可能,

1.藉助(1~n-1)臺階到達:這裡總共有多少種呢??答案:2^(n-1)-1種,即求集合的子集思想,這裡是求路徑中經過的點,

2.直接從第0臺階跳到第n臺階 :1種

綜上所述總共有2^(n-1)種。

歸納法:https://blog.csdn.net/qq_20304723/article/details/81384862

 

 

//青蛙跳臺階 這裡開闢了個dp陣列儲存自底向上的每一步,當然不儲存也可以 參見前面的思路二

class Solution2{
public:
    int slove(int n){
        vector<int>dp(n+1);
        dp[0]=0,dp[1]=1,dp[2]=2;
        if(n<=2)return dp[n];
        for(int i=3;i<=n;i++){
            dp[i]=dp[i-1]+dp[i-2];
        }
        return dp[n];
    }

};