删除及Java代码实现-红黑树-数据结构和算法
目录
内容
前面的章节我们了解了红黑树的基础知识、红黑树的基础操作、添加,那么下面我们继续探索下红黑的数的删除。
1、分析
- 删除思路:黑红数节点删除 涉及节点移除及节点调整。
- 节点(移除):如果目标节点有后继(或者前驱节点),那么节点和颜色互换,要移除节点变为后继(或者前驱)节点;如何没有那么要移除的节点还是当前节点。
- 节点(调整):如何要移除节点为后继(或者前驱)节点,判断是否有右节点(或者左节点),有则调整节点为右节点(或者左节点);没有右节点(或者左节点),调整节点为后继(或者前驱)节点。如何没有后继(或者前驱)节点, 调整节点为当前节点。
- 具体移除和调整步骤下面分情况具体分析
为什么要用节点的后继或者前驱来替换目标节点呢?
2、具体步骤
2.1、移除
-
如果根节点root为空,结束。
-
找到要删除的节点p(参考二叉搜索树的查找)
-
如果节点p为空,结束
-
pl为p的左节点,pr为p的右节点,replacement为要调整的节点(根据情况设置值) .
情况图示: -
如果p的左节点pl,右节点pr都不为空
- 那么一定存在后继节点s,查找后继节点s
- s与p互换颜色,后续根据情况互换位置
- sr为s的右节点,pp为p的父节点
- 如果s == pr(后继节点为p的右节点),图示:
- p的父节点指向s
- s的右节点指向p
- 否则,sp为s的父节点
- p的父节点指向sp,如果不为空
- 如果s是sp的左节点,那么sp的左节点指向p
- 否则sp右节点指向p
- s右节点指向pr,如果不为空
- pr的父节点指向s
- p的左节点设置为null
- p的右节点指向sr,如果不为空
- sr的父节点指向p
- s的左节点指向pl,如果不为空
- pl的父节点指向s
- s的父节点指向pp,如果为空
- 根据点root指向s
- 否则,判断如果p是pp的左节点
- pp的左节点指向s
- pp的右节点指向s
- 如果sr不为空
- replacement设置为sr
- 否则replacement设置为p
-
如果pl不为空
- replacement设置为pl
-
如果pr不为空
- replacement设置为pr
-
否则replacement设置为p
-
如果replacement 不等p,开始移除p节点,图示:
- replacement的父节点指向p的父节点pp
- 如果pp为空(根节点)
- root指向replacement
- 否则判断p是否pp的左节点
- pp的左节点指向replacement
- 否则pp的右节点指向replacement
- p左节点、右节点、父节点置空,p节点移除完毕
-
如果p为红色,则不需要调整;如果p为黑色,开始运行调整程序,具体步骤见下面
-
如果replacement 等于p,红黑树已经调整完成,开始移除p操作
- pp为p的父节点
- p的父节点置空
- 如果pp不为空
- 如果p是pp的左节点
- pp的左节点置空
- 否则pp的右节点置空
- 如果p是pp的左节点
-
红黑树元素个数减1 到此红黑树删除完毕
2.2、调整
以左子树为例,由之前的步骤得知,左侧现在是少1个黑色的节点的,那么为了调整符合性质5,需要向右子树借一个黑色节点。
根据上述程序,设置要调整的目标节点x,具体步骤如下:
- 循环开始
- 如果x为空或者x是根节点,结束
- 如果x的父节点xp为空(根节点)
- x设为黑色,结束
- 如果x颜色为红色(没有影响,查看红黑树性质)
- x设为黑色,结束
- 如果xp的左节点xpl等于x
-
如果xp的右节点xpr不为空且为红色,图示:
- xpr颜色设置为红色,xp颜色设置为红色
- 对xp节点左旋
-
如果xpr 为空
- x设置为xp
-
否则,xpr不为空且为黑色
- sl为xpr的左节点,sr为xpr的右节点
- 如果sl或者sr都不为红色
- xpr设置为红色
- x设置为xp
- 否则-sl或者sr至少有一个为红色(非空)
-
如果sr为空或者黑色,那么sl一定为红色,图示:
- sl设置为黑色,xpr设置为红色
- 对xpr右旋
-
如果xpr不为空,则sr为红色
- xpr设置为xp的颜色
- xpr的右节点指向sr,如果不为空
- sr设置为黑色
-
如果xp不为空
- xp设置为黑色
- 对xp左旋
-
-
- 否则x为xp的右节点,不在分析,可结合上面左节点情况对照分析
- 重复选好,直至符合1或者2 结束循环(调整)
3、Java代码
- 删除代码
// 删除
public void removeRBTreeNode(K k) {
if (root == null) {
return;
}
RBNode<K, V> p = findNode(k);
if (p == null)
return;
RBNode<K,V> pl = p.left, pr = p.right, replacement;
if (pl != null && pr != null) {
RBNode<K,V> s = pr, sl;
while ((sl = s.left) != null) // find successor
s = sl;
boolean c = s.red; s.red = p.red; p.red = c; // swap colors
RBNode<K,V> sr = s.right;
RBNode<K,V> pp = p.parent;
if (s == pr) { // p was s's direct parent
p.parent = s;
s.right = p;
}
else {
RBNode<K,V> sp = s.parent;
if ((p.parent = sp) != null) {
if (s == sp.left)
sp.left = p;
else
sp.right = p;
}
if ((s.right = pr) != null)
pr.parent = s;
}
p.left = null;
if ((p.right = sr) != null)
sr.parent = p;
if ((s.left = pl) != null)
pl.parent = s;
if ((s.parent = pp) == null)
root = s;
else if (p == pp.left)
pp.left = s;
else
pp.right = s;
if (sr != null)
replacement = sr;
else
replacement = p;
}
else if (pl != null)
replacement = pl;
else if (pr != null)
replacement = pr;
else
replacement = p;
if (replacement != p) {
RBNode<K,V> 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;
}
RBNode<K,V> r = p.red ? root : balanceDeletion(root, replacement);
if (replacement == p) { // detach
RBNode<K,V> 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--;
}
- 调整代码
// 代码调整(删除)
static <K,V> RBNode<K,V> balanceDeletion(RBNode<K,V> root,
RBNode<K,V> x) {
for (RBNode<K,V> 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) {
if ((xpr = xp.right) != null && xpr.red) {
xpr.red = false;
xp.red = true;
root = rotateLeft(root, xp);
xpr = (xp = x.parent) == null ? null : xp.right;
}
if (xpr == null)
x = xp;
else {
RBNode<K,V> sl = xpr.left, sr = xpr.right;
if ((sr == null || !sr.red) &&
(sl == null || !sl.red)) {
xpr.red = true;
x = xp;
}
else {
if (sr == null || !sr.red) {
sl.red = false;
xpr.red = true;
root = rotateRight(root, xpr);
xpr = (xp = x.parent) == null ?
null : xp.right;
}
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 { // symmetric
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 {
RBNode<K,V> 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;
}
}
}
}
}
- 测试代码
public static void testRBTree() {
RBTree<Integer, String> rbTree = new RBTree<>();
rbTree.add(1, "aaa");
rbTree.add(23, "sdf");
rbTree.add(63, "434");
rbTree.add(634, "r4f");
rbTree.add(223, "2342");
rbTree.add(22, "fwff");
System.out.println(rbTree);
System.out.println(rbTree.size());
// System.out.println(rbTree.findNode(1));
rbTree.removeRBTreeNode(223);
System.out.println(rbTree);
System.out.println(rbTree.size());
}
// 测试结果
[(1,aaa,黑),(22,fwff,红),(23,sdf,黑),(63,434,红),(223,2342,黑),(634,r4f,红)]
6
[(1,aaa,黑),(22,fwff,红),(23,sdf,黑),(63,434,红),(634,r4f,黑)]
5
红黑树学到这里后 ,有了不少疑问,相关的思考和讨论,留到下一篇将,先到这里。
后记 :
欢迎交流,本人QQ:806797785
项目源代码地址:https://gitee.com/gaogzhen/algorithm.git