Algorithm Search⌘K

Proofs: Left-leaning red-black tree

These are the human proofs of Left-leaning red-black tree, at full length.

Insertion and deletion keep the left-leaning red-black rules full

This proof shows: correctness.

The rules are (L1) search order, (L2) no red right child, (L3) no red node with a red left child, (L4) the same number of black nodes on every path from the root to nil, and (L5) a black root. For a node hh, write bh(h)bh(h) for the number of black nodes on a path from hh down to nil, counting hh itself, with bh(nil)=0bh(\text{nil}) = 0; it is well defined when (L4) holds in the subtree of hh. Call a subtree valid when (L1) to (L4) hold in it; its root may be red. In a valid subtree, a red node has two black children: the left one by (L3), the right one by (L2).

Lemma A (the steps). A rotation of a red link, where the child moves up, takes the color of the old top, and the old top becomes red, keeps (L1), keeps (L4) in the subtree, and keeps bhbh of the top. A color flip at a node whose two children both have the color opposite to its own keeps (L1), (L4) and bhbh of the top.

Proof. A rotation keeps the in-order sequence of the keys. For ROTATE-LEFT(h)(h) with x=h.rightx = h.right red, let cc be the color of hh and α\alpha, β\beta, γ\gamma the subtrees h.lefth.left, x.leftx.left, x.rightx.right. Before, a path from the top to α\alpha, β\beta or γ\gamma meets the node hh of color cc and, for β\beta and γ\gamma, the red xx. After, it meets the node xx of color cc and, for α\alpha and β\beta, the red hh. Red nodes add nothing, so each path has the same count. ROTATE-RIGHT is the mirror image. A flip at a black node with two red children makes the node red and the children black: a path from the top meets one black node before (the top) and one after (a child). The other flip is the reverse. Neither step changes keys. □\square

Insertion

Lemma 1 (PUT). Let hh be valid or nil, and k∉K(h)k \notin K(h). Then x=PUT(h,k)x = \text{PUT}(h, k) has the keys K(h)∪{k}K(h) \cup \{k\} and bh(x)=bh(h)bh(x) = bh(h), (L1), (L2) and (L4) hold in its subtree, and (L3) holds at every node except possibly at xx itself. If hh is black or nil, (L3) holds at xx too, so xx is valid. If hh is red, then xx is red.

Proof. By induction on the size. If hh is nil, line L1 returns a red node with two nil children, which is valid, with bh=0bh = 0. Else let cc be the child of hh on the side of kk and xcx_c the result of the recursive call at line L2 or L3. Every node below xcx_c satisfies the rules, and by Lemma A the steps of FIX-UP keep (L1), (L4) and bhbh. We follow FIX-UP in each case.

hh black, k<h.keyk < h.key. Then h.righth.right is black, by (L2). If cc is black or nil, xcx_c is valid by induction, so if xcx_c is red its left child is black. No test of FIX-UP is true, and hh is valid. If cc is red, xcx_c is red and only (L3) at xcx_c may fail. If xc.leftx_c.left is red, line L7 rotates right at hh: the black top xcx_c has the red left child xc.leftx_c.left and the red right child hh, whose new left child, the old xc.rightx_c.right, is black. Line L8 flips: the top is red with two black children, whose own children are black or valid. In both cases the result is valid.

hh black, k>h.keyk > h.key. Then cc is black or nil and xcx_c is valid. If xcx_c is black, nothing changes. If xcx_c is red and h.lefth.left is black, line L6 rotates left: the top xcx_c is black, its left child hh is red with two black children (the old h.lefth.left, and the old xc.leftx_c.left, black since xcx_c was red), and its right child is the black old xc.rightx_c.right. Lines L7 and L8 do nothing. If xcx_c is red and h.lefth.left is red, line L8 flips, and hh is red with two black children.

hh red. Its children are black. If k<h.keyk < h.key, xcx_c is valid, so line L7 does not apply (if xcx_c is red, its left child is black), and hh stays red, with a possibly red left child. If k>h.keyk > h.key and xcx_c is red, line L6 rotates left: the top xcx_c takes the red color of hh, and hh becomes its red left child, with two black children. Lines L7 and L8 do nothing. The top is red, and only (L3) at the top may fail. □\square

INSERT. The root is black or nil, so by Lemma 1 PUT returns a valid tree with the new key. Line L10 paints the root black; this adds one to every path, so (L4) holds, and (L5) holds. An insertion of a key that is already there stops at line L4 with nothing changed (the implementation searches first).

