/*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; } /* 遞歸的定義 在高級語言中,調用自己和其他函數並沒有本質的不同。我們把一個直接調用自己或通過一系列的調用自己的函數,稱做遞歸函數。 當然,寫遞歸程序最怕的就是陷入永不結束的無窮遞歸中,所以,每個遞歸定義必須至少有一個條件,滿足時遞歸不再進行,即不再 引用自身而是返回值退出。 對比了兩種實現斐波那契的代碼。迭代和遞歸的區別是:迭代使用的是循環結構,遞歸使用的是選擇結構。遞歸會使程序的結構更清晰 更簡潔、更容易讓人理解,從而減少讀懂代碼的時間。但是大量的遞歸調用會建立函數的副本,會耗費大量的時間和內存。迭代則不需要 反復調用函數和占用額外的內存。因此我們應該視不同情況選擇不同的代碼實現方式。 說了這么多遞歸和棧有什么關系呢? 前面我們已經看到遞歸是如何執行它的前行和退回階段的。遞歸過程退回的順序是他前行順序的逆序。在退回過程中,可能要執行 某些動作,包括恢復在前行過程中存儲起來的某些數據。 這種存儲某些數據,並在后面又以存儲的逆序恢復這些數據,以提供之后使用的需求,顯然很符合棧這樣的數據結構,因此,編譯器 使用棧實現遞歸就沒什么好驚訝的了。 簡單來說,就是在前行階段,對於每一層遞歸,函數的局部變量、參數值以及返回地址都被壓入棧中。在退回階段,位於棧頂的局部變量、 參數值和返回地址被彈出,用於返回調用層次中執行代碼的其余部分,也就是恢復調用的狀態。 當然,對於現在的高級語言,這樣的遞歸問題是不需要用戶來管理這個棧的,一切由系統代勞了。 */