一棵高度为h的二又搜索树,任何一种基本动态集合操作,如 SEARCH、PREDECESSOR、SUCCESSOR、MINIMUM、MAXIMUM、INSERT 和 DELETE 等,其时间复杂度均为 O(h),因此,如果搜索树的高度较低时,这些集合操作会执行得较快
红黑树 是许多“平衡"搜索树中的一种,可以保证 在最坏情况下基本动态集合操作的时间复杂度为 O(lgn)
1、红黑树的性质
1、红黑树是 一棵二叉搜索树,它在每个结点上 增加了一个存储位 来表示结点的颜色,可以是 RED 或 BLACK。通过 对任何一条从根到叶子的简单路径上 各个结点的颜色进行约束,红黑树 确保 没有一条路径会比其他路径长出2倍,因而是 近似平衡的
2、树中每个结点包含5个属性:color、key、left、right 和 p。如果一个结点没有子结点或父结点,则该结点 相应指针属性的值 为NIL。把这些NIL 视为指向二叉搜索树的叶结点(外部结点)的指针,而把带关键字的结点 视为树的内部结点
3、一棵红黑树 是满足下面红黑性质的二叉搜索树:
- 每个结点或是红色的,或是黑色的
- 根结点是黑色的
- 每个叶节点(NIL)是黑色的
- 如果一个结点 是红色的,则它的两个子结点 都是黑色的
- 对每个结点,从 该结点到其后代叶结点的简单路径上,均包含 相同数目的黑色节点
4、为了便于 处理红黑树代码中的边界条件,使用一个哨兵来代表NIL。对于一棵红黑树T,哨兵 T.nil 是一个与树中的普通结点 有相同属性的对象。它的 color 属性为 BLACK,而其他属性 p、left、right 和 key 可以设为任意值,如图所示,所有指向NIL的指针都 用指向哨兵 T.nil 的指针替换
使用哨兵后,就可以 将结点x的 NIL 孩子 视为一个普通结点,其父结点为x。尽管 可以为树内的每一个 NIL 新增一个不同的哨兵结点,但这种做法 会浪费空间。使用一个哨兵 T.nil 来代表所有的 NIL:所有的叶结点 和 根结点父结点。哨兵的属性 p、left、right 和 key 的取值并不重要
通常将注意力放在 红黑树的内部节点上,后面所有图中 红黑树都忽略了叶结点
5、从某个结点x出发(不含该结点)到达一个叶结点的任意一条简单路径上的 黑色结点个数 成为该结点的黑高,记为 bh(x)。从该结点出发的 所有下降到其叶子结点的简单路径的黑结点个数 都相同。于是 定义红黑树的黑高 为其根结点的黑高
6、一棵有n个内部结点的红黑树的高度 至多为 2lg(n + 1)
先证明 以任一结点x为根的子树中 至少包含2bh(x)-1个内部结点。要证明这点,对x的高度进行归纳
如果x的高度为0,子树至少包含 2bh(x)-1 = 20-1 = 0个内部结点
考虑一个高度为正值 且 有两个子结点的内部结点x。每个子结点有黑高 bh(x) 或 bh(x) - 1,其分别取决于 自身的颜色是红还是黑。由于x子结点的高度 比x本身的高度要低,利用归纳假设得出 每个子结点至少有 2bh(x) - 1个内部结点的结论。于是,以x为根的子树 至少包含 (2bh(x) - 1) + (2^bh(x) - 1^ - 1) + 1 = 2bh(x) - 1 个内部结点
从根到叶结点(不包括根结点)的任何一条简单路径上 都至少有一半的结点为黑色,因此,根的黑高至少为 h/2
n >= 2h/2 - 1 (b(h(x)) >= h / 2)
再对两边取对数,得到 lg(n + 1) >= h/2,或者 h <= 2lg(n + 1)
动态集合操作 SEARCH、MINIMUM、MAXIMUM、SUCCESSOR 和 PREDECESSOR 可在红黑树上在 O(lgn) 时间内执行,因为 这些操作在一棵高度为h的二叉搜索树上的运行时间为 O(h),而任何 包含n个结点的红黑树又都是高度为 O(lg n) 的二叉搜索树 仍是红黑树
7、画出在关键字集合{1,2,…,15}上高度为3的完全二叉搜索树。以三种不同方式向图中加入NIL叶结点并对各结点着色,使所得的红黑树的黑高分别为2、3和4
8、证明:在一棵红黑树中,从 某结点x到其后代叶结点的所有简单路径中,最长的一条至多是最短一条的2倍
根据红黑性质5,在一棵红黑树中,从某结点x到其后代叶结点的所有简单路径中,均包含相同数目的黑色结点。路径最短的情况是全是黑色结点,路径最长的情况是包含了尽可能多的红色结点。根据红黑性质4,路径中的红色结点之间要间隔至少一个黑色结点,所以最长路径中的红色结点数目至多与黑色结点相同。因此,在一棵红黑树中,从某结点x到其后代叶结点的所有简单路径中,最长的一条至多是最短一条的2倍
9、在一棵黑高为k的红黑树中,内部结点 最多可能有多少个?最少可能有多少个?
在一棵黑高为k的红黑树中,树的高度最多为2k,除去叶结点后由内部结点组成的树的高度最多为2k - 1,所以内部结点最多可能有 2(2k-1)+1 - 1 = 22k - 1 个,根据6,最少可能有2k-1个
10、试描述一棵含有n个关键字的红黑树,使其 红色内部结点个数 与 黑色内部结点个数的比值最大?该比值最小的树呢
2、旋转
1、TREE INSERT 和 TREE-DELETE 这两个操作对树做了修改, 结果可能违反红黑性质。为了维护这些性质,必须要改变树中某些结点的颜色以及指针结构
指针结构的修改是通过旋转 来完成的,这是一种能保持二叉搜索树性质的搜索树局部操作。左旋和右旋
字母α、β和γ代表任意的子树。旋转操作 保持了二叉搜索树的性质:α的关键字在 x.key之前,x.key在β的关键字之前,β的关键字在y.key之前,y.key在y的关键字之前(主要是旋转后β的插入)
LEFT-ROTATE(T, x)
1 y = x.right // set y
2 x.right = y.left // turn y's left subtree into x's right subtree
3 if y.left ≠ T.nil
4 y.left.p = x
5 y.p = x.p // link y's parent to x
6 if x.p = T.nil
7 T.root = y
8 elseif x = x.p.left
9 x.p.left = y
10 else x.p.right = y
11 y.left = x // put x on y's left
12 x.p = y
LEFT-ROTATE 和 RIGHT-ROTATE 都在 O(1) 时间内运行完成. 在旋转操作中 只有指针改变,其他所有属性都保持不变
2、RIGHT-ROTATE的伪代码
RIGHT-ROTATE(T, y)
x = y.left // set x
y.left = x.right // turn x's right subtree into x's left subtree
if x.right ≠ T.nil
x.right.p = y
x.p = y.p // link y's parent to x
if y.p == T.nil
T.root = x
elseif y == y.p.left
y.p.left = x
else y.p.right = x
x.right = y // put y on x's right
y.p = x
3、证明: 在任何一棵有n个结点的二叉搜索树中,恰有n-1种可能的旋转(旋转看边)
证明:因为在一棵有n个结点的二叉搜索树中,共有n-1条边,每条边都可以旋转(左旋或者右旋),所以恰有n-1种可能的旋转
4、证明任何一棵含n个结点的二叉搜索树可以通过O(n)次旋转, 转变为其他任何一棵 含n个结点的二叉搜索树
首先,因为可以将任何一个含有
n
n
n个节点的二叉搜索树变为一条右侧伸展的链。由于只需要至多
n
−
1
n-1
n−1次操作,所以这是一个线性时间复杂度内
O
(
n
)
O(n)
O(n)的过程
对于上述任意树变为链条的过程,反过来也是成立的。即通过左旋转,一条右侧伸展的链也可以变回二叉搜索树。对于任意的二叉搜索树结构,都可以先通过右旋转变为一条链,然后通过左旋转变为目标树结构。因为从链到任意树形状的二叉搜索树也最多需要 n − 1 n-1 n−1次左旋转操作
3、插入
1、可以在O(lgn)时间内完成向一棵含n个结点的红黑树中插人一个新结点。利用TREE-INSERT过程的一个略作修改的版本来将结点z插人树T内,就好像T是一棵普通的二叉搜索树 一样, 然后将立行为红色。为保证红黑性质能继续保持,调用一个辅助程序RB-INSERT-FIXUP来对结点重新着色并旋转
调用RB-INSERT(T, z)在红黑树T内插人结点z,假设z的key属性已被事先赋值
过程TREE-INSERT 和 RB-INSERT 之间有4处不同
第一,TREE-INSERT内的所有NIL都被T.nil代替
第二,RB-INSERT的第14~15行置z.left和z.right为T.nil,以保持合理的树结构
第三,在第16行将z着为红色
第四,因为将z着为红色 可能违反其中的一条红黑性质,所以在 RB-INSERT 的第17行中调用 RB-INSERT-FIXUP(T, z) 来保持红黑性质
把情况1,2都转成3
由于z和它的父结点z.p都是红色,所以 违反了性质4。由于z的叔结点y是红色的, 可以应用程序中的情况1。结点被重新着色,并且指针z沿树上升
再一次z及其父结点 又都为红色.但z的叔结点y是黑色的。因为z是z.p的右孩子。在执行1次左旋之后
z是其父结点的左孩子, 可以应用情况3。重新着色并执行一次右旋后得 合法的红黑树
2、在调用RB-INSERT-FIXUP操作时(光插入了一个红色的新结点), 哪些红黑性质可能会被破坏呢?
性质1和性质3继续成立。因为新插入的红结点的两个子结点 都是哨兵T.nil。性质5,即从一个指定结点开始的每条简单路径上的黑结点的个数 都是相等的 也会成立, 因为结点z代替了(黑色)哨兵, 并且 结点z本身是有哨兵孩子的红结点
仅可能被破坏的就是性质2(根结点是黑色的)和 性质4(如果一个结点 是红色的,则它的两个子结点 都是黑色的)
这两个性质可能被破坏 是因为z被着为红色。如果z是根结点,则破坏了性质2;如果z的父结点是红结点,则破坏了性质4
3、第1~15行中的while循环在每次迭代的开头保持下列3个部分的不变式
a. 结点z是红结点
b. 如果z.p是根结点, 则z.p是黑结点
c. 如果有任何红黑性质被破坏, 则至多只有一条被破坏, 或是性质2,或是性质4. 如果性质2被破坏, 其原因为z是根结点且是红结点 如果性质4被破坏,其原因为z和z.p都是红结点
不变式(满足红黑树性质)证明:
初始化:在循环的第一次迭代之前,从一棵正常的红黑树开始,并新增一个红结点z。要证明当 RB-INSERT-FIXUP被调用时, 不变式的每个部分都成立(只可能违反二 或 四)
终止: 循环终止是因为z.p是黑色的(如果z是根结点,那么z.p是黑色哨兵T.nil)。这样,树在循环终止时没有违反性质4。根据循环不变式, 唯一可能不成立的是性质2,第16行恢复这个性质
所以当RB-INSERT-FIXUP终止时,所有的红黑性质都成立
保持:实际需要考虑while循环中的6种情况, 而其中三种与另外三种是对称的。这取决于第2行中z的父结点z.p是z的祖父结点z.p.p的左孩子,还是右孩子。只给出z.p是左孩子时的代码
因为只有在z.p是红色时 才进入一次循环迭代,所以z.p不可能是根结点。因此,z.p.p存在
情况1和情况2、 情况3的区别在于 z父亲的兄弟结点(或称为"叔结点”)的颜色不同。如果y是红色的,那么执行情况1, 否则,控制转向 情况2和情况3上. 在所有三种情况中, z的祖父结点z.p.p是黑色的, 因为它的父结点z.p是红色的,故性质4 只在z和z.p之间被破坏了
1)情况1: z的叔结点y是红色的(情况1)
这种情况在z.p和y都是红色时发生。因为 z.p.p 是黑色的. 所以将 z.p 和 y 都着为黑色,以此解决 z 和 z.p 都是红色的问题,将z.p.p着为红色 以保持性质5(黑色结点数量一致)。 然后,把 z.p.p 作为新结点z 来重复while循环。指针z在树中上移两层
无论z是一个右孩子(图(a))还是一个左孩子(图(b))。都同样处理
现在性质4的破坏 只可能发生在新的红色结点z 和 它的父结点之间。条件是 如果父结点也为红色的
a. 因为这次迭代把 z.p.p 着为红色,结点 z 在下次迭代的开始是红色的
b. 在这次迭代中将结点 z 是 z.p.p。且这个结点的颜色不会改变
c. 我们已经证明情况 1 保持性质 5,而此也不会引起性质 1 或性质 3 的破坏
如果结点 z’ 在下次迭代开始时是根结点,则在这次迭代中 情况 1 修正了唯一被破坏的性质 4(红黑黑)。由于z’是红色的而且是根结点,所以 性质2 成为唯一被违反的性质
如果结点z’在下一次迭代开始时 不是根结点,则情况1不会导致 性质2(根黑)的破坏。情况1修正了 在这次迭代的开始唯一被破坏的性质4。然后它把 z’ 着为红色而 z’.p 不变。如果 z’.p 是黑色的,则没有违反性质4。如果 z’.p 是红色的,则把 z’ 着为红色会在 z’ 与 z’.p 之间造成性质4的违反
2)情况2: z的叔结点y是黑色的 且z是一个右孩子
3)z的叔结点y是黑色的 且z是一个左孩子
z的叔结点y是黑色的。在情况2和情况3中.z的叔结点y是黑色的。在情况2中,结点z是它的父结点的右孩子。可以 立即使用一个左旋 来将此情形转变成 情况3(第12~14行),此时结点z为 左孩子。因为z和z.p都是红色的,所以该旋转 对结点的黑高和性质5都无影响。z的叔结点y 总是黑色的
由于z和它的父结点 z.p 都是红色的,每一棵子树α、β、γ 和 ζ 都有一个 黑色根结点(α、β、γ是由性质4而来,ζ也有无色根结点, 因为 否则将导致情况1),而且具有相同的黑高。通过左旋 将情况2转变为情况3,以保持性质5(从一个结点向下到一个叶结点的所有简单路径都有相同数目的黑结点)。情况3 引起某些结点颜色的改变,以及一个同样为了保持性质5的右旋
情况2和情况3修正了对性质4 的违反, 也不会引起对其他红黑性质的违反
4、分析RB-INSERT 的运行时间:
由于一棵 有n个结点的红黑树的高度为O(lgn),因此RB-INSERT的第1~16行 要花费O(lgn)时间。在 RB-INSERT-FIXUP 中,仅当情况1发生, 然后指针z 沿着树 上升2层,whlle循环才会重复执行。所以while循环可能被执行的总次数为 O(lgn)
RB-INSERT 共花费 O(lgn) 时间。此外 该程序所做的旋转 从不超过2次, 因为只要执行了 情况2 或 情况3, whlle循环就结束了
5、在 RB-INSERT 的第16行, 将新插入的结点z着为红色。注意到,如果将z着为黑色,则红黑树的性质4(红黑黑)就不会被破坏,那么为什么不选择将z着为黑色呢
性质5(黑路同)就被破坏了,调整起来更加困难
6、将关键字41 38 31 12 19 8连续地插人一棵初始为空的红黑树之后, 试画出该结果树
7、Teach教授 担心RB-INSERT-FIXUP可能将T.nil.color 设为RED, 这时,当z为根时,第1行的测试 就不会让循环终止
讨论 RB-INSERT-FIXUP 永远不会将 Tnil.color 设为RED 来说明这位教授的担心是没有必要的
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
4 if y.color == RED
5 z.p.color = BLACK // case 1
6 y.color = BLACK // case 1
7 z.p.p.color = RED // case 1
8 z = z.p.p // case 1
9 else if z == z.p.right
10 z = z.p // case 2
11 LEFT-ROTATE(T, z) // case 2
12 z.p.color = BLACK // case 3
13 z.p.p.color = RED // case 3
14 RIGHT-ROTATE(T, z.p.p) // case 3
15 else (same as then clause with "right" and "left" exchanged)
16 T.root.color = BLACK
在RB-INSERT-FIXUP中将结点设为RED的只有第7行和第13行,执行它们的共同条件是z.p==z.p.p.left,此时z.p.p≠T.nil。所以第7行不会将T.nil.color设为RED,虽然在执行第13行之前,在第10行将z提升了一层,但在执行完第11行的左旋后z又下降了一层,所以z.p.p不变。因此,RB-INSERT-FIXUP永远不会将T.nil.color设置为RED
8、考虑一棵用RB-INSERT插入n个结点而成的红黑树。证明:如果n>1, 则该树至少有一个红结点
①若出现 case 2 或 case 3,则第 16 行的 T.root.color = BLACK 并不会起作用;所以会运行第 13 行的 z.p.p.color = RED,即在循环不变式中会出现红结点,且退出循环后红结点颜色不会变更。②若循环中只出现 case 1,则当n>1 时,插入的结点必不为根结点,且插入的结点为红色,所以第 16 行不会改变插入的红结点的颜色。综上,至少存在一个红结点
9、说明如果红黑树的表示中不提供父指针, 应当如何有效地实现RB-INSERT
通过用栈保存父节点,在适当的时候压入和弹出节点,从而有效地实现RB-INSERT
4、删除
1、与n个结点的红黑树上的其他基本操作一样,删除一个结点要花费 O(lgn) 时间。与插入操作相比,删除操作要稍微复杂些
删除节点过程是 基于 TREE-DELETE 过程而来的。首先,需要 特别设计一个供 TREE-DELETE 调用的子过程 TRANSPLANT,并将其应用到红黑树上(用v树替换u树)
RB-TRANSPLANT(T, u, v)
1 if u.p == T.nil
2 T.root == v
3 elif u == u.p.left
4 u.p.left == v
5 else u.p.right == v
6 v.p == u.p
过程 RB-TRANSPLANT 与 TRANSPLANT 有两点不同。首先,第1行引用哨兵 T.nil 而不是 NIL。其次,第6行对 v.p 的赋值是无条件执行:即使 v 指向哨兵,也要对 v.p 赋值。当 v==T.nil 时,也能给 v.p 赋值
2、过程 RB-DELETE 与 TREE-DELETE 类似,多出的几行代码记录结点 y(后继)的颜色,并有可能导致红黑性质的破坏。当想要删除结点 z,且此时 z 的子结点少于 2 个时,z 从树中删除,并让 y 成为 z。当 z 有两个子结点时,y 应该是 z 的后继,并且 y 将移至树中的 z 位置。在结点被移除 或者 在树中移动之前,必须记住 y 的颜色,并且记录结点 x 的位置,将 x 移至树中 y 的原始位置。因为结点 x 也可能引起红黑性质的破坏
删除结点 z 之后,RB-DELETE 调用一个辅助过程 RB-DELETE-FIXUP,该过程 通过改变颜色和执行旋转来恢复红黑性质
在 RB-DELETE 中能够找到 TREE-DELETE 的每一行语句(其中 NIL 被替换成了 T.nil,而调用 TRANSPLANT 换成了调用 RB-TRANSPLANT ),其执行的条件相同
下面是两个过程之间的其他区别:
- 始终维持结点y为 从树中删除的结点 或者 移至树内的结点。当z的子结点 少于2个时,第1行 将y指向z,并因此要移除。当z有两个子结点时, 第9行 将y指向z的后继,这与 TREE-DELETE 相同, y将移至 树中2的位置
- 由于结点y的颜色 可能改变。变量 y-original-color 存储了发生改变前的 y 颜色。当z有两个子结点时, 则y != z且 结点y移至红黑树中结点z的原始位置; 第20行 给y赋予和z一样的颜色。需要保存y的原始颜色,以在 RB-DELETE 结束时测试它;如果 它是黑色的,那么删除或移动y 会引起红黑性质的破坏
- 保存结点y的踪迹,使它移至结点y的原始位置上。第4、7和11行的赋值语句 令x 或指向y的唯一子结点 或指向哨兵T.nil(如果y没有子结点)
- 因为结点x移动到 结点y的原始位置,属性x.p总是被设置指向 树中y父结点的原始位置,甚至当x是哨兵 T.nil 时也是这样。除非 z是y的原始父结点(该情况 只在z有两个孩子且它的后继y是z的右孩子时发生),否则对x.p的赋值在 RB-TRANSPLANT 的第6行。(注意到, 在第5、 8或14行调用RB-TRANSPLANT时. 传递的 第2个参数与x相同。)然而,当y的原父结点是z时,我们并不想 让x.p指向y的原始父结点。因为 要在树中删除该结点。由于结点y 将在树中向上移动占据z的位置。第13行将 x.p 设置为y,使得x.p指向y父结点的原始位置,甚至当x = T.nil 时也是这样
- 最后 , 如果结点y是黑色 , 就有可能 已经引入了一个或多个红黑性质被破坏的情况, 所以在 第22行调用RB-DELETE-FIXUP来恢复红黑性质。如果y是红色,当y被删除 或 移动时,红黑性质仍然保持。原因如下:
1)树中的黑高没有变化
2)不存在两个相邻的红结点。因为y在树中占据了z的位置,再考虑到z的颜色,树中y的新位置 不可能有两个相邻的红结点。另外,如果y不是z的右孩子, 则y的原右孩子x代替y。如果y是红色,则x一定是黑色。因此用x替代y不可能 使两个红结点相邻
3)如果y是红色 就不可能是根结点,所以根结点仍旧是黑色
如果结点y是黑色的,则会产生三个问题。可以通过 调用 RB-DELETE-FIXUP 进行补救
- 如果y(待删结点)是原来的根结点,而y的一个红色的孩子成为新的根结点, 这就违反了性质2
- 如果x和x.p是红色的,则违反了性质4
- 在树中 移动y将导致 先前包含y的任何简单路径上 黑结点个数少1
因此,y的任何祖先 都不满足性质5。改正这一问题的办法是 将现在占有y原来位置的结点x视为还有一重额外的黑色。也就是说,将任意包含结点x的简单路径上 黑结点个数加1。当将黑结点y删除或移动时,将其黑色“下推” 给结点x
现在问题变为 结点x可能既不是红色,又不是黑色 从而违反了性质1。现在的结点x是双重黑色或者红黑色,这就分别给包含x的简单路径上 黑结点数贡献了2或1。结点x的 color 属性仍然是 RED(如果x是红黑色的)或者 BLACK(如果x是双重黑色的)
结点额外的黑色是 针对x结点的,而不是 反映在它的 color 属性上的
过程RB-DELETE-FIXUP恢复性质1、 性质2和性质4。第1~22行中while循环的目标是 将额外的黑色沿树上移,直到:
1)x指向红黑结点,此时在第23行中,将x着为(单个)黑色
2)x指向根结点,此时可以 简单地“移除”额外的黑色
3)执行适当的旋转和重新着色,退出循环
当在while循环中,x总是指向 一个具有双重黑色的非根结点。(一旦黑红直接着黑色就完事了)要判断x是其父结点x.p的左孩子还是右孩子。保持指针w指向x的兄弟。由于结点x是双重黑色的,故w不可能是T.nil,因为否则,从x.p至(单黑色)叶子w的简单路径上的黑结点个数 就会小于从x.p到x的简单路径上的黑结点数
在具体研究每一种情况之前,先看 如何证实每种情况中的变换 保持性质5 。关键思想是在每种情况中,从子树的根(包括根)到每棵子树 α, β, …, ζ 之间的黑结点个数(包括x的额外黑色)并不被变换改变。因此,如果性质5 在变换之前成立,那么变换之后也仍然成立(给结点x 增加了额外一重黑色)
加黑的结点color属性为 BLACK, 深阴影的结点color属性为RED,浅阴影的结点color属性用c和c’表示
情况1: x的兄弟结点w是红色的
因为w必须有黑色子结点,所以可以改变w和x.p的颜色, 然后对x.p做一次左旋 而不违反红黑树的任何性质。现在,x的新兄弟结点是 旋转之前w的某个子结点,其颜色为黑色。这样,就将情况1 转换为 情况2、3或4处理(当结点心为黑色时,属于情况2、3和4)
情况2: x的兄弟结点w是黑色的, 而且w的两个子结点都是黑色的(del(12))
w的两个子结点都是黑色的,因为w也是黑色的,所以 从x和w上去掉一重黑色,使得x只有一重黑色 而w为红色。为了补偿 从x和w中去掉的一重黑色,在原来是 红色或黑色的x.p上新增一重额外的黑色。通过 将x.p作为新结点x来重复while循环
如果通过 情况1 和 情况2,则新结点x是红黑色的, 因为原来的x.p是红色的。因此,新结点x的color属性值c为RED,并且在测试循环条件后 循环终止,然后,在第23行中 将新结点x着为(单一)黑色
情况3: x的兄弟结点w是黑色的,w的左孩子是红色的,w的右孩子是黑色的
可以交换w 和 其左孩子w.left的颜色,然后对w进行右旋 而不违反红黑树的任何性质。现在x的新兄弟结点w是一个有红色右孩子的黑色结点。这样我们 就将情况3转换成了情况4
情况4: x的兄弟结点w是黑色的 且w的右孩子是红色的
在结点x的兄弟结点w为黑色 且w的右孩子为红色时,通过 进行某些颜色修改(w变成x.p颜色(如果x.p不是黑 右子树就减1黑),x.p变成黑(左子树一定+1,右子树x.p不是黑+1),w.right变成黑(右子树+1),x.p左旋(保证左子树加1黑))并对x.p做次左旋。因为左子树加了1黑,右子树不变,所以 可以去掉x的额外黑色,从而使它变为单重黑色
将x设置为 根后,当while循环 测试其循环条件时,循环终止,结束
3、RB-DELETE的运行时间:因为含n个结点的红黑树的高度为O(lgn),不调用RB-DELETE-FIXUP时 该过程的总时间代价为O(lgn)。在 RB-DELETE-FIXUP 中,情况1、3和4 在各执行常数次数的颜色改变 和 至多3次旋转(1,3,4全中)后便终止, 情况2 是while循环可以重复执行的唯一情况,然后指针x沿树上升至多O(lgn)次,且不执行任何旋转。所以,过程RB-DELETE-FIXUP要花费 O(Ign)时间,做至多3次旋转 ,因此RB-DELETE运行的总时间为O(lgn)
4、将关键字41、38、31、12、19、8连续插入一棵初始的空树中,从而得到一棵红黑树。给出从该树中连续删除关键字8、12、19、31、38、41后的红黑树
5、Skelton和Baron教授担心在 RB-DELETE-FIXUP的情况1开始时,如果这两位教授是对的,则第5~6行就是错的,结点x.p可能不是黑色的
证明 x.p在情况l开始时 必是黑色的,从而说明这两位教授没有担心的必要
在RB-DELETE中y-original-color存储的是x.p的颜色,在第21行中,只有当y-original-color是黑色时才会调用RB-DELETE-FIXUP,所以x.p在情况1开始时必是黑色的,因此这两位教授没有担心的必要
6、假设用 RB-INSERT将一个结点x插人一棵红黑树,紧接着用 RB-DELETE 将它从树中删除。结果的红黑树 与初始的红黑树是否一样?
不一样