1. 红黑树(RED-BLACK TREE)引言:

-------------------------------------

红黑树(RBT)可以说是binary-search tree的非严格的平衡版本。与之相应的是平衡二叉树(Balanced Binary Tree)又称之为AVL树(因为是G.M. Adelson-Velsky 和 E.M. Landis在1962年发明的这棵树)是binary-search tree的严格的平衡版本。

BST达到最平衡的状态称之为AVL。在AVL树中任何节点的两个儿子子树的高度最大差别为一,查找、插入和删除在平均和最坏情况下都是O(lg n)。但因为增加和删除节点可能会破坏“平衡状态”,所以大多数情况下需要通过多次树旋转来重新平衡这个树。所以简单地说,如果你的应用查找次数远远多于增删操作,那么AVL是最好的,但是如果增删次数和查找次数不相上下时,RBT因为相比AVL没有过多的旋转操作,效率要比AVL高。并且在是实际情况中,RBT的应用也更为广泛。至少《intro to algo》这本书上主要讲了RBT。

2. 红黑树几个基本属性:

------------------------

因为RBT是binary-search tree的非严格的平衡版本,所以红黑树继承了BST的基本属性:key值,和基本性质:对于tree中任意节点x,都有x.left.key<x.key<=x.right.key. 此外,BST的叶子节点在RBT不称之为叶子节点,RBT的叶子节点为NIL,所以BST的ROOT节点的父亲节点在RBT表示里不再为空,而是NIL或者称之为TREE.sentinel. BST中的叶子节点也是一样,他们也有孩子为NIL节点。接下来是RBT的独有属性:color,即每个树节点都有自己的color,要么要么

然后是RBT的五个基本性质:1)每个树节点要么是红色要么是黑色;2)ROOT节点必须是黑色;3)NIL节点为黑色;4)如果一个节点是红色,那么它的孩子节点都是黑色(NIL节点除外),从每个叶子到根的所有路径上不能有两个连续的红色节点;5)从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。 如下图所示:

  

3. 旋转:

--------

因为在执行操作或者删除操作以后,RBT的性质可能会被改变。所以为了维护RBT的基本性质[2],我们需要改变一些树的节点的颜色,同时需要改变树的结构。下图展示了树的旋转动作:

          

旋转动作共有两种:left 和 right。left-rotation 就是逆时针旋转,right-rotation是顺时针旋转。以left-roation为例,如上图右侧所示,

第一步:建立y的左孩子β与x的“亲子关系”:β.parent=x AND x.right=β.

第二步:y取代x:(当x不是ROOT节点时) y.parent=x.parent AND (x.parent.left==x)? (y=x.parent.left): (y=x.parent.right).

(当x是ROOT节点时) y=ROOT

第三步:逆转x,y父子关系: x=y.left AND x.parent=y;

  1. 1 LEFT-ROTATE(T, x)
  2. 2 y=x.right
  3. 3 x.right=y.left
  4. 4 if y.left != T.nil
  5. 5 y.left.p=x
  6. 6 y.parent=x.parent
  7. 7 if x.parent==T.nil
  8. 8 T.root=y
  9. 9 else if x==x.parent.left
  10. 10 x.parent.left=y
  11. 11 else x.parent.left=y
  12. 12 y.left=x
  13. 13 x.parent=y

 4. 插入:

---------

红黑树的插入和BST基本类似。不清楚BST插入算法的可以参考我的这一篇博客:http://www.cnblogs.com/fu11211129/p/4214047.html

红黑树的插入在BST的插入上做了一些微调,这里把这两种插入的伪代码展示做个比较。

  1. TREE-INSERT(T, z)//BST插入算法
  2. 1 y=NIL
  3. 2 x=T.ROOT
  4. 3 while(x!=NIL)
  5. 4   y=x
  6. 5   if(z.key<x.key)
  7. 6     x=x.left
  8. 7   else x=x.right
  9. 8 z.p=y
  10. 9 if(y==NIL) T.ROOT=z
  11. 10 else if(z.key<y.key) y.left=z
  12. 11 else y.right=z
  1. RB-TREE-INSERT(T, z)//RBT插入算法
  2. 1 y=T.NIL
  3. 2 x=T.ROOT
  4. 3 while(x!=T.NIL)
  5. 4   y=x
  6. 5   if(z.key<x.key)
  7. 6     x=x.left
  8. 7   else x=x.right
  9. 8 z.p=y
  10. 9 if(y==T.NIL) T.ROOT=z
  11. 10 else if(z.key<y.key) y.left=z
  12. 11 else y.right=z
  13. 12 z.left=T.NIL
  14. 13 z.right=T.NIL
  15. 14 z.color=RED
  16. 17 RB-INSERT-FIXUP(T, z)

