Determine if a binary tree is height-balanced, meaning no node's left subtree and right subtree have a height difference greater than 1.
Output: False
New to this one? Start here. Below is the core idea in everyday language, plus a real-world analogy—then the sections that follow build on it with the formal reasoning, code, and step-by-step walkthroughs.
A hanging mobile stays level only if the arms on both sides are close in length at every joint. One overloaded arm anywhere and the whole thing tilts. A tree is balanced only if this “within one” rule holds at every node, not just the top.
For a binary tree to be balanced, all its subtrees would need to be balanced too. This implies that the height difference between the left and right subtrees of each node should be at most 1. A difference greater than 1 indicates a height imbalance.
This suggests we need a way to determine the heights of the left and right subtrees at each node in order to evaluate if the subtree rooting from that node is balanced or not.
A key insight is that the height of a tree is equal to the depth of its deepest subtree, plus 1, to include the tree’s root node.
The above formula reveals a recursive relationship, where we can recursively determine the heights of the left and right subtrees to calculate the height of the current subtree. The base case of the recursion would be returning 0 upon encountering a null node, since they have a height of 0.
The diagram below displays the heights returned from the left and right children of each node, and shows how we determine if a subtree is imbalanced. This highlights the recursive process, where values bubble up from the bottom and make their way up to the root node. At each node, we also evaluate whether that node represents a height-balanced subtree. This reveals an imbalance at node 7:
However, there's a flaw in only returning the subtree’s height at each node: upon detecting node 7 is imbalanced, all we did about this was return its height to its parent node. This consequently means its parent node (node 5) could be mistakenly considered balanced.
An important thing to remember is that if one subtree is imbalanced, the entire tree is considered imbalanced. This means node 5 should also be marked as imbalanced. We can fix this by returning -1 upon encountering an imbalanced node, essentially informing parent nodes of this imbalance:
To finalize our answer, we return false if the root node of the binary tree returns -1, and true otherwise.
from ds import TreeNode
def balanced_binary_tree_validation(root: TreeNode) -> bool:
return get_height_imbalance(root) != -1
def get_height_imbalance(node: TreeNode) -> int:
# Base case: if the node is null, its height is 0.
if not node:
return 0
# Recursively get the height of the left and right subtrees. If either subtree
# is imbalanced, propagate -1 up the tree.
left_height = get_height_imbalance(node.left)
right_height = get_height_imbalance(node.right)
if left_height == -1 or right_height == -1:
return -1
# If the current node's subtree is imbalanced (height difference > 1), return -1.
if abs(left_height - right_height) > 1:
return -1
# Return the height of the current subtree.
return 1 + max(left_height, right_height)int heightBal(TreeNode* n) {
if (n == nullptr) {
return 0;
}
int l = heightBal(n->left);
if (l == -1) {
return -1;
}
int r = heightBal(n->right);
if (r == -1) {
return -1;
}
if (abs(l - r) > 1) {
return -1;
}
return 1 + max(l, r);
}
bool balancedBinaryTreeValidation(TreeNode* root) {
return heightBal(root) != -1;
}int heightBal(TreeNode n) {
if (n == null) return 0;
int l = heightBal(n.left); if (l == -1) return -1;
int r = heightBal(n.right); if (r == -1) return -1;
if (Math.abs(l - r) > 1) return -1;
return 1 + Math.max(l, r);
}
boolean balancedBinaryTreeValidation(TreeNode root) { return heightBal(root) != -1; }Time complexity: The time complexity of balanced_binary_tree_validation is , where denotes the number of nodes in the tree. This is because it recursively traverses each node of the tree once.
Space complexity: The space complexity is due to the space taken up by the recursive call stack, which can grow as large as the height of the binary tree. The largest possible height of a binary tree is .
Two step-by-step walkthroughs of the algorithm below: a valid case (the expected happy path) and an invalid / edge case that exercises the tricky parts. Each step shows the program state as the code runs—the same steps apply to Python, C++ and Java.
| node | left h | right h | |diff| | returns |
|---|---|---|---|---|
| 4, 5, 3 | 0 | 0 | 0 | height 1 |
| 2 | 1 | 1 | 0 | height 2 |
| 1 | 2 | 1 | 1 | height 3 (≤ 1, OK) |
height(root) = 3 ≠ −1 → balanced = true ✓
| node | left h | right h | |diff| | returns |
|---|---|---|---|---|
| 5 | 0 | 0 | 0 | height 1 |
| 4 | 1 | 0 | 1 | height 2 |
| 2 | 2 | 0 | 2 | −1 (unbalanced!) |
| 1 | −1 | — | — | −1 (short-circuit) |
At node 2, the left subtree is height 2 but the right is 0 — difference 2. We return −1, and node 1 sees the −1 from its left child and immediately returns −1 without even looking right. balanced = false ✓
The helper height is nested so it can be pure. Each early return -1 stops us from touching the other subtree.
Compact one-liners with inline if (l == -1) return -1;. abs and max come from <cstdlib>/<algorithm>.
Same structure using Math.abs and Math.max. The −1 sentinel travels up the return values exactly as in Python and C++.