帮助我理解不使用递归的有序遍历
我能够在不使用递归的情况下理解前序遍历,但我很难理解中序遍历。也许我只是似乎不明白,因为我还没有理解递归的内部工作原理。
这是我到目前为止所尝试过的:
def traverseInorder(node):
lifo = Lifo()
lifo.push(node)
while True:
if node is None:
break
if node.left is not None:
lifo.push(node.left)
node = node.left
continue
prev = node
while True:
if node is None:
break
print node.value
prev = node
node = lifo.pop()
node = prev
if node.right is not None:
lifo.push(node.right)
node = node.right
else:
break
内部 while 循环感觉不正确。此外,某些元素会被打印两次;也许我可以通过检查该节点之前是否已打印来解决这个问题,但这需要另一个变量,这又感觉不对。我哪里错了?
我还没有尝试过后序遍历,但我想它是相似的,我也会在那里面临同样的概念障碍。
感谢您抽出时间!
PS:Lifo
和Node
的定义:
class Node:
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right
class Lifo:
def __init__(self):
self.lifo = ()
def push(self, data):
self.lifo = (data, self.lifo)
def pop(self):
if len(self.lifo) == 0:
return None
ret, self.lifo = self.lifo
return ret
I am able to understand preorder traversal without using recursion, but I'm having a hard time with inorder traversal. I just don't seem to get it, perhaps, because I haven't understood the inner working of recursion.
This is what I've tried so far:
def traverseInorder(node):
lifo = Lifo()
lifo.push(node)
while True:
if node is None:
break
if node.left is not None:
lifo.push(node.left)
node = node.left
continue
prev = node
while True:
if node is None:
break
print node.value
prev = node
node = lifo.pop()
node = prev
if node.right is not None:
lifo.push(node.right)
node = node.right
else:
break
The inner while-loop just doesn't feel right. Also, some of the elements are getting printed twice; may be I can solve this by checking if that node has been printed before, but that requires another variable, which, again, doesn't feel right. Where am I going wrong?
I haven't tried postorder traversal, but I guess it's similar and I will face the same conceptual blockage there, too.
Thanks for your time!
P.S.: Definitions of Lifo
and Node
:
class Node:
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right
class Lifo:
def __init__(self):
self.lifo = ()
def push(self, data):
self.lifo = (data, self.lifo)
def pop(self):
if len(self.lifo) == 0:
return None
ret, self.lifo = self.lifo
return ret
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(15)
从递归算法开始(伪代码):
这是尾递归的一个明显例子,因此您可以轻松地将其变成 while 循环。
你只剩下一个递归调用。递归调用的作用是将新的上下文压入堆栈,从头开始运行代码,然后检索上下文并继续执行其正在执行的操作。因此,您创建一个用于存储的堆栈,以及一个循环,该循环在每次迭代时确定我们是否处于“首次运行”情况(非空节点)或“返回”情况(空节点、非空堆栈) )并运行适当的代码:
最难掌握的是“返回”部分:您必须在循环中确定正在运行的代码是处于“进入函数”情况还是处于“从调用”的情况,并且您将拥有一个
if/else
链,其中包含与代码中的非终端递归一样多的情况。在这种特定情况下,我们使用节点来保存有关情况的信息。另一种方法是将其存储在堆栈本身中(就像计算机进行递归一样)。使用这种技术,代码不太优化,但更容易遵循
Start with the recursive algorithm (pseudocode) :
This is a clear case of tail recursion, so you can easily turn it into a while-loop.
You're left with a recursive call. What the recursive call does is push a new context on the stack, run the code from the beginning, then retrieve the context and keep doing what it was doing. So, you create a stack for storage, and a loop that determines, on every iteration, whether we're in a "first run" situation (non-null node) or a "returning" situation (null node, non-empty stack) and runs the appropriate code:
The hard thing to grasp is the "return" part: you have to determine, in your loop, whether the code you're running is in the "entering the function" situation or in the "returning from a call" situation, and you will have an
if/else
chain with as many cases as you have non-terminal recursions in your code.In this specific situation, we're using the node to keep information about the situation. Another way would be to store that in the stack itself (just like a computer does for recursion). With that technique, the code is less optimal, but easier to follow
这是一个简单的有序非递归 C++ 代码..
Here is a simple in-order non-recursive c++ code ..
PS:我不懂Python,所以可能存在一些语法问题。
PS: I don't know Python so there may be a few syntax issues.
下面是在 C# (.net) 中使用堆栈进行中序遍历的示例:(
对于后序迭代,您可以参考:无递归的二叉树后序遍历)
这是一个带有访问标志的示例:
binarytreenode、listtostring实用程序的定义:
Here is a sample of in order traversal using stack in c# (.net):
(for post order iterative you may refer to: Post order traversal of binary tree without recursion)
Here is a sample with visited flag:
the definitions of the binarytreenode, listtostring utility:
无需递归的简单迭代中序遍历
Simple iterative inorder traversal without recursion
状态可以被隐式记住,
State can be remembered implicitly,
@Victor,我对您尝试将状态推入堆栈的实现有一些建议。我认为没有必要。因为从堆栈中取出的每个元素都已被左遍历。因此,我们不需要将信息存储到堆栈中,而是需要一个标志来指示要处理的下一个节点是否来自该堆栈。以下是我的实现,效果很好:
@Victor, I have some suggestion on your implementation trying to push the state into the stack. I don't see it is necessary. Because every element you take from the stack is already left traversed. so instead of store the information into the stack, all we need is a flag to indicate if the next node to be processed is from that stack or not. Following is my implementation which works fine:
对 @Emadpres 的回答进行了一些优化
Little Optimization of answer by @Emadpres
这可能会有所帮助(Java 实现)
This may be helpful (Java implementation)
这是一个迭代 C++ 解决方案,作为 @Emadpres 发布的替代方案:
Here's an iterative C++ solution as an alternative to what @Emadpres posted:
这是用于中序遍历的迭代 Python 代码::
Here is an iterative Python Code for Inorder Traversal ::
为了编写这些递归方法的迭代等价物,我们首先可以了解递归方法本身如何在程序的堆栈上执行。假设节点没有父指针,我们需要为迭代变体管理我们自己的“堆栈”。
一种开始方法是查看递归方法并标记调用将“恢复”的位置(新的初始调用,或递归调用返回后)。下面这些标记为“RP 0”、“RP 1”等(“恢复点”)。以中序遍历为例。 (我将用 C 语言进行演示,但相同的方法适用于任何通用语言):
其迭代变体:
代码注释为
(x: 0)
和(x: 1)
对应于递归方法中的“RP 0”和“RP 1”恢复点。pushed
标志帮助我们推断出这两个恢复点之一。我们不需要在“RP 2”阶段处理节点,因此我们不会将此类节点保留在堆栈上。For writing iterative equivalents of these recursive methods, we can first understand how the recursive methods themselves execute over the program's stack. Assuming that the nodes do not have their parent pointer, we need to manage our own "stack" for the iterative variants.
One way to start is to see the recursive method and mark the locations where a call would "resume" (fresh initial call, or after a recursive call returns). Below these are marked as "RP 0", "RP 1" etc ("Resume Point"). Take example of inorder traversal. (I will present in C language, but same methodology applies to any general language):
Its iterative variant:
The code comments with
(x: 0)
and(x: 1)
correspond to the "RP 0" and "RP 1" resume points in the recursive method. Thepushed
flag helps us deduce one of these two resume-points. We do not need to handle a node at its "RP 2" stage, so we do not keep such node on stack.我认为部分问题在于“prev”变量的使用。您不必存储前一个节点,您应该能够维护堆栈(Lifo)本身的状态。
从 Wikipedia 来看,您的目标算法是:
在伪代码中(免责声明,我不懂 Python,所以对下面的 Python/C++ 风格代码表示歉意!)你的算法将类似于:
对于后序遍历,你只需交换你推送的顺序左子树和右子树入栈。
I think part of the problem is the use of the "prev" variable. You shouldn't have to store the previous node you should be able to maintain the state on the stack (Lifo) itself.
From Wikipedia, the algorithm you are aiming for is:
In pseudo code (disclaimer, I don't know Python so apologies for the Python/C++ style code below!) your algorithm would be something like:
For postorder traversal you simply swap the order you push the left and right subtrees onto the stack.