我想编写一个从二叉搜索树中删除节点的方法。
这是我的方法:
public void remove(Node node)
//Removes a given node and puts one of the nodes below it in its place until it reaches the end
{
if (node.left != null) //If the node to the left is not empty
{
node.value = node.left.value; //Moves up the left node to take its place
remove(node.left); //Goes to the next node
if (node.left.right == null && node.left.left == null)
node.left = null; //Removes the last node at the end of the tree after moving it up
}
else if (node.right != null)
{
node.value = node.right.value; //Moves up the left node to take its place
remove(node.right); //Goes to the next node
if (node.right.left == null && node.right.right == null)
node.right = null; //Removes the last node at the end of the tree after moving it up
}
}
问题是它只适用于某些情况。
例如,我输入60,70,65。(根节点为50) 树应该看起来像
50
/ \
60
/ \
70
/ \
65
然后让我说我选择删除60.这似乎开始工作正常。 但是,如果我然后运行我信任的搜索方法,则返回70在任何指针上都没有节点。
我假设发生的事情是,在65可以向上移动之前,70被设置为null。并且由于65技术上不再连接到树,因此搜索方法无法找到它。
这样的事情:
50
/ \
70
/ \
/ \
65
问题是,我不明白这是怎么回事。特别是因为如果它的指针都指向null,它应该将节点设置为null,这是由于if语句
if (node.left.right == null && node.left.left == null)
node.left = null;
和
if (node.right.left == null && node.right.right == null)
node.right = null;
另外,如果第一个if语句不为真(如果留下!= null),那么它不应该只是继续使用“else”(并删除正确的语句)吗?
非常欢迎任何建议或提示。
答案 0 :(得分:1)
删除方法的逻辑存在严重缺陷。首先,您没有移动节点而是复制值,这已经不正确了:因为任何节点都可以有两个链接,只复制左侧或右侧链接值,然后检查您是否在一片叶子上最终删除它是错误的:如果你不在叶子上怎么办?您放弃的其他链接怎么样?在您的情况下,您最终将在70右侧的值为65:不再是BST。请记住,规则是对于任何节点n,左子树中的所有节点必须小于n,并且右子树中的所有节点都大于n。
这也是为什么你找不到65:它不是因为70有两个像你想的那样附加到它的空指针,而是因为你的搜索方法,当它到达70时,因为它大于65,搜索65到节点70的左,并在那里找到空。
这是删除BST中节点的正确且经典的Hibbard算法:要删除节点x,必须将其替换为后继。哪个是继任者?因为x有一个正确的子节点,所以它的后继节点是右子树中具有最小键的节点。替换保留了树中的顺序,因为x.key和后继密钥之间没有密钥。我们通过以下四个步骤完成了替换x的任务:
在t
将x设置为指向其后继min(t.right)。
设置x的右侧链接(应该指向BST 包含大于x.key的所有键)deleteMin(t.right),. 链接到包含所有大于x.key的键的BST 删除后。 (要删除最小值,我们向左移动,直到找到具有空左链接的节点,然后通过其右侧链接替换该节点的链接)
将x的左侧链接(为空)设置为t.left(所有键都为 小于已删除的密钥及其后继密钥。