红黑树,必学经典,一文带你走进红黑树的插入和删除

红黑树

五大性质

1)每个结点要么是红的,要么是黑的。  
2)根结点是黑的。  
3)每个叶结点(叶结点即指树尾端NIL指针或NULL结点)是黑的。  
4)如果一个结点是红的,那么它的俩个儿子都是黑的。  不可能有两个连续的红
5)对于任一结点而言,其到叶结点树尾端NIL指针的每一条路径都包含相同数目的黑结点。 

左旋

左旋就是 成为右孩子的左孩子

LeftRate(node x)

y=x.right;  

x.right=y.left; y的左孩子成为x的右孩子 

y.parent=x.parent;   x的父节点成为y的父节点

y.left=x;  x成为y的左孩子

x.parent=y; 

右旋

成为左孩子的右孩子

RightRate(node x)

y=x.left;

x.left=y.right;   y的右孩子成为x的左孩子

y.parent=x.parent;  x的父亲 成为y的父亲

y.right=x;   x成为y的右孩子

x.parent=y;

插入节点

如果插入的是根结点,由于原树是空树,此情况只会违反性质2,因此直接把此结点涂为黑色;

如果插入的结点的父结点是黑色,由于此不会违反性质2和性质4,红黑树没有被破坏,所以此时什么也不做。

父节点是黑色,什么也不做

● 插入修复情况1:如果当前结点的父结点是红色,叔叔是红色

● 插入修复情况2:当前节点的父节点是红色,叔叔节点是黑色,当前节点是其父节点的右子

● 插入修复情况3:当前节点的父节点是红色,叔叔节点是黑色,当前节点是其父节点的左子

RB-INSERT(T, z)
y ← nil
x ← T.root
while x ≠ T.nil
	do y ← x
	if z.key < x.key
		then x ← x.left
	else x ← x.right
z.p ← y
if y == nil[T]
	then T.root ← z
else if z.key < y.key
	then y.left ← z
else y.right ← z
z.left ← T.nil
z.right ← T.nil
z.color ← RED  插入的节点的颜色 最先是红色。
RB-INSERT-FIXUP(T, z)

插入修复

RB-INSERT-FIXUP(T, z)
while z.p.color == RED
	do if z.p == z.p.p.left
		then y ← z.p.p.right
		if y.color == RED
			then z.p.color ← BLACK               ▹ Case 1  //父节点 叔叔节点都是红色
			y.color ← BLACK                    ▹ Case 1   //那么父节点 叔叔节点全部变黑
			z.p.p.color ← RED                    ▹ Case 1 // 爷爷节点变成红色
			z ← z.p.p                            ▹ Case 1 //爷爷节点变成当前节点
		else if z == z.p.right   //如果当前节点(可能是之前爷爷节点,对应情况1)是父节点的右孩子,且父亲是红色,叔叔是黑色 ,那么就是情况2
			then z ← z.p                          ▹ Case 2  //把当前节点的父节点作为当前节点
			LEFT-ROTATE(T, z)                   ▹ Case 2  //再对当前节点进行左旋
		z.p.color ← BLACK  ▹ Case 3 //情况3当前节点的父节点是红色,叔叔是黑色,当前节点是父节点的左子
		z.p.p.color ← RED                         ▹ Case 3 //把父节点变成黑色,爷爷变成红色
		RIGHT-ROTATE(T, z.p.p)                  ▹ Case 3//再对爷爷右 旋。
	else (same as then clause with "right" and "left" exchanged)
T.root.color ← BLACK  //最后再把根节点涂黑

删除节点

① 被删除节点没有儿子,即为叶节点。那么,直接将该节点删除就OK了。
② 被删除节点只有一个儿子。那么,直接删除该节点,并用该节点的唯一子节点顶替它的位置。
③ 被删除节点有两个儿子。那么,先找出它的后继节点;然后把“它的后继节点的内容”复制给“该节点的内容”;之后,删除“它的后继节点”。在这里,后继节点相当于替身,在将后继节点的内容复制给"被删除节点"之后,再将后继节点删除。这样就巧妙的将问题转换为"删除后继节点"的情况了,下面就考虑后继节点。 在"被删除节点"有两个非空子节点的情况下,它的后继节点不可能是双子非空。既然"的后继节点"不可能双子都非空,就意味着"该节点的后继节点"要么没有儿子,要么只有一个儿子。若没有儿子,则按"情况① "进行处理;若只有一个儿子,则按"情况② "进行处理。 情况三 转移为删除后继节点

 RB-DELETE(T, z) 删除z节点
1 if left[z] = nil[T] or right[z] = nil[T]  
 2    then y ← z  
 3    else y ← TREE-SUCCESSOR(z)   y=z的后继节点,后继节点只可能有一个子节点,或者没有节点
 4 if left[y] ≠ nil[T]  
 5    then x ← left[y]  x为被删节点的左子节点
 6    else x ← right[y]   否则x为被删节点的右子节点
 7 p[x] ← p[y]   让y的父亲变成x的父亲 ,再让父亲指向 x就可以了  
 8 if p[y] = nil[T]  
 9    then root[T] ← x  
10    else if y = left[p[y]]  
11            then left[p[y]] ← x  
12            else right[p[y]] ← x  
13 if y ≠ z   //把z的后驱节点找到以后 再把 y的值赋值给z 这样 z以前的值就被删了  然后再去
14    then key[z] ← key[y]  
15         copy y's satellite data into z  
16 if color[y] = BLACK 始终记住  y是被删的节点 x在y以前的位置,所以从x开始调整
17    then RB-DELETE-FIXUP(T, x)  
18 return y  

删除修复

“上面的修复情况看起来有些复杂,下面我们用一个分析技巧:我们从被删节点后来顶替它的那个节点开始调整,并认为它有额外的一重黑色。这里额外一重黑色是什么意思呢,我们不是把红黑树的节点加上除红与黑的另一种颜色,这里只是一种假设,我们认为我们当前指向它,因此空有额外一种黑色,可以认为它的黑色是从它的父节点被删除后继承给它的,它现在可以容纳两种颜色,如果它原来是红色,那么现在是红+黑,如果原来是黑色,那么它现在的颜色是黑+黑。有了这重额外的黑色,原红黑树性质5就能保持不变。现在只要恢复其它性质就可以了,做法还是尽量向根移动和穷举所有可能性。"

把当前节点 看成 红+黑 或者 黑黑 ,就可以保证性质5在删除一个节点后 不会改变,然后再恢复其他性质

不用修复的情况

  • a)当前节点是红+黑色
    解法,直接把当前节点染成黑色,结束此时红黑树性质全部恢复。
  • b)当前节点是黑+黑且是根节点, 解法:什么都不做,结束。

删除修复情况1:当前节点是黑+黑且兄弟节点为红色(此时父节点和兄弟节点的子节点分为黑)

 解法:把父节点染成红色,把兄弟结点染成黑色,之后重新进入算法(我们只讨论当前节点是其父节点左孩子时的情况)。此变换后原红黑树性质5不变,而把问题转化为兄弟节点为黑色的情况(注:变化前,原本就未违反性质5,只是为了把问题转化为兄弟节点为黑色的情况)
 //调用RB-DELETE-FIXUP(T, x) 的1-8行代码
 1 while x ≠ root[T] and color[x] = BLACK
 2     do if x = left[p[x]]
 3           then w ← right[p[x]]
 4                if color[w] = RED
 5                   then color[w] ← BLACK                        ▹  Case 1
 6                        color[p[x]] ← RED                       ▹  Case 1
 7                        LEFT-ROTATE(T, p[x])                    ▹  Case 1
 8                        w ← right[p[x]]                         ▹  Case 1

删除修复情况2:当前节点是黑加黑且兄弟是黑色且兄弟节点的两个子节点全为黑色

解法:把当前节点和兄弟节点中抽取一重黑色追加到父节点上,把父节点当成新的当前节点,重新进入算法。(此变换后性质5不变),即调用RB-INSERT-FIXUP(T, z) 的第9-10行代码操作
//调用RB-DELETE-FIXUP(T, x) 的9-11行代码
9                if color[left[w]] = BLACK and color[right[w]] = BLACK
10                   then color[w] ← RED                          ▹  Case 2
11                        x p[x]                                  ▹  Case 2

