【Java数据结构与算法】第十四章 红黑树
第十四章 红黑树
文章目录
- 第十四章 红黑树
- 一、红黑树
- 1.介绍
- 2.插入结点
- 3.删除结点
- 4.与平衡二叉树的对比
一、红黑树
1.介绍
红黑树(Red Black Tree)是平衡二叉树的其中一种实现方式,解决了二叉排序树多次插入新结点导致的不平衡问题
除了二叉排序树的基本特性外,它还具有下列附加规则:
- 结点是红色或黑色
- 根结点是黑色
- 每个叶子结点都是黑色的空结点(NIL 结点)
- 每个红色结点的两个子结点都是黑色(从每个叶子到根的所有路径上不能有两个连续的红色结点)
- 从任一结点到其每个叶子的所有路径都包含相同数目的黑色结点
- 平衡二叉树是严格平衡的二叉树,要求每个结点的左右子树高度差不超过 1。而红黑树则要宽松一些,只要求任何一条路径的长度不超过其他路径长度的 2 倍
当插入或删除结点的时候,红黑树的规则可能被打破。这时候就需要做出一些调整,从而继续维持我们的规则
向原红黑树插入值为 14 的新结点
由于父结点 15 是黑色结点,因此这种情况并不会破坏红黑树的规则,无需做任何调整
向原红黑树插入值为 21 的新结点
由于父结点 22 是红色结点,因此这种情况打破了红黑树的规则 4(每个红色结点的两个子结点都是黑色),必须进行调整,使之重新符合红黑树的规则
调整的方法有变色和旋转,和常规的平衡二叉树一样,旋转包含左旋转和右旋转
变色:
为了重新符合红黑树的规则,尝试把红色结点变为黑色,或者把黑色结点变为红色
下图所表示的是红黑树的一部分(子树),新插入的结点 Y 是红色结点,它的父结点 X 也是红色的,不符合规则 4,因此我们可以把结点 X 从红色变为黑色
但是,仅仅把一个结点变色,会导致相关路径凭空多出一个黑色结点,这样就打破了规则 5。因此,我们需要对其它结点做进一步的调整,后文会详细说明
2.插入结点
红黑树插入新结点的时候可以分为 5 种不同的局面,每一种局面有不同的调整方案
局面 1:新结点(A)位于树根,没有父结点
空心三角形代表结点下面的子树
这种局面,直接让新结点变色为黑色,规则 2 得到满足。同时,黑色的根结点使得每条路径上的黑色结点数目都增加了 1,所以并没有打破规则 5
局面 2:新结点(B)的父结点是黑色
这种局面,新插入的红色结点 B 并没有打破红黑树的规则,所以不需要做任何调整
局面 3:新结点(D)的父结点和叔叔结点都是红色
这种局面,两个红色结点 B 和 D 连续,违反了规则 4,因此我们先让结点 B 变为黑色
这样一来,结点 B 所在路径凭空多了一个黑色结点,打破了规则 5,因此我们让结点 A 变为红色:
这时候,结点 A 和 C 又成为了连续的红色结点,我们再让结点 C 变为黑色
经过上面的调整,这一局部重新符合了红黑树的规则
局面 4:新结点(D)的父结点是红色,叔叔结点是黑色或者没有叔叔,且新结点是父结点的右子结点,父结点(B)是祖父结点的左子结点
我们以结点 B 为轴,做一次左旋转,使得新结点 D 成为父结点,原来的父结点 B 称为 D 的左子结点
这样一来,进入了局面 5
局面 5:新结点(D)的父结点是红色, 叔叔结点是黑色或者没有叔叔,且新结点是父结点的左子结点,父结点(B)是祖父结点的左子结点
我们以结点 A 为轴,做一次右旋转,使得结点 B 成为祖父结点,结点 A 成为结点 B 的右子结点:
接下来,我们让结点 B 变为黑色,结点 A 变为红色
经过上面的调整,这一局部重新符合了红黑树的规则
如果局面 4 中的父结点(B)是右子结点,则成为了局面 5 的镜像,原本的右旋转改为左旋转
如果局面 5 中的父结点(B)是右子结点,则成为了局面 4 的镜像,原本的左旋转改为右旋转
我们不用刻意记住这些局面的处理方式,只需要有个大概了解即可
实例演示:
给定下面这棵红黑树,新插入的结点是 21
显然,新结点 21 和它的父结点 22 是连续的红色结点,违背了规则 4,我们应该如何调整呢?
让我们回顾一下刚才讲的 5 种局面,当前的情况符合局面 3:新结点的父结点和叔叔结点都是红色
于是我们经过三次变色,22 变为黑色,25 变为红色,27 变为黑色
经过上面的调整,以结点 25 为根的子树符合了红黑树规则,但结点 25 和结点 17 成为了连续的红色结点,违背了规则 4
于是,我们把结点 25 看作一个新结点,正好符合局面 5 的镜像:新结点的父结点是红色,叔叔结点是黑色或者没有叔叔,且新结点是父结点的右子结点,父结点是祖父结点的右子结点
于是我们以根结点 14 为轴进行左旋转,使得结点 17 成为了新的根结点
接下来,让结点 17 变为黑色,结点 13 变为红色
如此一来,我们的红黑树变得重新符合规则
3.删除结点
第一步:如果待删除结点有两个非空的子结点,转化成待删除结点只有一个子结点(或者没有子结点)的情况
上面的例子是一棵红黑树的局部,标数字的三角形代表任意形态的子树,假设结点 8 是待删除结点
根据上文讲解的二叉查找树的删除流程,由于结点 8 有两个子结点,我们选择仅大于 8 的结点 10 复制到 8 的位置,结点颜色变成待删除结点的颜色
接下来我们需要删除红色的结点 10
红色结点 10 能成为仅大于 8 的结点,必定没有左子结点,所以问题转换成了待删除结点只有一个右子结点(或没有子结点)的情况,接下来我们进入第二步
第二步:根据待删除结点和其唯一子结点的颜色,分情况处理
情况 1,自身是红色,子结点是黑色
这种情况最简单,按照二叉排序树的删除操作,删除结点 1 即可
情况 2,自身是黑色,子结点是红色:
这种情况也很简单,首先按照二叉查找树的删除操作,删除结点 1
此时,这条路径凭空减少了一个黑色结点,那么我们需要再把结点 2 变成黑色
情况 3,自身是黑色,子结点也是黑色,或者子结点是空叶子结点
这种情况最复杂,涉及到很多变化。首先我们还是按照二叉排序树的删除操作,删除结点 1
显然,这条路径上减少了一个黑色结点,而且结点 2 再怎么变色也解决不了
这时候我们进入第三步,专门解决父子双黑的情况
第三步:遇到双黑结点,在子结点顶替父结点之后,分成 6 种情况处理
情况 1,结点 2 是红黑树的根结点:
此时所有路径都减少了一个黑色结点,并未打破规则,不需要调整
情况 2,结点 2 的父亲、兄弟、侄子结点都是黑色
此时,我们直接把结点 2 的兄弟结点 B 改为红色
这样一来,原本结点 2 所在的路径少了一个黑色结点,现在结点 B 所在的路径也少了一个黑色结点,两边“扯平”了
可是,结点 A 以下的每一条路径都减少了一个黑色结点,与结点 A 之外的其它路径又造成了新的不平衡呢?
没关系,我们让结点 A 扮演原先结点 2 的角色,进行递归操作,重新判断各种情况
情况 3,结点2的兄弟结点是红色
首先以结点 2 的父结点 A 为轴,进行左旋转
然后将结点 A 变成红色、结点 B 变成黑色
这样的意义是什么呢?结点 2 所在的路径仍然少一个黑色结点呀?
别急,这样的变化有可能转换成情况 4、5、6 中的任意一种,在情况 4、5、6 当中会进一步解决
情况 4,结点 2 的父结点是红色,兄弟和侄子结点是黑色
这种情况,我们直接让结点 2 的父结点 A 变成黑色,兄弟结点 B 变成红色
这样一来,结点 2 的路径补充了黑色结点,而结点 B 的路径并没有减少黑色结点,重新符合了红黑树的规则
情况 5,结点 2 的父结点随意,兄弟结点 B 是黑色右子结点,左侄子结点是红色,右侄子结点是黑色
这种情况下,首先以结点 2 的兄弟结点 B 为轴进行右旋转
接下来结点 B 变为红色,结点 C 变为黑色
这样的变化转换成了情况 6
情况 6,结点 2 的父结点随意,兄弟结点 B 是黑色右子结点,右侄子结点是红色
首先以结点 2 的父结点 A 为轴左旋转
接下来让结点 A 和结点 B 的颜色交换,并且结点 D 变为黑色
经过结点 2 的路径由(随意+黑)变成了(随意+黑+黑),补充了一个黑色结点
经过结点 D 的路径由(随意+黑+红)变成了(随意+黑),黑色结点并没有减少
所以,这时候重新符合了红黑树的规则
实例演示:
给定下面这棵红黑树,待删除的是结点 17
第一步,由于结点 17 有两个子结点,子树当中仅大于 17 的结点是 25,所以把结点 25 复制到 17 位置,保持黑色
接下来,我们需要删除原本的结点 25
这个情况正好对应于第二步的情况三,即待删除结点是黑色,子结点是空叶子结点
于是我们删除框框中结点 25,进入第三步
此时,框框中的结点虽然是空叶子结点,但仍然可以用于判断局面,当前局面符合情况 5 的镜像
于是我们通过左旋和变色,把子树转换成情况 6 的镜像
再经过右旋和变色,子树最终成为了下面的样子
这样一来,整棵二叉树又重新符合了红黑树的规则
4.与平衡二叉树的对比
平衡二叉树是严格平衡的二叉树,要求每个结点的左右子树高度差不超过 1。而红黑树则要宽松一些,只要求任何一条路径的长度不超过其他路径长度的 2 倍
正因为这个差别,二叉平衡树的查找效率更高,但平衡调整的成本也更高,在需要频繁查找时,选用二叉平衡树更合适,在需要频繁插入删除时,选用红黑树更合适
【Java数据结构与算法】第十四章 红黑树相关推荐
- Java数据结构与算法(第四章栈和队列)
2019独角兽企业重金招聘Python工程师标准>>> 本章涉及的三种数据存储类型:栈.队列和优先级队列. 不同类型的结构 程序员的工具 数组是已经介绍过的数据存储结构,和其他结构( ...
- 【Java数据结构与算法】第四章 栈实现综合计算器
第四章 栈实现综合计算器 文章目录 第四章 栈实现综合计算器 一.栈 1.介绍 2.应用场景 3.思路 4.代码实现 二.综合计算器 v1.0 1.思路 2.代码实现 三.前缀.中缀和后缀表达式规则 ...
- 14_JavaScript数据结构与算法(十四)图
JavaScript 数据结构与算法(十四)图 图的概念 在计算机程序设计中,图也是一种非常常见的数据结构,图论其实是一个非常大的话题,在数学上起源于哥尼斯堡七桥问题. 什么是图? 图是一种与树有些相 ...
- Java 数据结构和算法(十五):无权无向图
Java数据结构和算法(十五)--无权无向图 前面我们介绍了树这种数据结构,树是由n(n>0)个有限节点通过连接它们的边组成一个具有层次关系的集合,把它叫做"树"是因为它看起 ...
- 斗地主AI算法——第十四章の主动出牌(3)
上一章已经排除了飞机.三带等牌型,那么除去炸弹王炸以外,我们只剩下单牌.对牌.三牌以及单顺.双顺.三顺了. 首先说单牌.对牌.三牌.其逻辑基本一样,只是出牌的个数有差别,即:如果该i牌数量满足这种牌型 ...
- Java数据结构和算法(十)——二叉树
接下来我们将会介绍另外一种数据结构--树.二叉树是树这种数据结构的一员,后面我们还会介绍红黑树,2-3-4树等数据结构.那么为什么要使用树?它有什么优点? 前面我们介绍数组的数据结构,我们知道对于有序 ...
- 《学习JavaScript数据结构与算法》 第四章笔记 栈
文章目录 前言 一.栈? 二.构建两种栈的大致步骤 三.创建基于数组的栈 创建class Stack 定义用于操作栈的方法 使用栈 四.创建基于对象的栈 创建class Stack 定义用于操作栈的方 ...
- Android版数据结构与算法汇总十二章
Android版数据结构与算法(一):基础简介 https://www.cnblogs.com/leipDao/p/9140726.html Android版数据结构与算法(二):基于数组的实现Arr ...
- 算法导论第十三章 红黑树
写在前面:这一章真的把我害惨了,之前至少尝试看过3遍,每次看之前都下定决定一定要把它拿下,可是由于内容较多,深度够深,以致于每次要不是中途有什么事放弃了就跳过了,要不是花时间太多仍然不能理解而放弃.这 ...
最新文章
- 【JS第28期】继承-借用构造函数
- Java学习笔记30
- RabbitMQ 一二事 - 简单队列使用
- 单片机汇编编程300例_pic单片机编程串烧,pic单片机汇编语言讲解下篇
- 我的Go语言学习之旅一:WIN下Go的安装
- html 两个表合并,SQL中将两个表合并成一个新表
- pcd格式点云的显示程序
- win10电脑装USB CAN 1报错代码39
- Win10 C盘修改用户文件夹
- 上原れな - 届かない恋
- cadence一些快捷键
- 哈工大计算机系统大作业:程序人生-Hello’s P2P
- [题单]多校补题 2017-2012
- echarts的词云图
- JAVA中下载文件名含有中文乱码一种少见的解决方案
- [C++] 获取IE代理服务器的账号密码
- python http服务器
- 新学期新环境学习计划
- html一行字不同颜色6,html语言的字体设置
- pycharm踩坑指南
热门文章
- 包含几通道数据_功率分析仪和数据采集记录仪有什么区别?
- leetcode 61. Rotate List
- Oracle安装-------实例化EM 配置文件时出错问题 ( 转 )
- 软件需求和问题解决-转载
- 用xib自定义UIView并在代码中使用--iOS
- 输入框聚焦隐藏提示语
- Client访问Tomcat简单流程(Struts2)
- ASP.NET实现多域名多网站共享Session值
- python安装 pip_多版本Python安装pip及pip版本管理终极教程
- eclipse编辑器未包含main类型_Shopify模版编辑器问题排查及解决办法汇总