Sedgewick的红黑树
红黑树一直是数据结构中的难点,大部分关于算法与数据结构的学习资料(包括《算法导论》)对于这部分的讲解都是上来就下定义,告诉我们红黑树这个性质那个性质,插入删除要注意1234点,但是基本没有讲为什么这样定义红色和黑色,让人理解起来十分费力。直到我看了下图这本书中关于红黑树部分的讲解,一时间豁然开朗,上网查了下这本书的作者Sedgewick,他是伟大的高德纳的学生!红黑树的发明者!
他在这本书中告诉了我们红黑树的根本模型:以二叉树的形式实现2-3树,通过红黑树与2-3树之间的一一对应,让我们对红黑树有了更直观的理解。
这本书里所讲的是左偏红黑树模型,理解了这个模型,再理解算法导论的完整红黑树模型就容易的多了。

2-3树
定义。2-3查找树允许树中的一个结点保存多个键,一棵2-3查找树或为一棵空树,或由以下结点组成:
2-结点,含有一个键(及其对应的值)和两条链接,左链接指向的2-3树中的键都小于该结点,右链接指向的2-3树中的键都大于该结点。
3-结点,含有两个键(及其对应的值)和三条链接,左链接指向的2-3树中的键都小于该结点,中链接指向的2-3树中的键都位于该结点的两个键之间,右链接指向的2-3树中的键都大于该结点。
一棵完美平衡的2-3查找树中的所有空链接到根结点的距离都应该是相同的。这里我们用2-3树指代一棵完美平衡的2-3查找树,如下图所示。

查找。将二叉查找树的查找算法一般化我们就能够直接得到2-3树的查找算法。要判断一个键是否在树中,我们先将它和根节点中的键比较。如果它和其中任意一个相等,查找命中;否则我们就根据比较的结果找到指向相应区间的链接,并在其指向的子树中递归地继续查找。如果这个是空连接,查找未命中。

插入。要在2-3树中插入一个新结点,我们可以和二叉查找树一样先进行一次未命中的查找,如果未命中的查找结束于一个2-结点,事情就好办了:我们只要把这个2-结点替换为一个3-结点,将要插入的键保存在其中即可。如果未命中的查找结束于一个3-结点,事情就要麻烦一些。
向2-结点中插入新键。

向一棵只含有一个3-结点的树中插入新键。

向一个父节点为2-结点的3-结点中插入新键。

向一个父节点为3-结点的3-结点中插入新键。

插入结点到根节点的路径上全都是3-结点。

和标准的二叉查找树由上向下生长不同,2-3树的生长是由下向上的:随着结点的插入,临时4-结点的中键不断上浮,一旦根节点变成临时4-结点,我们可以分解根结点完成树的生长,使得树高加1。
下图给出了我们的标准索引测试用例中产生的一系列2-3树,以及一系列由同一组键按照升序一次插入到树中时所产生的所有2-3树。如果是在二叉查找树中,按照升序插入10个键会得到高度为9的一棵最差查找树,而使用2-3树,树的高度是2。

在一棵大小为N的2-3树中,查找和插入操作访问的结点必然不超过lgN个。
尽管我们可以用不同的数据类型表示2-结点和3-结点并写出变换所需的代码,这样我们需要维护两种不同类型的结点,将被查找的键和结点中的每个键进行比较,将链接和其他信息从一种结点复制到另一种结点,将结点从一种数据类型转换到另一种数据类型等等。实现这些不仅需要大量的代码,而且它们所产生的额外开销可能会使算法比标准的二叉查找树更慢。幸运的是我们可以使用红黑树来解决这个问题,它以二叉树的形式实现了2-3树。

红黑树
定义。红黑树的一种定义是含有红黑链接并满足下列条件的二叉查找树:
红链接均为左链接(左偏红黑树);
没有任何一个结点同时和两条红链接相连;
该树是完美黑色平衡的,即任意空链接到根节点的路径上的黑链接数量相同。
满足这样定义的红黑树和相应的2-3树是一一对应的。
如果我们将一棵红黑树中的红链接画平,那么所有的空链接到根节点的距离都将是相同的。如果我们将有红链接相连的结点合并,得到的就是一棵2-3树。
相反,如果将一棵2-3树中的3-结点画作由红色左链接相连的两个2-结点,那么不会存在能够和两条红链接相连的结点,且树必然是完美黑色平衡的。
红黑树既是二叉查找树,也是2-3树。因此如果我们能够在保持一一对应关系的基础上实现2-3树的插入算法,那么我们就能够将两个算法的优点结合起来:二叉查找树中简洁高效的查找算法和2-3树中高效的平衡插入算法。

