假设为每个结点换一种设计,属性x.p指向x的双亲,属性x.succ指向x的后继。试给出使用这种表示法的二叉搜索树T上SEARCH、INSERT和DELETE操作的伪代码。这些伪代码应在O(h)时间内执行完,其中h为树T的高度。(提示:应该设计一个返回某个结点的双亲的子过程。)
日常吐槽算法导论中文版的翻译,先看一下英文原题是怎么写的:
Suppose that instead of each node x keeping the attribute x.p, pointing to x's parent, it keeps x.succ, pointing to x's successor. Give pseudocode for SEARCH, INSERT, and DELETE on a binary search tree T using this representation. These procedures should operate in time O(h), where h is the height of the tree T. (Hint: You may wish to implement a subroutine that returns the parent of a node)
上文加粗部分明显属于应被替换掉的,即用x.succ替换x.p。另外本章这个parent翻译成双亲属实拉跨(因为只有一个),直接译成父结点不好吗(怕被打拳你译成母结点也行啊)
然后是解答部分,这里因为SEARCH过程中并未用到有关于父结点的信息,因此照搬书中12.2的
TREE-SEARCH(x, k)
1 if x == NIL or k == x.key
2 return x
3 if k < x.key
4 return TREE-SEARCH(x.left, k)
5 else return TREE-SEARCH(x.right, k)
INSERT过程中关于后继(x.succ)属性的维护基本等同于一个单向链表,不难作出这样的一个推断:插入点z是在插入过程中,所有与点z进行了比较的结点中,键值(key)小于z.key中最大者的后继,下面的INSERT基于这一推断
TREE-INSERT(T, z)
1 y = NIL
2 x = T.root
3 min = NIL // 用于记录与结点z进行比较的结点中键值小于z的最大值
4 while x != NIL
5 y = x
6 if z.key < x.key
7 x = x.left
8 else
9 if min == NIL or min.key < x.key
10 min = x.key
11 x = x.right
12 if min == NIL // 说明z是树中新的最小值
13 z.succ = y
14 else
15 z.succ = min.succ
16 min.succ = z
17 if y == NIL
18 T.root = z
19 else if z.key < y.key
20 y.left = z
21 else
22 y.right = z
最后的DELETE过程根据题目中的提示,分别写出结点的父结点和前驱结点查找过程,思路是以树的根结点作为起点在树中查找对应键值,同时记录上一个比较的结点以及所有比较结点中小于查找结点的最大值。最终找到查找结点的父结点和前驱结点(类似于单向链表的prev查找)。过程如下
PARENT-SEARCH(T, z)
1 y = NIL
2 x = T.root
3 while x != NIL and z.key != x.key
4 y = x
5 if z.key < x.key
6 x = x.left
7 else
8 x = x.right
9 return y
PREDESESSOR-SEARCH(T, z)
1 y = NIL
2 x = T.root
3 while x != NIL
4 if x.key < z.key
5 x = x.right
6 else if x.key > z.key
7 if y == NIL or y.key < x.key
8 y = x
8 x = x.left
9 else
10 break
11 return y
然后就是根据书中的TRANSPLANT和TREE-DELETE进行操作,略过其中对父结点属性赋值,同时按单向链表结点的删除操作对succ属性进行维护