關於迴圈,遞迴和棧的一些思考
有一類問題,可以歸結為如下形式:
已知兩個條件:1,f(n) 和 f(n-1) 的遞推關係式; 2,某個f(x) 的具體值。求解:f(n)。
跟高中的某一類數列問題一模一樣...
這個問題有兩條思路:
a,正向推導:根據 f(x) 可以求解 f(x+1)的值,再求f(x+2),...,求得f(n)。
b,反向推導:根據 f(n) 和 f(n-1) 的關係,以及 f(n-1) 和 f(n-1) 的關係,可以推出 f(n) 和 f(n-2) 的關係,接著推匯出 f(n) 和 f(n-3) 的關係,...,直至求出 f(n) 和 f(x) 的關係,最後將 f(x) 的值代入得到 f(n) 的值。
思路a有明確的初值,有明確的迭代次數,每一次迭代求得的都是具體的值,易於用迴圈實現。
思路b不一樣,它每一步求得的是一個關係式,中間結果含有符號表達式,推到最後一步還要回代才能得到數值結果。遞迴正是這條思路的具體實現。
我們往往覺得比起迴圈,遞迴更難以理解,是因為在迴圈中只要把上一次迴圈的結果拿到放到這一次迴圈,不需要管早期的資料;而在遞迴中,每一次遞推,要用到前面出現過的所有資料。資料量大了,對人腦就是一項很重的負擔,演算也比較吃力。但計算機是絕妙的工具,在遞迴程式中,它會自動進行演算、儲存資料,只要我們確保初值和遞推關係是正確的,並且記憶體和運算能力足夠,就能得到最終結果。
在大多數情況下,迴圈和遞迴異曲同工,都能解決問題,也能相互轉化。但是在某些情況下,考慮到演算法的時間、空間複雜度,我們往往會選擇迴圈;另一些情況下,考慮到可讀性、實現的難易程度,我們會選擇遞迴。
我個人認為所有的遞迴實現都可以改為迴圈。我的理由如下:
在計算機底層,函式呼叫是通過“保護現場->提升堆疊->跳轉到函式對應的指令地址->執行指令->恢復堆疊->恢復現場”來實現的。對於同一個函式,整個過程都是相同的!不同的只是某些實參的值。這樣的話,我們可以利用棧和迴圈,模仿系統呼叫函式的過程,把遞迴函式改寫為迴圈。
有空的時候我改寫幾個試試!