红黑树的性质与包括旋转插入删除等操作(下)
Posted LNMP开发者
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了红黑树的性质与包括旋转插入删除等操作(下)相关的知识,希望对你有一定的参考价值。
插入
红黑树的插入是在二叉搜索树的基础上略作修改,在插入z元素后,并将其着为红色,为了保证红黑树的性质我们调用一个辅助程序对节点进行重新着色并旋转。
rb-insert(T, z)
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
if y == T.nil
T.root = z
elseif z.key < y.key
y.left = z
else
y.right = z
z.left = T.nil
z.right = T.nil
z.color = R
//z着为红色,可能违反其中一条红黑性质,调用fixup继续保持红黑性质
rb-insert-fixup(T, z)
rb-insert-fixup对节点重新着色并旋转:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
rb-insert-fixup(T, z) while z.p.color == R if z.p == z.p.p.left y = z.p.p.right if y.color == R z.p.color = B y.color = R z.p.p.color = R z = z.p.p elseif z == z.p.right z = z.p left-rotate(T, z)
z.p.color = B z.p.p.color = R right-rotate(T, z.p.p) else z.p == z.p.p.right ...(同上,至少right和left交换下。) T.root.color = B |
首先,要确定当z节点被插入并着为红色后,红黑的性质哪些不能保持了;其次while的总目标;最后分析while内的三种情况。
那么分析下fixup过程是如何工作的呢?
while 循环中每次迭代的开头保持下列3个部分的不变式:
a、 节点z是红节点:y.color == R时,红色的z.p.p 赋给了z,z==z.p.right 时,红色的z.p赋给了z;
b、 如果z.p是根节点,则z.p是黑节点
c、 如果有任何红黑性质被破坏,则至多只有一条被破坏,或是性质2,或是性质4.
在初始到终止过程中保持这个循环不变式。
在循环过程中,实际需要考虑while的六种情况,但其中三种和另外3种是对称的。这取决于第2行中z的父节点z.p是祖父节点的右孩子,还是左孩子,代码只给出了是左孩子的。
情况1:z的叔节点y是红色的
6-9行:y == R,由于z和z.p 都是红色的,违反了性质4,由于z的叔节点也是红色,所以执行6-9行节点被重新着色,并且指针z沿树上升两层;
情况2: z的叔节点y是黑色的且z是一个右孩子
情况3: z的叔节点y是黑色的且z是一个左孩子
11 -12行:z == z.p.right,在z上升时,如果发现z和z.p又都是红色,且z是z.p的右孩子时,执行11 -12行;
14-16行: z== z.p.left 如果z是z.p的左孩子时,改变某些节点的颜色并做一次右旋,以保持性质5。
其实,执行11-12行,左旋后,这种情况就可以变为z==z.p.left了。
删除
插入看上去复杂,那删除其实更复杂了。
从一棵红黑树中删除节点的过程是基于tree-delete过程而来的。对应tree-delete调用的子过程transplant,红黑树也要有rb-transplant:
rb-transplant(T, u, v) if u.p == T.nil T.root = v elseif u == u.p.left u.p.left = v else u.p.right = v v.p = u.p |
与transplant不同,rb的第一行用哨兵T.ni 替代了nil;第6行,对v.p的赋值时无条件执行。
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) elseif z.right == T.nil x = x.left rb-transplant(T, z, z.left) else y = tree-min(z.right) y-original-color = y.color x = y.right if y.p == z x.p = z else rb-transplant(T, y, y.right) y.right = z.right y.right.p = y rb-ransplant(T, z, y) y.left = z.left y.left.p = y y.color = z.color if y-original-color == B rb-delete-fixup(T, x)
|
我们来看下rb-delete,过程与tree-delete具有相同的基本结构。两者主要区别在:
1、始终维持节点y为从树中删除的节点,或者移至树内的节点。
2、由于节点y的颜色可能改变,变量y-original-color存储了发生改变前的颜色。
3、保存了x的踪迹,使它移至节点y的原始位置上。
4、因为节点x移动到y的原始位置,属性x.p总是被设置指向树中y父节点的原始位置,甚至当x是哨兵T.nil时也这样。
5、最后,如果节点y时黑色,可能已破坏红黑树的性质。所以调用rb-delete-fixup来修复。当y是红色时,也被删除或移动时,性质没有发生变化。因为黑高没有变化;不存在两个相邻的红节点;如果y是红节点,就不可能是根节点。
rb-delete-fixup(T,x) while x != T.root and x.color == B if x == x.p.left w = x.p.right if w.color == R //这4行处理case1 //x的兄弟节点w是红色的 w.color = B x.p.color =R left-rotate(T, x.p) w = x.p.right if w.left.color == B and w.right.color == B //这2行处理case2 //x的兄弟节点w是黑色的,而且w的两个子节点都是黑色的 w.color = R x = x.p elseif w.right.color == B //这4行处理case3 //x的兄弟节点w是黑色的,w的左孩子是红色的,w的右孩子是黑色 w.left.color = B w.color = R right-rotate(T, w) w = x.p.right //这5行处理case4 //x的兄弟节点w是黑色的,且w的右孩子是红色的 w.color = x.p.color x.p.color = B w.right.color = B left-rotate(T, x.p) x = T.root else //(同上,只是左右交换一下。)
x.color = B
|
以上是关于红黑树的性质与包括旋转插入删除等操作(下)的主要内容,如果未能解决你的问题,请参考以下文章