Python代码中的奇怪行为

时间:2013-02-14 07:10:09

标签: python tree depth-first-search

我正在尝试编写一种简单的方法,以这种方式将树的节点链接在一起:

  • 每个叶子都链接到树中的上一个叶子和下一个叶子
  • 每个非叶子都链接到树中的上一个叶子和下一个叶子

例如,如果我们有这棵树:

    A
  / |  \
 B  C    D
   / \  / \
  E  F  G  H
        |
        I

这应该是该方法的结果:

  • B.nextToken = E
  • C.prevToken = B
  • E.nextToken = F
  • E.prevToken = B
  • F.nextToken = I
  • C.nextToken = I
  • H.prevToken =我

以下是方法代码:

prevToken = None
def depthFirstTraverseTokenLinking(tree):
    global prevToken
    if len(tree.children) == 0:
        tree.prevToken = prevToken
        if prevToken != None :
            prevToken.nextToken = tree # Is something wrong with this line?
        prevToken = tree
        return

    for c in tree.children:
        depthFirstTraverseTokenLinking(c)

    tree.prevToken = tree.children[0].prevToken
    tree.nextToken = tree.children[-1].nextToken

由于某些奇怪的原因,非叶子没有链接到下一个叶子,例如:

  • C.nextToken =无

虽然

  • F.nextToken = I

我想知道为什么会这样?递归函数末尾的最后一行应该授予父项与下一个子项相同的下一行!

2 个答案:

答案 0 :(得分:1)

问题是,当你访问C时,你只会遍历它的孩子E& F。

“我”尚未访问过,C.children[-1].nextToken == None因为只访问“我”会设置F.nextToken

解决方案:您必须首先在所有叶子上运行,然后在内部节点上运行第二次运行。

例如:

prevToken = None
def depthFirstTraverseTokenLinking(tree):
    depthFirstTraverseTokenLinkingPhase1(tree)
    depthFirstTraverseTokenLinkingPhase2(tree)

def depthFirstTraverseTokenLinkingPhase1(tree):
    global prevToken
    if len(tree.children) == 0:
        tree.prevToken = prevToken
        if prevToken != None :
            prevToken.nextToken = tree # Is something wrong with this line?
        prevToken = tree
        return

    for c in tree.children:
        depthFirstTraverseTokenLinkingPhase1(c)

def depthFirstTraverseTokenLinkingPhase2(tree):
    if len(tree.children) == 0:
        return

    for c in tree.children:
        depthFirstTraverseTokenLinkingPhase2(c)

    if tree.children[0].prevToken is not None:
        tree.prevToken = tree.children[0].prevToken
    else:
        tree.prevToken = tree.children[0]

    if tree.children[-1].nextToken is not None:
        tree.nextToken = tree.children[-1].nextToken
    else:
        tree.nextToken = tree.children[-1]

另请注意内部节点的prevToken / nextToken的更改。如果您希望它们链接到实际的第一个/最后一个叶子,则需要这样做。

答案 1 :(得分:1)

或者,使用生成器和实例检查循环

如果节点没有子节点,则生成器将节点作为基本情况,否则另一个生成器沿树向下传播。这里需要注意的是node.children是从左到右排序的。

def leafs(node):
    if len(node.children) == 0:
        yield node
    else:
        for child in node.children:
            yield leafs(child)

...还有一堆带有生成器的循环......当我写这篇文章的时候,它变得更加丑陋 - 我想你可以把它清理一下然后摆脱真正的......

current_node = leafs(a)
stack = []
last_node = None
while True:
    if isinstance(current_node, types.GeneratorType):
        stack.append(current_node)
        current_node = current_node.next()
    else:
        if last_node and last_node != current_node:
            last_node.nextToken = current_node
            current_node.prevToken = last_node
            last_node = current_node
        try:
            current_node = stack[-1].next()
        except StopIteration:
            stack.pop()
        except IndexError:
            break