/* 由紅黑兩色節點組成的二叉搜索樹若知足如下條件,即爲"紅黑樹(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 } }