Trees · Binary Tree Fundamentals

Binary Tree Height/Balance Check

Computing each subtree's height bottom-up in a single pass, and piggybacking a second computation (an imbalance check, a running diameter) onto that same pass instead of a separate O(n) traversal for each.

  • 7 min read
  • 11 practice questions
  • 3 graded problems
On this page
  1. Description
  2. The approach: is the tree balanced?
  3. Diameter: one extra line
  4. A second flavor: clamping a negative branch to zero
  5. Where you'll see it
  6. Which tool for which job?
  7. Common mistakes
  8. Check yourself
  9. Key takeaways
  10. Quick revise
  11. Practice

Description

Three classic tree questions are one recursion in disguise. Is the tree height-balanced? How long is its longest path (the diameter)? What's its maximum path sum? Each one computes subtree heights from the bottom up. The only difference is the extra work at each node: passing an "unbalanced" signal up, or updating a best-so-far value from the two children's results. One pass, O(n), for all of them.

We'll go from simple to more technical:

  1. The pattern: height with a -1 "unbalanced" signal.
  2. Diameter: height plus a running best.
  3. A second flavor: maximum path sum, clamping negative branches to zero.
  4. The off-by-ones and lost signals that make these subtly wrong.

The approach: is the tree balanced?

A tree is height-balanced when, at every node, the two subtrees' heights differ by at most 1. Computing the height at each node separately would repeat work. Instead, compute heights bottom up as usual, and return -1 as a signal that something below is already unbalanced. Once a node sees -1, it passes it straight up without doing anything else.

5

3

8

1

0

5

3

8

1

0

NodeLeft heightRight heightReturns
0001
1102
320-1: the heights differ by 2
5-1not computed-1: passed straight up

Node 8 is never visited: the answer was known as soon as the left side returned -1.

Java

int height(TreeNode node) {
    if (node == null) return 0;
    int left = height(node.left);
    if (left == -1) return -1;                    // already found imbalance below - propagate it up
    int right = height(node.right);
    if (right == -1) return -1;
    if (Math.abs(left - right) > 1) return -1;      // this node itself is imbalanced
    return Math.max(left, right) + 1;
}
// isBalanced(root) = height(root) != -1

Python

def height(node: TreeNode | None) -> int:
    if node is None:
        return 0
    left = height(node.left)
    if left == -1:
        return -1                      # already found imbalance below - propagate it up
    right = height(node.right)
    if right == -1:
        return -1
    if abs(left - right) > 1:
        return -1                      # this node itself is imbalanced
    return max(left, right) + 1
# is_balanced(root) = height(root) != -1

Diameter: one extra line

The longest path between any two nodes bends at some node: it goes down the left side and down the right side. With heights counted in nodes, that path has left + right edges. So the diameter is the plain height function with one extra line before the return: diameter = max(diameter, left + right). In the tree above, the best bend is at the root: 3 + 1 = 4 edges, from 0 up to 5 and down to 8.

A second flavor: clamping a negative branch to zero

Maximum path sum has the same shape, with one new twist: values can be negative. Instead of a height, each call returns its gain: the best sum of a path that starts at this node and goes down. A branch whose gain is negative only lowers the total, so clamp it to 0, which means "don't go that way". At each node, the best path that bends there is the node's value plus both clamped gains.

Java

int best = Integer.MIN_VALUE;
int maxGain(TreeNode node) {
    if (node == null) return 0;
    int leftGain = Math.max(maxGain(node.left), 0);    // clamp: never include a net-negative branch
    int rightGain = Math.max(maxGain(node.right), 0);
    best = Math.max(best, node.val + leftGain + rightGain);   // best path THROUGH this node
    return node.val + Math.max(leftGain, rightGain);           // best path continuing UP from this node
}

Python

def max_path_sum(root: TreeNode | None) -> int:
    best = float("-inf")

    def max_gain(node: TreeNode | None) -> int:
        nonlocal best
        if node is None:
            return 0
        left_gain = max(max_gain(node.left), 0)     # clamp: never include a net-negative branch
        right_gain = max(max_gain(node.right), 0)
        best = max(best, node.val + left_gain + right_gain)   # best path THROUGH this node
        return node.val + max(left_gain, right_gain)           # best path continuing UP from this node

    max_gain(root)
    return best

Take a root of 2 with children -6 and 7, where -6 has a child 4 and 7 has children -1 and 3. The left branch's gain is -6 + 4 = -2, so it's clamped to 0. The best path is 2 + 7 + 3 = 12. Note that best itself may be negative, if every path in the tree is. Only the gain passed up to a parent is clamped.

Where you'll see it

The habit to keep is getting a second answer from a walk you're already doing. A checksum can be computed while serializing a structure, or a diameter while computing heights. On large structures walked often, skipping a second full pass adds up.

Which tool for which job?