对比两个插入算法,我们可以注意到四个不同,1)BST中的NIL被替换成了T.NIL,这是因为NIL在红黑树体系里是看做一个节点的。2)RBT-INSERT算法里14-15行,我们将T.NIL分别赋值给了z.left和z.right,是为了保持树的合理结构。3)在RBT-INSERT算法里16行将z的颜色设置为红色。4)因为给z着色以后可能会导致红黑树的基本性质被破坏,所以我们调用RB-INSERT-FIXUP函数(下面会具体讲到)来秀姑RBT树使之仍能保持它的基本性质。

5. 插入修复(这里用日本古代武士社会结构做比喻):

-------------------------------------------------

小日本武士中最大的叫将军,管着很多大名,大名是有城有地盘,有兵有旗号,有很多家臣为其服务。家臣又分许多等级,大名的地盘分一部分给家臣,这就是城主。每个家臣都可以有自己的家臣,只要你地盘的收入俸禄养得起。这个等级制度,非常森严,没有出身保证,就算功劳再大也爬不上去。因为除了将军,武士一定得效忠于某个主公的,大名也是将军的家臣。并且这种效忠是继承的,你的子子孙孙都要效忠主公的后代。

日本和中国情况不同,没有人敢喊出王候将相,宁有种乎。所以丰臣秀吉能从,最底层的普通武士做起,一步步做到关白(和将军平级,那时没设将军,相当于将军),成了不可思议的传奇。当与你的功劳或能力所匹配的待遇,超过了主公所能给予,就可能发生改换门庭,甚至颠覆主公的事情。人们都说日本战国时代就是一个下克上的时代。

好了,扯得有点远了,回到正题来。我们把红黑树两种颜色节点,代表两个基本的武士类型,黑色代表安分守已对上机忠心耿耿,但同时也庸碌无为没能力。红色表示既有才能,又有野心。

插入操作时,好像一个新的武士出仕,没有背景,没有功勋。但他有能力,有抱负(红色),就像丰臣秀吉那样。要是跟了个黑色类型的主公,虽然终究会不甘心居人之下,但却没条件“下克上”,因为这个主公对大主公(主公的主公)忠心耿耿,兢兢业业,你再有才也英雄无用武之地,只能待时而动了。这种情况对应着RBT的结构已经稳定。

但要是主公是红色的(有野心),那新武士就有想入非非了,成天跟大主公打小报告,说他谋反。反正主公忠心有限,把柄很多,只是大主公没那个洞察力。大主公看到一些莫须有的证据,谋反这事宁枉勿纵。于是新武士举报有功,主公反倒成了自己的家臣(如下图第二个状态)。但是昏庸的大主公很快就要付出代价,自己成了新武士的下一个目标(如下图第三个状态)。

            

               (B表示新武士,A表示B的主公)

还有一种情况就是,主公的有野心的,但是大主公的其他家臣如果也有野心的话,情况就不一样了,要是其他家臣也一样有能力,那就热闹了,这一家从此两虎相争,不得安宁(此处略去数万字)。大主公能力平庸,控制不住,最终酿成大乱,经过一番血雨腥风,大主公家出现了一位雄才伟略的家主,平定了内乱,原来两家的强势家主全部被消灭,家主换成了忠心可靠(变黑)的人。而大主公家的新家主,野心开始膨涨了(变红)

下面走一遍完整的流程:

好了从a)图开始,新武士编号为4(这里我就不标红圈了,阴影的就代表红色)在家臣5的手下做事。家臣5不愿意屈居人下,而4发现了这一点,4想撺掇5去篡权。但不幸的是,同样是7的家臣,家臣8,也是野心勃勃(红色)。所以在大名7的统治下,出现了混乱的局面,群雄四起。。。最终大名家族又出现了一名新的大名7,平定了内乱,将家臣5和8换成了对自己忠心的家臣。而且与此同时,新的大名7也是野心膨胀(变红)。(在途中对应的是状态1经过case1到达状态2)

新的大名7野心勃勃,而且他的主公2也是野心不小,所以大名7只能战而玩阴的了,大名7向大主公11说主公2的坏话。大主公也是二货(黑色),听风就是雨,大名7举报有功,这样一来,主公2反倒成了大名7的下属了。但是很快的,这位二货打主攻11也要自食恶果了,大名7直接占了他的位置。

6. 移植:

--------

因为移植操作是删除操作的一个基本动作,所以这里先做个简要说明。RBT的移植算法基本和BST的一致,大家可以参照博

  1. RB-TRANPLANT(T, u, v)
  2. 1 if (u.parent==NIL) T.ROOT=v
  3. 2 else if(u==u.parent;.left) u.parent.left=v
  4. 3 else u.parent.right=v
  5. 4 v.parent=u.parent

客:http://www.cnblogs.com/fu11211129/p/4214047.html

大家可以发现只有第四行不一样,原因很简单,RBT把NIL看做一个节点了。

