Facebook Pixel

111. Minimum Depth of Binary Tree

Problem Description

You are given a binary tree and need to find its minimum depth. The minimum depth is defined as the number of nodes along the shortest path from the root node down to the nearest leaf node.

A leaf node is a node that has no children (both left and right children are null).

For example, if you have a tree like:

      1
     / \
    2   3
   /
  4

The minimum depth would be 2 (path: 1 → 3), even though there's a longer path (1 → 2 → 4) with depth 3.

Key points to understand:

  • The depth counts the number of nodes in the path, not the number of edges
  • You must reach a leaf node (a node with no children)
  • If the tree is empty (root is null), the minimum depth is 0
  • A path must end at a leaf node - you cannot stop at a node that still has one child

The solution uses recursion to traverse the tree. It handles three main cases:

  1. If the current node is null, return 0
  2. If one child is null, continue only with the non-null child (since we must reach a leaf)
  3. If both children exist, explore both paths and take the minimum

The recursive approach adds 1 for the current node and recursively calculates the minimum depth of its subtrees. When only one subtree exists, it must follow that path since a node with one child is not a leaf.

Flowchart Walkthrough

First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:

Is it a graph?

  • Yes: A binary tree is a special type of graph where each node has at most two children and there are no cycles.

Is it a tree?

  • Yes: The problem explicitly states we're working with a binary tree structure.

DFS

  • Conclusion: Based on the flowchart, when we have a tree structure, the appropriate algorithm to use is DFS (Depth-First Search).

The flowchart leads us directly to DFS for tree problems. This makes sense for the Minimum Depth of Binary Tree problem because:

  1. We need to explore all paths from root to leaf nodes
  2. DFS allows us to traverse each path completely before backtracking
  3. We can recursively calculate depths of subtrees and combine results
  4. The recursive nature of DFS naturally fits the tree structure

In this specific implementation, DFS helps us:

  • Traverse down each branch to find leaf nodes
  • Track the depth as we go (adding 1 for each level)
  • Compare depths from different branches to find the minimum
  • Handle special cases where a node has only one child (must continue down that path)

Conclusion: The flowchart correctly identifies DFS as the optimal approach for this tree-based minimum depth problem.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

When thinking about finding the minimum depth of a tree, we need to understand what we're looking for: the shortest path from root to any leaf node.

The natural approach is to explore all possible paths and keep track of the shortest one. Since we're dealing with a tree structure, we can break down the problem recursively - the minimum depth of a tree is related to the minimum depths of its subtrees.

For any node, we have a few scenarios to consider:

  1. Base case: If we reach a null node, the depth is 0 since there's no node to count.

  2. Leaf node: If we're at a leaf (no children), we've found a valid path endpoint.

  3. Node with two children: We need to explore both subtrees and take the smaller depth. The minimum depth would be 1 + min(left_subtree_depth, right_subtree_depth).

  4. Node with one child: This is the tricky case. We might initially think to treat the null child as having depth 0, but that would be incorrect. A node with one child is NOT a leaf, so we cannot end our path there. We must continue down the non-null child. This is why when one child is null, we only consider the depth of the other child.

The recursive DFS approach naturally handles these cases:

  • It traverses down each branch completely
  • It returns depths bottom-up, allowing us to compare at each level
  • It ensures we only count paths that actually reach leaf nodes

The key insight is recognizing that the minimum depth must end at a leaf node, not just any node. This constraint shapes how we handle nodes with only one child - we're forced to go down the available path rather than incorrectly returning 1 (counting the current node with a null child as a valid endpoint).

Solution Approach

The solution implements a recursive DFS approach that handles the different cases we identified in our intuition. Let's walk through the implementation step by step:

Base Case - Empty Tree:

if root is None:
    return 0

When we encounter a null node, we return 0 since there are no nodes to count.

Single Child Cases:

if root.left is None:
    return 1 + self.minDepth(root.right)
if root.right is None:
    return 1 + self.minDepth(root.left)

