【问题标题】:python recursive iteration exceeding limit for tree implementationpython递归迭代超出树实现的限制
【发布时间】:2014-07-22 02:52:37
【问题描述】:

我正在 python 中动态实现一棵树。我已经定义了一个类如下

class nodeobject():

    def __init__(self,presentnode=None,parent=None):
        self.currentNode = presentnode
        self.parentNode = parent
        self.childs = []

我有一个函数可以从池中为每个节点获取可能的子节点

def findchildren(node, childs):` `# No need to write the whole function on how it gets childs

现在我有一个递归函数,它从头节点(无父节点)开始,并为每个节点递归地向下移动(基本情况是最后一个没有子节点的节点)

def tree(dad,children):

    for child in children:
        childobject = nodeobject(child,dad)
        dad.childs.append(childobject)
        newchilds = findchildren(child, children)
        if len(newchilds) == 0:
            lastchild = nodeobject(newchilds,childobject)
            childobject.childs.append(lastchild)
            loopchild = copy.deepcopy(lastchild)
            while loopchild.parentNode != None:
                print "last child"
                result.append(loopchild.currentNode) # result global to store values
                loopchild = copy.deepcopy(loopchild.parentNode)
        else:
            tree(childobject,newchilds)

树形结构仅适用于特定数量的输入。一旦池变大,它会导致“超过最大递归深度”

我尝试使用 set.recursionlimit() 设置递归限制,但它不起作用。程序崩溃。我想实现一个递归堆栈,有人可以帮忙吗,即使尝试了很长时间我也没有去哪里??另外,除了 stack 有没有其他方法可以解决这个问题?

【问题讨论】:

  • 您是否调查过您是否正确地从树中下降?根据您的描述,听起来 findchildren 可能总是会找到新的孩子(因此该函数永远不会真正耗尽自己)
  • @JeffTratner - 在你们所有人都将我指向 findchildren 之后......我做了更多的挖掘,似乎这就是问题所在。谢谢。

标签: python recursion tree stack


【解决方案1】:

当您尝试更改递归深度时,您的程序可能会崩溃,因为您超出了系统上堆栈大小所施加的硬递归限制。设置 sys.recursionlimit() 只会降低 Python 对深度的严格程度,但不会影响您的平台实际支持的内容。

Python 有一个相当幼稚的递归实现,这意味着它仅在您能够保证您的递归深度相当低时才有用。首先检查你的树是否足够深,可以炸掉堆栈;如果任何节点的子节点也是它们的父节点/祖先节点,则此代码将尝试永远运行,直到耗尽堆栈。一种检查方法是跟踪findchildren() 返回的所有节点,并确保它们永远不会重复。

如果您的数据正确且堆栈确实不够深,则您必须将代码转换为迭代版本并手动构建自己的堆栈。这是您的带有显式堆栈的代码(我没有对此进行测试,因此可能存在错误,但它应该让您知道如何去做):

def tree(dad, children):
    stack = [(dad, children)]
    while stack:
        dad, children = stack.pop()
        for index, child in enumerate(children):
            childobject = nodeobject(child,dad)
            dad.childs.append(childobject)
            newchilds = findchildren(child, children)
            if len(newchilds) == 0:
                lastchild = nodeobject(newchilds,childobject)
                childobject.childs.append(lastchild)
                loopchild = copy.deepcopy(lastchild)
                while loopchild.parentNode != None:
                    print "last child"
                    result.append(loopchild.currentNode) # result global to store values
                    loopchild = copy.deepcopy(loopchild.parentNode)
            else:
                stack.append((dad, children[index:]))
                stack.append((childobject,newchilds))
                break

需要注意的一个重要特性是,在我们推送新的子节点之前,我们必须将尚未在 for 循环中处理的子节点推回堆栈。

@Peter Gibson 提出了一个很好的观点,您可能不想深度复制您的节点,但这不应该导致您的堆栈爆炸(只是使用大量内存而我看不到任何好处)。

【讨论】:

  • 非常感谢。我没有使用任何复制功能并修改了代码以更好地处理 findchildren() 并且它起作用了。早些时候,当我在 findchildren 中遇到 deepcopy 和错误时……它曾经在瞬间给我“达到最大递归深度”,现在在修复后,程序运行了 20 分钟并给了我正确的答案,递归限制仍然没有违反。不明白这是怎么回事???还要感谢堆栈示例,我将尝试“仅仅因为”实现它
【解决方案2】:

我会说这段代码是问题所在:

        loopchild = copy.deepcopy(lastchild)
        while loopchild.parentNode != None:
            print "last child"
            result.append(loopchild.currentNode) # result global to store values
            loopchild = copy.deepcopy(loopchild.parentNode)

您遍历树直到到达叶子(没有子节点的子节点),然后将每个节点的deepcopy 带回起点。

deepcopy 复制nodeobjectparentNodechilds 中的每个孩子的副本。这意味着nodeobject 中的每个deepcopy 都是整个树以及所有数据的副本

考虑一棵 4 层深的简单树,例如

          A
    B           C
 D     E     F     G
H I   J K   L M   N O

当它到达第一个叶子 H 时,它会生成节点 HDBAdeepcopy - 每个节点都是整个树的副本,并且您的所有数据。因此,对于这个相当简单的结构,您最终得到了 32 个副本!我可以看到这很快就会失控。

您复制每个节点是否有原因?尝试用

替换该代码
        loopchild = lastchild
        while loopchild.parentNode != None:
            print "last child"
            result.append(loopchild.currentNode) # result global to store values
            loopchild = loopchild.parentNode

【讨论】:

  • 我用简单的赋值替换了 deepcopy,但我的程序仍然有问题。在我的项目的开始阶段,这个简单的分配不起作用,所以我想我应该为这个对象制作一个全新的副本。
  • @LuckyStarr 您可以编辑您的问题以添加一些示例数据和 findchildren 函数吗?
  • 所以现在在替换 deepcopy 并修改 findchildren() 的一些代码之后......我已经设法解决了这个问题。感谢您的帮助。
猜你喜欢
  • 2022-01-21
  • 2019-03-12
  • 1970-01-01
  • 2012-07-11
  • 1970-01-01
  • 2019-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多