转载请注明出处:http://blog.csdn.net/crazy1235/article/details/51494797
出处:https://leetcode.com/problems/binary-tree-postorder-traversal/
Hard 级别
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].
求二叉树的后续遍历。
递归方式
private List resultList = new ArrayList();
/**
* 递归方式
* 1ms
*
* @param root
* @return
*/
public List postorderTraversal(TreeNode root) {
postOrder(root);
return resultList;
}
private void postOrder(TreeNode node) {
if (node == null) {
return;
}
if (node.left != null) {
postOrder(node.left);
}
if (node.right != null) {
postOrder(node.right);
}
resultList.add(node.val);
}
LeetCode上Run Time 是 1ms。
Your runtime beats 70.01% of java submissions.
非递归方式的思想就是需要记录访问的结点是否是第一次访问的。因为当前结点需要在左右子数访问之后才能输出。
所以需要标志来记录当前结点是否是第一次访问。当第二次访问的时候才输出。
/**
* 后续遍历非递归方法
* 根据中序非递归方式改造而来
* @author jacksen
* @param node
*/
public List<Integer> postorderTraversal1(TreeNode node) {
if (node == null) {
return resultList;
}
Stack<WrapperNode> stack = new Stack<>();
WrapperNode wrapperNode = new WrapperNode(node, false);
stack.push(wrapperNode);
while (!stack.isEmpty()) {
while ((wrapperNode = stack.peek()).node != null) {
wrapperNode = new WrapperNode(wrapperNode.node.left, false);
stack.push(wrapperNode);
}
//
stack.pop();
//
wrapperNode = stack.peek();
while (wrapperNode.node != null) {
if (wrapperNode.isFinished) {
resultList.add(stack.pop().node.val);
if (stack.isEmpty()) {
return resultList;
}
wrapperNode = stack.peek();
} else {
wrapperNode.isFinished = true;
wrapperNode = new WrapperNode(wrapperNode.node.right, false);
stack.push(wrapperNode);
break;
}
}
}
return resultList;
}
/**
* TreeNode包装类
*
* @author jacksen
*/
private static class WrapperNode {
TreeNode node;
boolean isFinished;
public WrapperNode(TreeNode node, boolean isFinished) {
this.node = node;
this.isFinished = isFinished;
}
}
先从根节点往左,把最左侧的一条分支入栈。
最左侧走到叶子结点之后,去添加它的右子数。并将当前结点访问标志改变。判断访问标志来pop结点。
solution 2的非递归方式,是先访问左子树。
其实可以从根结点,然后访问右子树。参照 : http://www.cnblogs.com/Antech/p/3655594.html
/**
* 非递归方式
* 3ms
* http://www.cnblogs.com/Antech/p/3655594.html
*
* @param root
* @return
*/
private List postorderTraversal2(TreeNode root) {
if (root == null) {
return resultList;
}
Stack stack = new Stack<>();
stack.push(new WrapperNode(root, false));
WrapperNode wrapperNode = null;
while (!stack.isEmpty()) {
wrapperNode = stack.peek();
if (wrapperNode.node == null) {
stack.pop();
continue;
}
if (wrapperNode.isFinished) {
resultList.add(stack.pop().node.val);
} else {
stack.push(new WrapperNode(wrapperNode.node.right, false));
stack.push(new WrapperNode(wrapperNode.node.left, false));
wrapperNode.isFinished = true;
}
}
return resultList;
}
在solution 3的基础上进行改进。
判断左右子节点是否为null,进而少一次while判断,也少入栈出栈操作。
/**
* 方法二非递归方式的改进
* 3ms
*
* @param root
* @return
*/
private List postorderTraversal3(TreeNode root) {
if (root == null) {
return resultList;
}
Stack stack = new Stack<>();
stack.push(new WrapperNode(root, false));
WrapperNode wrapperNode = null;
while (!stack.isEmpty()) {
wrapperNode = stack.peek();
if (wrapperNode.isFinished) {
resultList.add(stack.pop().node.val);
} else {
wrapperNode.isFinished = true;
if (wrapperNode.node.right != null) {
stack.push(new WrapperNode(wrapperNode.node.right, false));
}
if (wrapperNode.node.left != null) {
stack.push(new WrapperNode(wrapperNode.node.left, false));
}
}
}
return resultList;
}
接下来介绍一个牛逼的方法。
该方法使用两个栈进行处理,代码看上去简单易懂。
/**
* 使用两个栈
* 2ms
*
* https://leetcode.com/discuss/102843/my-ac-java-solution-using-two-stacks
*
* @param root
* @return
*/
private List postorderTraversal4(TreeNode root) {
if (root == null) {
return resultList;
}
Stack stack1 = new Stack<>();
Stack stack2 = new Stack<>();
stack1.push(root);
TreeNode tempNode = null;
while (!stack1.isEmpty()) {
tempNode = stack1.pop();
if (tempNode.left != null) {
stack1.push(tempNode.left);
}
if (tempNode.right != null) {
stack1.push(tempNode.right);
}
stack2.push(tempNode);
}
while (!stack2.isEmpty()) {
resultList.add(stack2.pop().val);
}
return resultList;
}
对该方法仔细研究,就会发现stack2里面存储的就是后续遍历输出的整个顺序。
后序遍历就是【左右根】的形式输出。
所以stack1中先入根然后pop出来,然后入左和右。这样stack1先pop根,然后pop右,接着pop左;
正好,stack2先入根,然后入右,接着入左。故,stack2 循环pop的时候就是【左右根】的顺序。
其实,仔细想想,先序输出的时候是【根左右】,后序输出是【左右根】,
所以只需要按照先序遍历非递归的方法,输出的时候改改顺序即可。
/**
* https://leetcode.com/discuss/71943/preorder-inorder-and-postorder-
* iteratively-summarization
*
* LinkedList
*
* @param root
* @return
*/
public List postorderTraversal5(TreeNode root) {
LinkedList list = new LinkedList<>();
if (root == null) {
return list;
}
Stack stack = new Stack<>();
TreeNode tempNode = root;
while (!stack.isEmpty() || tempNode != null) {
if (tempNode != null) {
stack.push(tempNode);
list.addFirst(tempNode.val);
tempNode = tempNode.right;
} else {
tempNode = stack.pop().left;
}
}
return list;
}
该方法使用LinkedList
每次都调用addFirst()方法,将结点添加到头部。
所以顺序就是【左】【右】【根】。
根据solution 6的思想和solution 7的方式,可以将先序遍历非递归方式简单修改一下即可。
Binary Tree Inorder Traversal 非递归方法
/**
* 非递归方式
*
* 参考 Pre order 的preorderTraversal2
*
* @author jacksen
*
* @param root
* @return
*/
public List<Integer> postorderTraversal6(TreeNode root) {
LinkedList<Integer> list = new LinkedList<>();
if (root == null) {
return list;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
TreeNode tempNode = null;
while (!stack.isEmpty()) {
tempNode = stack.pop();
list.addFirst(tempNode.val);
if (tempNode.left != null) {
stack.push(tempNode.left);
}
if (tempNode.right != null) {
stack.push(tempNode.right);
}
}
return list;
}
该方法使用一个栈加上一个单链表。
OK! 介绍完毕~
欢迎留言,批评指正~~~