1.定义

红黑树是特殊的二叉查找树,又名R-B树(RED-BLACK-TREE),由于红黑树是特殊的二叉查找树,即红黑树具有了二叉查找树的特性,而且红黑树还具有以下特性:

1.每个节点要么是黑色要么是红色

2.根节点是黑色

3.每个叶子节点是黑色,并且为空节点(还有另外一种说法就是,每个叶子结点都带有两个空的黑色结点(被称为黑哨兵),如果一个结点n的只有一个左孩子,那么n的右孩子是一个黑哨兵;如果结点n只有一个右孩子,那么n的左孩子是一个黑哨兵。)

4.如果一个节点是红色,则它的子节点必须是黑色

5.从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。

有几点需要注意的是:

1.特性3中指定红黑树的每个叶子节点都是空节点,但是在Java实现中红黑树将使用null代表空节点,因此遍历红黑树时看不到黑色的叶子节点,反而见到的叶子节点是红色的

2.特性4保证了从根节点到叶子节点的最长路径的长度不会超过任何其他路径的两倍,例如黑色高度为3的红黑树,其最短路径(路径指的是根节点到叶子节点)是2(黑节点-黑节点-黑节点),其最长路径为4(黑节点-红节点-黑节点-红节点-黑节点)。

2.实践

2.1 红黑树操作

2.1.1 插入操作

首先红黑树在插入节点的时,我们设定插入节点的颜色为红色,如果插入的是黑色节点,必然会违背特性5,即改变了红黑树的黑高度,如下插入红色结点又存在着几种情况:

1.黑父

如图所示,这种情况不会破坏红黑树的特性,即不需要任何处理

image

2.红父

当其父亲为红色时又会存在以下的情况

红叔

红叔的情况,其实相对来说比较简单的,如下图所示,只需要通过修改父、叔的颜色为黑色,祖的颜色为红色,而且回去递归的检查祖节点即可

image

黑叔

黑叔的情况有如下几种,这几种情况下是不能够通过修改颜色达到平衡的效果,因此会通过旋转的操作,红黑树种有两种旋转操作,左旋和右旋(现在存在的疑问,什么时候使用到左旋,什么时候使用到右旋)

Case 1:[先右旋,在改变颜色(根节点必须为黑色,其两个子节点为红色,叔节点不用改变)],如下图所示,注意省略黑哨兵节点

image

Case 2:[先左旋变成Case1中的情况,再右旋,最后改变颜色(根节点必须为黑色,其两个子节点为红色,叔节点不用改变)],如下图所示,注意省略黑哨兵节点

image

Case 3:[先左旋,最后改变颜色(根节点必须为黑色,其两个子节点为红色,叔节点不用改变)],如下图所示,注意省略黑哨兵节点

image

Case 4:[先右旋变成Case 3的情况,再左旋,最后改变颜色(根节点必须为黑色,其两个子节点为红色,叔节点不用改变)],如下图所示,注意省略黑哨兵节点

image

以上就是红黑树新增节点所有可能的操作,下面会介绍红黑树中的删除操作

2.1.2 删除操作

删除操作相比于插入操作情况更加复杂,删除一个节点可以大致分为三种情况:

1.删除的节点没有孩子节点,即当前节点为叶子节点,这种可以直接删除

2.删除的节点有一个孩子节点,这种需要删除当前节点,并使用其孩子节点顶替上来

3.删除的节点有两个孩子节点,这种需要先找到其后继节点(树中大于节点的最小的元素);然后将其后继节点的内容复制到该节点上,其后继节点就相当于该节点的替身, 需要注意的是其后继节点一定不会有两个孩子节点(这点应该很好理解,如果后继节点有左孩子节点,那么当前的后继节点肯定不是最小的,说明后继节点只能存在没有孩子节点或者只有一个右孩子节点),即这样就将问题转换成为1,2中的方式。

在讲述修复操作之前,首先需要明白几点,

1.对于红黑树而言,单支节点的情况只有如下图所示的一种情况,即为当前节点为黑色,其孩子节点为红色,(1.假设当前节点为红色,其两个孩子节点必须为黑色,2.若有孙子节点,则必为黑色,导致黑子数量不等,而红黑树不平衡)

image

2.由于红黑树是特殊的二叉查找树,它的删除和二叉查找树类型,真正的删除点即为删除点A的中序遍历的后继(前继也可以),通过红黑树的特性可知这个后继必然最多只能有一个孩子,其这个孩子节点必然是右孩子节点,从而为单支情况(即这个后继节点只能有一个红色孩子或没有孩子)

