galactic-bot/structure/interfaces/BinaryTree.js
2020-09-30 19:24:46 +03:00

269 lines
6.1 KiB
JavaScript

/* 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<any>} 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;