Problem
Given the root of a binary tree, visit every node and collect the values in postorder: fully process the left subtree, then the right subtree, then the root. The root of any subtree is always the last node reported from that subtree.
[1,2,3,4,5]
- Input: root = [1, 2, 3, 4, 5]
- Output: [4, 5, 2, 3, 1]
- Explanation: left subtree yields 4 โ 5 โ 2, right subtree yields 3, root yields 1 last.
Compare with the single-right-child tree below โ postorder here is [3, 2, 1] because node 3 (deepest left child of 2) finishes before 2 finishes before 1.
[1,null,2,null,null,3]
Intuition
Postorder is the natural order for "bottom-up" processing โ a node's result depends on its children being done first. Recursion expresses this directly: define the traversal of any subtree as (left subtree) + (right subtree) + (root value), and the call stack handles the ordering automatically with no extra bookkeeping.
Solution โ Recursive DFS
Recursively traverse the left subtree, then the right subtree, then append the current node's value. The base case returns immediately on a null node.
- If the current node is null, return โ this handles both empty trees and leaf boundaries.
- Recurse into the left child.
- Recurse into the right child.
- Append the current node's value to the result list.
- Return the accumulated list after the initial call returns.
1def postorderTraversal(root: "TreeNode") -> list[int]:
2 result = []
3
4 def traverse(node: "TreeNode") -> None:
5 if not node:
6 return
7 traverse(node.left) # finish entire left subtree first
8 traverse(node.right) # then entire right subtree
9 result.append(node.val) # root of this subtree is always last
10
11 traverse(root)
12 return resultTime: O(n) โ every node is visited exactly once. Space: O(h) โ the call stack reaches the height of the tree; O(log n) for a balanced tree, O(n) for a skewed one.
Complexity Summary
| Approach | Time | Space | When to use |
|---|---|---|---|
| Recursive DFS | O(n) | O(h) | All practical cases; clean and direct expression of the algorithm |
Common Mistakes
- Appending the node value before recursing โ that produces preorder (root โ left โ right), not postorder. The append must come after both recursive calls return.
- Confusing the three traversal orders โ postorder is left โ right โ root; inorder is left โ root โ right; preorder is root โ left โ right. Postorder is the only one where the root is always last.
- Missing the null base case โ without
if not node: return, the recursion crashes on null children of leaf nodes. This is the single most common runtime error on tree problems. - Returning the inner function's value instead of the outer list โ in Python, the inner
traversefunction returnsNone; the result accumulates in the sharedresultlist, which the outer function returns. Doingreturn traverse(root)returnsNone. - Assuming the output order equals insertion order in a set or dict โ the traversal order must come from the recursion structure, not from any collection with undefined iteration order.
Related Problems
binary-tree-inorder-traversalโ same recursive skeleton, just append the value between the two recursive calls instead of afterbinary-tree-preorder-traversalโ same skeleton again, append before the recursive calls; comparing all three cements the patternbinary-tree-level-order-traversalโ switches from DFS to BFS, so nodes are collected in breadth-first layers instead of depth-first postorderbinary-tree-right-side-viewโ combines level-order traversal with a "last node per level" filter; builds on BFS intuition from traversal problemsflatten-binary-tree-to-linked-listโ rearranges tree nodes in-place in preorder; understanding all three DFS orders makes the required traversal obvious