Nginx高級數據結構源碼分析(五)-----紅黑樹

紅黑樹是一種二叉平衡樹,在每一個結點增加了一個存儲位表示結點的顏色,以維持它的平衡。

紅黑樹性質

(1)紅黑樹結點有如下域:color,key,left,right,p;我們把這些NIL結點是爲指向外結點的指針,可以自己定義;

(2)每一個結點不是紅的就是黑的,根節點和NIL結點都是黑的

(3)如果一個節點是紅的,那麼它的父親和兩個孩子都是黑的

(4)對於每一個結點,它到其子孫結點的所有路徑上的包含相同數目的black結點,並不要求父節點到每一層的黑高度相同;

注1:可以使用同一的一個NIL來表示所有的葉子結點和根部的父節點,這樣可以減少浪費內存,該NIL的父親就是上述的普通結點,這樣查找不成功時,就可以重新回到根節點root;
注2:設某個結點x出發到達任意一個節點的任意路徑上,黑結點的個數稱爲該節點的黑高度,bh(x)表示;可歸納證明出根節點至少有2^h-1個結點,故n>=2^h-1,求得h<=2lg(n+1);


紅黑樹插入

(1)爲了保持上述紅黑樹的性質(4),插入的節點z顏色一定是紅色,其父節點爲p;

(2)若剛開始樹爲空,則z是根節點,p爲NIL節點,則將z的結點改爲黑色;

(3)若p的顏色是黑色,則直接插入,因爲紅黑樹的性質完全保持

(4)若p的顏色是紅色,違反紅黑樹的性質3,此時要討論情況;

(4.1)若p是p父親的左孩子;

(4.1.1)若p的兄弟節點ps是紅色,則改變p和ps爲黑色,p父節點改爲紅色,此時p父節點爲新的z,繼續判別;

(4.1.2)若p的兄弟節點ps是黑色,且z是父親的右孩子,做一次左旋,到情況(4.1.3);

(4.1.3)若p的兄弟節點ps是黑色,且z是父親的左孩子,做一次右旋,改變z父親顏色爲黑色,z祖父顏色爲紅色,完畢;

(4.2)若p是p父親的右孩子;情況與(4.1)類似,執行相反的動作;

(5)始終將根節點的顏色置爲黑,因爲處理時根節點有可能變紅;

(6)時間複雜度爲O(lgn),空間複雜度爲O(1);


紅黑樹刪除

(1)首先根據二叉平衡樹的刪除原理,找到被刪除的節點y,x是y的子女(肯定只有一個),x會連接到y的父親上;

(2)若y爲紅色,直接刪掉,因爲紅黑樹的性質保持不變;

(3)若y爲黑色節點,x爲紅色,則直接刪掉y,將x變爲黑色;

(4)若y爲黑色節點,x(可以爲NIl節點)爲黑色,則刪掉y,將x變爲雙黑色

(4.1)若x是x父親的左孩子,分別有四種情況來討論;

(4.1.1)若x的兄弟是紅色,則左旋一次,改變兄弟顏色爲黑,父親顏色爲紅,則它仍爲新的x,繼續;

(4.1.2)若x的兄弟是黑色,且x兄弟的兩個孩子都是黑色,則去掉x的一重黑色,將兄弟顏色變爲紅,將去掉的一重黑色,給x的父親,若x父親是紅色,則直接改成黑色,否則將變成雙黑,成爲新的x,繼續;

(4.1.3)若x的兄弟是黑色,且x兄弟的右孩子黑色,左孩子是紅色,則先將x兄弟那一塊右旋,改色,使得x兄弟的右孩子變爲紅色;進入(4.1.4);

(4.1.4)若x的兄弟是黑色,且x兄弟的右孩子紅色,左孩子可任意色,則左旋,改變x兄弟右孩子顏色爲黑色,兄弟變爲父親的顏色,父親變爲黑色,自己去掉一重顏色,完畢;

(4.2)若x是x父親的右孩子,分別也有四種情況來討論,但與(4.1)對稱,相反來處理;

(5)若根爲雙黑,直接變爲單黑,返回;

(6)時間複雜度爲O(lgn),空間複雜度爲O(1);


紅黑樹定義:

typedef struct ngx_rbtree_node_s  ngx_rbtree_node_t;  
  
//紅黑樹節點的定義  
struct ngx_rbtree_node_s {  
    ngx_rbtree_key_t       key;   //關鍵字  
    ngx_rbtree_node_t     *left;  //左子節點  
    ngx_rbtree_node_t     *right; //右子節點  
    ngx_rbtree_node_t     *parent;//父節點  
    u_char                 color; //顏色,非紅即黑  
    u_char                 data;  //1個字節的節點數據,空間較小很少使用;  
};  
  
