Balanced Binary Tree
You are given the root of a binary tree. Decide whether it is height-balanced.
A tree is height-balanced when, at every node, the heights of its left and right subtrees differ by at most 1. The height of a subtree is the number of nodes on its longest downward path, so an empty subtree has height 0 and a single node has height 1.
Return true if the tree is height-balanced and false otherwise. An empty tree is balanced.
Trees are written in level order: the root first, then each level from left to right, with null marking a missing child. For example [4,2,null,1] is a root 4 whose left child 2 has a left child 1.
Example 1
Example 2
Example 3
Constraints
0 <= number of nodes <= 5000-104 <= Node.val <= 104- The depth of the tree is at most 100.
Pattern Path · Trees: DFS · problem 81 of 169
Start simple
For every node, compute the heights of both subtrees from scratch and compare them: O(n²) time on a skewed tree (O(n log n) on a balanced one).
The key idea
The balance condition at a node needs exactly one fact from each child: its height. Heights are naturally computed bottom-up, since a node's height is 1 + max(left height, right height). So compute heights in a post-order traversal and check the balance condition at the same time.
Let the helper return the height of a subtree, or -1 to mean "somewhere inside, this subtree is unbalanced". At each node:
- get the left result; if it is
-1, pass-1straight up without exploring further; - get the right result and do the same;
- if the two heights differ by more than 1, return
-1; otherwise return the node's height.
The tree is balanced exactly when the root's result is not -1. Each node is visited once, so the time is linear, compared with recomputing heights from every node. The recursion uses stack space proportional to the tree's height.
Complexity
- Time:
O(n) - Space:
O(h)
Python
class Solution:
def isBalanced(self, root):
def height(node):
"""Height of the subtree, or -1 if it is unbalanced somewhere inside."""
if node is None:
return 0
left = height(node.left)
if left == -1:
return -1
right = height(node.right)
if right == -1 or abs(left - right) > 1:
return -1
return 1 + max(left, right)
return height(root) != -1
JavaScript
var isBalanced = function(root) {
// Height of the subtree, or -1 if it is unbalanced somewhere inside.
const height = (node) => {
if (node === null) return 0;
const left = height(node.left);
if (left === -1) return -1;
const right = height(node.right);
if (right === -1 || Math.abs(left - right) > 1) return -1;
return 1 + Math.max(left, right);
};
return height(root) !== -1;
};
What the interviewer is checking
The interviewer wants to see whether you notice the repeated height computations of the naive solution and fuse the check into a single bottom-up pass that returns more than a boolean.
Mistakes to avoid
- Only comparing the heights of the root's two subtrees; an imbalance can hide deeper in the tree.
- Calling a separate height function from every node, which is quadratic on skewed trees.
- Confusing height with the number of edges and getting the empty-subtree base case wrong. Any consistent convention works if used everywhere.
Follow-ups to expect
- How would you rebalance an unbalanced BST? (In-order list, rebuild from the middle.)
- How do AVL trees keep this property after inserts? (Store heights, rotate on imbalance.)
- Can you return the first unbalanced node instead? (Report it from the bottom-up pass.)
In real software
Self-balancing search trees in databases and language libraries (AVL and red-black trees) maintain a balance invariant like this one so that lookups stay logarithmic.