0

我正在阅读《数据结构和算法:带示例的注释参考》一书中使用的二叉树删除节点算法

在第 34 页,案例 4(删除具有左右子树的节点),书中描述的以下算法看起来不起作用,可能我可能错了,有人可以帮我解决我所缺少的。

//Case 4
get largestValue from nodeToRemove.Left
FindParent(largestValue).Right <- 0
nodeToRemove.Value<-largestValue.Value

以下行如何从子树中删除最大值FindParent(largestValue).Right <- 0

4

5 回答 5

7

删除具有两个子节点的节点时,可以选择其有序后继节点或有序前驱节点。在这种情况下,它在左子树(即其左子树的最右边的孩子)中查找最大值,这意味着它正在查找节点的有序前驱节点。

找到替换节点后,实际上并没有删除要删除的节点。相反,您从后继节点获取值并将该值存储在要删除的节点中。然后,您删除后继节点。这样做可以保留二叉搜索树属性,因为您可以确定您选择的节点的值将低于原始节点左子树中所有子节点的值,并且大于该值原始节点的右子树中的所有子节点。

编辑

在阅读了你的问题之后,我想我已经找到了问题所在。

通常,除了delete函数之外,您还有一个replace替换相关节点的函数。我认为您需要更改这行代码:

FindParent(largestValue).Right <- 0

至:

FindParent(largestValue).Right <- largestValue.Left

如果largestValue节点没有左孩子,你只需得到nullor 0。如果它确实有一个左孩子,则该孩子将成为该largestValue节点的替代品。所以你是对的;该代码没有考虑largestValue节点可能有左孩子的情况。

另一个编辑

由于您只发布了一个片段,我不确定代码的上下文是什么。但是发布的片段似乎确实存在您建议的问题(替换错误的节点)。通常,有三种情况,但我注意到您的代码段中的评论说//Case 4(所以也许还有其他上下文)。

早些时候,我提到了delete通常带有replace. 因此,如果找到该largestValue节点,则根据两种简单情况(没有孩子的节点和有一个孩子的节点)将其删除。因此,如果您正在查看用于删除具有两个孩子的节点的伪代码,您将执行以下操作:

get largestValue from nodeToRemove.Left
nodeToRemove.Value <- largestValue.Value

//now replace largestValue with largestValue.Left    

if largestValue = largestValue.Parent.Left then   
   largestValue.Parent.Left <- largestValue.Left //is largestValue a left child?
else //largestValue must be a right child
   largestValue.Parent.Right <- largestValue.Left

if largestValue.Left is not null then
   largestValue.Left.Parent <- largestValue.Parent

我觉得很奇怪,一本数据结构和算法的书会遗漏这部分,所以我倾向于认为这本书进一步将删除分成几个案例(因为有三个标准案例),以便更容易理解。

为了证明上述代码有效,请考虑以下树:

  8
 / \
7   9

假设您要删除8. 您尝试largestValuenodeToRemove.Left. 这给了你,7因为左子树只有一个孩子。

然后你做:

nodeToRemove.Value <- largestValue.Value

意思是:

8.value <- 7.Value

或者

8.Value <- 7

所以现在你的树看起来像这样:

  7
 / \
7   9

您需要摆脱替换节点,因此您将替换largestValuelargestValue.Left(即null)。所以首先你要知道什么样的孩子7是:

if largestValue = largestValue.Parent.Left then

意思是:

if 7 = 7.Parent.Left then

或者:

if 7 = 8.Left then

由于78的左孩子,需要8.Left7.Right( largestValue.Parent.Left <- largestValue.Left) 代替。由于7没有孩子,7.Left因此为空。所以largestValue.Parent.Left被分配给 null (这有效地删除了它的左孩子)。所以这意味着你最终得到以下树:

  7
   \
    9
于 2010-06-29T20:17:32.040 回答
1

我认为您可能需要澄清什么不起作用。

如果有帮助,我将尝试解释二叉树中删除的概念。

让我们假设您在树中有一个节点,该节点有两个要删除的子节点。在下面的树中,假设您要删除节点 b
           a
         / \
       b c
     / \ / \
   d e f g

当我们删除一个节点时,我们需要重新附加它的依赖节点。

IE。当我们删除 b 时,我们需要重新连接节点 d 和 e。

我们知道左节点的值小于右节点的值,父节点的值在左右节点之间。在这种情况下,d < b 和 b < e。这是二叉树定义的一部分。

稍微不那么明显的是e < a。所以这意味着我们可以用 e 代替 b。现在我们已经重新附加了 e,我们需要重新附加 d。

如前所述 d < e 所以我们可以将 e 作为 e 的左节点。

删除现已完成。

(顺便说一句,将节点向上移动并以这种方式重新排列依赖节点的过程称为提升节点。您也可以提升节点而不删除其他节点。)


           a
         / \
       d c
         \ / \
          e f g

请注意,删除节点 b 还有另一个完全合法的结果。如果我们选择提升节点 d 而不是节点 e,那么树将如下所示。


           a
         / \
       e c
     / / \
   d f g

于 2010-06-29T20:44:47.727 回答
1

这个想法是简单地从左侧最大节点中获取值并将其移动到正在删除的节点,即根本不删除节点,只需替换它的内容。然后用移入“已删除”节点的值修剪掉节点。这保持了树的顺序,每个节点的值都大于它的所有左子节点并且小于它所有的右子节点。

于 2010-06-29T20:16:26.263 回答
1

如果我理解伪代码,它在一般情况下有效,但在“左子树中的一个节点”情况下失败。不错的收获。

它有效地将 node_to_remove 替换为它的左子树中的最大值(也使旧的最大值节点为空)。

请注意,在 BST 中,node_to_remove 的左子树都将小于 node_to_remove。node_to_remove 的右子树都将大于 node_to_remove。因此,如果您取左子树中的最大节点,它将保留不变量。

如果这是“子树中的一个节点”,它将破坏右子树。跛脚:(

正如 Vivin 指出的那样,它也无法重新附加最大节点的左子节点。

于 2010-06-29T20:16:40.933 回答
0

It may make more sense when you look at the Wikipedia's take on that part of the algorithm:

Deleting a node with two children: Call the node to be deleted "N". Do not delete N. Instead, choose either its in-order successor node or its in-order predecessor node, "R". Replace the value of N with the value of R, then delete R. (Note: R itself has up to one child.)

Note that the given algorithm chooses the in-order predecessor node.

Edit: what appears to be missing the possibility that R (to use Wikipedia's terminology) has one child. A recursive delete might work better.

于 2010-06-29T20:20:48.957 回答