104. Maximum Depth of Binary Tree - Explanation

Problem Link

Description

Given the root of a binary tree, return its depth.

The depth of a binary tree is defined as the number of nodes along the longest path from the root node down to the farthest leaf node.

Example 1:

Input: root = [1,2,3,null,null,4]

Output: 3

Example 2:

Input: root = []

Output: 0

Constraints:

  • 0 <= The number of nodes in the tree <= 100.
  • -100 <= Node.val <= 100


Topics

Recommended Time & Space Complexity

You should aim for a solution with O(n) time and O(n) space, where n is the number of nodes in the tree.


Hint 1

From the definition of binary tree's maximum depth, Can you think of a way to achieve this recursively? Maybe you should consider the max depth of the subtrees first before computing the maxdepth at the root.


Hint 2

We use the Depth First Search (DFS) algorithm to find the maximum depth of a binary tree, starting from the root. For the subtrees rooted at the left and right children of the root node, we calculate their maximum depths recursively going through left and right subtrees. We return 1 + max(leftDepth, rightDepth). Why?


Hint 3

The +1 accounts for the current node, as it contributes to the current depth in the recursion call. We pass the maximum depth from the current node's left and right subtrees to its parent because the current maximum depth determines the longest path from the parent to a leaf node through this subtree.


Company Tags

Please upgrade to NeetCode Pro to view company tags.



Prerequisites

Before attempting this problem, you should be comfortable with:

  • Binary Trees - Understanding tree node structure with left/right children
  • Recursion - Writing functions that call themselves with smaller subproblems
  • Depth-First Search (DFS) - Traversing trees by exploring as deep as possible before backtracking
  • Breadth-First Search (BFS) - Traversing trees level by level using a queue

1. Recursive DFS

Intuition

Recursive DFS computes the maximum depth of a binary tree by exploring every node.
The idea is simple:

  • The depth of a tree = 1 + maximum depth of its left and right subtrees.
  • If a node is None, its depth is 0.

So for each node:

  1. Recursively compute the depth of the left subtree.
  2. Recursively compute the depth of the right subtree.
  3. Take the maximum of the two.
  4. Add 1 for the current node.

Algorithm

  1. If root is null, return 0.
  2. Otherwise:
    • Recursively compute leftDepth = maxDepth(root.left).
    • Recursively compute rightDepth = maxDepth(root.right).
  3. Return 1 + max(leftDepth, rightDepth).
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        if not root:
            return 0

        return 1 + max(self.maxDepth(root.left), self.maxDepth(root.right))

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity: O(h)O(h)

Where nn is the number of nodes in the tree and hh is the height of the tree.


2. Iterative DFS (Stack)

Intuition

Instead of relying on recursion to explore the tree, we can simulate DFS explicitly using a stack.
The stack will store pairs of:

  • the current node
  • the depth of that node in the tree

Every time we pop a node from the stack:

  • We update the maximum depth seen so far.
  • We push its left and right children onto the stack with depth + 1.

This approach works like a manual DFS where we keep track of depth ourselves.
It avoids recursion and is useful when recursion depth may become too large.


Algorithm

  1. If the root is null, return 0.
  2. Initialize a stack with the pair (root, 1) to represent depth 1.
  3. Initialize maxDepth = 0.
  4. While the stack is not empty:
    • Pop (node, depth).
    • Update maxDepth = max(maxDepth, depth).
    • Push (node.left, depth + 1) onto the stack if left child exists.
    • Push (node.right, depth + 1) onto the stack if right child exists.
  5. When the stack becomes empty, return maxDepth.
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        stack = [[root, 1]]
        res = 0

        while stack:
            node, depth = stack.pop()

            if node:
                res = max(res, depth)
                stack.append([node.left, depth + 1])
                stack.append([node.right, depth + 1])
        return res

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity: O(n)O(n)

Intuition

Breadth-First Search (BFS) processes the tree level by level.
This makes it a perfect fit for computing the maximum depth because:

  • Every iteration of BFS processes one entire level of the tree.
  • So each completed level corresponds to increasing the depth by 1.

We simply count how many levels we traverse until the queue becomes empty.

Think of the queue like a moving frontier:

  • Start with the root → depth = 1
  • Add its children → depth = 2
  • Add their children → depth = 3
  • Continue until no nodes remain.

The number of BFS layers processed is exactly the depth of the tree.


Algorithm

  1. If the tree is empty (root == null), return 0.
  2. Initialize a queue and push the root.
  3. Initialize level = 0.
  4. While the queue is not empty:
    • Determine the number of nodes at the current level (size = len(queue)).
    • Process all size nodes:
      • Pop from the queue.
      • Push their left and right children if they exist.
    • After processing the entire level, increment level.
  5. Return level when the queue becomes empty.
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        q = deque()
        if root:
            q.append(root)

        level = 0
        while q:
            for i in range(len(q)):
                node = q.popleft()
                if node.left:
                    q.append(node.left)
                if node.right:
                    q.append(node.right)
            level += 1
        return level

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity: O(n)O(n)

Common Pitfalls

Confusing Depth with Height

Depth is measured from the root down (root has depth 1 or 0 depending on convention), while height is measured from the leaves up. The maximum depth of a tree equals its height when counting from root. Be consistent with whether you start counting at 0 or 1.

# If root is None, depth is 0 (no nodes)
# If root exists with no children, depth is 1 (one node)
def maxDepth(self, root):
    if not root:
        return 0  # Base case: empty tree has depth 0
    return 1 + max(self.maxDepth(root.left), self.maxDepth(root.right))

Forgetting to Handle the Empty Tree Case

An empty tree (null root) has a depth of 0. Failing to check for this base case before accessing node properties will cause a null pointer exception.

# Wrong: No null check
def maxDepth(self, root):
    return 1 + max(self.maxDepth(root.left), self.maxDepth(root.right))
    # Crashes when root is None

# Correct: Handle empty tree first
def maxDepth(self, root):
    if not root:
        return 0
    return 1 + max(self.maxDepth(root.left), self.maxDepth(root.right))