Sparround

Balancing: AVL and Red-Black

The whole problem with a plain BST fits in one sentence: its shape depends on insertion order. If sorted data arrives, the tree becomes a chain and the promise of O(log n) turns into O(n).

Self-balancing trees fix this at the root: after every insert and delete they nudge the structure back into shape, maintaining the guarantee h = O(log n). The word guarantee is the key — this is not "usually", it is "always".

Why does it matter so much? For 1 million elements the difference is:

  • Balanced: about 20 comparisons.
  • Degenerate: about 1,000,000 comparisons.

Balance is the only thing keeping the tree functional. The price is a little extra work on insert and delete — but that work never exceeds O(log n) either.

What does a rotation do? You only need to grasp it as a picture, not as an implementation.

Say the tree leans right: the root's right subtree is noticeably taller than its left. A left rotation lifts the right child up and makes the old root its left child. The tall branch drops one level, the short branch rises one level — the tree flattens.

Three important facts:

  • A rotation does not change the inorder order. The relative order of elements stays the same, so the BST invariant holds. This is the core idea explaining why rotations are safe.
  • A rotation is O(1) — a handful of pointers change and no subtree is walked.
  • After an insert at most one or two rotations are needed; after a delete an AVL tree may cascade rotations up to the root, but that is still O(log n).

There are four cases — LL, RR, LR, RL — and two of them (LR, RL) need two rotations in sequence. Knowing these names is enough for an interview; memorising which pointer moves where is not required.

PropertyAVLRed-Black
Balance conditionHeight difference between subtrees is at most 1 at every nodeColour rules; the longest path is at most twice the shortest
How balancedStricter — the tree is shallowerLooser — the tree can be somewhat taller
SearchSlightly faster (shallower tree)Slightly slower
Insert / deleteMore rotations — costlierFewer rotations — cheaper
When it winsRead-heavy workloadsWrite-heavy workloads
Where it is usedSome DB indexes, in-memory indexesJava/Kotlin `TreeMap`/`TreeSet`, C++ `std::map`, the Linux scheduler

Where do you meet these in real runtimes?

  • Kotlin / Java: TreeMap and TreeSet are built on a red-black tree. firstKey(), floorKey(k), ceilingKey(k), subMap(a, b) are all O(log n) and simply do not exist on a hash map.
  • Dart: SplayTreeMap and SplayTreeSet (dart:collection). A splay tree is a different self-adjusting family — it gives amortized O(log n) and moves recently used keys closer to the root.
  • JavaScript: the standard library has NO sorted map. If you need ordered behaviour you either extract and sort the keys, keep a sorted array with binary search, or reach for a library. Knowing this is useful in an interview.
  • C++: std::map and std::set are red-black trees.
  • Databases: indexes usually use B-trees/B+trees — the same idea scaled for disk: nodes hold many keys so that the number of disk reads stays small.

Remember the difference from a hash map in one line: a hash gives you speed, a tree gives you order.

Interview tip. "Implement an AVL tree" is almost never asked at middle level — and even when it is, the expected answer is not full code.

The honest and strong answer sounds like this: "I do not write the exact pointer manipulation of rotations from memory — in practice I use TreeMap or SplayTreeMap. But I can explain the principle: after each insert I check the balance factor, and if |difference| > 1 I apply the rotation matching one of the four cases (LL, RR, LR, RL); a rotation is O(1) and does not change the inorder order. If you like, I can write the simple right rotation."

That answer shows three things: you understand the mechanism, you know its boundaries, and you can make a practical choice. It beats improvising half-correct rotation code by a wide margin.

The question actually asked most often is a different one: "Why is balancing needed?" The answer: a plain BST's shape depends on insertion order, and sorted input turns it into an O(n) chain. Add one sentence — "that is exactly why Java's TreeMap is a red-black tree" — and the answer is complete.

📚 Sources and documentation