前言
本文将介绍红黑树两大操作之一:插入
插入
首先考察红黑树性质:
(1)根节点是黑色。
(2)每个外部节点(NULL)是黑色。
(3)如果一个节点是红色的,则它的子节点必须是黑色的。
(4)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
现拟插入关键码 e,这里我们将白色八边形节点作为颜色不定的节点(红/黑)
首先按照 BST 常规算法插入之
x = insert(e)
这里我们不妨假设 x的父亲p存在
p = x ->parent; // 不存在,直接设 x 为根节点
如果插入的节点是黑色,性质 (4) 必定违反。调整树高度过程极为复杂
为了最大限度不破坏红黑树的性质我们将 x 设为红色(除非它是根节点)
观察性质 (1)(2)(4)均满足,(3)可能不满足
将 x 染红
x->color = red
p 为黑时(3)满足,无需调整,插入结束
p 为红时(3)不满足,需要调整
我们将会看下如下情况
注意! 此时g是一定存在的,红节点必定有黑父亲
该现象称为 双红缺陷
我们将在下面分情况讨论 x 的叔叔节点 u 的颜色
双红缺陷
当插入的关键码 e 的父亲 p 为红色时,我们将其称为双红缺陷
为了便于说明,我们在这里约定一些变量:
x 为插入的节点(关键码 e)
p 为 x 的父亲
g 为 p 的父亲
u 为 p 的兄弟
RR - 1
u 的颜色为黑 ( u -> color == black )
为了不失一般情况我们考虑如下两种情况:
忽略掉对称情况
如上图
通过将所有红色孩子提升至他们的父亲同一高度,我们得到对应的B-树
(a') 对应 (a) 的B-树
(b') 对应 (b) 的B-树
我们可以看到,RR - 1问题的根本是 作为黑色节点的 g 并没有处于居中状态(RBR)而是处于边缘(RRB)
那么如何修正呢?
我们参照AVL,做局部3+4重构
将 x p g 三个节点及其四颗子树(T1…T4)按中序组合
T1 < a < T2 < b < T3 < c < T4
结果如下
并将 b 染黑 ,a/c 染红(只有一个)
让我们将对应的 (c’)还原到红黑树 (c),不难理解RR - 1 的修复过程
让我们回到(a)、(b)
对于(a)
1)直接右旋转节点 g
2)染红 g 染黑 p 即可变为 (c)的情况。修复完毕
对于(b)
1)我们先左旋 p 使其转换为情况(a),接着跟(a)做一样的处理
2)右旋转节点 g
3)染红 g 染黑 x (注意这里因为 1)的左旋转的关系 p 现在是 x 的左孩子)
关于g的左右子树对称情况请读者自行考虑
RR - 2
u 的颜色为红 ( u -> color == red )
为了不失一般情况我们考虑如下两种情况:
忽略掉对称情况
(a') 对应 (a) 的B-树
(b') 对应 (b) 的B-树
通过B-树我们可以直观的看到 RR - 2 的根本是:4阶B树 出现了5阶的超级节点
该情况在B-树中被描述为 上溢
那么我们怎么在B-树中修复上溢?
1)找到该超级节点居中的关键键码(对于图a’来说是 g)
2)以该键码为界,将该超级节点分裂为左、右两个节点
3)将该键码节点上移并插入到父节点中
如图
(c')即是 x 修复上溢完成的B-树
通过分离红色节点,我们能很轻易地得到对应的红黑树(c)
之后将 g 染红, p 染黑,u 染黑即可
不过,问题还没结束,因为这次只修复了 x 的 RR - 2
通过(c),我们不难发现 如果 g 的父亲节点也为红色则需要再次对 g 进行 RR - 2 修复
好在每次问题发生的位置会不断上升,直至根(p == nullptr)
或 p 为黑 (p->color == black)便会结束
下面我们给出处理过程
1)染红 g 染黑 u , p
2)对 g 经行双红缺陷修正(RR - 2 / RR - 1 / 或 p 为黑(nullptr也是黑)直接结束)
关于g的左右子树对称情况请读者自行考虑
至此,红黑树的插入已经讲完,下面给出 c++ 代码
C++代码
双红缺陷修正
// RedBlackTree - 解决双红缺陷
void _fix_after_insert(_Mytn *it) {
_Mytn *p,*g,*u;
while (it) {
p = it->parent;
if (!p) { // 没有父亲 当前为根节点则设为黑色
it->color = _Tree_Node::black;
this->_root = it;
return;
}
else {
if (p->color == _Tree_Node::red) { // 父亲是红色,一定有黑色的祖父节点
g = p->parent;
if (p == g->lc) { u = g->rc; } // 获取叔叔节点
else { u = g->lc; }
if (u && u->color == _Tree_Node::red) { // 叔叔为红
// RR - 2
// 染黑p和u 染红g _fix_double_red(g)
p->color = _Tree_Node::black;
u->color = _Tree_Node::black;
g->color = _Tree_Node::red;
//_fix_double_red(g); // 尾递归
// 改用非递归版本
it = g;
g = nullptr;
u = nullptr;
continue;
}
else { // 叔叔不存在或为黑
// RR -1
if (u == g->rc) { // 如果u是g的右儿子
// 如果是p的右儿子 左旋p
if (p->rc == it) {
this->_left_rotate(p);
// 更新p
p = p->parent;
}
// 右旋g
this->_right_rotate(g);
}
else { // u是g的左儿子
// 如果是p的左儿子 右旋p
if (p->lc == it) {
this->_right_rotate(p);
// 更新p
p = p->parent;
}
// 左旋g
this->_left_rotate(g);
}
// 染黑p 染红g
p->color = _Tree_Node::black;
g->color = _Tree_Node::red;
return;
}
}
else {
// RR - 0
// 父亲是黑色 无需修复
return;
}
}
}
}
其他相关函数
// 插入
_Myit insert(_Elem const &v) {
_Mytn *it = this->_insert(v);
_fix_after_insert(it);
return _Myit(it);
}
// 插入
_Mytn* _insert(_Elem const &v) {
_Mytn *it, *hot;
it = this->_root->find(v, &hot);
if (it) return it;
if (hot) {
if (v > hot->value) {
it = hot->rc = new _Mytn(v, _Tree_Node::red, nullptr, nullptr, hot);
}
else {
it = hot->lc = new _Mytn(v, _Tree_Node::red, nullptr, nullptr, hot);
}
}
else {
it = new _Mytn(v, _Tree_Node::black, nullptr, nullptr, nullptr);
_root = it;
}
++_size;
return it;
}
// 查找
_Mytn *find(_Elem v, _Mytn **hot = nullptr) {
_Mytn *it = this;
_Mytn *_hot = nullptr;
while (it && it->value != v) {
_hot = it;
if (it->value > v) {
it = it->lc;
}
else {
it = it->rc;
}
}
if (hot) *hot = _hot;
return it;
}
// 左旋节点
void _left_rotate(_Mytn *it) {
_Mytn *p = it->parent, *n = it->rc, *y = nullptr;
if (n) {
y = n->lc;
it->parent = n;
n->lc = it;
it->rc = y;
if (y) y->parent = it;
n->parent = p;
if (!p) {
this->_root = n;
}
else {
if (p->lc == it) p->lc = n;
else p->rc = n;
}
}
}
// 右旋节点
void _right_rotate(_Mytn *it) {
_Mytn *p = it->parent, *n = it->lc, *y = nullptr;
if (n) {
y = n->rc;
it->parent = n;
n->rc = it;
it->lc = y;
if (y) y->parent = it;
n->parent = p;
if (!p) {
this->_root = n;
}
else {
if (p->lc == it) p->lc = n;
else p->rc = n;
}
}
}