【问题标题】:Recursion Working problem with dictionary递归字典的工作问题
【发布时间】:2021-08-17 07:35:31
【问题描述】:

我对递归有疑问。请帮助澄清我的疑问。

我有一个存储二叉树节点的数据结构:

 class Node:
        def __init__(self, key, left=None, right=None):
            self.data = key
            self.left = left
            self.right = right
    # Recursive function to print left view of given binary tree
    def printLeftView(root,level=0,leveldic={}):
        if root==None:
            return
        print(root.data,leveldic)
        if level not in leveldic:
            leveldic[level]=root.data
            print(root.data)
        printLeftView(root.left,level+1,leveldic)
        printLeftView(root.right,level+1,leveldic)
    
"""
                      1
                    /    \
                   2       3
                  /      /    \
                4      5      6
                       \       
                        8

"""
if __name__ == '__main__':
    root = Node(1)
    root.left = Node(2)
    root.right = Node(3)
    root.left.right = Node(4)
    root.right.left = Node(5)
    root.right.right = Node(6)
    root.right.left.right = Node(8)
    printLeftView(root)

输出:

> ​1 {}
>     1
>     2 {0: 1}
>     2
>     4 {0: 1, 1: 2}
>     4
>     3 {0: 1, 1: 2, 2: 4}
>     5 {0: 1, 1: 2, 2: 4}
>     8 {0: 1, 1: 2, 2: 4}
>     8
>     6 {0: 1, 1: 2, 2: 4, 3: 8}

怀疑:

为什么字典返回后不回到原来的状态?

即,在 print 4 之后的字典是

{0:1、1:2、2:4}

由于它返回并且返回节点3时,它应该返回到之前的状态,同时返回成为

{0:1, 1:3}

这没有发生,字典没有得到更改,它与节点 4 中的相同。

{0:1、1:2、2:4}

可能是什么原因?

【问题讨论】:

    标签: python recursion tree binary-tree


    【解决方案1】:

    似乎字典参数不是字典的副本,而是在内存中引用它,所以当它应该返回节点 3 时,它使用的是旧键。您必须复制它。

    def printLeftView(root,level=0,oldic={}):
        if root==None:
            return
    
        leveldic={} #making new dict
        for i in oldic:
            leveldic[i]=oldic[i] #and copying it values, to work on copy
        
        leveldic[level]=root.data
        print(root.data)
        print(root.data,leveldic)
    
        printLeftView(root.left,level+1,leveldic)
        printLeftView(root.right,level+1,leveldic)
    

    【讨论】:

    • 嘿,这是特定于 python 语言还是在 c++/Java 等的情况下相同:?
    • @PranavM 它也可以在其他语言中发生,但在 C++ 中通常是有意的,因为函数的声明看起来像 void fun(datatype *variable),其中 ' * ' 表示它是对变量的引用。我不确定,但我认为库中的某些数据类型默认情况下可能会这样工作。
    【解决方案2】:

    接受的答案不正确:它会产生错误的输出。

    原始代码与算法应该工作的完全一样:所有递归执行上下文共享一个字典,它不应该在从递归调用。如果你试图“修复”这个问题,你实际上会破坏算法,然后输出通常是错误的。

    请注意,当您将字典对象作为参数传递给函数时,它不会被深度复制:函数将在新变量中获取参数,但它仍然是同一个字典。除非为该变量分配了一个新值,否则此字典的任何变异都将是该单个字典实例。

    但是,您的代码还有另一个问题:默认参数值leveldic={} 有问题。这种分配只发生一次,这是一种非常特定于 Python 的行为。这意味着如果您从主程序再次调用,您将仍然继续使用字典对象,就像上次调用之后一样。

    如果第二次调用,您需要重置它:

    def printLeftView(root, level=0, leveldic=None):
        if leveldic is None:
            leveldic = {}
        if root is None:
            return
        if level not in leveldic:
            leveldic[level] = root.data
            print(root.data)
        printLeftView(root.left, level + 1, leveldic)
        printLeftView(root.right, level + 1, leveldic)
    

    【讨论】:

    • 嗨,谢谢你的解释。据我了解,python 在函数调用中自动使用引用调用,其中作为 java 并且除非指定,否则全部不使用。那么在返回后将字典重置回其原始状态的选项是什么。制作一个 deepcopy 并在每次调用中使用它,返回后的 pop() 项?还是其他方法?
    • 你为什么要这个?正如我所解释的,它会使这个算法出错。如果您遇到实际需要算法才能正常工作的情况,请在新问题中询问。但在这里它应该保持原样。传递参数的方式实际上与 Java 非常相似。在 Java 中,当您将哈希映射作为参数传递时,它是一个引用——没有复制。它的工作方式不同的是在类似 C 的语言中,你有 struct
    • 我知道它出错了。我现在不在乎算法。只需要知道我的要求的可能替代方案的原因。
    • 您的要求是什么?我觉得很奇怪,您询问算法并且不在乎算法会被您想听到的答案打破。确实很奇怪。
    • 对于其他任何来到这里的人:接受的答案不正确。该代码将产生错误的输出。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-07
    • 1970-01-01
    • 2014-05-25
    • 1970-01-01
    • 2020-06-10
    • 2012-11-11
    • 1970-01-01
    相关资源
    最近更新 更多