Algorithm Search⌘K

Proofs: Red-black tree

These are the human proofs of Red-black tree, at full length.

Insertion and deletion keep the red-black properties full

This proof shows: correctness, rotations.insert, rotations.delete.

The properties are (R0) the search-tree order, (R1) the root is black, (R2) every leaf T.nilT.nil is black, (R3) a red node has two black children, and (R4) for each node vv, all paths from vv down to a leaf have the same number of black nodes. A path weight is the number of black nodes on a path. Rotations keep the in-order sequence of the keys (see tree rotation), so they keep (R0). Each recoloring and rotation below changes only nodes in a subtree SS whose root is at a fixed place; we check (R4) by comparing, for each leaf of SS, the weight of the path from the top of SS to that leaf before and after. If all these weights stay equal, then (R4) holds for each node above SS, because the paths from such a node agree outside SS.

Insertion

Initialization. Lines L1 to L7 are the insertion of a binary search tree, so (R0) holds and the key set gains kk (or line L4 returns and nothing changes, when kk is present). The new node zz is red, with two black children T.nilT.nil. It replaces a leaf: each path that ended at that leaf now goes on through the red zz to a black leaf, so its weight is unchanged, and (R4) and (R2) hold. The only possible failures are (R1), when the tree was empty and zz is the root, and (R3), when the parent of zz is red.

Invariant. At each test of line L9:

  1. (I1) zz is red.
  2. (I2) (R0), (R2) and (R4) hold.
  3. (I3) (R1) holds unless zz is the root.
  4. (I4) (R3) holds, except that zz and its parent may both be red.

This holds at the first test, by the initialization.

The grandparent. If the loop body runs, z.pz.p is red. By (I3), the root is black unless zz is the root, and the parent of the root is the black T.nilT.nil, so z.pz.p is not the root, and g=z.p.pg = z.p.p is a node. By (I4), the only red-red pair is zz and z.pz.p, so gg is black. Let p=z.pp = z.p, and assume that pp is the left child of gg (line L10); the other case is the mirror image (line L18). Let uu be the right child of gg, the uncle.

Case 1: uu is red (line L13). Here SS is the subtree of gg. Before, each path from gg to a leaf passes gg (black) and then exactly one of pp, uu (red). After the recoloring, it passes gg (red) and one of pp, uu (black). Nothing else changes, so each path weight from the top of SS is the same, and (R4) holds. Now pp and uu are black, and gg is red with two black children. The only possible red-red pair is gg and its parent. The new zz is gg, which is red, so (I1) and (I4) hold. The root changed color only if gg is the root, which is then zz, so (I3) holds.

Case 2: uu is black and zz is the right child of pp (line L15). Let α\alpha be the left subtree of pp, and β\beta, γ\gamma the subtrees of zz. Since pp and zz are red, (R4) at pp says that all paths into α\alpha, β\beta and γ\gamma have the same weight from their roots. The left rotation at pp makes zz the left child of gg, with left child pp; pp has subtrees α\alpha and β\beta, and zz has right subtree γ\gamma. Both rotated nodes are red, so each path from gg's left child has the same weight as before. The new zz is the old pp: it is red, it is the left child of its red parent (the old zz), and this is the only red-red pair, because α\alpha, β\beta and γ\gamma have black roots (they were children of red nodes, other than the pair pp, zz). So the state is that of case 3.

Case 3: uu is black and zz is the left child of pp (lines L16 and L17). Let γ\gamma be the right subtree of pp. The roots of γ\gamma and of the subtrees of zz are black by (I4), and uu is black. By (R4) at gg, with pp and zz red, the paths into the subtrees of zz, into γ\gamma, and into the subtree of uu (counting uu) all have the same weight bb. Lines L16 and L17 paint pp black and gg red and rotate right at gg. Now pp is the root of SS, with children zz (red) and gg (red), and gg has children γ\gamma and uu. Each path from pp has weight 1+b1 + b: the black pp, then a red node, then a path of weight bb. Before, each path from gg had weight 1+b1 + b too: the black gg, then a red node or the path through uu. So (R4) holds. The red nodes zz and gg have black children, and pp is black, so (R3) holds everywhere. If gg was the root, then pp is the new root and it is black.

Termination. Case 1 moves zz up two levels, so it runs at most h/2h/2 times in a row. After case 3, the parent of zz is the black pp, so the test of line L9 fails. At exit, z.pz.p is black, so by (I4) (R3) holds, and by (I3) (R1) can fail only at the root. Line L19 paints the root black. This adds one to the weight of every path from the root, so (R4) still holds, and it cannot create a red-red pair. So the tree satisfies (R0) to (R4). Cases 2 and 3 happen at most once, so an insertion does at most two rotations. □\square

Deletion

