Topics › Binary Trees › Medium Problems
Check for balanced binary tree
LeetCode ↗ Java source ↗ Edit this article ↗
Jump to code:
Problem
Given the root of a binary tree, decide whether it is height-balanced: for every node, the heights of its left and right subtrees differ by at most 1. The height of an empty tree is 0 and of a single node is 1. An empty tree is balanced.
Constraints: 0 ≤ n ≤ 5000 nodes in the LeetCode version, values fit in an int.
Example 1
tree (level order) = [3, 9, 20, null, null, 15, 7]
3
/ \
9 20
/ \
15 7
answer = true // at 3: heights 1 and 2; at 20: 1 and 1; leaves: 0 and 0
Example 2
tree (level order) = [1, 2, 2, 3, 3, null, null, 4, 4]
1
/ \
2 2
/ \
3 3
/ \
4 4
answer = false // at the root: left height 3, right height 1
The word “every” matters. Look at [1, 2, 2, 3, null, null, 3, 4, null, null, 4]: the root’s left and right subtrees both have height 3, so the root alone looks perfect, but each 2 has a chain of length two on one side and nothing on the other. The tree is not balanced. Checking only the root is the classic wrong answer.
Approach 1: Brute force
Intuition. Translate the definition literally. For the current node, compute the height of the left subtree and of the right subtree with a separate height function, and fail if they differ by more than one. Then apply the same check to both children. The problem is that the height function walks an entire subtree, and we call it again for every node inside that subtree, so the same nodes are measured over and over.
Algorithm.
- If the node is
null, returntrue. - If
|height(left) - height(right)| > 1, returnfalse. - Return
check(left) && check(right).
static int height(TreeNode node) {
if (node == null) return 0;
return 1 + Math.max(height(node.left), height(node.right));
}
static boolean bruteForce(TreeNode root) {
if (root == null) return true;
if (Math.abs(height(root.left) - height(root.right)) > 1) return false;
return bruteForce(root.left) && bruteForce(root.right);
}
Complexity. Time O(n^2) as a worst-case bound: each of the n nodes can trigger height computations over a subtree of size up to n. On a balanced tree the cost is O(n log n), because a node’s height is recomputed once for every ancestor and a balanced tree has only O(log n) levels. Space O(h) for the recursion.
Approach 2: Optimal, one post-order pass
Intuition. The waste in Approach 1 is that heights are computed top-down, again and again. Flip the direction: compute heights bottom-up, once, and check the balance condition at each node while its two child heights are already in hand. A post-order traversal does exactly that, because a node is processed after both its children.
We still need a way to report “this subtree is already unbalanced” upward. Heights are never negative, so a return value of -1 can serve as a sentinel: it means “unbalanced somewhere below”. Once any call returns -1, every caller above it returns -1 immediately without doing more work, and the root’s result tells us the answer. One function returns both pieces of information, height and validity, in a single int.
Algorithm.
checkHeight(null) = 0.- Compute
lh = checkHeight(left); if it is-1, return-1. - Compute
rh = checkHeight(right); if it is-1, return-1. - If
|lh - rh| > 1, return-1. - Otherwise return
1 + max(lh, rh), the real height. - The tree is balanced exactly when
checkHeight(root) != -1.
static boolean optimal(TreeNode root) {
return checkHeight(root) != -1;
}
static int checkHeight(TreeNode node) {
if (node == null) return 0;
int lh = checkHeight(node.left);
if (lh == -1) return -1;
int rh = checkHeight(node.right);
if (rh == -1) return -1;
if (Math.abs(lh - rh) > 1) return -1;
return 1 + Math.max(lh, rh);
}
Dry run on Example 2:
| node | lh | rh | returns |
|---|---|---|---|
| 4 (left) | 0 | 0 | 1 |
| 4 (right) | 0 | 0 | 1 |
| 3 (with children) | 1 | 1 | 2 |
| 3 (leaf) | 0 | 0 | 1 |
| 2 (left of root) | 2 | 1 | 3 |
| 2 (right of root) | 0 | 0 | 1 |
| 1 (root) | 3 | 1 | -1, difference is 2 |
The root returns -1, so the answer is false.
Complexity. Time O(n): every node is visited at most once, and each visit does constant work. Space O(h) for the recursion, where h is the height, O(log n) for balanced trees and O(n) for a chain (although a chain longer than two nodes is detected as unbalanced as soon as the recursion returns from its bottom).
Why the early exits matter. They are needed for correctness, not only for speed. Without them a -1 from one side would be mixed with a real height from the other. Take lh = -1 (the left subtree is broken) and rh = 0 (no right child): the difference is exactly 1, so the node would pass the balance test and return 1 + max(-1, 0) = 1, a perfectly ordinary height, and the failure would vanish. Two failing children, lh = rh = -1, would return 0 and vanish the same way. Returning -1 the moment a child reports it keeps the sentinel intact all the way to the root, and as a bonus skips the right subtree once the left one has failed.
Edge cases and pitfalls
- Empty tree. Balanced, and both methods return
true. - Single node, or a root with one leaf child. Balanced: heights
1and0differ by exactly one, which is allowed.[1, 2]is in the Java tests. - A chain of three.
[1, null, 2, null, 3]is unbalanced at the root (heights0and2). - Root-only check. The tree
[1, 2, 2, 3, null, null, 3, 4, null, null, 4]has equal subtree heights at the root yet is unbalanced deeper down. Approach 1 handles it by recursing into the children; Approach 2 handles it because the inner2nodes already return-1. - Sentinel choice.
-1works only because real heights are non-negative. If you count height in edges (empty tree-1), pick a different sentinel such asInteger.MIN_VALUE, or return a small result object. - Testing. The Java file compares the two methods on 500 seeded random trees and checks that both outcomes, balanced and unbalanced, occur among them.
Related problems
- Maximum Depth in BT is the height recursion this problem is built on.
- Diameter of Binary Tree uses the same bottom-up pass, recording a different value at each node.
- Maximum path sum is the same pattern with sums in place of heights.
- Check if two trees are identical or not is another “combine answers from both children” recursion.