红黑树 节点的删除

接上一篇。

红黑树的插入操作!


红黑树的删除继续分各种情况进行考虑。

首先考虑红黑树的单支情况,即只有父节点只有一个子节点,另外一个为NULL,这样的话,只有一种情况,即父节点为黑色,子节点为红色,因为其他情况都会是孩子为黑色,因为孩子为红色父为红色,则与红父黑子矛盾。而当孩子是黑色时,另一个孩子是黑色的NULL,则必定使左右路径上黑色节点数量不等。所以单支只能为黑父红子。

这里的单支情况,是对任意一点的,若其只有一个孩子,则其必为黑色,孩子必为红色,且孩子为叶子节点。孩子为红色,如果有孙子,其孙子必为黑色,则路径黑子数量不等,不平衡。

情况一:

单支,删除红色节点,则不会影响其他路径情况,不影响平衡,直接删除即可。

情况二:

单支,删除黑节点

若s节点为黑色,则用单支下的红色孩子替代自己,然后删除黑色节点,这样路径上少了一个黑色节点,影响平衡,需要进行调整。


情况三:

删除节点有两个非空孩子,这时候又用到向下旋转的思路,寻找删除节点p的中序遍历的后继节点s,即正常排序后位于p之后的数字,而且s节点必定最多有一颗子树,即s最多有一颗右子树,或者没有。若s节点有一颗右子树,则必为单支情况,其孩子为红色,且无孙子,则这就是情况二,转到情况二。若s节点为叶子节点,则其可为红色或黑色,红色为情况一。

这种情况将p与s的数据交换,但颜色不换,则情况转换为删除s点,而删除之前树还是平衡的。

或者这样分析:

若s节点为红色,则其必定不是单支,则其为叶子节点,则是情况一,直接删除即可。

若s节点为黑色,则用s的孩子null或者单支下的红色孩子替代自己,然后删除黑色节点,这样路径上少了一个黑色节点,影响平衡,需要进行调整。(这里又要注意,如果是孩子为NULL,替代后,如何找到s节点的父节点。没有哨兵节点,这个地方麻烦了。。。。好吧,调用的递归调整函数的参数加一个父节点指针)

然后如同插入一样设置一个递归的调整函数,调整有4种情况:

首先以删除判断节点为x,为黑色,父节点为p,兄弟节点为w。以x为根的子树的黑色节点树减一,则需要进行平衡操作。

一般是有一下4种情况,但,还有一个特殊情况,即x为根节点时,跟插入时判断相同,如果x为根节点,且现在为红色, 则将其改为黑色。对于以下情况2,可能转换到这种情况。第二次被别人的博客坑,等会写完了再测试有没有其他错误。

情况1:w为红色。

则w的孩子都为黑色,p为黑色。改变w和p的颜色,即w改为黑色,p改为红色,然后以p为支点进行左旋,或右旋,根据x位于p的位置决定,若x为左孩子,左旋,右孩子右旋。更新后x获得了新的兄弟节点,然后继续对x进行判断调节。


情况2:w为黑色,且w的孩子都为黑色。这里由于之前是在x所在子树上删除了一个黑色的节点,则w所在子树上必定有一个黑色节点,即w必定不为NULL。

将w置为红色,使w所在子树上黑色数量减一,则p所在子树上黑色节点数量也减一,则需要以p节点作为当前节点,即新的x节点。如果新的x为红,则将其置为黑色,则整个树平衡,结束,如果为黑,则继续判断新的x节点的情况。

情况3:w为黑色。

如果x为p的左孩子, w的左孩子为红色,右孩子为黑色。

则将w置为红色,左孩子为黑色,然后以w节点为支点右旋。

如果x为p的右孩子, w的右孩子为红色,左孩子为黑色、

则将w为红,右孩子为黑,然后以w节点左旋。

获得新的兄弟节点,进入情况4. 这里进行情况3的旋转不会对改变任何子树的平衡性。

情况4:w为黑色。

如果x为p的左孩子,w的右孩子为红色,左孩子可以为红色或黑色或NULL。

则交换w和p的颜色,因为之后w要成为新的父节点,所以要获得原来p的颜色,防止与p的父节点冲突。

将w的右孩子设置为黑色,这样w的右子树黑色节点树+1,

再以p节点为支点左旋,这样w的颜色黑色会移动到左子树,使其节点树 -1 + 1 =0,而右子树减少了一个黑节点 +1 -1 = 0;且新的p节点为原来的颜色,整个树是平衡的。结束、


如果x为p的右孩子,w的左孩子是红色。同理、

交换w和p的颜色,将w的做孩子改为黑色,以p为节点右旋。结束判断。


最终调整完成。这里也有一个很重要的地方,在左旋和右旋时交换父节点p与其对应左右孩子c的颜色,导致旋转后得到的新父节点p颜色未变,则不用再向上检测。

中间测试的时候有出错了,尝试画了一些图并找出了错误,只画了一张图,其他都是看别人的博客思考的。



删除其实也不是特别复杂,尝试写出代码:


template <typename T>
void RBTree<T>::GetParentLinked(RBNode<T> *a,RBNode<T> *b){
	if(a == root){
		root = b;
		if(b)
			b->parent = 0;
		return;
	}
	if(a->parent->left == a){
		a->parent->left = b;
		if(b)
			b->parent = a->parent;
	}else{
		a->parent->right = b;
		if(b)
			b->parent = a->parent;
	}

}
template <typename T>
bool RBTree<T>::Delete(const T &x){
	RBNode<T> *p = root ,*s,*fa;
	if(!root)return false;
	while(p){
		if(p->value == x)break;
		else if(x < p->value)p = p->left;
		else p = p->right;
	}
	if(p){
		if( p->left &&  p->right){//当存在左右孩子时,向下旋转
			s = p->right;
			while(s->left){
				s = s->left;
			}
			int temp = p->value;
			p->value = s->value;
			s->value = temp;
			p = s;
		}
		if(p->red){//为红色叶子节点,直接删除
			GetParentLinked(p,p->left);
			delete p;
		}else{//为黑色时,将其红色子孩子替代他
			//由于之前已经向下旋转,则这里只有一种特殊可能,即单支情况删除黑色根节点。			
			fa = p->parent;//之后可能没有父节点。
			if(p->left){
				s = p->left;
				GetParentLinked(p,p->left);				
			}
			else {
				s = p->right;
				GetParentLinked(p,p->right);
			}
			delete p;
			AdjustAfterDelete(s,fa);
		}
		return true;
	}else return false;

	
}
template <typename T>
void RBTree<T>::AdjustAfterDelete(RBNode<T> *x ,RBNode<T> *p){
	if(p){//如果有父指针,则有四种情况。
		RBNode<T> *w ;
		bool inLeftTree = true;//标记左右情况,左右情况不同旋转方向以及判断方式也不同。
		if(p->left == x)w = p->right;
		else {
			w = p->left;
			inLeftTree = false;
		}
		if(w->red){
			w->red = false;
			p->red = true;
			if(inLeftTree)
				LRotation(p);//以p为支点进行旋转,不用考虑当前节点是否为空。
			else
				RRotation(p);
			AdjustAfterDelete(x,p);
		}else{
			if( !GetColorWithNULL(w->left) && !GetColorWithNULL(w->right) ){
				w->red = true;
				x = p;
				if(x ->red)x->red = false;//出错一次,要根据新的x节点进行判断。
				else	AdjustAfterDelete(x,x->parent);
			}else if(( inLeftTree && GetColorWithNULL(w->right) ) //情况4
				|| ( !inLeftTree && GetColorWithNULL(w->left) )  ){
					bool tempRed = p->red;
					p->red = w->red;
					w->red = tempRed;
					if(inLeftTree){
						w->right->red = false;						
						LRotation(p);
					}else{
						w->left->red = false;
						RRotation(p);
					}
			}else {//情况3
				w->red = true;
				if(inLeftTree){
					w->left->red = false;
					RRotation(w);
					AdjustAfterDelete(x,p);
				}else{
					w->right->red = false;
					LRotation(w);
					AdjustAfterDelete(x,p);
				}
			}
		}
	}else{//如果不存在父节点,则此节点为根节点,则置为黑色结束。
		if(x)
			x->red = false;
		return ;
	}

}



我的红黑树代码!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值