删除操作需要处理的情况:

1.删除的是红色节点,则删除节点并不影响红黑树的树高,无需处理。

2.删除的是黑色节点,则删除后,删除节点所在子树的黑高BH将减少1,需要进行调整。

节点标记:

  • 正在处理的节点x
  • 父节点p
  • 兄弟节点s(sibling)
  • 左侄子LN(Left Nephew)
  • 右侄子RN(Right Nephew)

无需调整的情况(向上回溯时)

  • 当前x为根节点,无论root为什么颜色,都将root染黑,rootOver。
  • 当前x为红色,将其染黑,redOver。(增加所在子树黑高度,从而满足红黑树条件)。

删除左孩子情况:

  1. s为红; s染红,p染黑,左旋p。
  2. s为黑,LN与RN为黑; s染红,x回溯至p。
  3. s为黑,LN为红,RN为黑; LN染黑,s染红,右旋p。
  4. s为黑,LN随意,RN为红; s变为p的颜色,p和RN染黑,左旋p。

删除右孩子的情况和删除左孩子的情况相对称。

所有的情况为:



以删除左孩子为例,如下图,x为删除节点的后继节点。则p的左子树黑高度比右子树少1。若经过调整后,左右子树黑高恢复相等,则完成修复,否则,会转换为另一种情况,并按次方法继续进行修正。

修正后情况的转换

情况转换的的原因:经调整后,左右子树的黑高度仍不相等,需要针对新的情况继续进行调整。

若修正后的左右子树黑高相同,则修正结束,无需情况转换。

转换如下:

Case 1 ==>> Case 2-2,Case 3,Case 4-1或 Case 4-2 不会引起黑高变化

Case 2-1 ==>> all Cases 当p为root节点时,会使整个树的黑高度减少1(唯一减少树黑高的情况)。

Case 2-2 ==>> redOver 并结束调整。

Case 3 ==>> Case 4-1或 Case 4-2;

Case 4 ==>> rootOver;

根据JDK源代码,分析函数对各种情况的处理方法。

    private void fixAfterDeletion(TreeMap.Entry<K, V> var1) {
while(var1 != this.root && colorOf(var1)) {
//若当前节点为红色或者到达根节点,则修正结束。
TreeMap.Entry var2;
if (var1 == leftOf(parentOf(var1))) { //当前节点为左孩子
var2 = rightOf(parentOf(var1)); //兄弟节点s
if (!colorOf(var2)) { //s为红 [Case 1]
setColor(var2, true); //s染黑
setColor(parentOf(var1), false); //p染红
this.rotateLeft(parentOf(var1)); //左旋p
var2 = rightOf(parentOf(var1)); //更新兄弟s
} if (colorOf(leftOf(var2)) && colorOf(rightOf(var2))) {
//LN 和 RN都为黑[Case 2]
//若从[Case 1]转换而来,则p为红[Case 2-2]
//但[Case 2]两种情况的处理策略相同
setColor(var2, false); //s染红
var1 = parentOf(var1); //x向上回溯
} else { //LN 和 RN不全黑 [Case 3,4]
if (colorOf(rightOf(var2))) { //RN黑[Case 3]
setColor(leftOf(var2), true);//LN染黑
setColor(var2, false); //s染红
this.rotateRight(var2); //右旋s
var2 = rightOf(parentOf(var1));//更新s为LN
}
//经[Case 3]修正后转换为[Case 4]
//[Case 4]分为两种情况,但修正方法相同
setColor(var2, colorOf(parentOf(var1)));//将s染成p的颜色
setColor(parentOf(var1), true); //p染黑
setColor(rightOf(var2), true); //RN染黑
this.rotateLeft(parentOf(var1)); //左旋p
var1 = this.root; //x回溯至根节点
//至此修正已基本完成(还有可能对根节点染黑)
}
} else {
//当前节点为左孩子,对称情况
var2 = leftOf(parentOf(var1)); //兄弟节点s
if (!colorOf(var2)) { //兄弟节点为红[Case 1]
setColor(var2, true); //s染黑
setColor(parentOf(var1), false); //p染红
this.rotateRight(parentOf(var1)); //p右旋
var2 = leftOf(parentOf(var1)); //更新s为LN
} if (colorOf(rightOf(var2)) && colorOf(leftOf(var2))) {
//LN 和 RN都为黑[Case 2]
//若从[Case 1]转换而来,则p为红[Case 2-2]
//但[Case 2]两种情况的处理策略相同
setColor(var2, false); //s染红
var1 = parentOf(var1); //向上回溯
} else { //LN 和 RN不全黑 [Case 3,4]
if (colorOf(leftOf(var2))) { //LN黑,RN红[Case 3]
setColor(rightOf(var2), true);//RN染黑
setColor(var2, false); //s染红
this.rotateLeft(var2); //左旋s
var2 = leftOf(parentOf(var1));//更新s为LN
}
//经[Case 3]修正后转换为[Case 4]
//[Case 4]分为两种情况,但修正方法相同
setColor(var2, colorOf(parentOf(var1)));//s染成p的颜色
setColor(parentOf(var1), true); //p染黑
setColor(leftOf(var2), true); //LN染黑
this.rotateRight(parentOf(var1)); //右旋p
var1 = this.root; //x直接回溯到root
//至此修正已基本完成(还有可能对根节点染黑)
}
}
} //根红色节点染黑,或者将根节点染黑
setColor(var1, true);
}

