LeetCode509(力扣509) :斐波那契數列 C++ 多種思路與詳細解析

菜菜女孩要加油哦發表於2020-12-08

509 斐波那契數列

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

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

給定 N,計算 F(N)。

示例 1:

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

示例 2:

輸入:3
輸出:2
解釋:F(3) = F(2) + F(1) = 1 + 1 = 2.

示例 3:

輸入:4
輸出:3
解釋:F(4) = F(3) + F(2) = 2 + 1 = 3.

提示:

0 ≤ N ≤ 30

我的提交程式碼:

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

在這裡插入圖片描述

我的思想

沒有思想這個題,學習遞迴的必會程式碼啊!都不需要寫測試程式碼,直接提交就好啦~提交是能通過,但是同時拿去提交別的斐波那契題的時候就不行拉,會超時。因為這是遞迴,會重複計算很多沒有用的數,因此接下來要去學習優化的程式碼。

畫出遞迴樹:

​ 可以看出會重複一些fib(n)的值,使得演算法的效率變低

在這裡插入圖片描述
子問題的個數為: 遞迴樹中節點的總數,由樹的特徵可以得到遞迴計算斐波那契數列的時間複雜度為O(2^n)爆炸拉成指數增長。

官方題解二:記憶化自底向上的方法

自底向上通過迭代計算斐波那契數的子問題並儲存已計算的值,通過已計算的值進行計算。減少遞迴帶來的重複計算。

演算法:

  • 如果 N 小於等於 1,則返回 N。
  • 迭代 N,將計算出的答案儲存在陣列中。
  • 使用陣列前面的兩個斐波那契數計算當前的斐波那契數。
  • 計算到 N,則返回它的斐波那契數。
class Solution {
public:
    int fib(int N) {
        if (N <= 1) {
            return N;
        }
        return memoize(N);
    }

public:
    int memoize(int N) {
        vector<int> temp(N+1);
        temp[0] = 0;
        temp[1] = 1;
        for (int i = 2; i <= N; i++) {
            temp[i] = temp[i - 1] + temp[i - 2];
        }
        return temp[N];
    }
};

在這裡插入圖片描述
由此兩種方法的執行用時可以明顯看到演算法效率的優化。此種演算法以空間換了時間,新開闢了一個N+1的陣列來儲存在每一個fib(n)時候的值。此時的空間複雜度為O(n+1)

此處又可以對空間進行優化了,那麼我們每一次計算其實只需要n前面的兩個數而已,反正也是從底向頂,最後要計算n的斐波那契數為多少,因為底部算過的數對我們之後並沒有用,我們可以建立3個變數然後不斷覆蓋就能節省空間了。

class Solution {
public:
    int fib(int N) {
        if (N <= 1) {
            return N;
        }
        return memoize(N);
    }

public:
    int memoize(int N) {
        int sum = 0;
        int first = 0;
        int second = 1;
        for (int i = 2; i <= N; i++) {
            sum = first + second;
            first = second;
            second = sum;
            
        }
        return sum;
    }
};

在這裡插入圖片描述
可以顯然的看到,記憶體消耗有明顯的減低。此時的空間複雜度為O(1)

我學會拉

  1. 能不用遞迴,別用遞迴,複雜度爆炸!
  2. 分析子問題,可以用迴圈迭代來解決問題。
  3. 在解出一個問題後,要多思考如何操作能使得程式碼效率更高。例如此題,用過一次就不用的記憶體空間,我們可以考慮少申請,不斷覆蓋。

相關文章