LLRB——红黑树的现代实现

一、本文内容
以一种简明易懂的方式介绍红黑树背后的逻辑实现2-3-4树,以及红黑树的插入、删除操作,重点在2-3-4树与红黑树的对应关系上,并理清红黑树相关操作的来龙去脉。抛弃以往复杂的实现,而分析红黑树的一种简单实现LLRB。
 
 
二、算法应用
红黑树,给人以强烈的第一听觉冲击力——红与黑,好像很高端的感觉。事实上的确如此,红黑树是一种高级数据结构,在C++、Java的标准库里作为set、map的底层数据结构实现,以及linux中进程的公平调度。
 
 
三、2-3-4树
标题是红黑树,为什么讲2-3-4树?因为红黑树就是2-3-4树的一种等价形式,更准确地来说,我们用红黑树来完成2-3-4树的各种操作(如插入、删除)。原因就是2-3-4树的实现即维护太麻烦。所以理解2-3-4树才能真正理解红黑树。而历史就是这么发展的,了解过去,现在的一切才有了意义。算法导论关于红黑树这一节就忽略了这一点,让人知其然而不知其所以然。
 
OK,暂时先忽略复杂的红黑树,从简单的2-3-4树开始。
 
1、定义
 
2-3-4树是一种泛化的BST,它的每个结点允许1,2或者3个键(key),那么对应的有三种结点:
2-node:一个key,两个孩子;
3-node:二个key,三个孩子;
4-node:三个key,四个孩子。
注:k-node表示有k个链接(link)。泛化的BST还有2-3树,B树等。
 
从图中可以看出2-3-4树的另一个性质:它是完全平衡的(等高),即从根结点到叶子结点距离相等。
 
2、插入操作
2-3-4树本身就是一种查找树(中序遍历有序),故其查找操作同二叉查找。
 
2-3-4树的插入操作类似二叉查找树,先是查找操作失败(从根结点查找到叶子结点),然后在底部的叶子结点插入。
因为2-3-4树的结点有三种类型,所以操作有点差异。对于2-node和3-node,分别直接插入可变成3-node,4-node;但是对于4-node若直接插入则违反了定义。在4-node插入之前,先分裂4-node成2个2-node,再将待插入的key插入对应的2-node。 如下图,H查找失败,在H插入4-node(由三个key F、G、J组成)之前,先对该4-node分裂(将三个key的中间值提上父节点,剩余的二个key分别作为中间key的左右孩子),然后再将H插入2-node J中。这样操作的结果是查找到达底部叶子结点时,始终是2-node或者3-node。
 
插入算法思想:自下而上的算法由原作者Bayer在1972年提出,自上而下的算法由Guibas-Sedgewick(红黑树这个名字来源于他们)在1978年提出,然后30年后也就是2008年Sedgewick教授又改进了红黑树的操作,也就是后面要介绍的LLRB。
 
自上而下的算法思路是,从根结点向下的查找过程中,遇到4-node就分裂,最后在底部的叶子结点插入。
那么为什么遇到4-node就分裂呢?4-node不是2-3-4树的一种合法结点类型吗?
答案可以从后面LLRB的算法思路可以得出。
 
因为遇到4-node就分裂就保证了当前结点不是4-node,则分裂孩子的4-node有两种情形:
分裂4-node的case 1
 
 
分裂4-node的case 2
注:上面的变换在树中任意位置都成立。
 
 
下面两张图是完整的插入过程(只有分裂结点类型为4-node的根结点才会导致树高增1):
 
3、平衡性分析
2-3-4树的树高在最坏情况下为lgN(所有结点都是2-node型),最好情况下为lg4 N = 1/2 lgN(所有结点都是4-node型),2-3-4树的查找、插入操作都是lgN。
 
 
四、红黑树
 
终于到了高富帅——红黑树。。。
从2-3-4树的介绍可以看出,对2-node、3-node、4-node的不同数据类型进行转换,但所涉及的大部分任务使用这种直接的表示方法来实现并不方便。所以可以用
一种统一的方式完成转换,而只需很小的开销。这就是红黑树存在的意义,既有BST的标准搜索过程,又有2-3-4树的简单插入平衡过程。
 