总结:

 红黑树节点删除后的“双黑”现象修正,虽然情况复杂,情况间转换也复杂,但这些转换都是为了修正节点删除产生的叶节点黑深度不相等。而黑深度相等是红黑树的规定之一,红黑树的规定可以确保树中节点的高效插入,搜索和删除操作。这些努力都是为了实现数据结构的良好性能。

后记

 学习红黑树的知识,始于阅读《STL源码剖析》中的第五章:关联式容器。关联式容器的底层数据结构是二叉搜索树,红黑树作为一种高效且通用的平衡二叉搜索树,被作为STL关联数据类的底层数据结构。在继续阅读关联是容器的源代码前,需要对红黑树有一个初步的认识。而且数据结构是高效算法的基石,也是STL的基础。

 在学习红黑树的过程中,我看了网上的视频,作者是以JDK源代码进行讲解的。在阅读相关JDK源代码时,发现JDK源代码也很清晰明了,而且JDK里提供了丰富的容器类。相比之下,STL中提供的容器类就略显单一和老旧。但是阅读STL源码依然是学习程序语言,类层次结构设计,设计模式,数据结构和算法等知识的良好方法。而瞻仰JDK代码是今后的努力方向。

红黑树的删除操作---以JDK源码为例的更多相关文章

  1. 红黑树插入操作---以JDK 源码为例

    红黑树遵循的条件: 1.根节点为黑色. 2.外部节点(叶子节点)为黑色. 3.红色节点的孩子节点为黑色.(由此,红色节点的父节点也必为黑色) 4.从根节点到任一外部节点的路径上,黑节点的数量相同. 节 ...

  2. jdk1.8HashMap底层数据结构:散列表+链表+红黑树,jdk1.8HashMap数据结构图解+源码说明

    一.前言 本文由jdk1.8源码整理而得,附自制jdk1.8底层数据结构图,并截取部分源码加以说明结构关系. 二.jdk1.8 HashMap底层数据结构图 三.源码 1.散列表(Hash table ...

  3. JDK源码那些事儿之红黑树基础下篇

    说到HashMap,就一定要说到红黑树,红黑树作为一种自平衡二叉查找树,是一种用途较广的数据结构,在jdk1.8中使用红黑树提升HashMap的性能,今天就来说一说红黑树,上一讲已经给出插入平衡的调整 ...

  4. JDK源码分析之hashmap就这么简单理解

    一.HashMap概述 HashMap是基于哈希表的Map接口实现,此实现提供所有可选的映射操作,并允许使用null值和null键.HashMap与HashTable的作用大致相同,但是它不是线程安全 ...

  5. 【JDK】JDK源码分析-TreeMap(2)

    前文「JDK源码分析-TreeMap(1)」分析了 TreeMap 的一些方法,本文分析其中的增删方法.这也是红黑树插入和删除节点的操作,由于相对复杂,因此单独进行分析. 插入操作 该操作其实就是红黑 ...

  6. 红黑树的删除详解与思路分析——不同于教科书上的算法(dart语言实现)

    对于红黑树的删除,看了数据结构的书,也看了很多网上的讲解和实现,但都不满意.很多讲解都是囫囵吞枣,知其然,不知其所以然,讲的晦涩难懂. 红黑树是平衡二叉树的一种,其删除算法是比较复杂的,因为删除后还要 ...

  7. 【java基础之jdk源码】集合类

    最近在整理JAVA 基础知识,从jdk源码入手,今天就jdk中 java.util包下集合类进行理解 先看图 从类图结构可以了解 java.util包下的2个大类: 1.Collecton:可以理解为 ...

  8. 【JDK】JDK源码分析-LinkedHashMap

    概述 前文「JDK源码分析-HashMap(1)」分析了 HashMap 主要方法的实现原理(其他问题以后分析),本文分析下 LinkedHashMap. 先看一下 LinkedHashMap 的类继 ...

  9. JDK源码那些事儿之并发ConcurrentHashMap上篇

    前面已经说明了HashMap以及红黑树的一些基本知识,对JDK8的HashMap也有了一定的了解,本篇就开始看看并发包下的ConcurrentHashMap,说实话,还是比较复杂的,笔者在这里也不会过 ...

随机推荐

  1. vector存放结构体数据的2种方法

    如果要在Vector容器中存放结构体类型的变量,经常见到两种存放方式. 方式一:放入这个结构体类型变量的副本. 方式二:放入指向这个结构体类型变量的指针. 假设结构体类型变量是这样的, typedef ...

  2. 强大的VS插件CodeRush发布v19.1.4|支持Visual Studio 2019

    CodeRush是一个强大的Visual Studio .NET 插件,它利用整合技术,通过促进开发者和团队效率来提升开发者体验. [CodeRush for Visual Studio v19.1. ...

  3. 前端之CSS:属性操作2

    css样式之属性操作 一.文本属性 1.text-align:cnter 文本居中2.line heigth 垂直居中 :行高,和高度对应3.设置图片与文本的距离:vertical-align4.te ...

  4. Flask之 请求,应用 上下文源码解析

    什么是上下文? 每一段程序都有很多外部变量.只有像Add这种简单的函数才是没有外部变量的.一旦你的一段程序有了外部变量,这段程序就不完整,不能独立运行.你为了使他们运行,就要给所有的外部变量一个一个写 ...

  5. 【UOJ#400】暴力写挂

    题目链接 题意 两棵树 , 求出下面式子的最大值. \[dep[u]+dep[v]-dep[LCA(u,v)]-dep'[LCA'(u,v)]\] Sol 边分治. 与第一棵树有关的信息比较多,所以对 ...

  6. springboot2.0+mysql整合mybatis,发现查询出来的时间比数据库datetime值快了8小时

    参考:https://blog.csdn.net/lx12345_/article/details/82020858 修改后查询数据正常

  7. React Native 之TouchableOpacity组件

    使用TouchableOpacity组件 实现单击事件只需要声明onPress属性即可,其他同理,实现onPressIn,onPressOut,onLongPress constructor(prop ...

  8. spring mvc 绑定参数据默认值,是否必传,(RequestParam(value="id",defaultValue="1",required=true) )

    @RequestMapping(value = "/detail", method = RequestMethod.GET) public String newDetail(@Re ...

  9. [HG]AK 题解

    前言 什么鬼畜玩意,扶我起来,我要用__int128,这辈子都不珂能用龟速乘的... 真香. 题解 我们知道这个模数是个神奇的东西 \(2305843008676823040 = 2^{29} \ti ...

  10. Go简易分布式对象存储 合并文件的所有分块为一个文件

    项目 项目地址: https://github.com/Draymonders/cloud 欢迎大家Watch or Star 缘由 由于项目中对大文件进行5MB为一个分块上传(多线程,提升上传效率) ...