139. Binary Tree Inorder Traversal
🔗 LeetCode Problem: 94. Binary Tree Inorder Traversal
📊 Difficulty: Easy
🏷️ Topics: Binary Trees, DFS, Traversal, Stack, Recursion
Problem Statement
Given the root of a binary tree, return the inorder traversal of its nodes' values.
Example 1:
Input: root = [1,null,2,3]
Output: [1,3,2]
Example 2:
Input: root = []
Output: []
Example 3:
Input: root = [1]
Output: [1]
Follow up: Recursive solution is trivial, could you do it iteratively?
Constraints:
- The number of nodes in the tree is in the range [0, 100]
- -100 <= Node.val <= 100
🌳 Visual Understanding - The Three Traversal Orders!
What is Traversal?
Traversal = Visiting every node in a specific ORDER
Three main DFS orders:
1. Inorder: Left → Root → Right
2. Preorder: Root → Left → Right
3. Postorder: Left → Right → Root
Think of it as when you PROCESS the root!
Example Tree:
1
/ \
2 3
/ \
4 5
Let's see all three traversals:
Inorder Traversal (Left → Root → Right):
Process order:
1. Go all the way LEFT first
2. Process ROOT
3. Then go RIGHT
Visual order:
1 (3rd)
/ \
(2nd)2 3 (4th)
/ \
(1st)4 5
Result: [4, 2, 5, 1, 3]
Reading pattern:
- Start at leftmost leaf
- Process nodes as you come back up
- Think "ascending" for BST!
Preorder Traversal (Root → Left → Right):
Process order:
1. Process ROOT first
2. Then go LEFT
3. Then go RIGHT
Visual order:
1 (1st)
/ \
(2nd)2 3 (5th)
/ \
(3rd)4 5 (4th)
Result: [1, 2, 4, 5, 3]
Reading pattern:
- Process nodes as you encounter them
- Top-down order
- Good for copying tree structure!
Postorder Traversal (Left → Right → Root):
Process order:
1. Go LEFT
2. Then go RIGHT
3. Process ROOT last
Visual order:
1 (5th)
/ \
(3rd)2 3 (4th)
/ \
(1st)4 5 (2nd)
Result: [4, 5, 2, 3, 1]
Reading pattern:
- Process children before parent
- Bottom-up order
- Good for deletion/cleanup!
Memory Aid:
IN-order: Left → IN (root) → Right
PRE-order: PRE (root) → Left → Right
POST-order: Left → Right → POST (root)
Position of ROOT tells you the order!
🧠 The AHA Moment - Understanding Inorder
Why Inorder is Special for BST:
Binary Search Tree (BST):
4
/ \
2 6
/ \ / \
1 3 5 7
Inorder traversal: [1, 2, 3, 4, 5, 6, 7]
Result is SORTED! ✨
Why?
Left < Root < Right (BST property)
Inorder visits: Left → Root → Right
So we get ascending order!
This is why inorder is important!
Recursive Pattern:
Inorder(node):
1. Visit left subtree (recursive)
2. Process current node
3. Visit right subtree (recursive)
Simple recursive structure!
Visual Recursion Flow:
Tree:
1
/ \
2 3
Inorder execution:
inorder(1):
inorder(2): ← Go left first
inorder(null) ← Left of 2
process(2) ← Process 2
inorder(null) ← Right of 2
process(1) ← Process 1
inorder(3): ← Go right
inorder(null) ← Left of 3
process(3) ← Process 3
inorder(null) ← Right of 3
Result: [2, 1, 3]
🧠 Recursive Thinking - Building the Solution
Base Case:
What's the simplest tree?
→ null (empty tree)
What to do with null?
→ Nothing! Just return
Base Case:
if (node == null) return;
Or implicitly handled by checking before recursing
Recursive Case:
For any node:
1. Recurse on LEFT subtree
inorder(node.left)
2. Process CURRENT node
result.add(node.val)
3. Recurse on RIGHT subtree
inorder(node.right)
Order matters! This IS inorder!
The Complete Logic:
inorder(node, result):
if node is null:
return
// LEFT
inorder(node.left, result)
// ROOT
result.add(node.val)
// RIGHT
inorder(node.right, result)
🎯 Solution 1: Recursive (Trivial but Important)
Implementation:
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
inorder(root, result);
return result;
}
private void inorder(TreeNode node, List<Integer> result) {
// Base Case: null node, nothing to process
if (node == null) {
return;
}
// LEFT: Recursively traverse left subtree
// WHY? Inorder visits left first
inorder(node.left, result);
// ROOT: Process current node
// WHY? After left, before right = INorder
result.add(node.val);
// RIGHT: Recursively traverse right subtree
// WHY? Visit right last in inorder
inorder(node.right, result);
}
}
Ultra-Concise Version:
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root != null) {
result.addAll(inorderTraversal(root.left)); // LEFT
result.add(root.val); // ROOT
result.addAll(inorderTraversal(root.right)); // RIGHT
}
return result;
}
}
🎯 Solution 2: Iterative with Stack (Important!)
The Challenge:
Recursion uses CALL STACK implicitly
To do iteratively:
We need to simulate the call stack
Use explicit Stack data structure!
Key insight:
Stack remembers where to come back to
Just like recursion does!
The Strategy:
Inorder: Left → Root → Right
Process:
1. Go as far LEFT as possible (pushing onto stack)
2. When can't go left, POP and process
3. Then go RIGHT
4. Repeat
Why this works:
- Stack holds "pending" nodes
- Pop gives us nodes in correct order
- Right subtrees processed after parent
Implementation:
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode current = root;
while (current != null || !stack.isEmpty()) {
// Step 1: Go as far LEFT as possible
// Push all left nodes onto stack
while (current != null) {
stack.push(current);
current = current.left;
}
// Step 2: Process node (ROOT in inorder)
// Pop from stack - this gives us leftmost unprocessed node
current = stack.pop();
result.add(current.val);
// Step 3: Go RIGHT
// Now process right subtree
current = current.right;
}
return result;
}
}
Why Two Loops?
Outer while: Continue until all nodes processed
- Runs while we have current node OR stack not empty
Inner while: Push all left nodes
- Go left until we can't
- Stack remembers the path
After inner loop:
- current is null (no more left)
- Pop gives us node to process
- Then check its right subtree
🔍 Detailed Dry Run - Recursive
Tree:
1
/ \
2 3
/ \
4 5
Complete Recursion Flow:
CALL STACK VISUALIZATION:
Level 0: inorder(1, [])
│
├─ node = 1, not null
│
├─ LEFT: inorder(2, []) ────────────────────────┐
│ │
│ Level 1: inorder(2, []) │
│ │ │
│ ├─ node = 2, not null │
│ │ │
│ ├─ LEFT: inorder(4, []) ──────────────────┐ │
│ │ │ │
│ │ Level 2: inorder(4, []) │ │
│ │ │ │ │
│ │ ├─ node = 4, not null │ │
│ │ │ │ │
│ │ ├─ LEFT: inorder(null, []) │ │
│ │ │ └─ return (nothing to do) │ │
│ │ │ │ │
│ │ ├─ ROOT: result.add(4) → [4] │ │
│ │ │ │ │
│ │ ├─ RIGHT: inorder(null, [4]) │ │
│ │ │ └─ return (nothing to do) │ │
│ │ │ │ │
│ │ └─ return │ │
│ │ │ │
│ ├─ ROOT: result.add(2) → [4, 2] │ │
│ │ │ │
│ ├─ RIGHT: inorder(5, [4, 2]) ────────────┐│ │
│ │ ││ │
│ │ Level 2: inorder(5, [4, 2]) ││ │
│ │ │ ││ │
│ │ ├─ node = 5, not null ││ │
│ │ │ ││ │
│ │ ├─ LEFT: inorder(null, [4, 2]) ││ │
│ │ │ └─ return ││ │
│ │ │ ││ │
│ │ ├─ ROOT: result.add(5) → [4, 2, 5] ││ │
│ │ │ ││ │
│ │ ├─ RIGHT: inorder(null, [4, 2, 5]) ││ │
│ │ │ └─ return ││ │
│ │ │ ││ │
│ │ └─ return ││ │
│ │ │ │
│ └─ return │ │
│ │
├─ ROOT: result.add(1) → [4, 2, 5, 1] │
│ │
├─ RIGHT: inorder(3, [4, 2, 5, 1]) ──────────┐
│ │
│ Level 1: inorder(3, [4, 2, 5, 1]) │
│ │ │
│ ├─ node = 3, not null │
│ │ │
│ ├─ LEFT: inorder(null, [4, 2, 5, 1]) │
│ │ └─ return │
│ │ │
│ ├─ ROOT: result.add(3) → [4, 2, 5, 1, 3] │
│ │ │
│ ├─ RIGHT: inorder(null, [4, 2, 5, 1, 3]) │
│ │ └─ return │
│ │ │
│ └─ return │
│ │
└─ return result: [4, 2, 5, 1, 3] │
FINAL: [4, 2, 5, 1, 3]
Visiting Order:
1. Visit 1 → go left to 2
2. Visit 2 → go left to 4
3. Visit 4 → go left to null (return)
4. Process 4 → add to result [4]
5. Visit 4's right (null) → return to 2
6. Process 2 → add to result [4, 2]
7. Visit 2's right (5)
8. Visit 5 → left is null
9. Process 5 → add to result [4, 2, 5]
10. Visit 5's right (null) → return to 1
11. Process 1 → add to result [4, 2, 5, 1]
12. Visit 1's right (3)
13. Visit 3 → left is null
14. Process 3 → add to result [4, 2, 5, 1, 3]
15. Visit 3's right (null) → done!
🔍 Detailed Dry Run - Iterative
Tree:
1
/ \
2 3
Stack Operations:
Initial:
stack = []
current = 1
result = []
Iteration 1: Push left nodes
Inner while: current = 1 (not null)
push(1), current = 1.left = 2
Inner while: current = 2 (not null)
push(2), current = 2.left = null
Inner while: current = null (stop)
stack = [1, 2] (bottom to top)
current = null
Iteration 2: Process 2
current = pop() = 2
result.add(2) → result = [2]
current = 2.right = null
stack = [1]
current = null
Iteration 3: Process 1
Inner while: current = null (skip)
current = pop() = 1
result.add(1) → result = [2, 1]
current = 1.right = 3
stack = []
current = 3
Iteration 4: Push 3
Inner while: current = 3 (not null)
push(3), current = 3.left = null
Inner while: current = null (stop)
stack = [3]
current = null
Iteration 5: Process 3
current = pop() = 3
result.add(3) → result = [2, 1, 3]
current = 3.right = null
stack = []
current = null
Iteration 6: Done
current = null, stack = []
Outer while: false (exit)
FINAL: [2, 1, 3]
📊 All Three Traversals - Complete Comparison
Recursive Implementations:
// INORDER: Left → Root → Right
private void inorder(TreeNode node, List<Integer> result) {
if (node == null) return;
inorder(node.left, result); // LEFT
result.add(node.val); // ROOT
inorder(node.right, result); // RIGHT
}
// PREORDER: Root → Left → Right
private void preorder(TreeNode node, List<Integer> result) {
if (node == null) return;
result.add(node.val); // ROOT (first!)
preorder(node.left, result); // LEFT
preorder(node.right, result); // RIGHT
}
// POSTORDER: Left → Right → Root
private void postorder(TreeNode node, List<Integer> result) {
if (node == null) return;
postorder(node.left, result); // LEFT
postorder(node.right, result); // RIGHT
result.add(node.val); // ROOT (last!)
}
Visual Comparison:
Tree:
1
/ \
2 3
/ \
4 5
Inorder: [4, 2, 5, 1, 3] (Left → Root → Right)
└─┘ └───┘ └───┘
leaf subtree main
Preorder: [1, 2, 4, 5, 3] (Root → Left → Right)
└┘ └──────┘ └┘
root left right
Postorder: [4, 5, 2, 3, 1] (Left → Right → Root)
└──────┘ └──┘└┘
subtree sub root
📊 Complexity Analysis
Recursive Solution:
Time Complexity: O(n)
Visit each node exactly once
At each node: O(1) work (add to list)
Total: n × O(1) = O(n)
Space Complexity: O(n)
Recursion call stack: O(h) where h = height
Result list: O(n)
Best case (balanced): O(log n) + O(n) = O(n)
Worst case (skewed): O(n) + O(n) = O(n)
Overall: O(n)
Iterative Solution:
Time Complexity: O(n)
Visit each node exactly once
Each node pushed and popped once
Total: O(n)
Space Complexity: O(n)
Stack: O(h) where h = height
Result list: O(n)
Best case: O(log n) + O(n) = O(n)
Worst case: O(n) + O(n) = O(n)
Overall: O(n)
⚠️ Common Mistakes
Mistake 1: Wrong Order for Inorder
// ❌ WRONG - This is preorder, not inorder!
private void inorder(TreeNode node, List<Integer> result) {
if (node == null) return;
result.add(node.val); // ROOT first → PREORDER!
inorder(node.left, result);
inorder(node.right, result);
}
// ✓ CORRECT - Inorder
private void inorder(TreeNode node, List<Integer> result) {
if (node == null) return;
inorder(node.left, result); // LEFT first
result.add(node.val); // ROOT in middle
inorder(node.right, result); // RIGHT last
}
Mistake 2: Iterative - Forgetting Inner Loop
// ❌ WRONG - Only pushes one left node
while (current != null || !stack.isEmpty()) {
if (current != null) {
stack.push(current);
current = current.left; // Only goes one level!
}
// ...
}
// ✓ CORRECT - Push ALL left nodes
while (current != null || !stack.isEmpty()) {
while (current != null) { // Inner while loop!
stack.push(current);
current = current.left;
}
// ...
}
Mistake 3: Iterative - Wrong Stack Pop Order
// ❌ WRONG - Processes right before popping parent
while (!stack.isEmpty()) {
TreeNode node = stack.peek(); // Peek instead of pop
// ... process right first ...
}
// ✓ CORRECT - Pop then process
while (current != null || !stack.isEmpty()) {
// Push left nodes...
current = stack.pop(); // Pop to process!
result.add(current.val);
current = current.right;
}
Mistake 4: Modifying Tree During Traversal
// ❌ WRONG - Modifying tree while traversing
private void inorder(TreeNode node, List<Integer> result) {
if (node == null) return;
inorder(node.left, result);
result.add(node.val);
node.val = 0; // DON'T modify during traversal!
inorder(node.right, result);
}
// ✓ CORRECT - Read-only traversal
private void inorder(TreeNode node, List<Integer> result) {
if (node == null) return;
inorder(node.left, result);
result.add(node.val); // Just read!
inorder(node.right, result);
}
🎯 Pattern Recognition - Traversal Applications
Core Pattern: DFS Traversal Orders
Template:
void traverse(TreeNode node, List<Integer> result) {
if (node == null) return;
// Preorder: process here
traverse(node.left, result);
// Inorder: process here
traverse(node.right, result);
// Postorder: process here
}
Position of processing determines order!
When to Use Each Traversal:
Inorder (Left → Root → Right):
✓ BST: Get sorted order
✓ BST validation
✓ Finding kth smallest in BST
✓ BST iterator
Why? Left < Root < Right in BST!
Preorder (Root → Left → Right):
✓ Copy tree structure
✓ Serialize tree
✓ Expression tree evaluation
✓ Create tree from traversal
Why? Process parent before children!
Postorder (Left → Right → Root):
✓ Delete tree (cleanup)
✓ Calculate tree height
✓ Calculate directory size
✓ Dependency resolution
Why? Process children before parent!
Related Problems:
1. Kth Smallest Element in BST
Uses inorder traversal
BST property: inorder gives sorted order
Find kth element in inorder sequence
2. Validate Binary Search Tree
Uses inorder traversal
Check if inorder is strictly increasing
If yes, valid BST
3. Binary Tree Preorder/Postorder Traversal
Exact same pattern
Just change when you process node
Move result.add() line!
4. Serialize and Deserialize Binary Tree
Uses preorder traversal
Rebuild tree from serialization
Preorder preserves structure
📝 Quick Revision Notes
🎯 Core Concept
Inorder = Left → Root → Right. Three ways: (1) Recursive (trivial - just follow order), (2) Iterative (use stack - simulate call stack), (3) Morris (O(1) space - skip for now). For BST, inorder gives sorted order! Position of result.add() determines traversal type: before recursion = preorder, between = inorder, after = postorder.
⚡ Quick Implementation
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Stack;
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode() {}
TreeNode(int val) { this.val = val; }
TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
public static TreeNode buildTree(Integer[] arr) {
if (arr == null || arr.length == 0 || arr[0] == null) {
return null;
}
TreeNode root = new TreeNode(arr[0]);
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int i = 1;
while (!queue.isEmpty() && i < arr.length) {
TreeNode current = queue.poll();
// Process left child
if (i < arr.length) {
if (arr[i] != null) {
current.left = new TreeNode(arr[i]);
queue.offer(current.left);
}
i++;
}
// Process right child
if (i < arr.length) {
if (arr[i] != null) {
current.right = new TreeNode(arr[i]);
queue.offer(current.right);
}
i++;
}
}
return root;
}
public static void print(TreeNode root) {
if (root == null) {
System.out.println("[]");
return;
}
List<String> result = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode node = queue.poll();
if (node != null) {
result.add(String.valueOf(node.val));
queue.offer(node.left);
queue.offer(node.right);
} else {
result.add("null");
}
}
while (!result.isEmpty() && result.get(result.size() - 1).equals("null")) {
result.remove(result.size() - 1);
}
System.out.println(result);
}
}
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
// // Approach 1 - recursive DFS
// recurisveDFS(root, res);
// Approach 2 - iterative DFS (using stack)
iterativeDFS(root, res);
return res;
}
private void iterativeDFS(TreeNode root, List<Integer> res) {
if(root == null) {
return;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode curr = root;
while (curr != null || !stack.isEmpty()) {
// go left left left till the leaf node.
while (curr != null) {
stack.push(curr);
curr = curr.left;
}
// Now we are at leaf node. Add to res.
curr = stack.pop();
res.add(curr.val);
curr = curr.right;
}
}
private void recurisveDFS(TreeNode root, List<Integer> res) {
if(root == null) {
return;
}
// inorder => left, root, right
recurisveDFS(root.left, res);
res.add(root.val);
recurisveDFS(root.right, res);
}
public static void main(String[] args) {
Solution s = new Solution();
System.out.println(s.inorderTraversal(TreeNode.buildTree(new Integer[] {1,null,2,3})));
System.out.println(s.inorderTraversal(TreeNode.buildTree(new Integer[] {1,2,3,4,5,null,8,null,null,6,7,9})));
System.out.println(s.inorderTraversal(TreeNode.buildTree(new Integer[] {})));
System.out.println(s.inorderTraversal(TreeNode.buildTree(new Integer[] {1})));
}
}
🔑 Key Insights
The Three Orders:
INorder: Left → IN (root) → Right [4,2,5,1,3]
PREorder: PRE (root) → Left → Right [1,2,4,5,3]
POSTorder: Left → Right → POST (root) [4,5,2,3,1]
Tree: 1
/ \
2 3
/ \
4 5
Just move where you process root!
Why Iterative Uses Stack:
Recursion: Uses call stack (implicit)
Iterative: Uses Stack<TreeNode> (explicit)
Stack remembers:
- Where we came from
- What nodes to process next
- Same as recursion, but manual!
BST Magic:
BST Property: Left < Root < Right
Inorder traversal: Left → Root → Right
Result: Sorted order! ✨
This is why inorder is special for BST!
🎪 Memory Aid
"LEFT-IN-RIGHT for inorder!"
"Stack remembers the path!"
"BST inorder = sorted!"
"Three orders, one template!" ✨
⚠️ Don't Forget
- Order matters (position of process determines type)
- Stack simulates recursion (explicit vs implicit)
- Inner while loop (push ALL left nodes in iterative)
- Pop before process (don't peek in iterative)
- BST inorder is sorted (important property!)
- Result.add() placement (defines the traversal type)
🎉 Congratulations!
You've mastered tree traversal!
What You Learned:
✅ Three traversal orders - Inorder, Preorder, Postorder
✅ Recursive pattern - Simple and clean
✅ Iterative pattern - Stack simulation
✅ BST property - Inorder gives sorted order
✅ When to use each - Different applications
✅ Pattern template - Reusable for all three
The Foundation:
These traversals are FUNDAMENTAL!
You'll use them in:
✓ BST problems (inorder)
✓ Tree serialization (preorder)
✓ Tree deletion (postorder)
✓ Tree validation
✓ Path finding
✓ Expression evaluation
Master these, master trees! 🌳
Pattern Evolution:
Previous problems:
✓ Comparison (Same, Symmetric, Subtree)
✓ Calculation (Depth)
✓ Validation (Path Sum)
✓ Transformation (Invert)
✓ Collection (Paths)
Problem 139: Traversal
✓ Visit all nodes in specific ORDER
✓ Foundation for many algorithms
✓ Different orders for different uses
This is the CORE of tree algorithms!
Next Steps:
Perfect problems to apply traversals: - Preorder Traversal (same pattern!) - Postorder Traversal (same pattern!) - Kth Smallest in BST (uses inorder!) - Validate BST (uses inorder!)
You now have the traversal toolkit! 🧰🌳✨