js算法之旅:二叉搜索树实现

什么是二叉树

二叉树中的节点最多只能有两个子节点:一个是左侧子节点,另一个是右侧子节点。这些定义有助于我们写出更高效的向/从树中插入、查找和删除节点的算法。二叉树在计算机科学中的应用非常广泛。

二叉搜索树(BST)是二叉树的一种,但是它只允许你在左侧节点存储(比父节点)小的值,
在右侧节点存储(比父节点)大(或者等于)的值。

要实现的方法

  • insert(key):向树中插入一个新的键。
  • search(key):在树中查找一个键,如果节点存在,则返回 true;如果不存在,则返回 false。
  • inOrderTraverse:通过中序遍历方式遍历所有节点。
  • preOrderTraverse:通过先序遍历方式遍历所有节点。
  • postOrderTraverse:通过后序遍历方式遍历所有节点。
  • min:返回树中最小的值/键。
  • max:返回树中最大的值/键。
  • remove(key):从树中移除某个键。

实现代码

// 节点类
class Node {
  constructor(key) {
    this.key = key;
    this.left = null;
    this.right = null;
  }
}

// 二叉搜索树
class BinarySearchTree {
  constructor() {
    this.root = null;
  }
  // 新增节点
  insert(key) {
    var newNode = new Node(key); //{1}
    if (this.root === null) {
      //{2}
      this.root = newNode;
    } else {
      insertNode(this.root, newNode); //{3}
    }

    function insertNode(node, newNode) {
      if (newNode.key < node.key) {
        //{4}
        if (node.left === null) {
          //{5}
          node.left = newNode; //{6}
        } else {
          insertNode(node.left, newNode); //{7}
        }
      } else {
        if (node.right === null) {
          //{8}
          node.right = newNode; //{9}
        } else {
          insertNode(node.right, newNode); //{10}
        }
      }
    }
  }
  // 中序遍历(从小到大)
  inOrderTraverse(callback) {
    inOrderTraverseNode(this.root, callback); //{1}

    function inOrderTraverseNode(node, callback) {
      // 因为是同步执行,所以虽然看起来第一次进来的时候就执行了{{4}}但其实{{3}}会分解成无数个callback,然后按照二叉搜索树的特性,会从小到大依次打印
      if (node !== null) {
        //{2}
        inOrderTraverseNode(node.left, callback); //{3}
        callback(node.key); //{4}
        inOrderTraverseNode(node.right, callback); //{5}
      }
    }
  }
  // 先序遍历(先输出最下面的子节点,子节点全部输出完之后才会输出父节点)
  postOrderTraverse(callback) {
    postOrderTraverseNode(root, callback);
    function postOrderTraverseNode(node, callback) {
      if (node !== null) {
        postOrderTraverseNode(node.left, callback); //{1}
        postOrderTraverseNode(node.right, callback); //{2}
        callback(node.key); //{3}
      }
    }
  }
  // 搜索最小值
  minNode(node) {
    if (node) {
      while (node && node.left !== null) {
        //{2}
        node = node.left; //{3}
      }
      return node.key;
    }
    return null; //{4}
  }
  // 搜索最大值
  maxNode(node) {
    if (node) {
      while (node && node.right !== null) {
        //{2}
        node = node.right; //{3}
      }
      return node.key;
    }
    return null; //{4}
  }
  // 搜索值是否存在
  search(key) {
    return searchNode(root, key); //{1}
    function searchNode(node, key) {
      if (node === null) {
        //{2}
        return false;
      }
      if (key < node.key) {
        //{3}
        return searchNode(node.left, key); //{4}
      } else if (key > node.key) {
        //{5}
        return searchNode(node.right, key); //{6}
      } else {
        return true; //{7}
      }
    }
  }
  removeNode(node, key) {
    if (node === null) {
      //{2}
      return null;
    }
    if (key < node.key) {
      //{3}
      node.left = removeNode(node.left, key); //{4}
      return node; //{5}
    } else if (key > node.key) {
      //{6}
      node.right = removeNode(node.right, key); //{7}
      return node; //{8}
    } else {
      //键等于node.key
      //第一种情况——一个叶节点
      if (node.left === null && node.right === null) {
        //{9}
        node = null; //{10}
        return node; //{11}
      }
      //第二种情况——一个只有一个子节点的节点
      if (node.left === null) {
        //{12}
        node = node.right; //{13}
        return node; //{14}
      } else if (node.right === null) {
        //{15}
        node = node.left; //{16}
        return node; //{17}
      }
      //第三种情况——一个有两个子节点的节点
      var aux = findMinNode(node.right); //{18}
      node.key = aux.key; //{19}
      node.right = removeNode(node.right, aux.key); //{20}
      return node; //{21}
    }

    function findMinNode(node) {
      while (node && node.left !== null) {
        node = node.left;
      }
      return node;
    }
  }
}

var tree = new BinarySearchTree();
tree.insert(11);
tree.insert(7);
tree.insert(15);
tree.insert(5);
tree.insert(3);
tree.insert(9);
tree.insert(8);
tree.insert(10);
tree.insert(13);
tree.insert(12);
tree.insert(14);
tree.insert(20);
tree.insert(18);
tree.insert(25);

tree.inOrderTraverse(console.log);
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

易风有点疯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值