js封装数据结构_09_二叉搜索树

一、什么是二叉搜索树

二叉查找树(英语:Binary Search Tree),也称为二叉查找树有序二叉树(ordered binary tree)或排序二叉树(sorted binary tree),是指一棵空树或者具有下列性质的二叉树

  1. 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
  2. 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
  3. 任意节点的左、右子树也分别为二叉查找树;

二、二叉搜索树的有缺点

优点:二叉查找树相比于其他数据结构的优势在于查找、插入的时间复杂度较低。为O(log n)。

缺点:对于分布不均匀的不平衡树(类似链表),查找效率从O(log n) 变为了O(N),但是可以经由树高改良后的平衡树将搜索、插入、删除的时间复杂度都维持在、 O(log n),如AVL树、红黑树等。

三、属性与方法

为了方便,数据就直接用key表示

增:

  • insert(key),插入一个值

删:

  • remove(key),从树中移除指定值

查:

  • search(key),存在指定值返回true,不存在返回false

其他:

  • inOrderTraverse,中序遍历所有节点,以字符串形式返回
  • preOrderTraverse,先序遍历所有节点,以字符串形式返回
  • postOrderTraverse,后序遍历所有节点,以字符串形式返回

  • min,返回树中最小值

  • max,返回树中最大值

这里的先序、中序、后序可以理解为,什么时候处理节点(将节点的值加入输出对象里),先处理就算先序,中间处理就是中序,最后处理就是后序,如下面就是中序的的部分代码:

    if (node != null) {
      // 处理经过的节点的左子节点(想想回溯和函数调用栈)
      resultString = this.inOraderTraverseNode(node.left, resultString);

      // 处理经过节点
      resultString += node.key + " ";

      // 处理经过的右子节点(就算节点为空也不会改变resultString)
      resultString = this.inOraderTraverseNode(node.right, resultString);
    }

四、具体实现

