CSE3144 · Advanced Data Structures · Jul–Nov Semester 2026 · Lecture 9 of 36 · CO CSE3144.2

Red-Black Trees

AVL's pragmatic rival: slightly looser balance, much cheaper maintenance — five color rules that quietly run C++ std::map, Java TreeMap, and the Linux kernel.

Dr. Manu ShrivastavaCourse Instructor · Consultation Fri 2–5 PM, LHC 308F
40 minutesSession outcome: explain Red-Black properties and balancing rules
L9 · 00 — Agenda 40 min total

Today, minute by minute

The need: what's wrong with AVL?

00–05

Nothing — unless you insert a lot. The strictness–maintenance trade.

The five rules, and why they force balance

05–12

Colors as bookkeeping; black-height; h ≤ 2 log(n+1) from two rules.

Animation: insertion — recolor first, rotate if you must

12–24

A fully worked example (insert 10, 20, 30, 15, and more); uncle red vs uncle black.

Animation: deletion — the double-black debt

24–34

Deleting red is free; deleting black leaves a hole. The three sibling questions.

AVL vs Red-Black: choosing; recap + homework

34–40

Read-heavy vs write-heavy; where each lives in real systems.

CSE3144 — Lecture 9
L9 · 01 — The need ~5 min

AVL is a perfectionist. Perfection has a price.

The complaint against AVL

AVL's contract — every bf ∈ {−1, 0, +1} — is strict. Strictness buys the shortest possible trees (h ≤ 1.44 log n, superb for searching), but the enforcement is jumpy: modest insertions trigger rotations, and a single deletion can cascade rotations all the way to the root. If your workload modifies the tree constantly — an index ingesting records, a scheduler adding and removing tasks — you pay the enforcement cost on every operation.

Question a lazy engineer would ask: can we relax the balance rule just enough that repairs become rare and cheap, while h stays O(log n)?

The Red-Black answer

Yes: tolerate height up to 2 log(n+1) — about 40% taller than AVL in the worst case — and in exchange, most repairs become recolorings: O(1) paint jobs with no structural change at all. Rotations still exist but fire far less often (insertion: at most 2; deletion: at most 3).

The selection rule you'll be examined on: frequent insertion/deletion → Red-Black wins; search-dominated, rarely-modified data → AVL wins (it's more tightly balanced). That's why library implementers — who can't predict your workload but must survive the worst — almost universally chose Red-Black: std::map, std::set, Java TreeMap, the Linux kernel's scheduler and memory manager.

The intuition to carry: AVL stores balance as numbers (balance factors) and reacts to every ±2. Red-Black stores balance as one bit per node — a color — and reacts only when colors clash. Less information, looser guarantee, calmer maintenance. It's the same engineering trade you saw in Unit I: pay a small constant factor to avoid expensive reorganization.

CSE3144 — Lecture 9
L9 · 02 — The rules ~7 min

Five rules — two of which do all the work

