Given a binary tree, return the postorder traversal of its nodes' values.
For example:
Given binary tree {1,#,2,3}
,
1
\
2
/
3
return [3,2,1]
.
Note: Recursive solution is trivial, could you do it iteratively?
思路:recursion version:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<Integer>();
postorderTraversalHelper(root, list);
return list;
}
public void postorderTraversalHelper(TreeNode node, List<Integer> list) {
if(node == null) return;
postorderTraversalHelper(node.left, list);
postorderTraversalHelper(node.right, list);
list.add(node.val);
}
}
Iterative version:
思路:依照并改写pre-order travel;
Pre-order is: current, left, right;
our goal is: left, right, current.
we can do: current, right, left.
最后需要反过来记录,那么就是用LinkedList 去addFirst就可以了。
犯错点:声明的时候,需要用LinkedList<Integer> list = new LinkedList<>();
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
LinkedList<Integer> list = new LinkedList<Integer>();
if(root == null) {
return list;
}
Stack<TreeNode> stack = new Stack<TreeNode>();
stack.push(root);
// left, right, current;
// we can do preorder: current, left, right;
// we can do either: current, right, left;
// 所以如果我们逆序加result,也就是addFirst(val),那么就是 left, right, current;
while(!stack.isEmpty()) {
TreeNode node = stack.pop();
list.addFirst(node.val);
if(node.left != null) {
stack.push(node.left);
}
if(node.right != null) {
stack.push(node.right);
}
}
return list;
}
}
Morris travel: Space O(1) , Time O(n);
http://www.cnblogs.com/AnnieKim/archive/2013/06/15/morristraversal.html
后续遍历稍显复杂,需要建立一个临时节点dump,令其左孩子是root。并且还需要一个子过程,就是倒序输出某两个节点之间路径上的各个节点。
步骤:
当前节点设置为临时节点dump。
1. 如果当前节点的左孩子为空,则将其右孩子作为当前节点。
2. 如果当前节点的左孩子不为空,在当前节点的左子树中找到当前节点在中序遍历下的前驱节点。
a) 如果前驱节点的右孩子为空,将它的右孩子设置为当前节点。当前节点更新为当前节点的左孩子。
b) 如果前驱节点的右孩子为当前节点,将它的右孩子重新设为空。倒序输出从当前节点的左孩子到该前驱节点这条路径上的所有节点。当前节点更新为当前节点的右孩子。
3. 重复以上1、2直到当前节点为空。
在这里,我们需要创建一个临时的根节点dummy,把它的左孩子设为树的根root。同时还需要一个subroutine来倒序输出一条右孩子路径上的结点。跟迭代法一样我们需要维护cur指针和pre指针来追溯访问的结点。具体步骤是重复以下两步直到结点为空:
1. 如果cur指针的左孩子为空,那么cur设为其右孩子。
2. 否则,在cur的左子树中找到中序遍历下的前驱结点pre(其实就是左子树的最右结点)。接下来分两种子情况:
(1)如果pre没有右孩子,那么将他的右孩子接到cur。将cur更新为它的左孩子。
(2)如果pre的右孩子已经接到cur上了,说明这已经是回溯访问了,可以处理访问右孩子了,倒序输出cur左孩子到pre这条路径上的所有结点,并把pre的右孩子重新设为空(结点已经访问过了,还原现场)。最后将cur更新为cur的右孩子。
空间复杂度同样是O(1),而时间复杂度也还是O(n),倒序输出的过程只是加大了常数系数,并没有影响到时间的量级。如果对这个复杂度结果不是很熟悉的朋友,可以先看看Binary Tree Inorder Traversal中的分析,在那个帖子中讲得比较详细。
起始,理解了算法,代码都好写;按照图来就行了。
/**
* Definition for binary tree
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<Integer>();
if(root == null) return list;
//Morris postorder travel;
TreeNode dummy = new TreeNode(0);
dummy.left = root;
TreeNode cur = dummy;
TreeNode pre = null;
while(cur!=null){
pre = cur.left;
if(pre==null){
cur = cur.right;
}else{
while(pre.right!=null && pre.right != cur){
pre = pre.right;
}
if(pre.right == null){
pre.right = cur;
cur = cur.left;
}else{
reverse(cur.left,pre);
TreeNode node = pre;
while(node!=cur.left){
list.add(node.val);
node = node.right;
}
list.add(node.val);
reverse(pre,cur.left);
pre.right = null;
cur = cur.right;
}
}
}
return list;
}
public void reverse(TreeNode start, TreeNode end){
if(start == end){
return;
}
TreeNode pre = start;
TreeNode cur = start.right;
TreeNode next;
while(pre!=end){
next = cur.right;
cur.right = pre;
pre = cur;
cur = next;
}
}
}