与n个节点的红黑树上的其他操作一样,删除一个节点要花费O(lgn)时间,但与插入操作相比,删除操作要稍微复杂一点。
如果被删除节点是红色的,红黑性质将继续保持,但如果被删除节点是黑色的话,就有可能发生一个或多个红黑性质被破坏的情况。
其核心在于,进行删除操作后的红黑树如果不符合其性质,我们如何进行调节。
下面结合伪代码进行讲解:
RB-TRANSPLANT方法将节点v替换u节点的位置
RB-TRANSPLANT(T,u,v) //T为树,u,v为节点
if u.p==T.nil //如果u的父节点等于哨兵(哑节点,可以看作空)
T.root=v //树T的根节点设为v
else if u==u.p.left //否则如果u是其父节点的左孩子
u.p.left=v //将其父节点的左孩子换为v
else u.p.right=v //否则将其父节点的右孩子换为v
v.p=u.p //v的父节点改为u的父节点
RB-DELETE()方法用于执行红黑树的删除操作:
RB-DELETE(T,z) //T为树,z为删除节点
y=z
y-original-color=y.color //记住y的原始颜色
if z.left==T.nil //如果z节点的左孩子为空
x=z.right //x就为z的右孩子
RB-TRANSPLANT(T,z,z.right) //将z节点的右孩子替换z
else if z.right==T.nil //否则如果z节点的右孩子为空
x=z.left //x为z的左孩子
RB-TRANSPLANT(T,z,z.left)//将z节点的左孩子替换z
else y=TREE-MINIMUM(z.right) //否则y等于z右子树的最小节点
y-original-color=y.color //记录y的原始颜色
x=y.right //x等于y的右孩子
if y.p==z //如果z是y的父节点
x.p=y //将x的父节点设为y
else RB-TRANSPLANT(T,y,y.right)//否则将y的右孩子替换y
y.right=z.right //z的右孩子给y
y.right.p=y
RB-TRANSPLANT(T,z,y) //y替换z的位置
y.left=z.left //z的左孩子给y
y.left.p=y
y.color=z.color //z的颜色给y
if y-original-color==black //如果z右子树的最小节点是黑色的
RB-DELETE-FIXUP(T,x) //修复红黑树
RB-DELETE-FIXUP()方法如下:
RB-DELETE-FIXUP(T,x)
while x!=T.root &&x.color==black //当x不为根节点且x为黑色时
if x==x.p.left //如果x为左孩子
w=x.p.right //w等于x的兄弟节点
if w.color==red //如果w为红色
w.color=black //将w变为黑色
x.p.color=red //x的父节点变为红色
LEFT-ROTATE(T,x.p) //将x的父节点向左旋转
w=x.p.right //w为x父节点的右孩子
if w.left.color==black&&w.right.color==black //如果w左右孩子都为黑色
w.color=red //将w染成红色
x=x.p //x替换其父节点
else if w.right.color==black //否则如果w的右孩子为黑色
w.left.color=black //将w的左孩子染成黑色
w.color=red //将w染成红色
RIGHT-ROTATE(T,w) //将w向右旋转
w=x.p.right //w设为x的兄弟节点
w.color=x.p.color //否则w的颜色设为和x父节点一样
x.p.color=black //x父节点的颜色设为黑色
w.right.color=black //w的右孩子设为黑色
LEFT-ROTATE(T,x.p) //将x父节点向左旋转
x=T.root //x设为树的根节点
else(same as then clause with "right" and "left" exchanged)//否则在前面的基础上交换left和right
x.color=black//将x的颜色设为黑色
通过上面的代码我们可以看出调节过程有四种情况:
情况1:x的兄弟节点w是红色的
情况2:x的兄弟节点w是黑色且w的两个孩子都为黑色
情况3:x的兄弟节点w是黑色且w的左孩子为红色,右孩子是黑色
情况4:x的兄弟节点w是黑色且w的右孩子是红色的
针对者四种情况有不同的处理方案,可以跟随上面代码自己画一颗树操作来进行理解。