Diameter of Binary Tree
Given the root of a binary tree, return the length of the longest path between any two nodes in it. The path is measured in edges (links), not nodes, so a path touching k nodes has length k - 1. The path may bend — it can go up from one node and back down into the other subtree — and it is not required to pass through the root. A single-node tree has diameter 0.
- The number of nodes in the tree is in the range [1, 10⁴]
- -100 <= Node.val <= 100
- Length is counted in edges, not nodes
- The path need not include the root, and need not start or end at a leaf
root = [1,2,3,4,5]3root = [1,2]1root = 1 with left child 2 (a leaf) and right child 3; below 3 hang two chains of three nodes each, 4-5-6 on the left and 7-8-9 on the right6root = [1]0A path is just a walk from one node to another, stepping along the links that connect them and never using the same node twice. Its length is the number of links (edges) you step over. The diameter is the length of the longest path you can find anywhere in the tree — the two nodes that sit farthest apart.
1
/ \
2 3
/ \
4 5
longest path: 4 -> 2 -> 1 -> 3 it climbs UP to 1, then back DOWN
edges stepped over: 4-2, 2-1, 1-3 = 3 edges -> diameter = 3Read the picture: the path starts at leaf 4, climbs up through 2 and the root 1, then descends to 3. It bends once at the top. We count the links it crosses (3 of them), not the nodes.
One thing that trips people up: that longest path does not have to pass through the root. It might live entirely inside the left subtree, or the right one, far from the top. So we can't just measure "down the left of the root plus down the right of the root" and call it done.
Here's the whole plan in one line. Every node has a longest path that runs through it: go as deep as you can down its left side, and as deep as you can down its right side. That length is simply left height + right height. The diameter is the biggest of these over all nodes — check every node as the meeting point, keep the largest.
That's it. Since any path in the tree runs through some highest node, checking every node as that meeting point covers every possible path exactly once.
To answer "how far down can I reach on each side," a node needs the depth of its left subtree and the depth of its right subtree. It can't know those by itself, so it asks both children first and waits for their depths to come back. Only then does it do its own work — so the work happens after the children answer, which is post-order.
Here's the catch that makes this problem its own thing. Once the depths come back, a node actually wants two different values:
left + right. That's a candidate for the final answer.1 + max(left, right). A parent that runs a path through this node enters and leaves on a single side — it can't use both.A function returns only one value. So each node returns its depth (the parent needs that to keep going) and drops its left + right candidate into a best variable kept outside the recursion. Every node gets its turn as the top, and best holds the winner at the end.
best = 0
def depth(node):
nonlocal best
if node is None:
return 0
left = depth(node.left)
right = depth(node.right)
best = max(best, left + right) # path with THIS node on top: both sides
return 1 + max(left, right) # depth I report upward: one side only
depth(root)
return bestOne thing to hold onto: the returned value uses max (one side), the tracked value uses + (both sides). Swapping those two is the classic bug.
Same tree: root 1, children 2 and 3; under 2 hang leaves 4 and 5. Each node returns a depth (one side, in [brackets]) but also drops a bend candidate (both sides) into best:
1 [depth 2] best = max(2, 2+1) = 3 bend 4-2-1-3
/ \
2 [2] 3 [1] at 2: best = max(0, 1+1) = 2 bend 4-2-5
/ \
4 [1] 5 [1]best stays 0.best = max(0, 1 + 1) = 2 (path 4–2–5) → returns 1 + max(1,1) = 2.best = max(2, 2 + 1) = 3 (path 4–2–1–3) → returns 3.Notice a path that never touches the root needs no special handling: whichever node sits on top of it records it in best on its own.
The value a node hands up to its parent isn't always the answer you're really chasing. When the two differ, return the one the parent needs and keep the real answer in a variable on the side. Each node is asked once and answers once, so nothing is recomputed — O(N) time, O(H) stack.
Diameter Calculus
Global Max Tracker Trace
"Diameter is NOT always through the root. A shallow root might have a deep subtree cluster."
Strategy
The core trick is calculating height while simultaneously updating a global max diameter variable using `Height(L) + Height(R)`.
"Diameter = max(Diameter, L + R)"