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

遞歸


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

不少遞歸的操做均可以使用循環來替代。遞歸併無帶來算法性能上的優點,甚至更差,可是使用遞歸可能讓你的程序更易理解。因此理解遞歸這種概念很重要。編程

基線條件和遞歸條件


因爲遞歸函數調用本身,很容易致使無線循環。好比,你須要一個倒數的函數:bash

def countdown(i):
  print(i)
  countdown(i - 1)

countdown(3)
複製代碼

若是運行上述代碼,這個函數就會不停的運行。函數

編寫遞歸函數式,必須告訴它什麼時候中止遞歸,性能

每一個遞歸函數都有兩部分:基線條件(base case)和遞歸條件(recursive case)。遞歸條件指的是函數調用本身,而基線條件則 指的是函數再也不調用本身,從而避免造成無限循環。優化

好了,咱們嘗試爲上面的代碼增長基線條件:ui

def countdown(i):
  print(i)
  if i <= 0: # 基線條件
    return
  else: # 遞歸條件
    countdown(i - 1)

countdown(3)
# 3 2 1 0 程序中止
複製代碼


調用棧不只對於編程來講很重要,使用遞歸是也必須裂解這個概念。計算機在內部使用被稱爲調用棧的棧。spa

咱們簡單看一個例子:code

# 忽略 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執行完畢,出棧
複製代碼

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

遞歸調用棧


遞歸調用中,存儲詳盡的信息可能佔用大量的內存。這回付出很大的代價。若是佔用太高,你須要使用循環去代替遞歸,或者使用尾遞歸優化,前提是你的語言支持尾遞歸優化。

小結


  • 遞歸指的是調用本身的函數。
  • 每一個遞歸函數都有兩個條件:基線條件和遞歸條件。
  • 棧有兩種操做:壓入和彈出。
  • 全部函數調用都進入調用棧。
  • 調用棧可能很長,這將佔用大量的內存
相關文章
相關標籤/搜索