下面将详细介绍,在执行删除节点操作之后,将通过修复操作使得红黑树达到平衡的情况。

Case 1:被删除的节点为红色,则这节点必定为叶子节点(首先这里的被删除的节点指的是真正删除的节点,通过上文得知的真正删除的节点要么是节点本身,要么是其后继节点,若是节点本身则必须为叶子节点,不为叶子节点的话其会有左右孩子,则真正删除的是其右孩子树上的最小值,若是后继节点,也必须为叶子节点,若不是则其也会有左右孩子,从而和2中相违背),这种情况下删除红色叶节点就可以了,不用进行其他的操作了。

image

Case 2:被删除的节点是黑色,其子节点是红色,将其子节点顶替上来并改变其颜色为黑色,如下图所示

image

Case 3:被删除的节点是黑色,其子节点也是黑色,将其子节点顶替上来,变成了双黑的问题,此时有以下情况

Case 1:新节点的兄弟节点为红色,此时若新节点在左边则做左旋操作,否则做右旋操作,之后再将其父节点颜色改变为红色,兄弟节点

image

image

从图中可以看出,操作之后红黑树并未达到平衡状态,而是变成的黑兄的情况

Case 2:新节点的兄弟节点为黑色,此时可能有如下情况

红父二黑侄:将父节点变成黑色,兄弟节点变成红色,新节点变成黑色即可,如下图所示

image

image

黑父二黑侄:将父节点变成新节点的颜色,新节点变成黑色,兄弟节点染成红色,还需要继续以父节点为判定点继续判断,如下图所示

image

image

红侄:

情况一:新节点在右子树,红侄在兄弟节点左子树,此时的操作为右旋,并将兄弟节点变为父亲的颜色,父亲节点变为黑色,侄节点变为黑色,如下图所示

image

情况二:新节点在右子树,红侄在兄弟节点右子树,此时的操作为先左旋,后右旋并将侄节点变为父亲的颜色,父节点变为黑色,如下图所示

image

情况三:新节点在左子树,红侄在兄弟节点左子树,此时的操作为先右旋在左旋并将侄节点变为父亲的颜色,父亲节点变为黑色,如下图所示

image

情况四:新节点在右子树,红侄在兄弟节点右子树,此时的操作为左旋,并将兄弟节点变为父节点的颜色,父亲节点变为黑色,侄节点变为黑色,如下图所示

image

2.2 红黑树实现

如下是使用JAVA代码实现红黑树的过程,主要包括了插入、删除、左旋、右旋、遍历等操作

2.2.1 插入

/* 插入一个节点

* @param node

*/

private void insert(RBTreeNode node){

int cmp;

RBTreeNode root = this.rootNode;

RBTreeNode parent = null;

//定位节点添加到哪个父节点下

while(null != root){

parent = root;

cmp = node.key.compareTo(root.key);

if (cmp < 0){

root = root.left;

} else {

root = root.right;

}

}

node.parent = parent;

//表示当前没一个节点,那么就当新增的节点为根节点

if (null == parent){

this.rootNode = node;

} else {

//找出在当前父节点下新增节点的位置

cmp = node.key.compareTo(parent.key);

if (cmp < 0){

parent.left = node;

} else {

parent.right = node;

}

}

//设置插入节点的颜色为红色

node.color = COLOR_RED;

//修正为红黑树

insertFixUp(node);

}

/**

* 红黑树插入修正

* @param node

*/

private void insertFixUp(RBTreeNode node){

RBTreeNode parent,gparent;

//节点的父节点存在并且为红色

while( ((parent = getParent(node)) != null) && isRed(parent)){

gparent = getParent(parent);

//如果其祖父节点是空怎么处理

// 若父节点是祖父节点的左孩子

if(parent == gparent.left){

RBTreeNode uncle = gparent.right;

if ((null != uncle) && isRed(uncle)){

setColorBlack(uncle);

setColorBlack(parent);

setColorRed(gparent);

node = gparent;

continue;

}

if (parent.right == node){

RBTreeNode tmp;

leftRotate(parent);

tmp = parent;

parent = node;

node = tmp;

}

setColorBlack(parent);

setColorRed(gparent);

rightRotate(gparent);

} else {

RBTreeNode uncle = gparent.left;

if ((null != uncle) && isRed(uncle)){

setColorBlack(uncle);

setColorBlack(parent);

setColorRed(gparent);

node = gparent;

continue;

}

if (parent.left == node){

RBTreeNode tmp;

rightRotate(parent);

tmp = parent;

parent = node;

node = tmp;

}

setColorBlack(parent);

setColorRed(gparent);

leftRotate(gparent);

}

}

setColorBlack(this.rootNode);

}

插入节点的操作主要分为以下几步:

1.定位:即遍历整理红黑树,确定添加的位置,如上代码中insert方法中就是在找到添加的位置

2.修复:这也就是前面介绍的,添加元素后可能会使得红黑树不在满足其特性,这时候需要通过变色、旋转来调整红黑树,也就是如上代码中insertFixUp方法

2.2.2 删除节点

如下为删除节点的代码

private void remove(RBTreeNode node){

RBTreeNode child,parent;

boolean color;

//被删除节点左右孩子都不为空的情况

if ((null != node.left) && (null != node.right)){

//获取到被删除节点的后继节点

RBTreeNode replace = node;

replace = replace.right;

while(null != replace.left){

replace = replace.left;

}

//node节点不是根节点

if (null != getParent(node)){

//node是左节点

if (getParent(node).left == node){

getParent(node).left = replace;

} else {

getParent(node).right = replace;

}

} else {

this.rootNode = replace;

}

child = replace.right;

parent = getParent(replace);

color = getColor(replace);

if (parent == node){

parent = replace;

} else {

if (null != child){

setParent(child,parent);

}

parent.left = child;

replace.right = node.right;

setParent(node.right, replace);

}

replace.parent = node.parent;

replace.color = node.color;

replace.left = node.left;

node.left.parent = replace;

if (color == COLOR_BLACK){

removeFixUp(child,parent);

}

node = null;

return;

}

if (null != node.left){

child = node.left;

} else {

child = node.right;

}

parent = node.parent;

color = node.color;

if (null != child){

child.parent = parent;

}

if (null != parent){

if (parent.left == node){

parent.left = child;

} else {

parent.right = child;

}

} else {

this.rootNode = child;

}

if (color == COLOR_BLACK){

removeFixUp(child, parent);

}

node = null;

}

/**

* 删除修复

* @param node

* @param parent

*/

private void removeFixUp(RBTreeNode node, RBTreeNode parent){

RBTreeNode other;

//node不为空且为黑色,并且不为根节点

while ((null == node || isBlack(node)) && (node != this.rootNode) ){

//node是父节点的左孩子

if (node == parent.left){

//获取到其右孩子

other = parent.right;

//node节点的兄弟节点是红色

if (isRed(other)){

setColorBlack(other);

setColorRed(parent);

leftRotate(parent);

other = parent.right;

}

//node节点的兄弟节点是黑色,且兄弟节点的两个孩子节点也是黑色

if ((other.left == null || isBlack(other.left)) &&

(other.right == null || isBlack(other.right))){

setColorRed(other);

node = parent;

parent = getParent(node);

} else {

//node节点的兄弟节点是黑色,且兄弟节点的右孩子是红色

if (null == other.right || isBlack(other.right)){

setColorBlack(other.left);

setColorRed(other);

rightRotate(other);

other = parent.right;

}

//node节点的兄弟节点是黑色,且兄弟节点的右孩子是红色,左孩子是任意颜色

setColor(other, getColor(parent));

setColorBlack(parent);

setColorBlack(other.right);

leftRotate(parent);

node = this.rootNode;

break;

}

} else {

other = parent.left;

if (isRed(other)){

setColorBlack(other);

setColorRed(parent);

rightRotate(parent);

other = parent.left;

}

if ((null == other.left || isBlack(other.left)) &&

(null == other.right || isBlack(other.right))){

setColorRed(other);

node = parent;

parent = getParent(node);

} else {

if (null == other.left || isBlack(other.left)){

setColorBlack(other.right);

setColorRed(other);

leftRotate(other);

other = parent.left;

}

setColor(other,getColor(parent));

setColorBlack(parent);

setColorBlack(other.left);

rightRotate(parent);

node = this.rootNode;

break;

}

}

}

if (node!=null)

setColorBlack(node);

}

删除节点主要分为几种情况去做对应的处理:

1.删除节点,按照如下三种情况去删除节点

1.真正删除的节点没有子节点

2.真正删除的节点有一个子节点

3.正在删除的节点有两个子节点

2.修复红黑树的特性,如代码中调用removeFixUp方法修复红黑树的特性。

3.总结

以上主要介绍了红黑树的一些特性,包括一些操作详细的解析了里面的过程,写的时间比较长,感觉确实比较难理清楚。后面会持续的理解更深入,若有存在问题的地方,请指正,另红黑树实现代码

本人主要的参考链接如下:

1.红黑树(五)之 Java的实现

2.通过分析 JDK 源代码研究 TreeMap 红黑树算法实现

3.红黑树

