问题描述
请参阅下面的原始红黑树:
42B
/ \
10R 64B
/ \ / \
7B 29B 50R 83R
/
5R
尝试删除29时遇到麻烦。由于尝试删除该节点会导致双黑(我们称之为DB)情况,而DB的远侄(5R
)节点是RED节点,我们应该能够通过简单地交换父节点(10R
和兄弟节点(7B
)的颜色,在DB方向上旋转DB的父节点以及为DB的侄子BLACK着色来解决这个问题,结果将是:
42B
/ \
7R 64B
/ \ / \
5B 10B 50R 83R
但是我得到以下信息:
42B
/ \
10B 64B
/ \ / \
NIL 29B 50R 83R
请参见下面的代码,remove(value)
调用removeBST(root,value)
,如果需要,该调用将调用fixDoubleBlack()
。由于这是一个冗长的代码,因此我省略了不相关的部分,但我发布了代码here。我知道这可能需要一些时间,要问的问题很多,因此,非常感谢那些烦恼的人。我肯定已经尝试调试了几年。
class redBlackTree {
constructor () {
this.root = null;
}
... ...
// ------------- RED BLACK: NODE REMoval METHODS ------------->>
remove(value) {
let node = new Node(value);
if (this.root === null) { return; }
this.root = this.removeBST(this.root,node);
}
removeBST (root,node) {//Binary Search Tree (BST) regular remove() method.
if (root === null || node === null) { return null; } //Tree is either empty or node=null
if (node.value === root.value) { //value to be removed was found
if ((root.left === null) && (root.right === null)) { //node is a leaf node.
if(root === this.root) { return null; } //node is root,just remove it.
else if (root.color === 'RED') { return null; } //node is a leaf and is RED,just remove it.
else {
//Node being removed (29) is a BLACK node w/ no children,fix double-black.
this.fixDoubleBlack(root);
root = null;
//calling inorderTraversal() here shows the correct result.
}
}
}
... another cases ...
else if (node.value < root.value) { root.left = this.removeBST(root.left,node); }
else if (node.value > root.value) { root.right = this.removeBST(root.right,node); }
return root; //I believe the problem may be in this return statement !!!
}
fixDoubleBlack(node) {
if(node === this.root) { return; }
let sibling = this.getSibling(node);
let parent = node.parent;
if(!sibling) { this.fixDoubleBlack(parent); }
else {
if(sibling.color === 'RED') {... sibling is RED ... }
else {//As sibling is BLACK,we have three cases that can be applied:
if(this.anyRedChild(sibling)){//1-: sibling has at least one RED child
if(this.isLeftChild(sibling)){//sibling is left child
if(sibling.left && sibling.left.color === 'RED'){
//DB's far nephew is RED:
this.colorSwap(parent,sibling);
this.colorSwitch(sibling.left);
this.rightRotation(parent);
parent.right = null;
//calling inorderTraversal() here shows the correct result.
}
else { ... }
}
else { ... }
}
else { ... }
}
}
}
// ---------------------- SUPPORT METHODS -------------------->>
colorSwitch(node){ ... inverts the color of the node ...}
colorSwap(node1,node2){ ... swaps the colors of the nodes ...}
getSibling(node){ ... returns the sibling of the node passed as argument ...}
isLeftChild(node){... returns a boolean if the node is a left child ... }
anyRedChild(node){... returns a boolean if the node has a RED child ...}
// --------------------- ROTATION METHODS -------------------->>
rightRotation(node) {//LL condition --> Right Rotation
let tempNode = node.left;
node.left = tempNode.right;
//Update parent reference in case of temp node having a right subtree:
if(node.left !== null) { node.left.parent = node; }
//Update temp node parent to be node's parent:
tempNode.parent = node.parent;
//Update parent references for rotated node:
if(node.parent === null) { this.root = tempNode; }
else if(node === node.parent.left) { node.parent.left = tempNode; }
else { node.parent.right = tempNode; }
tempNode.right = node;
node.parent = tempNode;
}
...
// --------------------- TRAVERSAL METHODS ------------------->>
levelOrderTraversal() {//1st level --> 2nd level --> 3rd level ...
let queue = [];
if (this.root !== null) {
queue.push(this.root);
while (queue.length > 0) {
let node = queue.shift();
console.log(node.value);
if (node.left !== null) {
queue.push(node.left);
}
if (node.right !== null) {
queue.push(node.right);
}
}
} else {
return null;
}
}
}
let rbTree = new redBlackTree();
rbTree.insert(42); rbTree.insert(10); rbTree.insert(64);
rbTree.insert(7); rbTree.insert(29); rbTree.insert(50);
rbTree.insert(83); rbTree.insert(5);
rbTree.remove(29);
rbTree.levelOrderTraversal();
如上所述,在levelOrderTraversal()
调用之后调用fixDoubleBlack()
会显示正确的结果,因此我想到它可能是removeBST
return
语句
解决方法
我认为您的问题是removeBST(root,node)
总是返回root
(可能有不同的孩子)。如果进行轮换,则应返回子树的新根。
在您的示例中它在哪里引起问题?
您用root.left = this.removeBST(root.left,node);
调用root.value = 42,root.left.value = 10.
,在左侧子树中执行您需要做的事情,然后将值为10
的节点分配为42
的左子节点
当root.right = this.removeBST(root.right,node);
为root.value
并且10
为root.right.value
时,您也呼叫29
。您正确地进行了旋转(如果您在旋转之后立即查看this.root
,则看起来不错),但是随后您将返回值为29
的节点并将其分配给root.right
!>
下次尝试。 :)
您是否需要在removeBST
之前返回任何内容?我认为您没有,因为您在旋转和其他巧妙的操作时会修改相应的节点。
因此,不要写root.left = this.removeBST(root.left,node)
,而要写this.removeBST(root.left,node)
并对正确的孩子做同样的事情。另外,只需在this.removeBST
中调用remove
,但不要重新分配this.root
。它似乎可以在该示例上工作,但是在您确实需要它的其他情况下,我可能会错过它。 (code)