These two conditions handle nodes with only one child. When the left child is null, we must continue down the right subtree (we cannot stop here since it's not a leaf). Similarly, when the right child is null, we continue down the left subtree. We add 1 to count the current node in our depth calculation.

Both Children Present:

return 1 + min(self.minDepth(root.left), self.minDepth(root.right))

When both children exist, we recursively calculate the minimum depth of both subtrees and take the smaller value. Again, we add 1 for the current node.

How the Recursion Works:

  1. The function starts at the root and checks each condition
  2. For nodes with one child, it forces traversal down the non-null path
  3. For nodes with two children, it explores both paths
  4. The recursion naturally builds up the depth count as it returns from deeper levels
  5. Each recursive call adds 1 to represent the current node's contribution to the path length

Time Complexity: O(n) where n is the number of nodes, as we potentially visit every node in the worst case (a balanced tree).

Space Complexity: O(h) where h is the height of the tree, representing the recursion stack depth. In the worst case (skewed tree), this could be O(n).

The elegance of this solution lies in how it correctly handles the edge case of nodes with one child by forcing continuation down the available path, ensuring we only count complete paths to leaf nodes.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's trace through the algorithm with a concrete example to see how it handles different cases:

      3
     / \
    9   20
       /  \
      15   7

Goal: Find the minimum depth (shortest path from root to any leaf).

Step-by-step execution:

  1. Start at root (3):

    • Node 3 has both left (9) and right (20) children
    • We need to calculate: 1 + min(minDepth(9), minDepth(20))
  2. Explore left subtree - Node 9:

    • Node 9 has no children (both left and right are null)
    • This is a leaf node!
    • Since both children are null, we hit the base case for each: return 0
    • The function returns: 1 + min(0, 0) = 1
  3. Explore right subtree - Node 20:

    • Node 20 has both left (15) and right (7) children
    • Calculate: 1 + min(minDepth(15), minDepth(7))
  4. Node 15 (child of 20):

    • No children, so it's a leaf
    • Returns: 1 + min(0, 0) = 1
  5. Node 7 (child of 20):

    • No children, so it's a leaf
    • Returns: 1 + min(0, 0) = 1
  6. Back to Node 20:

    • Now we have: 1 + min(1, 1) = 2
    • The minimum depth from node 20 to its nearest leaf is 2
  7. Back to root (3):

    • Now we have: 1 + min(1, 2) = 2
    • The minimum depth of the entire tree is 2

The shortest path is 3 → 9 with a depth of 2 nodes.

Key observation: Notice how node 9, being a leaf, gave us the minimum depth. Even though the right subtree has more nodes, the algorithm correctly identified the shorter path by comparing depths at each level.

Solution Implementation

1# Definition for a binary tree node.
2# class TreeNode:
3#     def __init__(self, val=0, left=None, right=None):
4#         self.val = val
5#         self.left = left
6#         self.right = right
7
8from typing import Optional
9
10
11class Solution:
12    def minDepth(self, root: Optional[TreeNode]) -> int:
13        """
14        Find the minimum depth of a binary tree.
15        The minimum depth is the number of nodes along the shortest path
16        from the root node down to the nearest leaf node.
17      
18        Args:
19            root: The root node of the binary tree
20          
21        Returns:
22            The minimum depth of the tree
23        """
24        # Base case: empty tree has depth 0
25        if root is None:
26            return 0
27      
28        # If left subtree is empty, recurse only on right subtree
29        # We must continue to a leaf node, so we can't stop at a node with one child
30        if root.left is None:
31            return 1 + self.minDepth(root.right)
32      
33        # If right subtree is empty, recurse only on left subtree
34        if root.right is None:
35            return 1 + self.minDepth(root.left)
36      
37        # If both children exist, find minimum depth between left and right subtrees
38        # Add 1 to account for the current node
39        return 1 + min(self.minDepth(root.left), self.minDepth(root.right))
40
1/**
2 * Definition for a binary tree node.
3 * public class TreeNode {
4 *     int val;
5 *     TreeNode left;
6 *     TreeNode right;
7 *     TreeNode() {}
8 *     TreeNode(int val) { this.val = val; }
9 *     TreeNode(int val, TreeNode left, TreeNode right) {
10 *         this.val = val;
11 *         this.left = left;
12 *         this.right = right;
13 *     }
14 * }
15 */
16class Solution {
17    /**
18     * Finds the minimum depth of a binary tree.
19     * The minimum depth is the number of nodes along the shortest path 
20     * from the root node down to the nearest leaf node.
21     * 
22     * @param root The root node of the binary tree
23     * @return The minimum depth of the tree, or 0 if the tree is empty
24     */
25    public int minDepth(TreeNode root) {
26        // Base case: empty tree has depth 0
27        if (root == null) {
28            return 0;
29        }
30      
31        // If left subtree is null, only consider right subtree
32        // We must reach a leaf node, so we continue down the right path
33        if (root.left == null) {
34            return 1 + minDepth(root.right);
35        }
36      
37        // If right subtree is null, only consider left subtree
38        // We must reach a leaf node, so we continue down the left path
39        if (root.right == null) {
40            return 1 + minDepth(root.left);
41        }
42      
43        // If both children exist, find the minimum depth between them
44        // Add 1 to account for the current node
45        return 1 + Math.min(minDepth(root.left), minDepth(root.right));
46    }
47}
48
1/**
2 * Definition for a binary tree node.
3 * struct TreeNode {
4 *     int val;
5 *     TreeNode *left;
6 *     TreeNode *right;
7 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
8 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
9 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
10 * };
11 */
12class Solution {
13public:
14    /**
15     * Finds the minimum depth of a binary tree.
16     * The minimum depth is the number of nodes along the shortest path 
17     * from the root node down to the nearest leaf node.
18     * 
19     * @param root - Pointer to the root node of the binary tree
20     * @return The minimum depth of the tree
21     */
22    int minDepth(TreeNode* root) {
23        // Base case: empty tree has depth 0
24        if (root == nullptr) {
25            return 0;
26        }
27      
28        // If left subtree is empty, traverse only the right subtree
29        // We cannot consider this node as a leaf, so we must continue to the right
30        if (root->left == nullptr) {
31            return 1 + minDepth(root->right);
32        }
33      
34        // If right subtree is empty, traverse only the left subtree
35        // We cannot consider this node as a leaf, so we must continue to the left
36        if (root->right == nullptr) {
37            return 1 + minDepth(root->left);
38        }
39      
40        // Both subtrees exist, find the minimum depth between them
41        // Add 1 for the current node
42        return 1 + std::min(minDepth(root->left), minDepth(root->right));
43    }
44};
45
1/**
2 * Definition for a binary tree node.
3 * class TreeNode {
4 *     val: number
5 *     left: TreeNode | null
6 *     right: TreeNode | null
7 *     constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
8 *         this.val = (val===undefined ? 0 : val)
9 *         this.left = (left===undefined ? null : left)
10 *         this.right = (right===undefined ? null : right)
11 *     }
12 * }
13 */
14
15/**
16 * Finds the minimum depth of a binary tree.
17 * The minimum depth is the number of nodes along the shortest path 
18 * from the root node down to the nearest leaf node.
19 * 
20 * @param root - The root node of the binary tree
21 * @returns The minimum depth of the tree
22 */
23function minDepth(root: TreeNode | null): number {
24    // Base case: empty tree has depth 0
25    if (root === null) {
26        return 0;
27    }
28  
29    // Destructure left and right children for cleaner code
30    const { left, right } = root;
31  
32    // If left child is null, only traverse right subtree
33    // This handles the case where we shouldn't count a null child as a leaf
34    if (left === null) {
35        return 1 + minDepth(right);
36    }
37  
38    // If right child is null, only traverse left subtree
39    // This handles the case where we shouldn't count a null child as a leaf
40    if (right === null) {
41        return 1 + minDepth(left);
42    }
43  
44    // Both children exist, find the minimum depth between them
45    // Add 1 to account for the current node
46    return 1 + Math.min(minDepth(left), minDepth(right));
47}
48

Time and Space Complexity

Time Complexity: O(n), where n is the number of nodes in the binary tree.

The algorithm visits each node in the tree exactly once in the worst case. Even though we're finding the minimum depth, we may need to traverse the entire tree when all leaf nodes are at the same depth or when the tree is skewed. For each node visited, we perform constant time operations (checking if children are null and making recursive calls), resulting in O(n) total time complexity.

Space Complexity: O(n) in the worst case, where n is the number of nodes in the binary tree.

The space complexity is determined by the recursion call stack. In the worst case, when the tree is completely unbalanced (e.g., a skewed tree where each node has only one child), the recursion depth equals the number of nodes n, leading to O(n) space complexity. In the best case of a balanced tree, the recursion depth would be O(log n), but we consider the worst-case scenario for space complexity analysis.

Common Pitfalls

Pitfall 1: Incorrectly Treating Non-Leaf Nodes as Valid Endpoints

The Mistake: Many developers initially write code like this:

def minDepth(self, root: Optional[TreeNode]) -> int:
    if root is None:
        return 0
  
    # WRONG: This treats nodes with one child as valid endpoints
    left_depth = self.minDepth(root.left)
    right_depth = self.minDepth(root.right)
  
    return 1 + min(left_depth, right_depth)

Why It's Wrong: This approach fails because when one child is None, min(left_depth, right_depth) will always return 0, giving us an incorrect depth of 1 for nodes with only one child. Remember, we must reach a leaf node (a node with no children).

For example, with this tree:

    1
   /
  2

The incorrect code would return 1 (thinking the path ends at node 1), but the correct answer is 2 (path: 1 → 2).

The Fix: Handle single-child nodes explicitly by continuing down the non-null path:

if root.left is None:
    return 1 + self.minDepth(root.right)
if root.right is None:
    return 1 + self.minDepth(root.left)

Pitfall 2: Using BFS Without Proper Leaf Detection

The Mistake: When implementing a BFS solution, some might stop at the first None child encountered:

def minDepth(self, root: Optional[TreeNode]) -> int:
    if not root:
        return 0
  
    queue = [(root, 1)]
    while queue:
        node, depth = queue.pop(0)
      
        # WRONG: Checking for any None child instead of leaf nodes
        if not node.left or not node.right:
            return depth
      
        if node.left:
            queue.append((node.left, depth + 1))
        if node.right:
            queue.append((node.right, depth + 1))

Why It's Wrong: This returns immediately when finding a node with at least one None child, but that node might still have another child, making it not a leaf.

The Fix: Check for actual leaf nodes (both children are None):

def minDepth(self, root: Optional[TreeNode]) -> int:
    if not root:
        return 0
  
    queue = [(root, 1)]
    while queue:
        node, depth = queue.pop(0)
      
        # Correct: Check if it's actually a leaf node
        if not node.left and not node.right:
            return depth
      
        if node.left:
            queue.append((node.left, depth + 1))
        if node.right:
            queue.append((node.right, depth + 1))

Pitfall 3: Misunderstanding Depth vs Height

The Mistake: Confusing the problem by counting edges instead of nodes:

def minDepth(self, root: Optional[TreeNode]) -> int:
    if root is None:
        return 0
    if root.left is None and root.right is None:
        return 0  # WRONG: Leaf should have depth 1, not 0
    # ... rest of the code

Why It's Wrong: The problem asks for the number of nodes in the path, not the number of edges. A single node (leaf at root) has depth 1, not 0.

The Fix: Always count nodes, starting from 1 for any non-null node and adding as you traverse.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which type of traversal does breadth first search do?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More