Why balanced BSTs
Raw BST degrades to on sorted input. AVL and RB are BSTs that enforce a balance invariant so height stays worst case.
AVL: the invariant
For every node , balance factor Convention: height of empty subtree is ; single node is . AVL worst-case height .
AVL node
struct AVLNode {
int key;
int height = 0; // cached
AVLNode* left = nullptr;
AVLNode* right = nullptr;
AVLNode* parent = nullptr;
}; Update height after every structural change: using for null children.
Rotations (BST-order-preserving swap)
-
Right-rotate at : ‘s left child becomes root; becomes ‘s right child; ‘s old right subtree becomes ‘s new left.
-
Left-rotate: mirror.
In-order sequence is unchanged. This is the universal balancing primitive.
The four AVL imbalance cases
First unbalanced ancestor ; = taller child; = child of on the same side:
shape fix
LL (left, left) right-rotate at RR (right, right) left-rotate at LR (left, right) left-rotate at , then right-rotate at RL (right, left) right-rotate at , then left-rotate at
AVL insert
-
BST insert at leaf; new node has height 0.
-
Retrace: walk up from the new node, updating heights. At the first ancestor with , apply the matching rotation. One rotation (single or double) is enough — subtree height returns to its pre-insert value.
Cost: descent + retrace = .
AVL remove
-
BST remove (two-children copy successor key, remove successor — which has child).
-
Retrace from the removed node’s parent. Unlike insert, rebalancing may cascade: fixing one level can leave the next one unbalanced.
Red-black: the five rules
-
Every node is red or black.
-
The root is black.
-
A red node’s children are black (no two reds in a row).
-
Every null (NIL) leaf is black.
-
Every root-to-NIL path has the same number of black nodes (the black-height).
Together longest path shortest height .
RB insertion fix-up, case summary
New node inserted red. If parent is black, done. Else (parent red — rule 3 violated), by uncle:
-
Uncle red (case 3): recolor parent & uncle black, grandparent red. Recurse at grandparent.
-
Uncle black, zig-zag (case 4): rotate parent to straighten, then fall into case 5.
-
Uncle black, straight line (case 5): recolor, rotate grandparent. Done.
Then: root black. At most recolorings and rotations.
RB removal (the hard one)
BST-remove first. Only removing a black node is tricky — it breaks rule 5. Track a “double-black” token and fix via six cases (red sibling, black sibling + red-nephew variants, black sibling + black nephews push deficit up). Worst case with amortized rotations.
AVL vs. red-black
AVL red-black
height
search faster (tighter) slightly slower
insert up to 2 rotations rotations
remove may cascade rotations
used by specialized DBs std::map, Linux kernel
Rule of thumb: AVL if reads dominate; RB if writes dominate.
Top gotchas
-
Forgetting to update cached heights after a rotation.
-
Reading balance factor with null children as height instead of .
-
Applying rotations at the wrong pivot — read the cases as “name from grandparent down.”
-
Treating remove-retrace as “one rotation fixes it” — it can cascade all the way to the root.
-
In RB: forgetting to reset root to black after a case-3 recolor propagates it upward.