【问题标题】:Construct a tree from a list with levels从具有级别的列表构造一棵树
【发布时间】:2017-05-17 13:28:16
【问题描述】:

我有一些数据(Python list of dicts)看起来像:

[
    {'value': 'A', 'level': 0},
    {'value': 'B', 'level': 1},
    {'value': 'C', 'level': 2},
    {'value': 'D', 'level': 1},
    {'value': 'E', 'level': 2},
    {'value': 'F', 'level': 2},
    {'value': 'G', 'level': 0},
    {'value': 'H', 'level': 1},
    ...
]

它代表一棵树,看起来像:

<root>
|
+---A
|   |
|   +---B
|   |   |
|   |   +---C
|   |
|   +---D
|       |
|       +---E
|       |
|       +---F
+---G
    |
    +---H

这是我想要的:高效、优雅(Pythonic?)的方式来从仅具有级别(换句话说,深度)的数组构造一棵树。

这样我就可以访问树了:

root = build_tree(data) # or somewhat proper arguments

print(root.children) # => [<Node A>, <Node G>]
print(root.children[0].children) # => [<Node B>, <Node D>]
print(root.children[0].children[1].children]) # => [<Node E>, <Node F>]
print(root.children[1].children) # => [Node G]
print(root.children[1].children[0].children) # => []

我努力做一些递归函数来实现它,但突然我的大脑停止了工作。我在等你的帮助。

谢谢。

--- 已编辑 ---

这是我写的:

class TreeNode(object):
    def __init__(self, parent, value):
        self.parent = parent
        self.children = []

        self.__dict__.update(**value)

    def __repr__(self):
        return '<TreeNode %s>' % self.value

def build_tree(list, parent, start_idx=0, depth=0):
    current = TreeNode(parent, list[start_idx])
    parent.children.append(current)

    for idx in xrange(start_idx + 1, len(list)):
        if list[idx]['level'] == current.level:
            build_tree(list, parent, idx)
        elif list[idx]['level'] == current.level + 1:
            build_tree(list, current, idx)
        elif list[idx]['level'] < current.level:
            break

def print_tree(root, depth=0):
    for child in root.children:
        print('  ' * depth + '%r' % child)
        print_tree(child, depth + 1)

if __name__ == '__main__':
    data = [
        {'value': 'A', 'level': 0},
        {'value': 'B', 'level': 1},
        {'value': 'C', 'level': 2},
        {'value': 'D', 'level': 1},
        {'value': 'E', 'level': 2},
        {'value': 'F', 'level': 2},
        {'value': 'G', 'level': 0},
        {'value': 'H', 'level': 1},
    ]

    root = TreeNode(None, {'value': 'root'})
    build_tree(data, root)

    print_tree(root)

它给出了:

<TreeNode A>
  <TreeNode B>
    <TreeNode C>
    <TreeNode E>
    <TreeNode F>
    <TreeNode F>
  <TreeNode D>
    <TreeNode E>
    <TreeNode F>
    <TreeNode F>
  <TreeNode D>
    <TreeNode E>
    <TreeNode F>
    <TreeNode F>
  <TreeNode H>
<TreeNode G>
  <TreeNode H>

【问题讨论】:

  • 到目前为止你尝试过什么?展示你的作品。 SO 不是来为你编写代码的。
  • @Soviut 我已经写了一些测试代码,但是那些没有用而且非常难看。我应该发布这些代码吗?
  • 是的,发布您的尝试,以便我们可以帮助您解决问题。
  • @Soviut 我贴出来了。

标签: python algorithm tree


【解决方案1】:

代码应该很简单。你的方案意味着对孩子们有一个命令,所以我将使用list

In [8]: class Node:
   ...:     def __init__(self, val=None):
   ...:         self.value = val
   ...:         self.children = []
   ...:     def __repr__(self):
   ...:         return "<Node {}>".format(self.value)
   ...:

算法也很简单。从根开始。迭代数据。当您的深度小于"level" 节点时,继续向下移动到 最后一个附加的子节点 的子节点,尝试沿着子节点的最后一个节点向下移动。如果尝试对最后一个子节点进行索引失败,那么我们就知道我们必须在哪里(假设输入表现良好!)并且我们附加一个带有值"value" 的新节点。如果您没有失败并使其成为"level",请附加一个值为"value" 的新节点。返回根目录并在未完成对数据的迭代时重复。

In [9]: root = Node()

In [10]: for record in data:
    ...:     last = root
    ...:     for _ in range(record['level']):
    ...:         last = last.children[-1]
    ...:     last.children.append(Node(record['value']))
    ...:

现在,看看我们的树:

In [12]: root.children
Out[12]: [<Node A>, <Node G>]

In [13]: root.children[0].children
Out[13]: [<Node B>, <Node D>]

In [14]: root.children[0].children[1].children
Out[14]: [<Node E>, <Node F>]

In [15]: root.children[1].children
Out[15]: [<Node H>]

In [16]: root.children[1].children[0].children
Out[16]: []

使用方便的print_tree 函数:

In [24]: def print_tree(root, depth=0):
    ...:     for child in root.children:
    ...:         print('  ' * depth + '%r' % child)
    ...:         print_tree(child, depth + 1)
    ...:

In [25]: print_tree(root)
<Node A>
  <Node B>
    <Node C>
  <Node D>
    <Node E>
    <Node F>
<Node G>
  <Node H>

【讨论】:

  • @hallazzang 至于你的递归解决方案,请记住递归就像一个循环,但你有一个循环递归......在这种情况下你只需要一个!这就是为什么您的数据似乎翻了一番!
  • 好的,我找到了我的代码中出现重复的位置并修复了它,但你的仍然简单得多..顺便说一句,你的代码中的 try ~ except 块似乎没有抛出任何异常,因为总会有数据中的 level-0 元素。您能否解释一下您的评论# this should only occur at a terminal node! 中的“终端节点”是什么意思?
  • @hallazzang 你是对的,除了尝试之外,我没有想清楚。
  • 好吧,那么精简后的代码看起来很酷。再次感谢。您节省了我的生命和代码行数。
猜你喜欢
  • 2021-05-23
  • 1970-01-01
  • 1970-01-01
  • 2021-08-06
  • 1970-01-01
  • 1970-01-01
  • 2018-10-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多