數據結構與算法(九):遞歸原理及其Python實現

一、什麼是“遞歸”

遞歸,顧名思義,包括“遞”和“歸”兩個過程,“遞”的過程就是將原問題一層一層分解下去直到找到終止條件,“歸”的過程就是從終止條件開始一層一層向上迴歸直到歸併到原問題。

可以看出,遞歸需要滿足三個條件:

  • 原問題能夠分解爲若干個子問題;
  • 子問題與原問題求解思路相同,只是數據規模不同;
  • 存在終止條件;

一個經典的遞歸問題是:

假如這裏有 n 個臺階,每次你可以跨 1 個臺階或者 2 個臺階,請問走這 n 個臺階有多少種走法?

求解該問題,可按照遞歸三個條件來進行:

  1. 原問題可以分解爲兩個子問題:下一步走一個臺階、下一步走兩個臺階;
  2. 子問題與原問題求解思路相同,只是數據規模不同:f(n) = f(n-1) + f(n-2)
  3. 存在終止條件:f(1) = 1, f(2) = 2

二、Python使用遞歸實現走臺階

基於上述原理,可以輕鬆實現一個遞歸代碼:

def walk_steps(n:int)->int:
    if n == 1:
        return 1
    elif n == 2:
        return 2
    return walk_steps(n-1) + walk_steps(n-2)

print(walk_steps(7))

三、遞歸轉循環

遞歸編程思想簡單,幾行代碼即可實現一個人腦思考不過來的遞歸問題,但卻有着致命缺點:堆棧溢出、空間複雜度高

在編程語言進行函數調用時,採用棧來存放臨時變量,因此對於遞歸函數,其子問題的返回值都會臨時存放在棧中,當數據規模變大時,其空間複雜度將極大地提高,容易造成堆棧溢出,函數求解將變得極其困難。

對於上述實現的遞歸代碼,在小數據量時,感覺不到困難,但當數據規模(即總的臺階數n)變大時(大概超過40時)求解將變得極其緩慢,且繼續增大到一定規模,就會報堆棧溢出的錯誤:

RecursionError: maximum recursion depth exceeded in comparison

因此,對於某些遞歸函數,如果可以轉化爲非遞歸,則能大大降低其空間複雜度,同時避免堆棧溢出。下面使用循環方式實現階梯走法計算:

def walk_steps_circle(n:int)->int:
    if n == 1:
        return 1
    elif n == 2:
        return 2
    step1 = 1
    step2 = 2
    step_total = 0
    for i in range(3,n+1):
        step_total = step1 + step2
        step1 = step2
        step2 = step_total
    return step_total

walk_steps_circle(7)

大家可以在自己機器上試一下,分別給兩個函數傳入一個較大的參數,看看運行結果,即可發現其明顯的差別。

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