算法————笔试内容--->红黑树(三)

这一篇红黑树将讲解关于红黑树删除的操作,学习本内容的时候小编可是使出了吃屎的尽头~~~~奋斗,好下面开始讲解:

上代码:

节点替换的函数:

void RB_transplant(node *tree_root,node * u,node * v){
	if(u->parent == NULL){
		tree_root = u;
	}else if(u == u->parent->left){
		u->parent->left = v;
	}else {
		u->parent->right = v;
	}
	v->parent = u->parent;
}
节点删除操作的函数:

void RB_delete(node *tree_root,node *z){
	node *y;
	node *x;
	node *search;
	y = z;
	search = z->right;
	int original_color = y->color;
	//对z是否有左右子节点分情况讨论
	if(z->left ==NULL){
		x = z->right;
		RB_transplant(tree_root,z,z->right);
	}else if(z->right ==NULL){
		x=  z->left;
		RB_transplant(tree_root,z,z->left);
	}else{
		//寻找后继元素
		while(search!=NULL){
			y = search;
			search = search->left;
		}
		original_color = y->color;//记录此时的颜色
		x = y->right;//将y的右子树指针地址保存
		//此处的处理是针对y的右侧子树来的
		if(y->parent ==z){
			x->parent = y;
		}else{
			RB_transplant(tree_root,y,y->right);//用y右侧节点代替y的节点
			y->right = z->right;//将原来z右侧指针指向y的右侧节点
			y->right->parent = y;//将y的右侧的父指针指向y
		}
		RB_transplant(tree_root,z,y);//用y来代替z的位置
		//处理原来z的左侧子树的节点
		y->left = z->left;
		y->left->parent =y;
		y->color = z->color;
	}
	//如果颜色为黑色,则有可能打破了红黑树的平衡,因此要进行红黑树修正的工作
	if(original_color ==BLACK){
		RB_delete_fixup(tree_root,x);
	}
}
下面由小编我用图文并茂的方式一一解答每个部分代码的意思:

对于删除操作根据删除节点Z左右子树的存在与否可以分为三种情况:

这个是第一个if中的情况,其操作直接将z的右侧第一个节点来替换z即可。

if(z->left ==NULL){
		x = z->right;
		RB_transplant(tree_root,z,z->right);<span style="font-family: Arial, Helvetica, sans-serif;">}</span>

这个是第二个if中的情况,其操作直接将z的左侧第一个节点来替换z即可。

else if(z->right ==NULL){
		x=  z->left;
		RB_transplant(tree_root,z,z->left);
	}


对于第三种情况有点复杂:

                        

图一                                                   

     

                          图二

              

                                   图三 

  

                                                             图四

图一是第三个elsef要处理的情况:首先通过while来寻找Z的后继y,即最小值。然后找到y的右子树节点,交给x指针来管理,如果y的父节点为z直接将x的父指针指向y即可,如果不是则进行下一步处理:见图片二    原来y的位置由y的右子树节点占据,y的右指针指向原来z的有指针指向的位置,最后将这个节点父指针指向y这样 Z的右侧子树已经移交给y,然后用y开始代替z的位置,见图片三和四,进行最后的删除操作。

else{
		//寻找后继元素
		while(search!=NULL){
			y = search;
			search = search->left;
		}
		original_color = y->color;//记录此时的颜色
		x = y->right;//将y的右子树指针地址保存
		//此处的处理是针对y的右侧子树来的
		if(y->parent ==z){
			x->parent = y;
		}else{
			RB_transplant(tree_root,y,y->right);//用y右侧节点代替y的节点
			y->right = z->right;//将原来z右侧指针指向y的右侧节点
			y->right->parent = y;//将y的右侧的父指针指向y
		}
		RB_transplant(tree_root,z,y);//用y来代替z的位置
		//处理原来z的左侧子树的节点
		y->left = z->left;
		y->left->parent =y;
		y->color = z->color;
	}

上述的删除操作解释完成,最后针对颜色为黑色的时候来进行红黑树的修复工作,因为为黑色的时候打破了红黑树的平衡性,因此要进行修复工作:

那么在删除的时候会打破红黑树的哪些性质呢?

1.有可能打破性质二,当要删除的节点为根节点为黑色而其孩子为红色的时候,那么当删除根节点的时候,红色的孩子代替原来的根节点,这个时候就会打破性质二。

2.有可能打破性质四,如果原来y的父节点为红色y节点为黑色,y节点的右节x点为红色,当y移动到要删除的位置,x连接到y原来的父节点上的时候,就会出现连续2个红色节点的情况,这样就打破了性质四。如下图所示


3.有可能打破性质五,同样如上图当y移动到原来z的位置上的时候,会造成一条支路上黑色的节点丢失,破坏了整体红黑树的平衡性。

对于不知道红黑树怎么操作的可以访问这个网站,可以动态的查看红黑树的插入和删除------》》》》》》》》》》》》点击打开链接

算法导论上的讲解:

改正这一问题的办法是将现在占有y原来位置的节点x视为还有一重额外的黑色。也就是说,如果将任意包含节点x的简单路径上黑节点个数加1,则在这种假设下,性质5成立。当将黑色结点y删除或移动时,将其黑色“下推”给节点x。现在问题变为节点x可能既不是红色,又不是黑色,从而违反了性质1.现在的节点x是双重黑色或者红黑色,这就分别给包含x的简单路径上黑节点数贡献了2或1.x的color属性任然是RED(如果x是红黑色的)或者BLACK(如果x是双重黑色的)。换句话说,节点额外的黑色是针对x节点的,而不是反应在他的color属性上的。

下面分情况讨论一下删除节点所遇到的情况:


情况1:x的兄弟节点w是红色的;

代码片段:

//case1
//如果兄弟节点为红色的时候
if(w->color == RED){
	w->color = BLACK;//将兄弟节点w染成黑色
	x->parent->color = RED;//将x的父节点染成红色
	left_rotate(tree_root,x->parent);//将x的父亲节点左旋转
}

因为w必有黑色子节点,所以可以改变w和x->parent的颜色,然后对x->parent做一次左旋转而不违反红黑树的任何性质。现在,x的新兄弟节点是旋转之前w的某个子节点,其颜色为黑色。这样将情况1转换为情况2,3或4处理。


情况2:x的兄弟节点w是黑色的,而且w的两个子节点都是黑色的;

代码片段:

//case2
//如果x的兄弟节点的左侧子节点颜色为黑且他的右侧节点也为黑的时候
if(w->left->color == BLACK && w->right->color == BLACK){
	w->color = RED;//那么x的兄弟节点必须为红色,则需要将w染色成红色
	x = x->parent;//然后上移x指针到其父亲指针处
因为w也是黑色的,所以从x和w上去掉一重黑色,使得x只有一重黑色而w为红色。为了补偿重x和w中去掉的一重黑色,在原来是红色或黑色的x->parent上新增一重额外的黑色。通过将x->parent作为新节点x来重复while循环。注意到,如果通过情况1进入到情况2,则新结点x是红黑色的,因为原来的x->parent是红色的。因此,新节点x的color属性值c为RED,并且在测试循环条件后循环终止。


情况3:x的兄弟节点w是黑色的,w的左孩子是红色的,w的右孩子是黑色的;

//case3
//如果x的兄弟节点的右侧子节点颜色为黑色,w的左侧节点为红色
}else if(w->right->color ==BLACK){
	w->left->color = BLACK;
	w->color = RED;
	right_rotate(tree_root,w);
	w = x->parent->right;
}
当w为黑色且左子树节点为红色、右孩子为黑色时。可以交换w和其左孩子w->left的颜色,然后对w进行右旋转而不违反红黑树的任何性质。现在x的新兄弟节点w是一个有红色右子树节点的黑色结点,这样就将情况3转换成了情况4.


情况4:x的兄弟节点w是黑色的,w其w的右孩子是红色的;

//case4
w->color = x->parent->color;
x->parent->color = BLACK;
w->right->color = BLACK;
left_rotate(tree_root,x->parent);
x = tree_root;
通过进行某些颜色修改并对x->parent做一次左旋转,可以去掉x的额外黑色,从而使它变为单重黑色,而且不破坏红黑树的任何性质。将x设置为根后,当while循环测试其循环条件时,循环终止。


红黑树删除操作中修改红黑树操作代码如下:

void RB_delete_fixup(node * tree_root,node *x){
	node * w;
	while(x!=tree_root && x->color ==BLACK){
		//x在父节点的左侧的情况
		if(x==x->parent->left){
			//找到x的兄弟节点w
			w = x->parent->right;

			//case1
			//如果兄弟节点为红色的时候
			if(w->color == RED){
				w->color = BLACK;//将兄弟节点w染成黑色
				x->parent->color = RED;//将x的父节点染成红色
				left_rotate(tree_root,x->parent);//将x的父亲节点左旋转
			}
			//case2
			//如果x的兄弟节点的左侧子节点颜色为黑且他的右侧节点也为黑的时候
			if(w->left->color == BLACK && w->right->color == BLACK){
				w->color = RED;//那么x的兄弟节点必须为红色,则需要将w染色成红色
				x = x->parent;//然后上移x指针到其父亲指针处

			//case3
			//如果x的兄弟节点的右侧子节点颜色为黑色,w的左侧节点为红色
			}else if(w->right->color ==BLACK){
				w->left->color = BLACK;
				w->color = RED;
				right_rotate(tree_root,w);
				w = x->parent->right;
			}

			//case4
			w->color = x->parent->color;
			x->parent->color = BLACK;
			w->right->color = BLACK;
			left_rotate(tree_root,x->parent);
			x = tree_root;

			//一下情况与上述case1~case4对称出现
		}else if(x==x->parent->right){
			w = x->parent->left;

			if(w->color==RED){
				w->color = BLACK;
				x->parent->color =RED;
				right_rotate(tree_root,x->parent);
			}
			if(w->left->color ==BLACK && w->right->color==BLACK){
				w->color =RED;
				x = x->parent;
			}else if(w->left->color ==BLACK){
				w->right->color =BLACK;
				w->color =RED;
				left_rotate(tree_root,w);
				w = x->parent->left;
			}
			w->color = x->parent->color;
			x->parent->color =BLACK;
			x->left->color =BLACK;
			right_rotate(tree_root,x->parent);
			x=tree_root;
		}
	}
	x->color = BLACK ;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值