C++实现高级二叉搜索树——红黑树(RedBlack)

红黑树特性

  1. 树根 始终为 黑色
  2. 外部节点 均为 黑色
  3. 其余节点 均为 红色,则 其孩子、父亲 必为 黑色
  4. 从任一外部节点出发,到根节点,沿途黑节点数目必然相同
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;
}

红黑树操作:

  1. search:就是简单的二叉搜索树的search,因此直接继承即可。(二叉搜索树https://blog.csdn.net/weixin_43462583/article/details/132050664
  2. 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算法,直接删除节点;

  • 如果删除后树变为空,直接返回;
  • 如果被删除节点是根节点,将根染黑,更新高度,直接返回;
  • 如果删除节点后,其所有祖先均没有黑高度失衡,直接返回
  • 如果被删除节点的接替者为红色,将其染黑,直接返回;
  • 如果被删除节点为黑色,接替者也为黑色,此时需要双黑修正。

双黑修正分为四种情况(双黑修正看兄弟)

  1. 接替者r的兄弟s为黑,但是至少有一个孩子,在其红孩子处rotateAT,新根节点和以前根节点p的颜色保持一致,新根的左右孩子(如果存在),染黑;   

  2. r的兄弟s为黑没有任何一个孩子,但是父亲p为,此时s染红,p染黑,无需向上传播;
  3. r的兄弟s为黑没有任何一个孩子,但是父亲p为黑,此时s染红,p不变,从p开始向上传播;
    

  4. 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
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值