Weights with an extra black. During DELETE-FIXUP, the node xx carries an extra black: in path weights, xx counts one more than its color says. Write (R4′') for (R4) with this count.

Lemma 1 (RB-DELETE). Let yy be the node that leaves its place (line L23 or L27), cc its original color, and xx the node that takes its place. After lines L23 to L31, the tree holds the old keys without the key of zz, and (R0) and (R2) hold. If cc is red, (R1), (R3) and (R4) hold. If cc is black, (R4′') holds, and (R3) holds except for a red-red pair that contains xx, and (R1) holds except that xx may be a red root.

Proof. Lines L23 to L31 are the deletion of a binary search tree: if zz has at most one child, that child replaces zz; else the successor yy, which has no left child, is replaced by its right child xx, and then yy replaces zz. So (R0) holds and the key of zz is gone. Line L31 gives yy the color of zz, so at the old place of zz the colors are as before. The only change of colors on paths is that the node yy of color cc no longer lies above xx.

If cc is red, the path weights do not change, so (R4) holds. A red yy is not the root, so (R1) holds. Its parent was black and its children were black, so putting xx under the parent of yy makes no red-red pair. In the two-child case, yy gets the color of zz; if that color is red, the children of yy are z.leftz.left, and z.rightz.right or xx, all of which were children of a red node, so they are black. So (R3) holds.

If cc is black, each path through the old place of yy lost one black node, and these are exactly the paths through xx. With the extra black at xx, each weight is as before, so (R4′') holds. A red-red pair can appear only between xx and its new parent, and the root changes only if xx becomes the root. □\square

Invariant. At each test of line L33:

  1. (J1) (R0), (R2) and (R4′') hold.
  2. (J2) (R3) holds, except for red-red pairs that contain xx.
  3. (J3) (R1) holds, except that xx may be a red root.

By Lemma 1 this holds at the first test. When the loop body runs, xx is black and is not the root. So by (J2) there is no red-red pair at all, and by (J3) the root is black.

Assume xx is the left child of p=x.pp = x.p (line L34); the right case is the mirror image (line L45). Let ww be the right child of pp (line L35). The paths from xx have weight at least 2 (the black xx and its extra), so by (R4′') at pp the paths from ww have weight at least 2, and ww is not T.nilT.nil.

Case 1: ww is red (line L37). Then pp and the children aa, bb of ww are black. Let WW be the weight of each path from xx (with the extra), which by (R4′') at pp is also the weight of each path from ww, and so of each path from aa and from bb (the red ww adds nothing). Line L37 paints ww black and pp red and rotates left at pp. Now ww is the top, with children pp and bb, and pp has children xx and aa. Each path from the top has weight 1+W1 + W: through ww (black), then pp (red) and a path from xx or aa, or a path from bb. Before, each path from pp had weight 1+W1 + W too. So (R4′') holds. The red pp has the black children xx and aa, so (J2) holds, and if pp was the root, the black ww is the new root. The new sibling of xx is aa, which is black, and x.px.p is red. One of cases 2 to 4 follows.

Case 2: ww is black and both children of ww are black (line L39). Line L39 paints ww red and moves the extra black from xx to pp. Each path from pp through ww loses one, and each path through xx loses the extra; the extra at pp adds one to all paths through pp. So the weights from the top of the subtree of pp are unchanged, and (R4′') holds with the new x=px = p. The red ww has black children. If pp is red, then pp and ww form a red-red pair, which contains the new xx, as (J2) allows. If pp is the root, it is the new xx, as (J3) allows.

Case 3: ww is black, its left child aa is red and its right child bb is black (line L42). Let a1a_1 and a2a_2 be the children of aa; they are black. By (R4) at ww, the paths from a1a_1, from a2a_2 and from bb have the same weight dd. Line L42 swaps the colors of ww and aa and rotates right at ww. Now aa (black) has children a1a_1 and ww (red), and ww has children a2a_2 and bb. Each path from aa has weight 1+d1 + d; before, each path from ww had weight 1+d1 + d. So (R4′') holds. The red ww has black children a2a_2 and bb. The new sibling of xx is aa, black, and its right child ww is red. This is case 4.

Case 4: ww is black and its right child bb is red (lines L43 and L44). Let aa be the left child of ww, of any color, and let cpc_p be the color of pp (1 if black, 0 if red). Let WW be the weight of each path from xx with the extra. By (R4′') at pp and (R4) at ww, each path from aa and each path from bb has weight W−1W - 1, and bb is red, so each path from a child of bb has weight W−1W - 1. Before, each path from pp has weight cp+Wc_p + W. Lines L43 and L44 give ww the color cpc_p, paint pp and bb black, rotate left at pp, and drop the extra black of xx. Now ww is the top, with children pp and bb, and pp has children xx and aa. A path from the top through pp and xx has weight cp+1+(W−1)c_p + 1 + (W - 1). A path through pp and aa has weight cp+1+(W−1)c_p + 1 + (W - 1). A path through bb has weight cp+1+(W−1)c_p + 1 + (W - 1). All equal cp+Wc_p + W, so (R4) holds with no extra black anywhere. The nodes pp and bb are black. If ww is red, then cpc_p was red, so the parent of the top is black, since there was no red-red pair. So (R3) holds everywhere. If pp was the root, ww is the root with the color of pp, which was black by (J3). Line L44 sets xx to the root, so the loop ends.

Termination. Case 2 moves xx up one level. After case 1, the parent of xx is red, so case 2 ends the loop, and cases 3 and 4 end it in at most two more steps. Case 3 leads to case 4, which ends the loop. So the loop runs at most h+2h + 2 times. It does at most three rotations: one in case 1, one in case 3 and one in case 4, and each of these cases happens at most once.

Exit. At exit, xx is red or xx is the root. Line L46 paints xx black and drops its extra black. If xx was red, the paths through xx gain one from the color and lose the extra, so (R4) holds, and the red-red pairs that contained xx are gone. If xx is a black root, dropping the extra subtracts one from every path from the root, which keeps (R4). In both cases (R1) to (R4) hold. If cc was red, DELETE-FIXUP is not called and Lemma 1 already gives the properties. □\square

Conclusion

Theorem. Starting from the empty tree, after any sequence of RB-INSERT and RB-DELETE calls the tree satisfies (R0) to (R4) and holds exactly the keys inserted and not deleted. A search returns true exactly for these keys. Each insertion does at most two rotations, and each deletion at most three.

Proof. The empty tree, the sentinel alone, satisfies (R0) to (R4). The insertion and deletion parts above show that each operation keeps the properties and changes the key set as stated. A search is correct by (R0). ■\blacksquare

Back to Red-black tree