红黑树

1、红黑树的性质

一颗红黑树是满足下面红黑性质的二叉搜索树

  1. 每个结点或是红色的,或是黑色的。
  2. 根结点是黑色的。
  3. 每个叶节点(NIL)是黑色的。
  4. 如果一个结点是红色的,则它的两个子结点是黑色的。
  5. 对每一个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点。

1、为了便于处理红黑树代码中的边界条件,使用一个哨兵来代表NIL,哨兵T.nil是一个与树中普通结点有相同属性的对象,它的color为黑色,而其他属性可以为任意值,所有指向NIL的指针都用指向哨兵T.nil的指针替换。
2、树中每个结点包含5个属性:color、key、left(左孩子)、right(右孩子)和p(父结点)。
3、如果一个结点没有子结点或父结点,则该结点相应指针属性的值为NIL。
4、一棵有n内部结点的红黑树的高度至多为2lg(n+1)

红黑树

2、旋转

指针结构的修改是通过旋转(ratation)来完成的,这是一种能保证二叉搜索树性质的搜索树局部操作。分为:左旋和右旋。
左旋、右旋左旋伪代码如下:

//LEFT-ROTATE的伪代码中,假设x.right 不等于 T.nil且根结点的父结点为T.nil
LEFT-ROTATE(T,x)
	//赋值y,并把y的左子树赋值于x的右子树
	y = x.right
	x.right = y.left
	//将y的左子树设置成x
	if(y.left != T.nil){
		y.left.p = x;
	}
	//把x的父结点赋值于y的父结点
	y.p = x.p;
	if(x.p == T.nil){
		T.root = y;
	}else if(x == x.p.left){
		x.p.left = y;
	}else{
		xp.right = y;
	}
	//把x设置为y的左孩子
	y.left = x;
	x.p = y;
	 

3、插入

我们可以在O(lg n) 时间内完成像一个含n个结点的红黑树中插入一个新结点,并通过一个辅助程序对结点重新着色并旋转,保证红黑树的性质。

插入新结点(RB-INSERT)

思路:将z作为一个叶子结点的左孩子或右孩子,并对其维护相应的指针,同时将结点染成RED,之后在对其进行重新着色并旋转。

伪代码如下:

RB-INSERT(T,z)
	//找到待插入结点z的父结点,z的父结点是一个叶子结点(非NIL结点)
	y = T.nil;
	x = T.root;
	while(x != T.nil){
		y = x;
		if(z.key < x.key){
			x = x.left;
		}else{
			x = x.right;
		}
	}
	z.p = y;
	//插入z
	if(y == T.nil){
		T.root = z;
	}else if(z.key < y.key){
		y.left = z;
	}else{
		y.right = z;
	}
	//设置z的子结点属性并赋值红色
	z.left = T.nil;
	z.right = T.nil;
	z.color = RED;
	//z为红色,可能违反一条红黑树性质,重新染色
	RB-INSERT-FIXUP(T,z)

结点重新着色(RB-INSERT-FIXUP)

RB-INSERT-FIXUP(T,z)
	while(z.p.color == RED){
	if(z.p == z.p.p.left){
		y = z.p.p.left;
		if(y.color == RED){
			//case 1 (z的叔结点y是红色的) ,执行结果,保证z和z的父结点和叔结点保证红色树的性质
			z.p.color = BLACK;
			y.color = BLACK;
			//此可能会造成违反性质4,并转化为case 2
			z.p.p.color = RED;
			//将指针z在树中上移两层,()
			z = z.p.p;
		}else if( z == z.p.right){
			//case 2 (:z的叔结点y是黑色的且z是一个右孩子)
			z = z.p;
			//使用一个左旋转换成case 3
			LEFT-ROTATE(T,z);
		}
		//case 3( : z的叔结点y是黑色的且z是一个左孩子)
		z.p.color = BLACK;
		z.p.p.color = RED;
		RIGHT-ROTATE(T,z.p.p);
		}else{
			//另一种情况
		}
		T.root.color = BLACK;
	}

