紅黑樹

/*
 由紅黑兩色節點組成的二叉搜索樹若知足如下條件,即爲"紅黑樹(red-black tree)"
 (1)樹根始終爲黑色
 (2)外部節點均爲黑色
 (3)其他節點若爲紅色,則其孩子節點必爲黑色
 (4)從任一外部節點到根節點的沿途,黑節點的數目相等
 */


#include "BST.hpp"

template <typename T> class RedBlack : public BST<T> {
protected:
    void solveDoubleRed(BinNodePosi(T) x); //雙紅修正
    void solveDoubleBlack(BinNodePosi(T) x); //雙黑修正
    int updateHeight(BinNodePosi(T) x); //更新節點x的高度
public:
    BinNodePosi(T) insert(const T& e) override;
    bool remove(const T& e) override;
    
    //BST::search()等其他接口可直接沿用
};

#define IsBlack(p)  (!(p) || (RB_BLACK == (p)->color))       //外部節點也視作黑節點
#define IsRed(p) (!IsBlack(p))   //非黑即紅
#define BlackHeightUpdated(x)  (/*RedBlack高度更新條件*/\
(stature((x).lc) == stature((x).rc) ) && \
((x).height == ( IsRed(& x) ? stature( (x).lc ) : stature( (x).rc ) + 1 ) ) \
)


//更新節點高度(這裏的height指紅黑樹的黑高度)
template <typename T> int RedBlack<T>::updateHeight(BinNodePosi(T) x) {
    x->height = std::max(stature(x->lc), stature(x->rc));  //孩子通常黑高度相等,除非出現雙黑
    return IsBlack(x) ? x->height++ : x->height; //若當前節點爲黑,則計入黑深度
}//因統必定義stature(NULL)=-1,故height比黑高度少一,好在不至於影響到各類算法的比較判斷

template <typename T> BinNodePosi(T) RedBlack<T>::insert(const T &e) {
    BinNodePosi(T) & x = this -> search(e);
    if (x) {
        return x;  //確認目標不存在(留意對_hot的設置)
    }
    
    x = new BinNode<T>(e, this->_hot,NULL,NULL,-1); //建立紅節點x;以_hot爲父,黑高度-1
    this->_size++;
    
    solveDoubleRed(x);
    return x ? x : this->_hot->parent;  //經雙紅修正後,便可返回
}//不管e是否存在於原樹中,返回時總有x->data==e


template <typename T> void RedBlack<T>::solveDoubleRed(BinNodePosi(T) x) {
    //x當前必爲紅
    if ( IsRoot(*x)) {
        //若已(遞歸)轉至樹根,則將其轉黑,整樹黑高度也隨之遞增
        this->_root->color = RB_BLACK;
        this->_root->height++;
        return;
    }//不然,x的父親p必存在
    
    BinNodePosi(T) p = x -> parent;
    if (IsBlack(p)) {
        return;//若p爲黑,則可終止調整
    }
    
    BinNodePosi(T) g = g->parent;  //既然p爲紅,則x的祖父必存在,且必爲黑色
    BinNodePosi(T) u = uncle(x);  //如下,視x叔父u的顏色分別處理
    
    if (IsBlack(u)) {  //u爲黑色,含NULL時
        if (IsLChild(*x) == IsLChild(*p)) { //若x與p同側(即zig-zig或zag-zag),則p由紅轉黑,x保持紅
            p->color = RB_BLACK;
        }else{  //若x與p異側(即zig-zag或zag-zig),則x由紅轉黑,p保持紅
            x->color = RB_BLACK;
        }
        
        g->color = RB_RED; //g一定由黑轉紅
        //以上雖保證總共兩次染色,但因增長了判斷而得不償失
        //在旋轉後將根值黑、孩子置紅,雖需三次染色但效率更高
        BinNodePosi(T) gg = g->parent;
        BinNodePosi(T) r = FromParentTo(*g, this->_root) = this->rotateAt(x); //調整後的子樹根節點
        r -> parent = gg; //與原曾祖父相連
    }else{
        //若u爲紅色
        p->color = RB_BLACK;
        p->height++;
        u->color = RB_BLACK;
        u->height++;
        
        if (!IsRoot(*g)) {
            g->color = RB_RED; //g若非根,則轉紅
        }
        solveDoubleRed(g); //繼續調整g(相似於尾遞歸,可優化爲迭代形式)
    }
    
}

