1. 程式人生 > 其它 >【LeetCode】509.斐波那契數

【LeetCode】509.斐波那契數

斐波那契數,通常用\(F(n)\) 表示,形成的序列稱為 斐波那契數列 。該數列由0 和 1 開始,後面的每一項數字都是前面兩項數字的和。也就是:

\(F(0) = 0,F(1)= 1\)
\(F(n) = F(n - 1) + F(n - 2)\),其中 \(n > 1\)
給你 \(n\) ,請計算 \(F(n)\)

示例 :
輸入:2
輸出:1
解釋:F(2) = F(1) + F(0) = 1 + 0 = 1

演算法1(暴力遞迴)

時間複雜度:\(O(2^n)\)
遞迴演算法的時間複雜度: 子問題個數 * 解決一個子問題需要的時間。
本題中子問題個數即為遞迴樹的節點個數,\(O(2^n)\)

,解決一個子問題的時間是\(O(1)\),所以總的時間複雜度為\(O(2^n)\),指數級別。

class Solution {
public:
    int fib(int n) {
        if (n == 0) return 0;
        if (n == 1 || n == 2) return 1;
        return fib(n - 1) + fib(n - 2);
    }
};

演算法2(帶備忘錄的遞迴)

時間複雜度:\(O(n)\)
因為該演算法沒有冗餘的計算過程,所以子問題個數為n(每個數計算一次),即\(O(n)\),解決每個子問題需要的時間為\(O(1)\)

,所以演算法時間複雜度為\(O(n)\),線性!
由於演算法1中進行了多次的重複計算,例如在計算f(20)時需要計算f(19)f(18),在計算f(19)時需要計算f(18)f(17)f(18)被計算了兩次,為了避免這些重複計算,我們可以使用一個備忘錄(一般是陣列)記錄每次計算的結果,之後需要用到的時候直接從備忘錄中查詢。實際上,帶備忘錄的遞迴演算法,把一棵存在巨量冗餘的遞迴樹通過剪枝,改造成了一幅不存在冗餘的遞迴圖,極大減少了子問題(即遞迴圖中節點)的個數。

class Solution {
public:
    int fib(int n) {
        int memo[n + 1];
        for (int i = 0; i < n + 1; i ++ ) memo[i] = 0;
        return helper(memo, n);
    }

    int helper(int memo[], int n) {
        if (n == 0) return 0;
        if (n == 1) return 1;
        if (memo[n] != 0) return memo[n];
        memo[n] = helper(memo, n - 1) + helper(memo, n - 2);
        return memo[n];
    }
};

演算法3(DP陣列的迭代)

時間複雜度:\(O(n)\)
演算法2帶備忘錄的遞迴求解方法是自頂向下逐層分解,分解到f(1)f(2)這兩個base case就開始逐層返回答案。我們也可以通過自底向上逐層推出答案。

class Solution {
public:
    int fib(int n) {
        if (n == 0) return 0;
        int dp[n + 1];
        dp[0] = 0;
        dp[1] = 1;
        for (int i = 2; i <= n; i ++ ) {
            dp[i] = dp[i - 1] + dp[i - 2];
        }
        return dp[n];
    }
};

演算法4(DP陣列迭代優化)

時間複雜度:\(O(n)\)
由於每一次計算時只需用到前兩次的結果,所以我們不需要儲存長度為n的dp表,只需要儲存前兩次的計算結果。

class Solution {
public:
    int fib(int n) {
        if (n == 0) return 0;
        if (n == 2 || n == 1) return 1;
        int pre = 0, cur = 1;
        int res = 0;
        for (int i = 2; i <= n; i ++ ) {
            res = pre + cur;
            pre = cur, cur = res;
        }
        return res;
    }
};