算法导论13.4 Exercises 答案

13.4-1

首先考虑RB-DELETE的第1-8行代码,这个时候\(y=z\)。令\(p=y.p\),由于删除的节点是\(y\)是一个红色节点,拼接上去的是其唯一一个儿子\(son\)(要么是\(left\),要么是\(right\))的子树,因此\(p\)的右子树和\(son\)相同,黑高保持不变,那么就与左子树的黑高相同。因此,原结论成立。

13.4-2

由于情况1可以转换成情况2,3,4,并且情况3还可以转换成情况4,因此此处证明仅讨论情况2和情况4来解决(同样的,这里只考虑\(x\)是它父亲的左儿子的情况):

  • 情况2不会改变父节点的位置,也不会改变父节点的颜色,因此只要向上迭代,父节点的颜色永远不会发生改变。因此,由于\(T\)在之前根节点就是黑色的,因此在之后仍然是黑色的,原结论成立。

  • 情况4有两种情况需要考虑:如果\(x\)的父节点是根节点,那么经过左旋后,根节点成为\(x\)原来的兄弟节点\(w\),由于\(w\)是黑色节点,因此根节点也将会染成黑色节点,原结论成立;如果\(x\)的父节点不是根节点,那么根节点不会进行任何改变,保持原来的颜色,故原结论成立。

13.4-3

这种情况只会发生在\(z\)具有两个儿子的情况。删除\(y\)前,\(y\)的父节点\(p=x.p\)为红色,且\(x\)也为红色;删除\(y\)后,\(y\)原本的父节点\(p\)成为了\(x\)的父节点,这时RB-DELETE-FIXUP被调用。

根据算法RB-DELETE-FIXUP的第一行,由于\(x\)是红色,因此WHILE循环不被执行,被跳过。在第44行,\(x\)被修改成黑色。由于\(p\)原本是红色节点,因此\(x\)的兄弟节点必定是黑色。这时\(p\)的两个子节点都为黑色,性质4保持成立。

13.4-4

一开始整棵的形状如下:

graph TD
  41((41));38((38));19((19));12((12));
  31((31));8((8));
  A(( ));
  classDef red-node fill:red, color:white;
  classDef black-node fill:black, color:white;
  class 12,31,38,41 black-node;
  class 8,19 red-node;
  38---19;38---41;19---12;19---31;
  12---8;
  12---A;
  style A fill:#f100,stroke-width:0px
  linkStyle 5 stroke:#0ff,stroke-width:0px

如下是按顺序删除每个节点后得到的红黑树:

  • \(8\)
graph TD
  41((41));38((38));19((19));12((12));
  31((31));
  classDef red-node fill:red, color:white;
  classDef black-node fill:black, color:white;
  class 12,31,38,41 black-node;
  class 19 red-node;
  38---19;38---41;19---12;19---31;
  • \(12\)
graph TD
  41((41));38((38));19((19));31((31));
  A(( ));
  classDef red-node fill:red, color:white;
  classDef black-node fill:black, color:white;
  class 31,38,41 black-node;
  class 19 red-node;
  38---19;38---41;19---A;19---31;
  style A fill:#f100,stroke-width:0px
  linkStyle 2 stroke:#0ff,stroke-width:0px
  • \(19\)
graph TD
  41((41));38((38));31((31));
  classDef red-node fill:red, color:white;
  classDef black-node fill:black, color:white;
  class 31,38,41 black-node;
  38---31;38---41;
  • \(38\)
graph TD
  41((41));38((38));
  A(( ));
  classDef red-node fill:red, color:white;
  classDef black-node fill:black, color:white;
  class 31,38 black-node;
  class 41 red-node;
  38---A;38---41;
  style A fill:#f100,stroke-width:0px
  linkStyle 0 stroke:#0ff,stroke-width:0px
  • \(38\)
graph TD
  41((41));
  classDef red-node fill:red, color:white;
  classDef black-node fill:black, color:white;
  class 41 black-node;
  • \(41\)
graph TD
  A[NIL]
  classDef red-node fill:red, color:white;
  classDef black-node fill:black, color:white;
  class A black-node;

