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.
| Property | AVL | Red-Black |
|---|---|---|
| Balance condition | Height difference between subtrees is at most 1 at every node | Colour rules; the longest path is at most twice the shortest |
| How balanced | Stricter — the tree is shallower | Looser — the tree can be somewhat taller |
| Search | Slightly faster (shallower tree) | Slightly slower |
| Insert / delete | More rotations — costlier | Fewer rotations — cheaper |
| When it wins | Read-heavy workloads | Write-heavy workloads |
| Where it is used | Some DB indexes, in-memory indexes | Java/Kotlin `TreeMap`/`TreeSet`, C++ `std::map`, the Linux scheduler |
Where do you meet these in real runtimes?
- Kotlin / Java:
TreeMapandTreeSetare 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:
SplayTreeMapandSplayTreeSet(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::mapandstd::setare 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
- java.util.TreeMap (Red-Black tree)officialdocs.oracle.com
A real balanced-tree implementation: the log(n) guarantees are stated in the docs.
- Dart SplayTreeMapofficialapi.dart.dev