Post Order Tree Traversal in C++ (Time Complexity: O(n))


Given a binary tree, return the postorder traversal of its nodes' values.

Example:

Input: [1,null,2,3]
   1
    \
     2
    /
   3

Output: [3,2,1]

Follow up: Recursive solution is trivial, could you do it iteratively?

Understanding the Problem

The core challenge of this problem is to traverse a binary tree in postorder (left-right-root) and return the values of the nodes in the correct order. Postorder traversal is significant in various applications such as expression tree evaluations, file system traversals, and more.

Potential pitfalls include misunderstanding the traversal order and handling null nodes incorrectly.

Approach

To solve this problem, we can consider both recursive and iterative approaches:

Recursive Approach

The recursive approach is straightforward. We define a function that recursively visits the left subtree, then the right subtree, and finally processes the root node.

Iterative Approach

The iterative approach is more complex but can be achieved using a stack. We use two stacks to simulate the postorder traversal. The first stack is used to traverse the tree, and the second stack is used to store the nodes in postorder.

Algorithm

Recursive Approach

  1. Define a function that takes a node and a result vector.
  2. If the node is null, return.
  3. Recursively call the function on the left child.
  4. Recursively call the function on the right child.
  5. Add the node's value to the result vector.

Iterative Approach

  1. Initialize two stacks: one for traversal and one for storing the postorder nodes.
  2. Push the root node onto the first stack.
  3. While the first stack is not empty:
    • Pop a node from the first stack and push it onto the second stack.
    • Push the left child of the popped node onto the first stack (if it exists).
    • Push the right child of the popped node onto the first stack (if it exists).
  4. Pop all nodes from the second stack and add their values to the result vector.

Code Implementation

Recursive Solution

// Definition for a binary tree node.
struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

class Solution {
public:
    void postorderTraversalHelper(TreeNode* node, vector<int>& result) {
        if (node == NULL) return;
        // Traverse left subtree
        postorderTraversalHelper(node->left, result);
        // Traverse right subtree
        postorderTraversalHelper(node->right, result);
        // Visit the root node
        result.push_back(node->val);
    }

    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> result;
        postorderTraversalHelper(root, result);
        return result;
    }
};

Iterative Solution

#include <vector>
#include <stack>
using namespace std;

// Definition for a binary tree node.
struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> result;
        if (root == NULL) return result;

        stack<TreeNode*> s1, s2;
        s1.push(root);

        while (!s1.empty()) {
            TreeNode* node = s1.top();
            s1.pop();
            s2.push(node);

            if (node->left) s1.push(node->left);
            if (node->right) s1.push(node->right);
        }

        while (!s2.empty()) {
            result.push_back(s2.top()->val);
            s2.pop();
        }

        return result;
    }
};

Complexity Analysis

Both the recursive and iterative solutions have a time complexity of O(n), where n is the number of nodes in the tree. This is because each node is visited exactly once.

The space complexity for the recursive solution is O(h), where h is the height of the tree, due to the call stack. For the iterative solution, the space complexity is O(n) due to the use of two stacks.

Edge Cases

Potential edge cases include:

Each algorithm handles these cases by checking if the node is null and proceeding accordingly.

Testing

To test the solution comprehensively, consider the following test cases:

Use a testing framework like Google Test for C++ to automate the testing process.

Thinking and Problem-Solving Tips

When approaching tree traversal problems, it is helpful to:

To improve problem-solving skills, solve similar problems and study various tree traversal algorithms.

Conclusion

Postorder tree traversal is a fundamental problem in computer science with various applications. Understanding both recursive and iterative solutions is crucial for tackling more complex tree-related problems. Practice and exploration of different approaches will enhance your problem-solving skills.

Additional Resources

For further reading and practice, consider the following resources: