134. Minimum Depth of Binary Tree
🔗 LeetCode Problem: 111. Minimum Depth of Binary Tree
📊 Difficulty: Easy
🏷️ Topics: Binary Trees, Recursion, DFS, BFS, Tree Depth
Problem Statement
Given a binary tree, find its minimum depth.
The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node.
Note: A leaf is a node with no children.
Example 1:
Input: root = [3,9,20,null,null,15,7]
Output: 2
Example 2:
Input: root = [2,null,3,null,4,null,5,null,6]
Output: 5
Constraints:
- The number of nodes in the tree is in the range [0, 10^5]
- -1000 <= Node.val <= 1000
🌳 Visual Understanding - The CRITICAL Difference!
⚠️ THE GOTCHA - Why This is NOT Just "Use Min"
You might think:
"Maximum depth uses max(), so minimum depth uses min()!"
❌ WRONG! It's more subtle than that!
Why? Let's see...
Example 1: When Min Works
Input: [3,9,20,null,null,15,7]
Tree:
3 ← Root (level 1)
/ \
9 20 ← Level 2
/ \
15 7 ← Level 3
Paths to leaves:
Path 1: 3 → 9 (length 2) ✓ SHORTEST!
Path 2: 3 → 20 → 15 (length 3)
Path 3: 3 → 20 → 7 (length 3)
Minimum depth = 2
If we use min():
leftDepth = depth(9) = 1
rightDepth = depth(20) = 2
min(1, 2) = 1
1 + 1 = 2 ✓ WORKS!
Example 2: When Min FAILS! 🔥
Input: [2,null,3,null,4,null,5,null,6]
Tree:
2 ← Root
\
3 ← NOT a leaf!
\
4 ← NOT a leaf!
\
5 ← NOT a leaf!
\
6 ← LEAF!
Only ONE path to a leaf: 2 → 3 → 4 → 5 → 6 (length 5)
What if we blindly use min()?
leftDepth = depth(null) = 0 ← No left child!
rightDepth = depth(3) = 4
min(0, 4) = 0
1 + 0 = 1 ✓✗ WRONG! Should be 5!
Why wrong?
The left side has NO LEAF!
We can't count a path to null as a valid path!
KEY INSIGHT: We need a path to an ACTUAL LEAF!
What is a Leaf?
A LEAF is a node with NO CHILDREN (both left and right are null)
Leaf examples:
5 ← Leaf (no children)
7 ← Leaf
9 ← Leaf
NOT a leaf:
3 ← Has right child
\
4
8 ← Has left child
/
2
🧠 The AHA Moment - Understanding the Gotcha
Why Maximum Depth is Simple:
Maximum Depth:
"How deep can I go?"
If left is null → go right
If right is null → go left
If both exist → take deeper path
Formula: 1 + max(left, right)
Works because ANY path counts!
Why Minimum Depth is Tricky:
Minimum Depth:
"What's the shortest path to a LEAF?"
If left is null → Can't go left! Must go right!
If right is null → Can't go right! Must go left!
If both exist → take shallower path to a LEAF
Can't just use min() because null is not a leaf!
Need special handling for one-sided nodes!
Visual Comparison:
Tree:
1
\
2
\
3
Maximum Depth:
Left: null (depth 0)
Right: 2 (depth 2)
max(0, 2) = 2
1 + 2 = 3 ✓ Correct!
Minimum Depth:
Left: null (NOT A LEAF PATH!)
Right: path to leaf 3 (depth 3)
Can't use min(0, 3) = 0
Must use right path only
Result: 3 ✓ Correct!
🧠 Recursive Thinking - Building the Solution
Base Cases:
Base Case 1: Node is null
return 0
WHY? No node, no depth
BUT: This alone causes the problem!
Base Case 2: Node is a LEAF (both children null)
return 1
WHY? Leaf itself counts as depth 1
This is our SUCCESS case!
Recursive Cases (The Tricky Part):
Case 1: Only left child exists (right is null)
Can't use min() - right isn't a valid path!
Must go left only
return 1 + minDepth(left)
Case 2: Only right child exists (left is null)
Can't use min() - left isn't a valid path!
Must go right only
return 1 + minDepth(right)
Case 3: Both children exist
Both paths are valid
Take the shorter one
return 1 + min(minDepth(left), minDepth(right))
The Complete Logic:
minDepth(node):
if node is null:
return 0
if node is leaf (no children):
return 1
if only left child exists:
return 1 + minDepth(left)
if only right child exists:
return 1 + minDepth(right)
if both children exist:
return 1 + min(minDepth(left), minDepth(right))
🎯 Solution 1: Recursive DFS (Correct Approach)
Implementation:
class Solution {
public int minDepth(TreeNode root) {
// Base Case 1: Empty tree
if (root == null) {
return 0;
}
// Base Case 2: Leaf node
// WHY? We found a complete path to a leaf!
if (root.left == null && root.right == null) {
return 1;
}
// Recursive Case 1: Only left child exists
// WHY? Right side has no path to a leaf, must go left
if (root.left != null && root.right == null) {
return 1 + minDepth(root.left);
}
// Recursive Case 2: Only right child exists
// WHY? Left side has no path to a leaf, must go right
if (root.left == null && root.right != null) {
return 1 + minDepth(root.right);
}
// Recursive Case 3: Both children exist
// WHY? Both paths valid, take the shorter one
int leftDepth = minDepth(root.left);
int rightDepth = minDepth(root.right);
return 1 + Math.min(leftDepth, rightDepth);
}
}
Cleaner Version:
class Solution {
public int minDepth(TreeNode root) {
if (root == null) {
return 0;
}
// Leaf node
if (root.left == null && root.right == null) {
return 1;
}
// Get depths (will be 0 if child doesn't exist)
int leftDepth = minDepth(root.left);
int rightDepth = minDepth(root.right);
// If one side is 0 (doesn't exist), use the other side
// WHY? Can't count null as a valid path to a leaf!
if (leftDepth == 0) {
return 1 + rightDepth;
}
if (rightDepth == 0) {
return 1 + leftDepth;
}
// Both sides exist, use minimum
return 1 + Math.min(leftDepth, rightDepth);
}
}
Ultra-Concise Version (Using Integer.MAX_VALUE):
class Solution {
public int minDepth(TreeNode root) {
if (root == null) return 0;
// If leaf, return 1
if (root.left == null && root.right == null) return 1;
// Use MAX_VALUE for missing children
// WHY? So min() will choose the valid path
int left = (root.left != null) ? minDepth(root.left) : Integer.MAX_VALUE;
int right = (root.right != null) ? minDepth(root.right) : Integer.MAX_VALUE;
return 1 + Math.min(left, right);
}
}
🔍 Detailed Dry Run - Example 2 (The Tricky One!)
Input: [2,null,3,null,4,null,5,null,6]
Tree:
2
\
3
\
4
\
5
\
6
Complete Recursion Flow:
CALL STACK:
Level 0: minDepth(2)
│
├─ root = 2, not null ✓
├─ left = null, right = 3, not a leaf
├─ Only right child exists!
│
├─ Recurse RIGHT only ────────────────────┐
│ │
│ Level 1: minDepth(3) │
│ │ │
│ ├─ root = 3, not null ✓ │
│ ├─ left = null, right = 4, not a leaf │
│ ├─ Only right child exists! │
│ │ │
│ ├─ Recurse RIGHT only ────────────────┐
│ │ │
│ │ Level 2: minDepth(4) │
│ │ │ │
│ │ ├─ root = 4, not null ✓ │
│ │ ├─ left = null, right = 5 │
│ │ ├─ Only right child exists! │
│ │ │ │
│ │ ├─ Recurse RIGHT only ────────────┐
│ │ │ │
│ │ │ Level 3: minDepth(5) │
│ │ │ │ │
│ │ │ ├─ root = 5, not null ✓ │
│ │ │ ├─ left = null, right = 6 │
│ │ │ ├─ Only right child exists! │
│ │ │ │ │
│ │ │ ├─ Recurse RIGHT ──────────────┐
│ │ │ │ │
│ │ │ │ Level 4: minDepth(6) │
│ │ │ │ │ │
│ │ │ │ ├─ root = 6 │
│ │ │ │ ├─ left = null, right = null│
│ │ │ │ ├─ LEAF! ✓ │
│ │ │ │ └─ return 1 │
│ │ │ │ │
│ │ │ └─ Got: 1 │
│ │ │ return 1 + 1 = 2 │
│ │ │ │
│ │ └─ Got: 2 │
│ │ return 1 + 2 = 3 │
│ │ │
│ └─ Got: 3 │
│ return 1 + 3 = 4 │
│ │
└─ Got: 4 │
return 1 + 4 = 5 ✓ │
FINAL RESULT: 5 ✓
What Would Happen with Naive min()?
❌ WRONG APPROACH:
Level 0: minDepth(2)
leftDepth = minDepth(null) = 0 ← Problem!
rightDepth = minDepth(3) = 4
return 1 + min(0, 4) = 1 ✗ WRONG!
Why wrong?
Left side is null, not a leaf!
We counted a non-existent path!
🔍 Dry Run - Example 1 (Normal Case)
Input: [3,9,20,null,null,15,7]
Tree:
3
/ \
9 20
/ \
15 7
Recursion Flow:
Level 0: minDepth(3)
│
├─ Recurse LEFT ─────────────────┐
│ │
│ Level 1: minDepth(9) │
│ │ │
│ ├─ root = 9 │
│ ├─ left = null, right = null │
│ ├─ LEAF! ✓ │
│ └─ return 1 │
│ │
├─ Got leftDepth = 1 │
│ │
├─ Recurse RIGHT ────────────────┐
│ │
│ Level 1: minDepth(20) │
│ │ │
│ ├─ Both children exist │
│ │ │
│ ├─ LEFT: minDepth(15) │
│ │ │ │
│ │ ├─ LEAF! │
│ │ └─ return 1 │
│ │ │
│ ├─ RIGHT: minDepth(7) │
│ │ │ │
│ │ ├─ LEAF! │
│ │ └─ return 1 │
│ │ │
│ ├─ leftDepth = 1, rightDepth = 1
│ └─ return 1 + min(1, 1) = 2 │
│ │
├─ Got rightDepth = 2 │
│ │
├─ Both children exist │
└─ return 1 + min(1, 2) = 2 ✓ │
FINAL RESULT: 2 ✓
Why it works:
- Node 9 is a REAL leaf (depth 1)
- Node 20's subtree has leaves (depth 2)
- Both are valid paths
- Take minimum: min(1, 2) = 1
- Add current node: 1 + 1 = 2 ✓
🎯 Solution 2: BFS (Actually Better for This Problem!)
Why BFS is Great Here:
BFS processes level by level
For MINIMUM depth:
The FIRST leaf we encounter is at minimum depth!
Can return immediately - no need to explore further!
This is MORE EFFICIENT than DFS!
Implementation:
class Solution {
public int minDepth(TreeNode root) {
if (root == null) {
return 0;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int depth = 1; // Start at depth 1 (root level)
while (!queue.isEmpty()) {
int levelSize = queue.size();
// Process all nodes at current level
for (int i = 0; i < levelSize; i++) {
TreeNode node = queue.poll();
// Check if this is a leaf
// WHY? First leaf found is at minimum depth!
if (node.left == null && node.right == null) {
return depth; // EARLY RETURN!
}
// Add children for next level
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
// Move to next level
depth++;
}
return depth;
}
}
Dry Run - BFS Approach:
Tree:
3
/ \
9 20
/ \
15 7
Level 1 (depth = 1):
queue = [3]
Process 3: Not a leaf
Add children: 9, 20
queue = [9, 20]
Level 2 (depth = 2):
queue = [9, 20]
Process 9: Is a leaf! ✓
RETURN 2 immediately!
No need to process 20, 15, or 7!
Result: 2 ✓
Efficiency:
Only processed 2 nodes (3, 9)
DFS would process all 5 nodes
📊 Comparison: DFS vs BFS
DFS (Recursive):
✓ Natural for trees
✓ Clean recursive code
✗ Must explore ALL paths
✗ Can't early return on first leaf
Worst case tree (all leaves at same depth):
1
/ \
2 3
/ \ / \
4 5 6 7
Must visit all 7 nodes
Time: O(n)
Space: O(h) - recursion stack
BFS (Iterative):
✓ Can early return on first leaf
✓ Optimal for minimum depth
✓ Only explores needed levels
✗ Slightly more code
Same tree:
1
/ \
2 3
/ \ / \
4 5 6 7
Level 1: Process 1
Level 2: Process 2, 3
Level 3: Process 4 - LEAF! Return 3
Only visited 4 nodes (not all 7)
Time: O(n) worst case, but often much less!
Space: O(w) where w = max width
Which to Use?
For MINIMUM depth: BFS is better!
- Early return optimization
- Often fewer nodes processed
- Clearer intent (level-by-level)
For MAXIMUM depth: DFS is simpler!
- Must explore all paths anyway
- Cleaner recursive code
- Better space for tall trees
📊 Complexity Analysis
DFS Solution:
Time Complexity: O(n)
Worst case: Must visit all nodes
When?
- All nodes on one side (skewed tree)
- All leaves at same depth
Example:
1
/ \
2 3
/ \ / \
4 5 6 7
Must check all 7 nodes to find minimum
Space Complexity: O(h)
Recursion call stack
Best case (balanced): O(log n)
Worst case (skewed): O(n)
BFS Solution:
Time Complexity: O(n) worst case, often better!
Best case: O(w) where w = width at first leaf level
Example - early return:
1
/
2
/
3
Find leaf at level 3
Only process 3 nodes, not all!
Worst case: O(n) when must check all nodes
Space Complexity: O(w)
Queue holds one level at a time
Worst case: Complete tree
Last level has n/2 nodes
O(n) space
vs DFS: O(h)
For wide trees: DFS better
For tall trees: BFS better
⚠️ Common Mistakes - The Gotchas!
Mistake 1: Using min() Without Checking for Null Children
// ❌ WRONG - Counts null as valid path!
public int minDepth(TreeNode root) {
if (root == null) return 0;
int left = minDepth(root.left);
int right = minDepth(root.right);
return 1 + Math.min(left, right); // WRONG!
}
// Tree: 1
// \
// 2
// This returns: 1 + min(0, 1) = 1 ✗
// Should be: 2 (path to actual leaf 2)
// ✓ CORRECT - Check for null children
public int minDepth(TreeNode root) {
if (root == null) return 0;
if (root.left == null && root.right == null) return 1;
int left = minDepth(root.left);
int right = minDepth(root.right);
// If one side is null, use the other
if (left == 0) return 1 + right;
if (right == 0) return 1 + left;
return 1 + Math.min(left, right);
}
Mistake 2: Forgetting the Leaf Check
// ❌ WRONG - No explicit leaf handling
public int minDepth(TreeNode root) {
if (root == null) return 0;
// Missing: if (both children null) return 1;
if (root.left == null) return 1 + minDepth(root.right);
if (root.right == null) return 1 + minDepth(root.left);
return 1 + Math.min(minDepth(root.left), minDepth(root.right));
}
// Works but makes extra unnecessary calls
// Leaf check is cleaner and more efficient
// ✓ BETTER - Explicit leaf check
public int minDepth(TreeNode root) {
if (root == null) return 0;
if (root.left == null && root.right == null) return 1; // Clear!
// ... rest of code
}
Mistake 3: BFS Without Leaf Check
// ❌ WRONG - Returns depth even for non-leaf
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
// Missing: if (node is leaf) return depth;
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
depth++;
}
return depth; // Returns depth of last level, not first leaf!
// ✓ CORRECT - Check for leaf
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
// Check if leaf - CRITICAL!
if (node.left == null && node.right == null) {
return depth;
}
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
depth++;
}
Mistake 4: Confusing Min Depth with Max Depth Logic
// ❌ WRONG - Using max depth logic for min depth
public int minDepth(TreeNode root) {
if (root == null) return 0;
// This works for max depth, NOT min depth!
return 1 + Math.min(minDepth(root.left), minDepth(root.right));
}
// Why wrong?
// Min depth needs special handling for one-sided nodes!
// Max depth doesn't care - any path works
// ✓ CORRECT - Different logic for min depth
// (See correct implementations above)
Mistake 5: Not Understanding What "Leaf" Means
// ❌ WRONG - Thinks node with one child is a leaf
if (root.left == null || root.right == null) {
return 1; // WRONG! This isn't necessarily a leaf
}
// Tree: 1
// /
// 2
// Node 1 has left child, so NOT a leaf!
// ✓ CORRECT - Leaf has NO children (both null)
if (root.left == null && root.right == null) {
return 1; // TRUE leaf
}
🎯 Pattern Recognition - Min vs Max Pattern
Comparison with Maximum Depth:
Maximum Depth:
- ANY path counts
- Use max() - take longer path
- No special cases for one-sided nodes
Code:
return 1 + Math.max(left, right);
Minimum Depth:
- Only paths to LEAVES count
- Use min() - take shorter path to leaf
- Special handling for one-sided nodes!
Code:
if (left == 0) return 1 + right;
if (right == 0) return 1 + left;
return 1 + Math.min(left, right);
The Pattern Template:
// For MINIMUM path to leaf:
int minDepth(TreeNode node) {
// Base: null
if (node == null) return 0;
// Base: leaf
if (node.left == null && node.right == null) {
return 1;
}
// Recursive: one child missing
if (node.left == null) return 1 + minDepth(node.right);
if (node.right == null) return 1 + minDepth(node.left);
// Recursive: both children exist
return 1 + Math.min(minDepth(node.left), minDepth(node.right));
}
Related Problems:
1. Maximum Depth (Problem 133)
Simpler version - just use max()
No special handling needed
2. Path Sum
Also needs to reach a LEAF
Similar leaf checking logic
Must ensure path ends at leaf!
3. Sum Root to Leaf Numbers
Paths must end at leaves
Same leaf validation needed
4. Binary Tree Paths
Find all paths to leaves
Must identify leaves correctly
When to Use This Pattern:
✓ Finding shortest path to leaf
✓ Any problem requiring "reach a leaf"
✓ Path problems with leaf requirement
✓ Problems where null doesn't count as valid endpoint
📝 Quick Revision Notes
🎯 Core Concept
Minimum depth = shortest path to a LEAF. Key gotcha: null is NOT a leaf! Can't just use min() like max depth. Must handle one-sided nodes specially: if one child is null, MUST use the other side (it's not optional). Only use min() when BOTH children exist.
⚡ Quick Implementation
import java.util.LinkedList;
import java.util.Queue;
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;
}
}
class Solution {
public int minDepth(TreeNode root) {
// Approach 1 - Recursive DFS.
return recursiveDFS(root);
// // If time permits, check both methods also.
// iterativeDFS(root)
// iterativeBFS(root)
}
private int recursiveDFS(TreeNode root) {
// base case 1 - reached leaf nodes.
if(root == null) {
return 0;
}
// base case 1.1 - adds no value. If you ignore also, will be handled in next cycle.
if(root.left == null && root.right == null) {
return 1;
}
// base case 2 - when left is null. Do only for RIGHT.
if(root.left == null && root.right != null) {
return 1 + recursiveDFS(root.right);
}
// base case 3 - when right is null. Do only for LEFT.
if(root.right == null && root.left != null) {
return 1 + recursiveDFS(root.left);
}
// check child nodes now
return 1 + Math.min(recursiveDFS(root.left), recursiveDFS(root.right));
}
public static void main(String[] args) {
Solution s = new Solution();
System.out.println(s.minDepth(TreeNode.buildTree(new Integer[] {3,9,20,null,null,15,7})));
System.out.println(s.minDepth(TreeNode.buildTree(new Integer[] {2,null,3,null,4,null,5,null,6})));
}
}
// BFS - Often Better!
public int minDepth(TreeNode root) {
if (root == null) return 0;
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
int depth = 1;
while (!q.isEmpty()) {
int size = q.size();
for (int i = 0; i < size; i++) {
TreeNode node = q.poll();
if (node.left == null && node.right == null) return depth;
if (node.left != null) q.offer(node.left);
if (node.right != null) q.offer(node.right);
}
depth++;
}
return depth;
}
🔑 Key Insights
The Critical Gotcha:
Tree: 1
\
2
❌ Naive min approach:
left = 0 (null)
right = 1
min(0, 1) = 0
Result: 1 ✗ WRONG!
✓ Correct:
Left doesn't exist → must use right
Result: 2 ✓
Rule: Null is NOT a valid path to a leaf!
What Counts as a Leaf:
Leaf: node.left == null AND node.right == null
✓ Leaf: 5
✗ Not: 3
\
4
✗ Not: 7
/
2
Both children must be null!
When to Use Min:
Only when BOTH children exist:
if (left != 0 && right != 0)
return 1 + min(left, right);
Otherwise:
Use whichever child exists!
🎪 Memory Aid
"Leaf means both kids null"
"Null's not a leaf - don't count it!"
"One kid missing? Use the other!"
"Both kids present? Now use min!" ✨
⚠️ Don't Forget
- Leaf = both children null (not just one!)
- Null ≠ leaf (can't count as valid path)
- One-sided nodes need special handling
- BFS is better for this problem (early return!)
- Check for leaf before checking children
- Don't blindly use min() like max depth
🔗 Decision Tree
Is node null?
→ return 0
Is node a leaf (both children null)?
→ return 1
Does only left child exist?
→ return 1 + minDepth(left)
Does only right child exist?
→ return 1 + minDepth(right)
Both children exist?
→ return 1 + min(minDepth(left), minDepth(right))
🎉 Congratulations!
You've mastered the minimum depth gotcha!
What You Learned:
✅ The critical difference - Min depth is NOT just "use min()"
✅ Leaf definition - Both children must be null
✅ Null handling - Null is not a valid path
✅ One-sided nodes - Must use the existing child
✅ BFS advantage - Early return on first leaf
✅ DFS approach - Special cases for missing children
✅ Common mistakes - Why naive min() fails
The Gotcha Summarized:
Maximum Depth:
"How deep CAN I go?"
ANY path works
Use: 1 + max(left, right)
Minimum Depth:
"Shortest path to a REAL LEAF?"
Only LEAF paths count
Use: Special handling for one-sided nodes!
This is a COMMON interview gotcha!
Next Steps:
Perfect problems to practice this concept: - Path Sum (also needs to reach a leaf!) - Sum Root to Leaf Numbers (same leaf requirement) - Binary Tree Paths (finding all paths to leaves)
You now understand a subtle but important tree concept! 🌳🎯✨