//從紅黑樹中刪除關鍵碼
template <typename T> bool RedBlack<T>::remove(const T &e) {
    BinNodePosi(T) & x = this->search(e);
    if (!x) {
        return false;
    }
    
    BinNodePosi(T) r = removeAt(x, this->_hot);
    if (!(--this->_size)) {
        return true;
    }
    
    //assert:_hot某一孩子剛被刪除,且被r所指節點(多是NULL)接替。如下檢查時候失衡,並作必要調整
    if (!this->_hot) {
        //若剛被刪除的是根節點,則將其置黑,並更新黑高度
        this->_root->color = RB_BLACK;
        updateHeight(this->_root);
        return true;
    }
    
    //assert:如下,原x(現r)必非根,_hot必非空
    if (BlackHeightUpdated(*this->_hot)) {
        return true;
    }
    
    if (IsRed(r)) { //不然,若r爲紅,則只需令其轉黑
        r->color = RB_BLACK;
        r->height++;
        return true;
    }
    
    //assert: 如下,原x(現r)均爲黑色
    solveDoubleBlack(r);
    return true;       //經雙黑調整後返回
}//若目標節點存在且被刪除,返回true;不然返回false


template <typename T> void RedBlack<T>::solveDoubleBlack(BinNodePosi(T) r) {
    BinNodePosi(T) p = r ? r->parent : this->_hot;
    if (!p) {
        return;
    }
    
    BinNodePosi(T) s = ( r == p->lc) ? p->rc : p->lc;  //r的兄弟
    
    if (IsBlack(s)) {
        //兄弟s爲黑
        BinNodePosi(T) t = NULL; //s的紅孩子(若左右孩子皆紅,左者優先;皆黑時爲NULL)
        
        if (IsRed(s->rc)) {
            t = s->rc;  //右子
        }
        if (IsRed(s->lc)) {
            t = s->lc;//左子
        }
        
        if (t) {  //黑s由紅孩子:BB-1
            RBColor oldColor = p->color;//備份原子樹根節點p的顏色,並對t及其父親、祖父
            //如下,經過旋轉重平衡,並將新子樹的左右孩子染黑
            BinNodePosi(T) b = FromParentTo(*p, this->_root) = this->rotateAt(t);  //旋轉
            if (HasLChild(*b)) {
                b->lc->color = RB_BLACK;
                updateHeight(b->lc); //左子
            }
            
            if (HasRChild(*b)) {
                b->rc->color = RB_BLACK;
                updateHeight(b->rc);  //右子
            }
            
            b->color = oldColor;
            updateHeight(b);  //新子樹根節點繼承原根節點的顏色
        }else{
            //黑s無紅孩子
            s->color = RB_RED;
            s->height--;      //s轉紅
            
            if (IsRed(p)) { //BB-2R
                p -> color = RB_BLACK; //p轉黑,但黑高度不變
            }else{  //BB-2B
                p->height--;   //p保持黑,但黑高度降低
                solveDoubleBlack(p);  //遞歸上溯
            }
        }
        
    }else{
        //兄弟s爲紅:BB-3
        s->color = RB_BLACK;
        p->color = RB_RED;  //s轉黑,p轉紅
        
        BinNodePosi(T) t = IsLChild(*s) ? s->lc : s->rc; //取t與其父s同側
        this->_hot = p;
        FromParentTo(*p, this->_root) = this->rotateAt(t); //對t及其父親、祖父作平衡調整
        solveDoubleBlack(r); //繼續修正r處雙黑---此時的p已轉紅,古後續只能是BB-1或BB-2R
        
    }
}
相關文章
相關標籤/搜索