如何在BST中找到节点的前任,每个节点有3个属性 - succ,left和right?

时间:2015-08-29 07:02:58

标签: algorithm clrs

来自CLRS的问题,3ed。

  

12.3-5
  假设不是每个节点x保持属性x.p,指向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。所以现在问题出现了 - 如何在O(h)时间内找到x的前身?

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

请注意,操作INSERT也需要找到节点的前身。

有一个问题 - 如果所有密钥共享相同的值会怎么样?然后,我们无法使用比较找到x的前任,因为密钥a(等于密钥x)可能出现在x&#39}中左子树或右子树。

3 个答案:

答案 0 :(得分:1)

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

<强> P.S
即使它不是直接父级,您仍然不必反复调用parent来获取所有祖先,您可以像通常那样从树的根开始搜索x ,将x路径中的所有节点保存在O(h)的单个遍历中。在搜索x时您通过的所有节点,以及只有那些节点,根据定义是x的祖先。

答案 1 :(得分:0)

如果密钥是离散的,x的前身有&lt; = key(x)-1对的密钥?如果你在树上搜索键(x)-1会发生什么?在遍历过程中,你不一定遇到pred(x)吗?你必须仔细检查我。当然,非离散键(例如浮点)可能有问题(至于-1)。

答案 2 :(得分:0)

注意:O(h)只有在直接可用的情况下才有可能。(应该是这种情况)

稍微更改一下数据结构。

protected void GrdV_Projects_RowDataBound(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        DataRow row = ((DataRowView)e.Row.DataItem).Row;
        DateTime renewalDate = row.Field<DateTime>("RenewalDate");
        if (renewalDate.Date > DateTime.Today)
            e.Row.Cells[7].BackColor = System.Drawing.Color.Red;
        else
            e.Row.Cells[7].BackColor = System.Drawing.Color.Green;
    }
}

如果左子树是非空的,我们使用TREE_MAXIMUM,否则我们在BST中搜索从根开始搜索的x并维护(在可变目标中)与右子节点遇到的最后一个(即最新的)节点。在搜索结束时,目标是前任。

前身功能

typedef struct TREE{
 int key;
 struct TREE* left,right,succ;
}*BST;