基本上,程序包含一个BST类,它指向二叉树的第一个节点,节点也是它们自己的类。
BST调用此成员函数:
void remove(const T& x)
{
removeNode(m_root, x);
return;
}
这是remove节点的递归部分,它运行完成:
template <typename T>
void removeNode(TreeNode<T>* &p, const T& x)
{
if(p == NULL)
return;
if(x < p -> m_data)
removeNode(p -> m_left, x);
else if(x > p -> m_data)
removeNode(p -> m_right, x);
else
{
TreeNode<T>* tmp = new TreeNode<T>;
if(p -> m_left == NULL)
{
tmp = p -> m_right;
delete p;
p = tmp;
}
else if(p -> m_right == NULL)
{
tmp = p -> m_left;
delete p;
p = tmp;
}
else
{
tmp = p -> m_right;
TreeNode<T>* tmp2 = new TreeNode<T>;
while(tmp -> m_left != NULL)
{
tmp2 = tmp;
tmp = tmp -> m_left;
}
p -> m_data = tmp -> m_data;
if(tmp2 != NULL)
removeNode(tmp2 -> m_left, tmp -> m_left -> m_data);
else
removeNode(p -> m_right, p -> m_right -> m_data);
}
}
return;
}
当remove()函数返回时,我是正确的,我想知道为什么?
答案 0 :(得分:1)
用户@WhozCraig指出代码中最重要的错误:你分配了你从未使用过的对象(永远不会破坏!):
TreeNode<T>* tmp = new TreeNode<T>;
TreeNode<T>* tmp2 = new TreeNode<T>;
后一项任务导致条件
if(tmp2 != NULL)
始终满意,这会阻止您执行
else
removeNode(p -> m_right, ...)
分支。
修改强>
假设您有一个带有键2,5和7的三节点树。让我们分别表示节点node2
,node5
和node7
。假设node5
是树根。假设您正在移除键5.
然后:
*p == node5
; if
不满意,控件传递给else
分支; tmp
被指定为p->m_right
; &node7
tmp2
被分配了一个新的“空”对象 - 让我们称之为nodeEmpty
; node7
没有子节点,因此tmp->m_left
为NULL
并且跳过while
循环而不进行迭代; p->m_data = tmp->m_data
会导致node5
获取密钥7; tmp2 == &nodeEmpty
而非NULL
,您致电
removeNode(tmp2 -> m_left, tmp -> m_left -> m_data)
显然会从nodeEmpty
(?!)的不存在左子树中删除某些内容,但是......
但是tmp == &node7
此时node7
没有孩子,所以tmp->m_left == NULL
。因此,访问tmp->m_left->m_data
会触发内存访问错误,并且在递归调用removeNode
之前进程崩溃。