我一直在尝试在C中实现一个删除二叉树中的节点的函数,该节点应该(理论上)处理所有三种情况,即:
有没有办法处理整个删除功能而不分别检查每个案例?作为下面提到的评论者,我确实检查了很多案例,也许通过检查一个基本案例可以递归地解决整个问题。
我特别感兴趣的是我删除树中具有父节点的节点,并且它本身是两个子节点的父节点。
以下两个答案都很有用,但我不认为它们完全解决了这个问题。
这就是我所拥有的:
typedef struct Node
{
int key;
int data;
struct Node *left;
struct Node *right;
struct Node *parent;
} Node;
/* functions that take care of inserting and finding a node and also traversing and freeing the tree */
...
void delete(Node *root, int key)
{
Node *target = find(root, key); // find will return the node to be deleted
Node *parent = target->parent; // parent of node to be deleted
// no children
if (target->left == NULL && target->right == NULL)
{
// is it a right child
if (target->key > parent->key)
parent->right = NULL;
// must be a left child
else
parent->left = NULL;
free(target);
}
// one child
else if ((target->left == NULL && target->right != NULL) || (target->left != NULL && target->right == NULL))
{
// here we swap the target and the child of that target, then delete the target
Node *child = (target->left == NULL) ? target->right : target->left;
child->parent = parent;
if (parent->left == target) parent->left = child;
else if (parent->right == target) parent->right = child;
free(target);
}
// two children
else
{
// find the largest node in the left subtree, this will be the node
// that will take the place of the node to be deleted
Node *toBeRepl = max(target->left);
// assign the data of the second largest node
target->key = toBeRepl->key;
target->data = toBeRepl->data;
// if new node immediately to the left of target
if (toBeRepl == target->left)
{
target->left = toBeRepl->left;
Node *newLeft = target->left;
if (newLeft != NULL) newLeft->parent = target;
}
else
{
delete(target->left, toBeRepl->key);
// Node *replParent = toBeRepl->parent;
// replParent->right = NULL;
}
}
非常感谢您的反馈。
编辑:为了澄清,我正在尝试删除特定节点而不触及其子树(如果有的话)。它们应该保持完整(我通过交换要删除的节点的值和(取决于具体情况)其子节点之一来处理)。
编辑:我用作以下维基百科文章的参考:
http://en.wikipedia.org/wiki/Binary_search_tree#Deletion
这是我在两个孩子的情况下交换节点值的想法,特别是引用:
调用要删除的节点N.不要删除N.而是选择其中之一 它的有序后继节点或其有序前导节点R. 将N的值替换为R的值,然后删除R.
对于上述情况,C ++中有一些有趣的代码,但是我不确定交换是如何发生的:
else //2 children
{
temp = ptr->RightChild;
Node<T> *parent = nullptr;
while(temp->LeftChild!=nullptr)
{
parent = temp;
temp = temp->LeftChild;
}
ptr->data = temp->data;
if (parent!=nullptr)
Delete(temp,temp->data);
else
Delete(ptr->rightChild,ptr->RightChild->data);
}
有人可以解释一下该部分的内容吗?我假设递归与用户评论'在这里类似。
答案 0 :(得分:1)
我会使用递归来做,假设你在树的末尾有null
,找到null将是'返回'或返回条件。
一种可能的算法是:
Node* delete(Node *aNode){
if(aNode->right != NULL)
delete(aNode->right);
if(aNode->left != NULL)
delete(aNode->left);
//Here you're sure that the actual node is the last one
//So free it!
free(aNode);
//and, for the father to know that you're now empty, must return null
return NULL;
}
肯定会有一些错误,但这是主要的想法。 这个实现是dfs之类的。 希望这会有所帮助。
[编辑]节点* aNode已修复。忘了明星,我的坏。
答案 1 :(得分:1)
我没有在代码中看到任何“不雅”,这样的格式和注释代码很难得到。但是,是的,您可以将删除函数中的if-else结构减少到只有一种情况。如果你看一下删除操作最抽象的概念,你会注意到所有的情况基本上归结为最后一种情况(删除有两个孩子的节点)。
你只需在其中添加几行。就像在toBeRepl = max(left-sub-tree)之后,检查它是否为NULL,如果是,那么toBeRepl = min(right-sub-tree)。
所以,案例1(没有孩子):假设您的max()
方法正确实现,它将返回NULL
作为左子树中最右边的元素,右侧子树上的min()
也是如此。用toBeRepl替换你的目标,你将删除你的节点。
案例2(一个孩子):如果max()
确实返回NULL
,则min()
将不会,反之亦然。所以你将拥有一个非NULL toBeRepl
。再次用这个新的toBeRepl
替换你的目标,你就完成了。
案例3(两个孩子):与案例2相同,只有您可以确保max()
不会返回NULL
。
因此,您的整个delete()
函数将归结为最后一个else
语句(稍作更改)。有点像:
Node *toBeRepl = max(target->left);
if toBeRepl is NULL
{
toBeRepl = min(target->right);
}
if toBeRepl is not NULL
{
target->key = tobeRepl->key;
target->data = toBeRepl->data;
deallocate(toBeRepl); // deallocate would be a free(ptr) followed by setting ptr to NULL
}
else
{
deallocate(target);
}
答案 2 :(得分:0)
我很久以前就完成了这项工作,我认为为来这里遇到同样问题的人添加一个示例答案会更好(考虑到这个问题积累了400多个观点):
/* two children */
else
{
/* find the largest node in the left subtree (the source), this will be the node
* that will take the place of the node to be deleted */
Node* source = max(target->left);
/* assign the data of that node to the one we originally intended to delete */
target->key = source->key;
target->data = source->data;
/* delete the source */
delete(target->left, source->key);
}
维基百科有excellent article启发了这段代码。