function BinarySearchTree() {
  function Node(key) {
    this.key = key;
    this.left = null;
    this.right = null;
  }
  //属性
  this.root = null;
  // 方法
  // 1.增
  BinarySearchTree.prototype.insert = function (key) {
    let newNode = new Node(key);
    if (this.root === null) {
      this.root = newNode;
    } else {
      this.insertNode(this.root, newNode);
    }
  };

  BinarySearchTree.prototype.insertNode = function (node, newNode) {
    if (newNode.key < node.key) {
      //向左查找
      if (node.left === null) {
        node.left = newNode;
      } else {
        this.insertNode(node.left, newNode);
      }
    } else if (node.key == newNode.key) {
      return false;
    } else {
      //向右查找
      if (node.right === null) {
        node.right = newNode;
      } else {
        this.insertNode(node.right, newNode);
      }
    }
  };

  // 删
  BinarySearchTree.prototype.remove = function (key) {
    //1.寻找要删除的节点
    let current = this.root;
    let parent = null;
    let isLeftChild = true;

    while (current.key != key) {
      parent = current;
      if (current.key > key) {
        current = current.left;
        isLeftChild = true;
      } else {
        current = current.right;
        isLeftChild = false;
      }
      // 都找完没有发现key的情况
      if (current == null) return false;
    }
    //2.删除节点
    //2.1 删除叶子节点
    if (!current.left && !current.right) {
      if (current === this.root) {
        this.root = null;
      } else {
        if (isLeftChild) {
          parent.left = null;
        } else {
          parent.right = null;
        }
      }
    }
    //2.2 有一个子节点的节点
    else if (current.right == null) {
      if (current === this.root) {
        this.root = current.left;
        current.left = null;
      } else {
        // parent?
        if (isLeftChild) {
          parent.left = current.left;
        } else {
          parent.right = current.left;
        }
      }
    } else if (current.left == null) {
      if (current == this.root) {
        this.root = current.right;
        current.right = null;
      } else {
        // parent?
        if (isLeftChild) {
          parent.left = current.right;
        } else {
          parent.right = current.right;
        }
      }
    }
    //2.3 有两个节点的节点
    else {
      // 1.获取后续节点
      let succeed = this.getSuccssor(current);

      // 2.root?
      if (current == this.root) {
        this.root = succeed;
      } else if (isLeftChild) {
        parent.left = succeed;
      } else {
        parent.right = succeed;
      }

      // 3.删除节点的左子树
      succeed.left = current.left;
    }
  };
  BinarySearchTree.prototype.getSuccssor = function (delNode) {
    // 定义变量,保存继承节点
    let successor = delNode;
    let current = delNode.right;
    let successorParent = delNode;

    // 循环查找右子树最小值
    while (current != null) {
      successorParent = successor;
      successor = current;
      current = current.left;
    }

    // 判断继承节点是否是delNode的右节点
    if (successor != delNode.right) {
      // 这儿是嫁接切除操作
      successorParent.left = successor.right;
      successor.right = delNode.right;
    }

    return successor;
  };
  // 查
  BinarySearchTree.prototype.search = function (key) {
    let node = this.root;

    while (node != null) {
      if (key < node.key) {
        node = node.left;
      } else if (key > node.key) {
        node = node.right;
      } else {
        return true;
      }
    }
    return false;
  };

  // 中序遍历
  BinarySearchTree.prototype.inOraderTraverse = function () {
    return this.inOraderTraverseNode(this.root);
  };
  BinarySearchTree.prototype.inOraderTraverseNode = function (
    node,
    resultString = "中序:"
  ) {
    if (node != null) {
      // 处理经过的节点的左子节点(想想回溯和函数调用栈)
      resultString = this.inOraderTraverseNode(node.left, resultString);

      // 处理经过节点
      resultString += node.key + " ";

      // 处理经过的右子节点(就算节点为空也不会改变resultString)
      resultString = this.inOraderTraverseNode(node.right, resultString);
    }

    return resultString;
  };
  // 先序遍历
  BinarySearchTree.prototype.preOraderTraverse = function () {
    return this.preOraderTraverseNode(this.root);
  };
  BinarySearchTree.prototype.preOraderTraverseNode = function (
    node,
    resultString = "先序:"
  ) {
    if (node != null) {
      // 处理经过节点
      resultString += node.key + " ";

      // 处理经过的节点的左子节点(想想回溯和函数调用栈)
      resultString = this.preOraderTraverseNode(node.left, resultString);

      // 处理经过的右子节点(就算节点为空也不会改变resultString)
      resultString = this.preOraderTraverseNode(node.right, resultString);
    }

    return resultString;
  };
  // 后序遍历
  BinarySearchTree.prototype.postOraderTraverse = function () {
    return this.postOraderTraverseNode(this.root);
  };
  BinarySearchTree.prototype.postOraderTraverseNode = function (
    node,
    resultString = "后序:"
  ) {
    if (node != null) {
      // 处理经过的节点的左子节点(想想回溯和函数调用栈)
      resultString = this.postOraderTraverseNode(node.left, resultString);

      // 处理经过的右子节点(就算节点为空也不会改变resultString)
      resultString = this.postOraderTraverseNode(node.right, resultString);

      // 处理经过节点
      resultString += node.key + " ";
    }

    return resultString;
  };
  // min
  BinarySearchTree.prototype.min = function () {
    if (this.root === null) return null;
    let node = this.root;
    while (node.left != null) {
      node = node.left;
    }
    return node.key;
  };

  // max
  BinarySearchTree.prototype.max = function () {
    if (this.root === null) return null;
    let node = this.root;
    while (node.right != null) {
      node = node.right;
    }
    return node.key;
  };
}

// test
let bst = new BinarySearchTree();
bst.insert(9);
bst.insert(11);
bst.insert(10);
bst.insert(8);
bst.insert(8);
bst.insert(6);
bst.insert(5);
bst.insert(12);
bst.insert(13);
bst.insert(20);
bst.insert(7);
console.log(bst.preOraderTraverse());
console.log(bst.inOraderTraverse());
console.log(bst.postOraderTraverse());

console.log(bst.max());
console.log(bst.search(21));
console.log(bst.search(20));

bst.remove(9);
console.log(bst.inOraderTraverse());

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值