C++ 實現(xiàn)紅黑樹(一)

紅黑樹的簡單介紹

紅黑樹(Red-Black Tree,簡稱R-B Tree)蛉签,它一種特殊的二叉查找樹胡陪。這意味著它滿足二叉查找樹的特征,但是也有許多自己的特性碍舍。
紅黑樹的特性:

  • 每個節(jié)點的顏色非黑及紅柠座。
  • 根節(jié)點是黑色。
  • 每個葉子節(jié)點是黑色片橡。 [注:此葉子節(jié)點妈经,是指為空的葉子節(jié)點!]
  • 如果一個節(jié)點是紅色的,則它的子節(jié)點必須是黑色的吹泡。
  • 從一個節(jié)點到該節(jié)點的子孫節(jié)點的所有路徑上包含相同數(shù)目的黑色節(jié)點骤星。

這里推薦一個數(shù)據(jù)結(jié)構(gòu)可視化的網(wǎng)站,可以查看多種數(shù)據(jù)結(jié)構(gòu):數(shù)據(jù)結(jié)構(gòu)可視化

實現(xiàn)

結(jié)點和樹的定義
enum RBTColor{Red,Black};

template <typename keytype>
class RBTNode{
    public:
        RBTColor color;
        keytype key;
        RBTNode *left;
        RBTNode *right;
        RBTNode *parent;
        RBTNode(keytype k,RBTColor c):key(k),color(c),left(nullptr),right(nullptr),parent(nullptr){}
};

template <typename keytype>
class RBTree{
    typedef RBTNode<keytype> Node;

    private:
        Node *RBTRoot;
        void __insert(Node *&root,const keytype key); //插入內(nèi)部接口
        void in_correct(Node *&root,Node *node); //插入修正接口
        void left_rotate(Node *&root,Node *x); //左旋
        void right_rotate(Node *&root,Node *y); //右旋
        void __traversal(Node *&root); //遍歷(中序)內(nèi)部接口
        void __remove(Node *&root,Node *node); //刪除內(nèi)部接口
        void out_correct(Node *&root,Node *node); //刪除修正接口
        Node *__search(Node *&root,const keytype key); //查找內(nèi)部接口
        Node *find_successor_node(Node *node); //尋找后繼結(jié)點
        void deleteTree(Node *&root); //刪除樹

    public:
        RBTree():RBTRoot(nullptr){};
        ~RBTree();
        bool search(const keytype key); //查找外部接口
        void insert(const keytype key); //插入外部接口
        void remove(const keytype key); //刪除外部接口
        void traversal(); //遍歷(中序)外部接口
};
左旋和右旋

詳細的注釋和代碼:

/*
 * 左旋示意圖(對節(jié)點x進行左旋):
 *       px                              px
 *      /                               /
 *     x                               y                
 *   /  \      ---(左旋)--->          / \  
 *  lx   y                          x   ry     
 *      / \                        / \
 *    ly  ry                     lx  ly  
 *
 */

//左旋
template <typename keytype>
void RBTree<keytype>::left_rotate(Node *&root,Node *x){
    Node *y = x->right; //設x的右孩子為y
    x->right = y->left; //將y的左孩子設為x的右孩子
    if(y->left != nullptr) y->left->parent = x; //將y左孩子的父親設為x
    y->parent = x->parent; //將y的父親設為x的父親
    //情況1:x的父親為空結(jié)點
    if(x->parent == nullptr) root = y; //將y設為根結(jié)點
    else{
        //情況2:x是其父親的左孩子
        if(x->parent->left == x) x->parent->left = y; //將y設為x父結(jié)點的左孩子
        //情況3:x是其父親的右孩子
        else x->parent->right = y; //將y設為x父結(jié)點的右孩子
    }
    y->left = x; //將x設為y的左孩子
    x->parent = y; //將y設為x的父結(jié)點
}

