遞迴
在我看來,遞迴就類似俄羅斯套娃,一次次重複去執行相同的操作,最後得出結果的過程。
很多遞迴的操作都可以使用迴圈來替代。遞迴併沒有帶來演算法效能上的優勢,甚至更差,但是使用遞迴可能讓你的程式更易理解。所以理解遞迴這種概念很重要。
基線條件和遞迴條件
由於遞迴函式呼叫自己,很容易導致無線迴圈。比如,你需要一個倒數的函式:
def countdown(i):
print(i)
countdown(i - 1)
countdown(3)
複製程式碼
如果執行上述程式碼,這個函式就會不停的執行。
編寫遞迴函式式,必須告訴它何時停止遞迴,
每個遞迴函式都有兩部分:基線條件(base case)和遞迴條件(recursive case)。遞迴條件指的是函式呼叫自己,而基線條件則 指的是函式不再呼叫自己,從而避免形成無限迴圈。
好了,我們嘗試為上面的程式碼增加基線條件:
def countdown(i):
print(i)
if i <= 0: # 基線條件
return
else: # 遞迴條件
countdown(i - 1)
countdown(3)
# 3 2 1 0 程式停止
複製程式碼
棧
呼叫棧不僅對於程式設計來說很重要,使用遞迴是也必須裂解這個概念。計算機在內部使用被稱為呼叫棧的棧。
我們簡單看一個例子:
# 忽略 print 這個函式帶來的影響
# 開始執行函式 greet ,此時記憶體中開闢一塊空間 greet 進入棧中
def greet(name):
# 在 greet 的記憶體中,新增變數name
print "hello, " + name + "!"
# 開始執行 greet2 ,此時記憶體中開闢空間 greet2,greet2 入棧
# greet <-- greet2 類似這種鏈式結構
greet2(name)
# greet2 執行完畢,出棧
# greet
print "getting ready to say bye..."
# 開始執行bye ,此時記憶體中開闢空間 bye,bye 入棧
# greet <-- bye
bye()
# bye 執行完畢,出棧
# greet
# 最後greet執行完畢,出棧
複製程式碼
棧的執行邏輯就是:先進後出原則。
遞迴呼叫棧
遞迴呼叫中,儲存詳盡的資訊可能佔用大量的記憶體。這回付出很大的代價。如果佔用過高,你需要使用迴圈去代替遞迴,或者使用尾遞迴優化,前提是你的語言支援尾遞迴優化。
小結
- 遞迴指的是呼叫自己的函式。
- 每個遞迴函式都有兩個條件:基線條件和遞迴條件。
- 棧有兩種操作:壓入和彈出。
- 所有函式呼叫都進入呼叫棧。
- 呼叫棧可能很長,這將佔用大量的記憶體