/* eslint-disable max-classes-per-file */ class Node { constructor(data) { this.info = data; this.left = null; this.right = null; } } class BinaryTree { /** * Creates an instance of BinaryTree. * All of the stored values will be treated as strings. * @memberof BinaryTree */ constructor(client, values) { this.root = null; this.size = 0; this.curr = null; this.parent = null; this.client = client; if (values) this.populate(values); } /** * Populates the tree with the values, skips duplicates * * @param {Array} values * @return {BinaryTree} * @memberof BinaryTree */ populate(values) { if(!(values instanceof Array)) throw new Error('Values must be an array'); values.forEach(this.add.bind(this)); return this; } /** * Check whether or not the tree is empty * @returns {boolean} * @memberof BinaryTree */ isEmpty() { return this.root === null; } /** * Find a stored value from the tree * * @param {string} val The string to search for * @returns {boolean} Returns true if the item is found, leaves the cursor on the found item, returns false and puts cursor to null if not found */ find(val) { val.toLowerCase(); if(this.isEmpty()) return; this.curr = this.root; this.parent = null; while(this.curr !== null && this.curr.info !== val) { if(this.compare(val, this.curr.info) > 0) { this.parent = this.curr; this.curr = this.curr.right; } else { this.parent = this.curr; this.curr = this.curr.left; } } if(this.curr === null) return false; return true; } /** * Find and retrieve the item if found. * * @param {string} val The string to search for. * @returns {string} If found returns the item, if not returns undefined. */ retrieveNode(val) { if(this.find(val)) return this.curr.info; return null; } /** * Insert a value to the tree * * @param {string} val The value to be inserted */ add(val) { if(this.find(val)) return false; //no dupes this.size++; const newNode = new Node(val); if(this.parent === null) this.root = newNode; else{ if(this.compare(val, this.parent.info) < 0) this.parent.left = newNode; else this.parent.right = newNode; this.curr = newNode; } return true; } /** * Delete the specified value from the tree. * * @param {string} val The value to be searched for and deleted */ remove(val) { if(!this.find(val)) return; this.size--; if(this.curr.left === null && this.curr.right === null) { if(this.parent === null) this.root = null; else { if(this.curr === this.parent.left) this.parent.left = null; else this.parent.right = null; } } else { if(this.curr.left !== null && this.curr.right !== null) { //node has 2 children let temp = this.curr; let replacement = this.curr.left; while(replacement.right !== null) { temp = replacement; replacement = replacement.right; //choose the right one to get the bigger value as replacement } this.curr.info = replacement.info; if(this.curr === temp) { this.curr.left = replacement.left; replacement = null; } else { temp.right = replacement.left; replacement = null; } // end of 2 children } else { //node with 1 child if(this.parent === null) { //root node if(this.curr.left !== null) this.root = this.curr.left; else this.root = this.curr.right; } else { //non-root node if(this.curr === this.parent.left) { //delete left child if(this.curr.right === null) this.parent.left = this.curr.left; else this.parent.left = this.curr.right; } else { //delete right child if (this.curr.right === null) this.parent.right = this.curr.left; else this.parent.right = this.curr.right; } } }// end of node with 1 child this.curr = null; } } /** * Compares 2 strings by comparing their character codes at i. * * @param {string} val1 * @param {string} val2 * @returns {number} 1 if val1 is greater, -1 if val2 is greater and 0 if they are equal. */ compare(val1, val2) { const len = val1.length >= val2.length ? val1.length : val2.length; for(let i = 0; i < len; i++) { if(val1.charCodeAt(i) === val2.charCodeAt(i)) continue; if(isNaN(val1.charCodeAt(i))) return -1; if(isNaN(val2.charCodeAt(i))) return 1; if(val1.charCodeAt(i) > val2.charCodeAt(i)) return 1; return -1; } return 0; } inOrder(root = this.root) { if(root !== null) { this.inOrder(root.left); this.client.logger.info(root.info); this.inOrder(root.right); } } preOrder(root = this.root) { if(root !== null) { this.client.logger.info(root.info); this.preOrder(root.left); this.preOrder(root.right); } } postOrder(root = this.root) { if(root !== null) { this.postOrder(root.left); this.postOrder(root.right); this.client.logger.info(root.info); } } } module.exports = BinaryTree;