来自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}中左子树或右子树。
答案 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;