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;
}
红黑树的一些理解:
红黑树