8.棧的應用-----遞歸(斐波那契數列)


/*4.8 棧的應用-遞歸*/
/*
    斐波那契數列
說 如果兔子在出生兩個月后,就有繁殖能力,一對兔子每個月能生出一對小兔子來。假設所有兔都不死,那么一年以后可以繁殖多少對兔子呢?
我們拿新出生的一對小兔子分析一下:第一個月小兔子沒有繁殖能力,所以還是一對;兩個月后,生下一對小兔子數共有兩對;三個月以后,老兔子又生下
一對,因為小兔子沒有繁殖能力,所以一共是三對......依此類推四個月以后是5對...五個月之后是8對
兔子的對數 按月排依次是1,1,2,3,5,8,13.....構成一個序列,有十分明顯的特點

*/

 

 

 我們用數學函數定義就是:

$F(n) =\left\{ \begin{array}{ll} 0, & \textrm{當n=0}\\ 1, & \textrm{當n=1}\\ F(n-1)+F(n-2),  & \textrm{當n>1} \end{array} \right.$

*/
//假設我們需要打印出前40位斐波那契數列數。代碼如下:
int main()
{
    int i;
    int a[40];
    a[0] = 0;
    a[1] = 1;
    printf("%d", a[0]);
    printf("%d", a[1]);
    for (i=2; i<40; i++)
    {
        a[i] = a[i-1] + a[i-2];
        printf("%d", a[i]);
    }
    return 0;
}

//斐波那契數列的遞歸函數
int Fbi(int i)
{
    if(i < 2)
        return i == 0 ? 0 : 1;
    //這里Fbi就是函數自己,它在調用自己
    return Fbi(i-1) +Fbi(i-2);
}
int main()
{
    int i;
    for (i=0; i < 40; i++)
        printf("%d", Fbi(i));
    return 0;
}

/*
遞歸的定義
在高級語言中,調用自己和其他函數並沒有本質的不同。我們把一個直接調用自己或通過一系列的調用自己的函數,稱做遞歸函數。
當然,寫遞歸程序最怕的就是陷入永不結束的無窮遞歸中,所以,每個遞歸定義必須至少有一個條件,滿足時遞歸不再進行,即不再
引用自身而是返回值退出。
對比了兩種實現斐波那契的代碼。迭代和遞歸的區別是:迭代使用的是循環結構,遞歸使用的是選擇結構。遞歸會使程序的結構更清晰
更簡潔、更容易讓人理解,從而減少讀懂代碼的時間。但是大量的遞歸調用會建立函數的副本,會耗費大量的時間和內存。迭代則不需要
反復調用函數和占用額外的內存。因此我們應該視不同情況選擇不同的代碼實現方式。

說了這么多遞歸和棧有什么關系呢?
前面我們已經看到遞歸是如何執行它的前行和退回階段的。遞歸過程退回的順序是他前行順序的逆序。在退回過程中,可能要執行
某些動作,包括恢復在前行過程中存儲起來的某些數據。
這種存儲某些數據,並在后面又以存儲的逆序恢復這些數據,以提供之后使用的需求,顯然很符合棧這樣的數據結構,因此,編譯器
使用棧實現遞歸就沒什么好驚訝的了。
簡單來說,就是在前行階段,對於每一層遞歸,函數的局部變量、參數值以及返回地址都被壓入棧中。在退回階段,位於棧頂的局部變量、
參數值和返回地址被彈出,用於返回調用層次中執行代碼的其余部分,也就是恢復調用的狀態。
當然,對於現在的高級語言,這樣的遞歸問題是不需要用戶來管理這個棧的,一切由系統代勞了。
*/

 


免責聲明!

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



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