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);  
}  




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