vlambda博客
学习文章列表

红黑树到底是何方神圣?

本文的主角是红黑树,像Java8中的HashMap、TreeMap、TreeSet都使用了这种数据结构,下面来带大家认识一下。


红黑树的基本概念

红黑树是许多平衡搜索树(比如,还有前面学到的AVL树)中的一种,可以保证在最坏情况下时间复杂度为O(logn)

红黑树是一颗二叉查找树,并且每个结点多了一个存储位来表示结点颜色。每个结点都有5个属性:

  • color:颜色

  • key:关键字

  • left:左孩子

  • right:右孩子

  • p:指针


一颗红黑树是满足如下性质的二叉查找树:
  • 每个结点要么是红色的,要么是黑色的。
  • 根结点一定是黑色的。
  • 叶子结点(NIL)一定是黑色的。
  • 如果一个结点是红色的,那么它的两个子结点都是黑色的。
  • 对每个结点x,从该结点到其所有后代叶子结点的简单路径,均包含相同个数的黑色结点, 黑色结点个数称为黑高 bh(x)。

为什么要满足这些"稀奇古怪"的性质呢?
  • 当然是为了平衡啦,但是红黑树与AVL树相比,它是弱平衡树,而AVL树是高度平衡树



红黑树的结构

如果一个结点没有子节点(即叶子结点)或没有父结点(即根结点),则该结点的指针属性为NIL,NIL是指针,且指向外部结点(称为NIL结点)。非NIL结点称为树的内部结点。

外部结点与内部结点如下图所示(内部结点旁的数字表示黑高,NIL结点黑高为0)。


为什么需要NIL结点呢,是因为红黑树的代码处理需要设置一个边界条件。

然而,如果有大量的NIL结点,肯定会浪费空间,NIL结点虽然也是一个普通结点,但仅仅只是color属性为黑色,其它属性没有任何意义,也不存储数据,因此可以用一个哨兵结点T.nil替换。

哨兵结点与内部结点如下图所示。


一颗有n个内部结点的红黑树高度至多为2log(n+1),这也是为什么红黑树能将时间复杂度控制在O(logn)以内。至于为什么是2log(n+1)大家可以查阅相关资料,比如算法导论一书中就有相关证明。


红黑树的结点插入和删除操作会破坏红黑树的性质,为了维护这些性质,就必须要改变树中某些结点的颜色以及指针结构,指针结构是通过旋转来完成的

红黑树的插入和删除颇有些复杂,这里就不详细介绍了,下面主要介绍红黑树与AVL树的对比,这个比较重要。



红黑树与AVL树的对比


RB-Tree和AVL树作为BBST(平衡二叉查找树),其算法时间复杂度都为O(logn),AVL作为最先提出的BBST,貌似RB-tree实现的功能都可以用AVL树是代替,那么为什么还需要引入RB-Tree呢?


AVL优缺点总结:
  • AVL是严格平衡树,因此在增加或者删除节点的时候,旋转的次数比红黑树要多

  • 由于AVL高度平衡,因此AVL的Search效率更高

  • AVL更平衡,结构上更加直观,但维护稍慢,空间开销较大。


红黑树优缺点总结:
  • 红黑树不追求完全平衡,即不像AVL那样要求结点的左右子树高度差<=1。它只要求部分达到平衡,提出了为结点增加颜色,红黑是用弱平衡来换取增删结点时旋转次数的降低

  • 针对增删结点导致失衡后的rebalance操作,红黑树能够提供一个比较便宜的解决方案,降低开销,是对search、insert 、delete效率的折衷,总体来说,RB-Tree的统计性能高于AVL。

  • 引入RB-Tree是功能、性能、空间开销的折中结果。

  • 红黑树,读取略逊于AVL(比AVL会稍微不平衡最多一层,即红黑树只比相同内容的AVL树最多多一次比较),维护强于AVL,空间开销与AVL类似,内容极多时略优于AVL,维护优于AVL。


总结:
  • 基本上主要的几种平衡树看来,红黑树有着良好的稳定性和完整的功能,性能表现也很不错,综合实力强。

  • 实际应用中,若搜索的次数远远大于插入和删除,那么选择AVL,如果搜索,插入删除次数几乎差不多,应该选择RB。



看完本文后,能对红黑树有一个初步的了解,至少知道它是干嘛的,优点是什么,什么时候用它。但是本文没有介绍红黑树的原理,即为什么它能将时间复杂度控制在O(logn)以内,以及增删结点后违背红黑树性质又该如何调整。