Python Program to Check if Binary Tree is Balanced
def is_balanced(root): with a helper function that returns -1 if unbalanced.Examples
How to Think About It
Algorithm
Code
class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right def is_balanced(root): def height(node): if not node: return 0 left_height = height(node.left) if left_height == -1: return -1 right_height = height(node.right) if right_height == -1: return -1 if abs(left_height - right_height) > 1: return -1 return max(left_height, right_height) + 1 return height(root) != -1 # Example usage: root = TreeNode(1, TreeNode(2), TreeNode(3)) print(is_balanced(root))
Dry Run
Let's trace the example tree with root = 1, left child = 2, right child = 3 through the code
Start at root node 1
Call height on node 1
Check left subtree of node 1
Call height on node 2, which has no children, returns 1
Check right subtree of node 1
Call height on node 3, which has no children, returns 1
Compare heights at node 1
Left height = 1, Right height = 1, difference = 0 <= 1, so return 2
Final result
height(root) = 2, not -1, so tree is balanced (True)
| Node | Left Height | Right Height | Height Returned | Balanced? |
|---|---|---|---|---|
| 2 | 0 | 0 | 1 | Yes |
| 3 | 0 | 0 | 1 | Yes |
| 1 | 1 | 1 | 2 | Yes |
Why This Works
Step 1: Calculate height bottom-up
The function calculates the height of each subtree starting from the leaves up to the root using recursion.
Step 2: Check balance at each node
At each node, it compares the heights of left and right subtrees using abs(left_height - right_height) > 1 to detect imbalance.
Step 3: Use -1 as a signal
If any subtree is unbalanced, the helper returns -1 immediately to stop further checks and propagate the failure.
Alternative Approaches
def height(node): if not node: return 0 return max(height(node.left), height(node.right)) + 1 def is_balanced(root): if not root: return True left_height = height(root.left) right_height = height(root.right) if abs(left_height - right_height) > 1: return False return is_balanced(root.left) and is_balanced(root.right) # This method recalculates heights multiple times, less efficient.
def is_balanced(root): if not root: return True stack = [(root, 0)] heights = {} while stack: node, state = stack.pop() if node is None: continue if state == 0: stack.append((node, 1)) stack.append((node.right, 0)) stack.append((node.left, 0)) else: left_height = heights.get(node.left, 0) right_height = heights.get(node.right, 0) if abs(left_height - right_height) > 1: return False heights[node] = max(left_height, right_height) + 1 return True # Uses explicit stack to avoid recursion, more complex but useful for large trees.
Complexity: O(n) time, O(h) space
Time Complexity
The algorithm visits each node once, so it runs in O(n) time where n is the number of nodes.
Space Complexity
The recursion stack can go as deep as the tree height h, so space is O(h).
Which Approach is Fastest?
The bottom-up approach with early stopping is fastest because it avoids repeated height calculations unlike the top-down method.
| Approach | Time | Space | Best For |
|---|---|---|---|
| Bottom-up with early stop | O(n) | O(h) | Balanced check with best efficiency |
| Top-down recursive | O(n^2) | O(h) | Simple but slow for large trees |
| Iterative with stack | O(n) | O(h) | Avoid recursion, handle large trees |