(如有错漏,欢迎指正)
算法导论,第三版(Introduction to Algorithms, CLRS, 3rd edition)第十二章二叉搜索树(binary search tree)课后有这么一道练习题:
Exercises 12.3-5
Suppose that instead of each node x keeping the attribute x.p, pointing to x's parent, it keep x.succ, pointing tox's successor. Give pseudocode for SEARCH, INSERT, and DELETE on a binary search tree T using this representation. These procedures should operate in timeO(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中保存的指向其父节点的指针(x.p)替换为指向其后继节点的指针(x.succ),设计复杂度仍为O(h)(h为树的高度,即为O(lg n))的搜索节点,插入节点,删除节点算法。题目中的提示是设计一个能返回父节点的子程序。
x's successor: the node with the smallest key greater than x.key.
后继节点定义:在节点值比x大的节点中,节点值最小的节点为x的后继节点。
那么后继节点会在什么位置呢?先来看看在正常二叉搜索树中找到x的后继节点的伪代码
TREE-SUCCESSOR(x)
if x.right ≠ NIL
return TREE-MINIMUN(x.right)
y = x.p
while y ≠ NIL and x == y.right
x = y
y = y.p
return y
这个程序可以分为两部分:
(1)当x的右子树存在时,x的后继是x的右子树中最小的节点(也就是最左的节点)
(2)当x的右子树不存在时,如果x的后继节点存在,则x的后继是x的祖先节点中第一个左孩子也为x的祖先的节点(如下图)
有了上面的基础,接着思考一下题目的提示:找到一个在后继节点表示的二叉搜索树中找到父节点的方法,注意到SEARCH, INSERT, DELETE中都只调用了常数次的父指针,在后继表示的二叉搜索树中,如果能找到在O(h)时间内找到父节点,就能在O(h)时间内实现上述三种操作。
下面分析如何在O(h)时间内找到父节点(假设无相等的节点):
Case1:x是其父的左孩子且x无右子树
在x.p的左子树中x最大,因此x.p = x.succ,T = O(1)
Case2:x是其父的左孩子且x有右子树
在x.p的左子树中,x右子树的最大值最大,因此,令y = TREE-MAXIMUM(x.right),
x.p = y.succ, T = O(lg n)
以下两种情况较为复杂,因为x.p并不是x及其子树的后继节点
Case3:x是其父的右孩子且x无右子树
令z = x.succ,若z = NIL,证明在x的祖先节点中没有左孩子也为x的祖先的节点,若z ≠ NIL,证明z是第一个左孩子也为x的祖先的节点。因此,下述伪代码可以返回x.p
if z == NIL
m = T.root
else
m = z.left
while m ≠ x
p = m
m = m.right
return p
搜寻次数最多为树的高度,T = O(lg n)
Case4:x是其父的右孩子且x有右子树
方法同Case3,区别为令y = MAXIMUM(x.right),z = y.succ
综上所述,在O(lg n)时间内找到父节点的子程序为:
PARENT(z)
if z == NIL
m = T.root
else
m = z.left
while m ≠ x
p = m
m = m.right
return p
FIND-PARENT(T, x)
if x.right = NIL
if x == x.succ.left
x.p = x.succ
else
z = x.succ
x.p=PARENT(z)
else
y = TREE-MAXIMUM(x.right)
if x == y.succ.left
x.p = y.succ
else
z = y.succ
x.p=PARENT(z)