Deletion

A call REMOVE(h,k)(h, k) has the precondition that k∈K(h)k \in K(h), that (L1), (L3), (L4) hold in the subtree of hh and (L2) holds at every node except possibly at hh, and that one of these holds:

  1. (P1) hh is red, with two black children;
  2. (P2) hh is black, h.lefth.left is red and h.righth.right is black;
  3. (P3) hh is black, h.lefth.left is black, h.righth.right is red, and k≥h.keyk \ge h.key.

In the 2-3 view, (P1) and (P2) say that hh is not a 2-node, so a key can leave it. The postcondition is that the result xx is valid, with keys K(h)∖{k}K(h) \setminus \{k\} and bh(x)=bh(h)bh(x) = bh(h), and that xx is black if hh was black when the call began. DELETE-MIN(h)(h) has the same precondition with (P1) or (P2), with kk the least key, and the same postcondition.

Lemma 2 (FIX-UP in deletion). If both children of hh are valid with the same bhbh, and if hh is red only when both children are black, then FIX-UP(h)(h) returns a valid subtree with the same keys and bhbh. If hh is black and h.righth.right is black, the result is hh itself, black.

Proof. If hh is red, its children are black and no test is true. If hh is black: with two black children nothing changes; with a red left child and a black right child, line L7 does not apply, since the left child is valid and red, so its left child is black; with a black left child and a red right child, line L6 rotates left and gives a black top with a red left child hh whose two children are black, as in Lemma 1; with two red children, line L8 flips and gives a red top with two black children. □\square

Lemma 3 (MOVE-RED-LEFT). Let hh be red with valid black children ll and rr, and let l.leftl.left be black. After MOVE-RED-LEFT(h)(h), the subtree has the same keys and bhbh, (L1) and (L4) hold, and either (a) the top is hh, black, with two red children, each with two black children, or (b) the top is red, its right child is black and valid, and its left child is the old hh, black, with the red left child ll (whose children are black) and a black right child.

Proof. Line L16 flips at hh, a red node with two black children: hh is black, and ll and rr are red. The children of ll are black (l.leftl.left by assumption, l.rightl.right by (L2)), and r.rightr.right is black by (L2). If r.leftr.left is black, the result is (a). Else let s=r.lefts = r.left, red, with two black children. Line L17 rotates right at rr: ss takes the red color of rr and rr becomes its red right child, whose left child is the old s.rights.right, black. Then it rotates left at hh: ss moves up with the black color of hh, hh becomes its red left child, and the right child of hh is the old s.lefts.left, black. Then it flips at ss, a black node with two red children: ss is red, and hh and rr are black. Now hh has the red left child ll and a black right child, and rr has two black children. Every step is one of Lemma A. □\square

Lemma 4 (MOVE-RED-RIGHT). Let hh be red with valid black children ll and rr, and let r.leftr.left be black. After MOVE-RED-RIGHT(h)(h), the subtree has the same keys and bhbh, (L1) and (L4) hold, and either (a) the top is hh, black, with two red children, each with two black children, or (b) the top tt is red with two black children; t.leftt.left is valid, and t.rightt.right is the old hh, black, with a black left child and the red right child rr, whose children are black; and t.key<h.keyt.key < h.key.

Proof. Line L18 flips at hh: hh is black, and ll and rr are red. The children of rr are black (r.leftr.left by assumption, r.rightr.right by (L2)), and l.rightl.right is black by (L2). If l.leftl.left is black, the result is (a). Else l.leftl.left is red, with black children. Line L19 rotates right at hh: ll moves up with the black color of hh, hh becomes its red right child, and the left child of hh is the old l.rightl.right, black. Then it flips at ll, a black node with two red children: ll is red, l.leftl.left is black, and hh is black. The node hh keeps the red right child rr. Since ll was the left child of hh, l.key<h.keyl.key < h.key. □\square

Lemma 5 (REMOVE and DELETE-MIN). Under the precondition, REMOVE(h,k)(h, k) and DELETE-MIN(h)(h) meet the postcondition.

Proof. By induction on the size. In every case below, the recursive call meets the precondition, so by induction it returns a valid subtree with the same bhbh as the child it replaces; then Lemma 2 applies to the last line, and we check its two conditions and the color of the result.

