【问题标题】:Counting internal (non-leaf) nodes in a tree starting from any node从任何节点开始计算树中的内部(非叶)节点
【发布时间】:2020-07-28 00:56:12
【问题描述】:

这是我的一棵树。我想从树中的任何节点开始计算有多少内部节点(非叶节点)。

我现在拥有的函数只能计算 A 以下的所有值,即 10。但我只想要终端节点之前的那些。

  • 例如。对于 A,应该是 4 (B, C, E, H)
  • 例如。对于 E,它应该是 1 (H)

如何修改我的函数来做到这一点?请注意,我的数据非常大,因此递归函数会导致堆栈溢出。

  name employee
0   A   B
1   A   C
2   B   D
3   C   E
4   C   F
5   E   H
6   E   I
7   H   T
8   H   U
9   H   V

#collapse to dictionary
dict_a = {k: g["employee"].tolist() for k,g in em.groupby("name")}
dict_a

{'A': ['B', 'C'],
 'B': ['D'],
 'C': ['E', 'F'],
 'E': ['H', 'I'],
 'H': ['T', 'U', 'V']}
    
# recursive function to count lengths:
def total(k,connections):
    if k not in connections:
        return 0
    
    # number of direct connections plus their connections:
    return len(connections[k]) + sum(total(child_k, connections) for child_k in connections[k])

【问题讨论】:

    标签: python dictionary tree


    【解决方案1】:

    获取内部节点意味着运行您的遍历并且只产生具有至少一个孩子的节点。

    调用者可以灵活地将生成器转换为列表并根据需要获取长度,或者由于函数是通用的而简单地迭代(特定于计数的函数似乎不必要地狭窄,但如果您需要它,它是一个简单的包装器在下面的函数上)。

    def interior_nodes_from(tree, node):
        if node in tree:
            for child in tree[node]:
                if child in tree:
                    yield child
                    yield from interior_nodes_from(tree, child)
    
    if __name__ == '__main__':
        tree = {'A': ['B', 'C'],
                'B': ['D'],
                'C': ['E', 'F'],
                'E': ['H', 'I'],
                'H': ['T', 'U', 'V']}
        
        for target in 'ABCDEFGH':
            interior_nodes = list(interior_nodes_from(tree, target))
            print(f'{target} => {len(interior_nodes)} children, {interior_nodes}')
    

    输出:

    A => 4 children, ['B', 'C', 'E', 'H']
    B => 0 children, []
    C => 2 children, ['E', 'H']
    D => 0 children, []
    E => 1 children, ['H']
    F => 0 children, []
    G => 0 children, []
    H => 0 children, []
    

    如果您的树深度超过调用堆栈大小,您可以使用显式堆栈而不是递归:

    def interior_nodes_from(tree, node):
        if node in tree:
            stack = [node]
            
            while stack:
                for child in tree[stack.pop()]:
                    if child in tree:
                        yield child
                        stack.append(child)
    

    【讨论】:

    • 非常感谢!这解决了一切!在我实施之前,我会阅读有关产量的信息,因为它对我来说是新的:)
    • 我遇到了一个新问题。我的实际数据是巨大的,它进入了一个递归错误。
    • 有没有办法不用递归?
    • 当然,添加,但请确保您的数据没有循环(即是一个有效的树)。循环将使其成为有向循环图,这也会导致堆栈溢出而没有访问集以避免重新遍历节点。
    • 另外,根据经验,最好不要编辑您的帖子以完全改变问题。这正在移动球门柱,并且很难干净地回答您的问题。我将您的帖子回滚到原来的位置,但需要注意的是应该避免递归错误。如果您需要针对答案中提供的代码提出特定问题,请接受答案并打开一个新问题。谢谢。
    【解决方案2】:

    我在递归函数中建议的一种解决方案是:

    1. 检查参数k是否存在连接,否则return 1

    2. 将每个total(ck) 的总和存储在函数内部的一个变量中,其中 ck 是每个子项的键(for 循环)。

    3. 检查 sum == dict_a[k],如果是这种情况,那么那个节点就是你想要的,把它添加到终端节点列表中。

      #终端节点 术语 = []

      #recursive 计算长度的函数: 默认总(k): 如果 k 不在 dict_a 中: 返回 1

       # Number of child nodes.
       nc = 0;
       nc += total(child_k) for child_k in dict_a[k])
       if nc == len(dict_a[k]):
           termn.append(k)
      

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-06-10
      • 1970-01-01
      • 1970-01-01
      • 2014-08-12
      • 1970-01-01
      • 2017-05-01
      • 1970-01-01
      相关资源
      最近更新 更多