从内核源码中读懂红黑树

  1. 前言
    曾经有一段时间想自己写一个红黑树。但是有一块逻辑一直没有搞顺畅,是:在删除一个黑色节点,且父节点和兄弟节点为黑色。后来看内核网络子系统的源码时,又想到了红黑树。看到别人写的逻辑才知道自己走的路有多弯!
    本文主要讲一下删除和添加的逻辑。添加是源码,删除是我自己写的。

  2. 变色时需要注意的点
    一个兄节点和叔节点可以直接变黑,但不能直接变红。因为变红需要考虑到其子节点没有红色,所以不要用让兄节点和叔节点变红的操作,否则后边会有一堆麻烦,子子代代何其多呀!(我前边提到的没搞顺畅就是陷到这里边去了)。

  3. 添加逻辑
    此处直接介绍添加后进行重新恢复平衡的过程。
    添加时的平衡前置条件是: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);
}

  1. 删除逻辑
//如果需要的话,这里是将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);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值