silverlight   发布时间:2022-05-04  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了红黑树大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
@H_616_3@概述 今天我们来介绍另一种平衡二叉树:红黑树(Red Black Tree),红黑树由Rudolf Bayer于1972年发明,当时被称为平衡二叉B树(symmetric binary B-trees),1978年被Leonidas J. Guibas 和 Robert Sedgewick改成一个比较摩登的名字:红黑树。 红黑树和之前所讲的AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找

今天我们来介绍另一种平衡二叉树:红黑树(Red Black Tree),红黑树由Rudolf Bayer1972年发明,当时被称为平衡二叉B树(symmetric binary B-trees),1978年被Leonidas J. Guibas Robert Sedgewick改成一个比较摩登的名字:红黑树。

红黑树和之前所讲的AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。自从红黑树出来后,AVL树就被放到了博物馆里,据说是红黑树有更好的效率,更高的统计性能。不过在我了解了红黑树的实现原理后,并不相信这是真的,关于这一点我们会在后面进行讨论。

红黑树和AVL树的区别在于它使用颜色来标识结点的高度,它所追求的是局部平衡而不是AVL树中的非常严格的平衡。之前我们在讲解AVL树时,已经领教过AVL树的复杂,但AVL树的复杂比起红黑树来说简直是小巫见大巫。红黑树是真正的变态级数据结构。

@H_616_3@红黑树的平衡

红黑树首先是一棵二叉查找树,它每个结点都被标上了颜色(红色或黑色),红黑树满足以下5个性质:

1、 每个结点的颜色只能是红色或黑色。

2、 根结点是黑色的。

3、 每个叶子结点都带有两个空的黑色结点(被称为黑哨兵),如果一个结点n的只有一个左孩子,那么n的右孩子是一个黑哨兵;如果结点n只有一个右孩子,那么n的左孩子是一个黑哨兵。

4、 如果一个结点是红的,则它的两个儿子都是黑的。也就是说在一条路径上不能出现相邻的两个红色结点。

5、 对于每个结点来说,从该结点到其子孙叶结点的所有路径上包含相同数目的黑结点。

红黑树的这5个性质中,第3点是比较难理解的,但它却非常有必要。我们看图1中的左边这张图,如果不使用黑哨兵,它完全满足红黑树性质,结点50到两个叶结点8和叶结点82路径上的黑色结点数都为2个。但如果加入黑哨兵后(如图1右图中的小黑圆点),叶结点的个数变为8个黑哨兵,根结点50到这8个叶结点路径上的黑高度就不一样了,所以它并不是一棵红黑树。

红黑树

 

 

要看真正的红黑树请在以上动画中添加几个结点,看看是否满足以上性质。

@H_616_3@红黑树的旋转操作

红黑树的旋转操作和AVL树一样,分为LLRRLRRL四种旋转类型,差别在于旋转完成后改变的是结点的颜色,而不是平衡因子。旋转动画演示请参AVL这篇文章中的Flash动画:

http://www.cnblogs.com/abatei/archive/2008/11/17/1335031.html

@H_616_3@红黑树上结点的插入

在讨论红黑树的插入操作之前必须要明白,任何一个即将插入的新结点的初始颜色都为红色。这一点很容易理解,因为插入黑点会@L_197_14@某条路径上黑结点的数目,从而导致整棵树黑高度的不平衡。但如果新结点父结点为红色时(如图2所示),将会违返红黑树性质:一条路径上不能出现相邻的两个红色结点。这时就需要通过一系列操作来使红黑树保持平衡。

 

红黑树

 

 

为了清楚地表示插入操作以下在结点中使用“新”字表示一个新插入的结点;使用“父”字表示新插入点的父结点;使用“叔”字表示“父”结点的兄弟结点;使用“祖”字表示“父”结点的父结点。插入操作分为以下几种情况:

1黑父

如图3所示,如果新点的父结点为黑色结点,那么插入一个红点将不会影响红黑树的平衡,此时插入操作完成。红黑树比AVL树优秀的地方之一在于黑父的情况比较常见,从而使红黑树需要旋转的几率相对AVL树来说会少一些。

 

红黑树

 

 

2.红父

如果新点的父结点为红色,这时就需要进行一系列操作以保证整棵树红黑性质。如图3所示,由于父结点为红色,此时可以判定,祖父结点必定为黑色。这时需要根据叔父结点的颜色来决定做什么样的操作。青色结点表示颜色未知。由于有可能需要根结点到新点的路径上进行多次旋转操作,而每次进行不平衡判断的起始点(我们可将其视为新点)都不一样。所以我们在此使用一个蓝色箭头指向这个起始点,并称之为判定点。

 

@H_673_252@

 

 

@H_49_262@2.1 红叔

当叔父结点为红色时,如图4所示,无需进行旋转操作,只要将父和叔结点变为黑色,将祖父结点变为红色即可。但由于祖父结点的父结点有可能为红色,从而违反红黑树性质。此时必须将祖父结点作为新的判定点继续向上进行平衡操作。

红黑树

 

 

需要注意,无论“父”在“叔”的左边还是右边,无论“新”是“父”的左孩子还是右孩子,它们的操作都完全一样。

 

@H_49_262@2.2 黑叔

当叔父结点为黑色时,需要进行旋转,以下图示了所有的旋转可能

情形1

 

红黑树

 

情形2

红黑树

 

 

 

 

  (2010-3-25 注意:经网友BourneHan的指正,现已确定3.2.1和3.2.2中的新结点应为黑色,而不是现在的不确定颜色。基于以下2点原因,我并不打算代码及博文中更改这个错误

   1、这个错误代码及动画的正确性没有影响。

   2、之前的代码及动画经过了大量测试,需要花很多时间,更改代码意味着重新测试,现在的确抽不出这么多时间来做这项工作。

这里只能对各位读者说声对不起了,最快的补救方法就是在此点出错误,让读者明了。)

 

3.2.3 黑兄红侄

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

情形1

 

红黑树

 

情形2

@H_652_404@

 

情形3

 

@H_306_419@

 

 

情形4

 

红黑树

 

 

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

@H_616_3@红黑树的代码实现

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

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

下面把代码贴出来,如果理解了上面所讲内容是很容易读懂这些代码的。

大佬总结

以上是大佬教程为你收集整理的红黑树全部内容,希望文章能够帮你解决红黑树所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。
标签: