算法导论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$
graph TD 1((1)) classDef red-node fill:red, color:white; classDef black-node fill:black, color:white; class 1 black-node;
- 插入$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
- 插入$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;
- 删除$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 | RB-MINIMUM(T, x) |
那么接下来则是对RB-ENUMERATE的实现,这种实现考虑了$a,b$不在树$T$中的情形。
1 | RB-ENUMERATE(T, r, a, b) |
由于对RB-MAXIMUM和BR-LOWER-BOUND的调用仅有$1$次,再加上迭代求解$x$的后继(假设有$m$个),因此整个算法的时间复杂度为$\Theta(m+\lg n)$。