尾遞迴與斐波那契三種解法
阿新 • • 發佈:2019-01-27
常規的斐波那契數列解法
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).