二叉搜索树

const Compare = {
  LESS_THAN: -1,
  BIGGER_THAN: 1,
};

function defaultCompare(a, b) {
  if (a === b) {
    return 0;
  }

  return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN;
}

class Node {
  constructor(key) {
    this.key = key;
    this.left = null;
    this.rigth = null;
  }
}

class BinarySearchTree {
  constructor(compareFn = defaultCompare) {
    this.compareFn = compareFn; // 用来比较节点值的函数
    this.root = null; // Node 类型的根节点
  }

  // 向二叉搜索树中插入一个键
  insert(key) {
    if (this.root === null) {
      this.root = new Node(key)
    } else {
      this.insertNode(this.root, key)
    }
  }

  insertNode(node, key) {
    // 要插入节点比当前遍历到的键小的情况,则开始再左子树中插入
    if (this.compareFn(key, node.key) === Compare.LESS_THAN) {
      if (node.left === null) {
        // 如果左子树为空,则插入到左子树否则继续遍历
        node.left = new Node(key);
      } else {
        this.insert(node.left, key);
      }
    } else {
      // 否则插入右子树
      if (node.right === null) {
        // 如果右子树为空,则插入到右子树否则继续遍历
        node.right = new Node(key);
      } else {
        this.insert(node.right, key);
      }
    }
  }

  // 层次遍历:从上到下 bfs
  levelOrder(root) {
    const queue = []; // bfs 一般是队列,因为不需要回溯
    const result = []; // 保存结果数组
    if (root !== null) {
      queue.push(root); // 把根节点装进去
    }
    while (queue.length !== 0) {
      const level = []; // 每一层的数据
      const len = queue.length; // 当前层的元素个数
      for (let i = 0; i < len; i++) {
        const currentNode = queue.shift();
        level.push(currentNode.key); // 添加当前层的键
        if (currentNode.left !== null) {
          queue.push(currentNode.left); // 添加当前层的左子树
        }

        if (currentNode.right !== null) {
          queue.push(currentNode.right) // 添加当前层的右子树
        }
      }
      result.push(level); // 把当前层的数据存到总数据中
    }
    return result; // 最后返回出去的就是遍历后的结果
  }

  // 中序遍历【最小到最大的顺序访问所有节点】
  inOrderTraverse(node) {
    if (node !== null) {
      this.inOrderTraverse(node.left);
      console.log(node.key);
      this.inOrderTraverse(node.right);
    }
  }

  // 先序遍历,先访问自身节点,再去遍历左右
  preOrderTraverse(node) {
    if (node !== null) {
      console.log(node.key);
      this.preOrderTraverse(node.left);
      this.preOrderTraverse(node.right);
    }
  }

  // 后序遍历,先访问左子树,然后再访问右子树,再去遍历自身节点
  postOrderTraverse(node) {
    if (node !== null) {
      this.postOrderTraverse(node.left);
      this.postOrderTraverse(node.right);
      console.log(node.key);
    }
  }

  // 最小值
  min() {
    return this.minNode(this.root)
  }

  minNode(node) { // 一直往左寻找最小值
    let current = node;
    while (current !== null && current.left !== null) {
      current = current.left
    }

    return current;
  }

  // 最大值
  max() {
    return this.maxNode(this.root);
  }

  maxNode(node) { // 一直往右寻找最大值
    let current = node;
    while (current !== null && current.right !== null) {
      current = current.right
    }

    return current;
  }

  // 搜索特定的值
  search(key) {
    return this.searchNode(this.root, key);
  }

  searchNode(node, key) {
    if (node === null) {
      return false;
    }

    // 如果要找的比当前遍历到的要小,就去找左子树
    if (this.compareFn(key, node.key) === Compare.LESS_THAN) {
      return this.searchNode(node.left, key);
    } else if (this.compareFn(key, node) === Compare.BIGGER_THAN) {
      // 如果要找的比当前节点遍历的大,就去右子树找
      return this.searchNode(node.right, key);
    } else {
      return true;
    }
  }

  // 删除一个节点
  remove(key) {
    this.root = this.removeNode(this.root, key);
  }

  removeNode(node, key) {
    if (node === null) {
      // 如果正在检测的节点为 null,那么说明键不存在与树中,所以返回null,
      return null;
    }
    if (this.compareFn(key, node.key) === Compare.LESS_THAN) {
      // 如果要找的节点比当前节点的值小,就沿着树的左边即左子树找到下一个节点
      node.left = this.removeNode(node.left, key);
      return node;
    } else if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) {
      // 如果要找的键比当前节点的值大,那么久沿着右边的即右子树找到下一个节点
      node.right = this.removeNode(node.right, key);
      return node;
    } else {
      // 如果我们找到了要找的键(键和 node.key 相等),就需要处理三种不同的情况
      // 第一种情况,移除一个叶子节点
      if (node.left === null && node.right === null) {
        node = null;
        return node;
      }
      // 第二种情况:移除一个有左节点或右节点的节点
      if (node.left === null) {
        node = node.right;
        return node;
      } else if (node.right === null) {
        node = node.left;
        return node;
      }
      // 第三种情况:移除有两个子节点的节点
      // 当找到要移除的节点后,需要找到它右子树中的最小节点(当然也可以找到左子树中的最大节点)
      const aux = this.minNode(node.right);
      // 然后用它右子树中最小节点的值去更新这个节点的值。通过这一步,我们改变了节点的键,也就是说它被移除了
      node.key = aux.key;
      // 但是这样再树中就有两个相同的键的节点了,这是不行的。要继续把右子树的最小节点移除,毕竟它已经被移至要删除节点的位置了
      node.right = this.removeNode(node.right, aux.key);
      // 最后,向它的父节点返回更新后节点的引用
      return node;
    }
  }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值