7. 删除:

--------

删除操作是比较麻烦的一部分内容,但类似的,和BST删除操作也很相似,http://www.cnblogs.com/fu11211129/p/4214047.html

下面贴出RBT的删除操作的伪代码:

  1. RB-DELETE(T, z) 单纯删除结点的总操作
  2. 1 if left[z] = nil[T] or right[z] = nil[T]
  3. 2 then y z
  4. 3 else y TREE-SUCCESSOR(z)
  5. 4 if left[y] nil[T]
  6. 5 then x left[y]
  7. 6 else x right[y]
  8. 7 p[x] p[y]
  9. 8 if p[y] = nil[T]
  10. 9 then root[T] x
  11. 10 else if y = left[p[y]]
  12. 11 then left[p[y]] x
  13. 12 else right[p[y]] x
  14. 13 if y z
  15. 14 then key[z] key[y]
  16. 15 copy y's satellite data into z
  17. 16 if color[y] = BLACK
  18. 17 then RB-DELETE-FIXUP(T, x)

因为相比BST删除算法,就是加入一些节点颜色的处理机制,所以这里不再赘述。大家理解了BST的删除后,RBT也就差不多了。

8. 删除修复(还没有想到合适的比喻,想到了会更新这一部分):

----------------------------------------------------------

还是先贴上伪代码吧,然后结合代码做个分析。

  1. RB-DELETE-FIXUP(T, x) 恢复与保持红黑性质的工作
  2. 1 while x root[T] and color[x] = BLACK
  3. 2 do if x = left[p[x]]
  4. 3 then w right[p[x]]
  5. 4 if color[w] = RED
  6. 5 then color[w] BLACK Case 1
  7. 6 color[p[x]] RED Case 1
  8. 7 LEFT-ROTATE(T, p[x]) Case 1
  9. 8 w right[p[x]] Case 1
  10. 9 if color[left[w]] = BLACK and color[right[w]] = BLACK
  11. 10 then color[w] RED Case 2
  12. 11 x p[x] Case 2
  13. 12 else if color[right[w]] = BLACK
  14. 13 then color[left[w]] BLACK Case 3
  15. 14 color[w] RED Case 3
  16. 15 RIGHT-ROTATE(T, w) Case 3
  17. 16 w right[p[x]] Case 3
  18. 17 color[w] color[p[x]] Case 4
  19. 18 color[p[x]] BLACK Case 4
  20. 19 color[right[w]] BLACK Case 4
  21. 20 LEFT-ROTATE(T, p[x]) Case 4
  22. 21 x root[T] Case 4
  23. 22 else (same as then clause with "right" and "left" exchanged)
  24. 23 color[x] BLACK

前面,我已经说了,因为插入、或删除结点后,可能会违背、或破坏红黑树的原有的性质,所以为了使插入、或删除结点后的树依然维持为一棵新的红黑树,那就要做俩方面的工作:1、部分结点颜色,重新着色 2、调整部分指针的指向,即左旋、右旋。而下面所有的文字,则是针对红黑树删除结点后,所做的修复红黑树性质的工作。

情况1:当前节点的兄弟节点是红色;解法,在当前节点的父节点上进行左旋,结束。此时红黑树性质全部恢复。


情况2:当前节点的兄弟节点是黑色并且兄弟节点的孩子节点也都是黑色 解法:直接把兄弟节点变成红色就行了。

情况3:当前节点的兄弟节点是黑色,且兄弟节点的左孩子为红色,右孩子为黑色。解法:在兄弟节点的左孩子上右旋,并且互换父亲左孩子的颜色。

情况3:当前节点的兄弟节点是黑色,且兄弟节点的两个孩子都为红色。解法: 在父亲节点上左旋,并且互换父亲右孩子颜色。

