红黑树

来源:互联网 发布:360游戏优化有用吗 编辑:程序博客网 时间:2024/06/07 01:26

转载:http://hi.baidu.com/20065562/item/8ba7d823d1310d172b0f1caf

红黑树由来:

        他是在1972年 由Rudolf Bayer发明的,他称之为“对称二叉B树”,它现代的名字是Leo J. Guibas和 Robert Sedgewick 于1978

年写的一篇论文中获得的。它是复杂的,但它的操作有着良好的最坏情况运行时间,并且在实践中是高效的: 它可以在O(log n)时间内做查找,

插入和删除,这里的n 是树中元素的数目。


红黑树性质:

1. 每个结点或红或黑。

2. 根结点为黑色。

3. 每个叶结点(实际上就是NULL指针)都是黑色的。

4. 如果一个结点是红色的,那么它的周边3个节点都是黑色的。

5. 对于每个结点,从该结点到其所有子孙叶结点的路径中所包含的黑色结点个数都一样。


讨论的前提:

1,我们只讨论往树的左边和从树的左边删除的情况,与之对称的情况一样。

2,假设我们要删除一个元素的方法都是采取删除后继节点,而非前驱节点。

3,NL或全黑表示黑空节点,也可以不用画出。

4,“=>”这个符号我们用来表示“变成”的意思。

一. 插入

当红黑树中没有节点的时候,新节点直接涂黑就可以了。

当树中已有节点,我们就将新节点涂红,并且底下挂两个黑空节点。

1.1 新节点的父亲为黑色

这种情况最简单,只接插入将新节点可以了,不会出现红色警戒。

1.2 新节点的父亲为红色

这种情况出现红色警戒,并且通过红色的父亲,我们可以推出一定有一个黑色的父, 并且父亲不可能为树根(树根必须为黑)。

这种情况需要修复。

1.2.1 新节点的叔叔是红色。(红叔)

图1.2.1-1


图1.2.1-2

注解:在这种情况下,我们可以通过这样的方式来修复红黑树的性质。因为遇到红 色警戒所以我们首先可以想到的就是将父亲变成黑色,但这样祖父的左子树的黑高 就增加了,为了达到祖父的平衡,我们红叔变成黑色,这样祖父就平衡了。但是整 个祖父这颗树的高度增高了,所以再此将祖父的颜色变成红色来保持祖父这颗树的 高度不变。因为祖父变成了红色,因此往上遍历。

      方法:父=>黑;叔=>黑;祖=>红;往上遍历;

1.2.2 新节点的叔叔是黑色(黑叔)

图1.2.2-1

图1.2.2-2

注解:首先可以说明的是,这种情况下我们都可以通过改变颜色和旋转的方式到达 平衡,并且不再出现红色警戒,因为无需往上遍历。图1.2.2-1:首先我们将红父变 成黑色,祖父变成红色,然后对祖父进行右旋转。这样我们可以看到,整颗树的黑 高不变,并且这颗树的左右子树也达到平衡。新的树根为黑色。因此无需往上遍历。

方法:图1.2.2-1  父=>黑;祖=>红;祖父右旋转;

                图1.2.2-2   新=>黑;祖=>红;父左旋转;祖右旋转;


插入我们就算做完了,与之对称的右边插入的情况完全一样,请自己下来分析;

一. 删除

删除是比较经典但也是最困难的一件事了,所以我们必须一步一步地理解。为了中途思想不混乱,请始终记住一点,下面我们删除的节点都已经表示的是实际要删除的后继节点了。因此可以得出一下结论。

    首先,可以肯定的是我们要删除的节点要么是红色,要么是黑色。

    其次,既然我们要删除的结点是后继节点,那么要删除的节点的左子树一定为空。所以当删除的节点为黑色时只剩下两种情况。

    最后,如果删除的节点为红色,那么它必为叶子节点。(自己好好想象为什么)。

请在看下面的内容前先牢记上面的结论,这样更加方便让你理解下面的知识。

a:当删除的节点为黑色时

              删黑a               删黑b


b:当删除的节点为红色时

     上面的几附图都是很简单的。因为你可以将空节点 去掉不看。所就形成了要 删除的节点要么有一个右孩子,要么为叶子节点。


下面我们就开始真正的删除操作了。

2.1 删除红色节点

注解:这种情况是最简单的,因为根据我们可以推出子节点     一定为空, 也就是说删除的红色节点为叶子节点。只需将这个旧节点的右孩子付给父亲的左孩子就 可以了,高度不变。

方法:略


2.2 删除黑色节点

         遇到黑色节点情况就将变得复杂起来,因此我们一定要慢慢来,仔细分析。

         2.2.1当删除的节点有一个红色子孩子

注解:这种情况其实就是上面我们分析的三种情况之一,如图""。这种情况 是非常简单的,只需将旧节点的右孩子取代旧节点,并将子孩子的颜色变为黑色, 树平衡;

       方法:子取代旧;子=>黑;

2.2.2当删除的节点无左右孩子

