我正在阅读书籍Data Structures and Algorithms: Annotated Reference with Examples
中使用的二叉树删除节点算法 第34页的,案例4(删除具有左右子树的节点),按照本书中描述的算法看起来不起作用,可能我可能错了,有人可以帮助我,我错过了什么。
//Case 4
get largestValue from nodeToRemove.Left
FindParent(largestValue).Right <- 0
nodeToRemove.Value<-largestValue.Value
以下行如何从子树FindParent(largestValue).Right <- 0
答案 0 :(得分:6)
删除具有两个子节点的节点时,可以选择其有序后继节点或其有序前驱节点。在这种情况下,它找到左子树中的最大值(意味着其左子树的最右边的子节点),这意味着它正在查找节点的有序前导节点。
找到替换节点后,实际上删除要删除的节点。而是从后继节点获取值并将该值存储在要删除的节点中。然后,删除后继节点。这样做可以保留二进制搜索树属性,因为您可以确定所选节点的值低于原始节点左侧子树中所有子节点的值,并且大于值原始节点的右子树中的所有子节点。
修改强>
在仔细阅读了你的问题之后,我想我已经找到了问题。
除delete
函数外,您通常拥有的replace
函数可替换相关节点。我想你需要改变这行代码:
FindParent(largestValue).Right <- 0
为:
FindParent(largestValue).Right <- largestValue.Left
如果largestValue
节点没有左孩子,则只需获得null
或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
。您尝试从largestValue
中找到nodeToRemove.Left
。这为您提供7
,因为左子树只有一个孩子。
然后你做:
nodeToRemove.Value <- largestValue.Value
这意味着:
8.value <- 7.Value
或
8.Value <- 7
所以现在你的树看起来像这样:
7
/ \
7 9
您需要摆脱替换节点,因此您将largestValue
替换为largestValue.Left
(null
)。首先,你要找出什么样的孩子7
:
if largestValue = largestValue.Parent.Left then
这意味着:
if 7 = 7.Parent.Left then
或:
if 7 = 8.Left then
由于7
是8
的左侧孩子,因此需要将8.Left
替换为7.Right
(largestValue.Parent.Left <- largestValue.Left
)。由于7
没有子项,7.Left
为空。因此largestValue.Parent.Left
被赋值为null(这有效地删除了它的左子)。所以这意味着你最终得到了以下树:
7
\
9
答案 1 :(得分:1)
想法是简单地从左侧的最大节点获取值并将其移动到正被删除的节点,即,根本不删除该节点,只需替换它的内容。然后,使用移动到“已删除”节点的值删除节点。这维护了树的排序,每个节点的值都大于它的所有左边的子节点,并且小于它所有正确的子节点。
答案 2 :(得分:1)
如果我理解伪代码,它在一般情况下工作,但在“左子树中的一个节点”情况下失败。好的捕获。
它有效地将node_to_remove替换为其左子树中的maximum_value(也使旧的largest_value节点为空)。
请注意,在BST中,node_to_remove的左子树将全部小于node_to_remove。 node_to_remove的右子树将全部大于node_to_remove。因此,如果您使用左子树中的最大节点,它将保留不变量。
如果这是“子树案例中的一个节点”,它将破坏正确的子树。拉美:(
正如Vivin所指出的那样,它也无法重新连接maximumNode的左子女。
答案 3 :(得分:1)
我认为您可能需要澄清什么不起作用。
我会尝试在二叉树中解释删除的概念,以防万一。
让我们假设您在树中有一个节点,它有两个您想要删除的子节点。
在下面的树中,假设您要删除节点b
一个
/ \
b c
/ \ / \
d e f g
当我们删除一个节点时,我们需要重新附加其依赖节点。
即。当我们删除b时,我们需要重新连接节点d和e。
我们知道左侧节点的值小于右侧节点,父节点的值在左侧和右侧节点之间。在这种情况下,d&lt; b和b&lt;即这是二叉树定义的一部分。
稍微不那么明显的是e&lt;一个。所以这意味着我们可以用e替换b。现在我们重新连接了我们需要重新连接d。
如前所述d&lt; e所以我们可以将e作为e的左边节点。
删除现已完成。
(顺便说一下,以这种方式向上移动节点并重新排列从属节点的过程称为提升节点。您还可以在不删除其他节点的情况下提升节点。)
一个
/ \
d c
\ / \
e f g
请注意,删除节点b还有另一个完全合理的结果。 如果我们选择提升节点d而不是节点e,那么树就会像这样。
一个
/ \
e c
/ / \
d f g
答案 4 :(得分:0)
当您查看该算法的Wikipedia's时,可能会更有意义:
删除包含两个孩子的节点: 将节点调用为“N”。做 不要删除N.而是选择其中之一 它的有序后继节点或其 有序前驱节点“R”。 用值替换N的值 然后删除R.(注意:R本身 最多只有一个孩子。)
请注意,给定的算法选择有序的前任节点。
编辑:似乎缺少R(使用维基百科的术语)有一个孩子的可能性。递归删除可能会更好。