概述
红黑树是关联式容器set
、map
、multiset
、multimap
的底层实现。
二叉搜索树
BST(Binary Search Tree)
插入、删除、查找时间复杂度都是 **O(log n)
** ,但树不平衡的话其性能会变差,最差变为O(n)
.
二叉搜索树的节点放置规则是:**任何节点的键值一定大于其左子树中的每一个节点的键值、并小于其右子树中的每一个节点的键值。**因此,从根节点一直往左走,直至无左路可走,即得最小元素:从根节点一直往在走,直全无右路可走,即得最大元素。
二叉搜索树的插入删除
插入操作:
插人新元素时,可以根节点开始,遇键值较大者就向左,遇键值较小者就向右,一直到尾端,即为插人点。
删除操作:
分两种情况:如果 A只有一个子节点,我们就直接将 A的子节点连至A的父节点,并将A删除。如果A 有两个子节点,我们就以有子树内的最小节点取代A。注意,右子树的最小节点极易获得:从右子节点开始 (视为右子树的根节点),一直向左走至底即是。
平衡二叉树
AVL树具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。在平衡二叉搜索树中,我们可以看到,其高度一般都良好地维持在O(log2n),其各操作的时间复杂度(O(log2n))同时也由此而决定,大大降低了操作的时间复杂度。另外,最小二叉平衡树的节点的公式如下 F(n)=F(n-1)+F(n-2)+1 这个类似于一个递归的数列。
插入操作:
插人新元素时,可以根节点开始,遇键值较大者就向左,遇键值较小者就向右,一直到尾端,即为插人点。
当插入一个节点导致二叉树的平衡状态被打破,就需要调整“插入点到根节点”路径上 平衡被破坏的各节点最深的节点,便可使整棵树重新获得平衡。假设该最深节点为X,由于节点最多拥有两个字节点,而所谓“平衡被破坏”意味着X的左右两棵子树的高度相差2,情况大概分为四种:
- 插人点位于叉的左子节点的左子树——左左。
- 插人点位于×的左子节点的右子树——左右。
- 插人点位于又的右子节点的左子树——右左。
- 插人点位于 X的右子节点的右子树—— 右右。
情况1、4彼此对称,称为外侧插入,可以采用单旋转操作调整解决。
情况2、3彼此对称,称为内侧插入,可以采用双旋转操作调整解决。
删除导致失衡的操作先保留
有了二叉查找树和平衡二叉树,为什么还要设计红黑树?
二叉查找树的特点是:左子树的节点值比父亲节点小,而右子树的节点值比父亲节点大。可能出现一种极端的情况,输入一个有序链表时,二叉查找树就近似退化为一条链表,这样的二叉查找树的查找时间变成了O(n)。
平衡二叉树的特点是:具有二叉查找树全部特性的同时,每个节点的左子树和右子树的高度差至多等于1。这样就有效避免了上面二叉查找树出现的情况,能够把查找的时间控制在O(logn),不过却不是最佳的。因为平衡树要求每个节点的左子树和右子树的高度差至多等于1,这个要求实在是太严了,导致每次进行插入/删除节点的时候,几乎都会破坏平衡树的第二个规则,进而我们都需要通过左旋和右旋来进行调整,使之再次成为一颗符合要求的平衡树。显然,如果在那种插入、删除很频繁的场景中,平衡树需要频繁着进行调整,这会使平衡树的性能大打折扣。为了解决这个问题,于是有了红黑树。
红黑树
红黑树是一个平衡二叉搜索树。而且必须满足以下规则:
- 每个节点不是红色就是黑色(图中深色底纹代表黑色,浅色底纹代表红色,下同)。
- 根节点为黑色。
- 如果节点为红,其子节点必须为黑。
- 任一节点至 NULL(树尾端)的任何路径,所含之黑节点数必须相同。
**根据规则4,新增节点必须为红:根据规则3,新增节点之父节点必须为黑。**当新节点根据二叉搜素树的规则到达其插人点,却未能符合上述条件时,就必须调整颜色并旋转树形。
插入节点
红黑树插入删除节点后,迭代器可能会失效。
假设新节点为X,其父节点为 P,祖父节点为G,伯父节点(父节点之兄弟节点)为S,曾祖父节点为GG。现在,根据二叉搜索树的规则,新节点X必为叶节点。根据红黑树规则4,X必为红。若P亦为红(这就违反了规则了,必须调整树形),则G必为黑(因为原为RB-tree,必须遵循规则了)。于是,根据X的插人位置及外围节点(S和 GG)的颜色,有了以下四种考虑:
状况1:
S 为黑且X为外侧插人。对此情况,我们先对 P,G 做一次单旋转,再更改P,G 颜色,即可重新满足红黑树的规则了。
旋转以及改变节点颜色的目的是让二叉树满足红黑树的条件。
注意,此时可能产生不平衡状态(高度相差1以上)例如图中的A和B为pull,D或E不为null。这倒没关系,因为 RB-tree的平衡性本来就比 AVL-tree弱(**红黑树不用刻意维持高度差的绝对值小于1也能保持良好的平衡状态。**是的,经验告诉我们,RB-tree 的搜寻平均效率和 AVL-tree 几乎相等。
状况2:
S为黑且X为内侧插人。对此情況,我们必须先对 P,X做一次单旋转并更改G,X 颜色,再将结果对G做一次单旋转,即可再次满足红黑树规则3。
状况3:
s 为红且x 为外侧插人。对此情况,先对P和G做一次单旋转,并改变X的颜色。此时如果 GG 为黑,一切搞定。红黑树的父子节点可以都是黑色。
状况4:
S 为红且X为外侧插人。对此情况,先对P和G做一次单旋转,并改变X的颜色。此时如果GG 亦为红,还得持续往上做,直到不再有父子连续为红的情况。
为了避免状况4“父子皆为红色”的情况持续向RB-tree的上层结构发展,形成处理时效上的瓶颈,施行一个由上而下的程序:假设新增节点为A,那么延着根节点到A的路径,只要看到有某节点X的两个子节点皆为红色,就把X改为红色,并把两个子节点改为黑色。
如果X的父节点P亦为红色(注意,此时S绝不可能为红色)。就得像状况1一样地做一次单旋转并改变颜色,或是像状况2一样地做一次双旋转并改变颜色。
在此之后,节点35的插入就很单纯了:要么直接插入,要么插入后(若X的节点为红)再一次旋转(单双均可能)即可。
红黑树的头节点和根节点
在红黑树中,头节点和根节点是两个不同的概念。
头节点是一个特殊的节点,它不存储任何元素值,只用于表示红黑树的边界。头节点是一个指向 _Base_type
类型对象的指针,其中 _Base_type
是一个定义了节点基本结构的类。头节点的 _M_parent
成员变量指向红黑树的根节点,而 _M_left
和 _M_right
则分别指向红黑树中的最小值和最大值。
红黑树的根节点是指红黑树的顶层节点,根节点的_M_parent
成员指向头节点。通常来说,根节点在插入、删除、旋转等操作中需要特殊处理。
在实现中,红黑树的根节点的父节点是头节点。这是因为头节点是一个特殊的节点,它不存储任何元素值,只用于表示红黑树的边界。将根节点的父节点指向头节点,可以方便地处理根节点的插入、删除、旋转等操作。
key
const key_type &key(link_type x) { return KeyOfValue()(value(x)); }
在 C++ STL 中,KeyOfValue
是一个函数对象,它用于从容器中的元素中提取关键字。在红黑树中,每个节点都包含一个元素,其中包含了一个关键字和一个值。KeyOfValue
用于从节点的元素中提取关键字。
KeyOfValue()
表示创建一个 KeyOfValue
的实例,并调用其函数操作符(函数重载运算符 operator()
)来提取节点元素中的关键字。括号中的 value(x)
表示获取节点 x
的元素,即返回类型为 value_type&
的引用。因此,KeyOfValue()(value(x))
表示对节点 x
的元素调用 KeyOfValue
函数对象的函数操作符,以提取其关键字。
模板参数 KeyOfValue
在STL源码中,红黑树的模板参数中的 KeyOfValue
是一个函数对象,用于从红黑树节点的 value
中获取键值。
红黑树是一种用于实现关联容器的数据结构,它的每个节点存储一个键值对,其中键值用于对节点进行排序和查找。在STL源码中,红黑树节点的定义类似于以下形式:
template <typename Value>
struct RbTreeNode {
RbTreeNode* parent;
RbTreeNode* left;
RbTreeNode* right;
Value value;
bool color;
};
其中 Value
是节点存储的值类型,value
是节点存储的值。但是在红黑树的实现中,我们需要对节点进行排序和查找,因此需要从节点的 value
中获取键值。
由于 Value
类型的不同,获取键值的方式也可能不同,因此在红黑树的模板参数中,需要指定一个函数对象 KeyOfValue
,用于从 value
中获取键值。例如,如果我们的红黑树节点的 value
是一个 std::pair<K, V>
类型,其中 K
是键值类型,那么可以定义一个 KeyOfValue
函数对象如下:
template <typename T>
struct Select1st {
const typename T::first_type& operator()(const T& x) const {
return x.first;
}
};
template <typename K, typename V, typename Compare>
class RbTree {
public:
typedef std::pair<K, V> value_type;
typedef value_type* pointer;
typedef value_type& reference;
typedef Select1st<value_type> key_of_value;
// ...
};
在上述代码中,Select1st
是一个函数对象,它的 operator()
函数返回一个 std::pair
的第一个元素,即键值 K
。在 RbTree
模板参数中,key_of_value
指定了键值获取方式为 Select1st<value_type>
,即从 value
中获取第一个元素作为键值。
copy
深拷贝
函数有两个参数:第一个参数link_type x
是指向原始树根节点的指针,第二个参数link_type p
是指向复制树中根节点父节点的指针。
该函数的工作流程如下:
- 首先,它创建一个名为
top
的新节点,作为根节点x
的克隆,并将其父节点设置为p
。 - 然后,它检查
x
的右子节点是否存在。如果存在,它递归地复制以右子节点为根的子树,并将top
的右子节点设置为复制子树的根。 - 然后进入一个循环,迭代地复制
x
的左子树。在循环内部,它创建一个新节点y
作为x
的左子节点的克隆,将其父节点设置为p
,并将p
的左子节点设置为y
。 - 如果
x
的右子节点存在,则递归地复制以右子节点为根的子树,并将y
的右子节点设置为复制子树的根。 - 然后更新
p
指针为y
,x
指针为x
的左子节点。 - 循环会一直执行,直到
x
为NULL
。 - 如果在复制过程中抛出异常,则调用
erase()
函数删除已经克隆的节点,然后重新抛出异常。 - 如果复制过程成功完成,则函数返回复制后的树的根节点
top
。
template<class Key, class Value, class KeyOfValue, class Compare, class Alloc>
typename _rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::link_type
_rb_tree<Key, Value, KeyOfValue, Compare, Alloc>
::copy(link_type x, link_type p) { //x是指向原始树根节点的指针,p是指向复制树中根节点的父节点的指针
link_type top = clone_node(x);
top->parent = p;
try {
if (x->right) {
top->right = copy(right(x), top);
}
p = top;
x = left(x);
while (x != 0) {
link_type y = clone_node(x);
p->left = y;
y->parent = p;
if (x->right) {
y->right = copy(right(x), y);
}
p = y;
x = left(x);
}
}
catch (...) {
erase(top);
}
return top; //返回复制后树的根节点
}
红黑树插入
不同的插入顺序得到的二叉树不相同
RB-tree提供两种插人操作:insert_anique ()
和insert_equal ()
,前者表示被插人节点的键值 (key)在整棵树中必须独一无二(因此,如果树中已存在相同的键值,插人操作就不会真正进行),后者表示被插人节点的键值在整棵树中可以重复,因此,无论如何插人都会成功(除非空间不足导致配置失败)。
//insert_equal,插入新值,节点键值允许重复。往下找到一个空的叶子节点,插入
template<class Key, class Value, class KeyOfValue, class Compare, class Alloc>
typename _rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::iterator
_rb_tree<Key, Value, KeyOfValue, Compare, Alloc>
::insert_equal(const Value &v) {
link_type y = header;
link_type x = root();
while (x != 0) {
y = x;
//取两个节点的键值进行比较:v的键值小于x的键值,往左,v的键值大于x的键值,往右
x = key_compare(KeyOfValue()(v), _s_key(x)) ? left(x) : right(x);
}
//三个新参数为,x为新值的插入点,参数y为插入点的父节点,参数v为新值
return __insert(x, y, v);
}
//insert_unique,插入新值,节点键值允许重复,若重复则插入无效
template<class Key, class Value, class KeyOfValue, class Compare, class Alloc>
std::pair<typename _rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::iterator, bool>
_rb_tree<Key, Value, KeyOfValue, Compare, Alloc>
::insert_unique(const Value &v) {
link_type y = header;
link_type x = root();
bool comp = true;
while (x != 0) {
y = x;
comp = key_compare(KeyOfValue()(v), _s_key(x));
x = comp ? left(x) : right(x);
}//离开while循环后,y所指即插入点的父节点(此时,y节点必为叶子节点)
iterator j = iterator(y); //令迭代器j指向插入点的父节点y,其值是查找过程中最后一个比插入元素小的节点的迭代器
if (comp) { //如果离开while循环时,comp为true,说明插入元素应该放在红黑树的最左端
if (j == begin()) { //说明红黑树为空,或者插入元素比红黑树中所有元素都要小
return std::pair<iterator, bool>(__insert(x, y, v), true); //插入到最左端
} else {
--j; //将迭代器减一,使其指向最后一个比插入元素小的节点
}
}
//最后一个比插入元素小的节点不等于 begin 迭代器,并且该节点的元素值不等于插入元素的值,
// 则说明插入元素在红黑树中不存在,此时调用 _M_insert 函数插入元素
if (key_compare(_s_key(j.node), KeyOfValue()(v))) {
return std::pair<iterator, bool>(__insert(x, y, v), true);
} else {
//如果 _M_key_compare(_S_key(__j._M_node), _KeyOfValue()(__v)) 的值为 false,
// 说明最后一个比插入元素小的节点的元素值等于插入元素的值,此时不能将插入元素插入红黑树中。
//返回最后一个比插入元素小的节点的迭代器和 false。
return std::pair<iterator, bool>(j, false);
}
}
真正的插入函数
template<class Key, class Value, class KeyOfValue, class Compare, class Alloc>
typename _rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::iterator
_rb_tree<Key, Value, KeyOfValue, Compare, Alloc>
::__insert(base_ptr _x, base_ptr _y, const value_type &_v) { //三个参数:x为新插入点,y为插入点的父节点,v为新值
link_type x = (link_type) _x;
link_type y = (link_type) _y;
link_type z;
//新插入节点的父节点等于头节点 || 说明要插入的值在红黑树中已存在 || 新插入点的键值小于父节点y的键值
//为什么x != 0时,left(y) = z,传到这里的x参数不可能 != 0
if (_y == header || x != 0 || key_compare(KeyOfValue()(_v), _s_key(y))) {
z = create_node(_v);
left(y) = z; //插入到红黑树的左子树,
//并更新相关指针
if (y == header) {
root() = z;
rightmost() = z;
} else if (y == leftmost()) {
leftmost() = z;
}
} else { //父节点不为头节点,并且新节点不存在于红黑树中
z = create_node(_v);
right(y) = z;
if (y == rightmost()) {
rightmost() = z;
}
}
parent(z) = y;
left(z) = 0;
right(z) = 0;
_rb_tree_rebalance(z, header->parent);
++node_count;
return iterator(z);
}
插入过程的四种状况解析
- 可以在迭代器的基类中添加一个提取颜色的函数
bool get_color() const {
return link_type(node)->color;
}
- 以层序顺序插入,即可得到上文中的树
template<class Arg, class Result>
struct unary_function {
typedef Arg argument_type;
typedef Result result_type;
};
template<class T>
struct identity : public unary_function<T, T> {
const T &operator()(const T &x) const {
return x;
}
};
template<class Arg1, class Arg2, class Result>
struct binary_function {
typedef Arg1 first_argument_type;
typedef Arg2 second_argument_type;
typedef Result result_type;
};
template<class T>
struct less : public binary_function<T, T, bool> {
bool operator()(const T &x, const T &y) const {
return x < y;
}
};
template<class T=mystl::_rb_tree<int, int, identity<int>, less<int>>>
void print_tree(T &tree) {
typename T::iterator ite1 = tree.begin();
typename T::iterator ite2 = tree.end();
std::cout << "node.value: ";
for (; ite1 != ite2; ++ite1) {
std::cout << *ite1 << ':' << ite1.get_color() << ' ';
}
std::cout << std::endl;
}
int main() {
mystl::_rb_tree<int, int, identity<int>, less<int> > itree;
std::cout << itree.size() << std::endl;
itree.insert_unique(30);
itree.insert_unique(15);
itree.insert_unique(70);
itree.insert_unique(10);
itree.insert_unique(20);
itree.insert_unique(60);
itree.insert_unique(85);
itree.insert_unique(5);
itree.insert_unique(25);
itree.insert_unique(50);
itree.insert_unique(65);
itree.insert_unique(80);
itree.insert_unique(90);
itree.insert_unique(40);
itree.insert_unique(55);
print_tree(itree);
//输出结果:0表示红色,1表示黑色
//node.value: 5:0 10:1 15:1 20:1 25:0 30:1 40:0 50:1 55:0 60:0 65:1 70:1 80:0 85:1 90:0
}
下面都四种状况依次解析:
说明:
- 所谓的内侧和外侧插入的是针对最小的失衡树而言的。
- 每种情况之间相互独立,每次测试一种
状况1
伯父节点为黑,新节点为外侧插入
插入3:itree.insert_unique(3);
在insert_unique()
中确定3在红黑树中是否已经存在,如果存在,直接返回,不存在则确定新增节点X
要插入的位置和X
的父节点Y
以及插入节点的值v
,调用__insert(x,y,v)
。
在__insert(x,y,v)
中,以值v创建节点z,插入到y的左子树,链接节点z和y(left(y) = z;parent(z) = y;left(z) = 0;right(z) = 0;
),然后调用__rb_tree_rebalance(z,header->parent);
。
//状况1:参数为新增节点x,和红黑树的根节点
inline void __rb_tree_rebalance(__rb_tree_node_base *x, __rb_tree_node_base *&root) {
x->color = __rb_tree_red; //新节点必为红,不能有两个连续的红节点
while (x != root && x->parent->color == __rb_tree_red) { //此时父节点为红
if (x->parent == x->parent->parent->left) {//父节点为祖父节点的左节点,
//令s为伯父节点,伯父节点为黑
__rb_tree_node_base *s = x->parent->parent->right;
if (s && s->color == __rb_tree_red) { //伯父节点为黑,跳过
x->parent->color = __rb_tree_black;
s->color = __rb_tree_black;
x->parent->parent->color = __rb_tree_red;
x = x->parent->parent;
} else { //无伯父节点或伯父节点为黑
if (x == x->parent->right) { //外侧插入,跳过
x = x->parent;
__rb_tree_rotate_left(x, root); //第一参数为左旋点
}
//S为黑,且外侧插入(新节点为父节点的左节点),对应状况1,只右旋
//先改颜色后设定祖父节点为旋转点
x->parent->color = __rb_tree_black; //父节点改为黑
x->parent->parent->color = __rb_tree_red; //祖父节点改为红
__rb_tree_rotate_right(x->parent->parent, root); //第一参数为右旋点
}
} else { //父节点为祖父节点的右节点,和左节点操作原理相同,
__rb_tree_node_base *s = x->parent->parent->left; //那伯父节点就是祖父节点的左节点
if (s && s->color == __rb_tree_red) {
x->parent->color = __rb_tree_black;
s->color = __rb_tree_black;
x->parent->parent->color = __rb_tree_red;
x = x->parent->parent; //把x置为祖父节点
} else {
if (x == x->parent->left) { //内侧插入
x = x->parent;
__rb_tree_rotate_right(x, root);
}
x->parent->color = __rb_tree_black;
x->parent->parent->color = __rb_tree_red;
__rb_tree_rotate_left(x->parent->parent, root);
}
}
}
root->color = __rb_tree_black;
}
//右旋操作
inline void __rb_tree_rotate_right(__rb_tree_node_base *x, __rb_tree_node_base *&root) {
__rb_tree_node_base *x_l = x->left;
x->left = x_l->right;
if (x_l->right != 0) {
x_l->right->parent = x;
}
x_l->parent = x->parent;
if (x == root) {
root = x_l;
} else if (x == x->parent->right) {
x->parent->right = x_l;
} else {
x->parent->left = x_l;
}
x_l->right = x;
x->parent = x_l;
}
状况2:
伯父节点为黑,且为内侧插入
itree.insert_unique(8);
在insert_unique()
中确定8在红黑树中是否已经存在,如果存在,直接返回,不存在则确定新增节点X
要插入的位置和X
的父节点Y
以及插入节点的值v
,调用__insert(x,y,v)
。
在__insert(x,y,v)
中,以值v创建节点z,插入到y的右子树,链接节点z和y(left(y) = z;parent(z) = y;left(z) = 0;right(z) = 0;
),然后调用__rb_tree_rebalance(z,header->parent);
。
//参数为新增节点x,和红黑树的根节点
inline void __rb_tree_rebalance(__rb_tree_node_base *x, __rb_tree_node_base *&root) {
x->color = __rb_tree_red; //新节点必为红,不能有两个连续的红节点
while (x != root && x->parent->color == __rb_tree_red) { //如果父节点为红
if (x->parent == x->parent->parent->left) { //如果父节点为祖父节点的左节点,
__rb_tree_node_base *s = x->parent->parent->right; //令s为伯父节点
if (s && s->color == __rb_tree_red) { //S不存在,跳过
...
} else { //无伯父节点或伯父节点为黑
if (x == x->parent->right) { //S为黑且内侧插入,对应状况2;要旋转两次,先左旋再右旋
x = x->parent; //把x的父节点作为左旋点
__rb_tree_rotate_left(x, root); //左旋
}
//S为黑,且外侧插入新节点为父节点的左节点,对应状况1,只右旋
x->parent->color = __rb_tree_black; //父节点改为黑
x->parent->parent->color = __rb_tree_red; //祖父节点改为红
__rb_tree_rotate_right(x->parent->parent, root); //第一参数为右旋点
}
} else { //父节点为祖父节点的右节点
...
}
}
root->color = __rb_tree_black;
}
先左旋:
inline void __rb_tree_rotate_left(__rb_tree_node_base *x, __rb_tree_node_base *&root) {
__rb_tree_node_base *x_r = x->right;
x->right = x_r->left;
if (x_r->left != 0) {
x_r->left->parent = x;
}
x_r->parent = x->parent;
if (x == root) {
root = x_r;
} else if (x == x->parent->left) {
x->parent->left = x_r;
} else {
x->parent->right = x_r;
}
x_r->left = x;
x->parent = x_r;
}
再右旋:
//右旋操作
inline void __rb_tree_rotate_right(__rb_tree_node_base *x, __rb_tree_node_base *&root) {
__rb_tree_node_base *x_l = x->left;
x->left = x_l->right;
if (x_l->right != 0) {
x_l->right->parent = x;
}
x_l->parent = x->parent;
if (x == root) {
root = x_l;
} else if (x == x->parent->right) {
x->parent->right = x_l;
} else {
x->parent->left = x_l;
}
x_l->right = x;
x->parent = x_l;
}
状况3:
伯父节点为红,且为外侧插入
itree.insert_unique(75);
在insert_unique()
中确定8在红黑树中是否已经存在,如果存在,直接返回,不存在则确定新增节点X
要插入的位置和X
的父节点Y
以及插入节点的值v
,调用__insert(x,y,v)
。
在__insert(x,y,v)
中,以值v创建节点z,插入到y的右子树,链接节点z和y(left(y) = z;parent(z) = y;left(z) = 0;right(z) = 0;
),然后调用__rb_tree_rebalance(z,header->parent);
。
//参数为新增节点x,和红黑树的根节点
inline void __rb_tree_rebalance(__rb_tree_node_base *x, __rb_tree_node_base *&root) {
x->color = __rb_tree_red; //新节点必为红,不能有两个连续的红节点
while (x != root && x->parent->color == __rb_tree_red) { //父节点为红
if (x->parent == x->parent->parent->left) { //父节点为祖父节点的左节点,
__rb_tree_node_base *s = x->parent->parent->right; //令s为伯父节点
if (s && s->color == __rb_tree_red) { //S为红
x->parent->color = __rb_tree_black; //把x父节点的颜色改为黑
s->color = __rb_tree_black; //把伯父节点的颜色改为黑
x->parent->parent->color = __rb_tree_red; //把祖父的节点颜色改为红
x = x->parent->parent; //继续往上检查
} else { //跳过,进入下一轮循环,如果曾祖父节点为黑,则不需要旋转,这个函数就结束了
if (x == x->parent->right) {
x = x->parent;
__rb_tree_rotate_left(x, root);
}
//S为黑,且外侧插入新节点为父节点的左节点,只右旋
x->parent->color = __rb_tree_black; //父节点改为黑
x->parent->parent->color = __rb_tree_red; //祖父节点改为红
__rb_tree_rotate_right(x->parent->parent, root); //第一参数为右旋点
}
} else { //父节点为祖父节点的右节点
...
}
}
root->color = __rb_tree_black;
}
在使用源代码调试之后发现,状况3这种情况是不需要进行旋转的,节点的颜色改变后就可以满足红黑树的条件。
状况4:
itree.insert_unique(35);
伯父节点为红,为外侧插入,且曾祖父节点为红。
//参数为新增节点x,和红黑树的根节点
inline void __rb_tree_rebalance(__rb_tree_node_base *x, __rb_tree_node_base *&root) {
x->color = __rb_tree_red; //新节点必为红,不能有两个连续的红节点
while (x != root && x->parent->color == __rb_tree_red) { //父节点为红
if (x->parent == x->parent->parent->left) { //父节点为祖父节点的左节点,
__rb_tree_node_base *s = x->parent->parent->right; //令s为伯父节点
if (s && s->color == __rb_tree_red) { //S为红,第二轮循环跳过
x->parent->color = __rb_tree_black; //把x父节点的颜色改为黑
s->color = __rb_tree_black; //把伯父节点的颜色改为黑
x->parent->parent->color = __rb_tree_red; //把祖父的节点颜色改为红
x = x->parent->parent; //继续往上层查找,进行第二轮循环
} else { //第二轮:无伯父节点或伯父节点为黑
if (x == x->parent->right) { //第二轮:外侧插入,跳过
x = x->parent;
__rb_tree_rotate_left(x, root); //第一参数为左旋点
}
//S为黑,且外侧插入新节点为父节点的左节点,对应状况1,只右旋
x->parent->color = __rb_tree_black; //父节点改为黑
x->parent->parent->color = __rb_tree_red; //祖父节点改为红
__rb_tree_rotate_right(x->parent->parent, root); //第一参数为右旋点
}
} else { //父节点为祖父节点的右节点
...
}
}
root->color = __rb_tree_black;
}