Diameter of Binary Tree is the problem that teaches you the post-order DFS trick: have a recursive call return one thing (the height) while quietly updating something else on the side (the best path). Once you see it, a whole family of tree problems falls open.
Problem. Given the root of a binary tree, return the length of its diameter — the number of
edges on the longest path between any two nodes. That path may or may not pass through the root.
Example: for the tree with root 1, children 2 and 3, and 2 having children 4 and 5, the
answer is 3 (the path 4 - 2 - 1 - 3 has 3 edges).
The slow way first
The brute-force idea: for every node, compute the height of its left subtree and the height of its right subtree, add them to get the longest path through that node, and take the max over all nodes. That is correct, but each height call walks a whole subtree, and we redo that work at every node — so it is O(n²) on a skewed tree.
The question to ask: can I get the height AND update the best path in the same pass? Yes — compute each height exactly once, and as the value bubbles up, fold it into a running maximum.
The idea: one DFS returns height, a side variable tracks the best
Do a single post-order DFS. For each node, recurse into both children first; they hand back left and right heights. The longest path through this node is left + right edges, so update a shared best. Then return this node height, max(left, right) + 1, to the parent.
The key insight: a node returns a height to its parent, but the answer lives in the side variable best, updated at every node. Heights compose; the diameter is just the largest left + right seen anywhere.
Walk through it
Step through the animation. DFS dives to leaf 4 first (height 1), then 5, then combines at node 2: left + right = 1 + 1 = 2, so best becomes 2. It visits leaf 3, then the root: left = 2, right = 1, so the path through the root is 3 — the new and final best. That winning path 4 - 2 - 1 - 3 lights up at the end.
Pseudocode
best = 0
define height(node):
if node is None:
return 0 # empty subtree has height 0
left = height(node.left) # recurse first (post-order)
right = height(node.right)
best = max(best, left + right) # path THROUGH node, in edges
return max(left, right) + 1 # height handed to the parent
call height(root)
return bestThe Python solution
def diameter_of_binary_tree(root):
best = 0
def height(node):
nonlocal best
if node is None:
return 0
left = height(node.left)
right = height(node.right)
best = max(best, left + right)
return max(left, right) + 1
height(root)
return bestbestis the answer, shared vianonlocalso the innerheightcan update it.- The base case: an empty subtree (
None) has height0. - We recurse into both children before doing anything — that is what makes it post-order.
- Line 9 is the heart:
left + rightis the longest path through this node, measured in edges, and we fold it intobest. - We return
max(left, right) + 1so the parent gets this node height — one more than its taller child.
Complexity
| Case | Time | Notes |
|---|---|---|
| Brute force (height per node) | O(n²) (slow) | recompute heights repeatedly |
| Single DFS (this solution) | O(n) (moderate) | each node visited once |
O(h) (moderate)We visit every node once, so time is O(n). The extra space is the recursion stack, O(h) where h is the tree height — O(log n) for a balanced tree, O(n) for a skewed one.
When this pattern shows up
Whenever a tree problem asks for a longest path, a max path sum, or whether the tree is balanced, reach for post-order DFS that returns one quantity (usually height) while updating a side answer. The return value feeds the parent; the global variable holds the result.
Diameter is counted in edges, not nodes. The path through a node is left + right, NOT
left + right + 1. If a problem instead asks for the number of nodes on the path, add one.
Practice
At node 2 the two children each returned height 1. What is the path through node 2, and does best change?
1. What does the recursive height function return to its parent?
2. How is the longest path through a single node computed?
3. Why is the single-DFS solution O(n) instead of O(n²)?
4. Does the diameter path always pass through the root?