红黑树的删除操作

红黑树删除时的情况
由于红黑数是有序树,当删除红黑色的一个结点时,用它的后继结点(也可以是前驱结点)来代替它,然后去删除它的后继结点(或者前驱结点)。后继结点是右子树中值最小的结点(前驱结点是左子树中值最大的结点)。可以知道,后继结点一定没有左子树(前驱结点一定没有右子树),所以,当在红黑树中删除一个结点时,只会有以下两种情况:
1.删除的结点只有一个孩子
2.删除的是叶子结点


情况1
此时,要删除的结点(N)只有一个孩子(可以是左孩子(NL),也可以是右孩子(NR)),由红黑树的性质,可以知道,N一定是黑色,这个唯一的孩子一定是红色。直接将N删除,然后用N的唯一的孩子结点去代替N,再将N染黑既可。
情况2
此时,删除的结点(N)是叶子结点,如果N是红色,直接删除既可。
如果N是黑色,由红黑树的性质,它一定存在一个兄弟结点B,在删除N后,需要调节树,使其重新成为一个红黑树,在调节过程中,需要关注N的兄弟结点B的颜色。
情况如下:
N为黑,N一定有兄弟结点B
@
——N为左孩子
———— B红
—————— B是红的,此时N和B的父亲结点§一定是黑的,且B一定存在两个黑色的孩子结点。将B染黑、P染红,以P左旋,然后更新B(B=P->right),此时B为黑
———— B黑
—————— B没有红色孩子结点
———————— P红
—————————— 此时,将B染红、P染黑,即完成
———————— P黑
—————————— 将B染红,使N=P,P=N->parent,此时N为黑,回到@的地方继续判断
—————— B的左孩子(BL)红
———————— B染红、BL染黑,以B右旋,更新B(B=P->right)
—————— B的右孩子(BR)红
———————— B染成P的颜色、P染黑、BR染黑,以P左旋,此时完成
——N为右孩子
———— B红
—————— B是红的,此时N和B的父亲结点§一定是黑的,且B一定存在两个黑色的孩子结点。将B染黑、P染红,以P右旋,然后更新B(B=P->left),此时B为黑
———— B黑
—————— B没有红色孩子结点
———————— P红
—————————— 此时,将B染红、P染黑,即完成
———————— P黑
—————————— 将B染红,使N=P,P=N->parent,此时N为黑,回到@的地方继续判断
—————— B的右孩子(BR)红
———————— B染红、BR染黑,以B左旋,更新B(B=P->left)
—————— B的左孩子(BL)红
———————— B染成P的颜色、P染黑、BL染黑,以P右旋,此时完成


代码

//node路径上少了一个黑色节点  node是parent的左或者右,函数开始调用的时候node=NULL
void rbtree_delete_repair(RBTree *ptree,RBNode *node,RBNode *parent){
	while((node==NULL||node->col==BLACK)&&node!=*ptree){	//node为NULL是因为调用函数开始之前,node为黑色叶结点且已被删除;node不为NULL且为黑是在调整平衡的过程中产生的情况;如果node是根节点,那么就不需要调整了
		if(node == parent->left){	//node节点在左边
			RBNode *brother = parent->right;
			if(brother->col == RED){	//兄弟节点为红
				brother->col = BLACK;
				parent->col = RED;
				left_rotation(ptree,parent);	//旋转的代码在之前的博客中(红黑树的插入操作)
			}
			brother = parent->right;	//更新兄弟节点
			//兄弟节点为黑,兄弟节点不存在红色子节点
			if(!((brother->left!=NULL&&brother->left->col==RED)||(brother->right!=NULL&&brother->right->col==RED))){
				brother->col = RED;
				if(parent->col == RED){	//父亲为红
					parent->col = BLACK;
					return;	//完成了
				}
				//父亲为黑
				node = parent;
				parent = node->parent;
				continue;
			}
			//兄弟节点有红色子节点
			brother = parent->right;
			if(brother->right == NULL || brother->right->col==BLACK){	//这里兄弟左孩子为红,右孩子不存在或为黑
				brother->col = RED;
				brother->left->col = BLACK;
				right_rotation(ptree,brother);
			}
			brother = parent->right;
			//兄弟的右节点为红色
			brother->col = parent->col;
			parent->col = BLACK;
			brother->right->col = BLACK;
			left_rotation(ptree,parent);
		}else{//node节点在右边
			RBNode *brother = parent->left;
			if(brother->col == RED){ //兄弟红
				brother->col = BLACK;
				parent->col = RED;
				right_rotation(ptree,parent);
			}
			brother = parent->left;
			//兄弟为黑色,且兄弟结点没有红色的子节点
			if(!((brother->left!=NULL&&brother->left->col==RED)||(brother->right!=NULL&&brother->right->col==RED))){
				brother->col = RED;
				if(parent->col == RED){ //父亲红
					parent->col = BLACK;
					return;
				}
				//父亲黑
				node = parent;
				parent = node->parent;
				continue;
			}
			//兄弟有红色的右节点
			brother = parent->left;
			if(brother->left==NULL || brother->left->col==BLACK){
				brother->col = RED;
				brother->right->col = BLACK;
				left_rotation(ptree,brother);
			}
			brother = parent->left;
			//兄弟节点的左节点为红色
			brother->col = parent->col;
			parent->col = BLACK;
			brother->left->col = BLACK;
			right_rotation(ptree,parent);
			break;
		}
	}
	(*ptree)->col = BLACK;	//红黑树根节点必须是黑色
}

//在查找到需要删除的节点node后,将其删除
void rbtree_delete_rbnode(RBTree *ptree,RBNode *node){
	RBNode *left = node->left;
	RBNode *right = node->right;
	if(left == NULL && right == NULL){//叶子结点
		if(node == *ptree){//删除的是根节点
			*ptree = NULL;
			free(node);
			return;
		}
		//node为叶子节点  删除
		RBNode *parent = node->parent;
		if(node == node->parent->left){
			node->parent->left = NULL;
		}else{
			node->parent->right = NULL;	
		}
		char col = node->col;
		free(node);
		if(col == BLACK){//叶子节点是一个黑色节点,删除后需要平衡
			rbtree_delete_repair(ptree,NULL,parent);
		}
	}else if(left != NULL && right != NULL){//左右都在 
		//转化成删除左子树最大节点 或者 右子树最小节点
		while(right->left != NULL){
			right = right->left;	
		}
		node->data= right->data;
		rbtree_delete_rbnode(ptree,right);
	}else{//要删除的节点只有一个孩子
		RBNode *tree = left!=NULL?left:right;
		tree->col = BLACK;
		if(node->parent == NULL){//删除的是根节点
			*ptree = tree;
		}else{
			if(node == node->parent->left){
				node->parent->left = tree;
			}else{
				node->parent->right = tree;	
			}
		}
		free(node);
	}
}

//按值删除节点
int rbtree_delete(RBTree *ptree,T data,int (*compar)(T,T)){
	RBTree *proot = ptree;
	while(*ptree != NULL){
		int ret = compar(data,(*ptree)->data);
		if(ret == 0){
			rbtree_delete_rbnode(proot,*ptree);
			return 0;
		}else if(ret < 0){
			ptree = &(*ptree)->left;	
		}else{
			ptree = &(*ptree)->right;	
		}
	}
	return -1;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值