删除具有两个子节点的节点时,您可以选择其有序后继节点或有序前驱节点。在这种情况下,它在左子树(即其左子树的最右子树)中查找最大值,这意味着它正在查找该节点的有序前驱节点。
一旦找到替换节点,您实际上并没有删除要删除的节点。相反,您从后继节点获取值并将该值存储在要删除的节点中。然后,您删除后继节点。这样做可以保留二叉搜索树属性,因为您可以确定您选择的节点的值将低于原始节点左子树中所有子节点的值,并且大于该值原始节点的右子树中的所有子节点。
编辑
在阅读了你的问题之后,我想我已经找到了问题。
通常,除了 delete 函数之外,您还有一个 replace 函数来替换相关节点。我认为您需要更改这行代码:
FindParent(largestValue).Right <- 0
到:
FindParent(largestValue).Right <- largestValue.Left
如果largestValue 节点没有左子节点,您只需获得null 或0。如果它确实有一个左孩子,则该孩子将成为largestValue 节点的替代品。所以你是对的;代码没有考虑largestValue节点可能有左孩子的场景。
另一个编辑
由于您只发布了一个 sn-p,我不确定代码的上下文是什么。但是发布的 sn-p 似乎确实存在您建议的问题(替换错误的节点)。通常情况下,有三种情况,但我注意到您的 sn-p 中的评论说//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。您尝试从nodeToRemove.Left 中找到largestValue。这给了你7,因为左子树只有一个孩子。
然后你做:
nodeToRemove.Value <- largestValue.Value
这意味着:
8.value <- 7.Value
或
8.Value <- 7
所以现在你的树看起来像这样:
7
/ \
7 9
您需要摆脱替换节点,因此您将用largestValue.Left(即null)替换largestValue。所以首先你要知道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