下面介绍LLRB(Left-leaning red-black trees),而不是标准的红黑树。
1、定义
LLRB有三个特点:
(1)用BST来表示2-3-4树;
(2)用红边(红链接)来连接2-node来表示3-node和4-node(如下图);
(3)3-node必须是向左倾斜的(两者的大者作为根)。
 
LLRB相对于标准的RB多了特点3,在标准的RB中右向倾斜的红链接是允许的。对于特点2,在物理上用一个bit(红或黑)来存储以表示指向该结点的红链接。
红链接来连接3-node或者4-node的内部key,而黑链接则连接外部的key;为了理解,可以消除红链接并将它们连接的结点都折叠起来(即将看做红链接连
接的点缩为一个点),则可以看出黑链接个数不变。
2-3-4树与红黑树是一一对应的关系
 
 
且上下关系中不允许2个连续的红边
 
由特点3可以推出LLRB的一个特性,红黑树与2-3-4树一一对应。
 
2、插入算法
同样地,在LLRB中查找操作同BST。
在插入之前要知道一个操作:旋转。它有两种情况:左旋,右旋。
 
 
左旋 右旋
 
插入算法思路:即前面介绍的2-3-4树
具体实现时,插入一个结点时,始终是红结点,即用红边链接该结点。对于2-node、3-node直接插入(k-node有k个插入点),如违反上面的左红链接和连续的红链接,则旋转作调整。对于4-node(左右都为红链接),先分裂,物理实现是一个翻转(左右红链接变黑,父链接变红)。
2-node插入的两种case
 
 
3-node插入的三种case
 
 
 
4-node分裂操作
 
 
由4-node的分裂可知黑高度不变,分裂操作即翻转在图片上对应为红链接向上传递。
在介绍2-3-4树时,4-node分裂操作有两种情况,4-node的parent是2-node和3-node;再结合k-node有k个插入点,则总共有6种情况。
4-node的分裂case 1
 
 
 
4-node的分裂case 2
 
 
看了上面两幅图后,也许会让人觉得红黑树太复杂了,这么多case,其实不然,在LLRB实现中只有两种操作:旋转、翻转。旋转的目的是保持平衡,翻转的目的是分裂4-node。
看了下面的LLRB插入算法,你就会明白上面4-node的翻转、旋转其实是分开的两个过程(翻转自上而下,旋转自下而上),只是为了统一这个完整的过程而画在了一起,才会有那么多case。
 
LLRB的插入算法:
首先结合2-3-4树的插入算法思路,先从上至下查找(遇到4-node则翻转),然后在底部叶子结点插入,因为在从上至下的过程中,可能会产生不满足LLRB的性质的情况,故插入结点后需要从下至上调整以恢复LLRB性质。
下图是插入算法的核心代码,第2是分裂即翻转,第1是插入操作,第3、4是调整。
 
从插入算法可以看出,如果自下而上再分裂4-node,则会出现它的parent也可能是4-node,祖父结点也可能是4-node;我们可以一直向上分裂,这也正是上面提到的自上而下的思路;而更简单的方法是,在沿树向下的过程中,遇到4-node就分裂,这也正是自上而下与自下而上的区别。
插入算法的核心代码
 
上图的核心代码按照从上而下和从下而上的顺序放入BST的插入(递归版本)操作中即得到下图的完整的插入算法。
注:分裂(即翻转)是自上而下,所以放在递归之前;调整(即旋转)是自上而下,所以放在递归之后。
完整的插入代码
 
 
如果将分裂操作放到递归之后,也就是先自上而下查找,插入结点,然后自下而上调整也可同样完成插入操作而不破坏LLRB的性质。
2-3树的插入操作
 
其实上述描述的就是2-3树的插入操作,它与2-3-4树的插入的区别在于:2-3树先插入,再分裂(down)、调整(up);2-3-4树先分裂(down),再插入、调整(up)。又因为插入总是在最后一层进行,故翻转的位置决定了对应树的实现。
这也是为什么2-3-4树叫top-down,而2-3树叫bottom-up。
 