You needUse
Is it height-balanced?height, or -1 as soon as a subtree is unbalanced
Longest path between any two nodesheight + best of left + right
Largest path sumgain = value + max(0, best child gain); best of value + both gains
Any "best path through a node" questionreturn what the parent can extend; track the best that bends here
Results reused across callskeep the accumulator local to one call

Common mistakes

MistakeWhat happensDo this instead
Not passing the -1 signal upwardan imbalance deep down is missedreturn -1 if either child returned -1
Diameter in nodes when edges are asked (or the reverse)off by oneleft + right counts edges when height counts nodes
No clamp on a negative branchthe max path sum comes out too smallmax(gain, 0)
Best path sum starting at 0all-negative trees report 0start at -inf / Integer.MIN_VALUE
A static or global accumulatorthe next call reuses the old answerreset per call, or keep it local

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. One ancestor ignores the -1 from a child and computes its height normally. What's the risk?

Show answer

If that node's two heights happen to differ by at most 1, it returns a normal height. The imbalance found further down disappears, and the tree is wrongly reported as balanced. Once -1 appears, every level above must pass it straight up.

2. A diameter uses left + right + 1, with heights counted in nodes. What's wrong?

Show answer

left + right already counts the edges of the path that bends at this node. Adding 1 counts nodes instead, one too many. Diameter is usually asked in edges; check which one the problem wants.

3. A max path sum never clamps gains at 0. On a tree with only positive values, is the answer wrong?

Show answer

No. With only positive values, no gain is ever negative, so the clamp changes nothing. The bug shows only when a branch's gain is negative, like the -2 above. That's why tests need negative values.

Key takeaways

  • Balance, diameter and maximum path sum are the same bottom-up height walk with different extra work.
  • Pass the -1 "unbalanced" signal all the way up.
  • With heights in nodes, left + right is the diameter in edges.
  • In max path sum, clamp a negative gain to 0 before using it.
  • The best overall value may be negative; the gain returned to a parent never is.

Quick revise

The whole skill on one page, for the night before an interview.

Everything from the lesson and the deep dive on one page. Skim it before practice or an interview, and open the full lesson for anything that doesn't ring a bell.

Pick the tool

You needUse
Balanced?height or -1
Diameterheight + best left + right
Max path sumclamped gains + best bend
Any best pathreturn one side, record both

Snippets worth knowing by heart

Java

int best = Integer.MIN_VALUE;               // per call - not static
int gain(TreeNode n) {
    if (n == null) return 0;
    int left = Math.max(gain(n.left), 0);   // skip a losing branch
    int right = Math.max(gain(n.right), 0);
    best = Math.max(best, n.val + left + right);   // the path that bends here
    return n.val + Math.max(left, right);          // what the parent can extend
}

Python

def diameter(root) -> int:
    best = 0
    def height(node):
        nonlocal best                      # without it: UnboundLocalError
        if node is None:
            return 0
        left, right = height(node.left), height(node.right)
        best = max(best, left + right)     # edges through this node
        return 1 + max(left, right)
    height(root)
    return best

The traps

  • Losing the -1 signal: an imbalance deep down goes unnoticed. Return -1 if either child returned -1.
  • Edges vs nodes: the diameter comes out off by one. left + right counts edges when height counts nodes.
  • No clamp on gains: a negative branch drags the path sum down. Take max(gain, 0).
  • best = 0 for path sums: an all-negative tree reports 0. Start at minus infinity.
  • Static accumulators: the next call reuses the old answer. Reset per call, or keep it local.

Staff-level points, one line each

  • Fewest nodes for height 1-10: 1, 2, 4, 7, 12, 20, 33, 54, 88, 143.
  • Balanced height bound: 14 for 1,000 nodes, 28 for 1,000,000 (≈1.44 log2 n).
  • Return a one-sided path; record the path that bends here.
  • A static best gave 4 for a 2-node tree; local gave 1.
  • All-negative path sum: 0 with best = 0, -3 with -inf.

Say it in an interview

  • "One recursion computes heights; the extra work at each node answers the question."
  • "I return -1 as soon as a subtree is unbalanced, so the check is O(n)."
  • "Each node returns the best downward path and records the best path bending at it."
  • "The best starts at negative infinity, because the path must include a node."

Practice

This skill comes with 11 multiple-choice questions that tell you which misconception a wrong answer reveals and 3 graded problems. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice Binary Tree Height/Balance Check free

Balanced Binary Tree

MediumProCoding problemJava · Python

Given the root of a binary tree, return whether it is height-balanced: for every node, the heights of its left and right subtrees differ by at most 1.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Diameter of Binary Tree

MediumProCoding problemJava · Python

The diameter of a binary tree is the length, in edges, of the longest path between any two of its nodes. That path doesn't have to go through the root. Return it.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Binary Tree Maximum Path Sum

HardProCoding problemJava · Python

Each node of a binary tree holds a number, possibly negative. A path is a chain of nodes joined by parent-child edges that uses each node at most once; it can start and end anywhere. Return the largest sum a path can have.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice Binary Tree Height/Balance Check free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime