遞歸是個有意思的概念,正如在前面所說,遞歸能讓算法的可讀性大大提升,並且一般要比使用循環結構更能寫出準確的算法。這本書形象引入了遞歸,並無太深刻,因此我進行了一點「添油加醋」。python
遞歸其實就是本身調用本身。能夠從多種維度對遞歸分類,我見過的最多見的分類:算法
本身直接調用本身。如:數據結構
--haskell length' :: [a] -> Int length' [] = 0 length' (_:xs) = 1 + length' xs
上面定義的length'
就是經過直接遞調用自身完成列表長度的計算。app
能夠認爲只要不是直接調用本身的遞歸都是間接遞歸,其表現形式較多,如A->(調用)B,B->(調用)A,如奇偶謂詞函數:函數
--haskell odd' :: Int -> Bool odd' 0 = False odd' n = even' (n - 1) even' :: Int -> Bool even' 0 = True even' n = odd' (n - 1)
也能夠有A->B,B->C,... Z->A。這裏就不舉例子了。性能
有一個盒子,盒子裏套着一個或多個盒子,盒子裏的盒子又有盒子,依次類推。而鑰匙就在某個盒子裏,咱們怎麼找到鑰匙呢。
僞代碼以下:[僞代碼是對手頭問題的簡要描述,看着像代碼,但其實更接近天然語言。]學習
def look_for_key(main_box): pile = main_box.make_a_pile_to_look_through() while pile is not empty: box = pile.grab_a_box() for item in box: if item.is_a_box(): pile.append(item) elif item.is_a_key(): print "found the key!"
若是學習過樹的遍歷,是否是發現這就是廣度優先遍歷啊優化
僞代碼以下:spa
def look_for_key(box): for item in box: if item.is_a_box(): look_for_key(item) elif item.is_a_key(): print "found the key!"
對應的就是深度優先遍歷啊code
這兩種方法的做用相同,但第二種方法更清晰。遞歸只是讓解決方案更清晰,並無性能上的優點。實際上,在有些狀況下,使用循環的性能更好。Leigh Caldwell在Stack Overflow上說的一句話:「若是使用循環,程序的性能可能更高;若是使用遞歸,程序可能更容易理解。如何選擇要看什麼對你來講更重要。」
每一個遞歸函數都有兩部分:基線條件(base case)和遞歸條件(recursive case)。遞歸條件指的是函數調用本身,而基線條件則指的是函數再也不調用本身,從而避免造成無限循環。遞歸條件必定是向基線條件靠攏的,不然,只能無限遞歸下去。如
#python def countdown(i): print i if i <= 0: return else: countdown(i-1)
上述函數中i
的值收斂於0,即達到基線條件,從而不會無限遞歸下去。
主要講了與遞歸相關的一種數據結構--棧(stack)。棧是一種支持FILO(First In Last Out)的數據結構。
爲何要提這種數據結構呢?
這是由於現代計算機對函數的實現用到了調用棧(call stack)的棧。調用函數時,計算機將首先爲該函數調用分配一塊內存。每當你調用函數時,計算機都將函數調用涉及的全部變量的值存儲到內存中。
假設A函數調用B函數,此時計算機爲兩個函數分配了內存,暫且稱之爲A函數內存和B函數內存,它們的位置關係以下:
----棧頂----
B函數內存
—————
A函數內存
—————
若B函數執行完,計算機就能夠回收B函數內存了,即從棧頂彈出B函數內存,此時只有A函數內存了。
----棧頂----
A函數內存
—————
以上操做符合FILO的定義,調用棧是棧的一種具體應用。
那若是調用棧數量太多,會有什麼後果呢?
#python def fact(x): if x == 1: return 1 else: return x * fact(x-1)
對於較小的正整數,這個程序沒有問題;而若是x
較大,在fact
執行的時會發現內存量會飆升,甚至會出現程序沒法正常執行下去。這是由於此時遞歸調用棧的狀況相似:
----棧頂----
fact(1)函數內存
———————
...
———————
fact(n-2)函數內存
———————
fact(n-1)函數內存
———————
fact(n)函數內存
———————
fact(n)
依賴fact(n-1)
,依次類推,致使計算機存儲了大量函數調用的信息。這類問題大致有兩種解決方式:
個人公衆號地址