lgrthms
Version:
Algorithms and data structures for your JavaScript and TypeScript projects 🧑💻
246 lines (245 loc) • 7.46 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BinarySearchTree = void 0;
const Comparator_1 = require("../utils/Comparator");
class Node {
constructor(value) {
this.parent = null;
this.left = null;
this.right = null;
this.value = value;
}
}
class BinarySearchTree {
constructor(compareFn) {
this._root = null;
this._size = 0;
this.comparator = new Comparator_1.Comparator(compareFn);
}
get root() {
return this._root;
}
get size() {
return this._size;
}
// Average: O(log(n)) time | O(1) space
// Worst: O(n) time | O(1) space
insert(value) {
const nodeToInsert = new Node(value);
if (!this.root) {
this._root = nodeToInsert;
this._size++;
return nodeToInsert;
}
let current = this._root;
while (current) {
if (this.comparator.isLessThan(value, current.value)) {
if (current.left) {
current = current.left;
continue;
}
else {
current.left = nodeToInsert;
nodeToInsert.parent = current;
this._size++;
break;
}
}
else {
if (current.right) {
current = current.right;
continue;
}
else {
current.right = nodeToInsert;
nodeToInsert.parent = current;
this._size++;
break;
}
}
}
return nodeToInsert;
}
// Average: O(log(n)) time | O(1) space
// Worst: O(n) time | O(1) space
remove(node, root = this._root) {
if (!root || !this.contains(node, root)) {
return;
}
if (node.left && node.right) {
this.removeNodeWithTwoChildren(node);
}
else if (!node.parent) {
this.removeRoot(node);
}
else {
this.removeSingleChildOrLeafNode(node);
}
this._size--;
}
// Best: O(log(n)) time | O(1) space
// Average: O(log(n)^2) time | O(1) space
// Worst: O(n^2) time | O(1) space
removeWithValue(target, predicate) {
let nodeToRemove = this.findNode(target, predicate);
while (nodeToRemove) {
this.remove(nodeToRemove, nodeToRemove);
nodeToRemove = this.findNode(target, predicate);
}
}
// Average: O(log(n)) time | O(1) space
// Worst: O(n) time | O(1) space
contains(node, root = this._root) {
let current = root;
while (current) {
if (current === node) {
return true;
}
if (this.comparator.isLessThan(node.value, current.value)) {
current = current.left;
}
else {
current = current.right;
}
}
return false;
}
// Average: O(log(n)) time | O(1) space
// Worst: O(n) time | O(1) space
findNode(target, predicate) {
const isDesiredValue = predicate
? predicate
: (value) => value === target;
let current = this._root;
while (current) {
if (isDesiredValue(current.value)) {
return current;
}
if (this.comparator.isLessThan(target, current.value)) {
current = current.left;
}
else {
current = current.right;
}
}
}
// Average: O(log(n)) time | O(1) space
// Worst: O(n) time | O(1) space
find(target, predicate) {
const node = this.findNode(target, predicate);
return node ? node.value : undefined;
}
// Average: O(log(n)) time | O(1) space
// Worst: O(n) time | O(1) space
findMinNode(root = this._root) {
if (!root) {
return;
}
let current = root;
while (current.left) {
current = current.left;
}
return current;
}
// Average: O(log(n)) time | O(1) space
// Worst: O(n) time | O(1) space
findMin(root = this._root) {
const node = this.findMinNode(root);
return node ? node.value : undefined;
}
// Average: O(log(n)) time | O(1) space
// Worst: O(n) time | O(1) space
findMaxNode(root = this._root) {
if (!root) {
return;
}
let current = root;
while (current.right) {
current = current.right;
}
return current;
}
// Average: O(log(n)) time | O(1) space
// Worst: O(n) time | O(1) space
findMax(root = this._root) {
const node = this.findMaxNode(root);
return node ? node.value : undefined;
}
// O(n) time | O(h) space
traverseInOrder(callbackfn, node = this._root) {
if (node) {
this.traverseInOrder(callbackfn, node.left);
callbackfn(node.value);
this.traverseInOrder(callbackfn, node.right);
}
}
// O(n) time | O(h) space
traversePreOrder(callbackfn, node = this._root) {
if (node) {
callbackfn(node.value);
this.traversePreOrder(callbackfn, node.left);
this.traversePreOrder(callbackfn, node.right);
}
}
// O(n) time | O(h) space
traversePostOrder(callbackfn, node = this._root) {
if (node) {
this.traversePostOrder(callbackfn, node.left);
this.traversePostOrder(callbackfn, node.right);
callbackfn(node.value);
}
}
// Average: O(log(n)) time | O(1) space
// Worst: O(n) time | O(1) space
removeNodeWithTwoChildren(node) {
const nodeToReplaceWith = this.findMinNode(node.right);
node.value = nodeToReplaceWith.value;
this.removeSingleChildOrLeafNode(nodeToReplaceWith);
}
// O(1) time | O(1) space
removeRoot(node) {
if (node.left) {
node.value = node.left.value;
node.right = node.left.right;
node.left = node.left.left;
}
else if (node.right) {
node.value = node.right.value;
node.left = node.right.left;
node.right = node.right.right;
}
else {
this._root = null;
}
}
// O(1) time | O(1) space
removeSingleChildOrLeafNode(node) {
if (node.parent.left === node) {
if (node.left) {
node.parent.left = node.left;
node.left.parent = node.parent;
}
else if (node.right) {
node.parent.left = node.right;
node.right.parent = node.parent;
}
else {
node.parent.left = null;
}
}
if (node.parent.right === node) {
if (node.left) {
node.parent.right = node.left;
node.left.parent = node.parent;
}
else if (node.right) {
node.parent.right = node.right;
node.right.parent = node.parent;
}
else {
node.parent.right = null;
}
}
}
}
exports.BinarySearchTree = BinarySearchTree;