Postorder Traversal

Hero Image

DT

Dhaval Trivedi

Co-founder, Airtribe

Problem Overview

In the LeetCode problem "Binary Tree Postorder Traversal," we are tasked with traversing a given binary tree and returning the elements in postorder. Postorder traversal implies that for a given node in the tree, we first visit its left subtree, then its right subtree, and finally the node itself. This task is important for several computational processes, such as deleting a tree or evaluating certain mathematical expressions encoded in the tree structure.

The problem statement on LeetCode can be found here.

Understanding the Problem

The key aspect of this problem is understanding the concept of postorder traversal in a binary tree. In a binary tree, each node has at most two children, labeled as left and right. Postorder traversal is one of the depth-first traversal methods, where the nodes are recursively visited in the following order:

  1. Left child
  2. Right child
  3. Root node (current node)

Thus, the steps for a binary tree postorder traversal can be outlined as follows:

  • Traverse the left subtree.
  • Traverse the right subtree.
  • Visit the current node.

The challenge is to implement this traversal recursively and iteratively, ensuring a correct output of node values in postorder.

Key DSA Concepts and Theory

Binary Tree

A binary tree is a tree data structure where each node has at most two children. It's a hierarchical data structure crucial in data systems because of its dynamic node insertion and deletion capabilities.

Depth-First Search (DFS)

Postorder traversal is a type of DFS method, exploring as far as possible along each branch before backtracking. DFS has three variants based on the order of root node processing relative to its children: Preorder, Inorder, and Postorder.

Recursive Method

The recursive method leverages the call stack to remember nodes' states, processing leaves before roots.

Iterative Method with Stack

This approach simulates the system's call stack using an explicit stack, enabling a traversal that doesn't depend on recursion but still follows DFS principles.

Solution Approach

Recursive Solution

In the recursive approach, the function calls itself with the left and right children until it hits a null pointer. Once it starts returning back up, it processes the current node. Here's the recursive solution in C++ and Java:

C++ Code:

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

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

Java Code:

class Solution {
    public void postorder(TreeNode node, List<Integer> result) {
        if (node == null) return;
        postorder(node.left, result);   // Traverse left subtree
        postorder(node.right, result);  // Traverse right subtree
        result.add(node.val);           // Visit node
    }

    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        postorder(root, result);
        return result;
    }
}

Iterative Solution

The iterative solution uses a stack to simulate recursion by keeping track of nodes yet to be visited.

C++ Code:

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> result;
        if (root == nullptr) return result;
        
        stack<TreeNode*> st;
        TreeNode* current = root;
        TreeNode* lastVisited = nullptr;

        while (current != nullptr || !st.empty()) {
            if (current != nullptr) {
                st.push(current);
                current = current->left;
            } else {
                TreeNode* peekNode = st.top();
                // If right child exists and traversing node from left child, move to right child
                if (peekNode->right != nullptr && lastVisited != peekNode->right) {
                    current = peekNode->right;
                } else {
                    result.push_back(peekNode->val);
                    lastVisited = st.top();
                    st.pop();
                }
            }
        }
        return result;
    }
};

Java Code:

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        LinkedList<Integer> result = new LinkedList<>();
        if (root == null) return result;

        Stack<TreeNode> stack = new Stack<>();
        TreeNode current = root;

        while (current != null || !stack.isEmpty()) {
            if (current != null) {
                stack.push(current);
                result.addFirst(current.val); // Reverse the process of adding
                current = current.right;      // Essential to tackle right subtree
            } else {
                TreeNode node = stack.pop();
                current = node.left;          // Tracing back to left node
            }
        }
        return result;
    }
}

Time and Space Complexity Analysis

  • Time Complexity: Both the recursive and iterative solutions have a time complexity of O(n), where n is the number of nodes in the binary tree. This complexity arises because each node is processed once.

  • Space Complexity:

    • Recursive Solution: The space complexity is O(h), where h is the height of the tree due to the recursive call stack.
    • Iterative Solution: The space complexity is O(h) to store the stack contents, where h denotes the height of the tree.

For a balanced binary tree, O(h) = O(log n) whereas for a skewed tree, O(h) = O(n).

Common Mistakes to Avoid

  • Ignoring Base Case: Always consider null nodes in traversal or the function could make unnecessary recursive calls or access invalid memory areas.
  • Wrong Order of Operations: Ensure postorder is left, right, root; otherwise, results will be incorrect.

Similar Problems on LeetCode

  • Preorder Traversal - Question No. 144
  • Inorder Traversal - Question No. 94
  • Binary Tree Level Order Traversal - Question No. 102
  • Binary Tree Zigzag Level Order Traversal - Question No. 103

Additional Resources and References

This article should provide a comprehensive understanding of solving the Binary Tree Postorder Traversal problem while grasping the core concept of binary tree traversal methods.