[英]Binary tree stack overflow
我根據Alex Allain的例子找到了一個二叉樹。 在向其添加約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;
}
}
}
就像Öö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;
}
}
}
}
制作非遞歸的insert
算法。 您只需要搜索插入位置,這樣就不需要堆棧調用了。
由於缺乏提供的細節而進行猜測:假設最壞的情況,在6000次插入之后,堆棧深度是所有6000個遞歸調用。 假設合理的堆棧幀大小可能是20個字節 - 然后堆棧大小是6000 * 20 = 120,000個字節。 如果堆棧幀實際上是160字節(大8倍),則stak大小為6000 * 160,略小於1MB。 我想知道......你的元素數量是否有限制? 分配的堆棧大小是多少?
上面的評論告訴你如何實際解決問題(平衡樹)。 我可能會補充說,幾乎任何遞歸算法都可以轉換為迭代算法 - 它不是那么優雅而且需要付出努力來實現它,但是你不會填滿堆棧。 但是如果確實存在(不僅僅是你認為存在)輸入元素數量的限制,那么在我看來你可以確定插入的堆棧框架的大小並使堆棧大小足夠大以適應#elements *堆棧幀大小,最糟糕的情況,加上堆棧上的額外內容。
如上所述,最可能的原因是由於遞歸插入調用太多而導致堆棧溢出
以下是不同的選項
使用自平衡樹http://en.wikipedia.org/wiki/Self-balancing_binary_search_tree
在這個例子中使用非遞歸樹體面算法.Lookk使用c ++在二叉樹中使用非遞歸添加函數
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.