来自 CLRS 的问题,3ed。
12.3-5
假设不是每个节点 x 保持属性 xp 指向 x 的父节点,而是保持 x.succ 指向 x 的后继节点。使用这种表示法在二叉搜索树 T 上给出 SEARCH、INSERT 和 DELETE 的伪代码。这些过程应该在 O(h) 时间内运行,其中 h 是树 T 的高度。(提示:您可能希望实现一个返回节点父节点的子程序。)
我知道如何实现一个在 O(h) 时间内返回节点父节点的子程序。
要找到节点的父节点,我们应该首先找到以子树为根x
的最大键。然后,我们从 向右下。当我们到达时,我们之前遇到的节点是的父节点。M
x
M.succ.left
x
x
x
查看代码。
typedef struct TREE{
struct TREE* left,right,succ;
}*BST;
PARENT(BST x)
{
if(x==root) return NULL;
BST max=TREE_MAXIMUM(x);
BST parent=max->succ,t;
if(parent) t=parent->left;
else t=root;
while(t!=x){parent=t;t=t->right;}
return parent;
}
时DELETE
x
,x
前身的succ应该被修改为指向x.succ
,不再x
。那么问题来了——如何x
在 O(h) 时间内找到 的前身?
当 的左子树不x
为空时,它是 的左子树中最右边的节点x
。但是,当 的左子树x
为空时,前驱是x
的祖先,要找到调用 的 O(h) 次PARENT
。这需要 O(h*h) 时间吗?还是应该从根向下搜索?
请注意,操作INSERT
, 还需要找到一个节点的前任。
有一个问题——如果所有的键共享相同的值怎么办?那么我们不能通过x
比较找到 ' 的前任,因为与 keya
相等的 keyx
可能出现在x
' 的左子树或右子树中。