算法导论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)\)。