简体   繁体   English

与父母/孩子拼合一棵树并返回所有节点

[英]Flattening a tree with parents/children and return all nodes

It's probably too late, but I can't sleep until it's solved: 可能为时已晚,但是直到解决后我才能入睡:

I've got a tree with some parents, which have children, which have also children etc. 我有一棵树,有一些父母,他们有孩子,也有孩子等。

Now I need a function to get all nodes from the tree. 现在,我需要一个函数来从树中获取所有节点。

This is what currently works, but only with one level of depth: 这是当前有效的方法,但仅具有一个深度级别:

def nodes_from_tree(tree, parent):
    r = []
    if len(tree.get_children(parent)) == 0:
        return parent
    for child in tree.get_children(parent):
        r.append(nodes_from_tree(tree, child))
    return r

Then I tried to pass r through, so it remembers the children, but I'm using the function more then once and r stores cumulatively all nodes, although I'm setting it to r=[] : 然后我尝试将r传递给它,这样它就记住了孩子,但是我使用函数的次数超过了一次,并且r累计存储了所有节点,尽管我将其设置为r=[]

def nodes_from_tree(tree, parent, r=[]):
    r = []
    if len(tree.get_children(parent)) == 0:
        return parent
    for child in tree.get_children(parent):
        r.append(nodes_from_tree(tree, child, r))
    return r

Edit : This is the tree structure: 编辑 :这是树结构:

parent1    parent2    parent3
   |          |          |
   |          |          |
 child        |          |
              |          |
      +--------------+   |
      |       |      |   |
    child   child  child |
      |                  |
  +---+---+              |
child   child        +---+---+
                     |       |
                   child     |
                             |
                       +-----+-----+-----+
                       |     |     |     |
                     child child child child

Available methods: 可用方法:

tree.get_parents()       # returns the nodes of the very top level
tree.get_children(node)  # returns the children of parent or child

If I understand your question, you want to make a flat list containing all the values in a tree, in which case a tree represented by tuples the following would work: 如果我理解您的问题,您想制作一个包含树中所有值的平面列表,在这种情况下,用元组表示的树将起作用:

def nodes_from_tree(tree,nodes=list()):
    if isinstance(tree,tuple):
        for child in tree:
            nodes_from_tree(child,nodes=nodes)
    else:
        nodes.append(tree)

mynodes = []
tree = (('Root',
        ('Parent',(
            ('Child1',),
            ('Child2',)
            )
        ),
        ('Parent2',(
            ('child1',(
                ('childchild1','childchild2')
            )),
            ('child2',),
            ('child3',)
        )),
        ('Parent3',(
            ('child1',),
            ('child2',(
                ('childchild1',),
                ('childchild2',),
                ('childchild3',),
                ('childchild4',)
            ))
        ))
    ))
nodes_from_tree(tree,nodes=mynodes)
print(mynodes)

Produces 产生

['Root', 'Parent', 'Child1', 'Child2', 'Parent2', 'child1', 'childchild1', 'childchild2',
 'child2', 'child3', 'Parent3', 'child1', 'child2', 'childchild1', 'childchild2', 'childchild3', 'childchild4']

I think your problem is just that you're accumulating things incorrectly. 我认为您的问题仅仅是您不正确地积累了东西。

First, if you hit an intermediate node, each child should return a list, but you're append ing that list instead of extend ing it. 首先,如果您击中一个中间节点,则每个子节点都应返回一个列表,但是您要append该列表而不是extend它。 So, instead of [1, 2, 3, 4] you're going to get something like [[1, 2], [3, 4]] —in other words, you're just transforming it into a list-of-list tree, not a flat list. 因此,您将获得[[1, 2], [3, 4]]类的东西,而不是[1, 2, 3, 4] [[1, 2], [3, 4]] -换句话说,您只是将其转换为列表- -list树,而不是平面列表。 Change this to extend . 将其更改为extend

Second, if you hit a leaf node, you're not returning a list at all, just parent . 其次,如果您命中了一个叶子节点,那么您根本就不会返回任何列表,而只是返回parent Change this to return [parent] . 更改为return [parent]

Third, if you hit an intermediate node, you don't include parent anywhere, so you're only going to end up with the leaves. 第三,如果您击中一个中间节点,那么您将不会在任何地方包含parent节点,因此最终只会留下叶子。 But you wanted all the nodes. 但是您需要所有节点。 So change the r = [] to r = [parent] . 因此将r = []更改为r = [parent]

And with that last change, you don't need the if block at all. 有了最后的更改,您根本不需要if块。 If there are no children, the loop will happen 0 times, and you'll end up returning [parent] as-is, exactly as you wanted to. 如果没有子代,则该循环将发生0次,最终您将按原样返回[parent]

So: 所以:

def nodes_from_tree(tree, parent, r=[]):
    r = [parent]
    for child in tree.get_children(parent):
        r.extend(nodes_from_tree(tree, child, r))
    return r

Meanwhile, while this version will work , it's still confused. 同时,尽管此版本可以使用 ,但仍然感到困惑。 You're mixing up two different styles of recursion. 您正在混合两种不同的递归样式。 Passing an accumulator down the chain and adding to on the way down is one way to do it; 一种将累加器顺着链条传下来,然后再顺路加法的方法是这样做的。 returning values up the chain and accumulating results on the way up is the other. 另一个是返回价值链并在上升过程中累积结果。 You're doing half of each. 你们每个人都做一半。

As it turns out, the way you're doing the upstream recursion is making the downstream recursion have no effect at all. 事实证明,您执行上游递归的方式是使下游递归完全无效。 While you do pass an r down to each child, you never modify it, or even use it; 当您确实将r传递给每个孩子时,您永远不会修改它,甚至不会使用它。 you just create a new r list and return that. 您只需创建一个新的r列表并将其返回即可。

The easiest way to fix that is to just remove the accumulator argument: 最简单的解决方法是删除累加器参数:

def nodes_from_tree(tree, parent):
    r = [parent]
    for child in tree.get_children(parent):
        r.extend(nodes_from_tree(tree, child))
    return r

(It's worth noting that branching recursion can only be tail-call-optimized if you do it in downstream accumulator style instead of upstream gathering style. But that doesn't really matter in Python, because Python doesn't do tail call optimization. So, write whichever one makes more sense to you.) (值得注意的是,如果您以下游累加器样式而不是上游收集样式来执行分支递归,则只能对尾调用进行优化。但这在Python中并不重要,因为Python不会进行尾调用优化。因此,请写下对您更有意义的一种。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM