JAVA:红黑树详解
点击蓝色“程序猿DD”关注我
回复“资源”获取独家整理的学习资料!
本文转载自公众号:乱敲代码
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.黑父
如图所示,这种情况不会破坏红黑树的特性,即不需要任何处理
2.红父
当其父亲为红色时又会存在以下的情况
红叔
红叔的情况,其实相对来说比较简单的,如下图所示,只需要通过修改父、叔的颜色为黑色,祖的颜色为红色,而且回去递归的检查祖节点即可黑叔 黑叔的情况有如下几种,这几种情况下是不能够通过修改颜色达到平衡的效果,因此会通过旋转的操作,红黑树种有两种旋转操作,左旋和右旋(现在存在的疑问,什么时候使用到左旋,什么时候使用到右旋)
Case 1:[先右旋,在改变颜色(根节点必须为黑色,其两个子节点为红色,叔节点不用改变)],如下图所示,注意省略黑哨兵节点
Case 2:[先左旋变成Case1中的情况,再右旋,最后改变颜色(根节点必须为黑色,其两个子节点为红色,叔节点不用改变)],如下图所示,注意省略黑哨兵节点
Case 3:[先左旋,最后改变颜色(根节点必须为黑色,其两个子节点为红色,叔节点不用改变)],如下图所示,注意省略黑哨兵节点
Case 4:[先右旋变成Case 3的情况,再左旋,最后改变颜色(根节点必须为黑色,其两个子节点为红色,叔节点不用改变)],如下图所示,注意省略黑哨兵节点
以上就是红黑树新增节点所有可能的操作,下面会介绍红黑树中的删除操作
2.1.2 删除操作 删除操作相比于插入操作情况更加复杂,删除一个节点可以大致分为三种情况:
1.删除的节点没有孩子节点,即当前节点为叶子节点,这种可以直接删除
2.删除的节点有一个孩子节点,这种需要删除当前节点,并使用其孩子节点顶替上来
3.删除的节点有两个孩子节点,这种需要先找到其后继节点(树中大于节点的最小的元素);然后将其后继节点的内容复制到该节点上,其后继节点就相当于该节点的替身, 需要注意的是其后继节点一定不会有两个孩子节点(这点应该很好理解,如果后继节点有左孩子节点,那么当前的后继节点肯定不是最小的,说明后继节点只能存在没有孩子节点或者只有一个右孩子节点),即这样就将问题转换成为1,2中的方式。
在讲述修复操作之前,首先需要明白几点,
1.对于红黑树而言,单支节点的情况只有如下图所示的一种情况,即为当前节点为黑色,其孩子节点为红色,(1.假设当前节点为红色,其两个孩子节点必须为黑色,2.若有孙子节点,则必为黑色,导致黑子数量不等,而红黑树不平衡)
2.由于红黑树是特殊的二叉查找树,它的删除和二叉查找树类型,真正的删除点即为删除点A的中序遍历的后继(前继也可以),通过红黑树的特性可知这个后继必然最多只能有一个孩子,其这个孩子节点必然是右孩子节点,从而为单支情况(即这个后继节点只能有一个红色孩子或没有孩子)
下面将详细介绍,在执行删除节点操作之后,将通过修复操作使得红黑树达到平衡的情况。
Case 1:被删除的节点为红色,则这节点必定为叶子节点(首先这里的被删除的节点指的是真正删除的节点,通过上文得知的真正删除的节点要么是节点本身,要么是其后继节点,若是节点本身则必须为叶子节点,不为叶子节点的话其会有左右孩子,则真正删除的是其右孩子树上的最小值,若是后继节点,也必须为叶子节点,若不是则其也会有左右孩子,从而和2中相违背),这种情况下删除红色叶节点就可以了,不用进行其他的操作了。
Case 2:被删除的节点是黑色,其子节点是红色,将其子节点顶替上来并改变其颜色为黑色,如下图所示
Case 3:被删除的节点是黑色,其子节点也是黑色,将其子节点顶替上来,变成了双黑的问题,此时有以下情况
Case 1:新节点的兄弟节点为红色,此时若新节点在左边则做左旋操作,否则做右旋操作,之后再将其父节点颜色改变为红色,兄弟节点
从图中可以看出,操作之后红黑树并未达到平衡状态,而是变成的黑兄的情况
Case 2:新节点的兄弟节点为黑色,此时可能有如下情况
红父二黑侄:将父节点变成黑色,兄弟节点变成红色,新节点变成黑色即可,如下图所示
黑父二黑侄:将父节点变成新节点的颜色,新节点变成黑色,兄弟节点染成红色,还需要继续以父节点为判定点继续判断,如下图所示
红侄:情况一:新节点在右子树,红侄在兄弟节点左子树,此时的操作为右旋,并将兄弟节点变为父亲的颜色,父亲节点变为黑色,侄节点变为黑色,如下图所示
情况二:新节点在右子树,红侄在兄弟节点右子树,此时的操作为先左旋,后右旋并将侄节点变为父亲的颜色,父节点变为黑色,如下图所示
情况三:新节点在左子树,红侄在兄弟节点左子树,此时的操作为先右旋在左旋并将侄节点变为父亲的颜色,父亲节点变为黑色,如下图所示
情况四:新节点在右子树,红侄在兄弟节点右子树,此时的操作为左旋,并将兄弟节点变为父节点的颜色,父亲节点变为黑色,侄节点变为黑色,如下图所示
2.2 红黑树实现
如下是使用JAVA代码实现红黑树的过程,主要包括了插入、删除、左旋、右旋、遍历等操作
2.2.1 插入
private void insert(RBTreeNode<T> node){ int cmp; RBTreeNode<T> root = this.rootNode; RBTreeNode<T> 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);
}
private void insertFixUp(RBTreeNode<T> node){ RBTreeNode<T> parent,gparent; while( ((parent = getParent(node)) != null) && isRed(parent)){ gparent = getParent(parent); if(parent == gparent.left){ RBTreeNode<T> uncle = gparent.right; if ((null != uncle) && isRed(uncle)){ setColorBlack(uncle); setColorBlack(parent); setColorRed(gparent); node = gparent; continue; } if (parent.right == node){ RBTreeNode<T> tmp; leftRotate(parent); tmp = parent; parent = node; node = tmp; } setColorBlack(parent); setColorRed(gparent); rightRotate(gparent); } else { RBTreeNode<T> uncle = gparent.left; if ((null != uncle) && isRed(uncle)){ setColorBlack(uncle); setColorBlack(parent); setColorRed(gparent); node = gparent; continue; } if (parent.left == node){ RBTreeNode<T> 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<T> node){ RBTreeNode<T> child,parent; boolean color; if ((null != node.left) && (null != node.right)){ RBTreeNode<T> replace = node; replace = replace.right; while(null != replace.left){ replace = replace.left; } if (null != getParent(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;
}
private void removeFixUp(RBTreeNode<T> node, RBTreeNode<T> parent){ RBTreeNode<T> other; while ((null == node || isBlack(node)) && (node != this.rootNode) ){ if (node == parent.left){ other = parent.right; if (isRed(other)){ setColorBlack(other); setColorRed(parent); leftRotate(parent); other = parent.right; } if ((other.left == null || isBlack(other.left)) && (other.right == null || isBlack(other.right))){ setColorRed(other); node = parent; parent = getParent(node); } else { if (null == other.right || isBlack(other.right)){ setColorBlack(other.left); setColorRed(other); rightRotate(other); other = parent.right; } 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);
}
删除节点主要分为几种情况去做对应的处理:
删除节点,按照如下三种情况去删除节点
真正删除的节点没有子节点
真正删除的节点有一个子节点
正在删除的节点有两个子节点
修复红黑树的特性,如代码中调用removeFixUp方法修复红黑树的特性。
3.总结
以上主要介绍了红黑树的一些特性,包括一些操作详细的解析了里面的过程,写的时间比较长,感觉确实比较难理清楚。后面会持续的理解更深入,若有存在问题的地方,请指正,另红黑树实现代码
原文:遇见技术 https://www.jianshu.com/p/4cd37000f4e3
推荐阅读
7月编程语言排行榜来了,为什么不同媒体报道的结果不一样?
装x撩m必备的16条Linux 命令,了解一下?
使用 Optional 摆脱 NullPointException 的折磨
史上最易懂的Kubernetes儿童插图指南
要想下班早?插件少不了!
社群讨论
如果你喜欢自己装机、刷系统、玩黑群,或对各类科技产品有浓厚的兴趣!那么赶紧加入,一起聊聊喜欢的东西,在大促的时候种种草。亦或是聊聊你想买的东西,让大伙给你拔拔草。添加微信:zyc_enjoy,根据指引,加入讨论群!
来星球聊聊技术人的斜杠生活
点一点“阅读原文”小惊喜在等你
JAVA:红黑树详解相关推荐
- 《算法导论》红黑树详解(一):概念
在学习红黑树之前,读者应先掌握二叉查找树的相关知识.学习红黑树或者二叉查找树,推荐大家看<算法导论>.<算法导论>原书第3版 高清PDF 带详细书签目录下载 密码:acis & ...
- 随处可见的红黑树详解
随处可见的红黑树详解 前言 为什么要有红黑树 二叉搜索树 平衡二叉搜索树 红黑树 红黑树的应用场景 红黑树的性质(重点) 红黑树的定义 红黑树的左旋与右旋 红黑树插入结点与插入维护红黑树的三种情况 插 ...
- 红黑树详解(一)红黑树的介绍和操作
红黑树详解(一)红黑树的介绍和操作 摘要: 在很多源码涉及到大量数据处理的时候,通常都是用红黑树这一数据结构.红黑树是一种自平衡的二叉查找树,它能在进行插入和删除操作时通过特定操作保持二叉查找树的平衡 ...
- 红黑树详解(二)红黑树的插入(附动图和案例)
红黑树详解(二)红黑树的插入(附动图和案例) 摘要: 在很多源码涉及到大量数据处理的时候,通常都是用红黑树这一数据结构.红黑树是一种自平衡的二叉查找树,它能在进行插入和删除操作时通过特定操作保持二叉查 ...
- java红黑树_JAVA学习-红黑树详解
1.定义 红黑树是特殊的二叉查找树,又名R-B树(RED-BLACK-TREE),由于红黑树是特殊的二叉查找树,即红黑树具有了二叉查找树的特性,而且红黑树还具有以下特性: 1.每个节点要么是黑色要么是 ...
- 红黑树详解及其模板类实现
一.历史 1972年,Rudolf Bayer发明了一种数据结构,这是一种特殊的4阶B树.这些树维护从根到叶的所有路径保持相同数量的节点,从而创建完美平衡的树.但是,它们不是二叉搜索树.Bayer在他 ...
- 红黑树详解,对插入旋转独到理解
一.红黑树的简介 R-B Tree,全称是Red-Black Tree,又称为"红黑树",它一种特殊的二叉查找树.红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑 ...
- redis为什么采用跳表而不是红黑树详解
今天早上看到这样redis的面试题:redis为什么采用跳表而不是红黑树?? 面试题答案: 在做范围查找的时候,平衡树比skiplist操作要复杂.在平衡树上,我们找到指定范围的小值之后,还需要以中序 ...
- 红黑树详解三:红黑树的删除
系列文章目录 文章目录 系列文章目录 1.删除 2.红黑树的平衡 2.1.N为根节点 2.2.兄弟为黑色节点 2.2.1.兄弟子节点全黑 2.2.1.1.父亲为红色节点 2.2.1.2.父亲为黑色节点 ...
最新文章
- “算法不行,干啥都不行!”面试官:面试基本都会考这点!
- windows10 删除文件 的权限才能对此文件夹进行更改 解决办法
- 编程软件python图片-python Plotly绘图工具的简单使用
- html语言填充没有只有描边,HTML5 Canvas笔记——交互绘制文本(描边、填充、阴影、渐变填充、图案填充、文本的属性设置)...
- “老师,我写着写着就 强制交卷了……”
- Tomcat 申请证书配置https
- java 钩子 64位 操作系统_Java与系统钩子
- 几种常见排序算法的时间复杂度和简单描述
- 在linux服务器上安装jdk
- python实现3d人物建模_很强!用Python实现3D建模!
- Windows组策略禁止广告弹窗
- 中国脑计划颠覆性创新之路二,欧美脑计划存在重大缺陷
- python有道云笔记_Python自动同步有道云笔记到Hexo
- 引入思考的电影电视动漫
- 天呐!java兼职接单
- MT4软件IOS版如何下载
- TypeError: Object of type 'datetime' is not JSON serializable
- performance性能
- 多维度积分管理系统java_某店POS积分管理系统JAVA088
- 宏文件下载_SolidWorks模型英文特征改中文名字方法分享SolidWorks宏文件 [
热门文章
- java 框架 Dao层 Mapper层 controller层 service层 model层 entity层 简介
- linux centos7 createrepo 创建本地 yum 仓库
- linux ptrace反调试之抢占ptrace
- linux Rootkit:x86与ARM的内联内核函数Hooking
- CentOS6安装devtoolset(使用高版本gcc)GCC 4.8 GCC 4.9 GCC 5.2
- linux下 zip解压 tar解压 gz解压 bz2等各种解压文件命令
- 通过 SHGetSpecialFolderLocation、SHGetPathFromIDList 函数获取常用路径
- Shell编程基入门
- php单例模式的实例,PHP的单例模式的一个实例_php
- Android消息广播的使用