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;
}
}
}
二叉搜索树
最新推荐文章于 2024-04-30 20:06:58 发布