Balanced Trees (Intro)

In the previous lesson on the Binary Search Tree you saw that a BST's O(log n) promise for search, insert, and delete only holds if the tree is roughly balanced — meaning its height is close to log2(n). Insert already-sorted data into a plain BST and you don't get a nicely balanced tree at all; you get a skewed tree that behaves like a linked list. This lesson explains why that happens, the general idea of "self-balancing" trees that fix it, and where Java actually uses these structures under the hood.

The Skewed-Tree Problem

Consider inserting 10, 20, 30, 40, 50 — already sorted — into a plain BST. Every new value is larger than everything before it, so each node only ever gets a right child:

10
 \
  20
   \
    30
     \
      40
       \
        50

This is a valid BST, but its height is n instead of log n. Searching for 50 now means walking every node — the exact O(n) behavior a BST was supposed to avoid, identical to scanning a linked list.

class Node {
    int val;
    Node left, right;
    Node(int val) { this.val = val; }
}

// Inserting 10, 20, 30, 40, 50 in order into a plain BST
// produces a right-skewed chain — height n, not log n.
boolean contains(Node root, int target) {
    Node curr = root;
    while (curr != null) {
        if (curr.val == target) return true;
        curr = target < curr.val ? curr.left : curr.right;
    }
    return false;
}

Time complexity: O(h) where h is the tree's height — this becomes O(n) on the skewed tree above, but O(log n) on a balanced one. Space complexity: O(1) for the iterative search shown, since no extra structure grows with input size.

The core issue: a plain BST only balances itself by accident, based on insertion order. A self-balancing tree actively restructures itself after every insert/delete so the height stays close to log n no matter what order the data arrives in.

The Balance Concept

"Balanced" doesn't mean perfectly symmetric — it means the height difference between a node's left and right subtrees is kept small (bounded by a constant), so the overall height stays proportional to log n. Different self-balancing trees enforce this with different rules:

StructureHow it stays balanced
AVL TreeTracks a balance factor per node; rebalances with rotations the moment it's violated
Red-Black TreeColors nodes red/black and enforces coloring rules that bound height to roughly 2·log n

AVL Trees: Balance Factor

An AVL tree defines the balance factor of a node as:

balanceFactor(node) = height(node.left) - height(node.right)

A node is considered balanced if its balance factor is -1, 0, or 1. The moment an insert or delete pushes any node's balance factor outside that range, the tree performs a rotation to restore it — before the imbalance can compound further up the tree.

Rotation, Worked on 3 Nodes

The simplest case is inserting 10, then 20, then 30 in that order into an empty AVL tree. After inserting 30, the tree looks like this — right-skewed, balance factor of the root is -2:

Before rotation (unbalanced):

  10
   \
    20
     \
      30

Root (10): balance factor = 0 - 2 = -2   ← violation (right-heavy)

This is a "right-right" case, fixed with a single left rotation around the root. The middle node (20) becomes the new root, the old root (10) becomes its left child, and 30 stays the right child:

After a left rotation around 10:

      20
     /  \
    10   30

Balance factor of 20: 1 - 1 = 0   ← balanced

The mirror image — inserting 30, then 20, then 10 (left-left case) — is fixed with a single right rotation instead: the middle value (20) again ends up as the new root. When the imbalance zig-zags (e.g. left-right or right-left), AVL trees perform two rotations back to back to straighten it out. The mechanics differ slightly, but the goal is always the same: pull the middle value up to become the new subtree root so both sides end up roughly equal in height.

Practical takeaway: you will almost never be asked to implement AVL insert/delete with rotations from scratch in an interview — the logic is fiddly and easy to get subtly wrong under pressure. What matters is understanding why rotations exist (to stop a skewed tree from decaying to O(n)) and being able to reason about balance factors if asked conceptually.

Red-Black Trees: What Java Actually Uses

AVL trees keep a very tight balance (height difference of at most 1), which makes lookups slightly faster but makes inserts/deletes more expensive because rebalancing happens more often. Red-Black trees relax that constraint — each node is colored red or black, and a small set of coloring rules (root is black, no two red nodes in a row, every path from root to a null leaf passes through the same number of black nodes) guarantees the height never exceeds roughly 2 · log2(n). That's a looser bound than AVL, but it means fewer rotations on average during modification.

This trade-off is exactly why Java's TreeMap and TreeSet are implemented internally as Red-Black trees — they favor balanced overall performance across inserts, deletes, and lookups rather than optimizing lookups alone.

// TreeMap keeps keys sorted using a Red-Black tree internally.
// You never see rotations or colors — they're an implementation detail.
TreeMap<Integer, String> map = new TreeMap<>();
map.put(50, "fifty");
map.put(20, "twenty");
map.put(70, "seventy");
map.put(10, "ten");

System.out.println(map.firstKey());  // 10 — smallest key
System.out.println(map.get(20));     // "twenty" — guaranteed O(log n) even after many inserts

Time complexity: O(log n) for get/put/firstKey — Red-Black balancing guarantees height stays logarithmic no matter the insertion order. Space complexity: O(n) to store n key-value entries plus a small constant overhead per node for color/parent pointers.

Also note: Java's plain HashMap is unrelated to this — it uses hashing, not a sorted tree (covered next lesson). Reach for TreeMap/TreeSet specifically when you need keys in sorted order, range queries, or "closest key" operations like floorKey/ceilingKey.
Next up: we leave trees for a while and cover HashMap & HashSet — the O(1)-average structures you'll reach for constantly in interviews, and exactly why they're faster than a balanced tree for lookups that don't need ordering.