Given a binary tree, determine if it is height-balanced.
A height-balanced binary tree is defined as a binary tree in which the depth of the two subtrees of every node never differs by more than 1.
Example 1:
Consider the following binary tree:
3
/ \
9 20
/ \
15 7
Input: root = [3,9,20,null,null,15,7] Output: true
Example 2:
Consider the following binary tree:
1
/ \
2 2
/ \
3 3
/ \
4 4
Input: root = [1,2,2,3,3,null,null,4,4] Output: false
Example 3:
Input: root = [] Output: true
Write a function that takes the root of a binary tree as input and returns true if the tree is height-balanced, and false otherwise. Explain your approach and its time and space complexity. Consider both a naive and an optimized approach, detailing the trade-offs of each.
Given a binary tree, determine if it is height-balanced. A height-balanced binary tree is defined as a binary tree in which the depth of the two subtrees of every node never differs by more than 1.
The most straightforward approach is to check the balance condition for each node in the tree. This involves calculating the height of the left and right subtrees for every node and ensuring that the absolute difference between these heights is no more than 1. If this condition holds for every node, the tree is balanced.
Algorithm:
height(node)
to calculate the height of a subtree rooted at node
. The height of an empty tree is 0, and the height of a non-empty tree is 1 + max(height of left subtree, height of right subtree).isBalanced(root)
to check if the tree is balanced. This function recursively checks the balance condition for each node.height
function.false
.true
.Code (Java):
class Solution {
public boolean isBalanced(TreeNode root) {
if (root == null) return true;
int leftHeight = height(root.left);
int rightHeight = height(root.right);
if (Math.abs(leftHeight - rightHeight) > 1) return false;
return isBalanced(root.left) && isBalanced(root.right);
}
private int height(TreeNode node) {
if (node == null) return 0;
return 1 + Math.max(height(node.left), height(node.right));
}
}
Time Complexity: O(n^2), where n is the number of nodes in the tree. The height
function is called for each node, and in the worst case, it traverses all nodes in the subtree.
Space Complexity: O(h), where h is the height of the tree. This is due to the recursive call stack. In the worst case (skewed tree), h = n, so the space complexity becomes O(n).
A more efficient approach is to calculate the height and check for balance in a single traversal. This can be done using a modified depth-first search (DFS).
Algorithm:
getHeight(node)
that returns the height of the subtree rooted at node
. If the subtree is unbalanced, it returns -1.node
is null, return 0 (base case).isBalanced(root)
calls getHeight(root)
and returns true
if the result is not -1, and false
otherwise.Code (Java):
class Solution {
public boolean isBalanced(TreeNode root) {
return getHeight(root) != -1;
}
private int getHeight(TreeNode node) {
if (node == null) return 0;
int leftHeight = getHeight(node.left);
if (leftHeight == -1) return -1;
int rightHeight = getHeight(node.right);
if (rightHeight == -1) return -1;
if (Math.abs(leftHeight - rightHeight) > 1) return -1;
return 1 + Math.max(leftHeight, rightHeight);
}
}
Time Complexity: O(n), where n is the number of nodes in the tree. Each node is visited only once.
Space Complexity: O(h), where h is the height of the tree. This is due to the recursive call stack. In the worst case (skewed tree), h = n, so the space complexity becomes O(n). In the best case (balanced tree), h = log n, so the space complexity is O(log n).