尾遞歸和JAVA


簡單來說,遞歸即是調用自己本身。所有遞歸都應該有至少一個基本條件,在滿足基本條件時不進行遞歸。

給出一個遞歸實例:

1 int fact(int N){
2     if(N==1)
3         return 1;
4     else
5         return N*fact(N-1);
6 }        

每一個遞歸方法的執行都分為前進和回退兩個階段,上例中計算5的階乘,前進階段得到的結果是:

(5*(4*(3*(2*(1)))))

回退階段則由內向外,依次計算括號中的值。

應用到程序中,分別對應壓棧和出棧。考慮到這種做法,每次調用都會壓棧出棧,效率很低。除此之外,每次遞歸創建新的棧在遞歸深度過深的時候,會引起棧溢出,也就是可以分給創建棧的內存不足的情況。尾遞歸的方法由此提出。

尾遞歸,簡單來說,就是將遞歸語句寫到最后一行且不參與任何計算。本質上,每次遞歸將接受上一次遞歸的傳參,並將本次計算結果傳給下一次遞歸,當遞歸到達終結條件的時候,其計算值即為返回值。這樣相比普通遞歸,省去了遞歸的回退過程,也即不用再回退到上一次遞歸作運算,自然就省去了很多創建棧、壓棧與出棧的工作,性能得到提升。將上例改寫為尾遞歸為:

 1 int fact(int N){
 2     if(N==1)
 3         return 1;
 4     else
 5         return fact(N-1,N);
 6 }       
 7 
 8 int fact(int N, int M){
 9     if(N==1)
10         return M;
11     else
12         return fact(N-1, N*M);
13 }    

這是有外及里的尾遞歸,當計算斐波那契數列的時候就要換一種思路,即由里及外。

斐波那契問題描述:每個兔子出生后的第三個月后,每個月都可以生下一對兔子,如果不考慮兔子死亡問題,第一個月有新出生的一對兔子,那第N個月有多少兔子?

其尾遞歸代碼如下:

1 int Fibonacci(int N, int first, int second, int begin){
2     if(begin>=N)
5          return first;
6     else
7          return Fibonacci(N, second, first+second, begin+1);        
8 }

但這要求編譯器能對尾遞歸進行優化,每次重用或者說覆蓋原來遞歸方法的棧,而不是新建棧。遺憾的是JAVA和python都不支持尾遞歸的優化,JAVA的尾遞歸代碼與普通遞歸無異。可能JVM是想在出現異常時更好地輸出堆棧信息的緣故。所以,JAVA中一般能用迭代就不用遞歸。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM