Algorithm 如何查找节点';s BST中的前置节点,每个节点有3个属性——成功、左和右?

Algorithm 如何查找节点';s BST中的前置节点,每个节点有3个属性——成功、左和右?,algorithm,clrs,Algorithm,Clrs,来自CLRS的问题,第3版 12.3-5 假设不是每个节点x都保持属性x.p,指向x的父节点,而是保持x.succ,指向x的后续节点。给出使用此表示法在二进制搜索树T上搜索、插入和删除的伪代码。这些程序应在时间O(h)内运行,其中h是树的高度T。(提示:您可能希望实现一个返回节点父节点的子例程。) 我知道如何实现在O(h)时间内返回节点父节点的子例程 要查找节点x的父节点,我们应该首先在x根目录树中找到最大键M。然后,我们从M.succ.left向下到右侧。当我们到达x时,我们在x之前遇到的节点

来自CLRS的问题,第3版

12.3-5
假设不是每个节点x都保持属性x.p,指向x的父节点,而是保持x.succ,指向x的后续节点。给出使用此表示法在二进制搜索树T上搜索、插入和删除的伪代码。这些程序应在时间O(h)内运行,其中h是树的高度T。(提示:您可能希望实现一个返回节点父节点的子例程。)

我知道如何实现在O(h)时间内返回节点父节点的子例程

要查找节点
x
的父节点,我们应该首先在
x
根目录树中找到最大键
M
。然后,我们从
M.succ.left
向下到右侧。当我们到达
x
时,我们在
x
之前遇到的节点是
x
的父节点

请参阅代码

typedef结构树{
结构树*左、右、成功;
}*英国理工学院;
家长(英国夏令时x)
{
如果(x==root)返回NULL;
BST max=树的最大值(x);
BST parent=max->succ,t;
如果(父项)t=parent->left;
否则t=根;
而(t!=x){parent=t;t=t->right;}
返回父母;
}
DELETE
x
时,应将
x
的前身的succ修改为指向
x.succ
,而不再是
x
。现在问题来了——如何在O(h)时间内找到
x
的前身

x
的左子树为非空时,它是
x
的左子树中最右边的节点。但是,当
x
的左子树为空时,前一子树是
x
的祖先,以查找调用
PARENT
的O(h)次。这需要O(h*h)时间吗?还是应该从根本上向下搜索

请注意,操作
INSERT
,还需要查找节点的前置节点


有一个问题——如果所有键共享相同的值怎么办?然后,我们无法通过比较找到
x
,因为键
a
,它等于键
x
,可能出现在
x
的左子树或右子树中。

如果
x
的左子树为空,
x
的前子树不仅仅是它的祖先,它是
x
的直接父级。因此,您只需要对
parent
子例程进行一次调用,就可以使整个运行时
O(h)+O(h)=O(h)

p.S

即使它不是直接的父节点,您也不必重复调用
parent
来获取所有的祖先节点,您可以像通常一样从树的根开始搜索
x
,在一次长度
O(h)
的遍历中保存路径上的所有节点。搜索
x
时通过的所有节点,并且根据定义,只有这些节点是
x

的祖先。如果键是离散的,则x的前身的键是注意:只有根直接可用时才可能是O(h)。(应该是这种情况)

稍微改变一下你的数据结构

typedef struct TREE{
 int key;
 struct TREE* left,right,succ;
}*BST;
如果左子树非空,我们使用tree_max,否则我们在BST中搜索x,从根开始搜索,并维护(在变量目标中)右子树遇到的最后一个(即最新的)节点。在搜索结束时,目标是前置项

前置函数

 BST PRE(BST x)
    {
      if(x->left!=NULL)
          return TREE_MAXIMUM(x->left);
      if(x== root)
          return NULL;
      BST goal = NULL, y = root;
      while(1)
      {
        if(x->key <= y->key)
            y=y->left;
        else
        {
            goal=y;
            y=y->right;
        }
        if(x==y)
        {
            return goal;
        }
      }      
    }
BST前(BST x)
{
如果(x->左!=NULL)
返回树_最大值(x->左);
if(x==根)
返回NULL;
BST目标=空,y=根;
而(1)
{
如果(x->键)
y=y->左;
其他的
{
目标=y;
y=y->右;
}
如果(x==y)
{
回归目标;
}
}      
}

为什么需要调用
子例程O(h)次?为什么一次还不够?