当前位置: 首页 > 工具软件 > ngx-treeview > 使用案例 >

nginx之ngx_rbtree

韩宜春
2023-12-01

nginx实现了一个红黑树的容器提供使用,定义在ngx_rbtree里面,这个容器相对简单,提供了插入,删除,初始化等方法。
ngx_rbtree.h:

//ngx_rbtree_key为unsigned int或者int类型
typedef ngx_uint_t  ngx_rbtree_key_t;
typedef ngx_int_t   ngx_rbtree_key_int_t;


typedef struct ngx_rbtree_node_s  ngx_rbtree_node_t;

struct ngx_rbtree_node_s {
    ngx_rbtree_key_t       key;		//rbtree的key值
    ngx_rbtree_node_t     *left;	
    ngx_rbtree_node_t     *right;
    ngx_rbtree_node_t     *parent;
    u_char                 color;	//rbtree节点的颜色,0为黑,1为红
    u_char                 data;	//rbtree的data值
};


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;		//插入函数指针
};
//初始化红黑树
#define ngx_rbtree_init(tree, s, i)                                           \
    ngx_rbtree_sentinel_init(s);                                              \
    (tree)->root = s;                                                         \
    (tree)->sentinel = s;                                                     \
    (tree)->insert = i

//红黑树染色和判断节点的颜色
#define ngx_rbt_red(node)               ((node)->color = 1)
#define ngx_rbt_black(node)             ((node)->color = 0)
#define ngx_rbt_is_red(node)            ((node)->color)
#define ngx_rbt_is_black(node)          (!ngx_rbt_is_red(node))
#define ngx_rbt_copy_color(n1, n2)      (n1->color = n2->color)

//Nil节点为黑色节点(红黑树的叶子均为黑色)
#define ngx_rbtree_sentinel_init(node)  ngx_rbt_black(node)

//查找红黑树最小值,只需要找到最左边的节点即可
static ngx_inline ngx_rbtree_node_t *
ngx_rbtree_min(ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
{
    while (node->left != sentinel) {
        node = node->left;
    }

    return node;
}

ngx_rbtree.c:

void
ngx_rbtree_insert(ngx_rbtree_t *tree, ngx_rbtree_node_t *node)
{
...
/**红黑树节点的插入有以下几种情况:
 *1.插入红黑树时将插入的节点设置为红色
 *2.插入时红黑树为空树,则直接把插入结点作为根结点就行,并把插入结点设为黑色。
 *3.若插入结点的Key已存在,既然红黑树总保持平衡,在插入前红黑树已经是平衡的,那么把插入结点设置为将要替代结点的颜色,再把结点的值更新就完成插入。
 *4.插入节点的父节点为黑色,而插入的结点是红色的,当插入结点的黑色时,并不会影响红黑树的平衡,直接插入即可,无需做自平衡。
 *5.插入节点的叔叔节点存在,且为红色,说明祖父节点一定为黑色,则只需要设置祖父节点为红色,父节点和叔叔节点为黑色即可(即重新染色),若PP为根节点,则需染为黑色
 *6.插入节点插入节点的父节点为红色,此时祖父节点为黑色,有Left-Left,Left-Right,Right-Left,Right-Right的情况
*/
    root = (ngx_rbtree_node_t **) &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)) {
                ngx_rbt_black(node->parent);
                ngx_rbt_black(temp);
                ngx_rbt_red(node->parent->parent);
                node = node->parent->parent;

            } else {//对应L-R的情况,转换成L-L的情况
                if (node == node->parent->right) {
                    node = node->parent;
                    ngx_rbtree_left_rotate(root, sentinel, node);
                }
				//L-L情况
                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 {//对应R-L情况,转换成R-R情况
                if (node == node->parent->left) {
                    node = node->parent;
                    ngx_rbtree_right_rotate(root, sentinel, node);
                }
				//对应R-R情况
                ngx_rbt_black(node->parent);
                ngx_rbt_red(node->parent->parent);
                ngx_rbtree_left_rotate(root, sentinel, node->parent->parent);
            }
        }
    }
	//根节点一定为黑色
    ngx_rbt_black(*root);
}

nginx在处理红黑树的插入时,总是在寻找插入的共通性以便减少代码的体量,更加简单明了。红黑树插入平衡关注的是局部子树的平衡,当所有子树平衡的时候,整颗红黑树是平衡的。而子树平衡往往只关注从上至下不超过三代(祖父一代,父亲一代,自己一代)的节点。

void
ngx_rbtree_delete(ngx_rbtree_t *tree, ngx_rbtree_node_t *node)
{
...
    /* a binary tree delete */
/**节点删除的情况较为复杂,红黑树将删除的节点用可以替换的节点来代替
 *降低了维护树平衡的代价,寻找到一个可以替换删除节点的节点
 *往删除节点的孩子节点寻找
 */
    root = (ngx_rbtree_node_t **) &tree->root;
    sentinel = tree->sentinel;
	//删除节点无左孩子的情况
    if (node->left == sentinel) {
        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 {//要替换的节点无左孩子
            temp = subst->right;
        }
    }
    
	//根据前面的寻找,如果subst为root节点,可以确定该树的根节点为只有一个孩子
	//或者无孩子节点
    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);
	//将替换的节点的孩子节点挂载到父节点下
    if (subst == subst->parent->left) {
        subst->parent->left = temp;

    } else {
        subst->parent->right = temp;
    }
	//判断subst节点是否为删除的节点,也即删除节点仅有一个孩子节点
    if (subst == node) {

        temp->parent = subst->parent;

    } else {
		//判断subst节点的父亲节点是否为要删除的节点
        if (subst->parent == node) {
            temp->parent = subst;

        } else {
            temp->parent = subst->parent;
        }
		//将删除节点的左右孩子挂载到找到的替换节点,并将其染为同一颜色
        subst->left = node->left;
        subst->right = node->right;
        subst->parent = node->parent;
        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;
        }
    }
	//从红黑树中删除node
    /* 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) {
            w = temp->parent->right;
            //兄弟节点为红色的情况
            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;
            }
            //兄弟节点左右孩子均为黑色的情况
            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->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 {
        	//temp是父节点的右孩子
        	//情况跟左孩子一样,只不过旋转方向相反
            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);
}

红黑树的删除操作可以参考算法导论中红黑树删除操作的实现,ngx_rbtree的删除操作基本按照先删除后调整的操作。

/*红黑树的左旋右旋*/
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;
    node->right = temp->left;

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

    temp->parent = node->parent;

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

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

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

    temp->left = node;
    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;
}

红黑树的一些理解:
红黑树

 类似资料: