红黑树
红黑树的四个性质:
(1)每个节点要么红要么黑。
(2)根节点黑色。
(3)每个叶子节点是黑色。【叶子节点指的是NIL或者NULL的叶子节点】。
(4)如果一个节点是红色的,那么它的叶子节点必须是黑色的。
(5)任一节点至NULL(树尾端)的任何路径,所包含的黑节点数必须相同。
这些性质强制了红黑树的关键性质: 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。主要原因在于,属性4导致了路径不能有两个毗连的红色节点,最短的可能路径都是黑色节点,最长的可能路径有交替的红色和黑色节点,根据属性5所有最长的路径都有相同数目的黑色节点,这就表明了没有路径能多于任何其他路径的两倍长。
RM-tree不是严格的平衡树,比AVL-tree弱,然而RB-tree通常能够导致的平衡状态。所以说红黑树是接近平衡的二叉搜索树。
RB-tree的构造和内存管理
RB-tree的构造方式有两种,一种是以现有的RB-tree复制一个新的RB-tree,另一种是产生一棵空树。
//默认构造函数
rb_tree(const Compare& comp = Compare())
: node_count(0), key_compare(comp) { init(); }
rb_tree(const rb_tree<Key, Value, KeyOfValue, Compare, Alloc>& x)
: node_count(0), key_compare(x.key_compare)
{
header = get_node();
color(header) = __rb_tree_red;
if (x.root() == 0) {
root() = 0;
leftmost() = header;
rightmost() = header;
}
else {
__STL_TRY {
root() = __copy(x.root(), header);
}
__STL_UNWIND(put_node(header));
leftmost() = minimum(root());
rightmost() = maximum(root());
}
node_count = x.node_count;
}
~rb_tree() {
clear();
put_node(header);
}
默认构造函数中的init()是实现技巧上的一个关键点:
void init() {
header = get_node();//产生一个节点空间,令header指向它
color(header) = __rb_tree_red; // 令header为红色,以区分header和root
root() = 0; //root() {header->parent;}
leftmost() = header; //令header的左子节点为自己
rightmost() = header; //令header的右子节点为自己
}
树状结构的各种操作,最主要的就是处理边界情况,也就是走到根结点时要有特殊的处理。为了简化处理,SGI STL特别为根结点设计了一个父节点,名为header
下面是header的初始状态
接下来,每当插入新节点时,不但要依照RB-tree的规则来调整,并且维护header的正确性,使其父节点指向根结点,左子节点指向最小节点,右子节点指向最大节点。
节点插入带来的影响(如何旋转及换色)
为了方便讨论,假设新节点为X,其父节点为P,祖父节点为G,伯父节点为S,曾祖父节点为GG。现在,根据红黑树规则4,X必为红。若P亦为红,则G必为黑。于是,根据X的插入位置及外围节点(S和GG)的颜色,有以下四种考虑。
状况1:S为黑且X为外侧插入------先对P,G做一次单旋转,并更改P,G颜色。
状况2:S为黑且X为内侧插入------先对P,X做一次单旋转并更改X,G颜色,再将结果对G做一次单