Python为了遍历一个平面列表
Posted
技术标签:
【中文标题】Python为了遍历一个平面列表【英文标题】:Python in order traversal to a flat list 【发布时间】:2012-03-14 11:07:33 【问题描述】:我创建了一个 TreeNode 类的方法,我想要返回一个按顺序遍历树的平面列表
我的示例树是:
按顺序遍历输出应该是:[1, 1, 0, 2, 1, 3, 1, 1, 0]
但我得到:[2, 1, 1, 0, 1, 3, 1, 1, 0]
这是我的代码:
def in_order_list(self, r = []):
hasLeft = self.left is not None
hasRight = self.right is not None
if self is None:
return
else:
if hasLeft:
self.left.in_order_list(r)
r.append(self.value)
if hasRight:
self.right.in_order_list(r)
return r
谁能告诉我为什么会这样?
谢谢 亚历克斯
【问题讨论】:
预购清单在哪里定义。图的数据结构是什么? 【参考方案1】:您不是调用self.left/right.
in
_order_list()
,而是调用self.left/right.
pre
_order_list()
。
为了完成你想做的事情,生成器函数可能比在列表中累积值更好(更少的内存消耗和更多的pythonic):
class Tree(object):
...
def in_order(self):
if self.left is not None:
for value in self.left.in_order():
yield value
yield self.value # <-- yielding the value of the node, not the node itself
if self.right is not None:
for value in self.right.in_order():
yield value
...
tree = Tree(...)
in_order_values = list(tree.in_order())
这样,如果您只想迭代值,则不必创建列表:
for value in tree.in_order():
...
算法的工作原理是这样的:生成器首先沿着每个节点的左分支递归下降,直到它遇到一个没有左子节点的分支。然后它产生当前节点的值。之后,它在正确的子节点上执行相同的操作,但从当前节点开始,而不是从根节点开始。如果我们假设树中没有循环并且没有无限分支,那么肯定会有叶子节点,即没有左子节点或右子节点的节点。 IOW 节点,两种基本情况 (self.left/right is None
) 均已达到。因此,递归调用将返回,希望在内存不足或达到堆栈限制之前返回。
self.left/right.in_order()
上的循环是必要的,因为对 in_order()
的调用返回的是生成器,因此名称为 生成器函数。返回的生成器必须以某种方式耗尽,例如通过一个循环。在循环的主体中,我们将值重新生成一个级别,然后再次重新生成,直到它们达到***别。我们在那里使用这些值。
如果您想自己检索节点而不是仅检索它们的值字段,您可以这样做:
class Tree(object):
...
def in_order(self):
if self.left is not None:
for node in self.left.in_order():
yield node
yield self # <-- yielding the node itself
if self.right is not None:
for node in self.right.in_order():
yield node
您可能想要这样做,因为您不仅可以访问节点的值:
for node in tree.in_order():
do_something_with(node.value)
但你也可以对节点做任何你想做的事情:
for node in tree.in_order():
whatever(node.some_other_attribute)
【讨论】:
左右yield
的生成器是节点的值还是节点对象?
试图了解正在发生的事情,我会说 for left
和 for right
递归调用他们 yield
node object
- 对吗?【参考方案2】:
你可以把这种东西写成一个生成器,并且避免处理列表和追加:
def in_order(tree):
if tree is None: return
for value in in_order(tree.left): yield value
yield tree.value
for value in in_order(tree.right): yield value
例如:
>>> class Node(object):
... def __init__(self, value, left=None, right=None):
... self.value, self.left, self.right = value, left, right
...
>>> x = Node(3)
>>> x.left = Node(2)
>>> x.left.left = Node(1)
>>> x.left.left.left = Node(1)
>>> x.left.left.right = Node(0)
>>> x.left.right = Node(1)
>>> x.right = Node(1)
>>> x.right.left = Node(1)
>>> x.right.right = Node(0)
>>>
>>> in_order(x)
<generator object in_order at 0xb742dbbc>
>>> list(in_order(x))
[1, 1, 0, 2, 1, 3, 1, 1, 0]
【讨论】:
感谢您使用 generator 阅读的解决方案,看起来生成器负责处理调用之间的局部变量。虽然使用 generator 很强大,但你会说它是这样的最经典的实现吗?虽然它非常优雅。谢谢【参考方案3】:r = [] 的默认参数可能有问题
例子:
def foo(list=[]):
list.append(1)
print list
foo()
>>> [1]
foo()
>>> [1,1]
Python 在多个函数调用中保持相同的列表。
试着让你的函数开始像这样:
def in_order_list(self, r=None):
if r is None:
r = []
您可能想发布其余代码,以便我们知道 pre_order 函数的作用。
【讨论】:
嗨@Matt 和@campos,你们都问我pre_order
函数的作用突出了我的错误!该函数应自称in_order_list
而不是pre_order
函数。这个方法现在有效!感谢@campos.ddc 对list
多次调用的洞察。【参考方案4】:
A) 首先如 campos.ddc 所述,将 []
赋值给 r 参数是有问题的。有关此问题的详细信息,请咨询this answer on ***(这是一个非常常见的错误)
B) 看起来你的“如果 self 是 None:”测试是多余的,如果 self 是 None,你将无法调用 in_order_list 方法(假设这是一个类中的方法,而不是独立的函数)
C) 代码可以简化:
def in_order_list(self, r = None):
if not r:
r = []
if self.left:
self.left.in_order_list(r)
r.append(self.value)
if self.right:
self.right.in_order_list(r)
D) 可能是“家庭作业”问题的问题应该这样标记。 >:(
【讨论】:
使用if r is None
而不是if not r
来测试None
ness——否则你可能会感到困惑,例如0
。 (这在这种特殊情况下无关紧要,但一般来说是一个很好的规则。)
我不同意,期望 r 是一个列表,所以它可以被评估为 False 的唯一方法是如果 A) 它是 None,或者 B) 是空的。包含值 0 的列表将评估为 True,如果 r 是值 0,则表示调用者的错误。在任何一种情况下,if not r
都比if r is None:
更简洁易读(恕我直言)
是的,但我的观点是,为了与单身人士进行比较,您应该使用is
。对我来说,if not r
立即让我想到非[]
boolean-False
值。
换句话说,if r is None
测试上面的值,所以很明显,它成功的唯一方法是不覆盖默认参数。 if not r
不会针对该值进行测试,因此您必须仔细考虑案例才能确定它是否正确。
"所以很明显,它成功的唯一方法是不覆盖默认参数" - 或者用户传入None
。我们在这里分裂头发,我真的不认为这是特别对还是错。以上是关于Python为了遍历一个平面列表的主要内容,如果未能解决你的问题,请参考以下文章