LeetCode 110: Balanced Binary Tree Solution in Python Explained

Determining if a binary tree is balanced might feel like checking the stability of a carefully built tower, and LeetCode 110: Balanced Binary Tree is an easy-level challenge that makes it accessible! Given the root of a binary tree, you need to return true if it’s height-balanced—meaning the depth difference between the left and right subtrees of every node is at most 1—or false otherwise. In this blog, we’ll solve it with Python, exploring two solutions—Bottom-Up DFS (our best solution) and Top-Down DFS (a straightforward alternative). With step-by-step examples, detailed code breakdowns, and tips, you’ll master this problem. Let’s balance that tree!

Problem Statement

Section link icon

In LeetCode 110, you’re given the root of a binary tree. Your task is to return true if the tree is height-balanced—where the absolute difference in heights of left and right subtrees for every node is ≤ 1—or false if it’s not. This builds on BST construction like LeetCode 109: Convert Sorted List to Binary Search Tree, shifting focus to balance validation rather than creation.

Constraints

  • Number of nodes: 0 <= n <= 10^4
  • Node values: -2^31 <= Node.val <= 2^31 - 1

Example

Let’s see some cases:

Input: root = [3,9,20,null,null,15,7]
      3
     / \
    9  20
       / \
      15  7
Output: true
Explanation: Heights: Left (1), Right (2), diff = 1 ≤ 1, balanced.
Input: root = [1,2,2,3,3,null,null,4,4]
      1
     / \
    2   2
   / \
  3   3
 / \
4   4
Output: false
Explanation: Heights: Left (3), Right (1), diff = 2 > 1, unbalanced.
Input: root = []
Output: true
Explanation: Empty tree is balanced.

These examples show we’re checking height balance.

Understanding the Problem

Section link icon

How do you solve LeetCode 110: Balanced Binary Tree in Python? Take root = [3,9,20,null,null,15,7]. We need to confirm it’s balanced—left subtree (9) height 1, right subtree (20,15,7) height 2, difference 1 ≤ 1, true. For [1,2,2,3,3,null,null,4,4], left height 3, right height 1, difference 2 > 1, false. This isn’t a level-order traversal like LeetCode 107: Binary Tree Level Order Traversal II; it’s about validating balance. We’ll use: 1. Bottom-Up DFS: O(n) time, O(h) space—our best solution. 2. Top-Down DFS: O(n log n) time, O(h) space—an alternative.

Let’s dive into the best solution.

Best Solution: Bottom-Up DFS Approach

Section link icon

Explanation

Bottom-Up DFS computes each subtree’s height recursively, checking balance at each node as it returns upward. If a subtree is unbalanced (height difference > 1), return a sentinel value (e.g., -1) to propagate the failure. This is the best solution due to its O(n) time complexity, visiting each node once, and efficient space usage, avoiding redundant height calculations.

For [3,9,20,null,null,15,7]:

  • Leaf 9: Height 1.
  • Leaf 15, 7: Height 1.
  • Node 20: Heights 1, 1, diff = 0, total height 2.
  • Node 3: Heights 1, 2, diff = 1, total height 3.
  • Balanced, true.

Step-by-Step Example

Assume a TreeNode class: class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val; self.left = left; self.right = right.

Example 1: root = [3,9,20,null,null,15,7]

Goal: Return true.

  • Start: isBalanced(3)checkHeight(3).
  • Step 1: root = 3.
    • Left: checkHeight(9).
      • Leaf 9, no children, return 1.
    • Right: checkHeight(20).
      • Left: checkHeight(15) → 1.
      • Right: checkHeight(7) → 1.
      • 20: Heights 1, 1, diff = 0 ≤ 1, return 2.
    • 3: Heights 1, 2, diff = 1 ≤ 1, return 3.
  • Finish: checkHeight(3) ≠ -1, return true.

Example 2: root = [1,2,2,3,3,null,null,4,4]

Goal: Return false.

  • Start: checkHeight(1).
  • Step 1: root = 1.
    • Left: checkHeight(2).
      • Left: checkHeight(3).
        • Left: checkHeight(4) → 1.
        • Right: checkHeight(4) → 1.
        • 3: Heights 1, 1, diff = 0, return 2.
      • Right: checkHeight(3) → 1.
      • 2: Heights 2, 1, diff = 1, return 3.
    • Right: checkHeight(2) → 1.
    • 1: Heights 3, 1, diff = 2 > 1, return -1.
  • Finish: checkHeight(1) = -1, return false.

