繁体   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