斐波那契數列的遞歸與尾遞歸

引言

之前在lintcode上刷算法入門題,366題是求斐波那契數列,當時就想用遞歸應該很快就ac了,最後遞歸是沒寫錯,但是提交報時間超限了,也就引出了這篇文章——尾遞歸。

測試

先來看一下,常規的斐波那契遞歸寫法,假設第一項爲0,第二項爲1:

public int fibonacci(int n) {
    if(n == 1) {
	    return 0;
    }
    if(n == 2) {
	    return 1;
    }
    return fibonacci(n - 1) + fibonacci(n - 2);
}

測試第40項的耗時結果爲:

斐波那契的尾遞歸寫法:

public int lastFibonacci(int n, int ret1, int ret2) {
    if(n == 1) {
	    return ret1;
    }
    return lastFibonacci(n - 1, ret2, ret1 + ret2);
}

測試尾遞歸求第40項的耗時結果爲:

可以看到當求到比較後面的項時,尾遞歸還是要快很多的。

分析總結

下面就來分析下尾遞歸到底快在了哪裏

正常遞歸

對於斐波那契數列的正常遞歸,有點類似於二叉樹的結構,我以f(6)爲例,看下圖

可以看到遞歸計算第6項時,需要重複計算兩次f(4),三次f(3),以二叉樹的結構向下延伸,如測試的例子所示,當計算到第40項時,會有36項都需要重複計算,且越往下,重複次數越多,效率也很低下。

尾遞歸

尾遞歸快就快在它不需要重複計算某一項,利用了一種技巧就是每次遞歸調用時,它會把之前已經計算好的結果以參數的形式傳遞過去,同樣以f(6)爲例

n ret1 ret2
6 0 1
5 1 1
4 1 2
3 2 3
2 3 5
1 5 8

ret1就是第n項的值,ret2就是第n+1項的值。

遞歸求斐波那契時,棧內存佔用以指數形式增長,而尾遞歸則是以線性方式增加,且無需重複計算值。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章