HARD Lab · Trees · 62

104. Maximum Depth of Binary Tree

The maximum depth of a binary tree is the length of the longest root-to-leaf path. A single post-order DFS computes it in one pass: each node returns 1 + max(leftDepth, rightDepth), bubbling the answer up to the root.

EasyDFSBFSTypeScript

PROBLEM What we're solving

Given the root of a binary tree, return the number of nodes along the longest path from the root to the farthest leaf. A single node has depth 1; an empty tree has depth 0.

Example: 3,9,20,null,null,15,7 — the root is 3, its left subtree has depth 1 (just node 9), its right subtree has depth 2 (nodes 20→15 or 20→7). The maximum depth is 3.

KEY IDEA Post-order DFS: each node asks its children

Insight → the depth of a node is one more than the taller of its two subtrees. If you trust each child to return its own subtree's depth, the formula 1 + max(left, right) propagates the answer from the leaves to the root automatically. Every node answers a single small question — the recursion does the rest.

RECIPE 1 + max(left, right) — three-line recursion

  • 0 · Base case. If root === null, return 0. An empty subtree contributes zero depth.
  • 1 · Recurse left. Call maxDepth(root.left) to get the depth of the left subtree.
  • 2 · Recurse right. Call maxDepth(root.right) to get the depth of the right subtree.
  • 3 · Combine. Return 1 + Math.max(leftDepth, rightDepth). The +1 counts the current node; max picks the taller child.
Classic confusion → returning Math.max(left, right) without the +1 undercounts by 1 for every non-null node. The current node itself is a level — always add 1 before returning.

COST Complexity & alternatives

Brute force (re-measure each path)
O(n²)
Enumerate every root-to-leaf path naïvely.
DFS (one pass)
O(n)
Each node visited once. O(h) call-stack space.

BFS alternative

Level-order BFS with a queue also works: process the tree level by level, incrementing a counter each time the queue drains a full level. The answer is the number of levels. Same O(n) time, but O(w) space where w is the maximum width (can be O(n) for a wide tree, vs O(h) for DFS on a balanced tree).

Pattern transfer → the same 1 + max(left, right) backbone powers Diameter of Binary Tree (track max(left+right) on the side), Balanced Binary Tree (return −1 if |left−right| > 1), and Binary Tree Maximum Path Sum (replace max-depth arithmetic with path sums).

RUN IT DFS: return 1 + max(left, right) at each node

step 0 / 17
STARTStart DFS from root 3. We'll recurse left then right, then return 1 + max(leftDepth, rightDepth) at each node.
tree =3091202null3null415576
State
dfs(3)
call
current nodevisited / depth knownfinal result
slowfast

TYPESCRIPT The solution, annotated

maxDepth.ts
function maxDepth(root: TreeNode | null): number {
  if (root === null) return 0;            // base case: empty tree
  const leftDepth  = maxDepth(root.left);
  const rightDepth = maxDepth(root.right);
  return 1 + Math.max(leftDepth, rightDepth);
}

Reading it block by block

Line 2 — base case. A null node (empty subtree) has depth zero. This is what stops the recursion at leaves and beyond.
Lines 3–4 — recursive calls. Ask the left and right subtrees for their depths. These calls run to completion before the current node does any computation — this is the post-order pattern.
Line 5 — combine. 1 + Math.max(leftDepth, rightDepth). The 1 accounts for the current node; Math.max picks the taller child. The answer naturally bubbles up to the root.
Complexity → O(n) time — every node is visited exactly once. O(h) space on the call stack where h is the tree height (O(log n) for balanced trees, O(n) worst-case for a skewed tree). The BFS variant uses O(w) queue space (w = max width).

INTERVIEWFollow-ups they'll ask

  • "Can you do it iteratively?" Yes — use an explicit stack (DFS) or a queue (BFS). BFS is arguably more natural: count levels until the queue is empty.
  • "What about minimum depth?" Minimum depth is not symmetric. A node with only one child must not count the missing child as depth 1 — you need if (!left) return 1 + right and vice versa, otherwise a skewed tree returns 1 incorrectly.
  • "What's the diameter?" Track max(left + right) in a closure variable during the same DFS. Each node contributes a candidate path through itself (left + right edges), while still returning 1 + max(left, right) to its parent.
  • "Is the tree balanced?" Return -1 (sentinel) instead of the depth whenever |leftDepth - rightDepth| > 1. Propagate the sentinel upward; O(n) single pass.
  • "Handle a very deep tree (stack overflow)?" Switch to an iterative DFS with an explicit stack, or use BFS — neither has a call-stack limit.

MNEMONIC The one-liner

"Ask your children how tall they are, then stand one step taller than the tallest."

TRIGGERS When you see ___ → reach for ___

longest root-to-leaf pathpost-order DFS, return 1 + max(left, right)
"height" or "depth" of a treebase case null → 0, else recurse both sides
need per-subtree answer before parentpost-order pattern (left, right, root)
depth + extra metric (diameter, balance)same DFS + closure variable for extra info

SKELETON The reusable shape

skeleton.ts
function maxDepth(root: TreeNode | null): number {
  if (root === null) return 0;
  const left  = maxDepth(root.left);
  const right = maxDepth(root.right);
  return 1 + Math.max(left, right);
}

FLASHCARDS Tap to flip

Base case of maxDepth?
null node → return 0. An empty subtree has zero depth.
tap to flip
1 / 7
Answer to reveal explanations. No penalty for retries.Score: 0 / 7
QUESTION 01
What is maxDepth called with for the tree 3,9,20,null,null,15,7?
QUESTION 02
What does maxDepth return for a null node?
QUESTION 03
A programmer writes return Math.max(left, right) (forgetting the +1). What happens?
QUESTION 04
Time complexity of the recursive DFS solution?
QUESTION 05
What traversal order does the DFS maxDepth use?
QUESTION 06
For a completely skewed tree (a linked list of n nodes), the call-stack depth is:
QUESTION 07
The BFS approach to maxDepth counts: