【问题标题】:Why does my C++ code fail to delete all nodes in my BST?为什么我的 C++ 代码无法删除 BST 中的所有节点?
【发布时间】:2010-02-11 02:57:14
【问题描述】:

这应该是遍历一个 BST 并删除每个节点,包括根节点。但是,最后,我收到消息“根仍有左节点”。为什么没有删除所有节点?

void deleteTree()
{   
    deleteNode(root);
    if(root->right)
        cout << "root still has a right node" << endl;
    if(root->left)
        cout << "root still has a left node" << endl;
    root = 0;

}   

void deleteNode(node *p) 
{   
    if(p->left) 
    {   
        deleteNode(p->left);
        p->left = 0;
    }   
    if(p->right) 
    {   
        deleteNode(p->right);
        p->right = 0;
    }   

    cout << "Deleting node containing " << p->data << endl;
    delete p;
}   

【问题讨论】:

    标签: c++ pointers binary-tree binary-search-tree


    【解决方案1】:

    您正在删除最后的p (root),然后尝试访问deleteTree() 中的内容,其中root 不再指向分配的内存。结果将是未定义的。

    【讨论】:

    • 为什么它检查右边节点时没有发现任何东西,但是当它检查左边节点时却发现了一些东西?
    • 因为root 只是指向垃圾内存。其他结果可能包括 left only、both、none 或 crash。
    【解决方案2】:

    您正在删除root。然后你的代码试图访问它所在的内存。

    你很喜欢那里的未定义行为领域。

    【讨论】:

      【解决方案3】:

      deleteNode 中删除 root 后,不应取消引用它。使用调试器检查为什么 root-&gt;left 不为空。

      【讨论】:

      • +1 表示调试器建议,虽然我想他会发现函数返回时它实际上是 null。
      • ‘root’肯定是被删除了,但是‘root->left’处肯定还有别的东西;也许用调试器嗅探那个地址会发现一些东西......
      【解决方案4】:

      您在删除 root 后查看 root-&gt;left,使其可用于新分配的块。

      【讨论】:

        【解决方案5】:

        我会简单地改变树本身,这样处理起来会更容易:

        struct Node
        {
          Node(data_type data): mLeft(), mRight(), mData(data) {}
          Node(const Node& rhs): mLeft(), mRight(), mData(rhs.mData)
          {
            if (rhs.mLeft.get()) mLeft.reset(new Node(*rhs.mLeft));
            if (rhs.right.get()) mRight.reset(new Node(*rhs.mRight));
          }
          Node& operator=(Node rhs)
          {
            this->swap(rhs);
            return *this;
          }
          ~Node() { }
        
          void swap(Node& rhs)
          {
            using std::swap;
            swap(mLeft, rhs.mLeft);
            swap(mRight, rhs.mRight);
            swap(mData, rhs.mData);
          }
        
          Node* left() const { return mLeft.get(); }
          void left(std::auto_ptr<Node> node) { mLeft= node; }
        
          Node* right() const { return mRight.get(); }
          void right(std::auto_ptr<Node> node) { mRight = node; }
        
          data_type& data() { return mData; }
          const data_type& data() const { return mData; }
        
        private:
          std::auto_ptr<Node> mLeft;
          std::auto_ptr<Node> mRight;
          data_type mData;
        };
        

        通过面向对象,每个节点现在负责它处理的内存。此外,在界面中使用std::auto_ptr 可以清楚地表明它拥有所有权。

        请注意,它专为深度复制、任何其他需要boost::shared_ptr 或等效方法的方法而定制。是的,std::auto_ptr 让您自己处理复制,没有魔法。

        这种设计比使用普通的C-struct 更简洁,每个人都可以操纵资源。您仍然可以通过访问器完全访问底层数据...但他们注意不要调用未定义的行为...

        当然,你仍然可以让它崩溃:

        Node& node = ...
        delete node.left(); // haha
        

        但如果 C++ 可以防止意外问题,它就会为恶意代码敞开大门。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-08-19
          • 1970-01-01
          • 2014-05-26
          相关资源
          最近更新 更多