本文参考《算法导论(第三版)》和JDK1.8-HashMap源码
回顾红黑树基本性质
- 每个节点或是红色,或是黑色
- 根节点是黑色
- 每个叶节点是黑色
- 如果一个节点是红色,那么它的两个子节点都是黑色
- 对于每个节点,从该节点到其所有后代叶节点的简单路径上,包含相同数目的黑色节点
删除节点
节点的基本删除操作:二叉查找树-删除元素
《算法导论(第三版)》中的二叉查找树的删除操作原理是,使用删除节点的后继节点代替删除节点的位置
红黑树删除
获取双色节点(replacement)
由于Java实现红黑树中的性质3,每个叶节点都是Null,无法取得Null父节点指针,所以先使用删除节点作为双色节点,着色完毕后删除。
根据删除节点p的子节点作为不同情况处理:
1. p仅存在当个子节点,那么replacement设置为子节点。
2. p不存在子节点,那么replacement设置为p
3. p同时存在两个子节点,那么如果p的后继节点的右孩子sr不为空,那么replacement设置为sr,否则replacement设置为p
重新着色
当删除的节点为黑色节点时,删除操作会破坏性质5,需要重新着色。
replacement节点是其父节点的左孩子
- replacement的兄弟节点s是红色,左旋其父节点,交换其父节点和兄弟节点s颜色。
- 如果步骤1中执行操作后,replacement无兄弟节点s,则replacement向上传递。否则,执行步骤3。
- replacement的兄弟节点s不存在子节点,或存在两个黑色节点,那么s设为红色,向上传递replacement。否则执行步骤4。
- 兄弟节点s的右子节点不为红色的话,通过旋转设置为红色。
- 兄弟节点s的右子节点为红色。旋转replacement父节点,交换父节点和父节点的右子节点颜色,父节点设置为黑色,s的右子节点设为黑色。退出循环。
步骤1例图:
步骤3例图:
步骤4例图:
步骤5例图:
replacement节点是其父节点的右孩子
与上面基本一致
replacement为删除原节点
当删除的节点不存在子节点,或者删除节点的后继节点的右子节点不存在时,使用的是删除节点本身作为双色节点,重新着色后,需要断开删除节点与其父节点。
代码实现
public boolean delete(E e) {
RBTreeNode<E> p, pl, pr, replacement;
// 一、查找删除节点p
if ((p = this.find(e)) == null) {
return false;
}
pl = p.left;
pr = p.right;
// 二、判断p的子节点
if (pl != null && pr != null) {
// 1、p存在两个子节点
// 2、交换p和其后继节点s的位置和颜色
// 2.1、获取后继节点s
RBTreeNode<E> s = pr, sl;
while ((sl = s.left) != null) {
s = sl;
}
// 2.2、交换p和s的颜色
boolean c = s.red;
s.red = p.red;
p.red = c;
// 2.3、s是否为p的右子节点
RBTreeNode<E> sr = s.right, pp = p.parent;
if (s == pr) {
// 2.3.1、s为p的右子节点,s存在为p的父节点
// 关联s节点的右子节点
p.parent = s;
s.right = p;
} else {
// 2.3.2、s不为p的右子节点,s存在不为p的父节点
// sp的左子节点关联p
p.parent = s.parent;
s.parent.left = p;
// 关联s节点的右子节点
s.right = pr;
pr.parent = s;
}
// 2.4、s的右子节点不为空,关联p
if ((p.right = sr) != null) {
sr.parent = p;
}
// 2.5、关联s和pl,pp、清空p的左子节点指针
s.left = pl;
pl.parent = s;
if ((s.parent = pp) == null) {
root = s;
} else if (p == pp.left) {
pp.left = s;
} else {
pp.right = s;
}
p.left = null;
// 2.6、s节点是否存在右子节点
if (sr != null) {
// 存在,使用该节点作为替换节点,步骤三中,清空p
replacement = sr;
} else {
// 不存在,使用p作为替换节点
replacement = p;
}
} else if (pl != null) {
replacement = pl;
} else if (pr != null) {
replacement = pr;
} else {
// 删除节点为无子节点
replacement = p;
}
// 三、替换节点不为p时,删除p
if (replacement != p) {
// 删除节点存在子节点时,replacement替换p
RBTreeNode<E> pp = replacement.parent = p.parent;
if (pp == null) {
root = replacement;
} else if (p == pp.left) {
pp.left = replacement;
} else {
pp.right = replacement;
}
p.left = p.right = p.parent = null;
}
// 四、重新着色
if (!p.red) {
root = this.balanceDeletion(this.root, replacement);
}
// 五、分离
if (replacement == p) {
// 双色节点为p时,与其父节点断开
RBTreeNode<E> pp = p.parent;
p.parent = null;
if (pp != null) {
if (p == pp.left) {
pp.left = null;
} else if (p == pp.right) {
pp.right = null;
}
}
}
size--;
return true;
}
重新着色
public <E> RBTreeNode<E> balanceDeletion(RBTreeNode<E> root, RBTreeNode<E> x) {
for (RBTreeNode<E> xp, xpl, xpr; ; ) {
if (x == null || x == root) {
return root;
} else if ((xp = x.parent) == null) {
x.red = false;
return x;
} else if (x.red) {
x.red = false;
return root;
} else if ((xpl = xp.left) == x) {
// x为左子节点
if ((xpr = xp.right) != null && xpr.red) {
// x的兄弟节点存在,且是红色
xpr.red = false;
xp.red = true;
// 通过旋转将x的兄弟节点换成黑色节点
root = rotateLeft(root, xp);
xpr = (xp = x.parent) == null ? null : xp.right;
}
if (xpr == null) {
// 无兄弟节点,x向上传递
x = xp;
} else {
// 兄弟节点s的子节点分析
RBTreeNode<E> sl = xpr.left, sr = xpr.right;
if ((sr == null || !sr.red) && (sl == null || !sl.red)) {
// case 1:s不存在子节点
// case 2:s存在两个黑色子节点
xpr.red = true;
x = xp;
} else {
if (sr == null || !sr.red) {
// s右孩子不存在或是黑色
// 通过旋转将s的右孩子换成红色
sl.red = false;
xpr.red = true;
root = rotateRight(root, xpr);
xpr = (xp = x.parent) == null ? null : xp.right;
}
// s的左孩子为红色,去掉额外黑色
if (xpr != null) {
xpr.red = xp.red;
if ((sr = xpr.right) != null) {
sr.red = false;
}
}
if (xp != null) {
xp.red = false;
root = rotateLeft(root, xp);
}
// 退出循环
x = root;
}
}
} else {
if (xpl != null && xpl.red) {
xpl.red = false;
xp.red = true;
root = rotateRight(root, xp);
xpl = (xp = x.parent) == null ? null : xp.left;
}
if (xpl == null) {
x = xp;
} else {
RBTreeNode<E> sl = xpl.left, sr = xpl.right;
if ((sl == null || !sl.red) &&
(sr == null || !sr.red)) {
xpl.red = true;
x = xp;
} else {
if (sl == null || !sl.red) {
sr.red = false;
xpl.red = true;
root = rotateLeft(root, xpl);
xpl = (xp = x.parent) == null ?
null : xp.left;
}
if (xpl != null) {
xpl.red = xp.red;
if ((sl = xpl.left) != null) {
sl.red = false;
}
}
if (xp != null) {
xp.red = false;
root = rotateRight(root, xp);
}
x = root;
}
}
}
}
}