节点标识
待删除节点D
兄弟节点B
侄子节点C
父亲节点P
双黑节点N
删除步骤
首先分为三类:
- D有2个非空子节点
- 找到D的后续节点back,将back的值传递给D,此时待删除节点变为 back,而此时的back一定只有一个外部子节点,因此转为第三类
// 待删除节点有2个外部子节点
if (n.left != null && n.right != null) {
// 后继节点
RBNode<T> back = successor(n);
n.data = back.data;
delete(back);
return;
}
- D没有非空子节点
- 判断当前节点是否为根节点
root
,若是,则直接root = null。否则,直接删除该节点,判断该节点为黑,则出现双黑节点
N,需要进行双黑节点处理fixAfterDelete()
- 判断当前节点是否为根节点
// 待删除节点没有外部子节点
else if (n.left == null && n.right == null) {
if (isLeft(n))
p.left = null;
else if (isRight(n))
p.right = null;
else {// 说明待删除的节点子节点为null,且父节点为null,此时树中仅剩根节点
root = null;
return;
}
}
...
if (!isRed(n))
fixAfterDelete(n,p);
- D只有1个非空子节点
- 若D为根节点
root
,直接将非空子节点称为根节点root
,并将节点颜色修正为黑色,算法结束
- 若D为根节点
// n为根节点
else {
root = n.left == null?n.right:n.left;
root.parent = null;
root.color = BLACK;
return;
}
- 若D为红色,直接删除,算法结束。若D为黑色,且有一个
红色子节点
,则将子节点取代D的位置,并将颜色修正为黑色,结束算法 - 出现
双黑节点
,需要进行双黑节点处理fixAfterDelete()
双黑节点处理
分为三类:
- 双黑节点N的兄弟节点B为红色
- 交换
N.parent
和B
的颜色。B为右
,则B左旋
;B为左
,则B右旋
。该变换后,转为第二类情况
- 交换
// 兄弟节点为红色,先做颜色变换和旋转操作
if (isRed(brother)) {
// 父节点与兄弟交换颜色
boolean temp = p.color;
p.color = brother.color;
brother.color = temp;
// 兄弟在右,则左旋;反之
if (isRight(brother))
left_rotate(p);
else
right_rotate(p);
// 得到新的兄弟节点
brother = p.left == null?p.right:p.left;
}
- 双黑节点N的兄弟节点B为黑色
- B存在红色子节点C
(1). B和C都是左子节点
/右子节点
(远侄子):B.color
=N.parent.color
、C.color
=BLACK
、N.parent.color
=BLACK
,对N.parent
作左旋
/右旋
(2). B为右
/左
、C为左
/右
(近侄子):B先作右旋
/左旋
,转为情况1
- B存在红色子节点C
// 兄弟节点存在红色子节点
if (isRed(brother.left) || isRed(brother.right)) {
// 红色子节点
RBNode<T> redC = isRed(brother.left)?brother.left:brother.right;
fixWhenBrotherHasRed(p,brother,redC);
}
// 当兄弟节点存在红色子节点的情况下进行调整
// 输入参数:父节点、兄弟节点、红色子节点
private void fixWhenBrotherHasRed(RBNode<T> p, RBNode<T> b, RBNode<T> c) {
if (isRight(b) && isLeft(c)) {
right_rotate(b);
// 近侄子和兄弟互换,变为远侄子情况
RBNode<T> temp;
temp = c;
c = b;
b = temp;
}
if (isLeft(b) && isRight(c)) {
left_rotate(b);
// 近侄子和兄弟互换,变为远侄子情况
RBNode<T> temp;
temp = c;
c = b;
b = temp;
}
b.color = p.color;
p.color = BLACK;
c.color = BLACK;
if (isRight(b))
left_rotate(p);
else
right_rotate(p);
}
* B有两个黑色子节点(包括`NIL节点`)
作以下调整:B.color
= RED
,N.parent.color
= BLACK
。若N.parent.color
原来为红色,则算法结束。否则,将N.parent
作为新的双黑节点
,继续fixAfterDelete()
//兄弟节点存在2个黑色子节点,包含NIL节点
else {
brother.color = RED;
if (isRed(p)) {
p.color = BLACK;
}else {
// 若父节点原来为black,则父节点作为新的双黑节点继续调整,直到根节点
if (p.parent != null)
fixAfterDelete(p,p.parent);
}
}