104. Maximum Depth of Binary Tree
Problem Description
You are given the root of a binary tree. Your task is to find the maximum depth of the tree.
The maximum depth is defined as the number of nodes along the longest path from the root node down to the farthest leaf node. In other words, it's the length of the longest root-to-leaf path in the tree, counting the nodes themselves.
For example:
- If the tree is empty (root is
None
), the maximum depth is0
- If the tree has only a root node with no children, the maximum depth is
1
- If the tree has multiple levels, you need to find the deepest leaf and count how many nodes are on that path from root to leaf
The solution uses a recursive approach where:
- If we reach a
None
node (empty tree or beyond a leaf), we return depth0
- For any non-empty node, we recursively calculate the maximum depth of its left subtree and right subtree
- The depth at the current node is
1
(for the current node itself) plus the maximum of the depths from the left and right subtrees:1 + max(left_depth, right_depth)
This way, the recursion builds up the depth count from the bottom (leaves) to the top (root), ultimately returning the maximum depth of the entire tree.
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
- We arrive at DFS (Depth-First Search) as our algorithm choice.
Conclusion: The flowchart suggests using DFS (Depth-First Search) for finding the maximum depth of a binary tree.
This makes perfect sense because:
- Trees are acyclic connected graphs, making them ideal for DFS traversal
- Finding the maximum depth requires exploring all paths from root to leaves
- DFS naturally explores each path completely before backtracking, which aligns perfectly with calculating the depth of each path
- The recursive nature of DFS matches the recursive structure of trees, where we can solve the problem by combining solutions from subtrees
The solution implements DFS recursively by:
- Visiting each node (starting from root)
- Recursively calculating depths of left and right subtrees
- Returning
1 + max(left_depth, right_depth)
to build up the maximum depth from bottom to top
Intuition
When we think about finding the maximum depth of a tree, we need to understand what depth means - it's the number of nodes from root to the farthest leaf. This immediately suggests we need to explore all possible paths from root to leaves.
The key insight is that a tree's maximum depth can be broken down into smaller subproblems. For any node, the maximum depth passing through that node is:
1
(for the current node itself)- Plus the maximum depth of either its left or right subtree
This recursive relationship naturally emerges because trees have a recursive structure - each subtree is itself a complete tree with its own maximum depth.
Think of it like measuring the height of a corporate hierarchy: to find the total levels from CEO to the lowest employee, you'd ask each direct report "what's the maximum depth in your department?" Then you'd take the largest answer and add 1
(for the CEO level itself).
The base case becomes clear when we reach a leaf's children (which are None
) - there's no depth beyond that point, so we return 0
. This allows the recursion to "bubble up" from the leaves back to the root, with each level adding 1
to the maximum depth found below it.
The elegance of this DFS approach is that it mirrors the tree's structure perfectly - we're essentially asking each node "what's the deepest path beneath you?" and building our answer from the bottom up. The recursive calls handle all the traversal automatically, visiting every node exactly once to determine the overall maximum depth.
Solution Approach
The solution implements a recursive DFS approach to find the maximum depth of the binary tree. Let's walk through the implementation step by step:
Base Case:
if root is None: return 0
When we encounter a None
node (either an empty tree or we've gone past a leaf node), we return 0
as there's no depth to count.
Recursive Case:
l, r = self.maxDepth(root.left), self.maxDepth(root.right)
For any non-null node, we recursively calculate:
l
: the maximum depth of the left subtreer
: the maximum depth of the right subtree
These recursive calls will traverse down to the leaves and return the depths from bottom to top.
Combining Results:
return 1 + max(l, r)
The maximum depth at the current node is:
1
for counting the current node itself- Plus the maximum of the left and right subtree depths
This ensures we're always taking the longest path from the current node down to any leaf.
How the Recursion Unfolds:
The algorithm works by traversing the tree in a depth-first manner:
- It goes as deep as possible in the left subtree first
- When it hits
None
nodes, it returns0
- As the recursion unwinds, each level adds
1
to the maximum depth found below - The same process happens for the right subtree
- At each node, we take the maximum of left and right depths
Time and Space Complexity:
- Time:
O(n)
wheren
is the number of nodes, as we visit each node exactly once - Space:
O(h)
whereh
is the height of the tree, due to the recursion call stack. In the worst case (skewed tree), this could beO(n)
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through finding the maximum depth of this binary tree:
1 / \ 2 3 / 4
Step-by-step execution:
-
Start at root (1):
- Call
maxDepth(1)
- Need to calculate
maxDepth(1.left)
andmaxDepth(1.right)
- Call
-
Process left subtree of 1 (node 2):
- Call
maxDepth(2)
- Need to calculate
maxDepth(2.left)
andmaxDepth(2.right)
- Call
-
Process left subtree of 2 (node 4):
- Call
maxDepth(4)
- Calculate
maxDepth(4.left)
→ returns0
(None) - Calculate
maxDepth(4.right)
→ returns0
(None) - Return
1 + max(0, 0) = 1
- Call
-
Process right subtree of 2 (None):
- Call
maxDepth(None)
→ returns0
- Call
-
Complete node 2:
- Left depth =
1
(from node 4) - Right depth =
0
(None) - Return
1 + max(1, 0) = 2
- Left depth =
-
Process right subtree of 1 (node 3):
- Call
maxDepth(3)
- Calculate
maxDepth(3.left)
→ returns0
(None) - Calculate
maxDepth(3.right)
→ returns0
(None) - Return
1 + max(0, 0) = 1
- Call
-
Complete root node 1:
- Left depth =
2
(from subtree with nodes 2→4) - Right depth =
1
(from node 3) - Return
1 + max(2, 1) = 3
- Left depth =
Result: Maximum depth = 3
The algorithm correctly identifies that the longest path is 1→2→4, which contains 3 nodes.
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
8class Solution:
9 def maxDepth(self, root: TreeNode) -> int:
10 """
11 Calculate the maximum depth of a binary tree.
12
13 The maximum depth is the number of nodes along the longest path
14 from the root node down to the farthest leaf node.
15
16 Args:
17 root: The root node of the binary tree
18
19 Returns:
20 The maximum depth as an integer
21 """
22 # Base case: if the node is None, depth is 0
23 if root is None:
24 return 0
25
26 # Recursively calculate the depth of left and right subtrees
27 left_depth = self.maxDepth(root.left)
28 right_depth = self.maxDepth(root.right)
29
30 # The depth of current node is 1 plus the maximum of left and right depths
31 return 1 + max(left_depth, right_depth)
32
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 * Calculates the maximum depth of a binary tree.
19 * The maximum depth is the number of nodes along the longest path
20 * from the root node down to the farthest leaf node.
21 *
22 * @param root The root node of the binary tree
23 * @return The maximum depth of the tree
24 */
25 public int maxDepth(TreeNode root) {
26 // Base case: if the node is null, depth is 0
27 if (root == null) {
28 return 0;
29 }
30
31 // Recursively calculate the depth of the left subtree
32 int leftDepth = maxDepth(root.left);
33
34 // Recursively calculate the depth of the right subtree
35 int rightDepth = maxDepth(root.right);
36
37 // The depth of current node is 1 plus the maximum depth of its subtrees
38 return 1 + Math.max(leftDepth, rightDepth);
39 }
40}
41
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 * Calculate the maximum depth of a binary tree.
16 * The maximum depth is the number of nodes along the longest path
17 * from the root node down to the farthest leaf node.
18 *
19 * @param root - The root node of the binary tree
20 * @return The maximum depth of the tree
21 */
22 int maxDepth(TreeNode* root) {
23 // Base case: if the current node is null, depth is 0
24 if (!root) {
25 return 0;
26 }
27
28 // Recursively calculate the depth of left subtree
29 int leftDepth = maxDepth(root->left);
30
31 // Recursively calculate the depth of right subtree
32 int rightDepth = maxDepth(root->right);
33
34 // The depth of current node is 1 plus the maximum depth of its subtrees
35 return 1 + std::max(leftDepth, rightDepth);
36 }
37};
38
1/**
2 * Definition for a binary tree node.
3 * Each node contains a value and references to left and right child nodes.
4 */
5class TreeNode {
6 val: number;
7 left: TreeNode | null;
8 right: TreeNode | null;
9
10 constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
11 this.val = (val === undefined ? 0 : val);
12 this.left = (left === undefined ? null : left);
13 this.right = (right === undefined ? null : right);
14 }
15}
16
17/**
18 * Calculates the maximum depth of a binary tree.
19 * The maximum depth is the number of nodes along the longest path
20 * from the root node down to the farthest leaf node.
21 *
22 * @param root - The root node of the binary tree
23 * @returns The maximum depth of the tree
24 *
25 * Time Complexity: O(n) where n is the number of nodes in the tree
26 * Space Complexity: O(h) where h is the height of the tree (recursive call stack)
27 */
28function maxDepth(root: TreeNode | null): number {
29 // Base case: if the node is null, depth is 0
30 if (root === null) {
31 return 0;
32 }
33
34 // Recursively calculate the depth of left and right subtrees
35 // Add 1 for the current node and return the maximum depth
36 const leftDepth: number = maxDepth(root.left);
37 const rightDepth: number = maxDepth(root.right);
38
39 return 1 + Math.max(leftDepth, rightDepth);
40}
41
Time and Space Complexity
Time Complexity: O(n)
, where n
is the number of nodes in the binary tree. The algorithm performs a depth-first search traversal, visiting each node exactly once. At each node, it performs constant time operations (comparison and addition), so the total time is proportional to the number of nodes.
Space Complexity: O(h)
, where h
is the height of the binary tree. This space is used by the recursive call stack. In the worst case, when the tree is completely unbalanced (skewed), the height h
can be equal to n
, making the space complexity O(n)
. In the best case, when the tree is perfectly balanced, the height is log(n)
, making the space complexity O(log n)
.
Common Pitfalls
1. Confusing Depth vs Height Terminology
A common mistake is misunderstanding what "depth" means in this context. Some developers confuse:
- Depth of a node: Distance from root to that specific node
- Height of a node: Distance from that node to its farthest leaf
- Maximum depth of tree: What we're calculating here - the total number of nodes on the longest root-to-leaf path
Solution: Remember that this problem asks for the maximum depth of the entire tree, which equals the height of the root node plus 1. The recursive solution correctly counts nodes (returning 1 + max(left, right)
), not edges.
2. Incorrect Base Case Return Value
Some might incorrectly return 1
for the base case:
# WRONG! if root is None: return 1 # This would count non-existent nodes
Solution: Always return 0
for None
nodes. A None
node contributes nothing to the depth count.
3. Off-by-One Errors in Counting
Developers sometimes forget to add 1
for the current node or add it incorrectly:
# WRONG - Forgets to count current node
return max(left_depth, right_depth)
# WRONG - Adds 1 twice
return 1 + max(1 + left_depth, 1 + right_depth)
Solution: Add 1
exactly once per node to account for the current node itself: return 1 + max(left_depth, right_depth)
4. Stack Overflow with Extremely Deep Trees
For very deep or unbalanced trees (like a linked list shaped tree with 10,000 nodes), the recursive solution can cause stack overflow.
Solution: Use an iterative approach with explicit stack or queue:
def maxDepth(self, root: TreeNode) -> int:
if not root:
return 0
stack = [(root, 1)]
max_depth = 0
while stack:
node, depth = stack.pop()
max_depth = max(max_depth, depth)
if node.left:
stack.append((node.left, depth + 1))
if node.right:
stack.append((node.right, depth + 1))
return max_depth
5. Not Handling Edge Cases Properly
Forgetting to test with:
- Empty tree (
root = None
) - Single node tree
- Completely unbalanced tree (all nodes on one side)
Solution: Always validate your solution with these edge cases before considering it complete.
Which of the following array represent a max heap?
Recommended Readings
Everything About Trees A tree is a type of graph data structure composed of nodes and edges Its main properties are It is acyclic doesn't contain any cycles There exists a path from the root to any node Has N 1 edges where N is the number of nodes in the tree and
https assets algo monster cover_photos dfs svg Depth First Search Prereqs Recursion Review problems recursion_intro Trees problems tree_intro With a solid understanding of recursion under our belts we are now ready to tackle one of the most useful techniques in coding interviews Depth First Search DFS As the name suggests
https assets algo monster cover_photos bfs svg Breadth First Search on Trees Hopefully by this time you've drunk enough DFS Kool Aid to understand its immense power and seen enough visualization to create a call stack in your mind Now let me introduce the companion spell Breadth First Search BFS
Want a Structured Path to Master System Design Too? Don’t Miss This!