简单介绍
- avl树删除操作最坏情况需要O(logn)旋转;为了解决这一问题,需要将平衡的定义的更加宽泛些,这就是红黑树了(删除和新增只需要O(logn)时间)
适度平衡
- 没有平衡因子的概念,但是要求任一节点的左右子树高度不得相差2倍
红黑条件
- 树根均是黑色
- 外部节点均是黑色
- 其余节点如果是红色,则其孩子节点必需是黑色
- 从任一外部节点到根节点的沿途,黑节点数目相等
红黑条件解析
- 条件1,2: 红节点都是内部节点且父节点,左右孩子必然存在
- 条件3: 红节点的父节点必然是黑节点,黑红树的任一通路不含相邻的红节点,
从根节点通往任一节点的沿途,黑节点数目总不少于红节点数目
- 条件4: 从任一节点通往其任一后代的外部节点的沿途,黑节点的总数总是相等的。另外所有外部节点的黑深度相等
基本概念
- 黑深度
- 从节点到任一节点沿途经过的黑节点的数目-1(根节点) 就是黑深度
- 根节点黑深度是0
- 黑高度
- 从任一节点通往其任一后代外部节点的沿途经过的黑节点数目-1(外部节点)
- 外部节点黑高度是0
删除重平衡原理
代码实现
// 引入二叉树
#include "BstTree.h"
template<typename T>
class RedBlack : public BST<T> {
protected:
// 双红修正
void solveDoubleRed(BinNodePosi(T)x);
// 双黑修正
void solveDoubleBlack(BinNodePosi(T)x);
// 更新高度
int updateHeight(BinNodePosi(T)x);
public:
BinNodePosi(T)insert(const T &e);
bool remove(const T &e);
};
// 外部节点也当作黑色
#define IsBlack(p) (!(p) || (p)->color == RB_RED)
// 是否是红色
#define IsRed(p) (!IsBlack(p))
// RedBlack高度更新的条件
#define BlackHeightUpdated(x) (\
(stature(x.lc) == stature(x.rc)) && \
(x.height == (IsRed(&x) ? stature(x.lc) : stature(x.lc) + 1)) \
)
// 更新节点高度
template<typename T>
int RedBlack<T>::updateHeight(BinNodePosi(T)x) {
// 双黑的情况下,
x->height = max(stature(x->lc), stature(x->rc));
// 黑节点比子节点高1
return IsBlack(x) ? x->height++ : x->height;
};
// 节点插入
template<typename T>
BinNodePosi(T)::insert(const T &e) {
// 如果存在 则不进行后续操作
BinNodePosi(T)&x = search(e);
if (x) {
return x;
}
// 创建以_hot为父节点的红节点, 此节点黑高度-1
x = new BinNode(e, _hot, NULL, NULL, -1);
_size++;
// 双击红修正
return x ? x : _hot->parent;
};
/**
* 双红修正算法:解决节点x与父亲都是红色的问题,分为下面的两类
* RR-1. 叔父节点是黑色 2次颜色翻转,2次黑高度更新, 1--2次旋转
* RR-2. 叔父节点是黑色 3次颜色翻转,3次黑高度更新,0次旋转 需要递归
*
* */
template<typename T>
void RedBlack<T>::solveDoubleRed(BinNodePosi(T)x) {
// 递归基(根强制染成黑色, 整树高度+1)
if (IsRoot(*x)) {
x->color = RB_BLACK;
x->height++;
return;
}
// 如果p为黑 则递归终止
BinNodePosi(T)p = x->parent;
if (IsBlack(p)) {
return;
}
// p为红色节点,则g必为黑色节点
BinNodePosi(T)g = p->parent;
// 叔父节点
BinNodePosi(T)u = uncle(x);
// RR-1
if (IsBlack(u)) {
// 如果x p同侧
if (IsLChild(*p) == IsLChild(*x)) {
p->color = RB_BLACK;
} else {
v->color = RB_BLACK;
}
// g必定转成红色
g->color = RB_RED;
// 曾祖
BinNodePosi(T)gg = g->parent;
// 翻转之后的根
BinNodePosi(T)r = FromParentTo(*g) = rotateAt(x);
r->parent = gg;
// 此时黑高度恢复
} else {
p->color = RB_BLACK;
p->height++;
u->color = RB_BLACK;
u->height++;
// 如果g不是根节点,则染成红色
if (!IsRoot(*g)) {
g->color = RB_RED;
}
solveDoubleRed(g);
};
}
// 删除算法
template<typename T>
bool RedBlack<T>::remove(const T &e) {
// 节点不存在 不需要后续操作
BinNodePosi(T)&x = search(e);
if (!x) {
return false;
}
// 删除节点
BinNodePosi(T)r = removeAt(x, _hot);
// 更新节点数量
--size;
// 空树没有必要纠结双黑的问题
if (!_size) {
return true;
}
// 如果删除的根节点, 将树根染黑,更新书高
if (!_hot) {
_root->color = RB_BLACK;
updateHeight(_root);
return true;
}
/**
* 祖先的高度依然平衡 则不需要调整
* x红 r红 Y
* x红 r黑 Y
* x黑 r黑 N
* x黑 r红 N
**/
if (BlackHeightUpdated(*_hot)) {
return true;
}
// x黑 r红
if (IsRed(r)) {
r->color = RB_BLACK;
r->height++;
return true;
}
// x黑 r黑
// 双黑调整
solveDoubleBlack(r);
return true;
}
/** 双黑复原
* BB -1 黑S有红子t
* 1.参考B-树下溢算法 借父节点合并到本身第0个关键码,父节点借S, 调整颜色(新父节s继承原父节点的p的颜色, t改成黑色,原父节点改成黑色) 此时达到平衡
* 2.对于红黑树 t s p 以t为原点进行3+4旋转, 并修改颜色(新子根颜色继承原子根颜色,两个孩子修改成黑色),更新各自的高度
* BB-2-R 黑S无红子,父节点p为红色
* 1. 参考B-树下溢算法 左s右x(空)孩子以父节点p为粘合剂合并成一个节点 , 调整颜色(p黑色,s红色) (p所属的大节点中必然还有黑色关键码,所以此时不会产品下溢的传播 此时达到平衡)
* 2. 对于红黑树 s,p颜色交换
* BB-2-B 黑S无红子 父节点p为黑色
* 1. 参考B-树下溢算法 左s右x(空)孩子以父节点p为粘合剂合并成一个节点, s调整成红色; 因原p时黑色即,s和x都是黑色,所以原p所属的大节点必然只有p一个关键码;此时p下溢之后导致向上传播
* 2. 对红黑树 s颜色转成红色,递归解决下溢传播的问题solveDoubleBlack(p)
* BB-3 红S (因为S是红,所以此时P肯定是黑)
* 1. 参考B-树 p和s交换一下颜色,此时没有解决r下溢的问题,但是solveDoubleBlack(r)已经转换成BB-1 BB-2-R的情况
* 2. 对于红黑树 以和s同侧的孩子节点t为起点做了一次3+4的旋转并交换s和p的颜色
*
*/
template<typename T>
void RedBlack<T>::solveDoubleBlack(BinNodePosi(T)r) {
// 获取p级节点
BinNodePosi(T)p = r ? r->parent : _hot; // r为NULL时_hot的值未removeAt中没有修改
if (!p) {
return;
}
// r的兄弟
BinNodePosi(T)s = sibling(r);
if (IsBlack(s)) {
// s的红孩子 左孩子优先
BinNodePosi(T)t = NULL;
if (IsRed(s->rc)) {
t = s->rc;
}
if (IsRed(s->lc)) {
t = s->lc;
}
// BB - 1
if (t) {
RBColor old_color = p->color;
// 3+4旋转
BinNodePosi(T)b = FromParentTo(*p) = removeAt(t);
// 更新
if (HasRChild(b)) {
b->rc->color = RB_BLACK;
updateHeight(b->rc);
}
if (HasLChild(b)) {
b->lc->color = RB_BLACK;
updateHeight(b->lc);
}
b->color = old_color;
updateHeight(b);
} else {
s->color = RB_RED;
s->height--;
if (IsRed(p)) {
// BB-2-R
// p 黑高度保持不变 (p红变黑, 理论上应该+1 但是删除了黑x, 黑s又变成了红s 所以p高度维持不变)
p->color = RB_BLACK;
} else {
// BB-2-B
// 删除了黑x, 黑s又变成了红s 所以p高度-1
p->height --;
solveDoubleRed(p);
}
}
} else {
// BB-3 红s 黑p (黑x 黑r)
s->color = RB_BLACK;
p->color = RB_RED;
// 获取s同侧的孩子t
BinNodePosi(T) t = IsLChild(*s) ? s->lc : s->rc;
_hot = p;
FromParentTo(*p) = rotateAt(t);
// 继续修正r,转换成BB-1 BB-2-R的情况
solveDoubleRed(r);
}
}