Python 在到达某一深度的所有路径中查找n元树的所有唯一节点,无需递归

Python 在到达某一深度的所有路径中查找n元树的所有唯一节点,无需递归,python,recursion,tree,tree-traversal,Python,Recursion,Tree,Tree Traversal,我正在尝试用Python编写一个算法,以获得树中路径达到一定深度的所有节点的唯一列表 在遍历之前,每个子对象都有未知数量的子对象 可以通过iterable(例如B.get_children()中的child的)访问子项 例如,请参见此树(应包括的星号标记节点): 假设我正在尝试达到3的深度。我需要一个函数,它可以生成任意顺序的序列[G,H,I,J,K,D,F,B,C,a] 请注意,省略了: E(深度未达到3) L(深度超过3) 我觉得有一种方法可以递归地得到这个列表。大致如下: def

我正在尝试用Python编写一个算法,以获得树中路径达到一定深度的所有节点的唯一列表

  • 在遍历之前,每个子对象都有未知数量的子对象
  • 可以通过iterable(例如B.get_children()中的child的
    )访问子项
例如,请参见此树(应包括的星号标记节点):

假设我正在尝试达到3的深度。我需要一个函数,它可以生成任意顺序的序列
[G,H,I,J,K,D,F,B,C,a]

请注意,省略了:

  • E
    (深度未达到3)
  • L
    (深度超过3)
我觉得有一种方法可以递归地得到这个列表。大致如下:

def iterate_树(路径:List[T],所有_节点:Set[T]):
如果len(路径)==4:
对于路径中的节点:
如果节点不在所有_节点中:
所有_节点。添加(节点)
屈服点
其他:
对于路径[-1]中的节点,获取子节点():
path=path.copy()
path.append(节点)
从迭代树(路径,所有节点)生成
迭代_树([A],set())
我不知道上述方法是否有效,但我想我可以从中破解。我不喜欢(可能不正确)解决方案的地方是:

  • 递归:我写这篇文章的深度未知。我不希望堆栈溢出
  • 我真的觉得必须有一种方法可以做到这一点,而不必携带以前产生的
    节点的
    集合
  • 我必须在每次迭代时复制一个
    path
    ,这样我就不会弄乱递归的其他分支

  • 有什么建议吗?

    对于第1点,您可以使用显式堆栈和循环,而不是递归

    对于第2点,我不确定我是否看到了保留一组已生成节点的问题。内存很便宜,如果您需要检测重复项,那么每次生成时都要重新遍历树是非常昂贵的

    此外,您的实现基于节点散列性检查唯一性,但不清楚节点如何计算其散列。我假设您使用的是
    Node.val
    。如果您是基于对象引用进行散列,“唯一性”似乎毫无意义,因为您可以保证节点对象的树在标识上是唯一的。这里的例子并没有说明唯一性上的冲突会带来什么。我的实现假设散列是对象标识(应该是),并且您可以使用
    Node.val
    单独访问唯一性值

    对于第3点,如果您以递归方式工作,则无需复制路径列表,因为您可以重新访问调用帧,并且可以在单个列表上追加/弹出。以迭代方式,您可以将
    dict的父节点_与生成的
    节点_
    集合一起保留,该集合保留对每个节点父节点的引用。当我们到达所需深度的节点时,我们可以遍历此字典中的链接以重建路径,避免由于
    节点而多次访问分支。第二组,
    vals_generated
    可用于强制收益率的唯一性

    最后,我真的不知道你的数据结构是什么,所以为了a的利益,我提供了一些应该适合你的东西

    import collections
    
    def unique_nodes_on_paths_to_depth(root, depth):
        parent_of = {root: None}
        nodes_yielded = set()
        vals_yielded = set()
        stack = [(root, depth)]
    
        while stack:
            node, depth = stack.pop()
    
            if depth == 0:
                while node and node not in nodes_yielded:
                    if node.val not in vals_yielded:
                        vals_yielded.add(node.val)
                        yield node
    
                    nodes_yielded.add(node)
                    node = parent_of[node]
            elif depth > 0:
                for child in node.children:
                    parent_of[child] = node
                    stack.append((child, depth - 1))
    
    if __name__ == "__main__":
        """
               A*
               |
             -----
            |     |
            B*    C*
            |     |
            |    ---
            |   |   |
            D*  E   F*
          / | \     | \
         G* H* I*   J* K*
                       |
                       L
        """
        Node = collections.namedtuple("Node", "val children")
        root = Node("A", (
            Node("B", (
                Node("D", (
                    Node("G", ()),
                    Node("H", ()),
                    Node("I", ()),
                ))
            ,)),
            Node("C", (
                Node("E", ()),
                Node("F", (
                    Node("J", ()),
                    Node("K", (
                        Node("L", ())
                    ,)),
                )),
            ))
        ))
        print([x.val for x in unique_nodes_on_paths_to_depth(root, 3)])
        #      => ['K', 'F', 'C', 'A', 'J', 'I', 'D', 'B', 'H', 'G'] 
    
    import collections
    
    def unique_nodes_on_paths_to_depth(root, depth):
        parent_of = {root: None}
        nodes_yielded = set()
        vals_yielded = set()
        stack = [(root, depth)]
    
        while stack:
            node, depth = stack.pop()
    
            if depth == 0:
                while node and node not in nodes_yielded:
                    if node.val not in vals_yielded:
                        vals_yielded.add(node.val)
                        yield node
    
                    nodes_yielded.add(node)
                    node = parent_of[node]
            elif depth > 0:
                for child in node.children:
                    parent_of[child] = node
                    stack.append((child, depth - 1))
    
    if __name__ == "__main__":
        """
               A*
               |
             -----
            |     |
            B*    C*
            |     |
            |    ---
            |   |   |
            D*  E   F*
          / | \     | \
         G* H* I*   J* K*
                       |
                       L
        """
        Node = collections.namedtuple("Node", "val children")
        root = Node("A", (
            Node("B", (
                Node("D", (
                    Node("G", ()),
                    Node("H", ()),
                    Node("I", ()),
                ))
            ,)),
            Node("C", (
                Node("E", ()),
                Node("F", (
                    Node("J", ()),
                    Node("K", (
                        Node("L", ())
                    ,)),
                )),
            ))
        ))
        print([x.val for x in unique_nodes_on_paths_to_depth(root, 3)])
        #      => ['K', 'F', 'C', 'A', 'J', 'I', 'D', 'B', 'H', 'G']