13.4-5

前文提到,由于RB-DELETE-FIXUP传入的参数\(x\)可能是\(T.nil\),因此节点\(x\)被访问或修改的代码都符合要求。例如第1行就检查了哨兵的颜色,第2行就检查了当前哨兵节点\(x\)是否为\(x.p\)的左子节点。

当最后一个节点从\(T\)被删除时,\(T\)为空,WHILE循环会被跳过,第44行代码会对哨兵赋予了一次黑色。

13.4-6

以下图示中,\(v/k\)表示从根节点到子树\(\omega\)之间有\(k\)个黑色节点(包括多重的黑色节点)。它们分别代表a,b,c,d这\(4\)种情况。

a

graph TD
    A((A));B((B));C((C));D((D));E((E));
    a[a/3];b[β/3];c[γ/2];d[δ/2];e[ε/2];f[ζ/2]
    Z(( ));Z2(( ));
    classDef hide-appearance fill:transparent, stroke:transparent;
    classDef hide-node display:none;
    classDef red-node fill:red, color:white;
    classDef black-node fill:black, color:white;
    class A,B,C,E black-node;
    class D red-node;
    class a,b,c,d,e,f hide-appearance;
    class Z,Z2 hide-node;
    Z---B;B---A;B---D;
    D---C;D---E;a---Z2;
    A---a;A---b;C---c;C---d;
    E---e;E---f;
    linkStyle 5 stroke:#0ff,stroke-width:0px
graph TD
    A'((A));B'((B));C'((C));D'((D));E'((E));
    a'[α/3];b'[β/3];c'[γ/2];d'[δ/2];e'[ε/2];f'[ζ/2]
    Z'(( ));
    classDef hide-appearance fill:transparent, stroke:transparent;
    classDef hide-node display:none;
    classDef red-node fill:red, color:white;
    classDef black-node fill:black, color:white;
    class A',D',C',E' black-node;
    class B' red-node;
    class a',b',c',d',e',f' hide-appearance;
    class Z' hide-node;
    Z'---D';D'---B';D'---E';B'---A';B'---C';
    A'---a';A'---b';C'---c';C'---d';
    E'---e';E'---f'

b

graph TD
    A((A));B((B));C((C));D((D));E((E));
    a["α/2+count(c)"];b["β/2+count(c)"];c["γ/2+count(c)"];d["δ/2+count(c)"];e["ε/2+count(c)"];f["ζ/2+count(c)"]
    Z(( ));Z2(( ));
    classDef hide-appearance fill:transparent, stroke:transparent, stroke-width:21px;
    classDef hide-node display:none;
    classDef red-node fill:red, color:white;
    classDef black-node fill:black, color:white;
    classDef brown-node fill:#bc987e, color:white;
    class A,D,C,E black-node;
    %% class D red-node;
    class B brown-node;
    class a,b,c,d,e,f hide-appearance;
    class Z,Z2 hide-node;
    Z---B;B---A;B---D;
    D---C;D---E;a---Z2;
    A---a;A---b;C---c;C---d;
    E---e;E---f;
    linkStyle 5 stroke:#0ff,stroke-width:0px
graph TD
    A'((A));B'((B));C'((C));D'((D));E'((E));
    a'["α/1+count(c)"];b'["β/1+count(c)"];c'["γ/1+count(c)"];d'["δ/1+count(c)"];e'["ε/1+count(c)"];f'["ζ/1+count(c)"]
    Z'(( ));Z2'(( ));
    classDef hide-appearance fill:transparent, stroke:transparent, stroke-width:21px;
    classDef hide-node display:none;
    classDef red-node fill:red, color:white;
    classDef black-node fill:black, color:white;
    classDef brown-node fill:#bc987e, color:white;
    class A',C',E' black-node;
    class D' red-node;
    class B' brown-node;
    class a',b',c',d',e',f' hide-appearance;
    class Z',Z2' hide-node;
    Z'---B';B'---A';B'---D';
    D'---C';D'---E';a'---Z2';
    A'---a';A'---b';C'---c';C'---d';
    E'---e';E'---f';
    linkStyle 5 stroke:#0ff,stroke-width:0px

