STL 源码分析: RB_tree 红黑树 (一)

前言

今天,来看看大名鼎鼎的红黑树。

红黑树的定义

啥子叫做红黑树?

首先,红黑树是一颗二分查找树。二分查找树的递归定义是,一个树是二叉查找树,它的左右子树也是二叉查找树,而且根节点的数值域大于所有左子树子孙数值域,小于等于所有右子树数值域。

其次,红黑树是一颗平衡二分查找树。啥叫平衡二分查找树呢?
wiki是这么说的:
In computer science, a self-balancing (or height-balanced) binary search tree is any node-based binary search tree that automatically keeps its height (maximal number of levels below the root) small in the face of arbitrary item insertions and deletions.

平衡二叉树,是指树的高度是自平衡的,而且树的高度不会因为元素的插入、删除而变得特别大。
我们知道,一个拥有n个节点的二叉树,它的高度至少也是 l o g n logn 的。
如果某颗查找树,它的高度也是 O ( l o g n ) O(logn) 的,那么这颗查找树就是个平衡二分查找树。

为了能够控制住,查找树的高度,我们当然不能让树随便长,而应该给他加上一些约束条件让它的高度是 O ( l o g n ) O(logn) 的,注意这里只是要求 O ( l o g n ) O(logn) 意味着只要树的高度渐进与节点个数的对数就可以了。

AVL树是通过约束任意一个节点的两颗子树的高度差不能太大来实现整颗树的高度是 O ( l o g n ) O(logn) 的,AVL树因此需要维护每颗子树的高度。

而红黑树则是通过对节点进行着红色、黑色,通过颜色的某种约束来实现树的高度为 O ( l o g n ) O(logn) 的目的的。

具体来说,红黑树在二叉查找树的基础上还具有以下几个约束:
A. 每个节点,只有两个颜色。非红即白。
B. 根节点为黑色。
C. 不可出现父子节点同时为红的情况。但是可以出现父子节点同时为黑的情况。
D. 对于红黑树的任一节点,从该节点出发,到达其 NIL 子孙的路径上的黑色节点个数(节点本身除外)是相同的。这个节点的任意简单路径上的黑色节点个数又叫做黑高。NIL 子孙就是各个节点的NULL指针,NULL指针本身被当做黑色。

上图就是一个红黑树。我们记 b h ( x ) bh(x) 表示节点x的黑高。
于是在上图里面, b h ( 5 ) = 1 , b h ( 10 ) = 1 , b h ( 15 ) = 2 , b h ( 30 ) = 3 , b h ( 85 ) = 1 bh(5)=1,bh(10)=1,bh(15)=2,bh(30)=3,bh(85)=1 ,注意在计算节点 x x 黑高的时候,无需考虑 x x 自身的颜色。而树根的黑高 b h ( 30 ) = 3 bh(30)=3 又叫做整个红黑树的黑高。

为啥子,通过这几个颜色的约束就能把红黑树的高度限制在 O ( l o g n ) O(logn) 呢? 我们来证明一下。

红黑树高度为O(logn)的证明

首先,我们证明一个结论:如果某个节点 x x 的黑高为h,那么以 x x 为根的子树至少有 2 h 1 2^h-1 个节点。
这玩意,当然得用数学归纳法证明啦。
x x 子树只有一个节点的时候, b h ( x ) = 0 bh(x)=0 , 2 h 1 = 1 1 = 0 2^h-1=1-1=0 ,满足条件。
假设 x x 作为子树,它的黑高为h,它至少包含 2 h 1 2^h-1 个节点。
那么 x x 的父节点所在的子树的黑高应该 h + 1 h+1 h h 。这是为啥呢?如果x是黑色的,那么x的父亲的黑高是h+1,如果x是红色的,那么x父亲的黑高为h。同时,x的兄弟的黑高也是h,(这是因为,对于x的父亲来说,从父亲出发,不管是经过x也好,经过x的兄弟也好,到达nil节点的黑色节点个数都得相等)。于是以x的父节点为根的子树的节点个数就等于左子树节点个数加右子树节点个数,就至少有 2 h 1 + 2 h 1 + 1 = 2 h + 1 1 2^h-1+2^h-1 +1=2^{h+1}-1 个节点。不管父节点的黑高 b h bh 是h 还是h+1,以它为根的子树的节点个数都至少为 2 h 1 + 2 h 1 + 1 = 2 h + 1 1 2^h-1+2^h-1 +1=2^{h+1}-1 ,都满足节点个数至少为 2 b h 1 2^{bh}-1 这个条件。

于是 如果某个节点 x x 的黑高为h,那么以 x x 为根的子树至少有 2 h 1 2^h-1 个节点 这个结论是成立的。

接着,我们证明第二个结论:如果某颗红黑树的黑高为 h h ,那么这棵树的高度 l l 至少为 h h ,至多为 2 h 2h .

首先,既然黑高都为h了,那么树高 l l 当然最小就只能到 h h 了,这种情况发生于整个树全部都是黑色的情况。例如下图,30的黑高为2,那么树高当然最小为2.
在这里插入图片描述
OK,为了增加树高,那么就只能往每一条全部黑色的路径上插入红色的节点了。那么对于一条从根到NIL的含有h个黑色节点的简单路径,一共形成了 h h 个插入点。能插入多少个红色节点呢? 考虑到一条路径上不能出现连续的红色,于是每个插入点最多插一个红色节点。在这里插入图片描述于是,在这条全黑的路径上,全部最多只能插入 h h 个红色节点。
于是,我们得到 l l 最大只能为 2 h 2h 的上界。

现在,综合这两条结论。对于一个含有 n n 个节点的红黑树,假设它的黑高为 b h bh ,那么有 2 b h 1 < = n 2^{bh}-1 <=n ,于是 b h < = l o g ( n + 1 ) bh <=log(n+1) ,于是这颗树的树高 l l 应该有 l < = 2 l o g ( n + 1 ) l<=2*log(n+1) ,即树高是 O ( l o g n ) O(logn) 的。

因此,只要在红黑树的插入、删除过程中,保证这4条性质的成立就能保证树的高度是 O ( l o g n ) O(logn) 的。

接下来,就看STL的实现中是如何满足这四条性质的。