Binary Tree Postorder Traversal
Question
- leetcode: Binary Tree Postorder Traversal | LeetCode OJ
- lintcode: (68) Binary Tree Postorder Traversal
Problem Statement
Given a binary tree, return the postorder traversal of its nodes' values.
Example
Given binary tree {1,#,2,3}
,
1
\
2
/
3
return [3,2,1]
.
Challenge
Can you do it without recursion?
题解1 - 递归
首先使用递归便于理解。
Python - Divide and Conquer
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# @param {TreeNode} root
# @return {integer[]}
def postorderTraversal(self, root):
if root is None:
return []
else:
return self.postorderTraversal(root.left) +\
self.postorderTraversal(root.right) + [root.val]
源码分析
递归版的太简单了,没啥好说的,注意入栈顺序。
复杂度分析
时间复杂度近似为 $$O(n)$$.
题解2 - 迭代
使用递归写后序遍历那是相当的简单,我们来个不使用递归的迭代版。整体思路仍然为「左右根」,那么怎么才能知道什么时候该访问根节点呢?问题即转化为如何保证左右子节点一定先被访问到?由于入栈之后左右节点已无法区分,因此需要区分左右子节点是否被访问过(加入到最终返回结果中)。除了有左右节点的情况,根节点也可能没有任何子节点,此时也可直接将其值加入到最终返回结果中。
Python
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# @param {TreeNode} root
# @return {integer[]}
def postorderTraversal(self, root):
result = []
if root is None:
return result
s = []
# previously traversed node
prev = None
s.append(root)
while s:
curr = s[-1]
noChild = curr.left is None and curr.right is None
childVisited = (prev is not None) and \
(curr.left == prev or curr.right == prev)
if noChild or childVisited:
result.append(curr.val)
s.pop()
prev = curr
else:
if curr.right is not None:
s.append(curr.right)
if curr.left is not None:
s.append(curr.left)
return result
# Definition for a binary tree node
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# @param root, a tree node
# @return a list of integers
def recursive_postorder(self, root, list):
if root:
self.postorder( root.left, list )
self.postorder( root.right, list )
list.append(root.val)
def iterative_postorder(self, root, list):
stack = []
pre = None
if root:
stack.append(root)
while stack:
curr = stack[len(stack) - 1]
if (curr.left == None and curr.right == None) or (pre and (pre == curr.left or pre == curr.right)):
list.append(curr.val)
stack.pop()
pre = curr
else:
if curr.right: stack.append(curr.right)
if curr.left: stack.append(curr.left)
return list
def postorderTraversal(self, root):
list = []
self.iterative_postorder(root,list)
return list
源码分析
遍历顺序为『左右根』,判断根节点是否应该从栈中剔除有两种条件,一为无子节点,二为子节点已遍历过。判断子节点是否遍历过需要排除prev == null
的情况,因为 prev 初始化为 null.
将递归写成迭代的难点在于如何在迭代中体现递归本质及边界条件的确立,可使用简单示例和纸上画出栈调用图辅助分析。
复杂度分析
最坏情况下栈内存储所有节点,空间复杂度近似为 $$O(n)$$, 每个节点遍历两次或以上,时间复杂度近似为 $$O(n)$$.
题解3 - 反转先序遍历
要想得到『左右根』的后序遍历结果,我们发现只需将『根右左』的结果转置即可,而先序遍历通常为『根左右』,故改变『左右』的顺序即可,所以如此一来后序遍历的非递归实现起来就非常简单了。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def postorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
ans = []
if root == None:
return ans
stack = [root]
while len(stack) != 0:
p = stack.pop()
ans.append(p.val)
if p.left:
stack.append(p.left)
if p.right:
stack.append(p.right)
ans.reverse()
return ans
源码分析
注意入栈的顺序和最后转置即可。
复杂度分析
同先序遍历。