我试图在"Data Structures and Algorithms" by Granville Barnett中遵循BST算法,但我不理解它在下面描述的节点删除算法 .
第3.3节(第22页)
从BST中删除节点非常简单,需要考虑四种情况:要删除的值是叶节点;或者要删除的值有一个正确的子树,但没有左子树;或者要删除的值有一个左子树,但没有右子树;或者要删除的值同时具有左子树和右子树,在这种情况下,我们会提升左子树中的最大值 .
图3.2(第22页)
23
/ \
14 31
/
7
\
9
-
案例#1指向节点9 .
-
案例#2指向节点7 .
-
案例#3指向节点14 .
-
案例#4指向节点23 .
我将上面的文字解释为#4意味着当我们删除23时,我们将14提升为root并使31成为正确的孩子:
14
/ \
7 31
\
9
...但是这本书的算法(来自第23页)对于案例#4 bamboozles me(我在这里用Java重写了它):
1 boolean remove(T value) {
2 // ...
3
4 // case #4
5 Node largestValueNode = nodeToRemove.left;
6 while (largestValueNode.right != null) {
7 // find the largest value in the left subtree of nodeToRemove
8 largestValueNode = largestValueNode.right;
9 }
10
11 // delete the right child of largestValueNode's parent
12 findParent(largestValueNode.value).right = null;
13 nodeToRemove.value = largestValueNode.value;
14
15 count--;
16 return true; // successful
17}
如果我遵循该算法, largestValueNode
是节点14,因此其父节点是节点23. Why does the algorithm nullify the right child of the parent?
Why does line 13 copy the largestValueNode's value into the node to be deleted?
我希望第11-13行是:
11 if (largestValueNode != null)
12 largestValueNode.right = nodeToRemove.right;
13 nodeToRemove.right = null;
EDIT:
这本书的算法确实有一个bug . 修复如下:
1 boolean remove(T value) {
2 // ...
3
4 // case #4
5 Node largestValueNode = nodeToRemove.left;
6 while (largestValueNode.right != null) {
7 // find the largest value in the left subtree of nodeToRemove
8 largestValueNode = largestValueNode.right;
9 }
10
11 Node p = findParent(largestValueNode.value);
12 if (p != null) {
13 if (nodeToRemove == p)
14 nodeToRemove.left = largestValueNode.left;
15 else
16 p.right = largestValueNode.left;
17 }
18 nodeToRemove.value = largestValueNode.value;
19
20 count--;
21 return true; // successful
22}
4 回答
如果你这样做
你不是在考虑
14
可能有一个合适的孩子的情况 . 例如:你应该删除
23
时的解决方案因此,您将
15
的原始父级14
的正确子级设置为null . 这是第一个代码正在做的事情 .编辑:处理您的评论
有了你的解决方案,你就会得到
另外,原始代码也是错误的;尝试这样的事情:
还要回答:“为什么第13行将maximumValueNode的值复制到要删除的节点中?”
我们正在删除
largestValueNode
,之前我们're storing it'的值在nodeToRemove
中看起来这本书的算法对于这个特定的例子是错误的(假设你完全翻译成了Java :)) . 它正在做你提到的,但它适用于这种情况:
其中nodeToRemove = 23并且你的BST 14中有一个正确的孩子15.这本书的算法将在这里用15代替23并将14的右子设置为null . 在这种情况下,您的算法将失败 .
仔细看看这条线:
注意这一行如何导致
14
看起来像这样(忽略孙子):但这正是所期望的!由于
14
现在将31
作为其右子,因此31
不再是15
的正确子项,所以为了清理起见,15
的右子项设置为NULL .很高兴知道原始代码是错误的 - 我刚刚花了几个小时就开始思考我错过了一些东西 . 如果传递了根元素,则存在NPE问题,并且无论如何都不考虑根元素的移除 .
这是我的Java实现,可能会使用一些优化 - 建议欢迎 .
O (n log n)
最坏的情况 . 测试如下 .单元测试(显然全部通过):