typedef struct ngx_rbtree_s  ngx_rbtree_t;  
  
//爲解決不同節點含有相同關鍵字元素衝突問題,設置該指針,靈活的添加衝突元素  
typedef void (*ngx_rbtree_insert_pt) (ngx_rbtree_node_t *root,  
    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);  
  
//紅黑樹的定義  
struct ngx_rbtree_s {  
    ngx_rbtree_node_t     *root;      //指向樹的根節點,根結點也是數據元素  
    ngx_rbtree_node_t     *sentinel;  //指向哨兵NIL節點  
    ngx_rbtree_insert_pt   insert;    //添加元素的函數指針  
};  

左旋轉和右旋轉

//左旋轉  
static ngx_inline void  
ngx_rbtree_left_rotate(ngx_rbtree_node_t **root, ngx_rbtree_node_t *sentinel,  
    ngx_rbtree_node_t *node)  
{  
    ngx_rbtree_node_t  *temp;  
  
    temp = node->right;     //要旋轉節點的右孩子p  
    node->right = temp->left; //當前節點的右孩子指向改變p的左孩子  
  
    if (temp->left != sentinel) {  //不是NIL節點時  
        temp->left->parent = node; //修改父親的指向  
    }  
  
    temp->parent = node->parent;  //p指向要旋轉節點的父親  
  
    if (node == *root) {   //如果當前旋轉節點的是根結點,那麼根指向要改變  
        *root = temp;  
  
    } else if (node == node->parent->left) {  //左孩子時  
        node->parent->left = temp;  //新的左孩子  
  
    } else {  
        node->parent->right = temp; //新的右孩子  
    }  
  
    temp->left = node;   //p的左孩子指向  
    node->parent = temp; //父親改變  
}  
  
//右旋轉,與上述左邊旋轉對稱  
static ngx_inline void  
ngx_rbtree_right_rotate(ngx_rbtree_node_t **root, ngx_rbtree_node_t *sentinel,  
    ngx_rbtree_node_t *node)  
{  
    ngx_rbtree_node_t  *temp;  
  
    temp = node->left;  
    node->left = temp->right;  
  
    if (temp->right != sentinel) {  
        temp->right->parent = node;  
    }  
  
    temp->parent = node->parent;  
  
    if (node == *root) {  
        *root = temp;  
  
    } else if (node == node->parent->right) {  
        node->parent->right = temp;  
  
    } else {  
        node->parent->left = temp;  
    }  
  
    temp->right = node;  
    node->parent = temp;  
}  
插入節點:

void  
ngx_rbtree_insert_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node,  
    ngx_rbtree_node_t *sentinel)  
{  
    //temp爲初始時爲指向根節點  
      
    ngx_rbtree_node_t  **p;  
  
    for ( ;; ) {  
  
        p = (node->key < temp->key) ? &temp->left : &temp->right;  //目標節點的值小於當前節點的值時,向左走即走向左節點,否則向右  
  
        if (*p == sentinel) {   //一直找到該節點的孩子指向的是NIL節點,直接break  
            break;  
        }  
  
        temp = *p;      //改變方向  
    }  
  
    //注意使用地址的地址,才能改變某節點的孩子的指向,否則改變不了  
    *p = node;        //該處指向新的節點,*p(某節點的孩子)本來指向sentinel,現在指向node了  
    node->parent = temp;  
    node->left = sentinel;  //指向NIL  
    node->right = sentinel;  
    ngx_rbt_red(node);  //插入的節點均爲紅色  
}  

