【问题标题】:Delete a Binary Tree with a Stack用栈删除二叉树
【发布时间】:2011-03-08 14:09:41
【问题描述】:

我仍在研究我的二叉树,到目前为止,插入、查找、最大值、最小值函数都运行良好。所以接下来我想做一个删除功能。我包含了一个可以保存的堆栈 - 啊地狱,我将只显示代码:

class Tree
{
private:
    int value_;
    Tree *root_;
    Tree *left_;
    Tree *right_;

    std::stack<Tree*> treeStack;

删除功能:

int Tree::eraseTree()
{
    while( !treeStack.empty() )
    {
        Tree *temp = treeStack.top();
        treeStack.pop();
        delete temp;
    }
    if( treeStack.empty() )
        return 1;
    else
        return -1;
}

我现在遇到错误。这不是什么大问题——我尝试调试自己的代码——但这次它告诉我&lt;deque&gt; 库文件中有一个我什至没有使用的错误。

在程序关闭之前,我得到System.AccessViolationException,错误代码指向deque 文件。当然它不能在那里,它必须是我的代码中的一些指针。这让我相信我没有正确地处理堆栈,或者没有正确地推入堆栈。

我在这里做错了什么?当我在堆栈上调用 .pop 时,我实际上是在删除节点吗?

编辑:

if( !root_ )
{
    root_ = new Tree(val, 0, 0);
    treeStack.push(root_);
    return val;
}

Tree *parent = findInsertionPoint(val, root_);
    if( val < parent->value_ )
        parent->left_  = new Tree(val, 0, 0);
    else
        parent->right_ = new Tree(val, 0,0);

    treeStack.push(parent);
    return val;

是我将元素推入堆栈的位置。

其他问题: std::stack 是否必须在 ctor 中构建?

【问题讨论】:

  • 我看到eraseTreeTree 类的一部分,也许你的treeStack 中有this,所以你正在做delete this
  • 我从来没有对 this 指针太熟悉,或者它实际上是什么。我需要做些什么来确保 this 指针不再抱怨?
  • 乍一看,我看不到任何故障。这可能是我不习惯 std::stack 的原因,但如果错误不在发布的代码中,也许你应该包含构建堆栈的代码。 @cristis 检查 if(temp!=this) 是否足够?
  • @InsertNickHere:我试过了。 temp != this 是真的。 Temp 不是 this
  • 关于指针的小事:你应该用 != null 检查每个用法,例如。 if(parent!=null){...} 和 if(temp!=null),这样可以更轻松地避免/查找故障。

标签: c++ stl stack binary-tree


【解决方案1】:

听起来您可能要删除两次。

确保您要删除的内容没有在其他任何地方使用或删除,尤其是在调用您的 eraseTree() 方法之后。

【讨论】:

  • 我已经包含了元素的插入,不,这是我的第一个删除功能。到目前为止,擦除函数是我的程序中调用的最后一个函数。
  • 你试过不删除吗?我发现,很烦人的是,很多时候 C++ 会自动删除我分配的所有内容,如果我自己删除它会抱怨。
【解决方案2】:

root_*left_*right_* 更改为 auto_ptrs 以保证它们的销毁。那么当你来删除一个节点时,你可以删除 root_.release();一切都很好。得问问你为什么要使用堆栈。

【讨论】:

  • 我不明白你的意思。如果我想删除整棵树而不必沿着树递归然后备份,我只需将所有指针存储在堆栈中。提取的第一个指针位于树的底部。然后我只是删除内容。这就是为什么我想要一个堆栈。 auto_ptrs 将如何帮助我做到这一点?
  • @SoulBeaver:如果您使用 auto_ptrs,则不必在树上上下循环。您可以只删除根节点,整个树就会倒下。使用 auto_ptr,销毁是自动化和有保证的。您无需为任何堆栈垃圾而烦恼。
  • 我认为 auto_ptr 不是一个好选择...我建议改用 shared_ptr。
  • @EmbeddedProg:为什么?只有树节点有权删除它的子节点——没有其他人。虽然我刚刚意识到也许 root_ 不应该是一个 auto_ptr- 并没有指向它指向第一个节点的树枝。
【解决方案3】:

您在deque 中崩溃了,因为堆栈包含其中之一。如果您查看崩溃后的堆栈跟踪,那么您可以了解您的代码在发生崩溃时正在做什么。

看起来代码的最终 sn-p 将错误的节点放入堆栈;当然它应该是新创建的节点,而不是插入点?如果将两个节点添加到父节点,则父节点将两次进入堆栈,并被删除两次。

应该更像

Tree *parent = findInsertionPoint(val, root_);
Tree *child = new Tree(val, 0, 0);
if( val < parent->value_ )
    parent->left_  = child;
else
    parent->right_ = child;

treeStack.push(child);
return val;

但我赞成 DeadMGs 的建议,即为 left_right_ 使用智能指针(但不要将 root_ 设为 auto_ptr,如果所有节点共享),并让它们清理为你。我不确定我是否看到了堆栈的意义;如果是为了加快树的破坏,那么在添加复杂且容易出错的优化之前问自己两个问题:

  • 是否足够慢,值得付出优化的开发/调试成本?
  • 在树旁边构建和维护堆栈是否值得额外的运行时间和内存成本?

还有您的附加问题:stack 将在构造函数中构建,但您无需编写任何代码即可。它的默认构造函数将被自动调用,这将为您提供一个可用的空堆栈。

此外,除非这是一个学习练习,否则您应该使用 std::multiset 而不是重新发明它。

【讨论】:

  • +1 指出 std::stack 是一个默认使用 std::deque 的适配器。不过,我不会称这是无用的练习。实施一棵树是一种很好的学术实践!也许有一天他会想要实现一个 kd-tree、trie 或其他一些树结构,而他正在开发的平台没有方便的库可以访问这些树结构。这也将帮助他了解 std::set/std::map 实际上是多么优越。
  • @stinky472:我没有把它称为“无用的练习”。正如你所说,实现一棵树当然是一件很重要的事情。
【解决方案4】:

deque 中出现错误,因为默认情况下 std::stack 使用此类作为内部容器。看栈定义:

template<class _Ty, class _Container = deque<_Ty> > class stack;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-06-09
    • 2017-07-28
    • 2018-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多