Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
"scripts": {
"test": "eslint tests/*.js && eslint src/*.js && jest --verbose",
"lint": "eslint",
"test:watch": "npm test -- --watch"
"test:watch": "npm test -- --watch",
"test:nolint": "jest --verbose",
"mytest": "npm run test:nolint -- --watch"
},
"repository": {
"type": "git",
Expand Down
48 changes: 46 additions & 2 deletions src/binary-search-tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,80 @@
/* eslint-disable global-require */
/* eslint-disable no-unused-vars */
/* eslint-disable no-trailing-spaces */

const Queue = require('./queue-helper');

class BinarySearchTree {
constructor(value) {
this.value = value;
this.left = null;
this.right = null;
}

// Wraps the input value in a new BinarySearchTree and
// assigns it to either the left or right subtree,
// depending on its value
insert(value) {
const child = new BinarySearchTree(value); // newBST = value
if (child.value < this.value) {
if (this.left === null) {
this.left = child;
} else {
this.left.insert(value);
}
}

if (child.value > this.value) {
if (this.right === null) {
this.right = child;
} else {
this.right.insert(value);
}
}
}

// Checks the binary search tree for the input target
// Can be written recursively or iteratively
contains(target) {

// value = target? true
// if target < value -> left.contains(value)
// if target > value -> right.contains(value)
// false
if (this.value === target) return true;
if (this.left && target < this.value) return this.left.contains(target);
if (this.right && target > this.value) return this.right.contains(target);
return false;
}

// Traverses the tree in a depth-first manner, i.e. from top to bottom
// Applies the given callback to each tree node in the process
depthFirstForEach(cb) {

depthFirstForEach(cb) {
// run callback on current node's value
// if left, call depthFirstForEach on left
// if right, call depthFirstForEach on right
// don't forget to pass the callback down!
cb(this.value);
if (this.left) this.left.depthFirstForEach(cb);
if (this.right) this.right.depthFirstForEach(cb);
}

// Traverses the tree in a breadth-first manner, i.e. in layers, starting
// at the root node, going down to the root node's children, and iterating
// through all those nodes first before moving on to the next layer of nodes
// Applies the given callback to each tree node in the process
// You'll need the queue-helper file for this. Or could you roll your own queue
// again. Whatever floats your boat.
breadthFirstForEach(cb) {
const queue = new Queue();
queue.enqueue(this);

while (!queue.isEmpty()) {
const { value, left, right } = queue.dequeue();
cb(value);
if (left) queue.enqueue(left);
if (right) queue.enqueue(right);
}
}
}

Expand Down
67 changes: 46 additions & 21 deletions src/graph.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,25 @@
/* eslint-disable no-unused-vars */
/* eslint-disable no-underscore-dangle */
/* eslint-disable no-trailing-spaces */
// Do not modify this GraphNode class
// Use any of its methods as you see fit to implement your graph
/* eslint-disable class-methods-use-this */

class GraphNode {
constructor({ value, edges }) {
this._value = value;
this._edges = edges;
Object.assign(this, {
_value: value,
_edges: edges
})
}

get value() {
return this._value;
}

get edges() {
return this._edges;
}

get numberOfEdges() {
return this._edges.length;
}

set edges(x) {
this._edges = x;
}

pushToEdges(y) {
this._edges.push(y);
}
Expand All @@ -34,46 +29,76 @@ class Graph {
constructor() {
this.vertices = [];
}

// Wraps the input value in a new GraphNode and adds it to the array of vertices
// If there are only two nodes in the graph, they need to be automatically
// If there are only two nodes in the graph, they need to be automatically
// connected via an edge
// Optionally accepts an array of other GraphNodes for the new vertex to be connected to
// Returns the newly-added vertex
addVertex(value, edges = []) {

const vertex = new GraphNode({ value, edges });
if (edges.length) {
edges.forEach((edge) => {
this.addEdge(vertex, edge);
});
}
this.vertices.push(vertex);
if (this.vertices.length === 2) {
this.addEdge(this.vertices[0], this.vertices[1]);
}
return vertex;
}

// Checks all the vertices of the graph for the target value
// Returns true or false
contains(value) {

return this.vertices.some(v => v.value === value);
}
// Checks the graph to see if a GraphNode with the specified value exists in the graph

// Checks the graph to see if a GraphNode with the specified value exists in the graph
// and removes the vertex if it is found
// This function should also handle the removing of all edge references for the removed vertex
removeVertex(value) {

const [rmValue, index] = this.vertices.reduce((acc, v, idx) => {
if (v.value === value) return [v.value, idx];
return acc;
}, []);
return this.vertices.splice(index);
}

// Checks the two input vertices to see if each one references the other in their respective edges array
// Both vertices must reference each other for the edge to be considered valid
// If only one vertex references the other but not vice versa, should not return true
// Note: You'll need to store references to each vertex's array of edges so that you can use
// Note: You'll need to store references to each vertex's array of edges so that you can use
// array methods on said arrays. There is no method to traverse the edge arrays built into the GraphNode class
checkIfEdgeExists(fromVertex, toVertex) {

return fromVertex.edges.some(edge => edge.value === toVertex.value)
&& toVertex.edges.some(edge => edge.value === fromVertex.value);
}

// Adds an edge between the two given vertices if no edge already exists between them
// Again, an edge means both vertices reference the other
// Again, an edge means both vertices reference the other
addEdge(fromVertex, toVertex) {

if (this.checkIfEdgeExists(fromVertex, toVertex)) return;
fromVertex.pushToEdges(toVertex);
toVertex.pushToEdges(fromVertex);
}

// Removes the edge between the two given vertices if an edge already exists between them
// After removing the edge, neither vertex should be referencing the other
// If a vertex would be left without any edges as a result of calling this function, those
// vertices should be removed as well
removeEdge(fromVertex, toVertex) {
if (!this.checkIfEdgeExists(fromVertex, toVertex)) return;

fromVertex.edges = fromVertex.edges
.filter(edge => edge.value === toVertex.value);
toVertex.edges = fromVertex.edges
.filter(edge => edge.value === fromVertex.value);

if (!fromVertex.edges.length) this.removeVertex(fromVertex);
if (!toVertex.edges.length) this.removeVertex(toVertex);
}
}

module.exports = Graph;

104 changes: 73 additions & 31 deletions src/heap.js
Original file line number Diff line number Diff line change
@@ -1,44 +1,86 @@
/* eslint-disable */
class Heap {
constructor() {
this.storage = [null];
this.size = 0;
}
// Inserts the given value in the heap
// Calls bubbleUp in order to put the newly-inserted element in the right place in the heap
insert(val) {
constructor() {
Object.assign(this, { storage: [null], size: 0 });
}

}
// Deletes the element located at the front of the heap (the max if a max heap, or a min if a min heap)
// Calls siftDown in order to reorganize the heap with a new max/min
// In some specifications, this method is also called `poll`
delete() {
static getLeftIdx(pIndex) { return 2 * pIndex; }
static getRightIdx(pIndex) { return (2 * pIndex) + 1; }
static getParentIdx(cIndex) { return Math.floor(cIndex / 2); }

}
// Returns the maximum value in the heap in constant time
getMax() {
getMax() { return this.storage[1]; }
getSize() { return this.size; }
getStorage() { return this.storage; }

}
// Returns the size of the heap
getSize() {
/* Inserts the given value in the heap
* Calls bubbleUp in order to put the newly-inserted element in the right place in the heap */
insert(val) {
this.storage.push(val);
this.bubbleUp(++this.size);
}

}
// Returns the storage array
getStorage() {
/* Moves the element at the specified index "up" by swapping it with its parent
* if its parent value is less than the value located at the input index
* This method is only used by the heap itself in order to maintain the heap property */
bubbleUp(index) {
const pIndex = Heap.getParentIdx(index);
const parent = this.storage[pIndex];
const child = this.storage[index];

if (parent && child > parent) {
[this.storage[index], this.storage[pIndex]] = [parent, child];
this.bubbleUp(pIndex);
}
// Moves the element at the specified index "up" by swapping it with its parent
// if its parent value is less than the value located at the input index
// This method is only used by the heap itself in order to maintain the heap property
bubbleUp(index) {
}

/* Deletes the element located at the front of the heap (the max if a max heap, or a min if a min heap)
* Calls siftDown in order to reorganize the heap with a new max/min
* In some specifications, this method is also called `poll` */
delete() {
// Why I'm unshifting null
this.storage.shift();
const removed = this.storage.shift();
this.size--;

// Put tail at head to be sifted down
this.storage.unshift(this.storage.pop());

// Put null back at head
this.storage.unshift(null);
this.siftDown(1);

return removed;
}

/* First grabs the indices of this element's children and determines which of the children are larger
* If the larger of the child elements is larger than the parent, the child element is swapped with the parent
* This method is only used by the heap itself in order to maintain the heap property */
siftDown(index) {
if (!this.getSize()) return;

let left = this.storage[Heap.getLeftIdx(index)];
let right = this.storage[Heap.getRightIdx(index)];
const parent = this.storage[index];

/* Hacky! these two lines need to be here because setting
* child to the greater of left or right creates a weird bug
* because when one of the values is undefined, the statement
* always evaluates to false. */
if (!left) left = 0;
if (!right) right = 0;

const child = left > right ? left : right;
const cIndex = this.storage.indexOf(child);

if (parent < child) {
[this.storage[cIndex], this.storage[index]] = [parent, child];
return this.siftDown(cIndex);
}
// First grabs the indices of this element's children and determines which of the children are larger
// If the larger of the child elements is larger than the parent, the child element is swapped with the parent
// This method is only used by the heap itself in order to maintain the heap property
siftDown(index) {


if (parent < child) {
[this.storage[cIndex], this.storage[index]] = [parent, child];
return this.siftDown(cIndex);
}
}
}

module.exports = Heap;
13 changes: 11 additions & 2 deletions src/tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,24 @@ class Tree {
this.value = value;
this.children = [];
}

// Adds a new Tree node with the input value to the current Tree node
addChild(value) {

this.children.push(new Tree(value));
return this;
}

// Checks this node's children to see if any of them matches the given value
// Continues recursively until the value has been found or all of the children
// have been checked
contains(value) {

if (this.value === value) return true;
for (let i = 0; i < this.children.length; i++) {
if (this.children[i].contains(value)) {
return true;
}
}
return false;
}
}

Expand Down
Loading