【问题标题】:How to delete a tree properly in C++如何在 C++ 中正确删除树
【发布时间】:2019-05-03 22:02:05
【问题描述】:

我构建了一棵二叉树,它的每个右孩子都可能与其兄弟的左孩子相同。当我试图删除它时,我不确定整个树是否被完全删除。

class TreeNode
{
  public:
  int val;
  TreeNode* left;
  TreeNode* right;
  TreeNode(int v)
  {
    val = v;
    left = NULL;
    right = NULL;
  }
  ~TreeNode()
  {
    //testing if our tree is correct;
    cout<<val<<" ";
    delete left;
    delete right;
  }
};

  /*
              2
            4,  3
          3,  2,  6
        2,  9,  5,  2
      10, 5,  2,  15, 5
  */

   // 2->4   2->3    4->3  4->2   3->2 3->6

删除根目录;

所以,如您所见,我在删除节点值时打印它,但我的结果看起来像这样 {2 4 3 2 10 5 9 5 2 2 9 5 2 5 2 15 3 2 9 5 2 5 2 15 6 5 2 15 2 15 5},似乎当我们试图删除假设被删除为另一个节点的右孩子的左孩子时,系统仍然可以访问该节点并打印结果。我的意思是,它应该已经是 NULL 了吧?

【问题讨论】:

  • delete 没有将已删除的指针设置为NULL,看来你有一个误解。
  • 请确认。在第 2 层,4 和 3 都指向第 3 层的 2?如果是这样,您需要清理您的所有权。对于删除者,汉兰达定律完全有效。只能有一个。
  • 删除任何类型的指针不会将其设置为 nullptr。如果你想这样做,你必须添加一行来明确地这样做。允许删除 nullptr。
  • 删除 4 后将 3 指向 2 的指针设为空非常棘手。指针没有反向通道,也无法知道还有谁指向刚刚删除的分配。您可能会发现 std::shared_ptr 在这里很有帮助。
  • 你描述的数据结构不是树。在树中,一个节点只能有一个父节点。

标签: c++


【解决方案1】:

给定

每个右孩子都可能与其兄弟的左孩子相同。

deleteing 类的析构函数中的左右孩子势必会产生问题。无论您首先delete 哪个兄弟姐妹,另一个兄弟姐妹都会留下一个悬空指针。调用 delete 会导致未定义的行为。

我的意思是,它应该已经是 NULL 了,对吧?

这是不对的。即使这对于指针来说是正确的,也无法确保所有指向该对象的指针都设置为 NULL。简单例子:

int* p1 = new int;
int* p2 = p1;
delete p1;    // How does the run time know to also set p2 to NULL.

您需要使用不同的策略来删除树的所有节点。

  1. 将析构函数更改为原子删除操作。即

    ~TreeNode() {}
    
  2. 给定一个起始节点,编写一个函数来收集std::set&lt;TreeNode*&gt; 中的所有节点。

  3. 对聚集的std::set&lt;TreeNode*&gt; 中的每个对象调用delete

【讨论】:

  • 可能值得补充的是,set 用于防止重复。
【解决方案2】:

我构建了一棵二叉树,它的每个右孩子都可能与其兄弟的左孩子相同。

虽然类似于一棵树,但从技术上讲,这样的图并不是一棵树。我不熟悉这种结构的既定名称,所以我称之为金字塔。

我的意思是,它应该已经是 NULL 了,对吧?

通过指针删除对象不会使其他指向该对象的指针为空。

问题是您尝试删除共享子项两次。

鉴于节点对其子节点没有唯一所有权,因此在析构函数中删除它们是不正确的。所以你应该从析构函数中删除delete left; delete right;

您可以使用以下算法删除所有子项。这个想法是直接删除左边的根和所有孩子,同时保留那些金字塔下的所有右边孩子。所有那些未受影响的右孩子仍然是右金字塔的左孩子。然后以右孩子为根重复。一个例子:

void deletePyramid(TreeNode* node) {
    TreeNode* right;
    do {
        right = node->right;
        while(node) {
            TreeNode* left = node->left;
            delete node;
            node = left;
        }
    } while(node = right);
}

您可能会注意到,此算法是非递归的,并且具有恒定的空间复杂度。本质上,该算法假装结构是这样的:

          2
         / \
        4,  3
       /   / \
      3,  2,  6
     /   /   / \
    2,  9,  5,  2

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-02
    • 2020-06-19
    • 2018-09-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多