1. 程式人生 > >尾遞迴與斐波那契三種解法

尾遞迴與斐波那契三種解法

常規的斐波那契數列解法

int fib(int n) {
    if (n <= 2) {
        return 1;
    }
    else
        return fib(n - 1) + fib(n - 2);
}

要求第n個斐波那契數,子問題就是求每一個斐波那契數的前一項和前二項之和,典型的遞迴思想。

這個演算法的時間複雜度怎麼算呢?

遞迴的時間複雜度:遞迴次數*一次遞迴的基本語句執行次數

遞迴的空間複雜度:在棧空間最大的臨時變數個數

如果求f(5),畫出呼叫過程就是

              f(5)
       f(4
)
f(3) f(3) f(2) f(2) f(1) f(2) f(1)

一個樹狀結構,先從f5->f4->f3-f2->f1->f2左子樹就遞迴完了。每一個結點都是一次遞迴,每一個遞迴裡只有一個基本語句,而結點書大約等與2^(N-2)常數忽略也就是2^N次遞迴呼叫,所以時間複雜度O(2^n).

空間複雜度由圖可以看出f5->f2之後遞迴f1,之後歸值給f3,這時f2和f1的空間已經回收了,所以最大的空間佔用也只是f5->f1, 即O(n)。

迴圈優化斐波那契

int fib(int n) {
    int
pre = 1; int ppre = 0; int ret = 0; for (int i = 1; i <= n; i++) { ret = pre + ppre; ppre = pre; pre = ret; } return ret; }

迴圈迭代的精髓是每次計算的結果參與下次的計算,這種解法也是最優的,時間O(n),空間O(1).

尾遞迴優化斐波那契

int fib(int n, int ppre, int pre) {
    if (n <= 1)
        return
ppre; return fib(n - 1, pre, ppre + pre); } int main() { int ret = fib(5, 1, 1); }

遞迴呼叫返回的結果總被直接返回,則稱為尾部遞迴。尾部遞迴的函式有助將演算法轉化成函式程式語言,而且從編譯器角度來說,亦容易優化成為普通迴圈。這是因為從電腦的基本面來說,所有的迴圈都是利用重複移跳到程式碼的開頭來實現的。

尾遞迴的精髓就是吸取迴圈的精華——把每次計算的結果當作引數傳遞給下一次計算。

呼叫過程如圖,時間複雜度就是O(n),如果編譯器優化空間複雜度O(1).
這裡寫圖片描述