4.(图解)红黑树的插入和删除

5.红黑树深入剖析及Java实现

java红黑树_JAVA学习-红黑树详解相关推荐

  1. java jtable组件_java中jtable组件详解实例

    java中jtable组件详解实例 java 表格控件 JTable 常用操作详解 JTable 是 Swing 编程中很常用的控件,这里总结了一些常用方法以备查阅.欢迎补充,转载请注明作者与出处.一 ...

  2. java aqs原理_Java并发之AQS详解

    一.概述 谈到并发,不得不谈ReentrantLock:而谈到ReentrantLock,不得不谈AbstractQueuedSynchronized(AQS)! 类如其名,抽象的队列式的同步器,AQ ...

  3. java多线程代码_java多线程实例代码详解

    原文:http://blog.csdn.net/paranoidyang/article/details/70184523 作者:Paranoidyang 线程与进程的区别 (1)程序是一段静态的代码 ...

  4. java 枚举 方法_Java枚举使用方法详解

    在实际编程中,往往存在着这样的"数据集",它们的数值在程序中是稳定的,而且"数据集"中的元素是有限的. 例如星期一到星期日七个数据元素组成了一周的"数 ...

  5. java异常例子_java 异常的实例详解

    java 异常的实例详解 1.异常的定义:程序在运行时出现不正常情况. 异常的划分: Error:严重的问题,对于error一般不编写针对性的代码对其进行处理. Exception:非严重的问题,对于 ...

  6. java list用法_java list的用法详解

    java list的用法详解 java中可变数组的原理就是不断的创建新的数组,将原数组加到新的数组中.以下是百分网小编搜索整理的关于java list的用法详解,需要的朋友可以参考一下!想了解更多相关 ...

  7. java 自定义正则表达式_java中正则表达式实例详解

    Java中正则表达式运用实例(参看java中正则表达式运用详解): 测试代码 package test; /** * 在String的matches()方法,split()方法中使用正则表达式. * ...

  8. java file 实例_Java File类的详解及简单实例

    Java File类的详解及简单实例 1. File():构造函数,一般是依据文件所在的指定位置来创建文件对象. CanWrite():返回文件是否可写. CanRead():返回文件是否可读. Co ...

  9. java 返回值_Java方法返回值详解

    Java方法返回值详解 每个方法都是为了完成某个特定的功能,例如:登录功能.求和功能等,既然是功能,那么当这个功能完成之后,大多数情况下都会有一个结果的,比如,登录成功了或者失败了(true/fals ...

最新文章

  1. html标准模式与混杂模式,关于Doctype、严格模式与混杂模式
  2. 【EasyUI tree】Python 异步菜单树的实现
  3. 45 WM配置-作业-库存盘点-清除差异(库存管理接口)
  4. 利用ipsec配置tcp/ip用于安全连接
  5. mysql如何查询不等于_mysql查询不等于
  6. 20150401--Dede+搜索页面,评论
  7. Codeforces Round #318 [RussianCodeCup Thanks-Round] (Div. 1) B. Bear and Blocks 水题
  8. java spark yarn_java – 是否可以从代码中运行spark yarn cluster?
  9. 根据消费定额生成菜单的算法(原创)
  10. 微信公众平台版面设计需要服务器,谈谈微信公众号如何快速在线排版设计好看又实用的方法...
  11. 分析技术|3.2透射电子显微技术TEM
  12. camera基础概念之等效焦距 视场角的计算
  13. 使用ftp传输文件到arm板
  14. 京东静态网页设计案例(1)
  15. gromacs 中关于二级结构分析
  16. 南京美食指南(完美篇)
  17. c语言实验内容.doc答案,C语言程序设计实验内容与答案.doc
  18. 马宁伟-20年工作经验谈-4-男怕入错行
  19. 喜马拉雅xm文件转换为mp3?
  20. 10个免费的响应式布局HTML5+CSS3模板

热门文章

  1. 我是LinkedIn的SRE,我把LinkedIn搞挂了
  2. Java Swing:JPanel添加边框
  3. Windows安装Zookeeper和Dubbo(单机版本)
  4. java算法 第七届 蓝桥杯B组(题+答案) 3.凑算式
  5. jquery给元素添加样式表的方法
  6. JQuery 表单验证--jquery validation
  7. Struts2、Hibernate、Spring整合所需要的jar包
  8. linux fifo 视频,Linux FIFO学习
  9. pythonfor循环嵌套_python-嵌套和for循环中的字典和列表中的项...
  10. 体质测试java代码_求java代码,要求做一个测试类,实现以下功能之一。最好三个功能都有。...