UNPKG

esds

Version:

ES6 JS lightweight data structures (Priority Queue, Binary Search Tree (BST), Graph, Bloom Filters, Trie, Queue, Stack, Linked-List)

307 lines (286 loc) 9.05 kB
// Constants const IN_ORDER = "in-order"; const PRE_ORDER = "pre-order"; const POST_ORDER = "post-order"; const REV_PRE_ORDER = "reverse-in-order"; const RM_PREDECESSOR = "predecessor"; const RM_SUCCESSOR = "successor"; /** * TreeElement Class * Represents an element of the BST */ class TreeElement { /** * @param {number} key - Unique key for the element * @param {any=} [value] - Value of the element, null if not provided */ constructor(key, value = null) { this.key = key; this.value = value; this.left = null; this.right = null; } } /** * BST Class * Binary Search Tree */ class BST { /** * BST Constructor */ constructor() { this.root = null; this.nodes = 0; } /** * Clears the BST */ clear() { this.root = null; this.nodes = 0; } /** * Insert a new element in the BST * @param {number} key - Unique key for the element * @param {any} [value] - Value of the element, null if not provided * @returns {boolean} - True - If element was added * @throws {Error} - If Element already exists in the BST */ add(key, value = null) { const newNode = new TreeElement(key, value); // If tree is empty, sets new element as tree root if (!this.root) { this.root = newNode; this.nodes++; return true; } const addNode = (current, newNode) => { if (current.key > newNode.key) { if (current.left) addNode(current.left, newNode); else { current.left = newNode; this.nodes++; } } else if (current.key < newNode.key) { if (current.right) addNode(current.right, newNode); else { current.right = newNode; this.nodes++; } } else throw new Error("Node already exists"); }; addNode(this.root, newNode); } /** * Removes an existing element in the BST * @param {number} key - Unique key for the element * @param {string} [type=predecessor] - Specifies delete type, allows 'predecessor' (default) for in-order predecessor, and 'successor' for in-order successor * @returns {boolean} - True - If element was added; False if element does not exists * @throws {Error} - If delete type is invalid */ remove(key, type = RM_PREDECESSOR) { let parent = null; let current = this.root; while (current && current.key !== key) { parent = current; if (current.key > key) current = current.left; else current = current.right; } if (!current) return false; // Check children if (!current.left && !current.right) { // No children if (!parent) this.root = null; else if (parent.key > key) parent.left = null; else parent.right = null; this.nodes--; } else if (current.left && !current.right) { // Left child if (!parent) this.root = current.left; else if (parent.key > key) parent.left = current.left; else parent.right = current.left; this.nodes--; } else if (current.right && !current.left) { // Right child if (!parent) this.root = current.right; else if (parent.key > key) parent.left = current.right; else parent.right = current.right; this.nodes--; } else { // Both children are present if (type === RM_PREDECESSOR) { // Get Predecessor let { node: predecessor, parent: predecessorParent } = this.#max( current.left, true ); // Swap Predecessor with Current [current.key, predecessor.key, current.value] = [ predecessor.key, current.key, predecessor.value, ]; // Rearrange children if (predecessorParent.key === predecessor.key) { current.left = predecessor.left; } else if (predecessor.left) predecessorParent.right = predecessor.left; else predecessorParent.right = null; this.nodes--; } else if (type === RM_SUCCESSOR) { // Get Successor let { node: successor, parent: successorParent } = this.#min( current.right, true ); // Swap Successor with Current [current.key, successor.key, current.value] = [ successor.key, current.key, successor.value, ]; // Rearrange children if (successorParent.key === successor.key) { current.right = successor.right; } else if (successor.right) successorParent.left = successor.right; else successorParent.left = null; this.nodes--; } else throw new Error("Invalid remove type"); } return true; } /** * Updates an existing element in the BST * @param {number} key - Unique key for the element * @param {any} [value] - Value of the element, null if not provided * @returns {boolean} - True - If element was edited * @throws {Error} - If element is not found */ update(key, value = null) { let current = this.root; while (current && current.key !== key) { if (current.key > key) current = current.left; else current = current.right; } if (current) { current.value = value; return true; } else throw new Error("Node not found"); } /** * Returns an existing element in the BST * @param {number} [key=null] - Unique key for the element, if null, return root of BST * @returns {TreeElement} - Requested element, root of key is not provided * @throws {Error} - If element is not found */ get(key = null) { if (!key && key !== 0) return this.root; // If key is not provided return BST root let current = this.root; while (current && current.key !== key) { if (current.key > key) current = current.left; else current = current.right; } if (current) return current; else throw new Error("Node not found"); } /** * Indicates if an element exists in the BST * @param {number} key - Unique key for the element * @returns {boolean} - True if element exists, false if element does not exists */ has(key) { let current = this.root; while (current && current.key !== key) { if (current.key > key) current = current.left; else current = current.right; } return current !== null; } /** * Returns an array containing the key of the elements in the BST * @param {number} key - Unique key for the element * @param {string} [traversal="in-order"] - Specifies Traversal type, allows 'in-order' (default), 'pre-order', 'post-order', and 'reverse-in-order * @returns {number[]} - Array of element keys */ toArray(traversal = IN_ORDER, node = this.root, output = []) { if (![IN_ORDER, PRE_ORDER, POST_ORDER, REV_PRE_ORDER].includes(traversal)) throw new Error("Invalid traversal"); if (!node) return null; switch (traversal) { case IN_ORDER: this.toArray(traversal, node.left, output); output.push(node.key); this.toArray(traversal, node.right, output); break; case PRE_ORDER: output.push(node.key); this.toArray(traversal, node.left, output); this.toArray(traversal, node.right, output); break; case POST_ORDER: this.toArray(traversal, node.left, output); this.toArray(traversal, node.right, output); output.push(node.key); break; case REV_PRE_ORDER: this.toArray(traversal, node.right, output); output.push(node.key); this.toArray(traversal, node.left, output); break; } return output; } // Getters /** * Returns max element in the BST * @returns {TreeElement} - Max element, null if BST is empty */ get max() { let node = this.root; while (node.right) node = node.right; return node; } /** * Returns min element in the BST * @returns {TreeElement} - Min element, null if BST is empty */ get min() { let node = this.root; while (node.left) node = node.left; return node; } /** * Returns number of element in the BST * @returns {number} - Number of elements in the BST */ get size() { return this.nodes; } /** * Indicates if the BST is empty * @returns {boolean} - True if BST is empty, false if there's at least one element present */ get isEmpty() { return this.root === null; } // Private // Return Max element from given subtree, returns parent of element if returnParent is set to True #max(node, returnParent = false) { let parent = node; while (node.right) { parent = node; node = node.right; } return returnParent ? { node, parent } : { node, parent: null }; } // Return Min element from given subtree, returns parent of element if returnParent is set to True #min(node, returnParent = false) { let parent = node; while (node.left) { parent = node; node = node.left; } return returnParent ? { node, parent } : { node, parent: null }; } } export { BST };