微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

红黑树-JavaScript中的删除方法

如何解决红黑树-JavaScript中的删除方法

请参阅下面的原始红黑树:

         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并且10root.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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。