Case k<h.keyk < h.key (lines L20 to L22). Since (P3) needs k≥h.keyk \ge h.key, this case has (P1) or (P2). The key is in h.lefth.left, which is not nil.

  • (P2): h.lefth.left is red with black children, so line L21 does nothing and the call on h.lefth.left has (P1). Line L22 replaces h.lefth.left; the right child of hh is black and unchanged. By Lemma 2, the result is hh, black.
  • (P1) and h.left.lefth.left.left red: line L21 does nothing. The call on the black h.lefth.left has (P2) and returns a black node. At FIX-UP, hh is red with two black children, so Lemma 2 applies.
  • (P1) and h.left.lefth.left.left black: h.righth.right is not nil, since bh(h.right)=bh(h.left)≥1bh(h.right) = bh(h.left) \ge 1. Line L21 calls MOVE-RED-LEFT. In Lemma 3 (a), the call on the red left child has (P1), and at FIX-UP the top is black. In Lemma 3 (b), kk is less than the key of the old hh, which is the left child of the new top, so the recursion goes there, with (P2), and returns a black node; at FIX-UP the top is red with two black children.

Case k≥h.keyk \ge h.key (lines L23 to L27).

  • (P2): line L23 rotates right at hh. The new top tt is the old h.lefth.left, black, with the black left child h.left.lefth.left.left (by (L3)) and the red right child hh, whose children are black. Now k>t.keyk > t.key, line L24 does not apply (t.right≠t.right \ne nil), line L25 does nothing (t.rightt.right is red), and line L27 calls REMOVE on the red hh, which has (P1). At FIX-UP, tt is black with a black left child, so Lemma 2 gives a black result.
  • (P3): line L23 does nothing, line L24 does not apply (h.right≠h.right \ne nil), and line L25 does nothing (h.righth.right is red). The red right child has two black children, by (L3) and (L2) below hh. If k=h.keyk = h.key, line L26 calls DELETE-MIN on it, and else line L27 calls REMOVE on it; both calls have (P1). At FIX-UP, hh is black with a black left child, and the result is black.
  • (P1): line L23 does nothing. If k=h.keyk = h.key and h.righth.right is nil, then bh(h.left)=0bh(h.left) = 0 and h.lefth.left is black, so h.lefth.left is nil: hh is a red leaf, and line L24 returns nil, with bh=0=bh(h)bh = 0 = bh(h). Else h.righth.right is not nil, and neither is h.lefth.left, by (L4). If h.right.lefth.right.left is red, line L25 does nothing, and the call at line L26 or L27 on the black h.righth.right has (P2) and returns a black node; at FIX-UP, hh is red with two black children. If h.right.lefth.right.left is black, line L25 calls MOVE-RED-RIGHT. In Lemma 4 (a), the call on the red right child has (P1), and the top is black at FIX-UP. In Lemma 4 (b), the new top tt has t.key<h.key≤kt.key < h.key \le k, so line L26 does not apply, and line L27 calls REMOVE on the old hh, which is black, with a black left child and a red right child, and k≥h.keyk \ge h.key: (P3). It returns a black node, so at the FIX-UP of tt, tt is red with two black children.

When line L26 runs, h.keyh.key becomes the least key mm of h.righth.right. Every key of h.lefth.left is less than k<mk < m, and every other key of h.righth.right is greater than mm, so after DELETE-MIN removes mm from h.righth.right, (L1) holds and the key kk is gone.

DELETE-MIN. If h.lefth.left is nil, then (P2) is impossible, so hh is red, and as above hh is a red leaf and the result is nil. Else it runs the case k<h.keyk < h.key above, with DELETE-MIN in place of REMOVE.

In every case the keys are K(h)∖{k}K(h) \setminus \{k\}, and bhbh is kept, by Lemma A, by induction and by Lemma 2. A result that comes from a call with hh black is black, as shown in each case. □\square

DELETE. Line L11 makes sure that kk is in the tree. Before line L12 the tree is valid with a black root. If both children of the root are black, line L12 paints the root red, which subtracts one from every path and so keeps (L4); now (P1) holds. Else the root has a red child, which is the left one by (L2), and (P2) holds. By Lemma 5, REMOVE returns a valid tree without kk. Line L14 paints the root black, which keeps (L4) and gives (L5).

Conclusion

Theorem. Starting from the empty tree, after any sequence of insertions and deletions the tree satisfies (L1) to (L5) and holds exactly the keys inserted and not deleted since. A search returns true exactly for these keys.

Proof. The empty tree satisfies the rules. INSERT and DELETE keep them and change the key set as stated, by Lemma 1 and Lemma 5. A search is correct by (L1). ■\blacksquare

Back to Left-leaning red-black tree