foreach-ui logo
codeLanguages
account_treeDSA

Quick Actions

quizlock Random Quiz
trending_uplock Progress
  • 1
  • 2
  • 3
  • 4
  • quiz
Data Structures
  • Understand why balance matters: prevents O(n) degeneration
  • Learn AVL rotations (LL, RR, LR, RL) to maintain height balance
  • Compare AVL vs Red-Black: strict vs relaxed balance trade-offs

Balanced Binary Trees

BSTs are great. O(log n) operations. But only if balanced.

Insert sorted values? You get a linked list. O(n) operations. Terrible.

Solution: keep the tree balanced. Automatically.

What's Balance?

A balanced tree has height O(log n). Different definitions exist, but the goal is the same: prevent degeneration.

Height-balanced: For every node, height difference between left and right subtrees ≤ k (commonly k=1).

Weight-balanced: For every node, size of left and right subtrees differ by at most a constant factor.

AVL and Red-Black trees use height-balancing. B-trees use a different approach.

Why Balance Matters

Degenerate BST (sorted insertion):

1
 \
  2
   \
    3
     \
      4

Height = 4, nodes = 4. O(n).

Balanced BST (same values):

    2
   / \
  1   3
       \
        4

Height = 2, nodes = 4. O(log n).

Balance ensures height stays logarithmic.

AVL Trees

Rule: For every node, |height(left) - height(right)| ≤ 1.

Strictly balanced. Guarantees height ≤ 1.44 log n.

How it works: After insert/delete, check balance. If violated, perform rotations to restore balance.

Rotations

Four cases when node becomes unbalanced:

Left-Left (LL): Left child's left subtree too tall.

      z
     /
    y
   /
  x

Rotate right at z:

    y
   / \
  x   z

Right-Right (RR): Mirror of LL.

  x
   \
    y
     \
      z

Rotate left at x:

    y
   / \
  x   z

Left-Right (LR): Left child's right subtree too tall.

    z
   /
  x
   \
    y

Rotate left at x, then right at z:

    y
   / \
  x   z

Right-Left (RL): Mirror of LR.

AVL Implementation Sketch

class AVLNode:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None
        self.height = 1

def get_height(node):
    return node.height if node else 0

def get_balance(node):
    return get_height(node.left) - get_height(node.right) if node else 0

def rotate_right(z):
    y = z.left
    T3 = y.right
    
    y.right = z
    z.left = T3
    
    z.height = 1 + max(get_height(z.left), get_height(z.right))
    y.height = 1 + max(get_height(y.left), get_height(y.right))
    
    return y

def rotate_left(x):
    y = x.right
    T2 = y.left
    
    y.left = x
    x.right = T2
    
    x.height = 1 + max(get_height(x.left), get_height(x.right))
    y.height = 1 + max(get_height(y.left), get_height(y.right))
    
    return y

def insert_avl(root, value):
    if root is None:
        return AVLNode(value)
    
    if value < root.value:
        root.left = insert_avl(root.left, value)
    elif value > root.value:
        root.right = insert_avl(root.right, value)
    else:
        return root  # Duplicate
    
    # Update height
    root.height = 1 + max(get_height(root.left), get_height(root.right))
    
    # Check balance
    balance = get_balance(root)
    
    # Left-Left
    if balance > 1 and value < root.left.value:
        return rotate_right(root)
    
    # Right-Right
    if balance < -1 and value > root.right.value:
        return rotate_left(root)
    
    # Left-Right
    if balance > 1 and value > root.left.value:
        root.left = rotate_left(root.left)
        return rotate_right(root)
    
    # Right-Left
    if balance < -1 and value < root.right.value:
        root.right = rotate_right(root.right)
        return rotate_left(root)
    
    return root

Inserts like BST, then checks balance and rotates if needed. O(log n).

Red-Black Trees

Less strictly balanced than AVL. Allows more imbalance but fewer rotations.

Rules:

  1. Every node is red or black
  2. Root is black
  3. Leaves (null) are black
  4. Red node has black children (no two reds in a row)
  5. Every path from root to leaf has same number of black nodes

Guarantees height ≤ 2 log n. Slightly worse than AVL but faster insertions/deletions (fewer rotations).

Used in C++ std::map, Java TreeMap, Linux kernel.

More complex to implement than AVL. Requires color tracking and more rotation cases.

AVL vs Red-Black

Feature AVL Red-Black
Balance Strict (height diff ≤ 1) Relaxed (height ≤ 2 log n)
Lookup Slightly faster Slightly slower
Insert/Delete More rotations Fewer rotations
Use case Read-heavy Write-heavy

For most uses, Red-Black is preferred (fewer rotations, good enough balance).

Check if Balanced

def is_balanced(root):
    def check_height(node):
        if node is None:
            return 0
        
        left_height = check_height(node.left)
        if left_height == -1:
            return -1
        
        right_height = check_height(node.right)
        if right_height == -1:
            return -1
        
        if abs(left_height - right_height) > 1:
            return -1
        
        return 1 + max(left_height, right_height)
    
    return check_height(root) != -1

Returns -1 if unbalanced, otherwise height. O(n).

Balancing an Unbalanced BST

Given unbalanced BST, convert to balanced.

  1. Inorder traversal → sorted array
  2. Build balanced BST from sorted array (pick middle as root)
def balance_bst(root):
    # Step 1: Get sorted values
    values = []
    
    def inorder(node):
        if node is None:
            return
        inorder(node.left)
        values.append(node.value)
        inorder(node.right)
    
    inorder(root)
    
    # Step 2: Build balanced BST
    def build(arr):
        if not arr:
            return None
        mid = len(arr) // 2
        root = TreeNode(arr[mid])
        root.left = build(arr[:mid])
        root.right = build(arr[mid+1:])
        return root
    
    return build(values)

O(n). Works for one-time balancing. For ongoing balance, use AVL or Red-Black.

When to Use Balanced Trees

Frequent insertions/deletions with lookups: Use Red-Black. Good balance with fewer rotations.

Read-heavy workload: Use AVL. Stricter balance means slightly faster lookups.

Static data, occasional rebalancing: Build balanced BST from sorted array. Simple, no rotation complexity.

Databases, file systems: Use B-trees. Better for disk I/O (fewer, wider nodes).

The Trade-off

Plain BST: Simple, but can degenerate.

AVL: Strict balance, fast lookups, but more rotations.

Red-Black: Relaxed balance, fewer rotations, but slightly slower lookups.

Understanding balance means understanding:

  • The height matters for performance
  • Rotations restore balance locally
  • Different balancing strategies suit different workloads

Master balanced trees, and you'll use the right structure for your needs (AVL for reads, Red-Black for writes, B-trees for databases).

© 2026 forEach. All rights reserved.

Privacy Policy•Terms of Service