【发布时间】:2020-01-18 13:30:41
【问题描述】:
这件事困扰了我很久。我想要一个高效、简单和 Pythonic 的二叉树(或类似的嵌套结构)的迭代器。例如像这样的用法:
for value in values(root):
do_something_with_value(value)
print(sum(values(root)))
这里root是树的根节点,节点有.value、.left和.right。 values 应该为我提供所需的迭代器/可迭代树的值。
设 n 为树中的节点数,h 为树的高度。
尝试 1:太慢了
def values(root):
if root:
yield from values(root.left)
yield root.value
yield from values(root.right)
简单、pythonic、懒惰,并且只占用 O(h) 空间。这应该是它。但是......它是堆叠的生成器,每个值都通过整个生成器堆栈产生。所以整个迭代在最坏情况下需要 O(n2) 时间,即使在最好情况下也需要 O(n log n) 时间。
尝试 2:太复杂了
def values(root):
stack = []
while root or stack:
while root:
stack.append(root)
root = root.left
root = stack.pop()
yield root.value
root = root.right
使用一堆节点进行迭代。只需要 O(h) 空间和 O(n) 时间,但是比上面的完全自然递归要复杂得多。
尝试 3:太大
def values(root):
result = []
def collect_values(root):
if root:
collect_values(root.left)
result.append(root.value)
collect_values(root.right)
collect_values(root)
return result
这会在一个半全局列表中收集所有值。自然递归和 O(n) 时间,但遗憾的是 O(n) 空间并且不是惰性的。
尝试 4:无法正常工作
我想也许我可以滥用半全局生成器,而不是半全局列表。作为一种从递归内部直接到外部的管道。递归会将send 值放入其中,外部可以获取它们。像这样的:
def values(root):
pipe = magic_generator_pipe()
def iterate(root):
if root:
iterate(root.left)
pipe.send(root.value)
iterate(root.right)
iterate(root)
yield from pipe
但我无法让它工作,我不确定它是否可能。
尝试 5:也许
threading 或 asyncio 的东西?我的另一个想法是values 函数启动一个新线程。此线程递归地迭代树并将值传递给values 函数中的主线程,这会将它们产生给原始的外部调用者。他们适当地相互锁定。但我对此没有太多经验。
问题
有没有办法实现我想要的一切?
- Pythonic 惰性迭代器
- O(n) 总时间
- O(h) 空间
- 自然递归
所以基本上我想要尝试 1 之类的东西,但速度很快。因为我用递归yield from 解决了几个问题,总是对时间复杂度感到不好。
澄清一下:“太复杂”是指迭代算法(它并不复杂,但与自然递归相比)。仅以“技术”方式“复杂”的解决方案(例如使用额外的线程或@chepner 的蹦床想法)仍然很有趣。我只是坚持自然递归(或类似算法简单的东西)和其他三个目标。
【问题讨论】:
-
这听起来可能更更复杂,但复杂的部分至少被隔离在一个可重复使用的单个部分中。将
values写为尾递归函数,然后使用蹦床(例如github.com/0x65/trampoline)实现尾调用优化。 (虽然,您希望累加器成为一个迭代器,而不是一个列表。我认为这是可能的,虽然我没有考虑过。将一堆单项链接在一起的成本(或至少很小的)迭代器可能会破坏这种方法的目的。) -
@chepner 嗯,我们可以用我的 两个 递归调用进行尾递归吗?关于这些超出目的的额外内容:我的“太复杂”实际上是指迭代 algorithm (它还不错,但与自然递归相比)。仅以“技术”方式(例如使用额外的线程或蹦床)“复杂”的解决方案仍然很有趣。我只是坚持自然递归(或类似算法简单的东西)和其他三个既定目标。
-
嗯。还有eli.thegreenplace.net/2017/…,它解释了如何使用 CPS 将斐波那契数的自然实现转换为尾递归函数,但遗憾的是并没有像我之前的链接那样提供算法的实际实现。也许如果你自己写复杂的部分...... :)
标签: python performance iterator