简体   繁体   English

在不递归的情况下,在所有到达某个深度的路径中查找 n 叉树的所有唯一节点

[英]Find all unique nodes of a n-ary tree in all paths that reach a certain depth without recursion

I'm trying to write an algorithm in Python to get a unique list of all nodes in a tree where the path reaches a certain depth.我正在尝试用 Python 编写一个算法,以获取路径达到特定深度的树中所有节点的唯一列表。

  • Each child has an unknown number of children prior to traversal每个孩子在遍历之前都有未知数量的孩子
  • The children can be accessed via an iterable (eg for child in B.get_children() )可以通过迭代访问for child in B.get_children() (例如, for child in B.get_children()

For example, see this tree (asterisks mark node that should be included):例如,请参阅此树(星号标记应包含的节点):

       A*
       |
     -----
    |     |
    B*    C*
    |     |
    |    ---
    |   |   |
    D*  E   F*
  / | \     | \
 G* H* I*   J* K*
               |
               L

Let's say I'm trying to reach a depth of 3. I need a function that would yield the sequence [G, H, I, J, K, D, F, B, C, A] in any order.假设我试图达到 3 的深度。我需要一个函数,它可以按任何顺序生成序列[G, H, I, J, K, D, F, B, C, A]

Note the omission of:注意省略:

  • E (doesn't reach depth of 3) E (未达到 3 的深度)
  • L (exceeds depth of 3) L (超过深度 3)

I feel there is a way to get this list recursively.我觉得有一种方法可以递归地获取这个列表。 Something along the lines of:类似的东西:

def iterate_tree(path: List[T], all_nodes: Set[T]):
    if len(path) == 4:
        for node in path:
            if node not in all_nodes:
                all_nodes.add(node)
                yield node
    else:
        for node in path[-1].get_children():
            path = path.copy()
            path.append(node)
            yield from iterate_tree(path, all_nodes)

iterate_tree([A] ,set())

I don't know if the above works, but I think I can hack it from that.我不知道以上是否有效,但我想我可以从中破解它。 What I don't like about the (probably incorrect) solution is:我不喜欢(可能不正确)解决方案的是:

  1. The recursion: I'm writing this for an unknown depth.递归:我正在为一个未知的深度写这篇文章。 I don't want a stack-overflow.我不想要堆栈溢出。
  2. I really feel like there must be a way to do this without carrying around a set of previously yielded nodes.我真的觉得必须有一种方法可以在不携带一set先前yielded节点的情况下做到这一点。
  3. I have to make a copy of path at each iteration so I don't mess up other branches of the recursion.我必须在每次迭代时复制path ,这样我就不会弄乱递归的其他分支。

Any suggestions?有什么建议?

For point 1, you can use an explicit stack and loop instead of recursion.对于第 1 点,您可以使用显式堆栈和循环而不是递归。

For point 2, I'm not sure I see a problem with keeping a set of yielded nodes.对于第 2 点,我不确定保留一组产生的节点是否有问题。 Memory is cheap and if you need to detect duplicates, re-traversing the tree every time you yield is extremely expensive.内存很便宜,如果您需要检测重复项,每次让出时重新遍历树是非常昂贵的。

Furthermore, your implementation checks for uniqueness based on node hashability, but it's unclear how nodes compute their hash.此外,您的实现基于节点散列性检查唯一性,但不清楚节点如何计算它们的散列。 I assume you're using Node.val for that.我假设您Node.val使用Node.val If you're hashing based on object reference, "uniqueness" seems pointless since you're guaranteed that a tree of Node objects is unique by identity.如果您基于对象引用进行散列,那么“唯一性”似乎毫无意义,因为您可以保证 Node 对象树的身份是唯一的。 The example here doesn't show what a clash on uniqueness would entail.此处的示例并未显示对唯一性的冲突会带来什么。 My implementation assumes the hash is object identity (as it should be) and that you can access the value for uniqueness separately using Node.val .我的实现假设哈希是对象标识(应该是),并且您可以使用Node.val单独访问唯一性值。

For point 3, if you're working recursively there's no need to copy the path list since you revisit the call frame and can append/pop on a single list.对于第 3 点,如果您以递归方式工作,则无需复制路径列表,因为您重新访问调用框架并且可以在单个列表上附加/弹出。 Iteratively, you can keep a parent_of dict alongside the nodes_yielded set that keeps a reference to the parent of each node.迭代地,您可以在nodes_yielded集合旁边保留一个parent_of dict,该集合保留对每个节点的父节点的引用。 When we reach a node at the desired depth, we can walk the links in this dictionary to reconstruct the path, avoiding revisiting a branch more than once thanks to nodes_yielded .当我们到达所需深度的节点时,我们可以遍历字典中的链接以重建路径,避免多次重新访问分支,这要归功于nodes_yielded A second set, vals_yielded can be used to enforce uniqueness on the yields.第二组vals_yielded可用于强制执行产量的唯一性。

Lastly, I don't really know what your data structures are so in the interest of a minimal, complete example , I've provided something that should be adaptable for you.最后,我真的不知道你的数据结构是什么,所以为了一个最小的、完整的例子,我提供了一些应该适合你的东西。

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'] 

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

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