Python 使用收益率遍历树的时间复杂度是多少?

Python 使用收益率遍历树的时间复杂度是多少?,python,time-complexity,yield,Python,Time Complexity,Yield,深度优先树遍历的示例: class Node: def __init__(self, value): self._value = value self._children = [] def add_child(self, child): self._children.append(child) def __iter__(self): return iter(self._children) def

深度优先树遍历的示例:

class Node:
    def __init__(self, value):
        self._value = value
        self._children = []

    def add_child(self, child):
        self._children.append(child)

    def __iter__(self):
        return iter(self._children)

    def depth_first(self):
        yield self
        for c in self:
            yield from c.depth_first()
我知道
yield from
不会立即消耗生成器,而是将
yield
向上传递给调用方

但是这个传递的过程仍然存在,因此
yield
将从每个节点一直传递到其根节点,我们可以通过递归来描述运行时间(为了简单起见,假设它是一个二叉树,但其思想是相同的):

T(n)=2*T(n/2)+Θ(n)

Θ(n)
之所以存在,是因为该节点必须将从其子节点传递给其父节点的所有
产量。由上述公式得出的时间复杂度为:

O(非登录)

然而,如果我根本不使用
yield
yield from
,则树遍历的时间复杂度仅为
O(n)


我想知道我是否误解了
yield
的工作原理,或者像这样编写递归生成器根本不可行。

来自官方Python 3.3版本的
yield From

使用专门的语法可以进行优化 当有一长串发电机时。例如,这样的链条可能会出现 实例,递归遍历树结构时。头顶 传递next()调用,并在链上下产生值 在最坏的情况下,可能会导致本应是O(n)操作的情况发生 案例,O(n**2)

一种可能的策略是向生成器添加插槽 对象来保存要委派给的生成器。当一个下一个()或 在生成器上进行send()调用,首先检查此插槽,然后 如果它是非空的,则恢复它引用的生成器 相反如果它引发StopIteration,则插槽将被清除,主 发电机恢复运行

这将把委派开销减少到一个新的水平 不涉及Python代码执行的C函数调用链。A. 可能的增强措施是遍历整个数据链 发电机在一个回路中,并直接恢复到末端的发电机,尽管 StopIteration的处理则更为复杂


看起来,
yield from
仍然需要遍历树。但是这种遍历是由C语言的解释器而不是Python语言完成的。所以从技术上讲,这仍然是一个O(n)开销,但它并不像听起来那么糟糕

你的公式中的Θ(n)不应该是Θ(1)吗?平衡树中节点的后代数不是常数吗?@DYZ我认为应该是Θ(n)。它不传递子代的数量,但传递所有子代的
产量
语句。“所有子代”是否包括子代的子代,等等。?(我假设不是。)如果不是,它仍然是一个常数。@DYZ它确实包括子节点的子节点,因为每个节点将做两件事:(1)将自己让给调用方(2)让给它的子节点和它的子节点让给它的子节点,等等……哦,你是对的。实际上是O(n log n),因为您实际上是将遍历结果委托给根节点,而不是在本地使用它们。这是一个已解决的问题,还是他们仍在处理它?