我根据Alex Allain的example found here创建了一个二叉树。在向其添加约5000-6000个元素后,它会引发堆栈溢出异常。知道如何防止堆栈溢出?原因是Insert()
以递归的方式调用自己。
2013年3月6日更新
以下是我重构代码以避免堆栈溢出的方法:
void Insert(Key_T key, Value_T val, QuickMapNode<Key_T, Value_T> *leaf)
{
while (true)
if(key < leaf->key)
{
if(leaf->left) leaf = leaf->left;
else
{
leaf->left = new QuickMapNode<Key_T, Value_T>;
leaf->left->key = key;
leaf->left->val = val;
leaf->left->parent = leaf;
leaf->left->left = NULL; // Sets the left child of the child node to null
leaf->left->right = NULL; // Sets the right child of the child node to null
break;
}
}
else if (key >= leaf->key)
{
if(leaf->right) leaf = leaf->right;
else
{
leaf->right = new QuickMapNode<Key_T, Value_T>;
leaf->right->key = key;
leaf->right->val = val;
leaf->right->parent = leaf;
leaf->right->left = NULL; // Sets the left child of the child node to null
leaf->right->right = NULL; // Sets the right child of the child node to null
break;
}
}
}
答案 0 :(得分:5)
像ÖöTiib所说,你应该改变insert
不是递归的。通过将存储在堆栈中的数据存储在某些其他数据结构中,可以将每个递归函数转换为非递归函数。这样你可以使用堆来处理这些数据,并且没有堆栈上的函数调用开销(返回地址等)。你经常可以使用像堆栈一样使用的向量或列表:take(和pop)获取当前参数的向量的back()
,并且在当前代码将递归调用自身的位置,您push_back()
将传递给递归函数调用的内容。
以下是链接中的insert()
方法作为迭代版本:
void btree::insert(int key, node *leaf)
{
std::list<node*> leafs;
leafs.push_back(leaf);
while (leafs.size() > 0)
{
leaf = leafs.back();
leafs.pop_back();
if(key < leaf->key_value)
{
if(leaf->left!=NULL)
leafs.push_back(leaf->left);
else
{
leaf->left=new node;
leaf->left->key_value=key;
leaf->left->left=NULL; //Sets the left child of the child node to null
leaf->left->right=NULL; //Sets the right child of the child node to null
}
}
else if(key>=leaf->key_value)
{
if(leaf->right!=NULL)
leafs.push_back(leaf->right);
else
{
leaf->right=new node;
leaf->right->key_value=key;
leaf->right->left=NULL; //Sets the left child of the child node to null
leaf->right->right=NULL; //Sets the right child of the child node to null
}
}
}
}
我运行代码,它似乎工作。它比递归版慢得多,不知道为什么会这样......两个版本都可以使用10000个以上的元素,所以你的实现可能还有其它错误......
实际上,当像我们这样遍历二叉树时,不需要存储任何先前的信息,因为我们不进行任何回溯。一旦找到新元素的位置,我们就完成了。所以我们可以完全摆脱列表/向量:
void btree::insert(int key, node *leaf)
{
while (leaf != NULL)
{
if(key < leaf->key_value)
{
if(leaf->left!=NULL)
leaf = leaf->left;
else
{
leaf->left=new node;
leaf->left->key_value=key;
leaf->left->left=NULL; //Sets the left child of the child node to null
leaf->left->right=NULL; //Sets the right child of the child node to null
return;
}
}
else if(key>=leaf->key_value)
{
if(leaf->right!=NULL)
leaf = leaf->right;
else
{
leaf->right=new node;
leaf->right->key_value=key;
leaf->right->left=NULL; //Sets the left child of the child node to null
leaf->right->right=NULL; //Sets the right child of the child node to null
return;
}
}
}
}
答案 1 :(得分:1)
使insert
的算法不是递归的。您只需要搜索插入位置,这样就不需要堆栈调用了。
答案 2 :(得分:1)
由于缺乏提供的细节而进行猜测:假设最坏的情况,在6000次插入后,堆栈深度是所有6000个递归调用。假设合理的堆栈帧大小可能是20个字节 - 然后堆栈大小是6000 * 20 = 120,000个字节。如果堆栈帧实际上是160字节(大8倍),则stak大小为6000 * 160,略小于1MB。我想知道......你的元素数量是否有限制?分配的堆栈大小是多少?
上面的评论告诉你如何实际解决问题(平衡树)。我可能会补充说,几乎任何递归算法都可以转换为迭代算法 - 它不是那么优雅而且需要付出努力来实现它,但是你不会填满堆栈。但是如果确实存在(不仅仅是你认为存在)输入元素数量的限制,那么在我看来你可以确定插入的堆栈框架的大小并使堆栈大小足够大以适应#elements *堆栈帧大小,最糟糕的情况,加上堆栈上的额外内容多一点。
答案 3 :(得分:0)
如上所述,最可能的原因是由于递归插入调用太多而导致堆栈溢出
以下是不同的选项
使用自平衡树http://en.wikipedia.org/wiki/Self-balancing_binary_search_tree
在此示例中使用非递归树式算法.Lookk Non-recursive add function in a binary tree using c++