二叉树非递归中序遍历
非递归中序遍历的三种写法和前序遍历(见前一篇)非常相似,区别在于访问根节点的时间不同。注意,似乎前序遍历的方法1.2无法使用。
1. Using 1 stack
O(n) time, O(n) space
初始设置栈为空,当前节点为根节点
执行以下操作:
- 当前节点入栈,将左子节点设为当前节点,直到当前节点为空。
- 弹出栈顶并访问,将当前节点设置为弹出节点的右子节点。
直到栈为空且当前节点为空时停止
stack = []
node = root
while stack or node:
if node:
stack.append(node)
node = node.left
else:
node = stack.pop()
print(node.val)
node = node.right
2. Using 2 stacks
O(n) time, O(n) space
stack: 用于储存和遍历子节点,初始化=[root]
path: 记录从根节点到当前节点的路径
在前序遍历的基础上进行了修改,为了满足根节点在左右孩子之间,需要出栈再入栈
执行以下操作,直到stack为空:
- node = stack.pop()
- 如果path栈顶元素不是node:
– node入栈path
– 右孩子,当前节点,左孩子(如果非空),依次入栈stack - 如果stack和path栈顶元素相同:
– path弹出栈顶
– 访问node
stack = [root]
path = []
while stack:
node = stack.pop()
if path and path[-1] == node:
path.pop()
print(node.val)
else:
path.append(node)
if node.right: stack.append(node.right)
stack.append(node)
if node.left: stack.append(node.left)
3. Using no stacks (Morris traversal)
O(n) time, O(1) space
初始设置node = root
进行以下操作直到node为空:
- 如果当前节点node的左孩子非空,找到中序遍历的前驱节点,左孩子的最右后代
– 如果前驱节点的右孩子为空,将前驱节点的右孩子设置为当前节点。更新 node = node.left
– 如果前驱节点的右孩子非空(则为当前节点),访问当前节点,将右孩子重新设置为空,恢复树的形状。更新 node = node.right - 如果node的左孩子为空,则访问当前节点,更新 node = node.right
node = root
while node:
if node.left:
prev = node.left
while prev.right and prev.right != node:
prev = prev.right
if not prev.right:
prev.right = node
node = node.left
else:
print(node.val)
prev.right = None
node = node.right
else:
print(node.val)
node = node.right
参考资料:
https://leetcode.com/problems/binary-tree-inorder-traversal/discuss/31313/three-ways-of-iterative-inorder-traversing-easy-explanation
https://www.cnblogs.com/anniekim/archive/2013/06/15/morristraversal.html
https://blog.csdn.net/My_Jobs/article/details/43451187