簡體   English   中英

為什么此功能向后打印鏈接列表?

[英]Why does this function print a Linked List backwards?

我正在研究Downey的《如何像計算機科學家一樣思考》,並且對他的Linked List的 print_backward()函數有疑問。

首先,這是Downey在Python中的鏈表的實現:

 class Node:
     #initialize with cargo (stores the value of the node)
     #and the link. These are set to None initially.
     def __init__(self, cargo = None, next = None):
         self.cargo = cargo
         self.next = next

     def __str__(self):
         return str(self.cargo)

我們為此類提供以下貨物和鏈接值:

 #cargo
 node1 = Node('a')
 node2 = Node('b')
 node3 = Node('c')
 #link them 
 node1.next = node2
 node2.next = node3

要打印鏈接列表,我們使用Downey的另一個功能。

def printList(node):
     while node:
         print node,
         node = node.next

 >>>printList(node1)
 >>>a b c

一切都很簡單。 但是我不明白以下函數中的遞歸調用如何允許人們向后打印鏈表。

def print_backward(list):
     if list == None : return
     print_backward(list.next)
     print list,

>>>print_backward(node1)
>>>c b a

不會將“ list.next”用作print_backward的值只是給您“ bc”嗎?

注意:以下幾位人士指出,此功能的設計不正確,因為給定任何列表,我們都無法表明它總是可以到達基本情況。 唐尼還在同一章的后面指出了這個問題。

def print_backward(list):
     if list == None : return
     print_backward(list.next)
     print list,

不會將“ list.next”用作print_backward的值只是給您“ bc”嗎?

沒有; 想象一下當a-> b-> c傳遞給print_backward時會發生什么:

“ [bc]”傳遞給print_backward,然后打印“ a”。 但是在打印“ a”之前,“ print_backward”會自行調用。 所以:

  • [abc]不是None,所以b-> c被傳遞給print_backward
    • [bc]傳遞給print_backward
      • [c]傳遞給print_backward
      • 沒有任何內容傳遞給print_backward
        • 哪個返回
      • 然后打印“ c”
    • 然后打印“ b”
  • 然后打印“ a”
  • 退出。

在前向打印版本中,它將在執行遞歸調用之前打印每個節點。 在向后打印版本中,它將執行遞歸調用打印每個節點。

這不是偶然的。

這兩個函數都會遞歸直到到達列表的末尾。 區別在於在此過程中還是之后進行打印。

函數調用使用堆棧,這是一種后進先出的數據結構,可以記住進行函數調用時計算機在哪里執行代碼。 以一順序放在堆棧中的內容以相反的順序出現。 因此,遞歸以原始調用的相反順序“解開”。 打印在展開過程中進行,即在每個遞歸調用完成之后進行。

如果list不是None,它將調用print_backward,然后打印列表的第一個成員。 擴展,這自然是會發生的事情。 您會看到當呼叫開始返回時,先打印“ c”,然后打印“ b”,再打印“ a”。

實際打印列表時,它會打印第一個節點

print_backward(list='a','b','c')
  print_backward(list='b','c')
    print_backward(list='c')
      print_backward(list=None)
        list is None, so return
      print 'c'
    print 'b','c'
  print 'a','b','c'

有時,我發現將遞歸視為僅按一定順序構造要調用的列表就容易了。 隨着函數的繼續,它將建立一堆調用,直到最終到達基本情況為止。 基本情況是不需要進一步分解程序的情況; 在此函數中,基本情況是沒有任何可打印的內容,在這種情況下,我們無需執行return就可以離開。

當我們展開函數調用的遞歸堆棧時,很酷的事情通常發生在返回的路上。 當前,已在列表的每個元素上調用print_backward ,它現在將“展開”,首先完成最近的調用,最后完成較早的調用。 這意味着,當您在最后一個元素上調用print_backward創建的“實例”是第一個要完成的元素,因此最后一個元素是要打印的第一個元素,其后是倒數第二個,倒數第三個等等。 ,直到原始功能最終退出。

看一下發生了什么的這種表示形式:

print_backward(node1)            #first call to print_backward
    print_backward(node2)        #calls itself on next node
        print_backward(node3)    #calls itself on next node
            print_backward(None) #calls itself on None. We can now start unwinding as this is the base case:
        print Node3              #now the third invocation finishes...
    print Node2                  #and the second...
print Node1                      #and the first. 

雖然在較早的元素上首先調用該函數,但實際打印該元素的部分位於遞歸調用之后,因此,在遞歸調用完成之前,它實際上不會執行。 在這種情況下,這意味着print list部分要等到所有后來的元素都先被打印(以相反的順序)后才能執行,從而使您可以向后打印列表元素。 :D

它正在使用遞歸。 它一直向下“壓縮”直到結束,然后在每次調用返回時打印每個元素。 由於第一個要打印的是最近一次調用的,因此它將向后打印列表。

否。有兩種遞歸:

  1. 尾遞歸:如果函數返回后無事可做,則返回其值。 函數調用未堆疊。
  2. 遞歸首先查找基本情況(在這種情況下為null ,然后向后處理列表)。 每個函數調用都被壓入堆棧,以供以后處理。 在您的示例中,該函數堆疊為'a'->'b'->'c'->null ,然后在彈出堆疊時,作者通過向后打印來表明:`if null return:print'c' ->打印'b'->打印'a'

在您的情況下,作者僅演示了遞歸的不同概念,並使用該概念將列表向后打印。

您的節點如下所示:

node1 node2 node3
'a' => 'b' => 'c' => None

在第一次調用print_backward ,變量list的值為'a' ,隨后對print_backward調用進一步向下移動了一行。 請注意,他們沒有打印任何東西,直到你打中后衛( None ),此時,事情就從后到前的印刷print_backward收到節點'c'必須在返回之前print_backward收到節點'b'可以打印(因為print語句在函數調用之后)等等。

雖然我認識到這是別人的代碼,但是這里有一些不好的做法-最好在您學習時而不是以后告訴您。 首先,不要使用list作為變量名,因為它是python中內置函數/類型的名稱。 其次, if obj == None更好地完成if obj == None的相等性測試,最后,讓您的類繼承自objectclass node(object): if obj is None一個好主意,因為這使它成為新樣式的類。

暫無
暫無

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

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