树模型的迭代打印
我有一个像这样的节点树:
class Node:
next # the next node or None
prev # the previous node or None
parent # the parent or None
children[] # ordered list of child nodes
columns[] # a list of data. Currently only holdt the
# string representation of the node in the model.
因为我事先无法知道模型有多大,所以我得出结论,递归的方法不适合我。我想尽量减少内存中保留的节点数量。我的方法应该打印出这样的内容:
- 0
-- 0:0
--- 0:0:0
--- 0:0:1
---- 0:0:1:0
---- 0:0:1:1
--- 0:0:2
-- 0:1
- 1
但实际上它打印的是:
- 0
-- 0:0
-- 0:1
-- 0
- 1
--- 0:0:0
--- 0:0:1
--- 0:0:2
-- 0:1
-- 0
- 1
--- 0:0:1
---- 0:0:1:0
---- 0:0:1:1
--- 0:0:2
-- 0
---- 0:0:1:0
---- 0:0:1:1
--- 0:0:2
---- 0:0:1:1
---- 0:0:1:1
这是我写的代码:
def print_tree(from_path):
nodelist = []
root_node = model.get_iter(from_path)
nodelist.append((root_node, 0)) # the second item in the tuple is the indentation
while nodelist:
node = nodelist[0][0]
indent = nodelist[0][1]
del(nodelist[0])
print("{0} {1}".format("-" * indent, node.columns[0]))
if node.children:
child = node.children[0]
nodelist.append((child, indent +1))
while child.next:
nodelist.append((child.next, indent +1))
child = child.next
if node.next:
next = node.next
nodelist.append((next, indent))
任何帮助都非常感谢。
2 个回答
2
正如mgibsonbr提到的,由于你存储了一个父节点的指针,所以可以通过跟踪当前节点(以及它的缩进)来以迭代的方式完成这个任务。
def print_tree(from_path):
node, indent = model.get_iter(from_path), 0
while node:
if indent: # don't print the root
print "-" * indent, node.columns[0]
if node.children: # walk to first child before walking to next sibling
node = node.children[0]
indent += 1
elif node.next: # no children, walk to next sibling if there is one
node = node.next
else:
# no children, no more siblings: find closet ancestor w/ more siblings
# (note that this might not be the current node's immediate parent)
while node and not node.next:
node = node.parent
indent -= 1
node = node and node.next
你可以通过把print
这一行替换成yield indent, node
来将其变成一个生成器。
为了调试这个,我需要模拟一些测试数据。这是我准备的,如果其他人也想试试的话。 我把根节点当作不能有兄弟节点(其实它是可以有一个next
的,并且在columns
中存储数据,但那样的话,你的缩进就应该从1开始)。
class Node(object):
def __init__(self, parent=None, sib=None, value=""):
self.parent = parent
self.prev = sib
self.next = None
self.children = []
self.columns = [str(value)]
def child(self, value):
child = Node(self, None, value)
if self.children:
self.children[-1].next = child
child.prev = self.children[-1]
self.children.append(child)
return child
def sib(self, value):
return self.parent.child(value)
class model(object):
@staticmethod
def get_iter(_):
root = Node()
root.child("0").child("0:0").child("0:0:0").sib("0:0:1") \
.child("0:0:1:0").sib("0:0:1:0").parent.sib("0:0:2")
root.children[0].child("0:1").parent.sib("1")
return root
2
因为每个节点都有指向它父节点的链接,所以我觉得你可以在遍历整个树的时候,只用记住一个节点。对于你的代码,我有点难以理解(特别是每个节点是如何加载到内存中的),所以我会用伪代码来给出我的建议:
def visit(node,indent):
# Load your node data
print("{0} {1}".format("-" * indent, node.columns[0])) # Do something with your data
# Unload your node data
if len(node.children) > 0 :
return (node.children[0], indent+1) # Visit the first child, if there is one
while node.next is None: # If no sibling, your parent is done
node = node.parent
indent -= 1
if node is None: # Root node reached, end the traversal
return None
return (node.next, indent) # Visit your next sibling, if there is one
cursor = (root_node, 0)
while cursor is not None:
cursor = visit(*cursor)
如果节点本身需要动态加载(也就是说,next
、prev
、parent
和 children
只是包含指向其他节点数据的路径,而不是指向 Node
对象的引用),告诉我,我会更新这个回答(只需要稍微调整一下加载和卸载的位置)。当然,如果卸载只是把对象留给垃圾回收器处理,那就更简单了……