红黑树的旋转、插入与删除

红黑树是一种二叉查找树,但在每个结点上增加一个存储位表示结点的颜色,可以是RED或者BLACK。通常对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因而是平衡的。

树中的每个结点包含五个域:color、key、left、right、p。如果某结点没有一个子结点或父结点,则该结点相应得指针(p)域包含值NIL。我们将把这些 NIL 视为指向二叉查找树的外结点(叶子)的指针,而把带关键字的结点视为树的内结点。

一棵二叉查找树如果满足下面的红黑性质,则为一颗红黑树:

  •     每个结点或是红的,或是黑的
  •     根结点是黑的
  •     每个叶结点(NIL)是黑的
  •     如果一个结点是红的,则它的两个子结点都是黑的
  •     对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点

1. 旋转

当在含 n 个关键字的红黑树上运行时,查找树操作 TREE-INSERT(插入)和 TREE-DELETE(删除)的时间为O(lgn)。由于这两个操作对树作了修改,结果可能违反红黑树的性质。为保持这些性质,就要改变树中某些结点的颜色以及指针结构。

指针结构的修改是通过旋转来完成的,这是一种能保持二叉查找树性质的查找树局部操作。下面给出了两种旋转:左旋和右旋。当在某个结点 x 上做左旋时,我们假设它的右孩子 y 不是 nil[T];x 可以为树内任意右孩子不是 nil[T] 的结点。左旋以 x 到y 之间的链为“支轴”进行。它使 y 成为该子树新的根,x 成为 y 的左孩子,而 y 的左孩子则成为 x 的右孩子,如下图所示。

旋转过程代码:

//左旋
void RotateLeft(rb_node *head,rb_node *ptr)
{
	rb_node *newroot = ptr->rightchild;//#
	newroot->parent = ptr->parent; // 1
	ptr->rightchild = newroot->leftchild; // #
	if(newroot->leftchild != NULL)
	{
		newroot->leftchild->parent = ptr; //2
	}
	
	newroot->leftchild = ptr;  // #
	if(ptr == head->parent) // ptr => root;
	{
		head->parent = newroot;
	}
	else
	{
		if(ptr->parent->leftchild == ptr)
		{
			ptr->parent->leftchild = newroot;
		}
		else
		{
			ptr->parent->rightchild = newroot;
		}
	}
	ptr->parent = newroot; // 3
}

//右旋
void RotateRight(rb_node *head, rb_node *ptr)
{
	rb_node *newroot = ptr->leftchild;
	newroot->parent = ptr->parent; // 1
	ptr->leftchild = newroot->rightchild;
	if(newroot->rightchild != NULL)
	{
		newroot->rightchild->parent = ptr; //2
	}
	newroot->rightchild = ptr;
	if(ptr == head->parent)
	{
		head->parent = newroot;
	}
	else
	{
		if(ptr->parent->leftchild == ptr)
		{
			ptr->parent->leftchild = newroot;
		}
		else
		{
			ptr->parent->rightchild = newroot;
		}
	}
	ptr->parent = newroot; // 3
}

2. 插入

在红黑树中插入一个新的结点,为保证红黑树的性质,将新插入的结点着为红色。

回溯:如果在左边插入,找双亲的双亲的右结点;如果在右边插入,找双亲的双亲的左结点。

双亲:1)红色 --> 找双亲的双亲的左结点 --> :a.红色 --> 将双亲还有双亲的双亲的左结点变红,双亲的双亲变黑

                                                                            b.黑色 --> 旋转

           2)黑色 --> 结束

调整结点颜色代码:

void Adjust_RBTree(RBTree &myt,rb_node *ptr)
{
	rb_node *_X,*_Y;
	for(_X = ptr; _X->parent != myt.head && _X->parent->color == RED;)
	{
		if(_X->parent->parent->rightchild == _X->parent)  //_X的爷爷结点的右节点是否为_X的父亲节点
		{
			_Y = _X->parent->parent->leftchild;
			if(_Y->color == RED)
			{
				_Y->color = BLACK;
				_X->parent->color = BLACK;
				_X->parent->parent->color = RED;
				_X = _X->parent->parent;
			}
			else
			{
				if(_X->parent->leftchild == _X)
				{
					_X = _X->parent;
					RotateRight(myt.head,_X);
				}
				_X->parent->color = BLACK;
				_X->parent->parent->color = RED;
				RotateLeft(myt.head,_X->parent->parent);
			}
		}
		else // left;  _X的爷爷结点的左节点是否为_X的父亲节点
		{
			_Y = _X->parent->parent->rightchild;
			if(_Y->color == RED)
			{
				_Y->color = BLACK;
				_X->parent->color = BLACK;
				_X->parent->parent->color = RED;
				_X = _X->parent->parent;
			}
			else  //_Y->color == BLACK
			{
				if(_X->parent->rightchild == _X)
				{
					_X = _X->parent;
					RotateLeft(myt.head,_X);
				}
				_X->parent->color = BLACK;
				_X->parent->parent->color = RED;
				RotateRight(myt.head,_X->parent->parent);
			}
		}
	}

	myt.head->parent->color = BLACK;
}

3. 删除

(未完待续)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值