Check if Binary Tree is Balanced in DSA Python - Time & Space Complexity
We want to know how long it takes to check if a binary tree is balanced.
Specifically, how the time grows as the tree gets bigger.
Analyze the time complexity of the following code snippet.
class Node:
def __init__(self, val):
self.val = val
self.left = None
self.right = None
def is_balanced(root):
def check(node):
if not node:
return 0
left = check(node.left)
if left == -1:
return -1
right = check(node.right)
if right == -1:
return -1
if abs(left - right) > 1:
return -1
return max(left, right) + 1
return check(root) != -1
This code checks if a binary tree is balanced by measuring heights and verifying the difference is not more than one at any node.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Recursive calls visiting each node once.
- How many times: Each node is visited exactly one time.
As the tree grows, the function visits every node once to check heights.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 visits |
| 100 | About 100 visits |
| 1000 | About 1000 visits |
Pattern observation: The number of operations grows directly with the number of nodes.
Time Complexity: O(n)
This means the time to check balance grows linearly with the number of nodes in the tree.
[X] Wrong: "The function checks each node multiple times, so it is O(n²)."
[OK] Correct: The code uses a single pass recursion that returns height and balance status together, so each node is visited only once.
Understanding this linear time check shows you can analyze recursive tree problems efficiently, a key skill in many coding challenges.
"What if the function separately calculated height and balance in two passes? How would the time complexity change?"
Practice
balanced?Solution
Step 1: Understand the balance condition
A balanced binary tree means for every node, the height difference between its left and right subtree is at most 1.Step 2: Compare options with definition
The height difference between left and right subtrees of every node is at most 1 matches this definition exactly, while others describe different tree properties.Final Answer:
The height difference between left and right subtrees of every node is at most 1 -> Option CQuick Check:
Balanced tree = height difference ≤ 1 [OK]
- Confusing balanced with complete tree
- Thinking balanced means equal number of nodes
- Assuming all nodes must have two children
Solution
Step 1: Identify correct parameter type for binary tree root
The root of a binary tree is typically a TreeNode or None, so Optional[TreeNode] is correct.Step 2: Check return type for balance check
The function should return a boolean indicating if the tree is balanced, so return type bool is correct.Final Answer:
def is_balanced(root: Optional[TreeNode]) -> bool: -> Option DQuick Check:
Function signature matches tree root and bool return [OK]
- Using int or str as input instead of TreeNode
- Returning int instead of bool
- Using list instead of tree node
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def check_height(node):
if not node:
return 0
left_height = check_height(node.left)
right_height = check_height(node.right)
if left_height == -1 or right_height == -1 or abs(left_height - right_height) > 1:
return -1
return max(left_height, right_height) + 1
def is_balanced(root):
return check_height(root) != -1
root = TreeNode(1, TreeNode(2, TreeNode(3)), TreeNode(4))
print(is_balanced(root))Solution
Step 1: Analyze tree structure
The tree root has left child 2 with left child 3, and right child 4. Left subtree height is 2, right subtree height is 1.Step 2: Check balance condition
At root, height difference is abs(2 - 1) = 1, which is allowed. But node 2 has left child 3 and no right child, so difference is abs(1 - 0) = 1, allowed. So tree is balanced.Step 3: Re-examine code logic
Wait, the code returns -1 if difference > 1. Here difference never exceeds 1, so check_height returns height, is_balanced returns True.Step 4: Re-examine the tree carefully
Node 2 has left child 3 (height 1) and no right child (height 0), difference is 1 (allowed). Root has left subtree height 2, right subtree height 1, difference 1 (allowed). So tree is balanced.Final Answer:
True -> Option BQuick Check:
Height difference ≤ 1 means balanced = True [OK]
- Miscounting subtree heights
- Confusing -1 return as False directly
- Ignoring base case for None nodes
def is_balanced(root):
def height(node):
if not node:
return 0
left = height(node.left)
right = height(node.right)
if abs(left - right) > 1:
return False
return max(left, right) + 1
return height(root) != FalseSolution
Step 1: Understand return values in height function
The height function returns integer heights, but returns False when unbalanced, mixing types.Step 2: Identify impact on recursion
Returning False breaks max(left, right) + 1 calculation because max expects integers, causing errors or wrong results.Final Answer:
Returning False instead of -1 breaks height calculation -> Option AQuick Check:
Use -1 for unbalanced, not False [OK]
- Mixing boolean and integer return types
- Incorrect final check with != False
- Not propagating unbalanced signal properly
Solution
Step 1: Understand naive approach drawbacks
Calculating height separately for each node causes repeated work, leading to O(n²) time.Step 2: Identify efficient approach
Bottom-up recursion returns height if balanced, or -1 if unbalanced, stopping early and avoiding repeated height calculations.Step 3: Compare other options
Options B and C cause repeated calculations; D (BFS) does not directly check subtree heights efficiently.Final Answer:
Use a bottom-up recursion returning height or -1 if unbalanced -> Option AQuick Check:
Bottom-up recursion = efficient balance check [OK]
- Using top-down approach causing repeated height calls
- Trying BFS which doesn't check subtree heights easily
- Checking balance after separate height computations
Ready to prove it?
Pass the test and this pattern moves unknown