//右旋與左旋對稱
//右旋
template <typename keytype>
void RBTree<keytype>::right_rotate(Node *&root,Node *y){
    Node *x = y->left; //設y的左孩子為x
    y->left = x->right; //將x的左孩子設為y的右孩子
    if(x->right != nullptr) x->right->parent = y; //將x右孩子的父親設為y
    x->parent = y->parent; //將x的父親設為y的父親
    //情況1:y的父親為空結(jié)點
    if(y->parent == nullptr) root = x; //將x設為根結(jié)點
    else{
        //情況2:y是其父親的右孩子
        if(y->parent->right == y) y->parent->right = x; //將x設為y父結(jié)點的右孩子
        //情況3:y是其父親的左孩子
        else y->parent->left = x; //將x設為y父結(jié)點的作孩子
    }
    x->right = y; //將y設為x的右孩子
    y->parent = x; //將x設為y的父結(jié)點
}
插入
  1. 將紅黑樹當作二叉查找樹爆哑,插入結(jié)點
  2. 將結(jié)點著色為紅色
  3. 通過著色和旋轉(zhuǎn)操作洞难,使其重新成為一個紅黑樹
//插入
template <typename keytype>
void RBTree<keytype>::__insert(Node *&root,const keytype key){
    Node *node = new Node(key,Black); //新建結(jié)點
    //利用指針,非遞歸插入
    Node *p = nullptr;
    Node *q = root;
    while (q != nullptr){
        p = q;
        if(key < q->key) q = q->left;
        else q = q->right;
    }
    node->parent = p;
    if(p != nullptr){
        if(key < p->key) p->left = node;
        else p->right = node;
    }else root = node;
    node->color = Red;
    in_correct(root,node); //修正紅黑樹
}

這里將第三步細化為修正操作揭朝,如下

插入修正

根據(jù)插入結(jié)點的情況队贱,分為三種情況處理

  1. 插入的結(jié)點是根結(jié)點:將此結(jié)點著色為黑色
  2. 插入結(jié)點的父結(jié)點是黑色的:不做操作
  3. 插入結(jié)點的父結(jié)點是紅色的:

針對情況3,進一步細分為三種情況
(1) 插入結(jié)點的父結(jié)點是紅色的潭袱,且叔叔結(jié)點(即當前結(jié)點的祖父結(jié)點的另一個子結(jié)點)也是紅色
=>將父結(jié)點著色為黑色
=>將叔叔結(jié)點著色為黑色
=>將祖父結(jié)點著色為紅色
=>將祖父結(jié)點設為"當前結(jié)點"柱嫌,對"當前結(jié)點"繼續(xù)進行以上的操作

(2) 當前節(jié)點的父結(jié)點是紅色,叔叔節(jié)點是黑色屯换,且當前節(jié)點是其父節(jié)點的右孩子
=>將父結(jié)點設為”當前節(jié)點“
=>將”當前節(jié)點“進行左旋

(3) 當前節(jié)點的父結(jié)點是紅色编丘,叔叔節(jié)點是黑色,且當前節(jié)點是其父節(jié)點的左孩子
=>將父結(jié)點著色為黑色
=>將祖父結(jié)點著色為紅色
=>將祖父結(jié)點進行右旋

注:以上的三種情況趟径,針對當前節(jié)點的父結(jié)點是其祖父結(jié)點的左孩子瘪吏,則叔叔結(jié)點就是當前結(jié)點祖父結(jié)點的右孩子。若當前節(jié)點的父結(jié)點是其祖父結(jié)點的右孩子蜗巧,則上面操作中的"right"和"left"對調(diào),依次執(zhí)行蕾盯。

插入修正代碼:

