为什么此功能会向后打印链接列表?

时间:2012-07-19 21:10:42

标签: python linked-list

我正在通过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的值只是给你“b c”?

注意:下面有几个人指出这个功能设计得很糟糕,因为在给定任何列表的情况下,我们无法证明它总能达到基本情况。唐尼也在同一章后面指出了这个问题。

7 个答案:

答案 0 :(得分:2)

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

不会调用“list.next”作为print_backward的值只是给你“b c”吗?

没有;图片当a-> b-> c传递给print_backward时会发生什么:

“[b c]”传递给print_backward,然后打印“a”。 但是,在打印“a”之前,“print_backward”会调用自己。所以:

  • [a b c]不是None,所以b-> c传递给print_backward
    • [b c]传递给print_backward
      • [c]传递给print_backward
      • 没有传递给print_backward
        • 返回
      • 然后打印“c”
    • 然后打印“b”
  • 然后打印“a”
  • 退出。

答案 1 :(得分:2)

在正向打印版本中,它会在执行递归调用之前打印每个节点。在向后打印版本中,它在执行递归调用后打印每个节点

这不是巧合。

两个函数都会递归,直到到达列表末尾。不同的是在此过程中或之后是否发生打印。

函数调用使用堆栈,这是一种后进先出数据结构,可记住在进行函数调用时计算机执行代码的位置。在一个订单中堆叠的内容以相反的顺序出现。因此,递归以原始调用的相反顺序“展开”。打印发生在展开过程中,即每次递归调用完成后。

答案 2 :(得分:1)

如果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'

答案 3 :(得分:1)

有时我发现将递归视为仅构建按特定顺序进行的调用列表更容易。随着函数的继续,它会构建一堆调用,直到它最终到达基本情况。基本情况是不需要进一步破坏程序的情况;在这个函数中,基本情况是什么都没有打印,在这种情况下,我们只是离开而没有做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

答案 4 :(得分:0)

它正在使用递归。它一直“拉链”直到它结束,然后在每次调用返回时打印每个元素。由于第一个要打印的是最近调用的,它会向后打印列表。

答案 5 :(得分:0)

没有。有两种递归:

  1. 尾递归:如果函数返回后没有任何事可做,除了返回它的值。函数调用没有堆叠。
  2. 首先查找基本案例的递归(在本例中为null,然后向后处理列表)。每个函数调用都被推入堆栈,以便以后处理。在你的例子中,函数被堆叠为'a'->'b'->'c'->null,然后当弹出堆栈时,作者通过向后打印显示:`if null return:print'c' - > print'b' - >打印'a'
  3. 在您的情况下,作者只展示了不同的递归概念,并使用它来向后打印列表。

答案 6 :(得分:0)

您的节点看起来像这样:

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

第一次调用print_backward时,变量list的值为'a',后续调用print_backward会向下移动一行None。请注意,在你击中警卫(print_backward)之前,他们都不会打印任何东西,此时,事情会从后到前打印,因为收到节点'c'的{​​{1}}必须在返回之前返回收到节点print_backward的{​​{1}}可以打印(因为print语句在函数调用之后),依此类推。

虽然我认识到这是其他人的代码,但这里有一些不好的做法 - 我现在告诉你的是你学习的时候而不是以后。首先,不要使用'b'作为变量名,因为它是python中内置函数/类型的名称。第二,list更好地完成了等级测试if obj == None,最后,让你的类继承自if obj is Noneobject)总是一个好主意,因为它使它成为一个新的式班。