颜色表示。因为每个结点都只会有一条指向自己的链接(从它的父结点指向它),我们将链接的颜色保存在表示结点的Node数据类型的布尔变量color中。如果指向它的链接是红色的,那么该变量为true,黑色则为false。我们约定空链接为黑色。


旋转。首先,假设我们有一条红色的右链接需要被转化为左链接,这个操作叫左旋转。它只是将用两个键中的较小者作为根节点变为将较大者作为根节点。
实现将一个红色左链接转换为红色右链接的右旋转的代码完全相同,只需要将left换成right即可。

插入。在插入新的键时我们可以使用旋转操作帮助我们保证红黑树和2-3树之间的一一对应关系,因为旋转操作可以保持红黑树的两个重要性质:有序性和完美平衡性。我们只需考虑如何使用旋转操作来保持红黑树的另外两个重要性质:不存在两条连续的红链接和不存在红色的右链接。
用和二叉查找树相同的方式向一棵红黑树中插入一个新键会在树的底部新增一个结点,但总是用红链接将新结点和它的父结点相连。
向2-结点中插入新键。

向树底部的2-结点插入新键。

向一个3-结点中插入新键。这种情况又可分为三种子情况:新键大于树中的两个键,小于树中的两个键,或是在两者之间。

颜色转换。我们专门用一个方法flipColors()来转换一个结点的两个红色子结点的颜色。颜色转换会使根结点变成红色,两个子结点变成黑色。

向树底部的3-结点插入新键。

红黑树构造轨迹。

删除。和插入操作一样,我们也可以定义一系列局部变换来在删除一个结点的同时保持树的完美平衡性。这一过程比插入一个结点更加复杂,因为我们不仅要在(为了删除一个结点而)构造临时4-结点时沿着查找路径向下进行变换,还要在分解遗留的临时4-结点时沿着查找路径向上进行变换(同插入操作)。
删除最小键。从树底部的3-结点中删除键是很简单的,但2-结点则不然。从2-结点中删除一个键会留下一个空结点,一般我们会将它替换为一个空链接,但这样会破坏树的完美平衡性。为了保证我们不会删除一个2-结点,我们沿着左链接向下进行变换(最小键一定在左链接上),确保当前结点不是2-结点(即左链接上的结点可以是3-结点,也可以是临时的4-结点),最后能够得到一个含有最小键的3-结点或者临时4-结点,然后我们就可以直接从中将其删除,将3-结点变为2-结点,或者将临时4-结点变为3-结点。然后我们再回头向上分解所有临时的4结点。