1、插入一个红色结点两个子结点都是哨兵结点,我们可知红黑树的1,3,5性质均成立。性质2和性质四可能遭到破坏,即跟结点必须为黑色,一个红色结点不能有红孩子。
2、case 1 z的叔结点y是红色的(y是z的父结点的兄弟结点)。case 1 修正了被唯一破坏的性质4,但是也可能造成性质4破坏。
3、性质1、3、和5 都在case2和case3中得以保持,同时case2和case3修正了对性质4的违反。
4、最后改变跟结点颜色 保证性质2。
5、在该算法中,仅有case1 导致指针z沿树上升两成,while循环才会重复执行。此外该程序执行所做的旋转不会超过两次,只要执行了case2或case3,while循环就结束了。

伪代码执行示意图

4、删除

删除一个结点需要花费O(lg n)时间。

替代值(RB-TRANSPLANT)

//u是需要删除的结点,v是需要删除结点的子孩子
RB-TRANSPLANT(T,u,v)
	if(u.p == T.nil){
		T.root = v;
	}else if(u == u.p.left){
		u.p.left = v;
	}else{
		u.p.right = v;
	}
	v.p = u.p;

删除结点(RB-DELETE)

伪代码如下:

//T是红黑树,z是删除的结点
RB-DELETE(T,z)
	//记住要删除的结点和颜色
	y = z;
	y-original-color = y.color;
	//只有一个孩子
	if(z.left == T.nil){
		x = z.right;
		RB-TRANSPLANT(T,z,z.right);
	}else if(z.right == t.nil){
		x = z.left;
		RB-TRANSPLANT(T,z,z.left);
	}else{
		//两个孩子情况下
		//找到z的右孩子的最小值并赋值y(因此y是不存在左孩子的结点),并记录其颜色
		y = TREE-MINIMUM(z.right);
		y-original-color = y.color;
		x = y.right;
		if(y.p == z){
			//如果y 是z的直接孩子,改变y的右孩子的指向
			x.p = y;
		}else{
			//如果y不是z的直接孩子,则将红黑树中y的右孩子结点代替y
			RB-TRANSPLANT(T,y,y.right);
			y.right = z.right;
			y.right.p = y;
		}
		//移除z,并使用y替代
		RB-TRANSOLANT(T,z,y);
		y.left = z.left;
		y.left.p = y;
		y.color = z.color;
	}
	//将x移到原本y的位置,y是黑色结点的话,会破坏性质5,因此进行性质5校验
	if(y-original-color == BLACK){
		RB-DELETE-FIXUP(T,x);
	}

辅助过程(RB-DELETE-FIXUP) ,改变颜色和执行旋转来恢复红黑树性质

伪代码如下:

//
RB-DELETE-FIXUP(T,x)
	while(x != T.root and x.color == BLACK){
		if(x == x.p.left){
			w = x.p.right;
			if(w.color == RED){
				//case1:x的兄弟结点w是红色的。
				w.color = BLACK;
				x.p.color = RED;
				LEFT-ROTATE(T,x.p);
				w = x.p.right;	
			}
			if(w.left.color == BLACK and w.right.color == BLACK){
				//case2:x的兄弟结点w是黑色的,而且w的两个子结点都是黑色的。
				w.color = RED;
				x = x.p;	
			}else if(w.right.color == BLACK){
				//case3:x的兄弟结点w是黑色的,w的左孩子是红色的,w的右孩子是黑色的,做以下操作将case3转换成case4
				w.left.color = BLACK;
				w.color = RED;
				RIGHT-ROTATE(T,w);
				w = x.p.right
			}
			//case4 :x的兄弟结点w是黑色的,且w的右孩子是红色的。
			w.color = x.p.color;
			x.p.color = BLACK;
			w.right.color = BLACK;
			LEFT-ROTATE(T,x.p);
			x = T.root; 
		}else{
			//如果x是右孩子结点,相同的情况处理
		}	
	}
	x.color = BLACK;
	

case1、case3、case4执行常数次的颜色改变和至多3次旋转后便终止,case2是while循环可以重复执行的唯一情况

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值