3、删除算法
LLRB的删除类似于插入,只不过处理刚好相反。插入、删除都有临界点:插入4-node,删除2-node,对临界点的操作都会引起突变,因为它们会破坏LLRB的性质(黑高度)。
所以同插入一样,先从上至下查找,如果查找在3-node或4-node结束,则直接删除;
3-node和4-node的删除
 
对于2-node的删除同4-node的插入相反,2-node的删除是先合并2个2-node为1个4-node,然后再安全地删除对应的2-node中的key。
同样地,因为parent不为2-node(遇到即合并),再结合兄弟结点的2、3、4-node,则删除总共有6种情况。同样,实际的删除实现也
没这么复杂。
2-node的删除
 
在介绍删除任意一个结点时,先分析删除树中最小的结点。因为它是删除任意结点的一部分,后面可以看出来。
首先,为了保证可以直接删除最小的某个结点,需要假设当前结点h或者h.left是红色链。
然后从上而下查找过程中,2个2-node要变为1个4-node,则需反向翻转(红色父链接变黑,黑色子链接变红),
为了将红链从上向左子树传递(删除红结点,不改变黑高度),需保证h为红,h.left和h.left.left为黑;
当h.left和h.left.left都为黑时,
如果h.right.left为红,则要从右边借兄弟(下图case 2),如果h.right.left为黑,则不需要(下图case1)。
注:在翻转的同时,右子树可能会产生连续的红链,则需调整。
case 1
 
 
 
case 2
 
 
 
红链向左移动 红链向左移动对应的example
 
deleteMin的实现
 
 
 
deleteMin的example
 
 
完成了deleteMin就完成了LLRB的删除操作的一大半。现在是删除LLRB的任意一个key,
自上而下查找过程中,左边查找用moveRedLeft;右边查找用moveRedRight;直到最后的底部叶子结点,直接删除即可;同样,自下而上调整。
 
怎样将delete操作归约到delteMin去呢?算法导论提供的一个技巧是:replace,deleteMin(即用后继的key代替当前的key,再删除右孩子的最小结点)。
删除技巧
 
 
 
完整删除代码
 
 
 
参考:
《算法导论》
《algorithm in c》
 

PS:9.9忆山东兄弟,必登高望远,一览纵山小。

 
 