删除修复情况3:当前节点颜色是黑+黑,兄弟节点是黑色,兄弟的左子是红色,右子是黑色

解法:把兄弟结点染红,兄弟左子节点染黑,之后再在兄弟节点为支点解右旋,之后重新进入算法。此是把当前的情况转化为情况4,而性质5得以保持,即调用RB-INSERT-FIXUP(T, z) 的第12-16行代码
//调用RB-DELETE-FIXUP(T, x) 的第12-16行代码
12                   else if color[right[w]] = BLACK
13                           then color[left[w]] ← BLACK          ▹  Case 3
14                                color[w] ← RED                  ▹  Case 3
15                                RIGHT-ROTATE(T, w)              ▹  Case 3
16                                w ← right[p[x]]                 ▹  Case 3

删除修复情况4:当前节点颜色是黑-黑色,它的兄弟节点是黑色,但是兄弟节点的右子是红色,兄弟节点左子的颜色任意

 解法:把兄弟节点染成当前节点父节点的颜色,把当前节点父节点染成黑色,兄弟节点右子染成黑色,之后以当前节点的父节点为支点进行左旋,此时算法结束,红黑树所有性质调整正确,即调用RB-INSERT-FIXUP(T, z)的第17-21行代码
 //调用RB-DELETE-FIXUP(T, x) 的第17-21行代码
17                         color[w] ← color[p[x]]                 ▹  Case 4
18                         color[p[x]] ← BLACK                    ▹  Case 4
19                         color[right[w]] ← BLACK                ▹  Case 4
20                         LEFT-ROTATE(T, p[x])                   ▹  Case 4
21                         x ← root[T]                            ▹  Case 4

此时,我们需要调用RB-DELETE-FIXUP(T, x),来恢复与保持红黑性质的工作。

 1 while x ≠ root[T] and color[x] = BLACK  
 2     do if x = left[p[x]]  
 3           then w ← right[p[x]]  
 4                if color[w] = RED  //情况1兄弟为红色 ,兄弟的孩子都是黑
 5                   then color[w] ← BLACK     //  兄弟变黑                 ▹  Case 1  
 6                        color[p[x]] ← RED    //父亲变红                   ▹  Case 1  
 7                        LEFT-ROTATE(T, p[x])   //父亲左旋                 ▹  Case 1  
 8                        w ← right[p[x]]     //更新当前节点的兄弟           ▹  Case 1  
 9                if color[left[w]] = BLACK and color[right[w]] = BLACK//兄弟是黑,兄弟左右子都是黑
10                   then color[w] ← RED //各抽一成黑给父亲,父亲变成红+黑    ▹  Case 2  
11                        x ← p[x]    //  把父节点作为当前节点 ,当前节点是红+黑了       ▹  Case 2  
12                   else if color[right[w]] = BLACK  //兄弟是黑 兄弟 的左子是红 右子是黑
13                           then color[left[w]] ← BLACK   //兄弟左子变成黑       ▹  Case 3  
14                                color[w] ← RED           //兄弟变成红       ▹  Case 3  
15                                RIGHT-ROTATE(T, w)    //以兄弟右旋          ▹  Case 3  
16                                w ← right[p[x]]      //更新当前节点的兄弟           ▹  Case 3  
17                         color[w] ← color[p[x]]     //兄弟的颜色改成父亲的颜色            ▹  Case 4  
18                         color[p[x]] ← BLACK     //父亲的颜色变成黑               ▹  Case 4  
19                         color[right[w]] ← BLACK  //  兄弟的右子变成黑色              ▹  Case 4  
20                         LEFT-ROTATE(T, p[x])    //以父亲左旋               ▹  Case 4  
21                         x ← root[T]  //继续循环操作。                          ▹  Case 4  
22        else (same as then clause with "right" and "left" exchanged)  
23 color[x] ← BLACK  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值