Algorithm

Diameter of Binary Tree

Trees Pattern

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.

CONSTRAINTS
  • 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
EXAMPLE 1
Input: root = [1,2,3,4,5]
Output: 3
The path 4 → 2 → 1 → 3 uses 3 edges. It climbs from 4 up to the root and comes back down the other side, which is allowed — a path is any route through the tree that never repeats a node.
EXAMPLE 2
Input: root = [1,2]
Output: 1
Only one edge exists, so the longest path is that single link. Counting nodes would wrongly give 2.
EXAMPLE 3
Input: root = 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 right
Output: 6
The best path is 6 → 5 → 4 → 3 → 7 → 8 → 9, six edges, and it never touches the root. Any route through the root must spend most of its length on the stunted side containing only 2, so it cannot compete.
EXAMPLE 4
Input: root = [1]
Output: 0
With a single node there are no edges at all, so the longest path has length 0 rather than 1.
Is length counted in edges or nodes?
Edges. This differs from Maximum Depth, which counts nodes, so the two problems' base values do not line up — a frequent source of off-by-one errors when reusing the height helper.
Does the path have to pass through the root?
No. It can live entirely inside a subtree, which is exactly why checking only the root is wrong.
Can the path change direction?
Once. It may climb to some highest node and then descend on the other side, but it can never dip down and climb again, since that would revisit a node.

A 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.

text
        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 = 3

Read 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.

One longest path per node

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.

Two numbers at each node, and they differ

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:

- The path with this node on top uses both sides: left + right. That's a candidate for the final answer.
- But the depth this node reports to its own parent can only use one side: 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.

python
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 best

One thing to hold onto: the returned value uses max (one side), the tracked value uses + (both sides). Swapping those two is the classic bug.

Walk it through

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:

text
        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]
- Leaves 4 and 5 have only empty children → each returns depth 1; best stays 0.
- Node 2 gets 1 and 1 back → best = max(0, 1 + 1) = 2 (path 4–2–5) → returns 1 + max(1,1) = 2.
- Leaf 3 returns depth 1.
- Root 1 gets 2 (from 2) and 1 (from 3) → best = max(2, 2 + 1) = 3 (path 4–2–1–3) → returns 3.
- Answer 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 idea to keep

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.

Interactive Strategy Visualization

Diameter Calculus

Global Max Tracker Trace

1234
Leaf node (4). Height = 1. Max Diameter so far = 0.
Global Tracker
Max Diameter found
0
Current Node Sub-heights
Left: 0
Right: 0

"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)"

O(N × H) Height Recomputed Per Node
O(N) Time · O(H) Space Fused Pass With Running Maximum