//插入修正
template <typename keytype>
void RBTree<keytype>::in_correct(Node *&root,Node *node){
    Node *gparent = nullptr;
    while(node->parent != nullptr && node->parent->color == Red){
        gparent = node->parent->parent;
        if(node->parent == gparent->left){
            Node *uncle = gparent->right;
            if(uncle->color == Red){
                node->parent->color = Black;
                uncle->color = Black;
                gparent->color = Red;
                node = gparent;
            }else{
                if(node->parent->left == node){
                    node->parent->color = Black;
                    gparent->color = Red;
                    right_rotate(root,gparent);
                }else{
                    node = node->parent;
                    left_rotate(root,node);
                }
            }
        }else{
            Node *uncle = gparent->left;
            if(uncle->color == Red){
                node->parent->color = Black;
                uncle->color = Black;
                gparent->color = Red;
                node = gparent;
            }else{
                if(node->parent->right == node){
                    node->parent->color = Black;
                    gparent->color = Red;
                    right_rotate(root,gparent);
                }else{
                    node = node->parent;
                    left_rotate(root,node);
                }
            }
        }
    }
    root->color = Black;
}
遍歷(中序)
//遍歷(中序)內(nèi)部接口
template <typename keytype>
void RBTree<keytype>::__traversal(Node *&root){
    if(root == nullptr) return;
    __traversal(root->left);
    cout << root->key << " ";
    __traversal(root->right);
}

//遍歷(中序)外部接口
template <typename keytype>
void RBTree<keytype>::traversal(){
    __traversal(RBTRoot);
}
測試
int main(){
    RBTree<int> tree;
    tree.insert(20);
    tree.insert(10);
    tree.insert(50);
    tree.insert(15);
    tree.insert(40);
    tree.traversal();
    return 0;
}
測試結(jié)果
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末幕屹,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子级遭,更是在濱河造成了極大的恐慌望拖,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,807評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挫鸽,死亡現(xiàn)場離奇詭異说敏,居然都是意外死亡,警方通過查閱死者的電腦和手機丢郊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評論 3 399
  • 文/潘曉璐 我一進店門盔沫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人枫匾,你說我怎么就攤上這事架诞。” “怎么了干茉?”我有些...
    開封第一講書人閱讀 169,589評論 0 363
  • 文/不壞的土叔 我叫張陵谴忧,是天一觀的道長。 經(jīng)常有香客問我,道長沾谓,這世上最難降的妖魔是什么委造? 我笑而不...
    開封第一講書人閱讀 60,188評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮均驶,結(jié)果婚禮上昏兆,老公的妹妹穿的比我還像新娘。我一直安慰自己辣恋,他們只是感情好亮垫,可當我...
    茶點故事閱讀 69,185評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著伟骨,像睡著了一般饮潦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上携狭,一...
    開封第一講書人閱讀 52,785評論 1 314
  • 那天继蜡,我揣著相機與錄音,去河邊找鬼逛腿。 笑死稀并,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的单默。 我是一名探鬼主播碘举,決...
    沈念sama閱讀 41,220評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼搁廓!你這毒婦竟也來了引颈?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,167評論 0 277
  • 序言:老撾萬榮一對情侶失蹤境蜕,失蹤者是張志新(化名)和其女友劉穎蝙场,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體粱年,經(jīng)...
    沈念sama閱讀 46,698評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡售滤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,767評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了台诗。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片完箩。...
    茶點故事閱讀 40,912評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖拉庶,靈堂內(nèi)的尸體忽然破棺而出嗜憔,到底是詐尸還是另有隱情,我是刑警寧澤氏仗,帶...
    沈念sama閱讀 36,572評論 5 351
  • 正文 年R本政府宣布吉捶,位于F島的核電站夺鲜,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏呐舔。R本人自食惡果不足惜币励,卻給世界環(huán)境...
    茶點故事閱讀 42,254評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望珊拼。 院中可真熱鬧食呻,春花似錦、人聲如沸澎现。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽剑辫。三九已至干旧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間妹蔽,已是汗流浹背椎眯。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留胳岂,地道東北人编整。 一個月前我還...
    沈念sama閱讀 49,359評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像乳丰,于是被迫代替她去往敵國和親掌测。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,922評論 2 361