红黑树特性:
- 树根 始终为 黑色
- 外部节点 均为 黑色
- 其余节点 均为 红色,则 其孩子、父亲 必为 黑色
- 从任一外部节点出发,到根节点,沿途黑节点数目必然相同
template<typename T>
class RedBlack : public BST<T>{
public:
//search沿用
BinNode<T>* insert(T e);
bool remove(T e);
int updateHeight(BinNode<T>* v);//重写BinTree的高度更新方法
void solveDoubleRed(BinNode<T>* x);
void solveDoubleBlack(BinNode<T>* x);
};
template<typename T>
bool isBlack(BinNode<T>* x){//外部节点视作Black
if(x==NULL || x->color==RB_BLACK)return true;
else return false;
}
template<typename T>
bool isRed(BinNode<T>* x){//非黑即红
return !isBlack(x);
}
template<typename T>
int RedBlack<T>::updateHeight(BinNode<T> *v) {//重写为更新黑高度
if(!v) return -1;
int m = updateHeight(v->lchild);
int n = updateHeight(v->rchild);
if(v->color == RB_BLACK) v->height = max(m,n)+1;
else v->height = max(m,n);
return v->height;
}
template<typename T>
bool RedBlack<T>::isBlackBalancedAbove(BinNode<T>* x){//黑高度是否平衡
while(x){
updateHeight(x);
if(x->lchild->height != x->rchild->height)return false;
x = x->parent;
}
return true;
}
红黑树操作:
- search:就是简单的二叉搜索树的search,因此直接继承即可。(二叉搜索树https://blog.csdn.net/weixin_43462583/article/details/132050664)
- insert:由于插入节点是以红色插入的,因此其父亲可能也是红色,此时发生双红缺陷,需要双红修正——此时目光应该转向插入节点的叔叔(uncle),也就是父亲的兄弟(sibling)。
- 若被插入节点为根节点,此时直接将根染为黑色,树高+1;
双红修正:(看叔叔)
- 若uncle为黑色:此时,从被插入节点v处,以及其父亲p,爷爷g,做3+4重构,重构后的新根染为黑色,新根左孩子、右孩子染为红色;
- 若uncle为红色:此时,爷爷g染为红色,父亲、叔叔染为黑色,爷爷处此时可能继续双红缺陷,继续修正即可。
至多做一次3+4重构,O(logn)次节点染色
insert代码:
template<typename T>
BinNode<T>* RedBlack<T>::insert(T e) {
BinNode<T>*& x = search(e);
if(x)return x;
x = new BinNode<T>(e,this->_hot,NULL,NULL,-1);//插入红节点,所以高度为-1,这里与二叉树高度不同
this->_size++;
solveDoubleRed(x);
return x ? x : this->_hot->parent;
}
template<typename T>
void RedBlack<T>::solveDoubleRed(BinNode<T> *v) {
if(!v->parent){
//插入节点为根
this->_root->color = RB_BLACK;
this->_root->height++:
return;
}
BinNode<T>* p = v->parent;
if(p->color ==RB_BLACK) return;
BinNode<T>* g = p->parent;
BinNode<T>* u = (p == g->lchild) ? g->rchild : g->lchild;//uncle节点
if(isBlack(u)){//叔叔为黑色,旋转+改色,旋转操作为3+4重构,旋转后新根节点变为黑色,左右孩子变为红
BinNode<T>* gg = g->parent;
if (gg){
int temp;
if(g == gg->lchild)temp =1;
else temp = 2;
BinNode<T>*r = rotateAT(v);
if(temp == 1) r = gg->lchild;
else r = gg->rchild;
r->parent = gg;
r->color = RB_BLACK;
r->lchild->color = RB_RED;
r->rchild->color = RB_RED;
}else{
BinNode<T>* r = rotateAT(v);
this->_root = r;
r->parent = NULL;
r->color = RB_BLACK;
r->lchild = RB_RED;
r->rchild = RB_RED;
}
}else{//叔叔为红色,父亲、叔叔染黑,爷爷染红,无需旋转,继续solveDoubleRed(爷爷)
u->color = RB_BLACK;
u->height++;
p->color = RB_BLACK;
p->height++:
if(g->parent) g->color = RB_RED;
solveDoubleRed(g);
}
}
3.remove:删除操作较为复杂,首先,调用removeAT算法,直接删除节点;
- 如果删除后树变为空,直接返回;
- 如果被删除节点是根节点,将根染黑,更新高度,直接返回;
- 如果删除节点后,其所有祖先均没有黑高度失衡,直接返回
- 如果被删除节点的接替者为红色,将其染黑,直接返回;
- 如果被删除节点为黑色,接替者也为黑色,此时需要双黑修正。
双黑修正分为四种情况(双黑修正看兄弟)
接替者r的兄弟s为黑,但是至少有一个红孩子,在其红孩子处rotateAT,新根节点和以前根节点p的颜色保持一致,新根的左右孩子(如果存在),染黑;
r的兄弟s为黑,没有任何一个红孩子,但是父亲p为红,此时s染红,p染黑,无需向上传播;r的兄弟s为黑,没有任何一个红孩子,但是父亲p为黑,此时s染红,p不变,从p开始向上传播;
r的兄弟s为红,s变黑,父亲p变红,与s同侧的孩子t处左旋转,在r处继续双黑修正,此时变为以上1,2情况。
至多一次3+4重构,一次单旋转,O(logn)次染色
代码:
template<typename T>
bool RedBlack<T>::remove(T e) {
BinNode<T>*& x = search(e);
if(!x) return false;
BinNode<T>* r = removeAT(x,this->_hot);//removeAT返回实际被删除节点的位置,此时,应查看此位置与其父亲是否发生缺陷
this->_size--;
if(this->_size == 0)return true;//树变为空,return
if(!this->_hot){//如果实际被删除节点为根节点,染黑,更新高度即可
this->_root->color = RB_BLACK;
this->_root->height++;
return true;
}
if(isBlackBalancedAbove(this->_hot))return true;//若被删除节点父亲及其祖先依然黑高度平衡,则返回
if(isRed(r)){//接替者为红色,则之前实际被删除节点必为黑色,此时直接转黑即可
r->color = RB_BLACK;
r->height++;
return true;
}
solveDoubleBlack(r);return true;//双黑修正:被删除节点为黑,接替者也为黑
}
template<typename T>
void RedBlack<T>::solveDoubleBlack(BinNode<T> *r) {//删除时看兄弟
BinNode<T>*p = r->parent;
if(!p) return;
BinNode<T>* s = (r==p->lchild) ? p->rchild : p->lchild;//兄弟节点
if(isBlack(s)){
BinNode<T>* t = NULL;
if(isRed(s->rchild)) t = s->rchild;
if(isRed(s->lchild)) t = s->lchild;
if(t){//case1: //如果s有红孩子
//3+4重构
RBColor oldColor = p->color;
int temp;
BinNode<T>* pp = p->parent;
if (pp){
int temp;
if(p == pp->lchild)temp =1;
else temp = 2;
BinNode<T>*b = rotateAT(t);
if(temp == 1) b = pp->lchild;
else b = pp->rchild;
if(b->lchild){
b->lchild->color = RB_BLACK;
b->lchild->height++:
}
if(b->rchild){
b->rchild->color = RB_BLACK;
b->rchild->height++;
}
b->color = oldColor;
updateHeight(b);
}else{
BinNode<T>*b = rotateAT(t);
if(b->lchild){
b->lchild->color = RB_BLACK;
b->lchild->height++:
}
if(b->rchild){
b->rchild->color = RB_BLACK;
b->rchild->height++;
}
b->color = RB_BLACK;
b->height++;
}
}else{//兄弟s没有任何一个红孩子
if(p->color == RB_RED){//case2//分为父亲为红和黑两种情况讨论
s->color = RB_RED;
s->height--;
p->color = RB_BLACK;
}else{//case3//分为父亲为红和黑两种情况讨论
s->color = RB_RED;
p->height--;
s->height--;
solveDoubleRed(p);//可能向上传播
}
}
}else{//case4//兄弟节点为红色
BinNode<T>* t =NULL;
if(s == p->lchild)t = s->lchild;
else t = s->rchild;//t是和s同侧的孩子
s->color = RB_BLACK;
p->color = RB_RED;
BinNode<T>* pp = p->parent;
if(pp){
int temp;
if(p == pp->lchild)temp =1;
else temp = 2;
BinNode<T>* xxx = rotateAT(t);
if(temp ==1)xxx = pp->lchild;
else xxx = pp->rchild;
}else{
rotateAT(t);
this->_root = RB_BLACK;
}
solveDoubleBlack(r);//转为情况1或者2
}
}