1、概述
RB-tree(红黑树) 是除了 AVL-tree 之外,另一个被广泛使用的平衡二叉搜索树。所谓 RB-tree,不仅是一个二叉搜索树,而且必须满足以下规则:
1、每个节点不是红色就是黑色。
2、根节点为黑色。
3、如果节点为红,其子节点必须为黑。
4、任一节点至NULL(树尾端)的任何路径,所含之黑节点数必须相同。
根据规则4,新增节点必须为红;根据规则3,新增节点之父节点必须为黑。当新节点根据二叉搜索树的规则达到其插入点,却未能符合上述条件时,就必须调整颜色并旋转树形。
2、插入节点操作前述
假设我们在上图所示的 RB-tree 中分别插入 3,8,35,75,根据二叉搜索树的规则,这四个新节点的落脚处应该如下图所示:
他们都破坏了 RB-tree 的规则,因此我们必须调整树形,也就是旋转树形并改变节点颜色。
为了方便讨论,这里整理一些代名词。
X:新节点,P:父节点,G:祖父节点,S:伯父节点,GG:曾祖父节点。
根据二叉搜索树的规则,新节点 X 必为叶节点。根据红黑树的规则 4,X 必为红。
若此时 P 亦为红(这就违反了规则 3,必须调整树形),则 G 必为黑(因为原为 RB-tree,必须遵循规则3,隐含父子节点不不得同时为红)。于是,根据 X 的插入位置及外围节点(S 和 GG)的颜色,有了以下四种情况:
情况1:S 为黑且 X 为外侧插入。我们先对 P,G 做一次单旋转,并更改 P,G 颜色,即可重新满足红黑树的规则 3。
情况2:S 为黑且 X 为内侧插入。我们必须先对 P,X 做一次单旋转,并更改 G,X 颜色,再将结果对 G 做一次单旋转,即可满足红黑树规则。如下图:
情况3:S 为红, X 为外侧插入且 GG 为黑。先对 P 和 G 做一次单旋转,并改变 X 的颜色。
情况4:S 为红, X 为外侧插入且 GG 为红。先执行情况 3 中的步骤,因为改变了颜色,GG 亦为红,还要持续网上做,直到不再有父子节点同时为红的情况。
3、由上而下的程序
为了避免情况 4 “父子节点皆为红色” 的情况持续向 RB-tree 的上层结构发展,形成处理时效上的瓶颈,我们可以施行一个由上而下的程序:此程序的目的是通过提前的修改,把情况 4 给规避掉。假设新增节点为 A,那么就延着 A 的路径,只要看到有某节点 X 的两个子节点皆为红色,就把 X 改为红色,并把两个子节点改为黑色。如下图所示:
改掉之后,如果 X 的父节点 P 亦是红色(注意,此时 S 绝不可能为红),就得像情况 1 一样做一次单旋转并改变颜色,或是像情况2 一样做一次双旋转并改变颜色。
在此之后,节点 35 的插入就很单纯了:要么直接插入,要么插入后在做一次单或者双旋转就可以了。
4、RB-tree 的节点设计
RB-tree 有红黑二色,并且拥有左右子节点。由于 RB-tree 的各种操作时常需要上溯其父节点,所以特别在数据结构中安排了一个 parent 指针。为了有更大的弹性,节点分为两层。如下所示:
typedef bool