The five rules
  • 1. Every node is red or black.
  • 2. The root is black.
  • 3. Every leaf slot (NIL) counts as black.
  • 4. A red node's children are black — no two reds in a row on any path.
  • 5. Every root-to-NIL path contains the same number of black nodes (the tree's black-height, bh).

Why these force balance: rule 5 says all paths have equal black counts; rule 4 says reds can't stack, so a path can at most alternate black-red-black-red… Therefore the longest path (maximal reds) is at most twice the shortest (all black): h ≤ 2·bh ≤ 2 log₂(n+1). Two local rules, one global guarantee.

A legal Red-Black tree — check every rule

Verify rule 5 on any two paths: root→3→NIL crosses blacks {7, 3, NIL} = 3; root→18→10→8→NIL crosses {7, 10, NIL} = 3 (18 and 8 are red, they don't count). Every path: exactly bh = 3. Red nodes are the slack in the system — free vertical space that costs no black-height.

The mental model for everything that follows

Think of black nodes as bricks (structural, counted, rule 5 protects them) and red nodes as rubber spacers (uncounted, flexible, rule 4 just forbids stacking two spacers). Insertion always adds a rubber spacer (red) — it can never break the brick count, only bump into another spacer (red-red clash). Deletion is dangerous only when it removes a brick — one path suddenly has fewer bricks than the rest. Every algorithm today is just: resolve a spacer clash, or replace a missing brick.

CSE3144 — Lecture 9 · Properties
L9 · 03 — Insertion ~12 min

Insertion: paint it red, then ask the uncle

The algorithm
1. BST-insert the key; color it RED
   // red never breaks rule 5 —
   // only possibly rule 4 (red-red)
2. if x is root → paint BLACK, done
3. if parent is BLACK → done (no clash)
4. if parent is RED, look at the UNCLE:
   · uncle RED   → recolor: p → BLACK, u → BLACK,
                            g → RED;
                   x ← g, repeat from 2
   · uncle BLACK → rotate the g–p–x triple
                   (LL/LR/RR/RL, Lecture 8's moves);
                   recolor: new subtree root → BLACK,
                            old grandparent g → RED
                   // same two targets in all four cases —
                   // "new top" black, demoted g red
Setup, precisely

x is freshly inserted RED. If parent p is black, rules 4 and 5 both hold — done. If p is red, rule 4 is violated (g→p→x, two reds in a row). Since rule 4 held before this insertion, p's parent g must be black — otherwise g-p would already have violated rule 4. So the state is fixed: g black, p red, x red. The uncle u (g's other child) is the only unknown, and its color alone decides which fix is valid.

Case 1 — uncle u is RED. Fix: recolor p→black, u→black, g→red

Rule 4 check: x stays red, but its parent p is now black — no violation at x. g is now red; if g's own parent is red, that is the same violation one level up — hence "x ← g, repeat."

Rule 5 check, by direct count: any path through g into p's subtree contributed g(1)+p(0)=1 black before, and g(0)+p(1)=1 after — identical. Any path through g into u's subtree contributed g(1)+u(0)=1 before, g(0)+u(1)=1 after — identical. Every path's black count through this region is unchanged, because u's count rose by exactly 1 while g's fell by exactly 1 — the cancellation only works because u was red beforehand. No rotation needed; the arithmetic already balances.

Case 2 — uncle u is BLACK. Case 1's recolor is provably invalid here

Try the Case 1 recolor anyway (u→black is now a no-op, g→red) and check the u-side path: before, g(1)+u(1)=2; after, g(0)+u(1)=1 — that path lost a black unit it had no surplus to give. Rule 5 breaks, because there was no red node on u's side to absorb the change. A pure recolor is asymmetric and invalid whenever u is black.

Fix: rotate, then recolor. Rotate the g–p–x triple (L8's LL/RR/LR/RL moves) so the median of the three keys becomes the new local root; g is demoted to a child. Recolor: new top→black, demoted g→red. Check from above: before, a path entering from g's parent crossed g(1)+p(0)/x(0)=1 black across this whole span; after, it crosses new-top(1)+demoted-g(0)=1 — unchanged. g's original parent sees no difference, so the fix is fully absorbed locally — the loop terminates, it never climbs further.

Worked instantiation — hand-verifiable

Insert 10, 20, 30: 10 is root (black). 20 inserted red under 10 (black parent) — no clash. 30 inserted red under 20 (red parent) — clash. Grandparent 10, uncle = 10's other child = NIL, which counts as black (rule 3). Uncle black → Case 2. Rotate: 20 becomes new top (black), 10 and 30 become its children (both red). Check rule 5: root(20)→10→NIL = {20, NIL} = 2 blacks (10 red, skipped); root→30→NIL = {20, NIL} = 2 blacks. Equal.

Insert 25: lands as 30's left child, red, parent 30 red — clash. Grandparent 20, uncle = 20's other child = 10, which is red. Uncle red → Case 1. Recolor 30→black, 10→black, 20→red; since 20 is root, rule 2 forces it back to black. Check rule 5: every root-to-NIL path now crosses exactly 3 blacks (20, then 10 or 30, then NIL) — confirmed on all four paths.

Interactive — insert 10, 20, 30, 15, then 5 and 12
CSE3144 — Lecture 9 · Insertion
L9 · 04 — Deletion ~10 min

Deletion: the double-black debt, and how to pay it

Two definitions, stated precisely — everything below is just these two cases in detail

After BST-delete physically unlinks node y, look at y's color and the color of whatever replaces it in that spot (its one child, or NIL if y was a leaf — NIL counts as black, rule 3):

Case 1 — free, no repair. If y was RED, or its replacement is RED: (re)paint the replacement BLACK if it wasn't already, and stop. Rule 5 is untouched — a red node never counted toward any path's black total in the first place, so removing it or absorbing it changes nothing.

Case 2 — double-black (DB), repair required. If y was BLACK and its replacement is also BLACK (including a NIL replacement): that spot is marked double black — a bookkeeping label, not a real color, meaning "this position must count as 2 blacks until the tree is fixed, because the path through here is one black short of every other path." The three sibling questions below are exactly the procedure for discharging that debt.

From Lecture 7's BST-delete to here

L7 gave three cases: delete a leaf (unlink it); delete a one-child node (parent adopts the child); delete a two-children node by copying the in-order successor's key up, then deleting the successor from its original spot — which is itself always a leaf or one-child node. So every RB deletion, no matter which case it starts as, bottoms out at physically unlinking one leaf-or-one-child node, y.

The subtlety that matters here: in the two-children case, the key that disappears is the one you asked to delete — but the node that gets physically unlinked, and therefore the color that decides how hard the fix is, belongs to the successor, which can be a completely different color from the key you targeted. Always check the color of y, the node actually removed — never the color of the key you started with.

Why y's color is everything — on one small tree

Delete 5 (RED leaf): unlink it, 10's left becomes NIL directly. Path 20→10→NIL: blacks = {20, 10, NIL} = 3 — unchanged (5 was red, it was never counted). Free, exactly as claimed.

Delete 30 (BLACK leaf): unlink it, 20's right becomes NIL directly. Path 20→NIL: blacks = {20, NIL} = 2 — down from 3, while every other path is still 3. Rule 5 is broken by exactly one black, on exactly this one path. That empty NIL slot is marked double black (DB): a bookkeeping label meaning "this position must count as 2 blacks until the tree is repaired" — not a real color, just a flag tracking the one-black debt while the sibling-based repair (next card) pays it off.

The two-children case, traced — where the subtlety bites

Ask to delete 10 (black, two children) from the same starting tree. Successor = leftmost key in 10's right subtree = 15 itself. Copy 15's key up into node 10's position (that node keeps its own identity and color — still black — only its key changes), then delete the original 15-node, which is a red leaf. That's a Case-1 free deletion: node 20→[key 15, still black]→5→NIL gives blacks {20, node, NIL} = 3; the now-empty right side gives {20, node, NIL} = 3. Nothing broke — even though the key you asked to delete, 10, was black. The color that decided the outcome was the successor's (red), not the target's.

The three sibling questions — discharging a double-black debt

Insertion asked the uncle; deletion asks the sibling of the DB node — a good exam trap.

  • Sibling black, with a red child? Two shapes, decided by which child is red:
    • Far child red (the one on the side away from the hole) — regardless of the near child, even if it's red too: rotate the far child straight up through the parent. Recolor three nodes: new subtree top (the sibling) ← old parent's color; old parent ← BLACK; the rotated red child ← BLACK.
    • Only the near child red (far child black): first rotate that near child up through the sibling and swap those two nodes' colors — this converts the shape into "far child red" above. Then apply that fix.
    Either way: debt paid structurally, O(1), at most 2 rotations. (Four sub-shapes: LL/LR/RR/RL, as ever.)
  • Sibling black, both children black? No red to borrow — repaint the sibling RED (subtracting one black from both sides equalizes them) and push the DB up to the parent — three possible outcomes: parent is red → paint it black, done; parent is black and is the root → absorb, done, bh drops by 1; parent is black and is not the root → the parent itself is now the double-black node — repeat these same three questions one level higher, using the parent's own sibling. This is the only case that can climb, and it can climb repeatedly, bounded only by the tree's height.
  • Sibling red? Rotate the sibling up and recolor — this converts to one of the black-sibling cases above.
Interactive — five deletion scenarios, worked end to end
Interactive — when the parent isn't the root: the cascade actually climbs

Scenario 3 above resolved in a single push only because that parent happened to already be the root. Add one more level — root 40(B), children 20(B) and 60(B), each with two black leaf children — and watch the identical debt genuinely climb twice before it's settled.

Complexity: each case does O(1) recoloring/rotation; only the "push DB up" case climbs, so deletion is O(log n) with at most 3 rotations — versus AVL's possible O(log n) rotation cascade. This is precisely where Red-Black earns its keep in write-heavy systems.

CSE3144 — Lecture 9 · Deletion
L9 · 05 — Choosing ~4 min

AVL vs Red-Black: an engineering choice, not a ranking

AVL (Lecture 8)Red-Black (today)
Balance ruleevery bf ∈ {−1, 0, +1}five color rules
Height bound1.44 log n — shorter2 log(n+1)
Search speedfaster (shorter tree)slightly slower
Insert repair≤ 1 rotationoften just recoloring; ≤ 2 rotations
Delete repairup to O(log n) rotations≤ 3 rotations
Best forread-heavy, rarely modifiedwrite-heavy, constantly modified
Lives insidelookup tables, some databases' in-memory indexesstd::map / std::set, Java TreeMap/TreeSet, Linux CFS scheduler, epoll, nginx timers

And the third contender: when the tree lives on disk, neither wins — binary nodes are too small for 4 KB blocks. That problem demands nodes with hundreds of keys: the B-Tree, Lecture 11 — Unit I's disk lessons and Unit II's tree lessons finally meet.

CSE3144 — Lecture 9
L9 · 06 — Recap & what's next ~2 min

Three takeaways

Lecture 10

Splay Trees

No colors, no factors, no guarantees per operation — just "move what you touch to the root," and the Lecture 3 potential method finally earns its keep: O(log n) amortized.

Homework — bring to Lecture 10
  • Insert 7, 3, 18, 10, 22, 8, 11, 26 into an empty RB tree; show every recolor/rotation. (It should reproduce the rules slide's example tree.)
  • Continue today's animation: insert 13, then 6 into the final tree — name each case you hit.
  • From the final insertion tree, delete 30, then 20: identify each deletion case.
  • Give a coloring of a 7-node chain proving no valid RB coloring exists (argue via rules 4 and 5).
  • Compute bh(x) for every node of the rules slide's tree; verify h ≤ 2·bh.
  • One paragraph: your app does 95% lookups on a phonebook loaded once at startup — AVL or Red-Black, and why?
  • Reading: CLRS ch. 13 (Red-Black Trees).
CSE3144 — Lecture 9

Questions?

Dr. Manu Shrivastava — LHC 308F — Friday 2:00–5:00 PM

Next: Lecture 10 — Splay Trees and Self-Adjusting Trees, where Lecture 3's potential method pays off.