LeetCode之遞歸——遞歸的記憶化(斐波那契數、爬樓梯)

在遞歸中由於需要調用自身可能會導致一些重複的計算,函數的調用不僅需要時間,而且也會暫用棧上的空間。有時候如果調用的深度太深,還會導致無限循環的問題(實際上是深度太深,要長時間的調用,導致無法在預期的時間內返回)。要解決這個問題,可以使用“記憶化”的方式,將需要的數據緩存,以減少重複的計算。

斐波那契數

鏈接:斐波那契數
描述:

斐波那契數,通常用 F(n) 表示,形成的序列稱爲斐波那契數列。該數列由 0 和 1 開始,後面的每一項數字都是前面兩項數字的和。也就是:F(0) = 0, F(1) = 1。F(N) = F(N - 1) + F(N - 2), 其中 N > 1.給定 N,計算 F(N)。假設0 ≤ N ≤ 30。

一般解法:

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

帶緩存的解法:

由於題目中N的長度是有限的,且每個斐波那契數都只佔用一個INT的長度,因此最多只需要申請一個INT[31]長度的數組即可以緩存所有數據,代碼如下:

class Solution {
public:
    int fib(int N) {
        static int  sCaches[31] = {0};
        if (sCaches[N] != 0 || N == 0) return sCaches[N];
        if(N == 1) {
            sCaches[1] = 1;
            return 1;
        }

        sCaches[N] = fib(N - 1) + fib(N - 2);      
        return sCaches[N];
    }
};

爬樓梯

鏈接:爬樓梯
描述:

假設你正在爬樓梯。需要 n 階你才能到達樓頂。每次你可以爬 1 或 2 個臺階。你有多少種不同的方法可以爬到樓頂呢?注意:給定 n 是一個正整數。

分析:

假設有n(n >= 2)階臺階總共有f(n)總方法,你可以選擇先走一步,也可以選擇先走兩步,則f(n) = f(n - 1) + f(n - 2)。有了這個公式,遞歸的解法很容易給出。

class Solution {
public:
    int climbStairs(int n) {
        if (n <= 2) return n;
        return climbStairs(n - 1) +  climbStairs(n - 2);
    }
};

代碼很簡單,但是如果n比較大的時候,會出現上面提到的執行超時的問題,原因還是遞歸調用時重複執行問題。解決的辦法還是減少不必要的重複調用——利用緩存來解決問題。

class Solution {
public:
    
    int climbStairs(int n) {
        static vector<int> sCache = {1,2};
        if (sCache.size() >= n) return sCache[n - 1];
        sCache.push_back(climbStairs(n - 1) +  climbStairs(n - 2));
        return sCache[sCache.size() - 1];
    }
};

再看一下上面推導出來的公式,實際上和斐波那契數列一樣。斐波那契除了用遞歸實現,更快速的實現是使用循環。

class Solution {
    public int climbStairs(int n) {
        if (n < 3) {
            return n;
        }
        int a = 1, b= 2;
        for(int i = 0; i < (n - 1) / 2; i ++) {
            a = a + b;
            b = b + a;
        }
        return n % 2 == 0 ? b : a;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章