数据结构:JavaScript实现二叉查找树

二叉树是一种特殊的树,它的子节点个数不超过两个。

二叉查找树是一种是一种特殊的二叉树,相对较小的值保存在左节点中,较大的保存在右节点中,这一特性使得查找效率大大提高。


首先我们需要的对象就是Node,它包含了左子节点,右子节点以及存储的数据,另外还有一个方法来读取数据

function Node(data, left, right){
  this.data = data;
  this.left = left;
  this.right = right;
  this.show = show;
}
function show(){
  return this.data;
}

现在可以创建一个类,用来表示二叉查找树(BST)

function BST(){
  this.root = null;
  this.insert = insert;
  //中序遍历
  this.inOrder = inOrder;
  //先序遍历
  this.preOrder = preOrder;
  //后续遍历
  this.postOrder = postOrder;
  //查找最小值
  this.getMin = getMin;
  //查找最大值
  this.getMax = getMax;
  //查找特定值
  this.find = find;
  //删除节点
  this.remove = remove;
}

接下来是插入节点的函数insert

function insert(data){
  var n = new Node(data, null, null);
  if(this.root === null){
    this.root = n;
  }else{
    var currunt = this.root;
    var parent;
    while(true){
      parent = currunt;
      if(data < currunt.data){
        currunt = currunt.left;
        if(currunt === null){
          parent.left = n;
          break;
        }
      }else{
        currunt = currunt.right;
        if(currunt === null){
          parent.right = n;
          break;
        }
      }
    }
  }
}

遍历二叉查找树

有三种遍历BST的方式:中序,先序和后序

先序遍历:首先访问根结点然后遍历左子树,最后遍历右子树。在遍历左、右子树时,仍然先访问根结点,然后遍历左子树,最后遍历右子树,如果二叉树为空则返回。

中序遍历:中序遍历首先遍历左子树,然后访问根结点,最后遍历右子树。在遍历左、右子树时,仍然先遍历左子树,再访问根结点,最后遍历右子树。

后序遍历:后序遍历首先遍历左子树,然后遍历右子树,最后访问根结点,在遍历左、右子树时,仍然先遍历左子树,然后遍历右子树,最后遍历根结点。

代码如下:

function preOrder(node){
  if(node !== null){
    console.log(node.show());
    preOrder(node.left);
    preOrder(node.right);
  }
}
function postOrder(node){
  if(node !== null){
    postOrder(node.left);
    postOrder(node.right);
    console.log(node.show());
  }
}

接下来是获取最大值和最小值

在二叉树中查找最小值时,只需一直向下寻找左子树直到跳出循环,查找最大值时也是如此

function getMin(node){
  var currunt = node;
  while (currunt.left !== null) {
    currunt = currunt.left;
  }
  return currunt.data;
}
function getMax(node){
  var currunt = node;
  while (currunt.right !== null) {
    currunt = currunt.right;
  }
  return currunt.data;
}

最后是二叉查找树的节点的删除

首先回顾一下二叉查找树的三个重要的性质

1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值

2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;

3)左、右子树也分别为二叉排序树;

考虑到如下几种情况:

1)要删除的节点没有子节点

2)要删除的节点只有左子节点

3)要删除的节点只有右子节点

4)要删除的节点既有左子节点又有右子节点

第一种情况,也是最为简单易懂的一种情况,直接将待删除节点值设置为null即可

第二、三种情况,只需将需删除节点的父节点直接指向待删除节点的子节点即可

第四种情况,可以这样处理,首先找到以待删除节点的右子节点为根的子树中的最小值节点,将这个节点的data赋值给带删除节点,然后把找到的最小值节点从树种删除

代码如下

//找到待删除节点的父节点
function find(data){
  var currunt = nums.root;
  var parent = null;
  if(currunt.data === data){
    return null;
  }
  while (currunt !== null) {
    if(currunt.data === data){
      return parent;
    }else if(data < currunt.data){
      parent = currunt;
      currunt = currunt.left;
    }else{
      parent = currunt;
      currunt = currunt.right;
    }
  }
  //return null;
}
function remove(data){
  var parent = nums.find(data);
  //判断是否是根节点
  if(parent === null){
    nums.root = null;
    return 0;
  }
  //判断是否是左子树
  var flag = true;
  if(parent.right.data === data){
    flag = false;
    currunt = parent.right;
  }else{
    currunt = parent.left;
  }
  //没有子节点
  if((currunt.left === null) && (currunt.right === null)){
    if(flag === false){
      parent.right = null;
    }else{
      parent.left = null;
    }
  }else if((currunt.left === null) && (currunt.right !== null)){
    //只有右子节点
    if(flag === false){
      parent.right = currunt.right;
    }else{
      parent.left = currunt.right;
    }
  }else if((currunt.right === null) &&(currunt.left !== null)){
    //只有左子节点
    if(flag === false){
      parent.right = currunt.left;
      }else{
      parent.left = currunt.right;
    }
  }else{
    //既有左子节点又有右子节点
    var newNode = currunt;
    currunt = currunt.right;
    while (currunt.left !== null) {
      parent = currunt;
      currunt = currunt.left;
    }
    console.log(currunt);
    newNode.data = currunt.data;
    parent.left = null;
  }
}


以下为测试代码

var nums = new BST();
nums.insert(10);
nums.insert(2);
nums.insert(58);
nums.insert(1);
nums.insert(34);
nums.insert(7);
nums.insert(4);
console.log("中序遍历:");
inOrder(nums.root);
console.log("先序遍历:");
preOrder(nums.root);
console.log("后序遍历:");
postOrder(nums.root);
console.log("查找最小值:");
console.log(nums.getMin(nums.root));
console.log("查找最大值:");
console.log(nums.getMax(nums.root));
nums.remove(7);
console.log("删除节点后:");
preOrder(nums.root);

运行结果如下

➜  winterexcise  node bst
中序遍历:
1
2
4
7
10
34
58
先序遍历:
10
2
1
7
4
58
34
后序遍历:
1
4
7
2
34
58
10
查找最小值:
1
查找最大值:
58
删除节点后:
10
2
1
4
58
34
➜  winterexcise







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值