【问题标题】:How to delete a binary search tree from memory?如何从内存中删除二叉搜索树?
【发布时间】:2011-01-15 12:40:47
【问题描述】:

我有一个 BST,它是 C++ 中的链表。我将如何从内存中删除整个内容?会从类函数中完成吗?

【问题讨论】:

  • 根据定义,链表有前向链接,也可能有后向链接。 BST 有左孩子、右孩子,也许还有父链接。是哪个?
  • 我的 BST 的节点可以有左子、右子和父链接。

标签: c++ memory memory-management linked-list binary-tree


【解决方案1】:

只需删除孩子:

struct TreeNode {
    TreeNode *l, *r, *parent;
    Data d;

    TreeNode( TreeNode *p ) { l = nullptr; r = nullptr; parent = p; }
    TreeNode( TreeNode const & ) = delete;
    ~TreeNode() {
         delete l; // delete does nothing if ptr is 0
         delete r; // or recurses if there's an object
    }
};

或者如果你使用unique_ptr 或类似的,那甚至不需要:

struct TreeNode {
    unique_ptr< TreeNode > l, r;
    TreeNode *parent;
    Data d;

    TreeNode( TreeNode *p ) { l = nullptr; r = nullptr; parent = p; }
    TreeNode( TreeNode const & ) = delete;
    ~TreeNode() = default;
};

【讨论】:

  • 如果您需要保留子树的一部分,您只需提供一个 Detach() 方法,该方法将从其父节点中删除一个节点。然后删除不会级联到您要保留的子树。
  • 使用auto_ptr,只需分配例如auto_ptr&lt;TreeNode&gt; outside_ptr = my_node.l即可完成。
  • 我想提出一个警告:这个样本是邪恶的,可能会导致崩溃。问题是auto_ptr 的复制/赋值运算符有奇怪的语义...const TreeNode&amp; node = ...; TreeNode copy(node); node.l-&gt;data; 将崩溃(希望立即)。
  • @Matthieu:除非auto_ptr 对象是您代码中的...,否则根本没有auto_ptr 语义。 auto_ptr 赋值用于detach,正如我和 Scott 刚刚提到的。您不想访问无效的指针 post-detach。 (auto_ptr 总是实现move 语义,并被 C++0x 在这方面更普遍的支持所淘汰,适当地指出。)
  • 我在 auto_ptr 基础上重用了 TreeNode 的实现,因此编译器自动为 TreeNode 创建的复制构造函数将基于来自 auto_ptr 的复制构造函数及其混蛋copynode 形成的语义将最终被修改,尽管 const 这绝对是奇怪的。我完全赞成在这里使用智能指针,但不幸的是,如果你想要一个正确的语义,你仍然必须自己写下复制构造函数和赋值运算符。
【解决方案2】:

如果您可以访问链表本身,那就小菜一碟了:

// Making liberal assumptions about the kind of naming / coding conventions that might have been used...
ListNode *currentNode = rootNode;

while(currentNode != NULL)
{
    ListNode *nextNode = currentNode->Next;
    delete currentNode;
    currentNode = nextNode;
}

rootNode = NULL;

如果这是 BST 的自定义实现,那么如果它已将自身绑定到特定的数据结构,那么这很可能是它内部的工作方式。

如果您无法访问内部结构,那么 Potatoswatter 的答案应该是正确的。假设按照他们的建议设置了 BST,那么简单地删除根节点应该会自动删除所有分配的内存,因为树下的每个父节点都会删除它的子节点。

如果您询问如何手动遍历二叉树,那么您将执行以下递归步骤:

void DeleteChildren(BSTNode *node)
{
    // Recurse left down the tree...
    if(node->HasLeftChild()) DeleteChildren(node->GetLeftChild());
    // Recurse right down the tree...
    if(node->HasRightChild()) DeleteChildren(node->GetRightChild());

    // Clean up the data at this node.
    node->ClearData(); // assume deletes internal data

    // Free memory used by the node itself.
    delete node;
}

// Call this from external code.
DeleteChildren(rootNode);

我希望我没有错过这里的重点,并且这会有所帮助。

【讨论】:

  • 为什么需要函数 ClearData()?为什么不删除节点?这是否也删除了根节点?
  • 我包含了 ClearData() 以表明在每个节点上都需要执行 something 来释放该节点持有的数据(如果有的话)所占用的内存。如果节点只有一个指向外部数据的指针(很可能),那么 ClearData() 可能会简单地将指针设为 NULL。如果树实际上为数据本身分配了内存(比如每个节点直接将字符串数据保存为它已分配的 char[]),那么 ClearData() 将需要 delete[] 这个数组。我试图暗示的是这些步骤是:1)清除每个节点持有的所有数据(这可以在析构函数中完成)2)删除节点本身。
【解决方案3】:

执行树的后序遍历(即先访问子节点,再访问父节点),并在访问时删除每个节点。

这是否与类有关完全取决于您的实现。

【讨论】:

    【解决方案4】:

    由于提供的信息有限....

    如果您为节点分配了 new 或 malloc(或相关函数),则您需要遍历所有节点并释放或删除它们。

    另一种方法是将shared_ptr's (and weak_ptr's to kill cyclics) 放入您的分配中——只要您操作正确,您就不必手动释放节点

    如果您使用的是在互联网上获得的高质量实现,并且提供的类不会泄漏,那么您不必担心任何事情。

    【讨论】:

      【解决方案5】:

      使用智能指针并忘记它。

      【讨论】:

        猜你喜欢
        • 2017-07-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-04-11
        • 2015-04-08
        • 1970-01-01
        相关资源
        最近更新 更多