c

graph TD
    A((A));B((B));C((C));D((D));E((E));
    a["α/2+count(c)"];b["β/2+count(c)"];c["γ/1+count(c)"];d["δ/1+count(c)"];e["ε/2+count(c)"];f["ζ/2+count(c)"]
    Z(( ));Z2(( ));
    classDef hide-appearance fill:transparent, stroke:transparent, stroke-width:21px;
    classDef hide-node display:none;
    classDef red-node fill:red, color:white;
    classDef black-node fill:black, color:white;
    classDef brown-node fill:#bc987e, color:white;
    class A,D,E black-node;
    class C,D red-node;
    class B brown-node;
    class a,b,c,d,e,f hide-appearance;
    class Z,Z2 hide-node;
    Z---B;B---A;B---D;
    D---C;D---E;a---Z2;
    A---a;A---b;C---c;C---d;
    E---e;E---f;
    linkStyle 5 stroke:#0ff,stroke-width:0px
graph TD
    A'((A));B'((B));C'((C));D'((D));E'((E));
    a'["α/2+count(c)"];b'["β/2+count(c)"];c'["γ/1+count(c)"];d'["δ/1+count(c)"];e'["ε/2+count(c)"];f'["ζ/2+count(c)"]
    Z'(( ));Z2'(( ));
    classDef hide-appearance fill:transparent, stroke:transparent, stroke-width:21px;
    classDef hide-node display:none;
    classDef red-node fill:red, color:white;
    classDef black-node fill:black, color:white;
    classDef brown-node fill:#bc987e, color:white;
    class A',C',E' black-node;
    class D' red-node;
    class B' brown-node;
    class a',b',c',d',e',f' hide-appearance;
    class Z',Z2' hide-node;
    Z'---B';B'---A';B'---C';
    A'---a';A'---b';C'---c';C'---D';
    D'---d';D'---E';E'---e';E'---f';
    %% linkStyle 17 stroke:#0ff,stroke-width:0px

d

graph TD
    A((A));B((B));C((C));D((D));E((E));
a["α/2+count(c)"];b["β/2+count(c)"];c["γ/1+count(c)+count(c')"];d["δ/1+count(c)+count(c')"];e["ε/2+count(c)"];f["ζ/2+count(c)"]
    Z(( ));Z2(( ));
    classDef hide-appearance fill:transparent, stroke:transparent;
    classDef hide-node display:none;
    classDef red-node fill:red, color:white;
    classDef black-node fill:black, color:white;
    classDef brown-node fill:#bc987e, color:white;
    class A,D black-node;
    class E red-node;
    class B,C brown-node;
    class a,b,c,d,e,f hide-appearance;
    class Z,Z2 hide-node;
    Z---B;B---A;B---D;
    D---C;D---E;a---Z2;
    A---a;A---b;C---c;C---d;
    E---e;E---f;
    linkStyle 5 stroke:#0ff,stroke-width:0px
graph TD
    A'((A));B'((B));C'((C));D'((D));E'((E));
