簡體   English   中英

遞歸函數如何在“ for循環”中工作

[英]How recursive functions work inside a 'for loop'

我在“遞歸函數”中還很陌生。 因此,我正在努力思考為什么使用遞歸函數以及遞歸函數是如何工作的,我認為我對此有很好的理解。

兩天前,我正在嘗試解決最短路徑問題。 我有以下圖(在python中):

 graph = {'a': ['b', 'c'],
             'b': ['c', 'd'],
             'c': ['d'],
             'd': ['c'],
             'e': ['f'],
             'f': ['c']}

我只是想找到一條路徑,而不是最短的路徑,所以這是代碼:

def find_path(graph,start,end,path=[]):
    path = path + [start]
    #Just a Test
    print(path)

    if start == end:
        return path

    if start not in graph:
        return None

    for node in graph[start]:
        if node not in path:
            new_path = find_path(graph,node,end,path)
        if new_path:
            #Just a test
            print(path)
            return new_path

print(find_path({'a':['b','c'],'b':['c','d'],'c':['d'],'d':['c'],'e':
['f'],'f':['c']},'a','d'))

我的起始頂點為“ a”,結束頂點為“ d”。

在第四行中,我僅打印了“路徑”以查看路徑的去向。

在第17行,我還打印了“路徑”,再次進行測試。 結果如下:

['a']
['a', 'b']
['a', 'b', 'c']
['a', 'b', 'c', 'd']
['a', 'b', 'c']
['a', 'b']
['a']
['a', 'b', 'c', 'd']

結果的前四行是代碼第4行上的'print(path)'結果。 但是,第5、6和7行是代碼第17行的'print(path)'結果。

我的問題是為什么路徑列表每次都減少一個頂點?

我一直在尋找解決方案已有2天了。 我去了論壇,閱讀了有關遞歸的文檔並觀看了視頻。 但是,沒有運氣。

如果有人可以回答,我將非常感激。

這是因為遞歸產生的結果是從“最內層”調用到“最外層”調用。 也就是說,第一行17 print語句從路徑的節點最多的最深遞歸級別開始。 在該級別返回后,將打印下一個級別“向上”(路徑中少一個節點)。 請注意,您的print功能來遞歸調用 find_path

您可以將其可視化如下:

find_path(..., path=['a'])  # Recursion level 1.
|
|   find_path(..., path=['a', 'b'])  # Recursion level 2.
|   |
|   |   find_path(..., path=['a', 'b', 'c'])  # Recursion level 3.
|   |   print(path)  # Prints ['a', 'b', 'c'].
|   |   return  # Return from level 3.
|   |
|   print(path)  # Prints ['a', 'b'].
|   return  # Return from level 2.
|
print(path)  # Prints ['a'].
return  # Return from level 1.

如果你想在要打印的單(子)路徑“增加”命令,那么你可以簡單地將print功能的遞歸調用之前 find_path

它是new_path變量,用於保存遞歸找到的路徑,而path僅保存到當前節點的路徑。

順便說一句, if new_path:尚未輸入前一個if分支, if new_path:子句可能會失敗,因為那時new_path是未定義的。

首先,我將解釋回溯的含義。 我也在這里發布了這個答案。

遞歸意味着從同一函數內調用該函數。 現在發生的是,當函數遇到對自身的調用時。想象一下,當函數在此再次遇到調用時,將打開一個新頁面並將控件從舊頁面轉移到該新頁面上,直至該函數的開始。新頁面,旁邊會打開另一個頁面,這樣,新頁面會在舊頁面旁邊不斷彈出。 請注意,所有局部變量僅在其各自頁面的范圍內。 也就是說,如果您要訪問上一頁中的值,則可以將其傳遞給參數中的函數或將變量設置為全局變量。

返回的唯一方法是使用return語句。 當函數遇到該函數時,控件將從新頁返回到調用該頁所在的同一行的舊頁面,並開始執行該行下方的內容。 這是回溯開始的地方。 為了避免出現諸如在數據填滿后再次饋入數據之類的問題,通常需要在每次調用該函數之后放置一個return語句。

現在在您的代碼中

def find_path(graph,start,end,path=[]):
    path = path + [start]
    #Just a Test
    print(path)

    if start == end:
        return path

    if start not in graph:
        return None

    for node in graph[start]:
        if node not in path:
            new_path = find_path(graph,node,end,path)  <---- when function returns it will start executing from here again.
        if new_path:
            #Just a test
            print(path)
            return new_path

請注意,您的path變量不是全局變量。 這是當地人。 這意味着您每次調用它都將重置。 為避免這種情況,您需要在函數參數中(最后一個)再次傳遞路徑值。

所以最后,當函數在找到d之后返回時,它返回到先前的狀態,其中path變量中只有a, b, c 那就是你打印出來的。

編輯:-以防萬一有人反對,我對使用頁面進行遞歸的解釋完全是非技術性的,如果您想知道它是如何發生的,那么您將不得不閱讀有關激活記錄及其如何將所有狀態推入堆棧的信息

1) find_path方法首先調用a作為設定路徑為開始節點['a']並調用find_path與方法b在第17行打印路徑之前作為起始節點。

2)以b為起始節點的find_path方法的調用將路徑設置為['a','b']並以c為起始節點調用find_path方法,然后在第17行打印路徑。

3)以c作為起始節點的find_path方法的調用將路徑設置為['a','b','c']並在第17行打印路徑之前以d作為起始節點調用find_path方法。

4)以d作為起始節點的find_path方法的調用將路徑設置為['a','b','c','d']打印在第4行並返回。

5)現在,它在執行find_path方法的第14行中返回,其中c為起始節點(如第3點所述將路徑設置為['a','b','c'] ),並在第17行(即結果的第5行)並返回。

6)這在執行方法find_path第14行返回,其中b為起始節點(如第2點所述將路徑設置為['a','b'] ,並在第17行打印路徑(即結果的第6行)並返回。

7)這在執行方法find_path第14行返回, find_patha作為起始節點(如第1點所述將路徑設置為['a'] ),並在第17行(即您的第6行)打印路徑結果)並返回。

您可以把它想象成LIFO(后進先出)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM