低效的部分不是遍历树,而是建立节点列表。创建这样的列表似乎是明智的:
descendants[node] = []
for child in node.childs:
descendants[node].push(child)
for d in descendants[child]:
descendants[node].push(d)
由于每个后代节点都被复制到每个父节点的列表中,因此对于平衡树,平均而言,我们的复杂度为O(n log n),而对于实际上是链表的退化树,则为O(n²)最坏的情况。
如果我们使用延迟计算列表的技巧,则取决于是否需要进行任何设置,我们可以降至O(n)或O(1)。假设我们有一个child_iterator(node)
给我们该节点的子节点。然后,我们可以descendant_iterator(node)
像下面这样简单地定义一个:
def descendant_iterator(node):
for child in child_iterator(node):
yield from descendant_iterator(child)
yield node
由于迭代器控制流程很棘手(协程!),因此涉及到非递归解决方案更多。我将在今天晚些时候更新此答案。
由于树的遍历为O(n),并且列表的迭代也是线性的,因此该技巧完全推迟了成本,直到无论如何都要付清它。例如,打印出每个节点的后代列表具有O(n²)最坏情况的复杂性:遍历所有节点都是O(n),因此遍历每个节点的后代,无论它们存储在列表中还是临时计算。
当然,如果您需要实际的收藏夹来处理,这将不起作用。