红黑树理解

RB-INSERT-FIXUP(T, z) {
1   while(z.p.color == RED) {
2	if(z.p == z.p.p.left){ //父结点是祖父结点的左孩子
3		y = z.p.p.right //y是叔父结点
4		if(y.color == RED) //叔父结点也是红色的
5			z.p.color = BLACK
6			y.color = BLACK
7			z.p.p.color = RED
8			z = z.p.p  //不符要求的情形转移到了祖父结点
9		else {		   //叔父结点是黑色的,这时候再看
10		    if (z == z.p.right) {
11			z = z.p
12			LEFT-ROTATE(T, z)
13		     }
14		     z.p.color = BLACK
15		     z.p.p.color = RED
16		     RIGHT-ROTATE(T, z.p.p)
17		}
18	}
19	else {
20	    ……// same as then clause with "right" and "left" exchanged
21	}
22  }
23   T.root.color = BLACK
24 }


这是算法导论中关于红黑树的一段伪代码,说一下自己的理解。

红黑树其实是2-3-4树的一种实现,所以所有的调整操作都是为了使得它能够恢复成一颗2-3-4树。

插入一个节点,直接标为红色,这时候如果父节点是黑色的,那就相当于在2-3-4树中插入时碰到了一个2-结点,所以直接插入不会有影响。

但是如果父结点是红色的,那就是说碰到了一个3-结点或4-结点,这时候就要小心了。

首先是4-结点的情形,也就是第4行中的叔父结点也是红色。想想在2-3-4树中碰到这种情况怎么办?往上分解四结点,这样下面的结点还是能直接插入。

3-结点,也就是叔父结点是黑色的情况。本来在2-3-4树中也是直接插入的。但是这里有插入位置的问题,所以想办法旋转使之满足条件。第10行~13行将结点为左孩子的情形统一到新结点为右孩子(也就是与父结点一致),然后修改结点颜色,旋转。

既然红黑树是2-3-4树的一种实现,我觉得理解红黑树最好还是对应到2-3-4树的情况比较容易。


删除操作:

RB-DELETE-FIXUP(T, x) {
1    while(x != T.root and x.color == BLACK) {
2	if(x == x.p.left) {
3	    w = x.p.right
4	    if(w.color == RED) { //兄弟结点为红色的,则父结点肯定是黑色的
5		w.color = BLACK
6		x.p.color = RED
7		LEFT-ROTATE(T, x.p)
8		w = x.p.right
9	    }
	    //要注意的是,经过了第一种情形的处理之后,x的父结点就变成一个红结点了
	    //因为之前w是红结点,所以w的右孩子必须是黑结点,则新的w就必然是黑结点了
	    //所以说,兄弟结点为红结点的情况经过case1处理之后就变成case2、case3或case4的情况了
10	    if(w.left.color == BLACK and w.right.color == BLACK){//兄弟节点是一个黑结点,现在变红,则x\w两支路都少了一个黑结点,现在只有赋予它们的父结点一个附加的黑色属性才能
								 //解决问题,如果父结点原来就是红色的(比如从情形1转来的时候),则结束while,把它刷黑,否则
								//就针对父结点处理问题,相当于把问题结点上移
11		w.color = RED
12		x = x.p
13	    }
14	    else{ //注意这里进入else的条件是w至少有一个孩子结点为红色的
15		if(w.right.color == BLACK) {
16		    w.left.color = BLACK
17		    w.color =RED
18		    RIGHT-ROTATE(T, w)
19		    w = x.p.right
20		}
		//这里值得注意的是,可以肯定w是黑色的(红色情形在case1),但是x.p可黑可红
		//左旋操作会使得W变到原来x.p的位置,w的右孩子被刷黑是补偿旋转之后的这一支所损失掉的一个黑结点
		//多想一下:x.p原来为黑的话,即使w做了新的父结点,那也损失了x.p这个黑结点,x.p原来为红的话,一样的损失了w这个黑结点(这时w变成红的了)
		//把x.p变黑就使得x所在这一支补充了一个黑结点,于是x的多一层黑结点的性质就不用了,所以满足了红黑树的性质,于是直接退出
21		w.color = x.p.color
22		x.p.color = BLACK
23		w.right.color = BLACK
24		LEFT-ROTATE(T, x.p)
25		x = T.root
26          }
27	}
28	else {
29	    ……//same as then clause with "right" and "left" exchanged
30	}
31`    }
32    x.color = BLACK
33}

删除操作相对来说比较琐碎,涉及到的情形很难统一起来去理解。
首先要知道的是,这个调整伪代码执行的前提是:删除的y结点为黑色结点,而输入的x结点为y的子节点或nil[T]。
然后是逐行理解:
可以看到的是,伪代码中分了四种情形(其实是八种,后四种对称,所以没写)。这里我换个思路,先分成两种情况
case 1: x的兄弟结点为红结点
这一情况能够得到的事实是,x.p为黑结点,w的两个孩子都是黑结点。这时修改x.p为红结点,w为黑结点,在x.p处左旋
,其实这样做保持了红黑性质(也就是说出问题的还是x所在的那一支,不多不少),接着往下执行的话,就等于转到了case2
case 2:x的兄弟结点为黑结点
这时候推断不出什么,只能进一步分情况。
第一种情况,w的两个孩子都是黑的,这样就可以把W刷红,然后为了使得红黑性质不变,就把x.p附加黑色属性,这就一下
解决了x的双重黑色属性的问题,也使得w这一支不出问题。但是x.p也有可能有双重黑色属性。于是矛盾上移。
第二种情况,w至少有一个孩子是红色的。这里就进一步分成w.right是红色和w.right不是红色两种情况,然后将w.right不是红色的情况也统一的w.right是红色的情况再进行处理。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值