红黑树LLRB的更多相关文章

  1. LLRB——红黑树的现代实现

    一.本文内容 以一种简明易懂的方式介绍红黑树背后的逻辑实现2-3-4树,以及红黑树的插入.删除操作,重点在2-3-4树与红黑树的对应关系上,并理清红黑树相关操作的来龙去脉.抛弃以往复杂的实现,而分析红 ...

  2. Tree--RedBlackTree详解(2 - 3 - 4Tree)(红黑树)

    #topics h2 { background: #2B6695; color: #FFFFFF; font-family: "微软雅黑", "宋体", &qu ...

  3. 数据结构与算法--从平衡二叉树(AVL)到红黑树

    数据结构与算法--从平衡二叉树(AVL)到红黑树 上节学习了二叉查找树.算法的性能取决于树的形状,而树的形状取决于插入键的顺序.在最好的情况下,n个结点的树是完全平衡的,如下图"最好情况&q ...

  4. 2-3 树/红黑树(red-black tree)

    2-3 tree 2-3树节点: null节点,null节点到根节点的距离都是相同的,所以2-3数是平衡树 2叉节点,有两个分树,节点中有一个元素,左树元素更小,右树元素节点更大 3叉节点,有三个子树 ...

  5. 数据结构和算法(Golang实现)(29)查找算法-2-3树和左倾红黑树

    某些教程不区分普通红黑树和左倾红黑树的区别,直接将左倾红黑树拿来教学,并且称其为红黑树,因为左倾红黑树与普通的红黑树相比,实现起来较为简单,容易教学.在这里,我们区分开左倾红黑树和普通红黑树. 红黑树 ...

  6. 红黑树——算法导论(15)

    1. 什么是红黑树 (1) 简介     上一篇我们介绍了基本动态集合操作时间复杂度均为O(h)的二叉搜索树.但遗憾的是,只有当二叉搜索树高度较低时,这些集合操作才会较快:即当树的高度较高(甚至一种极 ...

  7. jdk源码分析红黑树——插入篇

    红黑树是自平衡的排序树,自平衡的优点是减少遍历的节点,所以效率会高.如果是非平衡的二叉树,当顺序或逆序插入的时候,查找动作很可能会遍历n个节点 红黑树的规则很容易理解,但是维护这个规则难. 一.规则 ...

  8. 谈c++ pb_ds库(二) 红黑树大法好

    厉害了,没想到翻翻pb_ds库看到这么多好东西,封装好的.现成的splay.红黑树.avl... 即使不能在考场上使用也可以用来对拍哦 声明/头文件 #include <ext/pb_ds/tr ...

  9. 定时器管理:nginx的红黑树和libevent的堆

    libevent 发生超时后, while循环一次从堆顶del timer——直到最新调整的最小堆顶不是超时事件为止,(实际是del event),但是会稍后把这个timeout的 event放到ac ...

随机推荐

  1. php+flash头像上传组件

    有会员系统的站点一般都会有一个头像上传组件,一般做的最简单的是 这样的方式长处是代码写的简单,仅仅要推断图片大小和类型,然后更新数据库.可是用户体验不高.并且站点其它页面假设要使用较小的20X20或1 ...

  2. [mysql]刷新windows恢复后mysql和&quot;Access denied for user&#39;root&#39;@&#39;IP&#39;&quot;处理问题

    mysql数据库软件实际上是绿色的,重装系统后能够继续使用. 1.重装系统保留原有的后mysql安装文件夹,数据文件夹. 2.制作用于启动一个批处理文件mysql:[run.bat]的文件存储在mys ...

  3. CSS3+HTML5特效9 - 简单的时钟

    原文:CSS3+HTML5特效9 - 简单的时钟 效果演示(加快了100倍)         实现原理 利用CSS3的transform-origin 及 transform 完成以上效果. 代码及说 ...

  4. SharePoint 2013 搜索SharePoint 特定列和特定文档(自己定义搜索)

    SharePoint 2013 搜索SharePoint 特定列和特定文档 1,操作步骤和图例,因语言和版本号的不同 我尽量使用抓图方式. 2.  In Central Administration, ...

  5. Unix命令操作

    基本命令 [ man 查看 ]--万能命令 1.ls 列出文件 (-al) 2.cd 转换目录 3.mkdir 建立新目录 4.cp 拷贝文件 (-R) 5.rm 删除文件 (-rf) 6.mv 移动 ...

  6. c# 数据类型转换 as(C# 参考)

    as    运算符类似于强制转换操作.               但是,因此,如果转换是不可能的,as 返回 null 而不引发异常.  请看下面的示例: expression is type ? ...

  7. 如何将经纬度利用Google Map API显示C# VS2005 Sample Code

    原文 如何将经纬度利用Google Map API显示C# VS2005 Sample Code 日前写了一篇如何用GPS抓取目前所在,并回传至资料库储存,这篇将会利用这些回报的资料,将它显示在地图上 ...

  8. C# 通过ImportNode AppendChild方法合并XmlDocument,XML转为DataTable

    var doc1 = new XmlDocument(); var doc2 = new XmlDocument(); XmlNode root1 = doc1.DocumentElement; do ...

  9. HDU 4324 Triangle LOVE 拓扑排序

    Problem Description Recently, scientists find that there is love between any of two people. For exam ...

  10. 【Heritrix基础教程2】Heritrix基本介绍

    1.版本号说明 (1)最新的版本号:3.3.0 (2)最新release版本号:3.2.0 (3)重要历史版本号:1.14.4 3.1.0及之前的版本号:http://sourceforge.net/ ...