我正在尝试在Python中为不一定二进制树实现迭代器类。在使用树的根节点构造迭代器之后,可以重复调用其next()
函数以按深度优先顺序遍历树(例如,this order),最后返回None
时没有节点。
以下是树的基本Node
类:
class Node(object):
def __init__(self, title, children=None):
self.title = title
self.children = children or []
self.visited = False
def __str__(self):
return self.title
正如您在上面所看到的,我为节点引入了visited
属性,这是我的第一种方法,因为我没有看到解决方法。通过额外的状态衡量,Iterator
类看起来像这样:
class Iterator(object):
def __init__(self, root):
self.stack = []
self.current = root
def next(self):
if self.current is None:
return None
self.stack.append(self.current)
self.current.visited = True
# Root case
if len(self.stack) == 1:
return self.current
while self.stack:
self.current = self.stack[-1]
for child in self.current.children:
if not child.visited:
self.current = child
return child
self.stack.pop()
这一切都很好,但是我想摆脱对visited
属性的需求,而不需要对Node
类进行递归或任何其他更改。
我需要的所有状态都应该在迭代器中处理,但我不知道如何做到这一点。保留整个树的访问列表是不可扩展的,而且不可能,因此必须有一种聪明的方法来使用堆栈。
特别让我感到困惑的是 - 由于next()
函数当然会返回,我怎么能记住我在没有标记任何内容或使用多余存储空间的情况?直觉上,我想到循环遍历孩子,但当next()
函数返回时,该逻辑被破坏/遗忘!
更新 - 这是一个小测试:
tree = Node(
'A', [
Node('B', [
Node('C', [
Node('D')
]),
Node('E'),
]),
Node('F'),
Node('G'),
])
iter = Iterator(tree)
out = object()
while out:
out = iter.next()
print out
答案 0 :(得分:7)
如果你真的必须避免递归,这个迭代器可以工作:
from collections import deque
def node_depth_first_iter(node):
stack = deque([node])
while stack:
# Pop out the first element in the stack
node = stack.popleft()
yield node
# push children onto the front of the stack.
# Note that with a deque.extendleft, the first on in is the last
# one out, so we need to push them in reverse order.
stack.extendleft(reversed(node.children))
话虽如此,我认为你很难想到这一点。一个好的' (递归)生成器也可以解决问题:
class Node(object):
def __init__(self, title, children=None):
self.title = title
self.children = children or []
def __str__(self):
return self.title
def __iter__(self):
yield self
for child in self.children:
for node in child:
yield node
这两个都通过了你的测试:
expected = ['A', 'B', 'C', 'D', 'E', 'F', 'G']
# Test recursive generator using Node.__iter__
assert [str(n) for n in tree] == expected
# test non-recursive Iterator
assert [str(n) for n in node_depth_first_iter(tree)] == expected
如果您愿意,可以轻松地使Node.__iter__
使用非递归表单:
def __iter__(self):
return node_depth_first_iter(self)
答案 1 :(得分:0)
但是,这仍然可能包含每个标签。我想要 迭代器一次只保留树的一个子集。
但是你已经 拿着一切。请记住,对象本质上是一个字典,每个属性都有一个条目。在self.visited = False
__init__
中Node
表示您为每个"visited"
对象存储了多余的False
密钥和Node
值,无论。至少,一个集合也有可能不保存每个节点ID。试试这个:
class Iterator(object):
def __init__(self, root):
self.visited_ids = set()
...
def next(self):
...
#self.current.visited = True
self.visited_ids.add(id(self.current))
...
#if not child.visited:
if id(child) not in self.visited_ids:
查找集合中的ID应该与访问节点的属性一样快。这可能比你的解决方案更浪费的唯一方法是set对象本身(而不是它的元素)的开销,如果你有多个并发迭代器(你显然没有,否则节点{{1),这只是一个问题。 }}属性对你没用。)