算法导论学习-RED-BLACK TREE

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

-------------------------------------算法

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

BST达到最平衡的状态称之为AVL。在AVL树中任何节点的两个儿子子树的高度最大差异为一,查找、插入和删除在平均和最坏状况下都是O(lg n)。但由于增长和删除节点可能会破坏“平衡状态”,因此大多数状况下须要经过屡次树旋转来从新平衡这个树。因此简单地说,若是你的应用查找次数远远多于增删操做,那么AVL是最好的,可是若是增删次数和查找次数不相上下时,RBT由于相比AVL没有过多的旋转操做,效率要比AVL高。而且在是实际状况中,RBT的应用也更为普遍。至少《intro to algo》这本书上主要讲了RBT。函数

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

------------------------3d

由于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)从任一节点到其每一个叶子的全部路径都包含相同数目的黑色节点。 以下图所示:code

  

3. 旋转:htm

--------blog

由于在执行操做或者删除操做之后,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 LEFT-ROTATE(T, x)
 2 y=x.right
 3 x.right=y.left
 4 if y.left != T.nil
 5         y.left.p=x
 6 y.parent=x.parent
 7 if x.parent==T.nil
 8         T.root=y
 9 else if x==x.parent.left
10         x.parent.left=y
11 else x.parent.left=y
12 y.left=x
13 x.parent=y
View Code

 

 4. 插入:

---------

红黑树的插入和BST基本相似。不清楚BST插入算法的能够参考个人这一篇博客:http://www.cnblogs.com/fu11211129/p/4214047.html

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

 

TREE-INSERT(T, z)//BST插入算法
1 y=NIL
2 x=T.ROOT
3 while(x!=NIL)
4   y=x
5   if(z.key<x.key)
6     x=x.left
7   else x=x.right
8 z.p=y
9 if(y==NIL) T.ROOT=z
10 else if(z.key<y.key) y.left=z
11 else y.right=z
View Code

 

 

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

 

对比两个插入算法,咱们能够注意到四个不一样,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的一致,你们能够参照博

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

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

你们能够发现只有第四行不同,缘由很简单,RBT把NIL看作一个节点了。

7. 删除:

--------

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

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

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

由于相比BST删除算法,就是加入一些节点颜色的处理机制,因此这里再也不赘述。你们理解了BST的删除后,RBT也就差很少了。

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

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

仍是先贴上伪代码吧,而后结合代码作个分析。

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

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

状况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,先是成功篡权,而后将本身的手下换成对本身中心的人(黑色

相关文章
相关标签/搜索