-
前言
曾经有一段时间想自己写一个红黑树。但是有一块逻辑一直没有搞顺畅,是:在删除一个黑色节点,且父节点和兄弟节点为黑色。后来看内核网络子系统的源码时,又想到了红黑树。看到别人写的逻辑才知道自己走的路有多弯!
本文主要讲一下删除和添加的逻辑。添加是源码,删除是我自己写的。 -
变色时需要注意的点
一个兄节点和叔节点可以直接变黑,但不能直接变红。因为变红需要考虑到其子节点没有红色,所以不要用让兄节点和叔节点变红的操作,否则后边会有一堆麻烦,子子代代何其多呀!(我前边提到的没搞顺畅就是陷到这里边去了)。 -
添加逻辑
此处直接介绍添加后进行重新恢复平衡的过程。
添加时的平衡前置条件是:node是红色的,且node以下节点都是平衡的,只要node的parent不是红色就行。
/*
*前半段逻辑归纳
* 1. parent为红
* 1.1 uncle为红
* parent变黑,uncle变黑,gparent变黑,
* node = gparent;
* continue
* 1.2 node 是右节点
* parent左旋,node与parent的角色互换
* 1.3 uncle是黑,且node是左节点
* parent变黑,gparent变红,gparent右旋
*/
//将node放入树之后会直接调用这个函数
void rb_insert_color(struct rb_node *node, struct rb_root *root)
{
struct rb_node *parent, *gparent;
//在每次重新开始循环时,可以保证node以下的节点是平衡的,且进入循环的原因是因为node和parent都是红色
while ((parent = rb_parent(node)) && rb_is_red(parent))
// 有父节点且为红。保证了不是根节点,且不是根节点的子节点,因为根节点是黑色
{
gparent = rb_parent(parent);
if (parent == gparent->rb_left) // parent is left
{
{
register struct rb_node *uncle = gparent->rb_right;
if (uncle && rb_is_red(uncle))// uncle is red
{
rb_set_black(uncle);
rb_set_black(parent);
rb_set_red(gparent);
node = gparent;
continue;
}
}
if (parent->rb_right == node) // node is right
{//这里是为了将node变为左节点,因为node和parent都是红色,所以旋转不影响平衡
register struct rb_node *tmp;
__rb_rotate_left(parent, root);
tmp = parent;
parent = node;
node = tmp;
}
rb_set_black(parent);
rb_set_red(gparent); //gparent原来是黑色
__rb_rotate_right(gparent, root);
//这里node的parent是黑色,所以下次不会再进入循环
}
else // parent is right,以下只是方向反过来了,逻辑相似
{
{
register struct rb_node *uncle = gparent->rb_left;
if (uncle && rb_is_red(uncle)) //uncle is red
{
rb_set_black(uncle);
rb_set_black(parent);
rb_set_red(gparent);
node = gparent;
continue;
}
}
if (parent->rb_left == node)
{
register struct rb_node *tmp;
__rb_rotate_right(parent, root);
tmp = parent;
parent = node;
node = tmp;
}
rb_set_black(parent);
rb_set_red(gparent);
__rb_rotate_left(gparent, root);
}
}
rb_set_black(root->rb_node);
}
- 删除逻辑
//如果需要的话,这里是将node找个前驱或后继节点替代,然后在“替代节点的位置”进行删除
void rb_erase(struct rb_node *node, struct rb_root *root)
{
struct rb_node *child, *parent;
int color;
if (!node->rb_left) // left is NULL
child = node->rb_right;
else if (!node->rb_right) // right is NULL
child = node->rb_left;
else // neigther left nor right are no NULL
{
struct rb_node *old = node, *left;
node = node->rb_right;
while ((left = node->rb_left) != NULL)
node = left;
if (rb_parent(old)) {
if (rb_parent(old)->rb_left == old)
rb_parent(old)->rb_left = node;
else
rb_parent(old)->rb_right = node;
} else
root->rb_node = node;
child = node->rb_right;
parent = rb_parent(node);
color = rb_color(node);
if (parent == old) {
parent = node;
} else {
if (child)
rb_set_parent(child, parent);
parent->rb_left = child;
node->rb_right = old->rb_right;
rb_set_parent(old->rb_right, node);
}
node->rb_parent_color = old->rb_parent_color;
node->rb_left = old->rb_left;
rb_set_parent(old->rb_left, node);
goto color;
}
parent = rb_parent(node);
color = rb_color(node);
if (child)
rb_set_parent(child, parent);
if (parent)
{
if (parent->rb_left == node)
parent->rb_left = child;
else
parent->rb_right = child;
}
else
root->rb_node = child;
color:
if (color == RB_BLACK)
__rb_erase_color(child, parent, root);
}
对下面的函数特殊批注一下。
下面代码是有问题的,问题在:
- 当parent为红,brother为黑,且brother有两个红子节点时。parent旋转后会出现parent和新的子节点都为红的现象。这个问题的解决方法为:将brother靠近node的子节点这个位置的节点变为黑色,可以不是原来的那个节点。
- node以下在开始的时候如果不平衡,那吗下面代码从开始就错了。
/*
*前半段逻辑归纳
* 1. parent为黑
* 1.1 brother为红
* 将brother变黑转为1.2
* 1.2 brother为黑
* parent变红,parent左旋。(旋转后brother代替了parent,现在brother以下的节点是平衡的,但比brother以上的节点在路径上少一个黑节点,所以还需要平衡)
* continue;
* 2. parent为红,brother肯定是黑了
* parent左旋。(这里node节点以下少个黑节点,brother正好为黑且parent为红。正好将brother放在parent的位置,node节点加一个凑齐。而brother的右分支没影响,左分支被放在了parent的右分支上,且parent为红色)
*/
//进入之前已经将node替换掉了,
static void __rb_erase_color(struct rb_node *node, struct rb_node *parent,
struct rb_root *root)
{
struct rb_node *other;
while ((!node || rb_is_black(node)) && node != root->rb_node)// node is black and not root
{
if (parent->rb_left == node) // node is left
{
other = parent->rb_right;
if (rb_is_black(parent)) // parent is black
//1 parent为黑
{
if(rb_is_red(other))
//1.1 brother为红
rb_set_black(other);
//1.2 brother为黑
rb_set_black(parent);
__rb_rotate_left(other, root); // other变为gparent
node = rb_parent(parent); //现在的node == other
continue;
}
//2 parent为红,brother肯定不为红
if(rb_is_black(other))
{
__rb_rotate_left(parent);
break;
}
}
else
{
other = parent->rb_left;
if (rb_is_black(parent)) // parent is black
//1 parent为黑
{
if(rb_is_red(other))
//1.1 brother为红
rb_set_black(other);
//1.2 brother为黑
rb_set_black(parent);
__rb_rotate_right(other, root); // other变为gparent
node = rb_parent(parent); //现在的node == other
continue;
}
//2 parent为红,brother肯定不为红
if(rb_is_black(other))
{
__rb_rotate_right(parent);
break;
}
}
}
if (node)
rb_set_black(node);
}