void  
ngx_rbtree_insert(ngx_rbtree_t *tree, ngx_rbtree_node_t *node)  
{  
    ngx_rbtree_node_t  **root, *temp, *sentinel;  
  
    /* a binary tree insert */  
  
    root = (ngx_rbtree_node_t **) &tree->root;  //指向根節點指針的指針,可改變指向根節點指針的指向  
    sentinel = tree->sentinel;        //取出NIL節點  
  
    if (*root == sentinel) {         //說明此時紅黑樹爲空  
        node->parent = NULL;  
        node->left = sentinel;      //指向NIL  
        node->right = sentinel;  
        ngx_rbt_black(node);        //node節點直接置爲黑  
        *root = node;               //根節點直接指向該節點  
  
        return;  
    }  
  
    tree->insert(*root, node, sentinel);     
    //插入節點,如ngx_rbtree_insert_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)  
      
    /* re-balance tree */  
  
    //紅黑樹平衡  
      
    //當前節點不爲根節點,而且當前節點的父親爲紅色時  
    while (node != *root && ngx_rbt_is_red(node->parent)) {  
  
        if (node->parent == node->parent->parent->left) {   //當前節點的父親是左節點時  
            temp = node->parent->parent->right;    //當前節點的父親的右兄弟  
  
            //向上轉移  
            if (ngx_rbt_is_red(temp)) {    
                //對應算法導論第三版P179的情況1  
                           
                ngx_rbt_black(node->parent);      //當前節點的父親變黑  
                ngx_rbt_black(temp);              //當前節點的父親的右兄弟變黑  
                ngx_rbt_red(node->parent->parent);//當前節點的父親的父親變紅  
                node = node->parent->parent;      //當前節點變爲該當前節點的父親的父親  
  
            } else {  //當前節點的父親的右兄弟爲黑色時  
              
                //對應算法導論第三版P179的情況2  
                  
                //當前節點是右節點時,先左旋轉  
                if (node == node->parent->right) {  
                    node = node->parent;   //當節點變爲父節點  
                    ngx_rbtree_left_rotate(root, sentinel, node);  //當前節點左旋轉  
                }  
  
                //對應算法導論第三版P179的情況3  
                ngx_rbt_black(node->parent);  //當前節點的父親變爲黑色  
                ngx_rbt_red(node->parent->parent); //當前節點的父親的父親變爲黑色  
                ngx_rbtree_right_rotate(root, sentinel, node->parent->parent);  //當前節點父親的父親右旋轉  
            }  
  
        } else {   //相反,對稱操作  
            temp = node->parent->parent->left;  
  
            if (ngx_rbt_is_red(temp)) {  
                ngx_rbt_black(node->parent);  
                ngx_rbt_black(temp);  
                ngx_rbt_red(node->parent->parent);  
                node = node->parent->parent;  
  
            } else {  
                if (node == node->parent->left) {  
                    node = node->parent;  
                    ngx_rbtree_right_rotate(root, sentinel, node);  
                }  
  
                ngx_rbt_black(node->parent);  
                ngx_rbt_red(node->parent->parent);  
                ngx_rbtree_left_rotate(root, sentinel, node->parent->parent);  
            }  
        }  
    }  
  
    ngx_rbt_black(*root);  //最後始終着色根結點爲黑色  
}  

刪除節點:

void  
ngx_rbtree_delete(ngx_rbtree_t *tree, ngx_rbtree_node_t *node)  
{  
    ngx_uint_t           red;  
    ngx_rbtree_node_t  **root, *sentinel, *subst, *temp, *w;  
  
    /* a binary tree delete */  
  
    root = (ngx_rbtree_node_t **) &tree->root;  
    sentinel = tree->sentinel;  
  
    //subst爲要替代的節點  
    //temp爲將要替代節點的孩子  
    if (node->left == sentinel) {   //刪除節點爲左孩子爲NIL節點  
        temp = node->right;     
        subst = node;  
  
    } else if (node->right == sentinel) {  
        temp = node->left;  
        subst = node;  
  
    } else {  
        subst = ngx_rbtree_min(node->right, sentinel);  //找到以刪除節點右孩子爲根的最小值節點  
  
        if (subst->left != sentinel) {    
            temp = subst->left;  
        } else {                        //通過ngx_rbtree_min找到的,該最小值節點的左孩子一定是NIL  
            temp = subst->right;  
        }  
    }  
  
    if (subst == *root) {  //subst爲要刪除的節點爲根節點時  
        *root = temp;     //指向新的根節點  
        ngx_rbt_black(temp);  
  
        /* DEBUG stuff */  
        node->left = NULL;  
        node->right = NULL;  
        node->parent = NULL;  
        node->key = 0;  
  
        return;  
    }  
  
    red = ngx_rbt_is_red(subst);    //直接取顏色  
  
    if (subst == subst->parent->left) {     
        subst->parent->left = temp;   //父親的左孩子指向要刪除節點的左孩子  
  
    } else {  
        subst->parent->right = temp;  //父親的右孩子指向要刪除節點的右孩子  
    }  
  
    if (subst == node) {   //要刪除的節點爲原始的節點時  
  
        temp->parent = subst->parent;  //父親節點指向的改變  
  
    } else {  
  
        if (subst->parent == node) {       //通過ngx_rbtree_min一開始就不成立,即node->right的left爲NIL節點  
            temp->parent = subst;          
  
        } else {  
            temp->parent = subst->parent;    //通過ngx_rbtree_min找到的  
        }  
  
        subst->left = node->left;   //指向node的左孩子  
        subst->right = node->right; //指向node的右孩子  
        subst->parent = node->parent;  
        ngx_rbt_copy_color(subst, node);  //將node節點的顏色複製給subst節點  
  
        if (node == *root) {  
            *root = subst;    //指向新的根節點  
  
        } else {  
            if (node == node->parent->left) {  
                node->parent->left = subst;  //改變父親孩子指向到subst  
            } else {  
                node->parent->right = subst;  
            }  
        }  
  
        if (subst->left != sentinel) {    //改變subst孩子的指向  
            subst->left->parent = subst;  
        }  
  
        if (subst->right != sentinel) {  
            subst->right->parent = subst;  
        }  
    }  
  
    /* DEBUG stuff */  
    node->left = NULL;  
    node->right = NULL;  
    node->parent = NULL;  
    node->key = 0;  
  
    if (red) {    //如果刪除的節點的顏色爲紅色,直接返回  
        return;  
    }  
  
    //平衡  
    /* a delete fixup */  
  
    //孩子節點,如果孩子節點爲黑色時  
    while (temp != *root && ngx_rbt_is_black(temp)) {  
  
        if (temp == temp->parent->left) {   //temp爲左孩子時  
            w = temp->parent->right;    //temp的右兄弟  
  
            //算法導論第三版P186中的情況a  
            if (ngx_rbt_is_red(w)) {  //temp的右兄弟爲紅色時  
                ngx_rbt_black(w);     //temp的右兄弟變爲黑色  
                ngx_rbt_red(temp->parent); //temp的父親變爲紅色色  
                ngx_rbtree_left_rotate(root, sentinel, temp->parent);   //以temp->parent左旋轉  
                w = temp->parent->right;    //新的右兄弟,將會到以下幾種情況  
            }  
  
            //算法導論第三版P186中的情況b  
            //當temp的右兄弟的兩個孩子均爲黑色時  
            if (ngx_rbt_is_black(w->left) && ngx_rbt_is_black(w->right)) {  
                ngx_rbt_red(w); //將右兄弟變爲紅色  
                temp = temp->parent;    //將以新的temp進行新的一次循環  
  
            } else {  
                //算法導論第三版P186中的情況c  
                if (ngx_rbt_is_black(w->right)) {   //如果右兄弟的右孩子是黑色的  
                    ngx_rbt_black(w->left);   //w的左孩子變黑  
                    ngx_rbt_red(w);           //w變紅  
                    ngx_rbtree_right_rotate(root, sentinel, w);   //以w右旋  
                    w = temp->parent->right;  //新的右兄弟,直接執行情況c  
                }  
  
                //算法導論第三版P186中的情況d  
                ngx_rbt_copy_color(w, temp->parent);  //右兄弟變成父親的顏色  
                ngx_rbt_black(temp->parent);  //temp的父親變黑  
                ngx_rbt_black(w->right);      //右兄弟的右孩子變黑  
                ngx_rbtree_left_rotate(root, sentinel, temp->parent);  //以temp的父親右旋  
                temp = *root;  //結束,直接指向根結點  
            }  
  
        } else {  //與上述情況相反,做對稱操作  
            w = temp->parent->left;  
  
            if (ngx_rbt_is_red(w)) {  
                ngx_rbt_black(w);  
                ngx_rbt_red(temp->parent);  
                ngx_rbtree_right_rotate(root, sentinel, temp->parent);  
                w = temp->parent->left;  
            }  
  
            if (ngx_rbt_is_black(w->left) && ngx_rbt_is_black(w->right)) {  
                ngx_rbt_red(w);  
                temp = temp->parent;  
  
            } else {  
                if (ngx_rbt_is_black(w->left)) {  
                    ngx_rbt_black(w->right);  
                    ngx_rbt_red(w);  
                    ngx_rbtree_left_rotate(root, sentinel, w);  
                    w = temp->parent->left;  
                }  
  
                ngx_rbt_copy_color(w, temp->parent);  
                ngx_rbt_black(temp->parent);  
                ngx_rbt_black(w->left);  
                ngx_rbtree_right_rotate(root, sentinel, temp->parent);  
                temp = *root;  
            }  
        }  
    }  
  
    //temp爲紅色時,直接將temp置爲黑色  
    ngx_rbt_black(temp);  
}  




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章