(日本武士版解释:在介绍插入的时候,我们“插入故事”的“主人翁”是一个刚出道的武士,雄姿英发,羽扇纶巾。。。,在这里我们“删除故事”的主人翁是一个“要退役”的武士x,但是秉承武士“猥琐的”特点,x下岗之前,总要把格局教的混乱不堪才行。但是因为x要“退役”了,所以他自己是没有能力的(黑色)。所以他把目光转移到了他的兄弟上,但这也要看他的兄弟w是个什么货色

如果他胸无大志,那么x就撩起兄弟w的野心(变红)(对应case2)。

如果他的兄弟w是有野心的(红色),x也就不用撺掇了,但是兵法有云,攘外必先安内(我也不知道是不是兵法上的,姑且这么说吧),如果w的两个家臣都安分守己,那么好,w就可以放心去抢班夺权了(对应case1),w成功之后志得意满,生活堕落(变黑),而被w强权的曾经上级B开始卧薪尝胆了(变红)(对应case1)。

如果他的兄弟w胸无大志,但是他的其中一个家臣野心不小,那么x就转而开始撺掇w的家臣犯上作乱(对应case3)。

如果他的兄弟w胸无大志,而且悲催的是,他的家臣没有一个真正沉服于他的。那么x心想我不用攒多了,让他们内斗吧,最后D家族出了一位新的w,先是成功篡权,然后将自己的手下换成对自己中心的人(黑色

 

算法导论学习-RED-BLACK TREE的更多相关文章

  1. 算法导论学习-binary search tree

    1. 概念: Binary-search tree(BST)是一颗二叉树,每个树上的节点都有<=1个父亲节点,ROOT节点没有父亲节点.同时每个树上的节点都有[0,2]个孩子节点(left ch ...

  2. 算法导论学习---红黑树具体解释之插入(C语言实现)

    前面我们学习二叉搜索树的时候发如今一些情况下其高度不是非常均匀,甚至有时候会退化成一条长链,所以我们引用一些"平衡"的二叉搜索树.红黑树就是一种"平衡"的二叉搜 ...

  3. 算法导论学习-Dynamic Programming

    转载自:http://blog.csdn.net/speedme/article/details/24231197 1. 什么是动态规划 ------------------------------- ...

  4. 算法导论学习-prim算法

    一. 关于最小生成树 对于无向连通图G=(V,E),其中V表示图的顶点,E表示图的边,对于每条边都有一个权值,可以理解为边a->b的权值C为从a走到b要走的路程为C.现在我们希望找到一个无回路的 ...

  5. 算法导论学习之线性时间求第k小元素+堆思想求前k大元素

    对于曾经,假设要我求第k小元素.或者是求前k大元素,我可能会将元素先排序,然后就直接求出来了,可是如今有了更好的思路. 一.线性时间内求第k小元素 这个算法又是一个基于分治思想的算法. 其详细的分治思 ...

  6. 算法导论学习-heapsort

    heap的定义:如果数组a[1,....n]满足:a[i]>a[2*i] && a[i]>a[2*i+1],1<=i<=n/2,那么就是一个heap,而且是ma ...

  7. 算法导论学习笔记1---排序算法(平台:gcc 4.6.7)

    平台:Ubuntu 12.04/gcc 4.6.7 插入排序 #include<vector> #include <algorithm> #include<iostrea ...

  8. [置顶] 《算法导论》习题解答搬运&&学习笔记 索引目录

    开始学习<算法导论>了,虽然是本大部头,可能很难一下子看完,我还是会慢慢地研究的. 课后的习题和思考有些是很有挑战性的题目,我等蒻菜很难独立解决. 然后发现了Google上有挺全的algo ...

  9. 《算法导论》学习总结 — XX.第23章 最小生成树

    一.什么叫最小生成树 一个无向连通图G=(V,E),最小生成树就是联结所有顶点的边的权值和最小时的子图T,此时T无回路且连接所有的顶点,所以它必须是棵树. 二.为什么要研究最小生成树问题 <算法 ...

随机推荐

  1. vim emmet配置

    http://nerd-is.in/2013-12/learning-vim-again-1-install-vundle/ http://nerd-is.in/2013-12/learn-vim-a ...

  2. js复制黏贴

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...

  3. WordPress数据库中的表、字段、类型及说明

    wp_categories: 用于保存分类相关信息的表.包括了5个字段,分别是: cat_ID – 每个分类唯一的ID号,为一个bigint(20)值,且带有附加属性auto_increment. c ...

  4. Windows下虚拟Linux

    andlinux cygwin virtualbox VMware XenServer

  5. Linux内核spin_lock与spin_lock_irq分析

    http://blog.csdn.net/zhanglei4214/article/details/6837697

  6. Structs 原理图

    Struts开源架构很好的实现了MVC模式,MVC即Model-View-Controller的缩写,是一种常用的设计模式.MVC 减弱了业务逻辑接口和数据接口之间的耦合,以及让视图层更富于变化.MV ...

  7. absolute和relative的几个Demo

    这些例子最好通过FireFox结合FireBug调试查看 1.absolute让元素inline-block化 <!DOCTYPE html> <html xmlns="h ...

  8. Etcd学习(二)集群搭建Clustering

    1.单个etcd节点(测试开发用) 之前我一直开发测试一直是用的一个Etcd节点,然后启动命令一直都是直接打一个etcd(我已经将etcd安装目录的bin目录加入到PATH环 境变量中),然后启动信息 ...

  9. 结构体dict_table_t

    typedef struct dict_table_struct dict_table_t; /** Data structure for a database table. Most fields ...

  10. bzoj1084

    乍一看这题好难,根本不会: 直到看到1<=m<=2…… 显然分类讨论dp, 很快想到这题的dp和poj2430相类似 m=2的时候f[i,j,k]表示到第i行用了j个矩阵结尾状态为k时最大 ...