Solution
// https://www.cs.usfca.edu/~galles/visualization/BST.html
class BinaryNode {
constructor(data) {
// a node has data, left, and right pointers
this.data = data;
// left and right are initialized as null
this.left = null;
this.right = null;
}
}
class BinaryTree {
constructor(){
// when a new Tree is made, it has a root property
this.root = null;
}
insert(data) {
// might as well create the node if we are going to add it anyways
let node = new BinaryNode(data);
// if we don't have a node at this.root, we can assign it and call it a day
if (!this.root) return this.root = node;
// otherwise, it's time to traverse
let walker = this.root;
// we need to somehow keep on iterating, we could use a while loop or recursion
// we chose while this time
while (true) {
// if the node with the data already exists, return out from the function
if (walker.data === data) return;
// remember how you can access object properties with expressions?
let direction = data <= walker.data ? 'left' : 'right';
// if the place we are looking for is empty, mission accomplished
if (!walker[direction]) return walker[direction] = node;
// otherwise, shift down walker in the right direction
walker = walker[direction];
}
}
search(val){
// search the Tree for a node with the given value
// if the node exists, return it
// if the node doesn't exist, return false
// if there is no this.root, you should auto return false;
if (!this.root) return false;
// otherwise, we have to traverse the list until we get to the value, or we get to the end;
// as usual, start walker at the top
let walker = this.root;
// this time, we are going to go until either walker gets to the value, or reaches null
while (walker) {
// if walker hits the value, that means we found it!
if (walker.data === val) return true;
// let's use that handy ternary operator again to say which way to go
walker = walker[val <= walker.data ? 'left' : 'right'];
}
// if walker didn't hit the value before it got to the end, then the value is not there
return false;
}
size(node){
// calculate the number of nodes in the tree, starting from the given node
// notice, you are not guaranteed to start at the root of the tree
// we need to count the size, so initialize a counter to zero
let count = 0;
// set up a recursive function that adds the values by looking left and right
const counter = node => {
// only count if the value is not null
if (node) {
// add
count++;
// check to the left to the left (lol Beyonce)
counter(node.left);
// check to the right
counter(node.right);
}
}
// we need to actually invoke our little function
counter(node);
// then return count when it's all over
return count;
}
getMax(){
// return the maximum value stored in the tree
// if there is nothing there, return null
if (!this.root) return null;
// otherwise, all we have to do is go to the furthermost right place, and return the value
let walker = this.root;
// since you only have access to the current walker
// you only want to shift down to the right if there is a node to the right
while (walker.right) {
walker = walker.right;
}
// we've reached the end, return the data
return walker.data;
}
// set up a default parameter to start at the root
height(node = this.root){
// calculate the maximum amount of nodes in any one path from the given node
// if there is nothing there, return 0
if (!node) return 0;
// let's keep track of the max height, initialized to zero
let maxHeight = 0;
// now we can use recursion to update the maximum height;
const checker = (node, height) => {
// if we are currently on a valid node
if (node) {
// update maxHeight to be the highest between the current height and maxHeight;
maxHeight = Math.max(maxHeight, height);
// then run the same function to the left side
checker(node.left, height + 1);
// and to the right
checker(node.right, height + 1);
}
}
// invoke the checker function, passing in 1 as the height, as we have already validated that this.root exists
checker(node, 1);
// and return the maxHeight;
return maxHeight;
}
isBalanced(node = this.root){
// return true or false based on whether the sub-tree starting at the given node is balanced
// A tree is imbalanced if the height of one branch exceeds the other side by more than one level
// A tree is balanced if all branches end within one level of each other.
// let's initialize a Boolean to be true
let balanced = true;
// let's define a function that checks the heights of the left and right sides of a node
// and returns whether or not it is "imbalanced"
const checkBalance = node => {
let leftHeight = this.height(node.left);
let rightHeight = this.height(node.right);
// A tree is imbalanced if the height of one branch exceeds the other side by more than one level
return Math.abs(leftHeight - rightHeight) <= 1;
}
// now why don't we make a function that accepts a node to start at
// and traverses it, while checking if each node under it is balanced
// the basic idea is to keep on going down paths until we get a false outcome
// if we exhaust all of the paths, we can say it is balanced
const traverseAndCheck = walker => {
if (walker) {
// if checkBalance returns false, then we can update balance and stop traversing
if (!checkBalance(walker)) return balanced = false;
// otherwise, we gotta run this function on the left side and the right side
traverseAndCheck(walker.left);
traverseAndCheck(walker.right);
}
}
// now we can invoke our actual function
traverseAndCheck(node);
// and return whether or not it is balanced
return balanced;
}
}
module.exports = {
BinaryNode,
BinaryTree
}