这种情况其实就是上面我们分析的三种情况之一,如图"删黑a"。我们推出子节点 一定为空,请务必记住这点,不然在后面你将很容易被混淆,父亲的颜 色我们标记为绿色,是表示,父亲的颜色可为红或黑。黄色的子节点其实就 是一个黑空节点,这样方便后面分析。

a:红兄


注解:当我们删除的节点拥有一个红色的兄弟时,这种情况还相对比较简单,因为 兄弟为黑色,我们可以推出父亲一定为黑色。因为父节点的左树高度减一,所 以我们可以通过左旋转父节点,来将节点1旋转到左边来恢复左边的高度。然 后将父变成红,兄变成黑。这样整颗树的高度不变,父节点也平衡记住子节点为空所以可以删除看,这样便于理解。     

       方法:父=>红;兄=>黑;左旋转父节点;

删除另类版本:更加全面


1、旧点为红色结点

若旧点为红色结点,则它必定是叶子结点,直接删除即可。如图11所示

 

 

 

2、一红一黑

当旧点为黑色结点,新点为红色结点时,将新点取代旧点位置后,将新点染成黑色即可(如图12所示)。这里需要注意:旧点为红色,新点为黑色的情况不可能存在。

 

 

 

3、双黑

当旧点和新点都为黑色时(新点为空结点时,亦属于这种情况),情况比较复杂,需要根据旧点兄弟结点的颜色来决定进行什么样的操作。我们使用“兄”来表示旧点的兄弟结点。这里可分为红兄和黑兄两种情况:

3.1 红兄

由于兄弟结点为红色,所以父结点必定为黑色,而旧点被删除后,新点取代了它的位置。下图演示了两种可能的情况:

 

 

 

红兄的情况需要进行RRLL型旋转,然后将父结点染成红色,兄结点染成黑色。然后重新以新点为判定点进行平衡操作。我们可以观察到,旋转操作完成后,判定点没有向上回溯,而是降低了一层,此时变成了黑兄的情况。


自己备注:

接着上面图12上面那个左旋操作之后新(空,即深度必为1)比黑兄1(必有左右两个黑孩子,即深度必为2)深度少一;所以得将1变红,之后红1和红父,为红红相

连,且以父为根节点的树高度减少1,为使整棵树需,所有将父亲染黑即可!


3.2 黑兄

黑兄的情况最为复杂,需要根据黑兄孩子结点(这里用“侄”表示)和父亲结点的颜色来决定做什么样的操作。

3.2.1 黑兄二黑侄红父

如图14所示,这种情况比较简单,只需将父结点变为黑色,兄结点变为黑色,新结点变为黑色即可,删除操作到此结束。

 

 

 

3.2.2 黑兄二黑侄黑父

如图15所示,此时将父结点染成新结点的颜色,新结点染成黑色,兄结点染成红色即可。当新结点为红色时,父结点被染成红色,此时需要以父结点为判定点继续向上进行平衡操作。

 


3.2.3 黑兄红侄

黑兄红侄有以下四种情形,下面分别进行图示:

情形1:

 

 

情形2:

 

情形3:

 

 

情形4:

 

 

 

由以上图例所示,看完以上四张图的兄弟有可能会有一个疑问,如果情形1和情形2中的两个侄子结点都为红色时,是该进行LL旋转还是进行LR旋转呢?答案是进行LL旋转。情形3和情形4则是优先进行RR旋转的判定。

红黑树的代码实现

本以为红黑树的代码非常容易,因为System.Collections.Generic.SortedDictionary类就是使用红黑树实现的,把代码的算法部分抠出来就搞定了。但看了SortedDictionary源码后有些失望,C#中真正实现红黑树的是TreeSet类,SortedDictionary只是在TreeSet的基础上进一步抽象,加上了Key/Value泛型对。TreeSet使用了一种新的红黑树算法,它在搜索插入点和删除点时预先进行旋转和染色操作,从而避免插入和删除后的回溯。这种算法看上去很美,但仔细想想,如果插入的是一个已经存在的结点,删除的结点并不存在,那这些预平衡处理不是白做了吗?更可怕的是如果在一条路径上间隔进行一次插入和一次删除,而这些操作没有命中目标,那么大家就会看到结点的颜色变来变去,这些都是无用功。来看看在寻找插入和删除点的路径上TreeSet每前进一步都要做些什么:给四个变量赋值;判断每个结点的两个孩子结点的颜色。这种算法在《java数据结构和算法》这本书中有详细讲述,不过只讲解了插入算法。另外国内也专门有一篇论文描述这个算法,他的测试结果是这种算法优于其他算法,估计测试时没有不命中目标的情况发生。总之我并不相信这是一个好的算法。

为了证实我的想法,我不得不自己实现红黑树,实现思路跟AVL树很类似,也是使用一个数组保存访问路径以进行回溯,当然,考虑到红黑树不严格的平衡,数组的长度设为64,这并不会给性能带来什么影响。过程很艰辛,需要做大量测试。很不幸,写完后继续做红黑树的Silverlight动画时不小心把原来的代码给覆盖掉了,结点删除部分的代码丢失。当时几乎崩溃,不过重写并没有我想象的那么困难,很快完成,感觉思路清晰了很多,实现比原来也有了改进,感谢上帝!


原创粉丝点击