145. Binary Tree Postorder Traversal - Explanation

Problem Link

Description

You are given the root of a binary tree, return the postorder traversal of its nodes' values.

Example 1:

Input: root = [1,2,3,4,5,6,7]

Output: [4,5,2,6,7,3,1]

Example 2:

Input: root = [1,2,3,null,4,5,null]

Output: [4,2,5,3,1]

Example 3:

Input: root = []

Output: []

Constraints:

  • 0 <= number of nodes in the tree <= 100
  • -100 <= Node.val <= 100

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



Topics

Company Tags

Please upgrade to NeetCode Pro to view company tags.



Prerequisites

Before attempting this problem, you should be comfortable with:

  • Binary Tree Structure - Understanding how nodes connect via left and right children
  • Recursion - Using recursive calls to naturally traverse tree structures
  • Stacks - Simulating recursion iteratively by explicitly managing the call stack and visit states

Intuition

Postorder traversal visits nodes in the order: left subtree, right subtree, current node. This is useful when we need to process children before their parent, such as when deleting nodes or evaluating expression trees. Recursion naturally handles this by processing both subtrees before adding the current node's value.

Algorithm

  1. Create a result list to store the node values.
  2. Define a recursive helper function that takes a node as input.
  3. If the node is null, return immediately (base case).
  4. Recursively call the function on the left child.
  5. Recursively call the function on the right child.
  6. Add the current node's value to the result list.
  7. Return the result list after traversing the entire tree.
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []

        def postorder(node):
            if not node:
                return

            postorder(node.left)
            postorder(node.right)
            res.append(node.val)

        postorder(root)
        return res

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity:
    • O(n)O(n) space for the recursion stack.
    • O(n)O(n) space for the output array.

2. Iterative Depth First Search - I

Intuition

We use a stack with a visited flag to track whether we've already processed a node's children. When we first encounter a node, we push it back with a visited flag set to true, then push its children. On the second visit (when the flag is true), we know both children have been processed, so we add the node's value to the result.

Algorithm

  1. Initialize a stack with the root node paired with a false visited flag.
  2. While the stack is not empty:
    • Pop a node and its visited flag.
    • If the node is null, continue.
    • If visited is true, add the node's value to the result.
    • Otherwise, push the node back with visited set to true, then push the right child (visited false), then the left child (visited false).
  3. Return the result list.
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        stack = [root]
        visit = [False]
        res = []

        while stack:
            cur, v = stack.pop(), visit.pop()
            if cur:
                if v:
                    res.append(cur.val)
                else:
                    stack.append(cur)
                    visit.append(True)
                    stack.append(cur.right)
                    visit.append(False)
                    stack.append(cur.left)
                    visit.append(False)

        return res

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity:
    • O(n)O(n) space for the stacks.
    • O(n)O(n) space for the output array.

3. Iterative Depth First Search - II

Intuition

Postorder is the reverse of a modified preorder traversal. If we traverse in the order: current, right, left (instead of current, left, right), and then reverse the result, we get the postorder sequence. This avoids the complexity of tracking visited nodes.

Algorithm

  1. Initialize an empty result list and an empty stack.
  2. Set the current node to the root.
  3. While the current node is not null or the stack is not empty:
    • If the current node is not null, add its value to the result, push it onto the stack, and move to the right child.
    • Otherwise, pop a node from the stack and move to its left child.
  4. Reverse the result list to get the correct postorder sequence.
  5. Return the result list.
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        stack = []
        cur = root

        while cur or stack:
            if cur:
                res.append(cur.val)
                stack.append(cur)
                cur = cur.right
            else:
                cur = stack.pop()
                cur = cur.left

        res.reverse()
        return res

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity:
    • O(n)O(n) space for the stack.
    • O(n)O(n) space for the output array.

4. Morris Traversal

Intuition

Similar to the iterative approach that reverses a modified preorder, Morris Traversal for postorder works by performing a reverse preorder traversal (current, right, left) without using extra space for a stack. We use temporary thread links from the leftmost node of the right subtree back to the current node. After the traversal, we reverse the result.

Algorithm

  1. Initialize the current node to the root.
  2. While the current node is not null:
    • If the current node has no right child, add its value to the result and move to the left child.
    • Otherwise, find the leftmost node in the right subtree.
    • If the leftmost node's left pointer is null, add the current value to the result, set the left pointer to the current node (create thread), and move to the right child.
    • If the left pointer already points to the current node, remove the thread and move to the left child.
  3. Reverse the result list to get the correct postorder sequence.
  4. Return the result list.
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        cur = root

        while cur:
            if not cur.right:
                res.append(cur.val)
                cur = cur.left
            else:
                prev = cur.right
                while prev.left and prev.left != cur:
                    prev = prev.left

                if not prev.left:
                    res.append(cur.val)
                    prev.left = cur
                    cur = cur.right
                else:
                    prev.left = None
                    cur = cur.left

        res.reverse()
        return res

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity:
    • O(1)O(1) extra space.
    • O(n)O(n) space for the output array.

Common Pitfalls

Adding Node Value Before Recursing on Children

Postorder requires visiting children before the current node. Adding the value before recursive calls produces preorder traversal instead.

# Wrong: this is preorder, not postorder
res.append(node.val)
postorder(node.left)
postorder(node.right)

Wrong Child Order in Iterative Approach

In the iterative approach using reversal, you must traverse current -> right -> left, then reverse. Traversing current -> left -> right and reversing gives incorrect results.