【演算法圖解】讀書筆記:第3章 遞迴

一顆賽艇?發表於2019-02-14

遞迴


在我看來,遞迴就類似俄羅斯套娃,一次次重複去執行相同的操作,最後得出結果的過程。

很多遞迴的操作都可以使用迴圈來替代。遞迴併沒有帶來演算法效能上的優勢,甚至更差,但是使用遞迴可能讓你的程式更易理解。所以理解遞迴這種概念很重要。

基線條件和遞迴條件


由於遞迴函式呼叫自己,很容易導致無線迴圈。比如,你需要一個倒數的函式:

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執行完畢,出棧
複製程式碼

棧的執行邏輯就是:先進後出原則。

遞迴呼叫棧


遞迴呼叫中,儲存詳盡的資訊可能佔用大量的記憶體。這回付出很大的代價。如果佔用過高,你需要使用迴圈去代替遞迴,或者使用尾遞迴優化,前提是你的語言支援尾遞迴優化。

小結


  • 遞迴指的是呼叫自己的函式。
  • 每個遞迴函式都有兩個條件:基線條件和遞迴條件。
  • 棧有兩種操作:壓入和彈出。
  • 所有函式呼叫都進入呼叫棧。
  • 呼叫棧可能很長,這將佔用大量的記憶體

相關文章