算法圖解第三章--遞歸的深層次理解(學習筆記)

一、推薦:

如果想更好的理解本文章,你可以看往期文章:

算法圖解第二章–選擇排序法(數組,鏈表的進一步理解)(學習筆記)

二、基線條件和遞歸條件

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

這個代碼會出現一直運行的情況(你可以按Ctrl+C停止運行),之所以會出現這樣的情況是因爲沒有告訴它何時停止遞歸,所以每個遞歸函數都應該包含兩部分:基線條件和遞歸條件,去避免形成無限循環。改良後的代碼:

def countdown():
    print i
    if i<=1:
        return
    else:
        countdown(i-1)

三、棧

大部分人學函數時就瞭解了遞歸,諸如的經典例子便是漢諾塔問題,如果想要了解的點擊下面:
用遞歸來解決漢諾塔問題(超詳細的個人解讀)(Python)

我們先來理解一個重要的編程概念——調用棧(call stack)。

調用棧不僅對編程來說很重要,使用遞歸時也必須理解這個概念。

那什麼是棧呢?
在這裏插入圖片描述
(如果想要全本電子書可以去我的主頁裏簡介獲取)
這是書上的解釋,可能你沒有耐心去理解,我總結了一下,看圖理解:

在這裏插入圖片描述

你可以假設這是一個只有一個出口,三面封閉的盒子。

你如果往裏面放東西,先放進去的東西就會被後放進去的東西壓在下面,而當你想要去拿出被壓在下面東西的時候,你就必須先取出上面的東西(也就是你後放進去的東西).

其實書上的例子就好比你寫了一堆標籤,然後一個釘子把他們釘在了牆上,你就只能從外面一個一個拿(注意後面寫的標籤在外面,也就是先依次寫一堆標籤,然後反向依次處理)。

四、調用棧

既然你都理解了“棧”,那麼如何調用它呢?我們先寫一段簡單代碼輔助理解。

def greet2(name):
    print("你好!{}".format(name))
def bye(name):
    print("再見!{}".format(name))
def greet(name):
    print("Hello!{}".format(name))
    greet2(name)
    print("最近好嗎?")
    bye(name)
greet("maggie")

結果:

Hello!maggie
你好!maggie
最近好嗎?
再見!maggie

下面來詳細介紹函數運行情況(我覺得這本書這裏寫得很明白,就照着書說一下):
首先,你調用greet(“maggie”),計算機將首先爲該函數調用分配一塊內存。如圖:
在這裏插入圖片描述

我們來使用這些內存。

變量name被設置爲“maggie”,這需要存儲到內存中。
在這裏插入圖片描述

每當你調用函數時,計算機都像這樣將函數調用涉及的所有變量的值存儲到內存中。

接下來,你打印 Hello!maggie,再調用greet2(“maggie”)。

同樣,計算機也爲這個函數調用分配一塊內存。
在這裏插入圖片描述

計算機使用一個棧來表示這些內存塊,其中第二個內存塊位於第一個內存塊上面。

你打印 你好!maggie,然後從函數調用返回。

此時,棧頂的內存塊被彈出。

在這裏插入圖片描述

現在,棧頂的內存塊是函數greet的,這意味着你返回到了函數greet。

當你調用函數greet2時,函數greet只執行了一部分。

這是本節的一個重要概念:調用另一個函數時,當前函數暫停並處於未完成狀態。該函數的所有變量的值都還在內存中。執行完函數greet2後,你回到函數greet,並從離開的地方開始接着往下執行,

首先打印 最近好嗎?,再調用函數bye。

在這裏插入圖片描述

在棧頂添加了函數bye的內存塊。然後,你打印再見!maggie,並從這個函數返回。在這裏插入圖片描述

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

五、遞歸調用棧

先寫一段階乘代碼輔助理解下:

def fact(x):
    if x==1:
        return 1
    else:
        return x*fact(x-1)

簡單理解的話就是:
假如x=3,,先運行else語句,就會得到 xfact(x-1),此時x爲3,也就是3fact(2),

fact(2)函數會運行else語句則返回xfact(x-1),此時x爲2,也就是2fact(1),綜合上一句就是32fact(1)

fact(1)函數會運行if 語句,則返回1,綜上所述便是311=6,

其實遞歸的話就是隻有運行到fact(1)語句纔會運行基線條件,也只有運行了基線條件,遞歸纔會結束

然後反向依次返回(就是依次返回1,2,6)。

注意每個fact調用都有自己的x變量。在一個函數調用中不能訪問另一個的x變量

.這是我的理解,可能有一些不足,下面放上書上的理論理解:

在這裏插入圖片描述
在這裏插入圖片描述

再次注意
每個fact調用都有自己的x變量。在一個函數調用中不能訪問另一個的x變量。
其實學下來,你會發現,使用棧雖然很方便,但是也要付出代價

存儲詳盡的信息可能佔用大量的內存。每個函數調用都要佔用一定的內存

如果棧很高,就意味着計算機存儲了大量函數調用的信息

所以遞歸只是讓解決方案更清晰,並沒有性能上的優勢。

實際上,在有些情況下,使用循環的性能更好。

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