Algorithm

Balanced Binary Tree

Trees Pattern

Balanced Binary Tree

Given the root of a binary tree, return true if the tree is height-balanced. Height-balanced means that at every node in the tree, the heights of its left and right subtrees differ by at most 1 — not just at the root. An empty tree is balanced, so a null root returns true. You return a boolean only, not the height.

CONSTRAINTS
  • The number of nodes in the tree is in the range [0, 5000]
  • -10⁴ <= Node.val <= 10⁴
  • The balance condition must hold at every node, not only at the root
  • An empty subtree has height 0
EXAMPLE 1
Input: root = [3,9,20,null,null,15,7]
Output: true
At the root, the left side (just 9) has height 1 and the right side (20 with its two children) has height 2 — a difference of 1, which is allowed. Every deeper node has two empty or equal sides, so the condition holds everywhere.
EXAMPLE 2
Input: root = [1,2,2,3,3,null,null,4,4]
Output: false
The root's left subtree reaches down three levels while its right subtree is a single node, a difference of 2. One violation anywhere is enough to make the whole answer false.
EXAMPLE 3
Input: root = [1,2,3,4,5,6,null,8]
Output: true
The tree is visibly lopsided — the left side is heavier — yet no single node's two sides differ by more than 1. Balance is a strictly local condition checked everywhere, not a judgement about how symmetric the tree looks.
EXAMPLE 4
Input: root = []
Output: true
There is no node at which the condition could fail, so an empty tree is balanced by definition rather than as a special case.
Does the condition apply only at the root, or at every node?
Every node. A tree whose root looks even can still be unbalanced deep inside, and that is the case most naive attempts get wrong.
How is height defined — nodes or edges?
Nodes here, matching Maximum Depth: an empty subtree is 0 and a leaf is 1. The convention does not change the answer, since only the *difference* of two heights matters, but mixing conventions within one solution does.
Should I return the height as well as the verdict?
The required return is just the boolean. Whether you compute heights internally is your business — and it turns out to matter a great deal for efficiency.
Is 'balanced' here the same as a perfectly balanced or complete tree?
No, it is looser. A difference of 1 is permitted at every node, so plenty of visibly uneven trees qualify. It is the AVL condition, which is enough to keep the height O(log N).

A tree is balanced when, at every node, its two sides differ in height by at most 1 — not just at the root. So a tree can look perfectly even up top and still be disqualified by one lopsided node buried deep inside.

text
          1                 check |left height - right height| at EVERY node
        /   \
       2     2              at root:  left height 3, right height 1
      / \                            differ by 2   ->  NOT balanced
     3   3
    / \
   4   4

Read the picture: the left side plunges three levels down (2 → 3 → 4) while the right side stops at a single node. At the root that is a height gap of 2, which breaks the rule — so the whole tree is false. Notice this is a purely local test repeated everywhere; one bad node sinks the answer.

One height check per node

Here's the whole plan in one line. At each node, measure how tall its left side is and how tall its right side is; if those two heights differ by more than 1, the tree is not balanced. Do that test at every node, and the tree is balanced only if nobody fails.

To run that test a node needs the height of its left subtree and the height of its right subtree. It can't know those alone, so it asks both children first and waits for their heights to come back — the work happens after the children answer, which is post-order.

A height to return, a verdict to keep

Once the two heights come back, a node has two separate things on its hands:

- The verdict: do these two sides differ by more than 1? If yes, balance is broken. That's the answer we actually want.
- The height to report to its own parent: 1 + max(left, right), because the parent needs it to run its own check.

A function returns only one value, and the parent needs the height. So each node returns its height and, if its own two sides disagree by too much, flips a shared balanced flag kept outside the recursion. Every node gets its turn as the meeting point; balanced holds the final verdict at the end.

python
balanced = True

def height(node):
    nonlocal balanced
    if node is None:
        return 0
    left  = height(node.left)
    right = height(node.right)
    if abs(left - right) > 1:
        balanced = False               # this node's two sides disagree too much
    return 1 + max(left, right)        # height I report upward

height(root)
return balanced

One thing to hold onto: the value a node returns is its height, but the thing we're really chasing is the boolean. When those two differ, return what the parent needs and keep the real answer in a variable on the side — exactly the move from Diameter of Binary Tree, where the side value was a running maximum instead of a flag.

Walk it through

Same tree. The number in [brackets] is the height each node returns upward; watch balanced flip at the root:

text
          1 [4]        sides (3, 1) differ by 2  ->  balanced = False
        /    \
    2 [3]     2 [1]    left 2: sides (2, 1) differ by 1  ->  ok
    /  \
 3 [2]  3 [1]          left 3: sides (1, 1) differ by 0  ->  ok
 /  \
4[1] 4[1]              leaves: sides (0, 0)  ->  height 1
- Each 4 is a leaf: children return 0, difference 0, reports height 1.
- Left 3 sees (1, 1): difference 0, reports 2. Right 3 is a leaf, reports 1.
- Left 2 sees (2, 1): difference 1, allowed, reports 3. Right 2 reports 1.
- Root sees (3, 1): difference 2 — flips balanced to False, and still reports its height 4 honestly.

Follow the left 2 carefully: it passed its own test and reported honestly. Balance isn't broken by a node being deep — only by a node whose two sides disagree by too much. The root was the first place that comparison went wrong.

The idea to keep

The value a node hands its parent isn't always the answer you're really chasing. When the two differ, return the one the parent needs (the height) and keep the real answer (the verdict) in a variable on the side. Each node is asked once and answers once, so nothing is recomputed — O(N) time, O(H) stack.

(An equivalent trick avoids the side variable: since real heights are never negative, let a node return -1 to mean "something below me is broken." A node returns its true height if all is well, or -1 the moment it or anything beneath it fails. That works only because -1 is impossible as a genuine height — pick a sentinel a real computation could produce and the code silently misreads good data as failure.)

Interactive Strategy Visualization

Balance Calculus

1234ABS(1 - 0)DIFF: 1
Checking Node 2. |1-0| <= 1. OK.
Recursive Height Check
Logic Trace
Visiting nodes in Post-Order. Comparing sub-heights...
Rule Definition
abs(height(L) - height(R)) ≤ 1

"A single -1 return bubbles up the failure immediately (Early Exit)."

Strategy

We calculate height and check balance in one pass. If a subtree is unbalanced, we return -1 instead of height.

"Height = max(L, R) + 1"

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