Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
二进制搜索树JavaScript实现-删除函数_Javascript_Algorithm_Data Structures_Binary Search Tree - Fatal编程技术网

二进制搜索树JavaScript实现-删除函数

二进制搜索树JavaScript实现-删除函数,javascript,algorithm,data-structures,binary-search-tree,Javascript,Algorithm,Data Structures,Binary Search Tree,下面是我用JavaScript实现的二进制搜索树。除remove功能外,所有功能均正常工作。具体来说,它似乎正在正确删除节点,直到树中剩下2个节点: var binaryTreeNode = function (value) { return { value : value, left : null, right : null }; }; var binarySearchTree = function () { var tree = Object.cre

下面是我用JavaScript实现的二进制搜索树。除
remove
功能外,所有功能均正常工作。具体来说,它似乎正在正确删除节点,直到树中剩下2个节点:

var binaryTreeNode = function (value) {
  return {
    value : value,
    left  : null,
    right : null
  };
};

var binarySearchTree = function () {
  var tree  = Object.create( binarySearchTreeMethods );
  tree.root = null;
  return tree;
};

var binarySearchTreeMethods = {

  insert: function (value, node) {
    var newNode = binaryTreeNode( value );

    // check if tree is empty
    if ( this.isEmpty() ) {
      this.root = newNode;
      return;
    }

    // initialize node
    if ( node === void 0 ) node = this.root;

    // compare value with node.value
    if ( value <= node.value ) {
      // check if left exists
      if ( node.left ) {
        this.insert( value, node.left );
      } else {
        node.left = newNode;
      }
    } else {
      if ( node.right ) {
        this.insert( value, node.right );
      } else {
        node.right = newNode;
      }
    }
  },

  remove: function (value, node) {
    var nextRightValue, nextLeftValue, minRight;

    if ( !this.isEmpty() ) {
      // initialize node
      if ( node === void 0 ) node = this.root;

      // compare the node's value with the value
      if ( value < node.value ) {
        // check if there is a left node
        if ( node.left ) {
          node.left = this.remove( value, node.left );
        }
      } else if ( value > node.value ) {
        // check if there is a right node
        if ( node.right ) {
          node.right = this.remove( value, node.right );
        }
      } else {
        // at this point, value === node.value
        // check if node is a leaf node
        if ( node.left === null && node.right === null ) {
          // edge case of single node in tree (i.e. root node)
          if ( this.getHeight() === 0 ) {
            this.root = null;
            return this.root;
          } else {
            node = null;
          }
        } else if ( node.left === null ) {
          node = node.right;
        } else if ( node.right === null ) {
          node = node.left;
        } else {
          // node has both left and right
          minRight   = this.findMinValue( node.right );
          node.value = minRight;
          node.right = this.remove( minRight, node.right );
        }
      }
      return node;
    }
  },

  contains: function (value, node) {
    if ( this.isEmpty() ) return false;
    // tree is not empty - initialize node
    if ( node === void 0 ) node = this.root;

    // check if node's value is the value
    if ( value === node.value ) return true;
    if ( value < node.value ) {
      // check if left node exists
      return node.left ? this.contains( value, node.left ) : false;
    } else {
      // check if right node exists
      return node.right ? this.contains( value, node.right ) : false;
    }
  },

  findMaxValue: function (node) {
    if ( !this.isEmpty() ) {
      if ( node === void 0 ) node = this.root;
      while ( node.right ) {
        node = node.right;
      }
      return node.value;
    }
  },

  findMinValue: function (node) {
    if ( !this.isEmpty() ) {
      if ( node === void 0 ) node = this.root;
      while ( node.left ) {
        node = node.left;
      }
      return node.value;
    }
  },

  getHeight: function (node) {
    if ( !this.isEmpty() ) {
      // initialize node
      if ( node === void 0 ) node = this.root;

      // base case
      if ( node.left  === null && node.right === null ) return 0;
      if ( node.left  === null ) return 1 + this.getHeight( node.right );
      if ( node.right === null ) return 1 + this.getHeight( node.left );
      return 1 + Math.max( this.getHeight( node.left ), this.getHeight( node.right ) );
    }
  },

  isEmpty: function () {
    return this.root === null;
  }

};
每次开始删除
根目录时,我都会遇到一个问题:

bst.remove(10); // this works fine and the resulting bst tree is structurally correct
bst.remove(18); // this works fine and the resulting bst tree is structurally correct
bst.remove(20); // this works fine and the resulting bst tree is structurally correct
bst.remove(22); // this works fine and the resulting bst tree is structurally correct
bst.remove(30); // THIS IS WHERE THE ISSUE OCCURS
在删除30之前,树只有两个值:30作为根值,5作为root.left值。我希望去掉30根会得到一棵树,它的根是5。然而,移除30对树没有任何作用;它保持不变

进一步的测试表明,如果我先删除了5个,然后删除了30个,那么一切都可以正常工作:

bst.remove(10); // this works fine and the resulting bst tree is structurally correct
bst.remove(18); // this works fine and the resulting bst tree is structurally correct
bst.remove(20); // this works fine and the resulting bst tree is structurally correct
bst.remove(22); // this works fine and the resulting bst tree is structurally correct
bst.remove(5);  // Results in a tree with 30 as the root value
bst.remove(30); // Results in the empty tree where root === null

有人能帮我理解为什么最初删除30不起作用吗?

您的代码有一个规定,当找到的节点是根节点并且是树中唯一的节点时,如果一个节点同时具有左和右子节点,则覆盖其值。但是,当要删除的节点是根节点并且只有一个子节点时,代码中没有任何内容会覆盖
this.root
,并且不会覆盖根节点的值,因此不会删除它,树也不会被修改

您可以通过更改以下内容来解决此问题:

if ( node === void 0 ) node = this.root;

// compare the node's value with the value
if ( value < node.value ) {
二进制树节点中

删除:函数(值){
if(值<此值){
this.left=this.left&&this.left.remove(值);
}else if(值>此.value){
this.right=this.right&&this.right.remove(值);
}else if(this.left&&this.right){
this.value=this.right.findMinValue();
this.right=this.right.remove(this.value);
}否则{
把这个.左| |这个.右;
}
归还这个;
},
findMinValue:函数(){
返回this.left?this.left.findMinValue():this.value;
}

这里是具有插入和删除功能的二叉树的完整示例

功能节点(val){
这个数据=val;
this.right=null;
this.left=null;
}
函数BST(){
this.root=null;
this.insert=插入;
this.inoorder=inoorder;
这个。移除=移除;
this.removeNode=removeNode;
this.kthSmallestNode=kthSmallestNode;
}
函数插入(val){
if(val==null | | val==未定义)
返回;
if(this.root==null){
this.root=新节点(val);
返回;
}
var current=this.root
var newNode=新节点(val);
while(true){
if(val<当前数据){
if(current.left==null){
current.left=newNode;
返回;
}
current=current.left;
}否则{
if(current.right==null){
current.right=newNode;
返回;
}
current=current.right;
}
}
}
功能删除(val){
this.root=removeNode(this.root,val);
}
函数removeNode(当前,值){
如果(值==null | |值==未定义)
返回;
如果(值==当前数据){
if(current.left==null&¤t.right==null){
返回null;
}else if(current.left==null)
返回当前值。右;
else if(current.right==null)
返回当前值。左;
否则{
var tempNode=kthSmallestNode(current.right);
current.data=tempNode.data;
current.right=removeNode(current.right,tempNode.data);
回流;
}
}else if(值<当前数据){
current.left=removeNode(current.left,值);
回流;
}否则{
current.right=removeNode(current.right,value);
回流;
}
}
函数kthSmallestNode(节点){
而(!(node.left==null))
node=node.left;
返回节点;
}
函数顺序(节点){
如果(!(节点==null)){
顺序(node.left);
console.log(node.data+“”);
顺序(node.right);
}
}
var tree=new BST();
插入(25);
插入(20);
插入(30);
插入(27);
插入(21);
插入(16);
插入(26);
插入(35);
树。移除(30)
日志(“顺序:”)
log(tree.inoorder(tree.root))

祝你好运

我有一个非常简单的答案,我想大多数人都会理解,它考虑了子节点。关键是,如果要删除具有左右子级的值,则首先要向左,然后一直向右,因为这样可以确保它不会有子级,并且更易于更新

  removeNode(val) {
    let currentNode, parentNode, nextBiggestParentNode=null, found=false, base=[this.root];
    while(base.length > 0 && !found) {
      currentNode = base.pop();
      if(currentNode.value === val) {
        found=true;
        if(!currentNode.left && !currentNode.right) {
          parentNode.right === currentNode ? parentNode.right = null : parentNode.left = null;
        }
        else if(!currentNode.right && currentNode.left) {
          parentNode.right === currentNode ? parentNode.right = currentNode.left : parentNode.left = currentNode.left;
        }
        else if(!currentNode.left && currentNode.right) {
          parentNode.right === currentNode ? parentNode.right = currentNode.right : parentNode.left = currentNode.right;
        }
        else {
          let _traverse = node => {
            if (node.right) {
              nextBiggestParentNode = node;
              _traverse(node.right);
            }
            else {
              currentNode.value = node.value;
              nextBiggestParentNode ? nextBiggestParentNode.right = null : currentNode.left = null;
            }
          }
          _traverse(currentNode.left);
        }
      }
      else {
        parentNode = currentNode;
        val > currentNode.value && currentNode.right ? base.unshift(currentNode.right) : base.unshift(currentNode.left);
      }
    }
    return this;
  }
该代码是类的一部分,如果有人感兴趣,下面是我的构造函数代码的其余部分

let TreeNode = class  {
  constructor(value, left=null, right=null) {
    this.value = value;
    this.left = left;
    this.right = right;
  }
}

let BST = class {
  constructor(root=null) {
    this.root = root;
  }

  insert(nodeToInsert) {
    if (this.root === null) {
      this.root = nodeToInsert;
    } else {
      this._insert(this.root, nodeToInsert);
    }
  }

  _insert(root, nodeToInsert) {
    if (nodeToInsert.value < root.value) {
      if (!root.left) {
        root.left = nodeToInsert;
      } else {
        this._insert(root.left, nodeToInsert);
      }
    } else {
      if (!root.right) {
        root.right = nodeToInsert;
      } else {
        this._insert(root.right, nodeToInsert);
      }
    }
  }

考虑不要在“代码>左”中初始化“左”和“右”:null < /代码>,因此在插入时,而不是<代码>(No.Lead) >如果“(左)在节点中”<代码>,否则将具有<代码>,否则,如果值为0,则可能会得到奇怪的结果。删除时相同:如果没有剩余,请删除属性。不确定
节点===void 0
,为什么不
节点===undefined
?好的,需要再输入3个字符,但更具表现力。@RobG即使
node.left.value
为0,如果存在
node.left
,则
node.left
将始终是真实的。我还想说,让BST删除其节点的
。left
。right
属性以清除它们在OOP设计中是非常奇怪的。世界上大多数的BST都不这样做(大多数语言甚至不允许您这样做)。@JLRishe那么,如果
node.left
为空,为什么返回false?还是未定义?还是假的?看见Javascript不是“大多数语言”。如果没有左节点,为什么要有左属性?对我来说有道理。使用null表示没有左节点意味着树不能存储null值。我不明白
remove: function (value, node) {
    if (!this.isEmpty()) {
        // initialize node
        if (!node) {
            this.root = this.remove(value, this.root);
        } else if (value < node.value && node.left) {
            node.left = this.remove(value, node.left);
        } else if (value > node.value && node.right) {
            node.right = this.remove(value, node.right);
        } else if (value === node.value) {
            // check if node is a leaf node
            if (node.left && node.right) {
                // node has two children. change its value to the min
                // right value and remove the min right node
                node.value = this.findMinValue(node.right);
                node.right = this.remove(node.value, node.right);
            } else {
                // replace the node with whichever child it has
                node = node.left || node.right;
            }
        }
        return node;
    }
},
remove: function (value) {
    this.root = this._removeInner(value, this.root);
},

_removeInner: function (value, node) {
    if (node) {
        if (value < node.value) {
            node.left = this._removeInner(value, node.left);
        } else if (value > node.value) {
            node.right = this._removeInner(value, node.right);
        } else if (node.left && node.right) {
            node.value = this.findMinValue(node.right);
            node.right = this._removeInner(node.value, node.right);
        } else {
            node = node.left || node.right;
        }
    }
    return node;
},
remove: function (value) {
    this.root && this.root.remove(value);
},
remove: function (value) {
    if (value < this.value) {
        this.left = this.left && this.left.remove(value);
    } else if (value > this.value) {
        this.right = this.right && this.right.remove(value);
    } else if (this.left && this.right) {
        this.value = this.right.findMinValue();
        this.right = this.right.remove(this.value);
    } else {
        return this.left || this.right;
    }
    return this;
},

findMinValue: function () {
    return this.left ? this.left.findMinValue() : this.value;
}
  removeNode(val) {
    let currentNode, parentNode, nextBiggestParentNode=null, found=false, base=[this.root];
    while(base.length > 0 && !found) {
      currentNode = base.pop();
      if(currentNode.value === val) {
        found=true;
        if(!currentNode.left && !currentNode.right) {
          parentNode.right === currentNode ? parentNode.right = null : parentNode.left = null;
        }
        else if(!currentNode.right && currentNode.left) {
          parentNode.right === currentNode ? parentNode.right = currentNode.left : parentNode.left = currentNode.left;
        }
        else if(!currentNode.left && currentNode.right) {
          parentNode.right === currentNode ? parentNode.right = currentNode.right : parentNode.left = currentNode.right;
        }
        else {
          let _traverse = node => {
            if (node.right) {
              nextBiggestParentNode = node;
              _traverse(node.right);
            }
            else {
              currentNode.value = node.value;
              nextBiggestParentNode ? nextBiggestParentNode.right = null : currentNode.left = null;
            }
          }
          _traverse(currentNode.left);
        }
      }
      else {
        parentNode = currentNode;
        val > currentNode.value && currentNode.right ? base.unshift(currentNode.right) : base.unshift(currentNode.left);
      }
    }
    return this;
  }
let TreeNode = class  {
  constructor(value, left=null, right=null) {
    this.value = value;
    this.left = left;
    this.right = right;
  }
}

let BST = class {
  constructor(root=null) {
    this.root = root;
  }

  insert(nodeToInsert) {
    if (this.root === null) {
      this.root = nodeToInsert;
    } else {
      this._insert(this.root, nodeToInsert);
    }
  }

  _insert(root, nodeToInsert) {
    if (nodeToInsert.value < root.value) {
      if (!root.left) {
        root.left = nodeToInsert;
      } else {
        this._insert(root.left, nodeToInsert);
      }
    } else {
      if (!root.right) {
        root.right = nodeToInsert;
      } else {
        this._insert(root.right, nodeToInsert);
      }
    }
  }
let bst = new BST();
const nums = [20,10,5,15,3,7,13,17,30,35,25,23,27,37,36,38];
function createBst() {
  for (let i of nums) {
    bst.insert(new TreeNode(i));
  }
  console.log(JSON.stringify(bst, null, 2));
  bst.removeNode(35);
}
createBst();
console.log(JSON.stringify(bst, null, 2));