a'["α/2+count(c)"];b'["β/2+count(c)"];c'["γ/1+count(c)+count(c')"];d'["δ/1+count(c)+count(c')"];e'["ε/2+count(c)"];f'["ζ/2+count(c)"]
    Z'(( ));
    classDef hide-appearance fill:transparent, stroke:transparent;
    classDef hide-node display:none;
    classDef red-node fill:red, color:white;
    classDef black-node fill:black, color:white;
    classDef brown-node fill:#bc987e, color:white;
    class A',B',E' black-node;
    %% class B' red-node;
    class a',b',c',d',e',f' hide-appearance;
    class Z' hide-node;
    class C',D' brown-node;
    Z'---D';D'---B';D'---E';B'---A';B'---C';
    A'---a';A'---b';C'---c';C'---d';
    E'---e';E'---f'

13.4-7

考虑使用反证法来证明。可见,\(w\)节点是\(x\)的兄弟叶节点,也就是说,\(w.p=x.p\)。如果进入了情况1,那么意味着节点\(w\)是红色。如果\(x.p\)也是红色,那么这将违反红黑树的性质4。这说明,节点\(w\)\(w.p\)的颜色在调用RB-DELETE-FIXUP之前都是红色,这说明\(T\)此前就不是一棵红黑树,这是不可能的。因此不需要担心\(x.p\)是红色,因为\(T\)此前是一棵红黑树。

13.4-8

不是。考虑如下\(4\)步操作。一开始\(T\)是一棵空树。

  1. 插入\(1\)
graph TD
  1((1))
  classDef red-node fill:red, color:white;
  classDef black-node fill:black, color:white;
  class 1 black-node;
  1. 插入\(2\)
graph TD

  1((1));2((2));
  A(( ));
  classDef red-node fill:red, color:white;
  classDef black-node fill:black, color:white;
  class 1 black-node;
  class 2 red-node;
  1---A;1---2;
  style A fill:#f100,stroke-width:0px
  linkStyle 0 stroke:#0ff,stroke-width:0px
  1. 插入\(3\)
graph TD

  1((1));2((2));3((3));
  classDef red-node fill:red, color:white;
  classDef black-node fill:black, color:white;
  class 1,3 black-node;
  class 2 red-node;
  2---1;2---3;
  1. 删除\(3\)
graph TD
  1((1));2((2));
  A(( ));
  classDef red-node fill:red, color:white;
  classDef black-node fill:black, color:white;
  class 2 black-node;
  class 1 red-node;
  2---1;2---A;
  style A fill:#f100,stroke-width:0px
  linkStyle 1 stroke:#0ff,stroke-width:0px

由此可见,添加一个节点后再立刻删除它,并不能使红黑树恢复成原样。

\(\star\) 13.4-9

代入题目12.2-8的结论,\(h=\Theta(\lg n)\),那么可以知道依次遍历\(x\)的第\(1,2,\dots,m\)个后继,可以以\(\Theta(m+\lg n)\)的时间求解出来。

一个简单的做法是找到第一个大于等于\(a\)的节点,不断地求后继并输出,直到这个后继为T.nil或者是超出了所求范围。我们将设计RB-LOWER-BOUND算法用于求解最小的大于等于某个值的节点,以及BR-SUCCESSOR算法求解某个节点的后继的后继,其中使用到的子程序RB-MINIUM用于求解子树\(r\)中的最小节点。

由于现在是求取子树\(r\)中,满足\(a\le k\le b\)的所有节点,但是\([a,b]\)所覆盖的范围有可能覆盖了子树\(T\)以外的节点。因此在这一步中,我们需要将右边界\(b\)缩小,以保证最大值恰好在子树\(r\)内,在红黑树上求解子树\(r\)的最大节点由RB-MAXIMUM给出,它和TREE-MAXIMUM非常类似:

这些程序的代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
RB-MINIMUM(T, x)
while x.left ≠ T.nil
x = x.left
return x

RB-MAXIMUM(T, x)
while x.right ≠ T.nil
x = x.right
return x

BR-SUCCESSOR(T, x)
if x.right ≠ T.nil
return BR-MINIMUM(T, x.right)
else
y = x.p
while y ≠ T.nil and x == y.right
x = y
y = y.p
return y

// 求解以x为子树中,第一个大于等于a的节点。
BR-LOWER-BOUND(T, x, a)
result = T.nil
while x != T.nil
if x.key >= a
result = x
x = x.left
else
x = x.right
return result

那么接下来则是对RB-ENUMERATE的实现,这种实现考虑了\(a,b\)不在树\(T\)中的情形。

1
2
3
4
5
6
7
RB-ENUMERATE(T, r, a, b)
t = RB-MAXIMUM(T, r)
b = min{b, r.key}
x = BR-LOWER-BOUND(T, x, a)
while x != T.nil and x.key <= b
print x.key
x = BR-SUCCESSOR(T, x)

由于对RB-MAXIMUMBR-LOWER-BOUND的调用仅有\(1\)次,再加上迭代求解\(x\)的后继(假设有\(m\)个),因此整个算法的时间复杂度为\(\Theta(m+\lg n)\)