WorkFlow学习分享:rbtree(红黑树)

一、数据结构

struct rb_node
{
	struct rb_node *rb_parent;//父节点
	struct rb_node *rb_right;//左孩子
	struct rb_node *rb_left;//有孩子
	char rb_color;
#define	RB_RED		0
#define	RB_BLACK	1
};
struct rb_root
{
	struct rb_node *rb_node;
};

二、左旋和右旋

//左旋
static void __rb_rotate_left(struct rb_node *node, struct rb_root *root)
{
    //init/
	struct rb_node *right = node->rb_right;

    //第一步//
    //如果该节点的孩子不为空,则更新该孩子的父节点
	if ((node->rb_right = right->rb_left))
		right->rb_left->rb_parent = node;
    

    /第二步
	right->rb_left = node;

    //此时更新右旋后的右节点的父节点,如果存在父节点则更新node的父节点左或右孩子的指向
    //如果不存在父节点则该节点为
	if ((right->rb_parent = node->rb_parent))
	{
        //判断node是左孩子还是右孩子
		if (node == node->rb_parent->rb_left)
			node->rb_parent->rb_left = right;
		else
			node->rb_parent->rb_right = right;
	}
	else
		root->rb_node = right;
    //更新node父节点指针的指向
	node->rb_parent = right;
}

//右旋
//逻辑和左旋一样,只不过反过来了
static void __rb_rotate_right(struct rb_node *node, struct rb_root *root)
{
	struct rb_node *left = node->rb_left;

	if ((node->rb_left = left->rb_right))
		left->rb_right->rb_parent = node;
	left->rb_right = node;

	if ((left->rb_parent = node->rb_parent))
	{
		if (node == node->rb_parent->rb_right)
			node->rb_parent->rb_right = left;
		else
			node->rb_parent->rb_left = left;
	}
	else
		root->rb_node = left;
	node->rb_parent = left;
}

没做高低平衡处理,仅为理解代码用

图 init 

第一步

 第二步

 三、插入结点后的调整

插入结点有一下几种情况

1、结点为根节点,直接插入

2、结点的父节点为黑色结点,可以直接插入,因为红色结点不会改变路径中的黑色结点数量,不会打破规则

3、父结点为红色,且舅舅结点也为红色,则需要调整结点颜色

4、如果都不满足则需要左旋活着右旋来调整

void rb_insert_color(struct rb_node *node, struct rb_root *root)
{
	struct rb_node *parent, *gparent;

	//判断条件:父节点存在 且 父节点为红色结点;插入结点必为红节点,一条路径上不能存在连个连续红结点
	while ((parent = node->rb_parent) && parent->rb_color == RB_RED)
	{
		//祖父结点
		gparent = parent->rb_parent;

		//如果 父结点 是 祖父结点 的左孩子
		if (parent == gparent->rb_left)
		{
			{
				//情况3:舅舅结点存在 且 舅舅结点为红结点
				//措施:红的变黑,黑的变红
				//完成后更新祖父结点,跳过此次循环进入新循环,向上递归更新结点
				register struct rb_node *uncle = gparent->rb_right;
				if (uncle && uncle->rb_color == RB_RED)
				{
					uncle->rb_color = RB_BLACK;
					parent->rb_color = RB_BLACK;
					gparent->rb_color = RB_RED;
					node = gparent;
					continue;
				}
			}

			//如果不存在舅舅结点,或者舅舅结点为黑色结点则不管,因为不会破坏舅舅结点下规则
			//情况4:如果新插入结点为右结点;此时为两个连续红结点;
			//平衡树失衡(左子树的右孩子导致:LR型),需要左旋然后右旋
			//左旋完成后,调整指针指向,此时新插入的结点为之前父节点的父亲,并且调整颜色
			if (parent->rb_right == node)
			{
				register struct rb_node *tmp;
				__rb_rotate_left(parent, root);
				tmp = parent;
				parent = node;
				node = tmp;
			}

			parent->rb_color = RB_BLACK;
			gparent->rb_color = RB_RED;
			//此时旧父节点和祖父节点都为红结点,新结点为黑结点,通过一次右旋可变成父节点为黑结点,两个孩子结点为红结点
			__rb_rotate_right(gparent, root);
			//此时的node指针为最左,即第一个结点,父节点为黑色,不达成循环条件,跳出循环
		} else {
			//如果 父结点 是 祖父结点 的左孩子
			{
				//情况3.1:舅舅结点存在 且 舅舅结点为红结点
				//措施:红的变黑,黑的变红
				//完成后更新祖父结点,跳过此次循环进入新循环,向上递归更新结点
				register struct rb_node *uncle = gparent->rb_left;
				if (uncle && uncle->rb_color == RB_RED)
				{
					uncle->rb_color = RB_BLACK;
					parent->rb_color = RB_BLACK;
					gparent->rb_color = RB_RED;
					node = gparent;
					continue;
				}
			}

			//如果不存在舅舅结点,或者舅舅结点为黑色结点则不管,因为不会破坏舅舅结点下规则
			//情况4.1:如果新插入结点为左结点;此时为两个连续红结点;
			//平衡树失衡(左子树的右孩子导致:RL型),需要右旋然后左旋
			//右旋完成后,调整指针指向,此时新插入的结点为之前父节点的父亲,并且调整颜色
			if (parent->rb_left == node)
			{
				register struct rb_node *tmp;
				__rb_rotate_right(parent, root);
				tmp = parent;
				parent = node;
				node = tmp;
			}

			parent->rb_color = RB_BLACK;
			gparent->rb_color = RB_RED;
			__rb_rotate_left(gparent, root);
		}
	}
	//情况1:当为根结点时,不进入循环,直接插入,为根结点
	//情况2:当父节点为黑结点时,不进入循环,直接插入;插入结点为红色,不会增加黑色结点,则不会破坏规则
	root->rb_node->rb_color = RB_BLACK;
}

四、结点的删除

void rb_erase(struct rb_node *node, struct rb_root *root)
{
	struct rb_node *child, *parent;
	int color;

	//情况1:如果删除的结点仅有左右孩子的的其中一个或是都没有,则直接跳过判断,与父结点进行连接
	if (!node->rb_left)
		child = node->rb_right;
	else if (!node->rb_right)
		child = node->rb_left;
	else
	{
		//情况2:存在左右孩子结点
		//措施:寻找比他大但比其他结点小的结点来取代他,即该结点的右孩子的最左结点
		struct rb_node *old = node, *left;

		node = node->rb_right;
		while ((left = node->rb_left))//寻找最左孩子,并更新指针
			node = left;
		child = node->rb_right;
		parent = node->rb_parent;
		color = node->rb_color;

		if (child)
			child->rb_parent = parent;
		//如果最左孩子存在右孩子,则将其父结点指针指向他的祖父结点
		//后续将祖父结点与右孩子结点相连接,断开最左结点的连接

		//判断是否为根节点,不是则和父结点连接,否则为根结点
		if (parent)
		{
			if (parent->rb_left == node)
				parent->rb_left = child;
			else
				parent->rb_right = child;
		}
		else
			root->rb_node = child;

		if (node->rb_parent == old)
			parent = node;
		
		//替换两个结点
		node->rb_parent = old->rb_parent;
		node->rb_color = old->rb_color;
		node->rb_right = old->rb_right;
		node->rb_left = old->rb_left;
		

		//更新旧结点的父节点指向
		if (old->rb_parent)
		{
			if (old->rb_parent->rb_left == old)
				old->rb_parent->rb_left = node;
			else
				old->rb_parent->rb_right = node;
		} else
			root->rb_node = node;

		//将旧结点的左子树与node相连,自此old结点已离开红黑树
		old->rb_left->rb_parent = node;
		if (old->rb_right)
			old->rb_right->rb_parent = node;
		goto color;
	}

	//只有情况一才会运行到这
	parent = node->rb_parent;
	color = node->rb_color;

	if (child)
		child->rb_parent = 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);
		//在情况2下,child指向的是最初时删除结点的右孩子的最左结点的右孩子树
		//通过调换结点,child前面的树的路径都没有受到变化,只有该孩子树的父结点受到了变化
		//如果删除的结点为黑色,则调整红黑树的颜色
}
static void __rb_erase_color(struct rb_node *node, struct rb_node *parent,
			     struct rb_root *root)
{
	struct rb_node *other;
	
	//判断条件:结点存在,且结点颜色为黑色
	while ((!node || node->rb_color == RB_BLACK) && node != root->rb_node)
	{
		//如果删除的结点为左结点
		if (parent->rb_left == node)
		{
			other = parent->rb_right;
			//如果存在兄弟结点,且为红色结点,进行左旋,变色,以此替代删掉的结点
			//第一种情况,兄弟节点为红色结点,此时需要把兄弟结点改为黑色
			if (other->rb_color == RB_RED)
			{
				other->rb_color = RB_BLACK;
				parent->rb_color = RB_RED;
				__rb_rotate_left(parent, root);
				other = parent->rb_right;//更新兄弟结点
			}
			
			//第二种情况,兄弟节点为黑,且兄弟的俩孩子也都为黑色
			//这种情况最好,兄弟结点分支多了一个black, 而且 brother 的两个儿子也是black,
			//直接将兄弟结点改为红色结点.
			//此时node和他的兄弟 黑色的个数一致了,但是,如果node的父亲是根,一切都很公平了,
			//但是如果node的父亲不是根,node父亲这一脉,其实比node叔叔这一脉 少一个黑色节点。
			//因此需要parent = node->rb_parent;来完成向上依次调整
			if ((!other->rb_left ||
			     other->rb_left->rb_color == RB_BLACK)
			    && (!other->rb_right ||
				other->rb_right->rb_color == RB_BLACK))
			{
				other->rb_color = RB_RED;
				node = parent;
				parent = node->rb_parent;
			}
			else
			{
				//情况三,如果兄弟结点存在右孩子且为黑色结点,左孩子为红
				//将兄弟结点的左孩子变黑,兄弟节点变红,右旋
				//右旋完成后,左孩子变成兄弟结点,且变为第四种情况
				if (!other->rb_right ||
				    other->rb_right->rb_color == RB_BLACK)
				{
					register struct rb_node *o_left;
					if ((o_left = other->rb_left))//兄弟结点存在左孩子时,改变左孩子颜色
						o_left->rb_color = RB_BLACK;
					other->rb_color = RB_RED;
					__rb_rotate_right(other, root);
					other = parent->rb_right;//更新兄弟结点
				}

				//第四种情况,兄弟为黑,右孩子为红,
				//兄弟继承父亲的颜色,父亲变为黑色,兄弟的右孩子变为黑色,左旋
				other->rb_color = parent->rb_color;
				parent->rb_color = RB_BLACK;
				if (other->rb_right)
					other->rb_right->rb_color = RB_BLACK;
				__rb_rotate_left(parent, root);
				node = root->rb_node;
				break;
				//由此,兄弟这一分支的黑色个数没有减少,又因为父亲变成了黑色,node结点增加了一个黑色结点,由此达到平衡
			}
		}
		else//反过来
		{
			other = parent->rb_left;
			if (other->rb_color == RB_RED)
			{
				other->rb_color = RB_BLACK;
				parent->rb_color = RB_RED;
				__rb_rotate_right(parent, root);
				other = parent->rb_left;
			}
			if ((!other->rb_left ||
			     other->rb_left->rb_color == RB_BLACK)
			    && (!other->rb_right ||
				other->rb_right->rb_color == RB_BLACK))
			{
				other->rb_color = RB_RED;
				node = parent;
				parent = node->rb_parent;
			}
			else
			{
				if (!other->rb_left ||
				    other->rb_left->rb_color == RB_BLACK)
				{
					register struct rb_node *o_right;
					if ((o_right = other->rb_right))
						o_right->rb_color = RB_BLACK;
					other->rb_color = RB_RED;
					__rb_rotate_left(other, root);
					other = parent->rb_left;
				}
				other->rb_color = parent->rb_color;
				parent->rb_color = RB_BLACK;
				if (other->rb_left)
					other->rb_left->rb_color = RB_BLACK;
				__rb_rotate_right(parent, root);
				node = root->rb_node;
				break;
			}
		}
	}
	
	if (node)
		node->rb_color = RB_BLACK;
}

五、其他

//逻辑和二叉排序树一样

//红黑树中的第一个结点,即最左结点
struct rb_node *rb_first(struct rb_root *root)
{
	struct rb_node *n;

	n = root->rb_node;
	if (!n)
		return (struct rb_node *)0;
	while (n->rb_left)
		n = n->rb_left;
	return n;
}

//红黑树中的最后一个结点,即最右结点
struct rb_node *rb_last(struct rb_root *root)
{
	struct rb_node *n;

	n = root->rb_node;
	if (!n)
		return (struct rb_node *)0;
	while (n->rb_right)
		n = n->rb_right;
	return n;
}

//下一个结点
struct rb_node *rb_next(struct rb_node *node)
{
    //如果有右子树,则右子树的最左结点就是下个结点
	if (node->rb_right) {
		node = node->rb_right; 
		while (node->rb_left)
			node = node->rb_left;
		return node;
	}
    
    //找到一个处于某个父节点下的左子树的结点,其结点的父节点就是下一个结点
    //右结点一定比父节点大
	while (node->rb_parent && node == node->rb_parent->rb_right)
		node = node->rb_parent;

	return node->rb_parent;
}

//上一个结点
struct rb_node *rb_prev(struct rb_node *node)
{
	//如果有左子树,左子树的最右结点就是上一个结点
	if (node->rb_left) {
		node = node->rb_left; 
		while (node->rb_right)
			node = node->rb_right;
		return node;
	}

	//左节点一定比父节点小,找到唯一那个右结点
	while (node->rb_parent && node == node->rb_parent->rb_left)
		node = node->rb_parent;

	return node->rb_parent;
}

//替换结点
void rb_replace_node(struct rb_node *victim, struct rb_node *newnode,
		     struct rb_root *root)
{
	struct rb_node *parent = victim->rb_parent;
	if (parent) {
		if (victim == parent->rb_left)
			parent->rb_left = newnode;
		else
			parent->rb_right = newnode;
	} else {
		root->rb_node = newnode;
	}
	if (victim->rb_left)
		victim->rb_left->rb_parent = newnode;
	if (victim->rb_right)
		victim->rb_right->rb_parent = newnode;
	*newnode = *victim;
}

//插入新节点
static inline void rb_link_node(struct rb_node *node, struct rb_node *parent,
				struct rb_node **link)
{
	node->rb_parent = parent;
	node->rb_color = RB_RED;
	node->rb_left = node->rb_right = (struct rb_node *)0;

	*link = node;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值