《算法圖解》學習筆記習題和代碼(第三章 遞歸)Python3

目錄

第三章 遞歸

3.1 遞歸 

3.2 基線條件和遞歸條件 

3.3 棧 

3.3.1 調用棧 

練習1

3.3.2 遞歸調用棧 

練習2

3.4 小結 



第三章 遞歸

3.1 遞歸 

遞歸——函數調用自己。

學習如何將問題分成基線條件和遞歸條件。

遞歸會讓解決方案更清晰,並沒有性能上的優勢。實際上,在有些情況下,使用循環的性能更好。

3.2 基線條件和遞歸條件 

比如,用遞歸方式編寫倒計時:

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

但運行這個程序,不會自己停止。編寫遞歸函數時,必須告訴它何時停止遞歸。

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

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

Out:

5 4 3 2 1 0

3.3 棧 

用一疊便條寫待辦事項,待辦事項清單隻有兩種操作:壓入(插入)和彈出(刪除並讀取)。 

 這種數據結構稱爲棧。

3.3.1 調用棧 

def greet(name):  
    print "hello, " + name + "!" 
    greet2(name) 
    print "getting ready to say bye..." 
    bye() 

greet函數要依次調用greet2,bye函數。

調用greet("maggie"),計算機爲greet函數分配一個內存,變量名爲“maggie”存儲到內存中。

 接着運行print(),再接着調用greet2函數。(調用另一個函數時,當前函數暫停並處於未完成狀態。)計算機也爲greet2分配內存。

 這個函數調用結束後,棧頂的內存塊被彈出。再調用下個函數,最後再返回最初的greet函數。

由於沒有別的事情要做,你就從函數greet返回。這個棧用於存儲多個函數的變量,被稱爲調用棧。 

練習1

3.1 根據下面的調用棧,你可獲得哪些信息? 

 答:調用函數GREET,變量名爲MAGGIE,在此過程中函數GREET2函數被調用,計算機爲它分配內存,堆疊上去,當GREET2調用結束時該內存塊會被彈開,返回到GREET函數的調用中。

3.3.2 遞歸調用棧 

計算階乘。

#計算階乘
def fact(x):
    if x == 1:      #基線條件
        return 1
    else:
        return x*fact(x-1)   #遞歸條件

print(fact(5))

Out: 120

 

使用棧雖然很方便,但是也要付出代價:存儲詳盡的信息可能佔用大量的內存。每個函數調用都要佔用一定的內存,如果棧很高,就意味着計算機存儲了大量函數調用的信息。在這種情況下,你有兩種選擇。 
重新編寫代碼,轉而使用循環。
使用尾遞歸。這是一個高級遞歸主題,不在本書的討論範圍內。另外,並非所有的語言都支持尾遞歸。

練習2

假設你編寫了一個遞歸函數,但不小心導致它沒完沒了地運行。正如你看到的,對於每次函數調用,計算機都將爲其在棧中分配內存。遞歸函數沒完沒了地運行時,將給棧帶來什麼影響? 

答:調用棧不斷加層,會越來越長,直至計算機內存不夠。

(在Python中,函數調用是通過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。由於棧的大小不是無限的,所以,遞歸調用的次數過多,會導致棧溢出。---百度百科)

3.4 小結 

遞歸指的是調用自己的函數。
每個遞歸函數都有兩個條件:基線條件和遞歸條件
棧有兩種操作:壓入和彈出。
所有函數調用都進入調用棧。
調用棧可能很長,這將佔用大量的內存。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章