Nginx源码初探之数据结构 - 红黑树数据结构

Nginx红黑树介绍

       红黑树是Nginx里的高级数据结构。什么是红黑树呢?红黑树是一种平衡二叉树,简单的说就是它是一棵空树,或者左右子树高差绝对值不超过1,且左右子树都是平衡二叉树。时间复杂度O(log n)。除此之外还有如下特性

(1)节点都有颜色,要么黑色,要么红色。
(2)根结点是黑色的,叶子结点是黑色的。
(3)每条从树到叶子的路上没有二个连续的红色。即红色的子树一定是黑色,黑色节点的子树不一定完全是红色。
(4)各条从叶子到根的路上的黑色结点相同。

其他的内容不再本文叙述,建议自行补充,红黑二叉树对比平衡二叉树理解很容易,多的一点规则就是节点颜色,下图我根据规则完成了一张节点颜色图,最主要说明规则3.红色节点的子树一定是黑色,如下图:

1.数据结构

1.1 红黑树节点
 

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;/*数据*/
};
struct ngx_rbtree_s {
    ngx_rbtree_node_t     *root;/*根节点元素*/
    ngx_rbtree_node_t     *sentinel;/*指向NIL节点*/
    ngx_rbtree_insert_pt   insert;/*节点插入操作,可以解决关键字冲突*/
};

       红黑树的节点在很多场景下允许不同的节点拥有相同的关键字。例如不同的字符串可能会散列出相同的HASH值(可以参考ngx_hash_t来理解为什么不同的字符串会散列出相同的HASH值)。这种情况下在添加时就不能覆盖原有节点,而是作为新节点插存在。

2.红黑树初始化

#define ngx_rbtree_init(tree, s, i)                                           
    ngx_rbtree_sentinel_init(s);/*初始化sentinel节点,sentinel节点必须是黑色的,参考ngx_rbtree_sentinel_init*/                                              
    (tree)->root = s;                                                          
    (tree)->sentinel = s;                                                     
    (tree)->insert = i/*插入新节点*/

3.红黑色插入节点

        红黑树节点插入主要有两个问题,一个是数据顺序的问题,这个满足平衡二叉树即可。另一个问题是节点颜色问题,插入操作大部分时间都是在维护颜色的稳定。

扫描二维码关注公众号,回复: 8587557 查看本文章
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;
    /*1.如果root节点为nil,则初始化红黑树,具体就是:
	*1.父节点为null,这点很容易理解,它自己就是根节点了
	*2.左右节点为sentinel,红黑树的叶子节点必须是nil或者null
	*3.节点颜色是黑色,红黑树的根节点必须是黑色
	*/
    if (*root == sentinel) {
        node->parent = NULL;
        node->left = sentinel;
        node->right = sentinel;
        ngx_rbt_black(node);
        *root = node;

        return;
    }
    /*2.将节点作为一个新节点插入红黑树,需要注意的一点,可能也是大家在学习构建红黑树时一直忽略的一点。
	*插入后并没有完成红黑树,红黑树为了保证结构的规范性,需要进行一系列的数据调整,调整完成保证数据插入后,**依然是红黑树。
	*/
    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)) {
                ngx_rbt_black(node->parent);
                ngx_rbt_black(temp);
                ngx_rbt_red(node->parent->parent);
                node = node->parent->parent;

            } else {
                if (node == node->parent->right) {
                    node = node->parent;
                    ngx_rbtree_left_rotate(root, sentinel, 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)) {
                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);
}
发布了15 篇原创文章 · 获赞 1 · 访问量 352

猜你喜欢

转载自blog.csdn.net/mlydaemon/article/details/103948880