nginx中红黑树代码注释

代码在src/core/ngx_rbtree.h和src/core/ngx_rbtree.c中

插入修复函数


/* 插入节点并且修复红黑树 */
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 = &tree->root;
    sentinel = tree->sentinel;

    if (*root == sentinel) { /* 空树,直接插入一个黑色节点 */
        node->parent = NULL;
        node->left = sentinel;
        node->right = sentinel;
        ngx_rbt_black(node);
        *root = node;

        return;
    }

    tree->insert(*root, node, 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)) {     /* 叔叔节点是红色的话,将叔叔节点和父节点改为黑色
                                                ,将祖父接点改为红色,并将祖父节点看成插入节点。
                                                这种情况记为LLr LRr */
                ngx_rbt_black(node->parent);
                ngx_rbt_black(temp);
                ngx_rbt_red(node->parent->parent);
                node = node->parent->parent;

            } else {
                /* LRb 先左旋转修正为LLb,此时父节点就看成插入的节点 */
                if (node == node->parent->right) {
                    node = node->parent;
                    ngx_rbtree_left_rotate(root, sentinel, node);
                }
                /* 此时改变颜色会将叔叔节点分支少了一个黑色节点,因此进行右旋转将node->parent成为叔叔节点和node
                    的公共节点,且不再需要循环下去*/
                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)) { /* RLr RRr */
                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) { /* 先右旋转父节点成RRb */
                    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); /* 根节点是黑色节点 */
}

删除节点并修复

// 红黑树的删除
// tree是红黑树结构体
// node是待删除的节点
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 = &tree->root;
    sentinel = tree->sentinel;
    // 对于node只有一棵子树,那么node就是实际被删除的节点,并且node这个位置就会被其唯一的子树替代
    // 也就是以temp为根的子树,注意这里从根到temp子树的所有路径会缺少一个节点,也就是node,如果node
    // 是黑色就需要修复
    if (node->left == sentinel) {
        temp = node->right;
        subst = node;

    } else if (node->right == sentinel) {
        temp = node->left;
        subst = node;

    }
    // node有两棵子树,subst是其右子树的最左节点,这里的subst将会替代node,并且将subst的颜色设置为node颜色,
    // 那么问题就转化为删除的节点只有一课子树的情况了,因为这样可以看做删除的是subst。
    else {
        subst = ngx_rbtree_min(node->right, sentinel);
        if (subst->left != sentinel) {
            temp = subst->left;
        } else { 
            temp = subst->right;
        }
    }
    // 以上做了统一处理,保证删除的节点只有一棵子树
    // 也就是说真正需要修复的以temp为根的子树少了一个黑色节点

    // 树只有一棵子树,且被删除的节点是根 
    if (subst == *root) {
        *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); 

    // 处理temp指针问题

    // subst的位置将会被temp替代
    // 修改subst->parent和temp的关系
    // 留意一点:如果subst->parent==node这种情况,node的右儿子指针已经被修改了
    // 后面再处理subst的指针关系是可以统一处理,减少重复代码
    if (subst == subst->parent->left) {
        subst->parent->left = temp;

    } else {
        subst->parent->right = temp; 
    }

    // 找temp的父节点稍微复杂点

    // node只有一棵子树,且node不为根
    // 只需要将以temp为根的子树放到node位置就可以了
    if (subst == node) {

        temp->parent = subst->parent;

    }
    // node有两棵子树
    else {
        // subst是node的右儿子
        // 这种情况subst是node的右儿子,且没有左子树
        // 只需要修改subst的左儿子指针指向node的左儿子就可以了
        // 但是上面已经设置好node的右指针了,这样设置subst的左右指针就能够和其他情况统一处理了
        if (subst->parent == node) {
            temp->parent = subst;
            // subst->left = node->left;

        }
        // node的右儿子有左子树
        else {
            temp->parent = subst->parent;
            // subst->left = node->left;
            // subst->right = node->right;
        }
        // 上面处理完temp的指针问题了

        // 处理subst的指针问题

        // 下面两行代码可以统一处理两种不同情况的前提是需要提前修改好node的儿子指针, Line62
        subst->left = node->left;
        subst->right = node->right; 

        subst->parent = node->parent;
        // 将subst的颜色改为和node颜色一致,这样能够保证根到node(subst)左子树是平衡的
        ngx_rbt_copy_color(subst, node);

        if (node == *root) {
            *root = subst;

        } else {
            if (node == node->parent->left) {
                node->parent->left = subst;
            } else {
                node->parent->right = subst;
            }
        }

        if (subst->left != sentinel) {
            subst->left->parent = subst;
        }

        if (subst->right != sentinel) {
            subst->right->parent = subst;
        }
    }

    // 上面代码完成subst替换node,下面代码修复以temp为根的子树的平衡

    /* DEBUG stuff */
    node->left = NULL;
    node->right = NULL;
    node->parent = NULL;
    node->key = 0;

    // subst是红色节点,那么以temp为根的子树就符合平衡情况,黑色节点数目不变
    if (red) {
        return;
    }

    /* a delete fixup */
    // 此时目标是将从根到temp节点的路径中增加一个黑色节点
    // 如果temp是红色,只需要将temp染成黑色节点就完成目标
    // 且如果temp为根节点,那么整个树都将少一个黑色节点,也不需要修复
    // 且此时temp必有兄弟节点
    while (temp != *root && ngx_rbt_is_black(temp)) {

        if (temp == temp->parent->left) {
            w = temp->parent->right;
            // 兄弟节点是红色的话,父节点必然是黑色
            // 将兄弟节点染黑,并对父节点左旋转,那么从根到temp节点的路径就多了个黑色节点
            if (ngx_rbt_is_red(w)) {
                ngx_rbt_black(w);
                ngx_rbt_red(temp->parent);
                ngx_rbtree_left_rotate(root, sentinel, temp->parent);
                w = temp->parent->right; // 此时以temp为根的子树还是少一个黑色节点
            }

            // w是黑色,且其左右儿子都是黑色,改变w颜色,现在以temp->parent为根的子树就少了个黑色节点,继续循环处理
            if (ngx_rbt_is_black(w->left) && ngx_rbt_is_black(w->right)) {
                ngx_rbt_red(w);
                temp = temp->parent;
            } 
            else {
                // 这里并不关心左儿子的颜色
                // w是黑色,右儿子是黑色,先对w为根的子树做右旋转,转换成temp的兄弟节点的右儿子是红色
                // 目的就是保证对temp->parent左旋转的时候,也就是往从根到temp的路径上增加个parent节点,且染成黑色
                // 建议画图才能弄的比较清楚
                if (ngx_rbt_is_black(w->right)) {
                    ngx_rbt_black(w->left);
                    ngx_rbt_red(w);
                    ngx_rbtree_right_rotate(root, sentinel, w);
                    w = temp->parent->right;
                }

                ngx_rbt_copy_color(w, temp->parent);
                ngx_rbt_black(temp->parent);
                ngx_rbt_black(w->right);
                ngx_rbtree_left_rotate(root, sentinel, temp->parent);
                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;
            }
        }
    }

    ngx_rbt_black(temp);
}

猜你喜欢

转载自blog.csdn.net/w1157984197/article/details/81202442