【问题标题】:BST delete doesn't actually deleteBST 删除实际上并没有删除
【发布时间】:2015-10-29 06:23:04
【问题描述】:

我有这个删除函数,它将 BST 中的一个节点作为参数来删除它。它在做任何事情之前检查三种情况:

  • 如果节点没有子节点 - 则删除节点
  • 如果节点有一个子节点 - 只需将节点替换为子节点
  • 如果节点有两个子节点 - 在右树中找到最小的节点,将其替换为我们要删除的节点(实质上是删除它),然后删除我们找到的最小节点

前两种情况有效,但最后一种情况有两个孩子。只是这种情况的代码如下

AccNode* smallest = pointer->getRight();
bool foundSmallest = false;

// Find the smallest node in the right branch
while (foundSmallest == false) {
    if (smallest->getLeft() == NULL && smallest->getRight() == NULL) {
        foundSmallest = true;
    } else {
        if (smallest->getLeft() == NULL && smallest->getRight() != NULL) {
            smallest = smallest->getRight();
        } else {
            smallest = smallest->getLeft();
        }
    }
}

// Replace the node we want to delete with the data in
// smallest node we found and delete smallest node in that tree
pointer->setData(smallest->getData());
delete smallest;

为了进一步调试,Xcode 有一些非常酷的工具可以让你可视化树,我发现了一些非常有趣的东西。在我们到达delete smallest; 行之前,树中最小的节点如下所示

我们删除后的行是这样的

那么这里发生了什么?我使用指针的方式是否搞砸了?


编辑:我刚刚意识到一些事情。在树后删除的图像中,在折叠部分中,右侧节点不再为 NULL 并且现在具有价值。我打开它,它是我们想要从头开始删除的节点的整个右分支(所以是原始指针的右分支)。所以我觉得我现在肯定对指针做错了。有什么线索吗?

【问题讨论】:

  • 所以问题是某些数据在您删除后发生了变化?没关系。即使你不再拥有它,你也不能指望它是一样的。
  • 在关于它的答案已经发布后回滚更改代码的编辑。 @SteppingHat:请不要在发布答案后更改问题的任何方面。您可以修改问题,但不要更改

标签: c++ binary-search-tree


【解决方案1】:

您忘记将指向最小的指针(在最小的父节点中)归零。这使得该指针成为一个悬空指针,一个指向已释放内存的指针。调试器显示此类内存的任意内容,在许多情况下就是之前的内容。

您需要跟踪父节点。

此外,当前逻辑在不能向左分支时向右分支,而向右分支是可能的。但这会导致一个具有更大值的节点,包括它的所有子节点。所以这个逻辑不好。

复制数据、删除父节点中的指针并将指针置空的另一种方法是重新排列树(将最小的节点移动到您真正想要删除的节点的位置),然后删除您真正想要的节点删除。这有两个优点,通常做的工作更少,并且保持其他指向节点的指针有效。

Niklaus Wirth 的书“算法 + 数据结构 = 程序”是这方面的小宝石。我阅读了基于 Pascal 的原始版本。我相信现在有基于 Oberon 的免费电子版。


一般建议:在现代 C++ 中,使用 nullptr 代替宏 NULL 可能是个好主意。一般来说,它的类型更安全。

还有,而不是

foundSmallest == false

只是(1)

not foundSmallest

!foundSmallest

(1) 对于至少一个不完全符合标准的编译器,您必须包含 <iso646.h> 标头才能使用保留字 andornot 作为运营商。但是,这可以通过命令行中的强制包含指令来完成。它不需要在代码中显示。

【讨论】:

  • 为了我的代码,我将如何将指针设置为空?我已经将父节点作为节点,但不知道如何处理它。为了你的选择,你能提供一个例子吗?谢谢!
  • @SteppingHat:喜欢,parent->setLeft( nullptr )。或者,parent->left = nullptr。除了逻辑更正后它会变得更复杂,因为最小节点可以有一个正确的孩子,本质上复制数据的切换技术将不起作用。
【解决方案2】:

删除smallest持有的内存后,就不能再读取了。这只是垃圾数据。这可能有意义,也可能没有,但不能保证任何事情。

正如 Cheers 所指出的,从算法上讲,您忘记将 smallest 节点作为其父节点的子节点删除。换句话说,它的原始部分仍然指向它。您可以通过跟踪 parent 节点来解决此问题。

【讨论】:

    【解决方案3】:
    1. 在 Cheers 回答时,您忘记将指针设为空。
    2. 我不认为你找到最小节点的算法是正确的。要找到 BST 的最小节点,您不应该向右走,因为右子树的任何节点都不会小于节点本身。正确的方法应该是一直向左走,直到它没有左子树,这意味着没有节点小于那个。

    【讨论】:

    • 我刚刚意识到这一点,并且已经解决了寻找最小节点的问题。我还将它更新为在 while 语句中使用 !foundSmallest 的欢呼答案。整个代码部分是一个快速而肮脏的编写,重点是在这个阶段实际删除节点,并打算稍后清理它:P
    • 其实不一定是null,因为最小的节点可能有右子树。而且我更喜欢修改指针而不是替换数据。
    猜你喜欢
    • 1970-01-01
    • 2011-12-19
    • 1970-01-01
    • 2011-10-16
    • 2011-02-25
    • 2014-09-06
    • 1970-01-01
    • 2017-03-19
    • 1970-01-01
    相关资源
    最近更新 更多