代码在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);
}