diff --git a/structure/interfaces/BinaryTree.js b/structure/interfaces/BinaryTree.js new file mode 100644 index 0000000..464e3f4 --- /dev/null +++ b/structure/interfaces/BinaryTree.js @@ -0,0 +1,269 @@ +/* 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; \ No newline at end of file