Example 3: root = []

Goal: Return true.

  • Start: checkHeight(None).
  • Step 1: Null root, return 0.
  • Finish: 0 ≠ -1, return true.

How the Code Works (Bottom-Up DFS) – Detailed Line-by-Line Explanation

Here’s the Python code with a thorough breakdown:

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def isBalanced(root: TreeNode) -> bool:
    # Line 1: Define helper function to compute height and check balance
    def checkHeight(node: TreeNode) -> int:
        # node = current node (e.g., root=3)

        # Line 2: Base case - empty node
        if not node:
            # If node is None (e.g., leaf’s child), height is 0
            return 0

        # Line 3: Compute left subtree height
        left_height = checkHeight(node.left)
        # Recursively gets left height (e.g., 9 → 1)
        # Returns -1 if unbalanced

        # Line 4: Check for left subtree imbalance
        if left_height == -1:
            # If left subtree is unbalanced (e.g., diff > 1), propagate failure
            return -1

        # Line 5: Compute right subtree height
        right_height = checkHeight(node.right)
        # Recursively gets right height (e.g., 20 → 2)
        # Returns -1 if unbalanced

        # Line 6: Check for right subtree imbalance
        if right_height == -1:
            # If right subtree is unbalanced, propagate failure
            return -1

        # Line 7: Check balance condition
        if abs(left_height - right_height) > 1:
            # If height difference > 1 (e.g., 3 vs. 1), unbalanced
            return -1

        # Line 8: Return total height of current subtree
        return 1 + max(left_height, right_height)
        # Height includes current node (e.g., 1 + max(1, 2) = 3 for root=3)

    # Line 9: Start validation and check result
    return checkHeight(root) != -1
    # Calls helper, returns true if height isn’t -1 (e.g., 3 → true)
    # -1 indicates imbalance detected

This detailed breakdown clarifies how bottom-up DFS efficiently validates tree balance.

Alternative: Top-Down DFS Approach

Section link icon

Explanation

Top-Down DFS recursively checks each node’s balance by computing left and right subtree heights using a separate height function, verifying the difference ≤ 1 at every step. It’s a straightforward alternative but less efficient, recomputing heights for each subtree, leading to O(n log n) time in balanced cases.

For [3,9,20,null,null,15,7]:

  • Root 3: Left height 1, Right height 2, diff = 1.
  • All nodes checked similarly, true.

Step-by-Step Example (Alternative)

For [1,2,2,3,3,null,null,4,4]:

  • Step 1: root = 1.
    • Left height = 3, Right height = 1, diff = 2 > 1, false.
  • Finish: Return false.

How the Code Works (Top-Down DFS)

def isBalancedTopDown(root: TreeNode) -> bool:
    def height(node: TreeNode) -> int:
        if not node:
            return 0
        return 1 + max(height(node.left), height(node.right))

    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 isBalancedTopDown(root.left) and isBalancedTopDown(root.right)

Complexity

  • Bottom-Up DFS:
    • Time: O(n) – visits each node once.
    • Space: O(h) – recursion stack, h = height.
  • Top-Down DFS:
    • Time: O(n log n) – height recomputed per node.
    • Space: O(h) – recursion stack.

Efficiency Notes

Bottom-Up DFS is the best solution with O(n) time and O(h) space, efficiently checking balance in one pass—Top-Down DFS uses O(n log n) time due to repeated height calculations, making it a simpler but less efficient alternative.

Key Insights

  • Balance: Subtree height diff ≤ 1.
  • Bottom-Up: Single pass efficiency.
  • Top-Down: Intuitive but redundant.

Additional Example

Section link icon

For root = [1,2,3,4,null,null,5]:

  • Goal: true.
  • Bottom-Up: Heights: 4 (1), 2 (2), 3 (2), 1 (3), diff ≤ 1.

Edge Cases

Section link icon
  • Empty Tree: []true.
  • Single Node: [1]true.
  • Skewed: [1,2,3]false.

Both solutions handle these well.

Final Thoughts

Section link icon

LeetCode 110: Balanced Binary Tree in Python is a key balance validation challenge. The Bottom-Up DFS solution shines with its efficiency and elegance, while Top-Down DFS offers a more intuitive approach. Want more? Try LeetCode 104: Maximum Depth of Binary Tree for depth basics or LeetCode 108: Convert Sorted Array to Binary Search Tree for balanced BST creation. Ready to practice? Solve LeetCode 110 on LeetCode with [3,9,20,null,null,15,7], aiming for true—test your skills now!