Red-Black Trees: Deletion
by math_explorer, Oct 26, 2010, 10:51 AM
Before we talk about that deletion procedure, we need to figure out how to delete a node in the general case.
![[asy]unitsize(.4cm);
pair pa=(0,0);
pair pb=(-3, -2);
pair pc=(-5, -4);
pair pd=(-1, -4);
pair pe=(3, -2);
pair pf=(-2, -6);
draw(pa--pb);
draw(pb--pc);
draw(pb--pd);
draw(pa--pe);
draw(pd--pf);
transform t = shift(pa);
fill(t*unitcircle, red+gray(.5));
label("$5$", t*(0, 0.6), S);
t = shift(pb);
fill(t*unitcircle, gray(.5));
label("$e$", t*(0, 0.6), S);
t = shift(pc);
fill(t*unitcircle, gray(.5));
label("$\varphi$", t*(0, 0.6), S);
t = shift(pd);
fill(t*unitcircle, blue+gray(.5));
label("$\pi$", t*(0, 0.6), S);
t = shift(pe);
fill(t*unitcircle, gray(.5));
label("$4!$", t*(0, 0.6), S);
t = shift(pf);
fill(t*unitcircle, green+gray(.5));
label("$2.9$", t*(0, 0.6), S);
[/asy]](//latex.artofproblemsolving.com/f/4/8/f4870e447a6062bb15549c716c09409d5bb49c73.png)
So anyway, suppose we have this awkward binary search tree which probably can't be exactly expressed on a computer unless you've got symbolic manipulation and very intelligent comparison features. There are essentially three cases.
1. Delete the green node,
, with 0 children. Since it's a leaf node, no problem. Just remove it, and replace with nil if necessary.
2. Delete the blue node,
, with 1 child. This is pretty easy, too: just take it out and reconnect its child with its parent, in this case,
and
.
3. Delete the red node,
, with 2 children. (It also happens to be the root, but that's not important.) We instead overwrite it with its predecessor,
, and delete the old
node. This reduces to case 2 or 1, since the predecessor can't have a right child. Yay.
Yep, so that's how you delete.
Righty then, on to the red-black deletion procedure. So first we do what we outlined above, of course, and figure out how badly we messed up the Red-Black Rules, and try to fix it.
Okay. Case 1 is actually a special case of Case 2, when the part below the deleted node consists of nothing. Case 3 also ends up in Case 2, given that the overwrite keeps the host's original color, so the color configuration there don't change. So we just have to deal with a spliced-out node, maybe
in this instance.
Suppose
was red. Then both parts that were originally had to be connected to
were black, so splicing them together won't break the no-reds-together rule. Also, the equal-black-paths aren't broken either (annoying meticulous two-column casework left to the reader). It works, just that way.
Suppose
was black. We've now gotten rid of a black node in one of those paths, and possibly made two reds touch as well. Ouch.
Let's see what we can do to fix this. We know that whatever node we spliced out,
, only had one child (or none). Here that would be
. Well, every path that went through
and still exists now must go through
, and we need to readd a black node to each of those paths. Let's make
black! We're done!
Dammit.
might have already been black. If it's red, we're done, so let's see what happens if
was black. Let's pretend we can make
DOUBLE-BLACK. That works, but we don't allow that kind of color, so we'll have to figure out how to fix it. Hmm...
(Also, to prevent us somehow using
as a variable later, we'll just call the DOUBLY-BLACK node
. And we'll color it a bright magenta, which is what DOUBLY-BLACK looks like in cartoons. Or maybe, I don't know.)
Okay, let's look at
's sibling.
![[asy]
unitsize(.4cm);
pair pa=(0,0);
pair pb=(-3, -2);
pair pc=(3, -2);
pair pd=(1, -4);
pair pe=(5, -4);
draw(pa--pb);
draw(pa--pc);
draw(pc--pd);
draw(pc--pe);
transform t = shift(pa);
fill(t*unitcircle, black);
label("$A$", t*(0, 0.6), S, white);
t = shift(pb);
fill(t*unitcircle, red+blue);
label("$B$", t*(0, 0.6), S);
t = shift(pc);
fill(t*unitcircle, red+gray(.5));
label("$C$", t*(0, 0.6), S);
t = shift(pd);
fill(t*unitcircle, black);
label("$D$", t*(0, 0.6), S, white);
t = shift(pe);
fill(t*unitcircle, black);
label("$E$", t*(0, 0.6), S, white);
[/asy]](//latex.artofproblemsolving.com/f/7/b/f7bd06b873a7f681edf458978429ecb59eea3650.png)
Suppose it's red. We don't want that, because they're siblings, and they're so far (read (no pun intended (depending on whether you read "read" in such a situation as "red" or as "reed" (read: whatever (
(invalid reference to xkcd 541 (which I saw used in the Python documentation SOMEWHERE (don't overuse caps, guys (Python rocks)))))))): two black-levels) apart! Let's fix it for them, by left-rotating
and
, so that
becomes
's brother. Crunch. (Picture left to the reader as an exercise.)
![[asy]
unitsize(.4cm);
pair pa=(0,0);
pair pb=(-3, -2);
pair pc=(3, -2);
pair pd=(1, -4);
pair pe=(5, -4);
draw(pa--pb);
draw(pa--pc);
draw(pc--pd);
draw(pc--pe);
transform t = shift(pa);
fill(t*unitcircle, gray);
label("$A$", t*(0, 0.6), S);
t = shift(pb);
fill(t*unitcircle, red+blue);
label("$B$", t*(0, 0.6), S);
t = shift(pc);
fill(t*unitcircle, black);
label("$C$", t*(0, 0.6), S, white);
t = shift(pd);
fill(t*unitcircle, gray);
label("$D$", t*(0, 0.6), S, black);
t = shift(pe);
fill(t*unitcircle, gray);
label("$E$", t*(0, 0.6), S, black);
[/asy]](//latex.artofproblemsolving.com/9/9/3/993a8ef2eb0d73e7ecb187b12d86971b6f6754e5.png)
Otherwise,
's sibling is black. You'll notice the other nodes are gray, which isn't a color, but I don't like using other people's storage for these images even though certain other people *cough* really like it, so I'm going to smoosh all these other cases into one.
Suppose
and
are both black. Then we can take away one black-level from
and
and give it to
. If
is now DOUBLY-BLACK, we can use RECURSION. Whee.
Suppose
is red,
is black. Then we can right-rotate
and
, swap their colors, and reduce to the next case.
Suppose
is red. Then we can left-rotate
and
, take away the extra blackness from
, and give it to
. No recursion necessary!
Yay, we're done!
![[asy]unitsize(.4cm);
pair pa=(0,0);
pair pb=(-3, -2);
pair pc=(-5, -4);
pair pd=(-1, -4);
pair pe=(3, -2);
pair pf=(-2, -6);
draw(pa--pb);
draw(pb--pc);
draw(pb--pd);
draw(pa--pe);
draw(pd--pf);
transform t = shift(pa);
fill(t*unitcircle, red+gray(.5));
label("$5$", t*(0, 0.6), S);
t = shift(pb);
fill(t*unitcircle, gray(.5));
label("$e$", t*(0, 0.6), S);
t = shift(pc);
fill(t*unitcircle, gray(.5));
label("$\varphi$", t*(0, 0.6), S);
t = shift(pd);
fill(t*unitcircle, blue+gray(.5));
label("$\pi$", t*(0, 0.6), S);
t = shift(pe);
fill(t*unitcircle, gray(.5));
label("$4!$", t*(0, 0.6), S);
t = shift(pf);
fill(t*unitcircle, green+gray(.5));
label("$2.9$", t*(0, 0.6), S);
[/asy]](http://latex.artofproblemsolving.com/f/4/8/f4870e447a6062bb15549c716c09409d5bb49c73.png)
So anyway, suppose we have this awkward binary search tree which probably can't be exactly expressed on a computer unless you've got symbolic manipulation and very intelligent comparison features. There are essentially three cases.
1. Delete the green node,

2. Delete the blue node,



3. Delete the red node,



Yep, so that's how you delete.
Righty then, on to the red-black deletion procedure. So first we do what we outlined above, of course, and figure out how badly we messed up the Red-Black Rules, and try to fix it.
Okay. Case 1 is actually a special case of Case 2, when the part below the deleted node consists of nothing. Case 3 also ends up in Case 2, given that the overwrite keeps the host's original color, so the color configuration there don't change. So we just have to deal with a spliced-out node, maybe

Suppose


Suppose

Let's see what we can do to fix this. We know that whatever node we spliced out,





Dammit.



(Also, to prevent us somehow using


Okay, let's look at

![[asy]
unitsize(.4cm);
pair pa=(0,0);
pair pb=(-3, -2);
pair pc=(3, -2);
pair pd=(1, -4);
pair pe=(5, -4);
draw(pa--pb);
draw(pa--pc);
draw(pc--pd);
draw(pc--pe);
transform t = shift(pa);
fill(t*unitcircle, black);
label("$A$", t*(0, 0.6), S, white);
t = shift(pb);
fill(t*unitcircle, red+blue);
label("$B$", t*(0, 0.6), S);
t = shift(pc);
fill(t*unitcircle, red+gray(.5));
label("$C$", t*(0, 0.6), S);
t = shift(pd);
fill(t*unitcircle, black);
label("$D$", t*(0, 0.6), S, white);
t = shift(pe);
fill(t*unitcircle, black);
label("$E$", t*(0, 0.6), S, white);
[/asy]](http://latex.artofproblemsolving.com/f/7/b/f7bd06b873a7f681edf458978429ecb59eea3650.png)
Suppose it's red. We don't want that, because they're siblings, and they're so far (read (no pun intended (depending on whether you read "read" in such a situation as "red" or as "reed" (read: whatever (





![[asy]
unitsize(.4cm);
pair pa=(0,0);
pair pb=(-3, -2);
pair pc=(3, -2);
pair pd=(1, -4);
pair pe=(5, -4);
draw(pa--pb);
draw(pa--pc);
draw(pc--pd);
draw(pc--pe);
transform t = shift(pa);
fill(t*unitcircle, gray);
label("$A$", t*(0, 0.6), S);
t = shift(pb);
fill(t*unitcircle, red+blue);
label("$B$", t*(0, 0.6), S);
t = shift(pc);
fill(t*unitcircle, black);
label("$C$", t*(0, 0.6), S, white);
t = shift(pd);
fill(t*unitcircle, gray);
label("$D$", t*(0, 0.6), S, black);
t = shift(pe);
fill(t*unitcircle, gray);
label("$E$", t*(0, 0.6), S, black);
[/asy]](http://latex.artofproblemsolving.com/9/9/3/993a8ef2eb0d73e7ecb187b12d86971b6f6754e5.png)
Otherwise,

Suppose






Suppose




Suppose





Yay, we're done!
This post has been edited 1 time. Last edited by math_explorer, Aug 19, 2011, 9:40 AM