Tree Traversals
The pattern: Visit every node in a binary tree in a specific order. The order you choose determines what information you can extract — sorted values (inorder), structure cloning (preorder), bottom-up computation (postorder), or level-by-level processing (BFS).
Why this matters in interviews: Almost every tree problem is a traversal with extra logic plugged in. Once you master the four traversal orders, tree problems become “which traversal + what do I track?”
When to Recognize It
- The input is a binary tree (or n-ary tree)
- You need to visit all nodes and collect/compute something
- “Print nodes level by level” → BFS
- “Validate BST” or “kth smallest” → Inorder
- “Serialize/clone the tree” → Preorder
- “Calculate heights, check balance” → Postorder
How It Works
Think of a tree as a family hierarchy. Each traversal visits the “parent” at a different time relative to its children:
flowchart TD
A["Root: 4"]:::client
B["Left: 2"]:::service
C["Right: 6"]:::service
D["1"]:::data
E["3"]:::data
F["5"]:::data
G["7"]:::data
A --> B
A --> C
B --> D
B --> E
C --> F
C --> G
classDef client fill:#4c3a5e,stroke:#818cf8,color:#e2e8f0
classDef service fill:#1a3a2a,stroke:#4ade80,color:#e2e8f0
classDef data fill:#3b3520,stroke:#fbbf24,color:#e2e8f0
| Order | Visit sequence | Output for tree above | Use case |
|---|---|---|---|
| Inorder | Left → Root → Right | 1,2,3,4,5,6,7 | BST gives sorted order |
| Preorder | Root → Left → Right | 4,2,1,3,6,5,7 | Clone/serialize tree |
| Postorder | Left → Right → Root | 1,3,2,5,7,6,4 | Delete tree, calc heights |
| Level-order | Level by level | [[4],[2,6],[1,3,5,7]] | BFS, shortest path in tree |
Template Code
Code
# Recursive traversals
def inorder(root):
if not root:
return []
return inorder(root.left) + [root.val] + inorder(root.right)
def preorder(root):
if not root:
return []
return [root.val] + preorder(root.left) + preorder(root.right)
def postorder(root):
if not root:
return []
return postorder(root.left) + postorder(root.right) + [root.val]
# Iterative inorder (most common in interviews)
def inorder_iterative(root):
result, stack = [], []
current = root
while current or stack:
while current:
stack.append(current)
current = current.left
current = stack.pop()
result.append(current.val)
current = current.right
return result
# BFS level-order
from collections import deque
def level_order(root):
if not root:
return []
result = []
queue = deque([root])
while queue:
level = []
for _ in range(len(queue)):
node = queue.popleft()
level.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
result.append(level)
return result
// Iterative inorder
List<Integer> inorderIterative(TreeNode root) {
List<Integer> result = new ArrayList<>();
Deque<TreeNode> stack = new ArrayDeque<>();
TreeNode current = root;
while (current != null || !stack.isEmpty()) {
while (current != null) {
stack.push(current);
current = current.left;
}
current = stack.pop();
result.add(current.val);
current = current.right;
}
return result;
}
// BFS level-order
List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if (root == null) return result;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
int size = queue.size();
List<Integer> level = new ArrayList<>();
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
level.add(node.val);
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
result.add(level);
}
return result;
}
// Iterative inorder
vector<int> inorderIterative(TreeNode* root) {
vector<int> result;
stack<TreeNode*> stk;
TreeNode* current = root;
while (current || !stk.empty()) {
while (current) {
stk.push(current);
current = current->left;
}
current = stk.top(); stk.pop();
result.push_back(current->val);
current = current->right;
}
return result;
}
// BFS level-order
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> result;
if (!root) return result;
queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
int size = q.size();
vector<int> level;
for (int i = 0; i < size; i++) {
TreeNode* node = q.front(); q.pop();
level.push_back(node->val);
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
result.push_back(level);
}
return result;
}
// Iterative inorder
function inorderIterative(root) {
const result = [], stack = [];
let current = root;
while (current || stack.length) {
while (current) {
stack.push(current);
current = current.left;
}
current = stack.pop();
result.push(current.val);
current = current.right;
}
return result;
}
// BFS level-order
function levelOrder(root) {
if (!root) return [];
const result = [];
const queue = [root];
while (queue.length) {
const size = queue.length;
const level = [];
for (let i = 0; i < size; i++) {
const node = queue.shift();
level.push(node.val);
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
result.push(level);
}
return result;
}
Variations
Iterative Preorder (Stack-Based)
Push right child first, then left — so left gets processed first (LIFO).
Code
def preorder_iterative(root):
if not root:
return []
result, stack = [], [root]
while stack:
node = stack.pop()
result.append(node.val)
if node.right:
stack.append(node.right)
if node.left:
stack.append(node.left)
return result
Morris Traversal (O(1) Space Inorder)
Threads the tree using right pointers to avoid a stack. O(n) time, O(1) space — rarely asked but impressive if you know it.
Validate BST Using Inorder
A valid BST has a strictly increasing inorder traversal. Track the previous value and ensure each node is greater.
Complexity
| Traversal | Time | Space |
|---|---|---|
| Recursive (any order) | O(n) | O(h) call stack |
| Iterative with stack | O(n) | O(h) |
| BFS level-order | O(n) | O(w) where w = max width |
| Morris traversal | O(n) | O(1) |
Where h = height (log n for balanced, n for skewed), w = maximum width of any level.
Common Mistakes
- Forgetting the base case — always check
if not root: returnfirst - Confusing iterative inorder pointer movement — after popping from stack, move to
current.right, notcurrent.left - Not using
for _ in range(len(queue))in BFS — without fixing the level size, you’ll mix nodes from different levels - Using recursion on very deep trees — can hit stack overflow. Switch to iterative for trees that might be 10000+ nodes deep
Practice Problems
- Binary Tree Inorder Traversal
- Binary Tree Level Order Traversal
- Maximum Depth of Binary Tree
- Validate Binary Search Tree
- Binary Tree Right Side View
Key Takeaways
- Inorder on a BST = sorted order. This is the most-tested property in interviews.
- Preorder captures structure (root first) — use it for serialization and cloning
- Postorder computes bottom-up — heights, subtree sizes, deletion
- BFS (level-order) is just a queue — use it whenever the problem mentions “levels” or “depth”