Java实现数据结构——红黑树删除
Posted x_k
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java实现数据结构——红黑树删除相关的知识,希望对你有一定的参考价值。
本文参考《算法导论(第三版)》和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;
以上是关于Java实现数据结构——红黑树删除的主要内容,如果未能解决你的问题,请参考以下文章