删除任意键。在查找路径上进行和删除最小键相同的变换同样可以保证在查找过程中任意当前结点均不是2-结点。如果被查找的的键在树的底部,我们可以直接删除它。如果不在,我们需要将它和它的后继结点交换。因为当前结点必然不是2-结点,问题已经转化为在一棵根节点不是2-结点的子树中删除最小的键,我们可以在这棵子树中使用上述的算法,删除之后我们需要向上回溯并分解余下的临时4-结点。
红黑树最广泛的应用是在C++的STL中,map和set都是用红黑树实现的。
Sedgewick的红黑树的更多相关文章
- Atitit 常见的树形结构 红黑树 二叉树 B树 B+树 Trie树 attilax理解与总结
Atitit 常见的树形结构 红黑树 二叉树 B树 B+树 Trie树 attilax理解与总结 1.1. 树形结构-- 一对多的关系1 1.2. 树的相关术语: 1 1.3. 常见的树形结构 ...
- 通过分析 JDK 源代码研究 TreeMap 红黑树算法实现
本文转载自http://www.ibm.com/developerworks/cn/java/j-lo-tree/ 目录: TreeSet 和 TreeMap 的关系 TreeMap 的添加节点 Tr ...
- java中treemap和treeset实现(红黑树)
java中treemap和treeset实现(红黑树) TreeMap 的实现就是红黑树数据结构,也就说是一棵自平衡的排序二叉树,这样就可以保证当需要快速检索指定节点. TreeSet 和 Tre ...
- D&F学数据结构系列——红黑树
红黑树 定义:一棵二叉查找树如果满足下面的红黑性质,则为一棵红黑树: 1)每个结点不是红的就是黑的 2)根结点是黑的 3)每个叶结点是黑的 4)如果一个结点是红的,它的两个儿子都是黑的(即不可能有两个 ...
- 通过分析 JDK 源代码研究 TreeMap 红黑树算法实现--转
TreeMap 和 TreeSet 是 Java Collection Framework 的两个重要成员,其中 TreeMap 是 Map 接口的常用实现类,而 TreeSet 是 Set 接口的常 ...
- 平衡树 - 红黑树(JQuery+Js+Canvas版本的,帮助大家理解)
红黑树 1.红黑树介绍 年写的一篇论文中获得的.它是复杂的,但它的操作有着良好的最坏情况运行时间,并且在实践中是高效的:它可以在O(log n)时间内做查找,插入和删除,这里的n是树中元素的数目. 2 ...
- 红黑树LLRB
LLRB——红黑树的现代实现 一.本文内容 以一种简明易懂的方式介绍红黑树背后的逻辑实现2-3-4树,以及红黑树的插入.删除操作,重点在2-3-4树与红黑树的对应关系上,并理清红黑树相关操作的来龙去脉 ...
- 通过分析 JDK 源代码研究 TreeMap 红黑树算法实
TreeMap和TreeSet是Java Collection Framework的两个重要成员,其中TreeMap是Map接口的常用实现类,而TreeSet是Set接口的常用实现类.虽然HashMa ...
- [转载] 红黑树(Red Black Tree)- 对于 JDK TreeMap的实现
转载自http://blog.csdn.net/yangjun2/article/details/6542321 介绍另一种平衡二叉树:红黑树(Red Black Tree),红黑树由Rudolf B ...
随机推荐
- python的基础类源码解析——collection类
1.计数器(counter) Counter是对字典类型的补充,用于追踪值的出现次数. ps:具备字典的所有功能 + 自己的功能 ################################### ...
- 使用ueditor中的setContent() 时经常报innerHtml错误(笔记)
1)今天遇到个问题,使用ueditor中的setContent() 时经常报innerHtml错误:网上找了下解决方案:发现这个可以用: 不能创建editor之后马上使用ueditor.setCont ...
- app——分享wap站,数据处理页面展示
无意中接到了一个小的工作任务:配合手机app端的分享功能做一个wap站,简言之:将手机app端分享的文章id传过来,利用此id再进行一系列的操作,由于文章分为纯文本,图文以及图集的三种类型的文章,因此 ...
- 分布式系统开发的一些相关理论基础——CAP、ACID、BASE
本文主要讲述分布式系统开发的一些相关理论基础. 一.ACID 事务的四个特征: 1.Atomic原子性 事务必须是一个原子的操作序列单元,事务中包含的各项操作在一次执行过程中,要么全部执行成功,要么全 ...
- [转]深入理解Java 8 Lambda(语言篇——lambda,方法引用,目标类型和默认方法)
以下内容转自: 作者:Lucida 微博:@peng_gong 豆瓣:@figure9 原文链接:http://zh.lucida.me/blog/java-8-lambdas-insideout-l ...
- IT的灵魂是流程,流程的灵魂是业务,业务的灵魂是战略
IT的灵魂是流程,流程的灵魂是业务,业务的灵魂是战略.高效的IT平台不在于IT技术,而在于好的管理模式与流程设计 从以组织为核心转向以流程为核心 流程管理核心是从流程角度出发,关注流程是否增值,籍此建 ...
- Linux系统真正的优势以及学习方法
作为一名Linux爱好者,在Linux的世界中也算是半个老司机了,从桌面玩到服务器.从ubuntu到centos.从计算机到路由器,各种Linux的花俏玩法都略有体验.作者并非职业Linux选手,我仅 ...
- Java中的异常处理
描述: 如果Java中的函数有可能抛出异常,则该异常要么被catch住,要么在声明函数时必须声明该函数体会throws exception. 处理的时候的流程是,当发生异常时,首先结束当前函数后续语句 ...
- Android之View和SurfaceView
Android之View和SurfaceView Android游戏当中主要的除了控制类外就是显示类View.SurfaceView是从View基类中派生出来的显示类.android游戏开发中常用的三 ...
- Swift语言—常量、变量、类型赋值
常量:常量在作用域内不能被赋值且不会发生改变,只读不写,每个常量都必须有唯一的名字和内存空间:关键字:let 变量:变量在作用区域内可以它的值可以被改变,可读可写可覆盖,每个常量都必须有唯一的名字 ...