【LEETCODE OJ】Binary Tree Postorder Traversal

Posted on 2014-04-03 23:36  卢泽尔  阅读(443)  评论(0)    收藏  举报

Problem Link:

http://oj.leetcode.com/problems/binary-tree-postorder-traversal/

The post-order-traversal of a binary tree is a classic problem, the recursive way to solve it is really straightforward, the pseudo-code is as follows.

 RECURSIVE-POST-ORDER-TRAVERSAL(TreeNode node)
  if node is NULL
    return
  else:
    RECURSIVE-POST-ORDER-TRAVERSAL(node.left)
    RECURSIVE-POST-ORDER-TRAVERSAL(node.right)
    visit(node)

 The python code is as follows, which is accepted by oj.leetcode.

# 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 postorderTraversal(self, root):
        """
        Best way is to DFS the tree in the iterative way by using stack.
        However, we first try the recursive version
        """
        self.res = []
        self.recursiveTraversal(root)
        return self.res
    
    def recursiveTraversal(self, node):
        if node is None:
            return
        else:
            self.recursiveTraversal(node.left)
            self.recursiveTraversal(node.right)
            self.res.append(node.val)

However, the best way should be iterative. Among pre-order, in-order, and post-order traversals, I think interative post-order traversal is most hard. We need to concern following issues.

1. A child node cannot be put into stack before its parent. This means, we can ONLY put a node while we are going down the tree, and we can ONLY pop a node while we are going up the tree.

2. We need some way to denote we are traversing down or up currently

In practice, I use two pointers corresponding to the concerns above:

go_down: go_down = NULL means we are going down from children to parent, then we need to check if we visit the node or traverse its non-visited sub-tree.

prev_visited: keep track the node just visited which help us to identify the node should be visited or not.

For each iteration, we may be going down or up. In the case of GoDown (go_down != NULL), we just go go_down.left until we touch the bottom of the tree (go_down == NULL), then we begin GoUP. In the case of GoUp (go_down == NULL), we check the top node in the stack (lets say parent). In this case, we are traversing back from the sub-tree of the node parent, and we can guarantee that at least left sub-tree of parent is already visited. So we have three cases according to parent.child: 1) parent.child == NULL, parent has no right sub-tree; 2) parent.child == prev_visited, we just visited parent's right child, which means the right sub-tree of parent is also visited; 3) parent.child != NULL or prev_visited, the right sub-tree is not traversed yet. For case 1) and 2), we just visit the parent (and pop it from the stack) and keep GoUp, for case 3) we need to swith to GoDown case for the right sub-tree (just set go_down = parent.right). The pseudocode is as follows.

 ITERATIVE-POST-ORDER-TRAVERSAL(Node root)
  go_down = root
  prev_visited = NULL
  non_visited_parent = Stack()
  while (non_visited_parent is not empty) or (go_down is not NULL)
    if go_down
      non_visited_parent.put(go_down)
      go_down = go_down.left
    else
      parent = non_visited_parent.top() // Get the last non-visited parent without pop
      if (parent.right is NULL) or (prev_visited == parent.right)
        VISIT(parent)
        non_visited_parent.pop()
      else
        go_down = parent.right

I wrote the solution in OJ.leetcode in python, however LTE again! WTF... I guess the problem may be the stack implementation in python.

class Solution:
    # @param root, a tree node
    # @return a list of integers
    def postorderTraversal(self, root):
        """
        DFS the tree in the iterative way by using stack.
        The stack stores the parent node in the GoDown path 
        p: tell us we are traversing down or up
        prev_visited: keep track the node just visited
        We start from p=root, for each step, there are two cases:
         - GoDown (p != NULL). This means p is still a node (even a leaf)
         - GoUp (p == NULL). This means we touch the bottom of the tree.
        [GoUp] Note that we only VISIT node in GoUp case (p==NULL).
        In this case, we get check the top node in the parent_stack,
        and check its right child (we do not need to check left child since
        p == NULL means we are traversing back and the left sub-tree is visited):
         - If the right child is NULL, then VISIT the node and pop it, and keep
           GoUp (keep p=NULL and check the top of parent_stack)
         - If the right child is just visited, which means we traverse back
           from the right sub-tree, also keep GoUp.
         - Otherwise, the right sub-tree is not traversed before, then we need
           to set p == right child, start GoDown of the right sub-tree
        [GoDown] We repeat going left until we touch the bottom of the tree
        """
        res = []
        p = root  # p != None, means we need to go down
        prev_visited = None  # No node is visited yet
        parent_stack = []  # I use the build-in data structure list as the stack
        while parent_stack or p is not None:
            if p is not None:  # GoDown case
                parent_stack.append(p)
                p = p.left
            else:  # GoUp case
                last_not_visited_parent = parent_stack[-1]
                if last_not_visited_parent.right is None or last_not_visited_parent.right == prev_visited:
                    # In these two cases, we need visit the parent and keep going up
                    res.append(last_not_visited_parent.val)
                    prev_node = last_not_visited_parent
                    parent_stack.pop()
                else:
                    # In this case, we need change to go down for the right sub-tree
                    p = last_not_visited_parent.right
        return res

 Also, I also post C++ version here, which is accepted by OJ.leetcode.

/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
#include <stack>

class Solution {
public:
    vector<int> postorderTraversal(TreeNode *root) {
        vector<int> res;
        std::stack<TreeNode*> non_visited;
        TreeNode * parent = NULL;
        TreeNode * go_down = root;
        TreeNode * just_visited = NULL;
        while (!non_visited.empty() || go_down != NULL) {
            if (go_down != NULL) {
                non_visited.push(go_down);
                go_down = go_down->left;
            }
            else {
                parent = non_visited.top();
                if (parent->right == NULL || parent->right == just_visited) {
                    res.push_back(parent->val);
                    just_visited = parent;
                    non_visited.pop();
                }
                else {
                    go_down = parent->right;
                }
            }
        }
        return res;
    }
};