├── .gitignore ├── BinaryHeapMaxPQ ├── README.md ├── binaryHeapMaxPQ.js ├── binaryHeapMaxPQ.test.js └── binaryHeapMaxPQStart.js ├── BinarySearchTree ├── BST.js ├── BST.test.js ├── BSTStart.js └── README.md ├── CircularBuffer ├── README.md ├── circularBuffer.js ├── circularBuffer.test.js └── circularBufferStart.js ├── Graphs ├── adjacencyListGraph.js ├── breadthFirstSearch.js └── depthFirstSearch.js ├── HashTables ├── README.md ├── hashTables.js ├── hashTables.test.js └── hashTablesStart.js ├── HeapSort ├── README.md ├── heapSort.js ├── heapSort.test.js └── heapSortStart.js ├── InsertionSort ├── README.md ├── insertionSort.js ├── insertionSort.test.js └── insertionSortStart.js ├── KnuthShuffle ├── README.md ├── knuthShuffle.js ├── knuthShuffle.test.js └── knuthShuffleStart.js ├── LCS ├── LCS.js ├── LCS.start.js ├── LCS.test.js └── README.md ├── LeftLeaningRedBlackBST ├── LLredBlackBST.js ├── LLredBlackBST.test.js ├── LLredBlackBSTStart.js └── README.md ├── LinkedList ├── README.md ├── linkedList.js ├── linkedList.test.js └── linkedListStart.js ├── MergeSort ├── README.md ├── baselineTests │ ├── bubbleBaseline.js │ └── insertionBaseline.js ├── mergeSort.js ├── mergeSort.test.js └── mergeSortStart.js ├── Queues ├── README.md ├── queueLL.js ├── queueLL.test.js └── queueLLStart.js ├── QuickFindUF ├── README.md ├── quick-find.js ├── quick-find.test.js └── quick-findStart.js ├── QuickSort ├── README.md ├── quickSort.js └── quickSortStart.js ├── QuickUnion ├── README.md ├── quick-union.js ├── quick-union.test.js └── quick-unionStart.js ├── README.md ├── SelectionSort ├── README.md ├── selectionSort.js ├── selectionSort.test.js └── selectionSortStart.js ├── ShellSort ├── README.md ├── shellSort.js ├── shellSort.test.js └── shellSortStart.js ├── Stacks ├── README.md ├── stackArr.js ├── stackLL.js ├── stackLL.test.js └── stackLLStart.js ├── Tries └── tries.js ├── UnorderedMaxPQ ├── README.md ├── unorderedMaxPQ.js ├── unorderedMaxPQ.test.js └── unorderedMaxPQStart.js ├── WeightedQuickUnion ├── README.md ├── weightedQuickUnion.js └── weightedQuickUnionStart.js ├── WeightedQuickUnionPathCompressed ├── README.md ├── weightedQuickUnionPathCompressed.js └── weightedQuickUnionPathCompressedStart.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /BinaryHeapMaxPQ/README.md: -------------------------------------------------------------------------------- 1 | # Binary Heap Max Priority Queue 2 | 3 | # What is it? 4 | Binary Heap Max Prioriy Queue implements a tree structure into an array. Since the queue is a representation of a binary tree, the elements within the array are already ordered. 5 | 6 | Taken from Princeton's Algorithms 4th Edition: http://algs4.cs.princeton.edu/24pq/ 7 | 8 | ![](http://algs4.cs.princeton.edu/24pq/images/heap-representations.png) 9 | 10 | The implementation requires a new way to add and remove the max element from the tree. As you'll notice from the tree the element at the top of the tree is the highest element. Just like a regular binary tree you'll notice that the index of one node in the tree points to another node which the index is double it and another node that is double + 1. Clever right? 11 | 12 | ### What do we need? 13 | 14 | `sink` moves down the tree and calls exchange until we are in the right position 15 | 16 | `swim` moves up the tree and calls exchange until we are in the right position 17 | 18 | `isEmpty` checks if tree is empty 19 | 20 | `delMax` swaps the root of the tree with the last element at the bottom of the tree and deletes it. If the last element in the tree is not in the proper position we need to `sink` it into the right position 21 | 22 | `insert` add the element to the end of the tree and then call `swim` to put it in the right position 23 | 24 | `exchange` swaps position 25 | 26 | `isLess` checks which is less 27 | -------------------------------------------------------------------------------- /BinaryHeapMaxPQ/binaryHeapMaxPQ.js: -------------------------------------------------------------------------------- 1 | class BinaryHeapMaxPQ { 2 | constructor(array) { 3 | this.array = [null, ...array]; // put null in front to make it easier for the arithmetic 4 | this.size = array.length // make sure length comes before you unshift 5 | } 6 | 7 | isEmpty() { 8 | return this.size === 0; 9 | } 10 | 11 | // when a higher element is placed below a lower element 12 | // we need to move it up the tree 13 | swim(idx) { 14 | // while idx > 1 because we don't use the 0th index 15 | // and array[idx/2] < array[idx] 16 | while (idx > 1 && this.isLess(Math.floor(idx / 2), idx)) { 17 | 18 | // swap them 19 | this.exchange(idx, Math.floor(idx / 2)); 20 | 21 | // go up to that index 22 | idx = Math.floor(idx / 2); 23 | } 24 | } 25 | 26 | // opposite of swim 27 | // we try to move the lower element down the tree 28 | sink(idx1) { 29 | // while the index is still inside the array 30 | while (2 * idx1 <= this.size) { 31 | 32 | // the next index is double 33 | let idx2 = 2 * idx1; 34 | 35 | // if the next index is not the last element and idx2 + 1 is larger, we increment 36 | // so we can swap with the 37 | if (idx2 < this.size && this.isLess(idx2, idx2 + 1)) idx2++; 38 | 39 | // if idx1 element is not less than idx2 element break 40 | if (!this.isLess(idx1, idx2)) break; 41 | 42 | // swap this element with the element at idx2 43 | // now we should be at the index double from before 44 | this.exchange(idx1, idx2); 45 | 46 | // we are at idx2 we check again until we reach the right spot 47 | idx1 = idx2; 48 | } 49 | } 50 | 51 | // delete max 52 | delMax() { 53 | 54 | // the max is at the top of the heap which would be index 1 55 | let max = this.array[1] 56 | 57 | // we swap the top with the end of the array 58 | this.exchange(1, this.size--); 59 | 60 | // we need to run the sink because after we swapped things might be out of order 61 | this.sink(1); 62 | 63 | // delete the element 64 | this.array[this.size + 1] = null; 65 | 66 | // return the new max 67 | return max; 68 | } 69 | 70 | // add element into queue 71 | insert(element) { 72 | 73 | // add it to the end 74 | this.array[++this.size] = element; 75 | 76 | // swim because it might be out of order 77 | this.swim(this.size); 78 | } 79 | 80 | // checks if less 81 | isLess(idx1, idx2) { 82 | return this.array[idx1] < this.array[idx2]; 83 | } 84 | 85 | // swaps the two elements in array 86 | exchange(idx1, idx2) { 87 | const temp = this.array[idx1]; 88 | this.array[idx1] = this.array[idx2]; 89 | this.array[idx2] = temp; 90 | } 91 | } 92 | 93 | module.exports = BinaryHeapMaxPQ; 94 | -------------------------------------------------------------------------------- /BinaryHeapMaxPQ/binaryHeapMaxPQ.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect 2 | const BinaryHeapMaxPQ = require('./binaryHeapMaxPQStart') 3 | 4 | xdescribe('Binary Heap Max Priority Queue', () => { 5 | let BHMPQ; 6 | 7 | beforeEach(() => { 8 | BHMPQ = new BinaryHeapMaxPQ(['T', 'P', 'R', 'N', 'H', 'O', 'A', 'E', 'I', 'G']) 9 | }) 10 | 11 | describe('Constructor', () => { 12 | it('should have array on its property with input array that starts at index 1', () => { 13 | expect(BHMPQ).to.have.ownProperty('array') 14 | expect(BHMPQ.array).to.deep.equal([null, 'T', 'P', 'R', 'N', 'H', 'O', 'A', 'E', 'I', 'G']) 15 | }) 16 | 17 | it('should have size on its property and its equal to length of input array excluding index 0', () => { 18 | expect(BHMPQ).to.have.ownProperty('size') 19 | expect(BHMPQ.size).to.equal(10) 20 | }) 21 | }) 22 | 23 | describe('isEmpty', () => { 24 | it('should be a property on the class', () => { 25 | expect(BHMPQ).to.have.property('isEmpty') 26 | }) 27 | 28 | it('should check if array is empty', () => { 29 | expect(BHMPQ.isEmpty()).to.be.false 30 | 31 | BHMPQ.array = []; 32 | BHMPQ.size = 0; 33 | 34 | expect(BHMPQ.isEmpty()).to.be.true 35 | }) 36 | }) 37 | 38 | describe('isLess', () => { 39 | it('should be a property on the class', () => { 40 | expect(BHMPQ).to.have.property('isLess') 41 | }) 42 | 43 | it('should receive two indicies and check the array to see if first < second', () => { 44 | expect(BHMPQ.isLess(1, 2)).to.be.false 45 | expect(BHMPQ.isLess(7, 8)).to.be.true 46 | expect(BHMPQ.isLess(10, 3)).to.be.true 47 | expect(BHMPQ.isLess(4, 9)).to.be.false 48 | }) 49 | }) 50 | 51 | describe('exchange', () => { 52 | it('should be a property on the class', () => { 53 | expect(BHMPQ).to.have.property('exchange') 54 | }) 55 | 56 | it('should receive two indicies and swap those elements in the array', () => { 57 | BHMPQ.exchange(1,2) 58 | expect(BHMPQ.array).to.deep.equal([null, 'P', 'T', 'R', 'N', 'H', 'O', 'A', 'E', 'I', 'G']) 59 | BHMPQ.exchange(2,4) 60 | expect(BHMPQ.array).to.deep.equal([null, 'P', 'N', 'R', 'T', 'H', 'O', 'A', 'E', 'I', 'G']) 61 | BHMPQ.exchange(3,5) 62 | expect(BHMPQ.array).to.deep.equal([null, 'P', 'N', 'H', 'T', 'R', 'O', 'A', 'E', 'I', 'G']) 63 | BHMPQ.exchange(10,7) 64 | expect(BHMPQ.array).to.deep.equal([null, 'P', 'N', 'H', 'T', 'R', 'O', 'G', 'E', 'I', 'A']) 65 | }) 66 | }) 67 | 68 | describe('insert', () => { 69 | it('should be a property on the class', () => { 70 | expect(BHMPQ).to.have.property('insert') 71 | }) 72 | 73 | it('should receive an element and add it to the queue in the property place(call swim)', () => { 74 | BHMPQ.insert('S') 75 | expect(BHMPQ.array).to.deep.equal([null, 'T', 'S', 'R', 'N', 'P', 'O', 'A', 'E', 'I', 'G', 'H']) 76 | }) 77 | }) 78 | 79 | describe('swim', () => { 80 | it('should be a property on the class', () => { 81 | expect(BHMPQ).to.have.property('swim') 82 | }) 83 | 84 | it('should move elements up the binary tree', () => { 85 | const TPRNHOAEIGS = 'TPRNHOAEIGS' 86 | BHMPQ.array = [null, ...TPRNHOAEIGS.split('')] 87 | BHMPQ.size = TPRNHOAEIGS.length 88 | BHMPQ.swim(11) 89 | expect(BHMPQ.array).to.deep.equal([null, ...'TSRNPOAEIGH'.split('')]) 90 | }) 91 | }) 92 | 93 | describe('sink', () => { 94 | it('should be a property on the class', () => { 95 | expect(BHMPQ).to.have.property('sink') 96 | }) 97 | 98 | it('should move elements down the binary tree', () => { 99 | const HSRNPOAEIG = 'HSRNPOAEIG' 100 | BHMPQ.array = [null, ...HSRNPOAEIG.split('')] 101 | BHMPQ.size = HSRNPOAEIG.length 102 | BHMPQ.sink(1) 103 | expect(BHMPQ.array).to.deep.equal([null, ...'SPRNHOAEIG'.split('')]) 104 | }) 105 | }) 106 | 107 | describe('delMax', () => { 108 | it('should be a property on the class', () => { 109 | expect(BHMPQ).to.have.property('delMax') 110 | }) 111 | 112 | it('should swap the max element to end of array and set last element to null', () => { 113 | BHMPQ.insert('S') 114 | BHMPQ.delMax() 115 | expect(BHMPQ.array).to.deep.equal([null, 'S', 'P', 'R', 'N', 'H', 'O', 'A', 'E', 'I', 'G', null]) 116 | }) 117 | 118 | // After decrementing the size of the array we are saying the last element doesn't exist anymore 119 | it('should decrement the size', () => { 120 | const sizeBefore = BHMPQ.size 121 | const correctSizeAfter = sizeBefore - 1 122 | BHMPQ.delMax() 123 | expect(BHMPQ.size).to.be.equal(correctSizeAfter) 124 | }) 125 | }) 126 | 127 | }) 128 | -------------------------------------------------------------------------------- /BinaryHeapMaxPQ/binaryHeapMaxPQStart.js: -------------------------------------------------------------------------------- 1 | 2 | class BinaryHeapMaxPQ { 3 | 4 | } 5 | 6 | module.exports = BinaryHeapMaxPQ; 7 | -------------------------------------------------------------------------------- /BinarySearchTree/BST.js: -------------------------------------------------------------------------------- 1 | class BinarySearchTree { 2 | constructor(val) { 3 | this.value = val; 4 | this.left = null; 5 | this.right = null; 6 | this.magnitude = 1; 7 | } 8 | 9 | insert(val) { 10 | const direction = val < this.value ? 'left' : 'right'; 11 | if (this[direction]) this[direction].insert(val); 12 | else this[direction] = new BinarySearchTree(val); 13 | this.magnitude++; 14 | } 15 | 16 | // recursive binary search 17 | containsRecursive(val) { 18 | if (this.value === val) return true; 19 | let direction = val < this.value ? 'left' : 'right'; 20 | if (this[direction]) return this[direction].containsRecursive(val); 21 | else return false; 22 | } 23 | 24 | // iterative binary search 25 | containsIterative(val) { 26 | let tree = this; 27 | 28 | while (tree !== null) { 29 | if (val < tree.value) tree = tree.left; 30 | else if (val > tree.value) tree = tree.right; 31 | else return true; 32 | } 33 | 34 | return false; 35 | } 36 | 37 | // all in one implementation 38 | depthFirstForEach(fn, opt = 'in-order') { 39 | if (opt === 'pre-order') fn(this.value); 40 | if (this.left) this.left.depthFirstForEach(fn, opt); 41 | if (opt === 'in-order') fn(this.value); 42 | if (this.right) this.right.depthFirstForEach(fn, opt); 43 | if (opt === 'post-order') fn(this.value); 44 | } 45 | 46 | // OR separate appraoches for each 47 | 48 | dfsInOrder(fn) { 49 | if (this.value === null) return; 50 | if (this.left) this.left.dfsInOrder(fn); 51 | fn(this.value); 52 | if (this.right) this.right.dfsInOrder(fn); 53 | } 54 | 55 | dfsPreOrder(fn) { 56 | if (this.value === null) return; 57 | fn(this.value); 58 | if (this.left) this.left.dfsPreOrder(fn); 59 | if (this.right) this.right.dfsPreOrder(fn); 60 | } 61 | 62 | dfsPostOrder(fn) { 63 | if (this.value === null) return; 64 | if (this.left) this.left.dfsPostOrder(fn); 65 | if (this.right) this.right.dfsPostOrder(fn); 66 | fn(this.value); 67 | } 68 | 69 | breadthFirstForEach(fn) { 70 | const queue = [this]; 71 | while (queue.length) { 72 | let current = queue.shift(); 73 | if (current.left) queue.push(current.left); 74 | if (current.right) queue.push(current.right); 75 | fn(current.value); 76 | } 77 | } 78 | 79 | size(){ 80 | return this.magnitude; 81 | } 82 | } 83 | 84 | module.exports = BinarySearchTree; 85 | -------------------------------------------------------------------------------- /BinarySearchTree/BST.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const BinarySearchTree = require('./BSTStart'); 3 | 4 | xdescribe('Binary Search Tree', () => { 5 | var tree, 6 | testArr, 7 | valuesToInsert = [15, 25, 5, 17, 21, 28, 0, 14, 50, 1, 45, 13, 12, 11, 30, 35, 33, 31, 34]; 8 | 9 | beforeEach(function() { 10 | tree = new BinarySearchTree(20); 11 | testArr = []; 12 | }); 13 | 14 | describe('ContainsIterative', () => { 15 | it('returns false if `containsIterative` is passed a value not in the tree', () => { 16 | valuesToInsert.forEach(function(value){ 17 | tree.insert(value); 18 | }); 19 | [6, 23, 37, 51].forEach(function(value){ 20 | expect(tree.containsIterative(value)).to.be.false; 21 | }); 22 | }); 23 | 24 | it('returns true if `containsIterative` is passed a value in the tree', () => { 25 | valuesToInsert.forEach(function(value){ 26 | tree.insert(value); 27 | }); 28 | valuesToInsert.forEach(function(value){ 29 | expect(tree.containsIterative(value)).to.be.true; 30 | }); 31 | }); 32 | }) 33 | 34 | describe('ContainsRecursive', () => { 35 | it('returns false if `containsRecursive` is passed a value not in the tree', () => { 36 | valuesToInsert.forEach(function(value){ 37 | tree.insert(value); 38 | }); 39 | [6, 23, 37, 51].forEach(function(value){ 40 | expect(tree.containsRecursive(value)).to.be.false; 41 | }); 42 | }); 43 | 44 | it('returns true if `containsRecursive` is passed a value in the tree', () => { 45 | valuesToInsert.forEach(function(value){ 46 | tree.insert(value); 47 | }); 48 | valuesToInsert.forEach(function(value){ 49 | expect(tree.containsRecursive(value)).to.be.true; 50 | }); 51 | }); 52 | }) 53 | 54 | // obvious advantage: values are processed respecting their comparative order 55 | describe('dfsInOrder', () => { 56 | it('runs depth-first with in-order traversal', function() { 57 | valuesToInsert.forEach(function(value){ 58 | tree.insert(value); 59 | }); 60 | tree.dfsInOrder(function(val){ testArr.push(val); }); 61 | expect(testArr).to.deep.equal([ 0, 1, 5, 11, 12, 13, 14, 15, 17, 20, 21, 25, 28, 30, 31, 33, 34, 35, 45, 50 ]); 62 | testArr = []; 63 | tree.dfsInOrder(function(val){ testArr.push(val); }); 64 | expect(testArr).to.deep.equal([ 0, 1, 5, 11, 12, 13, 14, 15, 17, 20, 21, 25, 28, 30, 31, 33, 34, 35, 45, 50 ]); 65 | }); 66 | }) 67 | 68 | // one use case: copying a tree (processes roots first) 69 | describe('dfsPreOrder', () => { 70 | it('runs depth-first with pre-order traversal', function() { 71 | valuesToInsert.forEach(function(value){ 72 | tree.insert(value); 73 | }); 74 | tree.dfsPreOrder(function(val){ testArr.push(val); }); 75 | expect(testArr).to.deep.equal([20, 15, 5, 0, 1, 14, 13, 12, 11, 17, 25, 21, 28, 50, 45, 30, 35, 33, 31, 34]); 76 | }); 77 | }) 78 | 79 | // one use case: deleting a tree (processes leaves first) 80 | describe('dfsPostOrder', () => { 81 | it('runs depth-first with post-order traversal', function() { 82 | valuesToInsert.forEach(function(value){ 83 | tree.insert(value); 84 | }); 85 | tree.dfsPostOrder(function(val){ testArr.push(val); }); 86 | expect(testArr).to.deep.equal([ 1, 0, 11, 12, 13, 14, 5, 17, 15, 21, 31, 34, 33, 35, 30, 45, 50, 28, 25, 20 ]); 87 | }); 88 | }) 89 | }) 90 | -------------------------------------------------------------------------------- /BinarySearchTree/BSTStart.js: -------------------------------------------------------------------------------- 1 | class BinarySearchTree { 2 | 3 | } 4 | 5 | module.exports = BinarySearchTree; 6 | -------------------------------------------------------------------------------- /BinarySearchTree/README.md: -------------------------------------------------------------------------------- 1 | # Binary Search Tree 2 | 3 | # Notes: 4 | Taken mostly from Full Stack Academy Data Structures Workshop. 5 | 6 | Added additional implmentations of some functions for iterative approach and added additional tests for them. 7 | 8 | Also added separate functions for each tree traversal along with respective tests. 9 | -------------------------------------------------------------------------------- /CircularBuffer/README.md: -------------------------------------------------------------------------------- 1 | # Circular Buffer 2 | 3 | [](https://upload.wikimedia.org/wikipedia/commons/f/fd/Circular_Buffer_Animation.gif) 4 | 5 | # What does it do? 6 | 7 | The circular buffer data structure is a type of queue with a static size. As the name suggest we circle around and replace old items. 8 | 9 | When adding we add to the queue and and when we reach the end of the static structure we start again at the beginning of the queue and replace our past data with our new data. 10 | 11 | Similarly when removing we are removing the first element we added. Following these rules, Circular Buffer follows a first-in first-out rule (FIFO). 12 | -------------------------------------------------------------------------------- /CircularBuffer/circularBuffer.js: -------------------------------------------------------------------------------- 1 | class CircularBuffer { 2 | constructor(capacity) { 3 | this.buffer = Array(capacity); 4 | this.capacity = capacity; 5 | this.head = this.tail = this.size = 0; 6 | } 7 | 8 | // add to array at head index; 9 | enqueue(data) { 10 | this.tail = (this.head + this.size) % this.capacity // adjust the tail 11 | 12 | let nextIdx = this.head + 1; // next index we are adding into the array 13 | 14 | if (nextIdx >= this.capacity) nextIdx = 0; // loop around to beginning of array 15 | if (this.size < this.capacity) this.size++; // increase the size if it's under capacity 16 | 17 | this.buffer[this.head] = data; // put data in the array 18 | this.head = nextIdx; // set the pointer to the new index; 19 | 20 | } 21 | 22 | // remove from array at tail index; 23 | dequeue() { 24 | if (this.size === 0) return undefined; 25 | 26 | let data = this.buffer[this.tail]; // the item to be removed 27 | this.buffer[this.tail] = null; // delete item 28 | 29 | let nextIdx = this.tail + 1; // increment the tail 30 | 31 | if (nextIdx >= this.capacity) nextIdx = 0; // check if tail circles around 32 | if (this.size > 0) this.size--; // decrement size 33 | this.tail = nextIdx; // set new tail 34 | 35 | return data; // return the data 36 | } 37 | 38 | } 39 | 40 | module.exports = CircularBuffer; 41 | -------------------------------------------------------------------------------- /CircularBuffer/circularBuffer.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const CircularBuffer = require('./circularBufferStart'); 3 | 4 | xdescribe('CircularBuffer', () => { 5 | let newCB; 6 | beforeEach(() => { 7 | newCB = new CircularBuffer(8); 8 | }) 9 | 10 | describe('Constructor', () => { 11 | it('should have a head, tail, size, capacity, and buffer on its class', () => { 12 | expect(newCB).to.have.ownProperty('head') 13 | expect(newCB).to.have.ownProperty('tail') 14 | expect(newCB).to.have.ownProperty('size') 15 | expect(newCB).to.have.ownProperty('capacity') 16 | expect(newCB).to.have.ownProperty('buffer') 17 | }) 18 | 19 | it('should have head, tail and size default to zero', () => { 20 | expect(newCB.head).to.equal(0) 21 | expect(newCB.size).to.equal(0) 22 | expect(newCB.tail).to.equal(0) 23 | }) 24 | 25 | it('should have buffer set to be an array of length equal to input', () => { 26 | expect(newCB.buffer).to.be.instanceof(Array) 27 | expect(newCB.buffer.length).to.equal(8) 28 | }) 29 | 30 | it('should have the property capacity set to the array length', () => { 31 | expect(newCB.capacity).to.equal(8) 32 | }) 33 | }) 34 | 35 | describe('enqueue', () => { 36 | it('should be a function on the class', () => { 37 | expect(newCB).to.have.property('enqueue') 38 | expect(newCB.enqueue).to.be.instanceof(Function) 39 | }) 40 | 41 | it('should increment the size when called but not if it is greater than or equal to the capacity', () => { 42 | newCB.enqueue(1) 43 | expect(newCB.size).to.deep.equal(1) 44 | 45 | newCB.size = 8; 46 | newCB.enqueue(2) 47 | expect(newCB.size).to.deep.equal(8) 48 | }) 49 | 50 | it('should increment the head when called', () => { 51 | newCB.enqueue(1) 52 | expect(newCB.head).to.deep.equal(1) 53 | }) 54 | 55 | it('should insert one item into the buffer at the next index', () => { 56 | newCB.enqueue(1) 57 | let solution = Array(8) 58 | solution[0] = 1; 59 | expect(newCB.buffer).to.deep.equal(solution) 60 | }) 61 | 62 | it('should be able to circle around and add to the front of the array', () => { 63 | let arrBefore = [0,1,2,3,4,5,6,7]; 64 | newCB.buffer = arrBefore; 65 | newCB.head = 0; 66 | newCB.enqueue(8); 67 | let solution = [8,1,2,3,4,5,6,7] 68 | expect(newCB.buffer).to.deep.equal(solution); 69 | }) 70 | 71 | it('should replace items from the array when it circles around', () => { 72 | let arrBefore = [8,1,2,3,4,5,6,7]; 73 | newCB.buffer = arrBefore; 74 | newCB.head = 1; 75 | newCB.enqueue(9); 76 | let solution = [8,9,2,3,4,5,6,7] 77 | expect(newCB.buffer).to.deep.equal(solution); 78 | }) 79 | 80 | it('should work with MANY enqueuees', () => { 81 | for(let i = 0; i < 42; i++) { 82 | newCB.enqueue(i); 83 | } 84 | let solution = [40,41,34,35,36,37,38,39] 85 | expect(newCB.buffer).to.deep.equal(solution); 86 | }) 87 | 88 | it('should work with MANY enqueuees', () => { 89 | for(let i = 0; i < 20; i++) { 90 | newCB.enqueue(i); 91 | } 92 | const solution = [16,17,18,19,12,13,14,15] 93 | expect(newCB.buffer).to.deep.equal(solution); 94 | }) 95 | }) 96 | 97 | describe('dequeue', () => { 98 | it('should be a function on the class', () => { 99 | expect(newCB).to.have.property('dequeue') 100 | expect(newCB.dequeue).to.be.instanceof(Function) 101 | }) 102 | 103 | it('should return undefined when trying to dequeue with a size of 0', () => { 104 | expect(newCB.dequeue()).to.be.undefined; 105 | }) 106 | 107 | it('should decrement the size', () => { 108 | newCB.size = 1; 109 | newCB.buffer = [1] 110 | newCB.dequeue() 111 | expect(newCB.size).to.equal(0); 112 | }) 113 | 114 | it('should return the first item added that was not replaced and set the element to null', () => { 115 | newCB.buffer = [0,1,2,3,4,5,6,7] 116 | newCB.tail = 0; 117 | newCB.size = 8; 118 | expect(newCB.dequeue()).to.equal(0); 119 | expect(newCB.dequeue()).to.equal(1); 120 | expect(newCB.dequeue()).to.equal(2); 121 | expect(newCB.dequeue()).to.equal(3); 122 | expect(newCB.dequeue()).to.equal(4); 123 | expect(newCB.dequeue()).to.equal(5); 124 | expect(newCB.dequeue()).to.equal(6); 125 | expect(newCB.dequeue()).to.equal(7); 126 | expect(newCB.buffer).to.deep.equal([null,null,null,null,null,null,null,null]) 127 | }) 128 | 129 | it('should return the first item added that was not replaced and circle around the array', () => { 130 | newCB.buffer = [8,9,2,3,4,5,6,7] 131 | newCB.tail = 2; 132 | newCB.size = 8; 133 | expect(newCB.dequeue()).to.equal(2); 134 | expect(newCB.dequeue()).to.equal(3); 135 | expect(newCB.dequeue()).to.equal(4); 136 | expect(newCB.dequeue()).to.equal(5); 137 | expect(newCB.dequeue()).to.equal(6); 138 | expect(newCB.dequeue()).to.equal(7); 139 | expect(newCB.dequeue()).to.equal(8); 140 | expect(newCB.dequeue()).to.equal(9); 141 | expect(newCB.buffer).to.deep.equal([null,null,null,null,null,null,null,null]) 142 | }) 143 | }) 144 | }) 145 | -------------------------------------------------------------------------------- /CircularBuffer/circularBufferStart.js: -------------------------------------------------------------------------------- 1 | class CircularBuffer { 2 | } 3 | 4 | module.exports = CircularBuffer; 5 | -------------------------------------------------------------------------------- /Graphs/adjacencyListGraph.js: -------------------------------------------------------------------------------- 1 | class Graph { 2 | constructor(v, e) { 3 | this.numVertices = v; 4 | this.numEdges = e; 5 | this.vertices = Array(v); 6 | this.verticesInit(); 7 | } 8 | 9 | // initialize each vertex in verticies array with another array to house connections 10 | verticesInit() { 11 | for (let i = 0; i < this.numVertices; i++) { 12 | this.vertices[i] = []; 13 | } 14 | } 15 | 16 | // add edges to each corresponding vertex 17 | // make sure to add the connection to both vetices 18 | addEdge(vertex1, vertex2) { 19 | this.vertices[vertex1].push(vertex2); 20 | this.vertices[vertex2].push(vertex1); 21 | } 22 | 23 | // returns the connections to specific vertex 24 | adjacentVertices = (v) => this.vertices[v]; 25 | 26 | // the number of connections to a specific vertex 27 | degree = (v) => this.vertices[v].length; 28 | 29 | // the furthest connection 30 | maxDegree = () => Math.max(...this.vertices.map((arr, index) => this.degree(index))); 31 | 32 | // average connection distance 33 | averageDegree() { 34 | const degrees = this.vertices.map((arr, index) => this.degree(index)); 35 | return degrees.reduce((a, b) => a + b) / degrees.length; 36 | } 37 | } 38 | 39 | module.exports = Graph; 40 | -------------------------------------------------------------------------------- /Graphs/breadthFirstSearch.js: -------------------------------------------------------------------------------- 1 | class BreadthFirstSearch { 2 | constructor(graph) { 3 | this.marked = Array(graph.numVertices); 4 | this.edgeTo = Array(graph.numVertices); 5 | this.markedInit(); 6 | } 7 | 8 | markedInit() { 9 | for (var i = 0; i < this.marked.length; i++) { 10 | this.marked[i] = false 11 | } 12 | } 13 | 14 | bfs(graph, source) { 15 | let queue = [source]; 16 | this.marked[source] = true; 17 | 18 | while (queue.length) { 19 | let v = queue.shift(); 20 | const adjacent = graph.adjacentVertices(v); 21 | adjacent.forEach(w => { 22 | if (!this.marked[w]) { 23 | queue.push(w); 24 | // console.log(queue) 25 | this.marked[w] = true; 26 | this.edgeTo[w] = v; 27 | } 28 | }); 29 | } 30 | } 31 | } 32 | 33 | module.exports = BreadthFirstSearch; 34 | 35 | // const ALgraph = require('./adjacencyListGraph'); 36 | 37 | // let y = new ALgraph(6, 8); 38 | // y.addEdge(0,1); 39 | // y.addEdge(0,2); 40 | // y.addEdge(0,5); 41 | // y.addEdge(1,2); 42 | // y.addEdge(2,4); 43 | // y.addEdge(2,3); 44 | // y.addEdge(3,4); 45 | // y.addEdge(3,5); 46 | 47 | // let search = new BreadthFirstSearch(y); 48 | // console.log(y) 49 | // search.bfs(y, 0); 50 | // console.log(search.marked); 51 | // console.log(search.edgeTo); 52 | -------------------------------------------------------------------------------- /Graphs/depthFirstSearch.js: -------------------------------------------------------------------------------- 1 | 2 | class DepthFirstSearch { 3 | constructor(graph) { 4 | this.marked = Array(graph.numVertices); 5 | this.edgeTo = Array(graph.numVertices); 6 | this.markedInit(); 7 | this.components = Array(graph.numVertices); 8 | this.componentId = 0; 9 | } 10 | 11 | markedInit() { 12 | for (var i = 0; i < this.marked.length; i++) { 13 | this.marked[i] = false 14 | } 15 | } 16 | 17 | dfs(graph, vertex) { 18 | this.marked[vertex] = true; 19 | this.components[vertex] = this.componentId; 20 | const adjacent = graph.adjacentVertices(vertex); 21 | adjacent.forEach(w => { 22 | if (!this.marked[w]) { 23 | this.dfs(graph, w); 24 | } 25 | this.edgeTo[w] = vertex; 26 | }) 27 | } 28 | 29 | hasPathTo(vertex) { 30 | return this.marked[vertex] 31 | } 32 | 33 | path(v, w) { 34 | if (!this.hasPathTo(v)) return null; 35 | 36 | let directions = w.toString(); 37 | 38 | while (this.edgeTo[w] !== v) { 39 | directions += ' --> ' + this.edgeTo[w].toString() 40 | w = this.edgeTo[w] 41 | } 42 | 43 | directions += ' --> ' + v.toString(); 44 | 45 | return directions; 46 | } 47 | 48 | connectedComponents(graph) { 49 | for (let i = 0; i < graph.numVertices; i++) { 50 | if (!this.marked[i]) { 51 | this.dfs(graph, i); 52 | this.componentId++; 53 | } 54 | } 55 | } 56 | 57 | } 58 | 59 | module.exports = DepthFirstSearch; 60 | -------------------------------------------------------------------------------- /HashTables/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dye784/Algos-and-Data-Structures/147d898c17560c4e31b292ac8b078a647f57f291/HashTables/README.md -------------------------------------------------------------------------------- /HashTables/hashTables.js: -------------------------------------------------------------------------------- 1 | 2 | class HashNode { 3 | constructor(key, val, next = null) { 4 | this.value = val; 5 | this.key = key; 6 | this.next = next; 7 | } 8 | } 9 | 10 | class HashTable { 11 | constructor() { 12 | this.numBuckets = 35; 13 | this.buckets = new Array(35); 14 | } 15 | 16 | set(key, val) { 17 | let hash = this.hash(key); 18 | if (!this.buckets[hash]) this.buckets[hash] = new HashNode(key, val); 19 | else this.buckets[hash] = new HashNode(key, val, this.buckets[hash]) 20 | } 21 | 22 | get(key) { 23 | let hash = this.hash(key); 24 | let bucket = this.buckets[hash]; 25 | 26 | while (bucket !== null) { 27 | if (key === bucket.key) return bucket.value 28 | bucket = bucket.next 29 | } 30 | 31 | return null; 32 | } 33 | 34 | hash(str) { 35 | let sum = 0; 36 | for (let i = 0; i < str.length; i++) { 37 | sum += str.charCodeAt(i) 38 | } 39 | return sum % this.numBuckets; 40 | } 41 | } 42 | 43 | 44 | module.exports = { HashTable, HashNode } 45 | -------------------------------------------------------------------------------- /HashTables/hashTables.test.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const expect = require('chai').expect; 3 | const chaiProperties = require('chai-properties'); 4 | chai.use(chaiProperties); 5 | 6 | const HashTable = require('./hashTables').HashTable; 7 | const HashNode = require('./hashTables').HashNode; 8 | 9 | describe('Hash Table', () => { 10 | let HT; 11 | beforeEach(() => { 12 | HT = new HashTable(); 13 | }); 14 | 15 | describe('Constructor', () => { 16 | it('should have properties numBuckets and buckets', function() { 17 | expect(HT).to.have.ownProperty('numBuckets'); 18 | expect(HT).to.have.ownProperty('buckets'); 19 | }); 20 | }); 21 | 22 | describe('hash', () => { 23 | it('should be an instance method', () => { 24 | expect(HT).to.have.property('hash'); 25 | expect(HT).to.not.have.ownProperty('hash'); 26 | expect(HT.hash).to.be.a('function'); 27 | }); 28 | 29 | it('should convert sum up the character code of each letter and modulo by the number of buckets', () => { 30 | expect(HT.hash('foo')).to.equal(9); 31 | expect(HT.hash('this is a key')).to.equal(27); 32 | expect(HT.hash('what about this one')).to.equal(13); 33 | }); 34 | }); 35 | 36 | describe('get', () => { 37 | it('should be an instance method', () => { 38 | expect(HT).to.have.property('get'); 39 | expect(HT).to.not.have.ownProperty('get'); 40 | expect(HT.get).to.be.a('function'); 41 | }); 42 | 43 | it('should get the correct value when separate chaining is not needed', () => { 44 | const i = HT.hash('hello'); 45 | HT.buckets[i] = { key: 'hello', value: 'goodbye'}; 46 | expect(HT.get('hello')).to.equal('goodbye'); 47 | }); 48 | 49 | it('should work with collisions', () => { 50 | const i = HT.hash('foo'); 51 | HT.buckets[i] = { key: 'foo', value: 'bar'}; 52 | HT.buckets[i].next = { key: 'ofo', value: 'SOMETHING ELSE ENTIRELY'}; 53 | expect(HT.get('ofo')).to.equal('SOMETHING ELSE ENTIRELY'); 54 | }); 55 | }); 56 | 57 | describe('set method', () => { 58 | it('should be an instance method', () => { 59 | expect(HT).to.have.property('set'); 60 | expect(HT).to.not.have.ownProperty('set'); 61 | expect(HT.set).to.be.a('function'); 62 | }); 63 | 64 | it('should handle setting a value without collisions', () => { 65 | HT.set('foo', 'bar'); 66 | const match = {key: 'foo', value: 'bar', next: null}; 67 | const testBucket = HT.buckets.filter(elem => elem)[0]; 68 | expect(testBucket).to.be.deep.equal(match); 69 | expect(testBucket.key).to.equal('foo'); 70 | expect(testBucket.value).to.equal('bar'); 71 | expect(testBucket.next).to.be.null; 72 | }); 73 | 74 | it('should handle collisions and place newly hashed value at the head of the bucket', () => { 75 | HT.set('foo', 'bar1'); 76 | HT.set('ofo', 'bar2'); 77 | // find the bucket that includes any values 78 | const testBucket = HT.buckets.filter(elem => elem)[0]; 79 | expect(testBucket).to.have.properties({key: 'ofo', value: 'bar2'}); 80 | expect(testBucket.next).to.have.properties({key: 'foo', value: 'bar1'}); 81 | expect(HT.get('ofo')).to.equal('bar2'); 82 | expect(HT.get('foo')).to.equal('bar1'); 83 | }); 84 | 85 | it('should overwrite if the keys are the same', () => { 86 | HT.set('foo', 'bar1'); 87 | HT.set('foo', 'bar2'); 88 | expect(HT.get('foo')).to.equal('bar2'); 89 | }); 90 | }); 91 | }); 92 | 93 | describe('Hash Node', () => { 94 | 95 | let hNode; 96 | beforeEach(() => { 97 | hNode = new HashNode('very special key', 'very special value'); 98 | }); 99 | 100 | describe('Constructor', () => { 101 | it('should have properties key, value, and next', () => { 102 | expect(hNode).to.have.ownProperty('key'); 103 | expect(hNode).to.have.ownProperty('value'); 104 | expect(hNode).to.have.ownProperty('next'); 105 | }); 106 | 107 | it('should key and value set to input value', () => { 108 | expect(hNode.key).to.equal('very special key'); 109 | expect(hNode.value).to.equal('very special value'); 110 | }); 111 | 112 | it('should next set to null by default', () => { 113 | expect(hNode.next).to.be.null; 114 | }); 115 | 116 | it('should set next correctly if argument is provided', () => { 117 | const newHNode = new HashNode('key 2', 'value2', hNode); 118 | expect(newHNode.next).to.be.equal(hNode); 119 | }); 120 | }); 121 | }); 122 | -------------------------------------------------------------------------------- /HashTables/hashTablesStart.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dye784/Algos-and-Data-Structures/147d898c17560c4e31b292ac8b078a647f57f291/HashTables/hashTablesStart.js -------------------------------------------------------------------------------- /HeapSort/README.md: -------------------------------------------------------------------------------- 1 | # Heap Sort 2 | 3 | # What does it do? 4 | Sorts a Binary Heap. Very similar to BinaryHeapMaxPq implementation. 5 | 6 | # How does it sort? 7 | First we must go through the tree and construct it to be a tree that is a Binary Heap Max Priority Queue. Then we need to start exchanging the elements down throughout the tree. 8 | -------------------------------------------------------------------------------- /HeapSort/heapSort.js: -------------------------------------------------------------------------------- 1 | 2 | class HeapSort { 3 | constructor(array) { 4 | this.array = array; 5 | this.size = array.length 6 | } 7 | 8 | isEmpty() { 9 | return this.size === 0 10 | } 11 | 12 | sink(idx1, N) { 13 | 14 | // while the index is still inside the array 15 | while(2 * idx1 <= N) { 16 | 17 | // the next index is double 18 | let idx2 = 2 * idx1 19 | 20 | // if the next index is not the last element and idx2 + 1 is larger, we increment 21 | // so we can swap with the 22 | if(idx2 < N && this.isLess(idx2, idx2 + 1)) idx2++; 23 | 24 | // if idx1 element is not less than idx2 element break 25 | if(!this.isLess(idx1, idx2)) break; 26 | 27 | // swap this element with the element at idx2 28 | // now we should be at the index double from before 29 | this.exchange(idx1, idx2) 30 | 31 | // we are at idx2 we check again until we reach the right spot 32 | idx1 = idx2; 33 | } 34 | } 35 | 36 | // checks if less 37 | isLess(idx1, idx2) { 38 | return this.array[idx1 - 1] < this.array[idx2 - 1] 39 | } 40 | 41 | // swaps the two elements in array 42 | exchange(idx1, idx2) { 43 | let temp = this.array[idx1 - 1]; 44 | this.array[idx1 - 1] = this.array[idx2 - 1]; 45 | this.array[idx2 - 1] = temp; 46 | } 47 | 48 | sort() { 49 | let size = this.size 50 | 51 | // construct the heap to be in max order 52 | // start from 2nd to last row from the right and move to the left 53 | // don't can't sink further than last row 54 | for (let i = Math.floor(size / 2); i >= 1; i--) { 55 | this.sink(i, size) 56 | } 57 | 58 | // sort the heap 59 | while(size > 1) { 60 | 61 | this.exchange(1, size--) 62 | this.sink(1, size) 63 | } 64 | 65 | return this.array 66 | } 67 | 68 | } 69 | 70 | module.exports = HeapSort; 71 | -------------------------------------------------------------------------------- /HeapSort/heapSort.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect 2 | const HeapSort = require('./heapSort') 3 | 4 | xdescribe('Heap Sort for Binary Heap', () => { 5 | const SORTEXAMPLE = 'SORTEXAMPLE'; 6 | let HS; 7 | 8 | beforeEach(() => { 9 | HS = new HeapSort(SORTEXAMPLE.split('')) 10 | }) 11 | 12 | describe('Constructor', () => { 13 | it('should have array on its property with input array that starts at index 1', () => { 14 | expect(HS).to.have.ownProperty('array') 15 | expect(HS.array).to.deep.equal(SORTEXAMPLE.split('')) 16 | }) 17 | 18 | it('should have size on its property and its equal to length of input array excluding index 0', () => { 19 | expect(HS).to.have.ownProperty('size') 20 | expect(HS.size).to.equal(11) 21 | }) 22 | }) 23 | 24 | describe('isEmpty', () => { 25 | it('should be a property on the class', () => { 26 | expect(HS).to.have.property('isEmpty') 27 | }) 28 | 29 | it('should check if array is empty', () => { 30 | expect(HS.isEmpty()).to.be.false 31 | 32 | HS.array = []; 33 | HS.size = 0; 34 | 35 | expect(HS.isEmpty()).to.be.true 36 | }) 37 | }) 38 | 39 | // Note: Since Binary Heaps begin at index 1 we need to adjust this method to check one index to the left. Our array actually starts at index 0 40 | describe('isLess', () => { 41 | it('should be a property on the class', () => { 42 | expect(HS).to.have.property('isLess') 43 | }) 44 | 45 | 46 | it('should receive two indicies and check the array to see if first < second', () => { 47 | expect(HS.isLess(1, 2)).to.be.false 48 | expect(HS.isLess(7, 8)).to.be.true 49 | expect(HS.isLess(10, 3)).to.be.true 50 | expect(HS.isLess(4, 9)).to.be.false 51 | }) 52 | }) 53 | 54 | // Note: Since Binary Heaps begin at index 1 we need to adjust this method to check one index to the left. Our array actually starts at index 0 55 | describe('exchange', () => { 56 | it('should be a property on the class', () => { 57 | expect(HS).to.have.property('exchange') 58 | }) 59 | 60 | it('should receive two indicies and swap those elements in the array', () => { 61 | HS.exchange(1,2) 62 | expect(HS.array).to.deep.equal('OSRTEXAMPLE'.split('')) 63 | HS.exchange(2,4) 64 | expect(HS.array).to.deep.equal('OTRSEXAMPLE'.split('')) 65 | HS.exchange(3,5) 66 | expect(HS.array).to.deep.equal('OTESRXAMPLE'.split('')) 67 | HS.exchange(10,7) 68 | expect(HS.array).to.deep.equal('OTESRXLMPAE'.split('')) 69 | }) 70 | }) 71 | 72 | describe('sink', () => { 73 | it('should be a property on the class', () => { 74 | expect(HS).to.have.property('sink') 75 | }) 76 | 77 | it('should move elements in the tree down to appropriate place', () => { 78 | HS.sink(5, 11) 79 | expect(HS.array).to.deep.equal('SORTLXAMPEE'.split('')) 80 | HS.sink(4, 11) 81 | expect(HS.array).to.deep.equal('SORTLXAMPEE'.split('')) 82 | HS.sink(3, 11) 83 | expect(HS.array).to.deep.equal('SOXTLRAMPEE'.split('')) 84 | HS.sink(2, 11) 85 | expect(HS.array).to.deep.equal('STXPLRAMOEE'.split('')) 86 | HS.sink(1, 11) 87 | expect(HS.array).to.deep.equal('XTSPLRAMOEE'.split('')) 88 | }) 89 | }) 90 | 91 | describe('sort', () => { 92 | it('should be a property on the class', () => { 93 | expect(HS).to.have.property('sort') 94 | }) 95 | 96 | it('should sort the heap', () => { 97 | const SOMECLEVERJOKEHERE = 'SOMECLEVERJOKEHERE'; 98 | const LOTSOFMEMES = 'LOTSOFMEMES'; 99 | const SOMANYHEAPSSENDHALPPLS = 'SOMANYHEAPSSENDHALPPLS'; 100 | const FULLSTACK = 'FULLSTACK'; 101 | 102 | HS.sort() 103 | expect(HS.array).to.deep.equal(SORTEXAMPLE.split('').sort()) 104 | 105 | HS.array = SOMECLEVERJOKEHERE.split('') 106 | HS.size = SOMECLEVERJOKEHERE.length 107 | HS.sort() 108 | expect(HS.array).to.deep.equal(SOMECLEVERJOKEHERE.split('').sort()) 109 | 110 | HS.array = LOTSOFMEMES.split('') 111 | HS.size = LOTSOFMEMES.length 112 | HS.sort() 113 | expect(HS.array).to.deep.equal(LOTSOFMEMES.split('').sort()) 114 | 115 | HS.array = SOMANYHEAPSSENDHALPPLS.split('') 116 | HS.size = SOMANYHEAPSSENDHALPPLS.length 117 | HS.sort() 118 | expect(HS.array).to.deep.equal(SOMANYHEAPSSENDHALPPLS.split('').sort()) 119 | 120 | HS.array = FULLSTACK.split('') 121 | HS.size = FULLSTACK.length 122 | HS.sort() 123 | expect(HS.array).to.deep.equal(FULLSTACK.split('').sort()) 124 | }) 125 | }) 126 | }) 127 | -------------------------------------------------------------------------------- /HeapSort/heapSortStart.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dye784/Algos-and-Data-Structures/147d898c17560c4e31b292ac8b078a647f57f291/HeapSort/heapSortStart.js -------------------------------------------------------------------------------- /InsertionSort/README.md: -------------------------------------------------------------------------------- 1 | # Insertion Sort 2 | 3 | ### What is it? 4 | Insertion sort relies on the concept of dividing a list into sorted and unsorted sections. It iterates through the unsorted section and moves each unsorted element into its proper place in the sorted section. 5 | 6 | Because we potentially need to check the current unsorted element against every element in the sorted section, this algorithm has a run time of O(n^2), or a "quadratic" run time. 7 | 8 | 9 | 10 | ### Hint (Approach): 11 | 12 | Given array `[1, 3, 4, 2]` 13 | 14 | The 0th index is the only one on the "sorted" side so we don't need to check it. 15 | 16 | Moving on to the 1st index, `3`, we check it against the sorted array's last element, `1`. Since `3` > `1` we leave it where it is. 17 | 18 | Moving on to the 2nd index, `4`, we check it against the sorted array's last element, `3`. Since `4` > `3` we leave it where it is. 19 | 20 | Moving on to the 3rd index, `2`, we check it against the sorted array's last element, `4`. Since `2` < `4` we shift `4` once to the right. 21 | 22 | Now we check `2` against the sorted array's second-to-last element, `3`. Since `2` < `3` we shift `3` once to the right. 23 | 24 | Finally, we check `2` against the sorted array's third-to-last element, `1`. Since `2` > `1` we insert `2` between `1` and the next sorted element, `3`. 25 | 26 | -------------------------------------------------------------------------------- /InsertionSort/insertionSort.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const insertionSort = function(arr) { 4 | 5 | let length = arr.length 6 | 7 | for (let i = 1; i < length; ++i) { 8 | 9 | const elToSort = arr[i]; 10 | const startIdx = i; // We don't need to reassign `i`, but for clarity's sake... 11 | 12 | this.shiftElement(arr, startIdx, elToSort); // When the test is called we'll be in the module.exports' context 13 | 14 | } 15 | 16 | return arr; 17 | 18 | }; 19 | 20 | 21 | const shiftElement = function(arr, startIdx, elToSort) { 22 | 23 | while (startIdx > 0 && elToSort < arr[startIdx - 1]) { 24 | arr[startIdx] = arr[startIdx - 1]; 25 | --startIdx; 26 | } 27 | 28 | arr[startIdx] = elToSort; 29 | 30 | }; 31 | 32 | 33 | module.exports = { 34 | insertionSort, 35 | shiftElement 36 | }; 37 | 38 | 39 | 40 | // It may be to understand how this algorithm works if we don't extrapolate out the shift: 41 | 42 | const insertionSort2 = function(arr) { 43 | 44 | let length = arr.length 45 | 46 | for (let i = 1; i < length; ++i) { 47 | 48 | const elementToSort = arr[i]; 49 | let j = i; 50 | 51 | while (j > 0 && elementToSort < arr[j - 1]) { 52 | arr[j] = arr[j - 1]; 53 | --j; 54 | 55 | // Alternatively: 56 | // --j 57 | } 58 | 59 | arr[j] = elementToSort; 60 | 61 | // Then here: 62 | // arr.splice(j, 0, elementToSort) 63 | 64 | } 65 | 66 | return arr; 67 | 68 | } 69 | 70 | -------------------------------------------------------------------------------- /InsertionSort/insertionSort.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const expect = require('chai').expect, 4 | sinon = require('sinon'); 5 | 6 | require('chai').use(require('sinon-chai')); 7 | 8 | const insertionSort = require('./insertionSortStart'); 9 | 10 | xdescribe('Insertion sort', function() { 11 | 12 | it('sorts the array', function() { 13 | expect(insertionSort.insertionSort([10, 9, 1, 2, 5, 4])).to.deep.equal([1, 2, 4, 5, 9, 10]); 14 | }); 15 | 16 | 17 | describe('shiftElement', function() { 18 | 19 | let shiftElementSpy; 20 | 21 | beforeEach(function() { 22 | shiftElementSpy = sinon.spy(insertionSort, 'shiftElement'); 23 | }); 24 | 25 | afterEach(function() { 26 | shiftElementSpy.restore(); 27 | }) 28 | 29 | 30 | it('sorts in the right order', function() { 31 | const testArr = [2, 1, 10, 4, 5, 20] 32 | const sorted = insertionSort.insertionSort(testArr); 33 | 34 | expect(sorted).to.deep.equal([1, 2, 4, 5, 10, 20]); 35 | 36 | expect(shiftElementSpy).to.have.been.calledWith(testArr, 1, 1); 37 | expect(shiftElementSpy).to.have.been.calledWith(testArr, 2, 10); 38 | expect(shiftElementSpy).to.have.been.calledWith(testArr, 3, 4); 39 | expect(shiftElementSpy).to.have.been.calledWith(testArr, 4, 5); 40 | expect(shiftElementSpy).to.have.been.calledWith(testArr, 5, 20); 41 | 42 | }); 43 | 44 | }); 45 | 46 | }); 47 | -------------------------------------------------------------------------------- /InsertionSort/insertionSortStart.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | 5 | insertionSort: function(arr){ 6 | 7 | }, 8 | 9 | shiftElement: function(arr, startIdx, elToSort){ 10 | 11 | } 12 | 13 | }; -------------------------------------------------------------------------------- /KnuthShuffle/README.md: -------------------------------------------------------------------------------- 1 | # Knuth Shuffle aka Fisher-Yates Shuffle 2 | 3 | ### What is a the Knuth Shuffle? 4 | The Knuth Shuffle / Fisher-Yates shuffle is actually a sorting algo. But instead of sorting into ascending or descending order we are performing a uniformly random placement. 5 | 6 | -------------------------------------------------------------------------------- /KnuthShuffle/knuthShuffle.js: -------------------------------------------------------------------------------- 1 | const swap = (array, idx1, idx2) => { 2 | const temp = array[idx1]; 3 | array[idx1] = array[idx2]; 4 | array[idx2] = temp; 5 | }; 6 | 7 | const knuthShuffle = (deck) => { 8 | for (let i = 0; i < deck.length; i++) { 9 | 10 | // i needs to be inclusive 11 | const r = Math.floor(Math.random() * (i + 1)); 12 | swap(deck, i, r); 13 | } 14 | }; 15 | 16 | 17 | module.exports = knuthShuffle; 18 | -------------------------------------------------------------------------------- /KnuthShuffle/knuthShuffle.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const KnuthShuffle = require('./knuthShuffle') 3 | -------------------------------------------------------------------------------- /KnuthShuffle/knuthShuffleStart.js: -------------------------------------------------------------------------------- 1 | 2 | const knuthShuffle = (deck) => { 3 | 4 | } 5 | 6 | const swap = (array, idx1, idx2) => { 7 | 8 | } 9 | 10 | module.exports = knuthShuffle 11 | -------------------------------------------------------------------------------- /LCS/LCS.js: -------------------------------------------------------------------------------- 1 | const L = [[]]; 2 | 3 | const subproblem = (i, j, str1, str2) => { 4 | // if (L[i][j] < 0) { 5 | // if (str1[i] === '' || str2[j] === '') L[i][j] = 0 6 | // else if (str1[i] === str2[j]) L[i,j] = 1 + subproblem(i + 1, j + 1) 7 | // else L[i, j] = Math.max(subproblem(i + 1, j), subproblem(i, j + 1)) 8 | // } 9 | 10 | // return L[i][j] 11 | } 12 | 13 | const LCS_length = (str1, str2) => { 14 | // const m = Math.max(str1.length, str2.length) 15 | // // console.log(m) 16 | // for (let i = 0; i < m; i++) { 17 | // for (let j = 0; j < m; j++) { 18 | // if (!L[i][j]) L[i][j] = [] 19 | // console.log('ARRAY L : ', L) 20 | // L[i][j] = -1; 21 | // } 22 | // } 23 | // return subproblem(0,0, str1, str2) 24 | } 25 | 26 | 27 | 28 | module.exports = { LCS_length, subproblem } 29 | -------------------------------------------------------------------------------- /LCS/LCS.start.js: -------------------------------------------------------------------------------- 1 | const LCS = (str1, str2) => { 2 | 3 | } 4 | 5 | const subproblem = (idx1, idx2) => { 6 | 7 | } 8 | 9 | module.exports = { LCS, subproblem } 10 | -------------------------------------------------------------------------------- /LCS/LCS.test.js: -------------------------------------------------------------------------------- 1 | const { expect } = require('chai'); 2 | const { LCS_length, subproblem } = require('./LCS'); 3 | 4 | xdescribe('Longest Common Subsequence (dynamic programming)', () => { 5 | 6 | 7 | it('should use two functions', () => { 8 | expect(LCS_length).to.exist; 9 | expect(subproblem).to.exist 10 | }) 11 | 12 | it('should work', () => { 13 | expect(LCS_length('abc', 'ac')).to.equal('ac') 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /LCS/README.md: -------------------------------------------------------------------------------- 1 | # Longest Common Subsequence 2 | 3 | ### Problem 4 | 5 | Write a function called LCS that returns the longest subsequence common to the two passed in sequences. 6 | 7 | A subsequence is a collection of characters that maintains the same order but does not need to be consecutive. 8 | 9 | For example: 10 | 11 | sequence one is 'abc' 12 | sequence two is 'axyzc' 13 | 14 | subsequencesOne = 'a', 'b', 'c', 'ab', 'ac', 'bc' 15 | Notice how 'ac' is a subsequence that skips the letter b 16 | 17 | LCS('abc', 'axyzc') = 'ac' 18 | 19 | ### Note: 20 | Implement LCS using dynamic programming techniques. In this case use specifically memoization. 21 | 22 | -------------------------------------------------------------------------------- /LeftLeaningRedBlackBST/LLredBlackBST.js: -------------------------------------------------------------------------------- 1 | const RED = 'RED' // less problems when debugging 2 | const BLACK = 'BLACK' 3 | 4 | class Node { 5 | constructor(key, value, color = BLACK, left = null, right = null) { 6 | this.key = key; 7 | this.value = value; 8 | this.color = color; 9 | this.left = left; 10 | this.right = right; 11 | } 12 | } 13 | 14 | class LLredBlackBST { 15 | constructor() { 16 | this.root = null; 17 | } 18 | 19 | isRed(node) { 20 | if (node === null) return false; 21 | return node.color === RED; 22 | } 23 | 24 | // iterates through tree to check if item exists in tree. Similar to elementary BST only with the added key and values. 25 | search(key) { 26 | let currentNode = this.root; 27 | 28 | while (currentNode !== null) { 29 | let currentNodeKey = currentNode.key; 30 | 31 | if (key < currentNodeKey) currentNode = currentNode.left 32 | else if (key > currentNodeKey) currentNode = currentNode.right 33 | else return currentNode.value 34 | } 35 | 36 | return null; 37 | } 38 | 39 | // makes right leaning 3-tree into left leaning 3-tree 40 | rotateLeft(node) { 41 | if (!node.right) return; 42 | 43 | let temp = node.right; // temp var to prevent garbage collection 44 | node.right = temp.left; // set right connect to be left connection 45 | temp.left = node; // set temp left to node So temp has a right and left 46 | temp.color = node.color; // keep the attachment color(can only be black) 47 | node.color = RED; // set the new attachment to be red because this is the left 48 | return temp; 49 | } 50 | 51 | // exact opposite of rotate left for the most part 52 | rotateRight(node) { 53 | if (!node.left) return; 54 | 55 | let temp = node.left; 56 | node.left = temp.right; 57 | temp.right = node; 58 | temp.color = node.color; 59 | node.color = RED; 60 | return temp; 61 | } 62 | 63 | // in the temporary case where we have a node with two red branches on its children connection 64 | // we made the attachment to the node RED and its branches black 65 | flipColor(node) { 66 | node.color = RED; 67 | node.left.color = BLACK; 68 | node.right.color = BLACK; 69 | } 70 | 71 | // set new nodes with key and value 72 | // calls our private _insert method 73 | insert(key, value) { 74 | this.root = this._insert(this.root, key, value); // sets root to return value 75 | this.root.color = BLACK; // root node default will be black until flipColor is called to change this to red 76 | } 77 | 78 | _insert(node, key, value) { 79 | if (!node) return new Node(key, value, RED) // base case 80 | 81 | // recursively traverse tree to add node 82 | if (key < node.key) node.left = this._insert(node.left, key, value); 83 | else if (key > node.key) node.right = this._insert(node.right, key, value); 84 | else node.value = value; // if key exists we set the value 85 | 86 | // restructure tree 87 | 88 | // rotate left if the the right is red and left is black 89 | if (this.isRed(node.right) && !this.isRed(node.left)) node = this.rotateLeft(node); 90 | 91 | // rotate right if parent and child are both red 92 | if (this.isRed(node.left) && this.isRed(node.left.left)) node = this.rotateRight(node); 93 | 94 | // flip colors if both children are red 95 | if (this.isRed(node.left) && this.isRed(node.right)) this.flipColor(node); 96 | 97 | return node; // don't forget to return the node 98 | } 99 | 100 | } 101 | 102 | module.exports = { LLredBlackBST, RED, BLACK, Node } 103 | -------------------------------------------------------------------------------- /LeftLeaningRedBlackBST/LLredBlackBST.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const exportedModules = require('./LLredBlackBSTStart'); 3 | const LLredBlackBST = exportedModules.LLredBlackBST; 4 | const Node = exportedModules.Node; 5 | const RED = exportedModules.RED; 6 | const BLACK = exportedModules.BLACK; 7 | 8 | xdescribe('RED and BLACK', () => { 9 | it('should just be equal to the strings RED and BLACK for easier debugging', () => { 10 | expect(RED).to.equal('RED'); 11 | expect(BLACK).to.equal('BLACK'); 12 | }) 13 | }) 14 | 15 | xdescribe('Node', () => { 16 | let node; 17 | beforeEach(() => { 18 | node = new Node() 19 | }) 20 | 21 | describe('Constuctor', () => { 22 | 23 | it('should have a property key, value, left, right, and color on the class', () => { 24 | expect(node).to.have.ownProperty('key') 25 | expect(node).to.have.ownProperty('value') 26 | expect(node).to.have.ownProperty('left') 27 | expect(node).to.have.ownProperty('right') 28 | expect(node).to.have.ownProperty('color') 29 | }) 30 | 31 | it('should have left and right default to null', () => { 32 | expect(node.left).to.be.null 33 | expect(node.right).to.be.null 34 | }) 35 | 36 | it('should have color default to black', () => { 37 | expect(node.color).to.equal(BLACK) 38 | }) 39 | }) 40 | }) 41 | 42 | xdescribe('Left Leaning Red Black Binary Search Tree', () => { 43 | 44 | let LLRBBST; 45 | beforeEach(() => { 46 | LLRBBST = new LLredBlackBST() 47 | }) 48 | 49 | describe('Constuctor', () => { 50 | it('should have a property root on the class', () => { 51 | expect(LLRBBST).to.have.ownProperty('root') 52 | }) 53 | 54 | it('should default to null', () => { 55 | expect(LLRBBST.root).to.be.null 56 | }) 57 | }) 58 | 59 | describe('isRed', () => { 60 | it('should be a property on the class', () => { 61 | expect(LLRBBST).to.have.property('isRed') 62 | }) 63 | 64 | it('should return false if the node is null', () => { 65 | expect(LLRBBST.isRed(null)).to.be.false; 66 | }) 67 | 68 | it('should return the correct boolean if the node is red', () => { 69 | expect(LLRBBST.isRed({color: RED})).to.be.true; 70 | expect(LLRBBST.isRed({color: BLACK})).to.be.false; 71 | }) 72 | }) 73 | 74 | describe('Search', () => { 75 | it('should be a property on the class', () => { 76 | expect(LLRBBST).to.have.property('search') 77 | }) 78 | 79 | it('should return null if the key is not found', () => { 80 | expect(LLRBBST.search('GIBBERISH')).to.be.null; 81 | }) 82 | 83 | it('should return the value if the search key and the root key are equal', () => { 84 | let aNode = new Node('A', 1) 85 | let rNode = new Node('R', 2) 86 | let tNode = new Node('T', 3) 87 | let zNode = new Node('Z', 4) 88 | 89 | let leftNode = new Node('E', 5, null, aNode, rNode) 90 | let rightNode = new Node('U', 6, null, tNode, zNode) 91 | let mid = new Node('S', 7, null, leftNode, rightNode) 92 | LLRBBST.root = mid; 93 | 94 | expect(LLRBBST.search('A')).to.equal(1) 95 | expect(LLRBBST.search('R')).to.equals(2) 96 | expect(LLRBBST.search('T')).to.equal(3) 97 | expect(LLRBBST.search('Z')).to.equal(4) 98 | expect(LLRBBST.search('E')).to.equal(5) 99 | expect(LLRBBST.search('U')).to.equal(6) 100 | expect(LLRBBST.search('S')).to.equal(7) 101 | }) 102 | }) 103 | 104 | describe('rotateLeft', () => { 105 | it('should be a property on the class', () => { 106 | expect(LLRBBST).to.have.property('rotateLeft') 107 | }) 108 | 109 | it('should reorient node to lean left if it is leaning right with correct color category', () => { 110 | let aNode = new Node('A') 111 | let leftChild = new Node('R') 112 | let rightChild = new Node('T') 113 | let sNode = new Node('S', null, RED, leftChild, rightChild) 114 | let mid = new Node('E', null, null, aNode, sNode) 115 | let newNode = LLRBBST.rotateLeft(mid) 116 | 117 | expect(newNode.key).to.equal('S') 118 | expect(newNode.left.key).to.equal('E') 119 | expect(newNode.left.color).to.equal(RED) // the left node is red 120 | expect(newNode.left.left.key).to.equal('A') 121 | expect(newNode.left.left.color).to.equal(BLACK) 122 | expect(newNode.left.right.key).to.equal('R') 123 | expect(newNode.left.right.color).to.equal(BLACK) 124 | expect(newNode.right.key).to.equal('T') 125 | expect(newNode.right.color).to.equal(BLACK) 126 | }) 127 | 128 | }) 129 | 130 | describe('rotateRight', () => { 131 | it('should be a property on the class', () => { 132 | expect(LLRBBST).to.have.property('rotateLeft') 133 | }) 134 | 135 | it('should reorient node to lean right if it is leaning left with correct color category', () => { 136 | let tNode = new Node('T') 137 | let leftChild = new Node('A') 138 | let rightChild = new Node('R') 139 | let eNode = new Node('E', null, RED, leftChild, rightChild) 140 | let mid = new Node('S', null, null, eNode, tNode) 141 | 142 | let newNode = LLRBBST.rotateRight(mid) 143 | expect(newNode.key).to.equal('E') 144 | expect(newNode.left.key).to.equal('A') 145 | expect(newNode.left.color).to.equal(BLACK) 146 | expect(newNode.right.key).to.equal('S') 147 | expect(newNode.right.color).to.equal(RED) // the right node is red 148 | expect(newNode.right.left.key).to.equal('R') 149 | expect(newNode.right.left.color).to.equal(BLACK) 150 | expect(newNode.right.right.key).to.equal('T') 151 | expect(newNode.right.right.color).to.equal(BLACK) 152 | }) 153 | }) 154 | 155 | describe('flipColor', () => { 156 | it('should be a property on the class', () => { 157 | expect(LLRBBST).to.have.property('flipColor') 158 | }) 159 | 160 | it('should set a nodes color to red and its children to black', () => { 161 | let leftNode = new Node('LEFT', null, RED) 162 | let rightNode = new Node('RIGHT', null, RED) 163 | let node = new Node('KEY', null, BLACK, leftNode, rightNode) 164 | LLRBBST.flipColor(node); 165 | expect(node.color).to.equal(RED) 166 | expect(node.left.color).to.equal(BLACK) 167 | expect(node.right.color).to.equal(BLACK) 168 | }) 169 | 170 | }) 171 | 172 | describe('insert', () => { 173 | it('should be a property on the class', () => { 174 | expect(LLRBBST).to.have.property('insert') 175 | }) 176 | 177 | it('should take two parameters and call the _insert function with the root and those two parameters', () => { 178 | LLRBBST._insert = (root, key, value) => { 179 | return { key, value } 180 | } 181 | 182 | LLRBBST.insert('k' , 'v') 183 | expect(LLRBBST.root.key).to.equal('k') 184 | expect(LLRBBST.root.value).to.equal('v') 185 | }) 186 | 187 | it('should set the root color to black after the _insert function is called', () => { 188 | expect(LLRBBST.root).to.be.null 189 | LLRBBST.insert('S') 190 | expect(LLRBBST.root.color).to.equal(BLACK) 191 | }) 192 | }) 193 | 194 | describe('_insert', () => { 195 | it('should be a property on the class', () => { 196 | expect(LLRBBST).to.have.property('_insert') 197 | }) 198 | 199 | it('should be able to add one new node to the tree', () => { 200 | LLRBBST.insert('S', 1) 201 | expect(LLRBBST.root.key).to.equal('S') 202 | expect(LLRBBST.root.value).to.equal(1) 203 | }) 204 | 205 | it('should be able to add two nodes to the tree into the proper place with their proper colors', () => { 206 | LLRBBST.insert('S', 1) 207 | LLRBBST.insert('E', 2) 208 | expect(LLRBBST.root.left.key).to.equal('E') 209 | expect(LLRBBST.root.left.value).to.equal(2) 210 | }) 211 | 212 | // This test spec will require you to call rotateRight and flipColors 213 | it('should be able to more than two nodes to the tree into the proper place with their proper colors', () => { 214 | // expect(1).to.equal(2) 215 | LLRBBST.insert('S', 1) 216 | LLRBBST.insert('E', 2) 217 | LLRBBST.insert('A', 3) 218 | expect(LLRBBST.root.key).to.equal('E') 219 | expect(LLRBBST.root.value).to.equal(2) 220 | expect(LLRBBST.root.color).to.equal(BLACK) 221 | expect(LLRBBST.root.left.key).to.equal('A') 222 | expect(LLRBBST.root.left.value).to.equal(3) 223 | expect(LLRBBST.root.left.color).to.equal(BLACK) 224 | expect(LLRBBST.root.right.key).to.equal('S') 225 | expect(LLRBBST.root.right.value).to.equal(1) 226 | expect(LLRBBST.root.right.color).to.equal(BLACK) 227 | }) 228 | 229 | // This test spec will require you to call rotateLeft, rotateRight, and flipColors 230 | it('should be able to add MANY NODES to the tree into their proper place, with the proper colors', () => { 231 | let SEARCH = { 232 | S: 1, 233 | E: 2, 234 | A: 3, 235 | R: 4, 236 | C: 5, 237 | H: 6, 238 | } 239 | 240 | LLRBBST.insert('S', 1) 241 | LLRBBST.insert('E', 2) 242 | LLRBBST.insert('A', 3) 243 | LLRBBST.insert('R', 4) 244 | LLRBBST.insert('C', 5) 245 | LLRBBST.insert('H', 6) 246 | 247 | // ROOT 248 | 249 | expect(LLRBBST.root.key).to.equal('R') 250 | expect(LLRBBST.root.value).to.equal(SEARCH.R) 251 | expect(LLRBBST.root.color).to.equal(BLACK) 252 | 253 | // LEFT OF R COMPLETE LEFT SIDE OF ROOT ================ 254 | 255 | expect(LLRBBST.root.left.key).to.equal('E') 256 | expect(LLRBBST.root.left.value).to.equal(SEARCH.E) 257 | expect(LLRBBST.root.left.color).to.equal(RED) 258 | 259 | // LEFT OF E 260 | 261 | expect(LLRBBST.root.left.left.key).to.equal('C') 262 | expect(LLRBBST.root.left.left.value).to.equal(SEARCH.C) 263 | expect(LLRBBST.root.left.left.color).to.equal(BLACK) 264 | 265 | // LEFT OF C 266 | 267 | expect(LLRBBST.root.left.left.left.key).to.equal('A') 268 | expect(LLRBBST.root.left.left.left.value).to.equal(SEARCH.A) 269 | expect(LLRBBST.root.left.left.left.color).to.equal(RED) 270 | 271 | // RIGHT OF R COMPLETE RIGHT SIDE OF ROOT ================ 272 | 273 | expect(LLRBBST.root.right.key).to.equal('S') 274 | expect(LLRBBST.root.right.value).to.equal(SEARCH.S) 275 | expect(LLRBBST.root.right.color).to.equal(BLACK) 276 | }) 277 | 278 | // this test spec requires MANY rotations and restructures the entire tree 279 | it('should be able to restructure with many inserts', () => { 280 | let SEARCHEXAMPLE = { 281 | S: 1, 282 | E: 2, 283 | A: 3, 284 | R: 4, 285 | C: 5, 286 | H: 6, 287 | X: 7, 288 | M: 8, 289 | P: 9, 290 | L: 10 291 | } 292 | 293 | LLRBBST.insert('S', 1) 294 | LLRBBST.insert('E', 2) 295 | LLRBBST.insert('A', 3) 296 | LLRBBST.insert('R', 4) 297 | LLRBBST.insert('C', 5) 298 | LLRBBST.insert('H', 6) 299 | LLRBBST.insert('X', 7) 300 | LLRBBST.insert('M', 8) 301 | LLRBBST.insert('P', 9) 302 | LLRBBST.insert('L', 10) 303 | 304 | // ROOT 305 | 306 | expect(LLRBBST.root.key).to.equal('M') 307 | expect(LLRBBST.root.value).to.equal(SEARCHEXAMPLE.M) 308 | expect(LLRBBST.root.color).to.equal(BLACK) 309 | 310 | // LEFT OF M COMPLETE LEFT SIDE OF ROOT =========================== 311 | 312 | expect(LLRBBST.root.left.key).to.equal('E') 313 | expect(LLRBBST.root.left.value).to.equal(SEARCHEXAMPLE.E) 314 | expect(LLRBBST.root.left.color).to.equal(BLACK) 315 | 316 | // LEFT OF E 317 | 318 | expect(LLRBBST.root.left.left.key).to.equal('C') 319 | expect(LLRBBST.root.left.left.value).to.equal(SEARCHEXAMPLE.C) 320 | expect(LLRBBST.root.left.left.color).to.equal(BLACK) 321 | 322 | // LEFT OF C 323 | expect(LLRBBST.root.left.left.left.key).to.equal('A') 324 | expect(LLRBBST.root.left.left.left.value).to.equal(SEARCHEXAMPLE.A) 325 | expect(LLRBBST.root.left.left.left.color).to.equal(RED) 326 | 327 | // RIGHT OF E 328 | 329 | expect(LLRBBST.root.left.right.key).to.equal('L') 330 | expect(LLRBBST.root.left.right.value).to.equal(SEARCHEXAMPLE.L) 331 | expect(LLRBBST.root.left.right.color).to.equal(BLACK) 332 | 333 | // LEFT OF L 334 | 335 | expect(LLRBBST.root.left.right.left.key).to.equal('H') 336 | expect(LLRBBST.root.left.right.left.value).to.equal(SEARCHEXAMPLE.H) 337 | expect(LLRBBST.root.left.right.left.color).to.equal(RED) 338 | 339 | // RIGHT OF M COMPLETE RIGHT SIDE OF ROOT =========================== 340 | 341 | expect(LLRBBST.root.right.key).to.equal('R') 342 | expect(LLRBBST.root.right.value).to.equal(SEARCHEXAMPLE.R) 343 | expect(LLRBBST.root.right.color).to.equal(BLACK) 344 | 345 | // LEFT OF R 346 | 347 | expect(LLRBBST.root.right.left.key).to.equal('P') 348 | expect(LLRBBST.root.right.left.value).to.equal(SEARCHEXAMPLE.P) 349 | expect(LLRBBST.root.right.left.color).to.equal(BLACK) 350 | 351 | // RIGHT OF R 352 | 353 | expect(LLRBBST.root.right.right.key).to.equal('X') 354 | expect(LLRBBST.root.right.right.value).to.equal(SEARCHEXAMPLE.X) 355 | expect(LLRBBST.root.right.right.color).to.equal(BLACK) 356 | 357 | // LEFT OF X 358 | 359 | expect(LLRBBST.root.right.right.left.key).to.equal('S') 360 | expect(LLRBBST.root.right.right.left.value).to.equal(SEARCHEXAMPLE.S) 361 | expect(LLRBBST.root.right.right.left.color).to.equal(RED) 362 | }) 363 | 364 | }) 365 | 366 | }) 367 | -------------------------------------------------------------------------------- /LeftLeaningRedBlackBST/LLredBlackBSTStart.js: -------------------------------------------------------------------------------- 1 | class LLredBlackBST { 2 | 3 | } 4 | 5 | class Node { 6 | 7 | } 8 | 9 | module.exports = { LLredBlackBST, Node } 10 | -------------------------------------------------------------------------------- /LeftLeaningRedBlackBST/README.md: -------------------------------------------------------------------------------- 1 | # Left Leaning Red Black Binary Search Tree 2 | 3 | # What is it? 4 | This BST is a simple implementation of a 2-3 Tree. So what is a 2-3 Tree? 5 | 6 | The 2-3 Tree is that: 7 | 8 | It allows 1 or 2 keys per node. 9 | * 2-node : has one key and two children 10 | * 3-node: has two keys and three children 11 | * When searching 12 | 1. value < left key go left 13 | 2. value > right key go right 14 | 3. value between left and right key go down middle branch 15 | 16 | Perfect Balance: 17 | * Every path from root to node has the same length 18 | 19 | Symmetric Order: 20 | * Inorder traversal yields keys in ascending order 21 | 22 | Insert Photo Example Here: 23 | 24 | Searching 25 | 26 | Inserting 27 | 28 | In a LLRBBST(LOL sorry too lazy to type it out) the red links would indicate if the the current node has 3 children. It "glues" the nodes together. Black would indicate it is just a regular node. It is "Left leaning" because the the node on the left holds 2 of 3 of the node's children. The right is just one of the three children. 29 | 30 | Image photo here: 31 | 32 | So any 2-3 Tree can be representing as a LLRBBST. There is a 1-1 correspondence between the two. If it is difficult to see it, just imagine the red links are horizontal. And the horizontal connections with the red link is one node(with two keys). Here's a photo to clear it up. 33 | 34 | Image Photo here: 35 | 36 | Key points: 37 | It is a BST such that: 38 | * No node has two red links conneted to it. 39 | * Every path from the root to null has the same number of black links 40 | * "Perfect black balance" 41 | 42 | # What needs to be implemented. 43 | 44 | `search` search is actually exactly the same as an elementary BST. We can just ignore color. (Although the search will be faster because of better balance) 45 | * One of the reasons why LLRBBST is used is because it is simple to implement there is no need to create many new functions as they are the same as a elementary BST. 46 | 47 | -------------------------------------------------------------------------------- /LinkedList/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dye784/Algos-and-Data-Structures/147d898c17560c4e31b292ac8b078a647f57f291/LinkedList/README.md -------------------------------------------------------------------------------- /LinkedList/linkedList.js: -------------------------------------------------------------------------------- 1 | class Node { 2 | constructor(v) { 3 | this.value = v; 4 | this.previous = null; 5 | this.next = null; 6 | } 7 | } 8 | 9 | class LinkedList { 10 | constructor() { 11 | this.head = null; 12 | this.tail = null; 13 | } 14 | 15 | addToHead(v) { 16 | const newNode = new Node(v); 17 | const formerHead = this.head; 18 | this.head = newNode; 19 | 20 | if (formerHead) { 21 | formerHead.previous = newNode; 22 | newNode.next = formerHead; 23 | } 24 | 25 | if (!this.tail) this.tail = newNode; 26 | } 27 | 28 | addToTail(v) { 29 | const newNode = new Node(v); 30 | const formerTail = this.tail; 31 | this.tail = newNode; 32 | 33 | if (formerTail) { 34 | formerTail.next = newNode; 35 | newNode.previous = formerTail; 36 | } 37 | 38 | if (!this.head) this.head = this.tail; 39 | } 40 | 41 | removeHead() { 42 | const removedHead = this.head; 43 | 44 | if (!removedHead) return; 45 | 46 | if (removedHead.next) { 47 | this.head = removedHead.next; 48 | this.head.previous = null; 49 | } else { 50 | this.head = null; 51 | this.tail = null; 52 | } 53 | 54 | return removedHead.value; 55 | } 56 | 57 | removeTail() { 58 | const removedTail = this.tail; 59 | 60 | if (!removedTail) return; 61 | 62 | if (removedTail.previous) { 63 | this.tail = removedTail.previous; 64 | this.tail.next = null; 65 | } else { 66 | this.head = null; 67 | this.tail = null; 68 | } 69 | 70 | return removedTail.value; 71 | } 72 | 73 | search(comparator) { 74 | let currentNode = this.head; 75 | 76 | if (typeof comparator === 'string') { 77 | const comparatorString = comparator; 78 | comparator = function (elementValue) { 79 | return comparatorString == elementValue; 80 | } 81 | } 82 | 83 | while (currentNode !== null) { 84 | if (comparator(currentNode.value)) return currentNode.value; 85 | currentNode = currentNode.next; 86 | } 87 | 88 | return null; 89 | 90 | } 91 | 92 | size() { 93 | let i = 0; 94 | let currentNode = this.head; 95 | 96 | while (currentNode !== null) { 97 | i++; 98 | currentNode = currentNode.next; 99 | } 100 | 101 | return i; 102 | } 103 | 104 | } 105 | 106 | module.exports = { Node, LinkedList } 107 | -------------------------------------------------------------------------------- /LinkedList/linkedList.test.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dye784/Algos-and-Data-Structures/147d898c17560c4e31b292ac8b078a647f57f291/LinkedList/linkedList.test.js -------------------------------------------------------------------------------- /LinkedList/linkedListStart.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dye784/Algos-and-Data-Structures/147d898c17560c4e31b292ac8b078a647f57f291/LinkedList/linkedListStart.js -------------------------------------------------------------------------------- /MergeSort/README.md: -------------------------------------------------------------------------------- 1 | # Merge Sort 2 | 3 | ### What is it? 4 | Merge sort continuously (read: recursively) divides a list until it's been split into its smallest possible elements (arrays of length `1`). It may be helpful to picture a binary tree. 5 | 6 | Merge sort then joins the smaller elements into larger ones while sorting them, taking advantage of the fact that each list being merged has already been sorted (we only need to compare the first element of each list). 7 | 8 | Since we're breaking down the list into smaller items to be sorted as many times as there are elements, this algorithm has a runtime of O(n log n), or "linearithmic" runtime, generally the fastest possible time in a sort and often faster than insertion or selection sorts, for example, which have quadratic runtimes (O(n^2)). However since merge sort doesn't mutate the array in place, it does use (usually trivially) more memory. 9 | 10 | 11 | ### Note: 12 | This implementation is taken from a Full Stack Academy Workshop. 13 | 14 | The Coursera course implements a classic version of merge sort using insertion sort as its sorting algo. But the basis is the same. 15 | 16 | -------------------------------------------------------------------------------- /MergeSort/baselineTests/bubbleBaseline.js: -------------------------------------------------------------------------------- 1 | // from http://www.stoimen.com/blog/2010/07/09/friday-algorithms-javascript-bubble-sort/ 2 | const bubbleSort = (toBeSorted) => { 3 | var a = toBeSorted.slice(0); 4 | var swapped; 5 | do { 6 | swapped = false; 7 | for (var i=0; i < a.length-1; i++) { 8 | if (a[i] > a[i+1]) { 9 | var temp = a[i]; 10 | a[i] = a[i+1]; 11 | a[i+1] = temp; 12 | swapped = true; 13 | } 14 | } 15 | } while (swapped); 16 | } 17 | 18 | module.exports = bubbleSort -------------------------------------------------------------------------------- /MergeSort/baselineTests/insertionBaseline.js: -------------------------------------------------------------------------------- 1 | const insertionSort = (toBeSortedArray) => { 2 | let array = toBeSortedArray.slice(0), 3 | i, j; 4 | for (let i = 0; i < array.length; i++) { 5 | let value = array[i]; 6 | for (j = i - 1; j > -1 && array[j] > value; j--) { 7 | array[j + 1] = array[j]; 8 | } 9 | array[j + 1] = value; 10 | } 11 | return array; 12 | } 13 | 14 | module.exports = insertionSort; -------------------------------------------------------------------------------- /MergeSort/mergeSort.js: -------------------------------------------------------------------------------- 1 | // From Full Stack Sorting Workshop 2 | const mergeSort = {}; 3 | 4 | mergeSort.split = function (array) { 5 | const center = array.length / 2, 6 | left = array.slice(0, center), 7 | right = array.slice(center); 8 | return [left, right]; 9 | } 10 | 11 | mergeSort.merge = function (left, right) { 12 | const merged = []; 13 | let leftIdx = 0, 14 | rightIdx = 0; 15 | while (leftIdx < left.length && rightIdx < right.length) { 16 | if (left[leftIdx] < right[rightIdx]) { 17 | merged.push(left[leftIdx++]); 18 | } else { 19 | merged.push(right[rightIdx++]); 20 | } 21 | } 22 | 23 | merged.push(...left.slice(leftIdx)); 24 | merged.push(...right.slice(rightIdx)); 25 | return merged; 26 | } 27 | 28 | mergeSort.main = function (array) { 29 | if (array.length < 2) return array; 30 | const splits = mergeSort.split(array), 31 | left = splits[0], 32 | right = splits[1]; 33 | 34 | let leftResult = left.length > 1 ? mergeSort.main(left) : left; 35 | let rightResult = right.length > 1 ? mergeSort.main(right) : right; 36 | 37 | return mergeSort.merge(leftResult, rightResult); 38 | } 39 | 40 | module.exports = mergeSort; 41 | -------------------------------------------------------------------------------- /MergeSort/mergeSort.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const chai = require('chai'); 4 | const expect = chai.expect; 5 | 6 | const sinon = require('sinon'); 7 | const chaiSinon = require('chai-sinon'); 8 | chai.use(chaiSinon); 9 | 10 | const { split, merge } = require('./mergeSort'); 11 | const mergeSort = require('./mergeSort').main; 12 | const mergeFuncs = require('./mergeSort'); 13 | 14 | const insertionSort = require('./baselineTests/insertionBaseline'); 15 | const bubbleSort = require('./baselineTests/bubbleBaseline'); 16 | 17 | describe.only('Merge sort', () => { 18 | 19 | describe('split', () => { 20 | 21 | it('splits an array in half', () => { 22 | expect(split([])).to.deep.equal([[], []]); 23 | }); 24 | 25 | it('splits array of even length down the middle', () => { 26 | expect(split([1, 2])).to.deep.equal([[1], [2]]); 27 | }); 28 | 29 | it('splits array of odd length down the middle, wighted to the right', () => { 30 | expect(split([1, 2, 3])).to.deep.equal([[1], [2, 3]]); 31 | }); 32 | 33 | }); 34 | 35 | describe('merge', () => { 36 | 37 | it('combines two arrays into one', function () { 38 | expect(merge([], [])).to.deep.equal([]); 39 | }); 40 | 41 | it('combines two sorted arrays of deep.equal length into one sorted array', () => { 42 | expect(merge([1, 3, 9], [2, 6, 15])).to.deep.equal([1, 2, 3, 6, 9, 15]); 43 | expect(merge([1, 2, 3, 4], [5, 6, 7, 8])).to.deep.equal([1, 2, 3, 4, 5, 6, 7, 8]); 44 | }); 45 | 46 | it('combines sorted arrays of unequal lengths into one sorted array', () => { 47 | expect(merge([1, 2, 3, 7, 9, 11], [4, 5, 50])).to.deep.equal([1, 2, 3, 4, 5, 7, 9, 11, 50]); 48 | }); 49 | 50 | }); 51 | 52 | describe('mergeSort', () => { 53 | 54 | it('returns a sorted array even if it\'s 0 or 1 elements long', function () { 55 | expect(mergeSort([])).to.deep.equal([]); 56 | expect(mergeSort([42])).to.deep.equal([42]); 57 | }); 58 | 59 | it('sorts correctly', () => { 60 | const sorted = mergeSort([29, 8, 100, 17, 60, 43, -20]); 61 | expect(sorted).to.deep.equal([-20, 8, 17, 29, 43, 60, 100]); 62 | }); 63 | 64 | describe('function calls', () => { 65 | const toBeSortedFour = [4, 3, 2, 1]; 66 | const toBeSortedEight = [8, 7, 6, 5, 4, 3, 2, 1]; 67 | 68 | beforeEach('populate spies', () => { 69 | sinon.spy(mergeFuncs, 'main'); 70 | sinon.spy(mergeFuncs, 'merge'); 71 | sinon.spy(mergeFuncs, 'split'); 72 | }); 73 | 74 | afterEach('release spies', () => { 75 | mergeFuncs.main.restore(); 76 | mergeFuncs.merge.restore(); 77 | mergeFuncs.split.restore(); 78 | }); 79 | 80 | it('uses recursion', () => { 81 | mergeFuncs.main(toBeSortedFour); 82 | expect(mergeFuncs.main.callCount).to.be.greaterThan(1); 83 | }); 84 | 85 | it('calls the `merge` and `split` functions', () => { 86 | mergeFuncs.main(toBeSortedEight); 87 | expect(mergeFuncs.merge.called).to.be.true; 88 | expect(mergeFuncs.split.called).to.be.true; 89 | expect(mergeFuncs.merge.callCount).to.be.greaterThan(1); 90 | expect(mergeFuncs.split.callCount).to.be.greaterThan(1); 91 | }); 92 | 93 | }); 94 | 95 | describe('execution time', () => { 96 | const toBeSortedSixteen = [16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; 97 | 98 | it('is considerably faster than insertion sort or bubble sort with a large input set', function (done) { 99 | // ensure that mocha doesn't time out. 100 | // If you are on a slower machine and getting timeout errors, speed this up 101 | this.timeout(5000); 102 | // make a quite long array in very bad sorting order 103 | const toBeSortedLong = toBeSortedSixteen 104 | .reduce((accum, curr) => { 105 | return accum.concat(new Array(2000).fill(curr)); 106 | }, []); 107 | 108 | var mergeStart = new Date(); 109 | mergeSort(toBeSortedLong); 110 | var mergeTime = new Date() - mergeStart; 111 | 112 | var insertStart = new Date(); 113 | insertionSort(toBeSortedLong); 114 | var insertionTime = new Date() - insertStart; 115 | 116 | var bubbleStart = new Date(); 117 | bubbleSort(toBeSortedLong); 118 | var bubbleTime = new Date() - bubbleStart; 119 | 120 | expect(mergeTime).to.be.lessThan(insertionTime); 121 | expect(mergeTime).to.be.lessThan(bubbleTime); 122 | 123 | expect(mergeTime).to.be.at.most(insertionTime / 3); 124 | expect(mergeTime).to.be.at.most(bubbleTime / 10); 125 | 126 | done(); 127 | }); 128 | 129 | }); 130 | 131 | }); 132 | 133 | }); 134 | -------------------------------------------------------------------------------- /MergeSort/mergeSortStart.js: -------------------------------------------------------------------------------- 1 | const mergeSort = {}; 2 | 3 | 4 | mergeSort.split = function (array) { 5 | 6 | } 7 | 8 | mergeSort.merge = function (left, right) { 9 | 10 | } 11 | 12 | mergeSort.main = function (array) { 13 | 14 | } 15 | 16 | module.exports = mergeSort -------------------------------------------------------------------------------- /Queues/README.md: -------------------------------------------------------------------------------- 1 | # Queues 2 | 3 | ### The Queue 4 | 5 | Queses are very similar to Stacks except for some terminology and a few key features. The Queue follows the FIFO rule, first in - first out. 6 | 7 | Adding to the queue we add to the front. Removing from the Queue we remove from the end. This is the same as if you were on line for something like say a line to see the movie Rogue One. The implementation of a queue will be very similar to the Stack. 8 | 9 | `enqueue` is adding to Queue 10 | `dequeue` is removing from Queue 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Queues/queueLL.js: -------------------------------------------------------------------------------- 1 | class QueueLL { 2 | constructor() { 3 | this.head = null; 4 | this.tail = null; 5 | } 6 | 7 | // check if stack has anything 8 | isEmpty() { 9 | return this.head === null; 10 | } 11 | 12 | // add to tail 13 | enqueue(str) { 14 | const newNode = new Node(str); 15 | const oldTail = this.tail; 16 | this.tail = newNode 17 | 18 | if(this.isEmpty()) this.head = newNode; 19 | else oldTail.next = this.tail 20 | } 21 | 22 | // remove from head 23 | dequeue() { 24 | const str = this.head.str 25 | this.head = this.head.next 26 | 27 | if(this.isEmpty()) this.tail = null; 28 | 29 | return str; 30 | } 31 | } 32 | 33 | class Node { 34 | constructor(str) { 35 | this.str = str; 36 | this.next = null; 37 | } 38 | } 39 | 40 | module.exports = { 41 | QueueLL, 42 | Node 43 | } 44 | -------------------------------------------------------------------------------- /Queues/queueLL.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const QueueLL = require('./queueLLStart').QueueLL; 3 | const Node = require('./queueLLStart').Node; 4 | 5 | xdescribe('QueueLL Algorithm', () => { 6 | describe('Node class', () => { 7 | describe('Constructor', () => { 8 | let node = new Node('42') 9 | 10 | it('should have a str property', () => { 11 | expect(node).to.have.ownProperty('str') 12 | }) 13 | 14 | it('should set str property to input', () => { 15 | expect(node.str).to.equal('42') 16 | }) 17 | 18 | it('should have a next property', () => { 19 | expect(node).to.have.ownProperty('next') 20 | }) 21 | 22 | it('should have the next property have default value of null', () => { 23 | expect(node.next).to.be.null 24 | }) 25 | }) 26 | }) 27 | 28 | describe('QueueLL', () => { 29 | let queue; 30 | let node; 31 | beforeEach(function() { 32 | queue = new QueueLL() 33 | node = new Node('HELLO EVERYBODY') 34 | }) 35 | 36 | describe('Constructor', () => { 37 | it('should have a head and tail property with default values of null', () => { 38 | expect(queue).to.have.ownProperty('head') 39 | expect(queue).to.have.ownProperty('tail') 40 | expect(queue.head).to.be.null 41 | expect(queue.tail).to.be.null 42 | }) 43 | }) 44 | 45 | describe('isEmpty', () => { 46 | it('should be a property on the class', () => { 47 | expect(queue).to.have.property('isEmpty') 48 | }) 49 | 50 | it('should be a function', () => { 51 | expect(queue.isEmpty).to.be.a('function') 52 | }) 53 | 54 | it('should be able to check if the queue is empty', () => { 55 | expect(queue.isEmpty()).to.be.true 56 | 57 | queue.head = { str: '42', next: null } 58 | 59 | expect(queue.isEmpty()).to.be.false 60 | }) 61 | }) 62 | 63 | describe('enqueue', () => { 64 | it('should be a property on the class', () => { 65 | expect(queue).to.have.property('enqueue') 66 | }) 67 | 68 | it('should be a function', () => { 69 | expect(queue.enqueue).to.be.a('function') 70 | }) 71 | 72 | it('should be able to add one item to the queue', () => { 73 | queue.enqueue('peanut butter') 74 | let whatItShouldLookLike = {str: 'peanut butter', next: null} 75 | expect(queue.head).to.deep.equal(whatItShouldLookLike) 76 | }) 77 | 78 | it('should be able to add MANY items to the queue', () => { 79 | queue.enqueue('0') 80 | queue.enqueue('1') 81 | queue.enqueue('2') 82 | queue.enqueue('3') 83 | expect(queue.head.str).to.equal('0') 84 | expect(queue.head.next.str).to.equal('1') 85 | expect(queue.head.next.next.str).to.equal('2') 86 | expect(queue.head.next.next.next.str).to.equal('3') 87 | }) 88 | }) 89 | 90 | describe('dequeue', () => { 91 | it('should be a property on the class', () => { 92 | expect(queue).to.have.property('dequeue') 93 | }) 94 | 95 | it('should be a function', () => { 96 | expect(queue.dequeue).to.be.a('function') 97 | }) 98 | 99 | it('should be able to remove one item from the queue', () => { 100 | queue.enqueue('0') 101 | queue.enqueue('1') 102 | queue.enqueue('2') 103 | expect(queue.dequeue()).to.equal('0') 104 | expect(queue.head.str).to.equal('1') 105 | expect(queue.head.next.str).to.equal('2') 106 | expect(queue.tail.str).to.equal('2') 107 | }) 108 | 109 | it('should be able handle special cases', () => { 110 | queue.enqueue('0') 111 | queue.enqueue('1') 112 | expect(queue.dequeue()).to.equal('0') 113 | expect(queue.head.str).to.equal(queue.tail.str) 114 | }) 115 | 116 | it('should be able to remove MANY items from the queue', () => { 117 | queue.enqueue('0') 118 | queue.enqueue('1') 119 | queue.enqueue('2') 120 | queue.enqueue('3') 121 | expect(queue.dequeue()).to.equal('0') 122 | expect(queue.head.next.next.next).to.be.null 123 | expect(queue.dequeue()).to.equal('1') 124 | expect(queue.head.next.next).to.be.null 125 | expect(queue.dequeue()).to.equal('2') 126 | expect(queue.head.next).to.be.null 127 | expect(queue.dequeue()).to.equal('3') 128 | expect(queue.head).to.be.null 129 | expect(queue.tail).to.be.null 130 | }) 131 | 132 | }) 133 | }) 134 | }) 135 | -------------------------------------------------------------------------------- /Queues/queueLLStart.js: -------------------------------------------------------------------------------- 1 | class QueueLL { 2 | 3 | } 4 | 5 | class Node { 6 | 7 | } 8 | 9 | module.exports = { 10 | QueueLL, 11 | Node 12 | } 13 | -------------------------------------------------------------------------------- /QuickFindUF/README.md: -------------------------------------------------------------------------------- 1 | # Quick Find 2 | 3 | This is an algo that is used to find if a there is a connection between nodes 4 | We represent it through the use of an array. Simple algorithm with a time complexity of 5 | n-squared. 6 | 7 | You begin by defining an array with each element being its respective index 8 | 9 | The function has two methods `areTheyConnected` and `connect` 10 | 11 | `areTheyConnected` is a function that takes two parameters. It is a function that returns a boolean. It checks if the two elements in the array have the same id. 12 | 13 | `connect` is a function that takes two parameters, index1 and index2. It sets the id of the first element to the second element. It also changes the all elements with the same connection(id) of the first element to the second element. 14 | -------------------------------------------------------------------------------- /QuickFindUF/quick-find.js: -------------------------------------------------------------------------------- 1 | // O(n^2) Quadratic time. Pretty slow 2 | class QuickFindUF { // Union Find Algo 3 | constructor(length) { 4 | this.arrayOfIds = Array(length); 5 | 6 | // sets id to its index in the array 7 | for (var i = 0; i < this.arrayOfIds.length; i++) { 8 | this.arrayOfIds[i] = i 9 | } 10 | } 11 | 12 | 13 | // checks if they are connected, returns a boolean 14 | // if the ids are the same then they are connected 15 | areTheyConnected(firstIndex, secondIndex) { 16 | return this.arrayOfIds[firstIndex] === this.arrayOfIds[secondIndex] 17 | } 18 | 19 | 20 | // connects the element at the firstIndex with the element in the secondIndex 21 | connect(firstIndex, secondIndex) { // AKA UNION 22 | 23 | // first and second ids of the elements 24 | // we must store it here as a variable because as we loop through the array we might erase it 25 | let firstId = this.arrayOfIds[firstIndex] 26 | let secondId = this.arrayOfIds[secondIndex] 27 | 28 | // goes through the whole array and any element with the id equal to the firstId 29 | // will be set to the secondId 30 | for (var i = 0; i < this.arrayOfIds.length; i++) { 31 | if(this.arrayOfIds[i] === firstId) this.arrayOfIds[i] = secondId 32 | } 33 | } 34 | } 35 | 36 | module.exports = QuickFindUF 37 | -------------------------------------------------------------------------------- /QuickFindUF/quick-find.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const QuickFindUF = require('./quick-findStart'); 3 | 4 | xdescribe('Quick Find Union Find Algorithm', () => { 5 | describe('QuickFindUF', () => { 6 | 7 | let newQF; 8 | beforeEach(function() { 9 | newQF = new QuickFindUF(10) 10 | }) 11 | 12 | describe('Constructor', () => { 13 | 14 | it('should have a property of arrayOfIds', () => { 15 | expect(newQF).to.have.ownProperty('arrayOfIds') 16 | }) 17 | 18 | it('arrayOfIds has a length equal to input', () => { 19 | expect(newQF.arrayOfIds.length).to.be.equal(10) 20 | }) 21 | 22 | it('arrayOfIds should be an array where the element is equal to its index', () => { 23 | expect(newQF.arrayOfIds).to.deep.equal([0,1,2,3,4,5,6,7,8,9]) 24 | }) 25 | }) 26 | 27 | describe('areTheyConnected', () => { 28 | 29 | it('should be a property on the class', () => { 30 | expect(newQF).to.have.property('areTheyConnected') 31 | }) 32 | 33 | it('should be a function', () => { 34 | expect(newQF.areTheyConnected).to.be.a('function') 35 | }) 36 | 37 | it('should tell if two elements have the same id', () => { 38 | const arr1 = [0,1,2,3,3,5,6,7,8,9] 39 | newQF.arrayOfIds = arr1; 40 | 41 | expect(newQF.areTheyConnected(0,1)).to.be.false; 42 | expect(newQF.areTheyConnected(3, 4)).to.be.true; 43 | expect(newQF.areTheyConnected(4, 8)).to.be.false; 44 | }) 45 | }) 46 | 47 | describe('connect', () => { 48 | it('should be a property on the class', () => { 49 | expect(newQF).to.have.property('connect') 50 | }) 51 | 52 | it('should be a function', () => { 53 | expect(newQF.connect).to.be.a('function') 54 | }) 55 | 56 | it('should change the ids for a single connect', () => { 57 | newQF.connect(4,5) 58 | expect(newQF.arrayOfIds).to.deep.equal([0,1,2,3,5,5,6,7,8,9]) 59 | }) 60 | 61 | it('the elements should be connected by the second parameters id(parameter)', () => { 62 | newQF.connect(8,3) 63 | expect(newQF.arrayOfIds).to.deep.equal([0,1,2,3,4,5,6,7,3,9]) 64 | }) 65 | 66 | it('should change the ids for a two connections', () => { 67 | newQF.connect(4,5) 68 | newQF.connect(4,6) 69 | expect(newQF.arrayOfIds).to.deep.equal([0,1,2,3,6,6,6,7,8,9]) 70 | }) 71 | 72 | it('should change the ids for a MANY connections', () => { 73 | newQF.connect(4,5) 74 | newQF.connect(6,0) 75 | newQF.connect(3,1) 76 | newQF.connect(8,2) 77 | newQF.connect(7,9) 78 | expect(newQF.arrayOfIds).to.deep.equal([0,1,2,1,5,5,0,9,2,9]) 79 | }) 80 | 81 | }) 82 | 83 | }) 84 | }) 85 | -------------------------------------------------------------------------------- /QuickFindUF/quick-findStart.js: -------------------------------------------------------------------------------- 1 | class QuickFindUF { 2 | 3 | } 4 | 5 | module.exports = QuickFindUF 6 | 7 | -------------------------------------------------------------------------------- /QuickSort/README.md: -------------------------------------------------------------------------------- 1 | # Quick Sort 2 | 3 | ### What does it do? 4 | Quick sort uses partitioning to speed up the run time of sorting an array. 5 | 6 | It uses a function `partition` selects a pivot then iterates through the array with two indicies, `i` an `j`. This function's job is to break up the array into two parts. We can place the pivot in such a way that it splits the array into two distinct parts. The left part of the array should be less than the pivot and the right part of the array should be greater than. 7 | 8 | We now know that the element that was the pivot will be in the correct position. 9 | 10 | If we perform this operation again on the sub arrays we will have a sorted array. Notice that quickSort can be more simply applied recursively. 11 | -------------------------------------------------------------------------------- /QuickSort/quickSort.js: -------------------------------------------------------------------------------- 1 | const swap = (array, idx1, idx2) => { 2 | let temp = array[idx1]; 3 | array[idx1] = array[idx2]; 4 | array[idx2] = temp; 5 | }; 6 | 7 | // function that goes through the array 8 | const partition = (array, lo, hi) => { 9 | let i = lo; 10 | let j = hi + 1; 11 | 12 | while (true) { 13 | 14 | // scan from left to right of array 15 | // as long as array[i] < array[lo] 16 | // increment i till we get something that violates 17 | while (array[++i] < array[lo]) { 18 | 19 | // break loop if index are the same 20 | if (i === hi) break; 21 | } 22 | 23 | // scan from right to left of array 24 | // as long as array[j] > array[lo] 25 | // decrement j till we get something that violates 26 | while (array[lo] < array[--j]) { 27 | 28 | // break loop if index are the same 29 | if (j === lo) break; 30 | } 31 | 32 | // break out if i >= j because we've looked 33 | // at entire array already 34 | if (i >= j) break; 35 | 36 | // swap the i and j elements 37 | swap(array, i, j); 38 | } 39 | 40 | // swap the pivot element with the j element to put it 41 | // in the middle of the array 42 | // this ensures that everything to the left of j 43 | // is less and everything to the right is greater 44 | swap(array, lo, j); 45 | return j; 46 | }; 47 | 48 | const quickSort = (array, lo = 0, hi = array.length - 1) => { 49 | if (hi <= lo) { return; } 50 | 51 | // find the index where everything pivots 52 | const j = partition(array, lo, hi); 53 | 54 | // recursively implement quick sort for each sub array 55 | 56 | // this is to the left of j 57 | quickSort(array, lo, j - 1); 58 | 59 | // this is to the right of j 60 | quickSort(array, j + 1, hi); 61 | 62 | return array; 63 | }; 64 | 65 | module.exports = quickSort; 66 | -------------------------------------------------------------------------------- /QuickSort/quickSortStart.js: -------------------------------------------------------------------------------- 1 | 2 | const quickSort = (array, lo = 0, hi = array.length - 1) => { 3 | 4 | } 5 | 6 | // function that goes through the array 7 | const partition = (array, lo, hi) => { 8 | 9 | } 10 | 11 | const swap = (array, idx1, idx2) => { 12 | 13 | } 14 | 15 | module.exports = quickSort; 16 | -------------------------------------------------------------------------------- /QuickUnion/README.md: -------------------------------------------------------------------------------- 1 | # Quick Union 2 | 3 | This is an improvement on the Quick Find Union Find algo. QFUF had a quadratic run time aka O(n^2). Quick Union still has a quadratic run time but it is still faster. 4 | 5 | It implements this through the use of a tree like structure. Don't freak out. It's not an actual tree inside each array element. But the idea is virtually the same. 6 | 7 | ## Sorry Need to add to this. Will do it later. 8 | 9 | 10 | -------------------------------------------------------------------------------- /QuickUnion/quick-union.js: -------------------------------------------------------------------------------- 1 | class QuickUnion { // Union Union Algo 2 | // starts out the same as the QuickFindUF algo 3 | // the construction of the Algo is the same 4 | constructor(length) { 5 | this.arrayOfIds = Array(length); 6 | 7 | for (let i = 0; i < this.arrayOfIds.length; i++) { 8 | this.arrayOfIds[i] = i; 9 | } 10 | } 11 | 12 | // Here is where the it starts to change 13 | // function that chases the pointer until it reaches the root 14 | root(i) { 15 | // while i is not equal to the id 16 | while (i !== this.arrayOfIds[i]) { 17 | 18 | // change i to the id and keep going to find the root 19 | i = this.arrayOfIds[i]; 20 | } 21 | 22 | // return i which is now the id of the root 23 | return i; 24 | } 25 | 26 | // checks if they are connected, returns a boolean 27 | 28 | // We cannot check if the top id is the same because now the array 29 | // is a tree structure 30 | // we have to point to the root 31 | 32 | // are the roots connected? 33 | areTheyConnected(firstIndex, secondIndex) { 34 | return this.root(firstIndex) === this.root(secondIndex); 35 | } 36 | 37 | // connects the element at the firstIndex with the element in the secondIndex 38 | // in this case we do not need to loop through the array to change all the indecies 39 | // now we can just change the index of one of them 40 | // we connect by connecting the id of the root 41 | connect(firstIndex, secondIndex) { // AKA UNION 42 | 43 | // first we find the root Id of both indicies 44 | let firstId = this.root(firstIndex); 45 | let secondId = this.root(secondIndex); 46 | 47 | // connect the root node of the first index to the root node of second index 48 | this.arrayOfIds[firstId] = secondId; 49 | } 50 | } 51 | 52 | module.exports = QuickUnion; 53 | -------------------------------------------------------------------------------- /QuickUnion/quick-union.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const QuickUnion = require('./quick-unionStart'); 3 | 4 | xdescribe('Quick Union Algorithm', () => { 5 | describe('QuickUnion', () => { 6 | 7 | let newQU; 8 | beforeEach(function() { 9 | newQU = new QuickUnion(10) 10 | }) 11 | 12 | describe('Constructor', () => { 13 | 14 | it('should have a property of arrayOfIds', () => { 15 | expect(newQU).to.have.ownProperty('arrayOfIds') 16 | }) 17 | 18 | it('arrayOfIds has a length equal to input', () => { 19 | expect(newQU.arrayOfIds.length).to.be.equal(10) 20 | }) 21 | 22 | it('arrayOfIds should be an array where the element is equal to its index', () => { 23 | expect(newQU.arrayOfIds).to.deep.equal([0,1,2,3,4,5,6,7,8,9]) 24 | }) 25 | }) 26 | 27 | describe('root', () => { 28 | 29 | it('should be a property on the class', () => { 30 | expect(newQU).to.have.property('root') 31 | }) 32 | 33 | it('should be a function', () => { 34 | expect(newQU.root).to.be.a('function') 35 | }) 36 | 37 | it('should loop through the arrayOfIds and return the root id on a simple tree', () => { 38 | const arr1 = [0, 1, 2, 3, 3, 5, 6, 7, 8, 9] 39 | newQU.arrayOfIds = arr1; 40 | 41 | expect(newQU.root(4)).to.equal(3); 42 | }) 43 | 44 | it('should loop through the arrayOfIds and return the root id on a semi-complex tree', () => { 45 | const arr1 = [1, 1, 1, 8, 3, 0, 5 ,1, 8, 8] 46 | newQU.arrayOfIds = arr1; 47 | 48 | expect(newQU.root(6)).to.equal(1); 49 | expect(newQU.root(7)).to.equal(1); 50 | expect(newQU.root(4)).to.equal(8); 51 | }) 52 | 53 | it('should loop through the arrayOfIds and return the root id on a very complex tree', () => { 54 | const arr1 = [1, 8, 1, 8, 3, 0, 5 ,1, 8, 8] 55 | newQU.arrayOfIds = arr1; 56 | 57 | expect(newQU.root(1)).to.equal(8); 58 | expect(newQU.root(4)).to.equal(8); 59 | expect(newQU.root(6)).to.equal(8); 60 | }) 61 | }) 62 | 63 | describe('areTheyConnected', () => { 64 | 65 | it('should be a property on the class', () => { 66 | expect(newQU).to.have.property('areTheyConnected') 67 | }) 68 | 69 | it('should be a function', () => { 70 | expect(newQU.areTheyConnected).to.be.a('function') 71 | }) 72 | 73 | it('should tell if two elements have the same id', () => { 74 | const arr1 = [0,1,2,3,3,5,6,7,8,9] 75 | newQU.arrayOfIds = arr1; 76 | 77 | expect(newQU.areTheyConnected(0,1)).to.be.false; 78 | expect(newQU.areTheyConnected(3, 4)).to.be.true; 79 | expect(newQU.areTheyConnected(4, 8)).to.be.false; 80 | }) 81 | }) 82 | 83 | describe('connect', () => { 84 | it('should be a property on the class', () => { 85 | expect(newQU).to.have.property('connect') 86 | }) 87 | 88 | it('should be a function', () => { 89 | expect(newQU.connect).to.be.a('function') 90 | }) 91 | 92 | it('should change the ids for a single connect', () => { 93 | newQU.connect(4,5) 94 | expect(newQU.arrayOfIds).to.deep.equal([0,1,2,3,5,5,6,7,8,9]) 95 | }) 96 | 97 | it('the elements should be connected by the second parameters id(parameter)', () => { 98 | newQU.connect(8,3) 99 | expect(newQU.arrayOfIds).to.deep.equal([0,1,2,3,4,5,6,7,3,9]) 100 | }) 101 | 102 | it('should change the ids for a two connections', () => { 103 | newQU.connect(4,5) 104 | newQU.connect(4,6) 105 | expect(newQU.arrayOfIds).to.deep.equal([0,1,2,3,5,6,6,7,8,9]) 106 | }) 107 | 108 | it('should change the ids for a MANY connections', () => { 109 | newQU.connect(4,5) 110 | newQU.connect(6,0) 111 | newQU.connect(3,1) 112 | newQU.connect(8,2) 113 | newQU.connect(7,9) 114 | expect(newQU.arrayOfIds).to.deep.equal([0,1,2,1,5,5,0,9,2,9]) 115 | }) 116 | 117 | it('should change the ids for a MANY MANY connections', () => { 118 | newQU.connect(4, 3) 119 | expect(newQU.arrayOfIds).to.deep.equal([0, 1, 2, 3, 3, 5, 6, 7, 8, 9]) 120 | newQU.connect(3, 8) 121 | expect(newQU.arrayOfIds).to.deep.equal([0, 1, 2, 8, 3, 5, 6, 7, 8, 9]) 122 | newQU.connect(6, 5) 123 | expect(newQU.arrayOfIds).to.deep.equal([0, 1, 2, 8, 3, 5, 5, 7, 8, 9]) 124 | newQU.connect(9, 4) 125 | expect(newQU.arrayOfIds).to.deep.equal([0, 1, 2, 8, 3, 5, 5, 7, 8, 8]) 126 | newQU.connect(2, 1) 127 | expect(newQU.arrayOfIds).to.deep.equal([0, 1, 1, 8, 3, 5, 5, 7, 8, 8]) 128 | }) 129 | 130 | it('should change the ids for a MANY MANY MANY connections. With this, everything is connected to one root node', () => { 131 | newQU.connect(4, 3) 132 | newQU.connect(3, 8) 133 | newQU.connect(6, 5) 134 | newQU.connect(9, 4) 135 | newQU.connect(2, 1) 136 | expect(newQU.arrayOfIds).to.deep.equal([0, 1, 1, 8, 3, 5, 5, 7, 8, 8]) 137 | newQU.connect(5, 0) 138 | expect(newQU.arrayOfIds).to.deep.equal([0, 1, 1, 8, 3, 0, 5, 7, 8, 8]) 139 | newQU.connect(7, 2) 140 | expect(newQU.arrayOfIds).to.deep.equal([0, 1, 1, 8, 3, 0, 5, 1, 8, 8]) 141 | newQU.connect(6, 1) 142 | expect(newQU.arrayOfIds).to.deep.equal([1, 1, 1, 8, 3, 0, 5, 1, 8, 8]) 143 | newQU.connect(7, 3) 144 | expect(newQU.arrayOfIds).to.deep.equal([1, 8, 1, 8, 3, 0, 5, 1, 8, 8]) 145 | }) 146 | 147 | }) 148 | 149 | }) 150 | }) 151 | -------------------------------------------------------------------------------- /QuickUnion/quick-unionStart.js: -------------------------------------------------------------------------------- 1 | 2 | class QuickUnion { 3 | 4 | } 5 | 6 | module.exports = QuickUnion; 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Collection of Algorithms and Data Structures 2 | 3 | ## Starting 4 | 1. run `npm install` 5 | 2. Select Algo/Data Structure you want to work on 6 | * remove `x` from `xdescribe` in the `.test.js` file 7 | * Add `.only` to the first `describe` in the `.test.js` file 8 | * like so: `describe.only('FANCY ALGO', () => {})` 9 | 3. run `npm test` 10 | 4. ??? 11 | 5. Profit 12 | 13 | ## Description 14 | Each folder contains the solution, an empty file, and a file for the test specs. Most files also include a README describing the data structure or algo as well as the functions you need to implement. 15 | 16 | Most of these implementations are what I have learned from the Coursera Princton Introduction to Algos Course, Part 1 and 2, and at Fullstack Academy. Although the Coursera course is in Java, I've implemented it in JavaScript. 17 | 18 | I have written extensive test specs so others can use this repo to do test first learning. And also to check if the functions I wrote were actually doing what they were supposed to :joy:. 19 | 20 | ### Note: 21 | 22 | A couple of the algorithms and data structures do not yet have test specs. Also the README's could be better. Will get on that...eventually... 23 | 24 | Said Stuff: 25 | 26 | 1. Test Specs 27 | * All sorting algos 28 | * KnuthShuffle 29 | * Heap Sort Readme 30 | * Graphs 31 | * bfs 32 | * dfs 33 | 2. Write better README's for sorting algos 34 | 35 | ### Coming Soon: 36 | 37 | 1. Suffix Tree 38 | 2. Topological Sort (using Tarjan's algorithm) 39 | 3. Dijkstra's Algorithm 40 | 4. Longest Common Subsequence (using dynamic programming) 41 | 5. Knapsack Problem (using dynamic programming) 42 | -------------------------------------------------------------------------------- /SelectionSort/README.md: -------------------------------------------------------------------------------- 1 | # Selection Sort 2 | 3 | ### What is it? 4 | Selection sort relies on the concept of dividing a list into sorted and unsorted sections. It iterates through the list and moves the smallest unsorted value to the end of the sorted section. 5 | 6 | Because we need to iterate through the entire list, and within that loop iterate through the unsorted section to find the minimum for each remaining unsorted element, this algorithm has a run time of O(n^2), or a "quadratic" run time. 7 | 8 | 9 | ### Hint (Approach): 10 | 11 | Given array `[2, 4, 1, 3]` 12 | 13 | Find the current unsorted minimum (`1`) and swap it with the first unsorted element (`2`) 14 | 15 | `[1, 4, 2, 3]` 16 | 17 | Find the current unsorted minimum (`2`) in unsorted section `[ 4, 2, 3 ]` 18 | Swap the unsorted minimum (`2`) with the first unsorted element (`4`) 19 | 20 | `[1, 2, 4, 3]` 21 | 22 | Find the current unsorted minimum (`3`) in unsorted section `[ 4, 3 ]` 23 | Swap the unsorted minimum (`3`) with the first unsorted element (`4`) 24 | 25 | Since the last element will necessarily be sorted we don't need to check it. -------------------------------------------------------------------------------- /SelectionSort/selectionSort.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const selectionSort = function(arr) { 4 | 5 | let length = arr.length 6 | 7 | for (let i = 0; i < length - 1; i++) { // Final element will have already been sorted 8 | 9 | let minimumIdx = i; // index to swap 10 | 11 | for (let j = i + 1; j < length; j++) { 12 | 13 | // if the jth (i.e. current unsorted) element < minimumIdx's value, j becomes the new minimumIdx 14 | if (arr[j] < arr[minimumIdx]) minimumIdx = j 15 | } 16 | 17 | // if the ith (i.e. 1st unsorted) index !== the min's index swap 18 | if (i !== minimumIdx) { 19 | this.swap(arr, i, minimumIdx); // When the test calls `swap` it' ll be in the module.export's context 20 | } 21 | 22 | } 23 | 24 | return arr; 25 | }; 26 | 27 | const swap = function(arr, idxA, idxB) { 28 | [arr[idxA], arr[idxB]] = [arr[idxB], arr[idxA]]; // Swaps the two indices' values using ES6 array destructuring assignment 29 | } 30 | 31 | module.exports = { 32 | selectionSort, 33 | swap 34 | }; 35 | -------------------------------------------------------------------------------- /SelectionSort/selectionSort.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const expect = require('chai').expect, 4 | sinon = require('sinon'); 5 | 6 | require('chai').use(require('sinon-chai')); 7 | 8 | const selectionSort = require('./selectionSortStart'); 9 | 10 | 11 | xdescribe('Selection sort', function() { 12 | 13 | it('sorts the array', function() { 14 | expect(selectionSort.selectionSort([10, 9, 1, 2, 5, 4])).to.deep.equal([1, 2, 4, 5, 9, 10]); 15 | }); 16 | 17 | describe('Swap', function() { 18 | 19 | let swapSpy; 20 | 21 | beforeEach(function() { 22 | swapSpy = sinon.spy(selectionSort, 'swap'); 23 | }); 24 | 25 | afterEach(function() { 26 | swapSpy.restore(); 27 | }) 28 | 29 | it('sorts in the right order', function() { 30 | 31 | const testArr = [2, 1, 10, 4, 5, 20] 32 | const sorted = selectionSort.selectionSort(testArr); 33 | 34 | expect(sorted).to.deep.equal([1, 2, 4, 5, 10, 20]); 35 | expect(swapSpy).to.have.been.calledWith(testArr, 0, 1); 36 | expect(swapSpy).to.have.been.calledWith(testArr, 2, 3); 37 | expect(swapSpy).to.have.been.calledWith(testArr, 3, 4); 38 | 39 | }); 40 | 41 | }); 42 | 43 | }); 44 | -------------------------------------------------------------------------------- /SelectionSort/selectionSortStart.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | selectionSort: function(arr) { 4 | 5 | }, 6 | 7 | 8 | swap: function(arr, idxA, idxB) { 9 | 10 | } 11 | 12 | }; 13 | -------------------------------------------------------------------------------- /ShellSort/README.md: -------------------------------------------------------------------------------- 1 | # Shell Sort 2 | 3 | ## What does it do? 4 | Shell Sort is more general sorting algo that is much faster than Insertion Sort. In fact you can think of Insertion Sort as a specific variation of Shell Sort. 5 | 6 | In insertion sort we sort by swapping the previous value if it is less. In Shell sort we sort by swapping a distance h instead of just 1. The distance, h, will be a distance that we increment throughout. 7 | 8 | 9 | For example: 10 | if h is 2 11 | 12 | <--2---> 13 | [4, 3, 2, 1, 5, 6] 14 | 2 4 15 | 1 3 16 | 17 | and so on. 18 | 19 | ## How will we implement it? 20 | Seeing how we already have Insertion Sort set up we just need to modify it to not simply sort by 1. We will be implementing a simple and easy to compute equation for h. 21 | 22 | h = 3h + 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /ShellSort/shellSort.js: -------------------------------------------------------------------------------- 1 | 2 | const shellSort = (array) => { 3 | let length = array.length 4 | let h = 1; 5 | 6 | // start at the highest h and move down 7 | // 1, 4, 13, 40, 121, 364, ... 8 | while(h < length / 3) h = 3 * h + 1 9 | 10 | // while h is >= 1 because h = 0 means swapping itself 11 | while(h >= 1) { 12 | 13 | // we start at h 14 | for (let i = h; i < length; i++) { 15 | 16 | // starting at i if j >= h and the item h disance away 17 | // is less than this number, we swap 18 | for (let j = i; j >= h; j -= h) { 19 | if(array[j] < array[j - h]) { 20 | swap(array, j, j - h) 21 | console.log(array) 22 | } 23 | } 24 | 25 | } 26 | 27 | // decrements by 3 so if we start at 364 we get 28 | // 121 after Math.floor 29 | h = Math.floor(h / 3) 30 | } 31 | 32 | return array 33 | } 34 | 35 | const swap = (array, idx1, idx2) => { 36 | let temp = array[idx1]; 37 | array[idx1] = array[idx2]; 38 | array[idx2] = temp; 39 | } 40 | 41 | module.exports = {shellSort, swap}; 42 | -------------------------------------------------------------------------------- /ShellSort/shellSort.test.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const expect = chai.expect; 3 | const spies = require('chai-spies'); 4 | chai.use(spies); 5 | 6 | const shellSort = require('./shellSort').shellSort; 7 | const swap = require('./shellSort').swap; 8 | 9 | xdescribe('Shell Sort', () => { 10 | let testArr; 11 | beforeEach(() => { 12 | testArr = [5,4,6,8,2,4,6]; 13 | }) 14 | 15 | it('should have a swap method swaps two elements in an array', () => { 16 | swap(testArr, 0, 1) 17 | expect(testArr).to.deep.equal([4,5,6,8,2,4,6]) 18 | }) 19 | 20 | it('shellSort should actually sort stuff', () => { 21 | expect(shellSort(testArr)).to.deep.equal(testArr.sort()) 22 | }) 23 | 24 | it('only need to swap a certain number of times if working properly', () => { 25 | const spy = chai.spy(shellSort); 26 | spy(testArr) 27 | expect(spy).to.have.been.called.with(testArr) 28 | // expect(spy).to.have.been.called.exactly(0) 29 | }) 30 | 31 | }) 32 | 33 | 34 | -------------------------------------------------------------------------------- /ShellSort/shellSortStart.js: -------------------------------------------------------------------------------- 1 | 2 | const shellSort = (array) => { 3 | 4 | } 5 | 6 | const swap = (array, idx1, idx2) => { 7 | 8 | } 9 | 10 | module.exports = shellSort; 11 | -------------------------------------------------------------------------------- /Stacks/README.md: -------------------------------------------------------------------------------- 1 | # Stacks with Linked List Implementation 2 | 3 | ### The Stack 4 | The stack data structure is one in which items are placed in a linear fashion. The most familiar implementation of the stack data structure that you may be familiar with is the call stack. The stack follows a defining rule which is "last in first out" or LIFO. What this means is when adding we add to the end of the structure(LAST IN) and when we are removing from the structure we also remove from the end(FIRST OUT). We can also think when we are removing we are removing the last thing added. In essence, stacks use push and pop methods of an array. 5 | 6 | Example: 7 | 8 | [1,2,3,4] 9 | 10 | push(5) 11 | 12 | [1,2,3,4] <-- 5 13 | 14 | [1,2,3,4,5] BOOM TADA! 15 | 16 | pop() 17 | 18 | [1,2,3,4] --> 5 19 | 20 | [1,2,3,4] BOOM TADA! 21 | 22 | ## What do we need to implement the Stack Data Structure? 23 | 1. We need to be able to insert (push) 24 | 2. We need to be able to remove (pop) 25 | 3. We need to be able to check the stack is empty 26 | 27 | ## How are we REALLY going to do this? 28 | We are going to use a string implementation. We will have two classes. One for the Stack and another, Node, for the data to pass into the stack. 29 | 30 | The implementation will have 3 31 | 32 | StackLL will be a class with these methods 33 | * `isEmpty` is a method to check if the stack is empty 34 | * `push` is adds to the stack 35 | * `pop` removes from the stack 36 | 37 | Node will be a class to represent our data. 38 | 39 | ### Note: 40 | I also included the array implementation in this folder. 41 | 42 | 43 | -------------------------------------------------------------------------------- /Stacks/stackArr.js: -------------------------------------------------------------------------------- 1 | class StackArr { // simple implementations 2 | constructor() { 3 | this.stack = Array(1); // array of strings of length one 4 | this.size = 0; // stuff filled in stack 5 | } 6 | 7 | // check if stack has anything 8 | isEmpty() { 9 | return this.size === 0; 10 | } 11 | 12 | // add to the end 13 | push(str) { 14 | // if the array is filled we resize the array to double its length 15 | if(this.size === this.stack.length) this.resize(2 * this.stack.length) 16 | 17 | this.stack[this.size++] = str; 18 | } 19 | 20 | // remove from end 21 | pop() { 22 | let item = this.stack[this.size--] 23 | 24 | // avoids clogging memory in array aka 'loitering' not necessary but nice 25 | this.stack[this.size] = null; 26 | 27 | // we shrink array when the array is a quarter full 28 | if(this.size > 0 && this.size === this.stack.length / 4) this.resize(this.stack.length / 2) 29 | return item; 30 | } 31 | 32 | resize(size) { // copy array over to a new array 33 | const copy = Array(size); 34 | 35 | for (var i = 0; i <= this.size; i++) { 36 | copy[i] = this.stack[i] 37 | } 38 | 39 | return copy; 40 | } 41 | 42 | } 43 | 44 | module.exports = StackArr 45 | -------------------------------------------------------------------------------- /Stacks/stackLL.js: -------------------------------------------------------------------------------- 1 | class StackLL { 2 | constructor() { // set head to empty at beginning 3 | this.head = null; 4 | } 5 | 6 | // check if stack has anything 7 | isEmpty() { 8 | return this.head === null; 9 | } 10 | 11 | // add to head 12 | push(str) { 13 | const newNode = new Node(str); 14 | newNode.next = this.head 15 | this.head = newNode; 16 | } 17 | 18 | // remove from head 19 | pop() { 20 | const str = this.head.str 21 | this.head = this.head.next 22 | return str; 23 | } 24 | } 25 | 26 | class Node { 27 | constructor(str) { 28 | this.str = str; 29 | this.next = null; 30 | } 31 | } 32 | 33 | module.exports = { 34 | StackLL, 35 | Node 36 | } 37 | -------------------------------------------------------------------------------- /Stacks/stackLL.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const StackLL = require('./stackLLStart').StackLL; 3 | const Node = require('./stackLLStart').Node; 4 | 5 | xdescribe('StackLL Algorithm', () => { 6 | describe('Node class', () => { 7 | describe('Constructor', () => { 8 | let node = new Node('42') 9 | 10 | it('should have a str property', () => { 11 | expect(node).to.have.ownProperty('str') 12 | }) 13 | 14 | it('should set str property to input', () => { 15 | expect(node.str).to.equal('42') 16 | }) 17 | 18 | it('should have a next property', () => { 19 | expect(node).to.have.ownProperty('next') 20 | }) 21 | 22 | it('should have the next property have default value of null', () => { 23 | expect(node.next).to.be.null 24 | }) 25 | }) 26 | }) 27 | 28 | describe('StackLL', () => { 29 | let stack; 30 | let node; 31 | beforeEach(function() { 32 | stack = new StackLL() 33 | node = new Node('What do you get when you multiply six by nine?') 34 | }) 35 | 36 | describe('Constructor', () => { 37 | it('should have a head property with default value of null', () => { 38 | expect(stack).to.have.ownProperty('head') 39 | expect(stack.head).to.be.null 40 | }) 41 | }) 42 | 43 | describe('isEmpty', () => { 44 | it('should be a property on the class', () => { 45 | expect(stack).to.have.property('isEmpty') 46 | }) 47 | 48 | it('should be a function', () => { 49 | expect(stack.isEmpty).to.be.a('function') 50 | }) 51 | 52 | it('should be able to check if the stack is empty', () => { 53 | expect(stack.isEmpty()).to.be.true 54 | 55 | stack.head = { str: '42', next: null } 56 | 57 | expect(stack.isEmpty()).to.be.false 58 | }) 59 | }) 60 | 61 | describe('push', () => { 62 | it('should be a property on the class', () => { 63 | expect(stack).to.have.property('push') 64 | }) 65 | 66 | it('should be a function', () => { 67 | expect(stack.push).to.be.a('function') 68 | }) 69 | 70 | it('should be able to add one item to the stack', () => { 71 | stack.push('peanut butter') 72 | let whatItShouldLookLike = {str: 'peanut butter', next: null} 73 | expect(stack.head).to.deep.equal(whatItShouldLookLike) 74 | }) 75 | 76 | it('should be able to add MANY items to the stack', () => { 77 | stack.push('0') 78 | stack.push('1') 79 | stack.push('2') 80 | stack.push('3') 81 | expect(stack.head.str).to.equal('3') 82 | expect(stack.head.next.str).to.equal('2') 83 | expect(stack.head.next.next.str).to.equal('1') 84 | expect(stack.head.next.next.next.str).to.equal('0') 85 | }) 86 | }) 87 | 88 | describe('pop', () => { 89 | it('should be a property on the class', () => { 90 | expect(stack).to.have.property('pop') 91 | }) 92 | 93 | it('should be a function', () => { 94 | expect(stack.pop).to.be.a('function') 95 | }) 96 | 97 | it('should be able to remove one item from the stack', () => { 98 | stack.push('0') 99 | stack.push('1') 100 | expect(stack.pop()).to.equal('1') 101 | expect(stack.head.next).to.be.null 102 | }) 103 | 104 | it('should be able to remove MANY items from the stack', () => { 105 | stack.push('0') 106 | stack.push('1') 107 | stack.push('2') 108 | stack.push('3') 109 | expect(stack.pop()).to.equal('3') 110 | expect(stack.head.next.next.next).to.be.null 111 | expect(stack.pop()).to.equal('2') 112 | expect(stack.head.next.next).to.be.null 113 | expect(stack.pop()).to.equal('1') 114 | expect(stack.head.next).to.be.null 115 | expect(stack.pop()).to.equal('0') 116 | expect(stack.head).to.be.null 117 | }) 118 | }) 119 | }) 120 | }) 121 | -------------------------------------------------------------------------------- /Stacks/stackLLStart.js: -------------------------------------------------------------------------------- 1 | class StackLL { 2 | 3 | } 4 | 5 | class Node { 6 | 7 | } 8 | 9 | module.exports = { 10 | StackLL, 11 | Node 12 | } 13 | -------------------------------------------------------------------------------- /Tries/tries.js: -------------------------------------------------------------------------------- 1 | class Node { 2 | constructor(val) { 3 | this.val = val; 4 | this.next = {}; 5 | } 6 | } 7 | 8 | class TrieST { 9 | constructor() { 10 | this.root = null; 11 | this.numKeys = 0; 12 | } 13 | 14 | put(key, val) { 15 | if (val === undefined) this.delete(key); 16 | else this.root = this._put(this.root, key, val, 0) 17 | } 18 | 19 | _put(node, key, val, int) { 20 | if (!node) node = new Node(); 21 | if (int === key.length) { 22 | if (node.val === undefined) this.numKeys++; 23 | node.val = val; 24 | return node; 25 | } 26 | 27 | const char = key.charAt(int); 28 | node.next[char] = this._put(node.next[char], key, val, int + 1); 29 | return node; 30 | } 31 | 32 | get(key) { 33 | let node = this._get(this.root, key, 0); 34 | if (!node) return null; 35 | return node.val; 36 | } 37 | 38 | _get(node, key, int) { 39 | if (!node) return null; 40 | if (int === key.length) return node; 41 | const char = key.charAt(int); 42 | return this._get(node.next[char], key, int + 1); 43 | } 44 | 45 | delete(key) { 46 | this.root = this._delete(this.root, key, 0); 47 | } 48 | 49 | _delete(node, key, int) { 50 | if (!node) return null; 51 | if (int === key.length) { 52 | if (node.val !== undefined) this.numKeys--; 53 | node.val = null; 54 | } else { 55 | const char = key.charAt(int); 56 | node.next[char] = this._delete(node.next[char], key, int + 1) 57 | } 58 | 59 | // removes subtrie if it is completely empty 60 | if (node.val !== null) return node; 61 | for (let letter in node.next) { 62 | if (node.next[letter]) return node; 63 | } 64 | 65 | return null; 66 | } 67 | } 68 | 69 | module.exports = { Node, TrieST }; 70 | -------------------------------------------------------------------------------- /UnorderedMaxPQ/README.md: -------------------------------------------------------------------------------- 1 | # Unordered Max Priority Queue 2 | 3 | ## What is it? 4 | It is just like the Queue data structure but with each element weighted. When we are adding to the queue things are added the same way. BUT when we are removing things from the queue we are removing the MAX/MIN weighted element regardless of where they are placed in the Queue. 5 | 6 | This means that the elements in the Queue must be comparable in some way. 7 | 8 | ## What do we need? 9 | 10 | `insert` add element to the array 11 | 12 | `del(Max|Min)` removes max/min key in Queue 13 | 14 | `isEmpty` checks if is empty 15 | 16 | `exchange` swap two elements in the array 17 | -------------------------------------------------------------------------------- /UnorderedMaxPQ/unorderedMaxPQ.js: -------------------------------------------------------------------------------- 1 | class UnorderedMaxPQ { 2 | // have a prop array and size equal to input length 3 | constructor(array) { 4 | this.array = array; 5 | this.size = array.length; 6 | } 7 | 8 | // checks if empty 9 | isEmpty() { 10 | return this.array.length === 0; 11 | } 12 | 13 | // remove max from array 14 | delMax() { 15 | let max = 0; // index 16 | 17 | // iterate through the array 18 | for (let i = 0; i < this.size; i++) { 19 | // if array[max] is less than array[i] the new max is at index i 20 | if (this.isLess(max, i)) max = i; 21 | } 22 | 23 | // move the max to the end of the array 24 | this.exchange(max, this.size - 1); 25 | 26 | // return the last element and decrement the size of array 27 | return this.array[--this.size]; 28 | } 29 | 30 | // input element into end of array and incement size 31 | insert(element) { 32 | this.array[this.size++] = element; 33 | } 34 | 35 | // for more modular approach but not necessary 36 | isLess(idx1, idx2) { 37 | return this.array[idx1] < this.array[idx2]; 38 | } 39 | 40 | // same as sorting method 41 | exchange(idx1, idx2) { 42 | const temp = this.array[idx1]; 43 | this.array[idx1] = this.array[idx2]; 44 | this.array[idx2] = temp; 45 | } 46 | } 47 | 48 | module.exports = UnorderedMaxPQ; 49 | -------------------------------------------------------------------------------- /UnorderedMaxPQ/unorderedMaxPQ.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect 2 | const UnorderedMaxPQ = require('./unorderedMaxPQStart') 3 | 4 | xdescribe('Unordered Max Priority Queue', () => { 5 | let UMPQ; 6 | 7 | beforeEach(() => { 8 | UMPQ = new UnorderedMaxPQ([3,2,5,1,4]) 9 | }) 10 | 11 | describe('Constructor', () => { 12 | it('should have array on its property and is equal to input array', () => { 13 | expect(UMPQ).to.have.ownProperty('array') 14 | expect(UMPQ.array).to.deep.equal([3,2,5,1,4]) 15 | }) 16 | 17 | it('should have size on its property and its equal to length of input array', () => { 18 | expect(UMPQ).to.have.ownProperty('size') 19 | expect(UMPQ.size).to.equal(5) 20 | }) 21 | }) 22 | 23 | describe('isEmpty', () => { 24 | it('should be a property on the class', () => { 25 | expect(UMPQ).to.have.property('isEmpty') 26 | }) 27 | 28 | it('should check if array is empty', () => { 29 | expect(UMPQ.isEmpty()).to.be.false 30 | 31 | UMPQ.array = []; 32 | UMPQ.size = 0; 33 | 34 | expect(UMPQ.isEmpty()).to.be.true 35 | }) 36 | }) 37 | 38 | describe('isLess', () => { 39 | it('should be a property on the class', () => { 40 | expect(UMPQ).to.have.property('isLess') 41 | }) 42 | 43 | it('should receive two indicies and check the array to see if first < second', () => { 44 | expect(UMPQ.isLess(0, 1)).to.be.false 45 | expect(UMPQ.isLess(1, 0)).to.be.true 46 | expect(UMPQ.isLess(2, 5)).to.be.false 47 | expect(UMPQ.isLess(3, 1)).to.be.true 48 | }) 49 | }) 50 | 51 | describe('exchange', () => { 52 | it('should be a property on the class', () => { 53 | expect(UMPQ).to.have.property('exchange') 54 | }) 55 | 56 | it('should receive two indicies and swap those elements in the array', () => { 57 | UMPQ.exchange(0,1) 58 | expect(UMPQ.array).to.deep.equal([2,3,5,1,4]) 59 | UMPQ.exchange(2,4) 60 | expect(UMPQ.array).to.deep.equal([2,3,4,1,5]) 61 | UMPQ.exchange(3,0) 62 | expect(UMPQ.array).to.deep.equal([1,3,4,2,5]) 63 | UMPQ.exchange(2,1) 64 | expect(UMPQ.array).to.deep.equal([1,4,3,2,5]) 65 | }) 66 | }) 67 | 68 | describe('insert', () => { 69 | it('should be a property on the class', () => { 70 | expect(UMPQ).to.have.property('insert') 71 | }) 72 | 73 | it('should receive an element and add it to the queue', () => { 74 | UMPQ.insert(6) 75 | expect(UMPQ.array).to.deep.equal([3,2,5,1,4,6]) 76 | UMPQ.insert(7) 77 | expect(UMPQ.array).to.deep.equal([3,2,5,1,4,6,7]) 78 | UMPQ.insert(8) 79 | expect(UMPQ.array).to.deep.equal([3,2,5,1,4,6,7,8]) 80 | }) 81 | }) 82 | 83 | describe('delMax', () => { 84 | it('should be a property on the class', () => { 85 | expect(UMPQ).to.have.property('delMax') 86 | }) 87 | 88 | it('should swap the max element to end of array', () => { 89 | UMPQ.delMax() 90 | const last = UMPQ.array[UMPQ.array.length - 1] 91 | const maxInArray = Math.max.apply(null, UMPQ.array) 92 | expect(last).to.be.equal(maxInArray) 93 | }) 94 | 95 | // After decrementing the size of the array we are saying the last element doesn't exist anymore 96 | it('should decrement the size', () => { 97 | const sizeBefore = UMPQ.size 98 | const correctSizeAfter = UMPQ.size - 1 99 | UMPQ.delMax() 100 | expect(UMPQ.size).to.be.equal(correctSizeAfter) 101 | }) 102 | 103 | it('should replace the "last element"(the element that was deleted already) on insert', () => { 104 | const last = UMPQ.array[UMPQ.size - 1] 105 | expect(last).to.be.equal(4) 106 | UMPQ.delMax() 107 | UMPQ.insert(6) 108 | expect(UMPQ.array).to.deep.equal([3,2,4,1,6]) 109 | }) 110 | }) 111 | 112 | }) 113 | -------------------------------------------------------------------------------- /UnorderedMaxPQ/unorderedMaxPQStart.js: -------------------------------------------------------------------------------- 1 | class UnorderedMaxPQ { 2 | 3 | } 4 | 5 | module.exports = UnorderedMaxPQ; 6 | -------------------------------------------------------------------------------- /WeightedQuickUnion/README.md: -------------------------------------------------------------------------------- 1 | # Weighted Quick Union 2 | 3 | The problem with QuickUnion, just like linked binary search trees, is that you can have everything attached onto one tree. Essentially instead of a tree you have something that looks like a linked list. Everthing is attached to the same tree vertically. This is why the worst case is quadratic run time. So having a weighted approach to adding to the tree prevents that. This speeds up our run time. The run time is somewhere between Quadratic and Linear. Almost there! But WE CAN DO BETTER. 4 | 5 | Weighted Quick Union is an improvement on Quick Union. 6 | 7 | The implementation involves weighing the important of a tree by it's depth. If two trees are to be connected we connect the smaller tree BELOW the larger tree. 8 | 9 | ### For example: 10 | 11 | 1. You have a single tree of depth one.(D1) 12 | 2. You have another tree of depth two.(D2) 13 | 14 | Scenario One: 15 | When connecting to the root node if you attach the D2 BELOW D1 then you get a tree of depth 3. The tree now looks like a linked list where each branch has one child branch. 16 | 17 | Scenario Two: 18 | When connecting to the root node if you attach the D1 BELOW D2 then you get a tree of depth 2. The tree now looks like a root node that has 2 branches. The tree has depth of 2. 19 | 20 | This way we can limit the depth of the tree as we add onto each tree. 21 | 22 | Now the tree has a larger breadth but shorter depth. This is easier to handle when searching. Yay! 23 | 24 | ## The modifications to QuickUnion : 25 | 26 | ### Data Structure Changes: 27 | The general data structure remains the same. The only thing we need to add to the structure is another array that holds the size of the tree 28 | 29 | ### Connect Changes: 30 | Find out which tree is larger. Link smaller tree to larger tree. Update the arrayOfSizes. 31 | 32 | # Note: 33 | No test specs for this because I'm lazy. Also because it's pretty simple to implement. There are only a few modifications to do. Try to implement yourself and then look at the solution if you get stuck or you can just look at the solution and learn from that. ¯\_(ツ)_/¯ <-- lost his arm from writing test specs 34 | 35 | See README for Weighted Quick Union Path Compression for full improvements on this algo. 36 | 37 | If you want to try and implement and see how this affects your performance run time you can use 38 | 39 | `const timerName = "OPTINAL STRING HERE TO CALL YOUR TIMER"` 40 | 41 | `console.time(timerName)` 42 | 43 | and then to end the timer 44 | 45 | `console.timeEnd(timerName)` 46 | -------------------------------------------------------------------------------- /WeightedQuickUnion/weightedQuickUnion.js: -------------------------------------------------------------------------------- 1 | class WeightedQuickUnion { 2 | constructor(length) { // adds arrayOfSizes all starting at depth 1 3 | this.arrayOfIds = Array(length); 4 | this.arrayOfSizes = Array(length); 5 | 6 | for (var i = 0; i < this.arrayOfIds.length; i++) { 7 | this.arrayOfIds[i] = i; 8 | this.arrayOfSizes[i] = 1; 9 | } 10 | } 11 | 12 | root(i) { 13 | while( i !== this.arrayOfIds[i] ) { 14 | i = this.arrayOfIds[i]; 15 | } 16 | 17 | return i; 18 | } 19 | 20 | areTheyConnected(firstIndex, secondIndex) { 21 | return this.root(firstIndex) === this.root(secondIndex) 22 | } 23 | 24 | // weighs trees on depth 25 | // connects tree of less depth to the tree of greater than or equal depth 26 | connect(firstIndex, secondIndex) { 27 | let firstId = this.root(firstIndex) 28 | let secondId = this.root(secondIndex) 29 | 30 | // minor optimization to not run rest of code if they have the same id 31 | if(firstId === secondId) return; 32 | 33 | // attach based on depth 34 | if(this.arrayOfSizes[firstIndex] < this.arrayOfSizes[secondIndex]) { 35 | this.arrayOfIds[firstId] = secondId; 36 | this.arrayOfSizes[firstId]++; //increment depth of tree 37 | } else { 38 | this.arrayOfIds[secondId] = firstId; 39 | this.arrayOfSizes[secondId]++; 40 | } 41 | } 42 | } 43 | 44 | module.exports = WeightedQuickUnion 45 | -------------------------------------------------------------------------------- /WeightedQuickUnion/weightedQuickUnionStart.js: -------------------------------------------------------------------------------- 1 | class WeightedQuickUnion { 2 | constructor(length) { 3 | this.arrayOfIds = Array(length); 4 | 5 | for (var i = 0; i < this.arrayOfIds.length; i++) { 6 | this.arrayOfIds[i] = i 7 | } 8 | } 9 | 10 | root(i) { 11 | while( i !== this.arrayOfIds[i] ) { 12 | i = this.arrayOfIds[i]; 13 | } 14 | 15 | return i; 16 | } 17 | 18 | areTheyConnected(firstIndex, secondIndex) { 19 | return this.root(firstIndex) === this.root(secondIndex) 20 | } 21 | 22 | connect(firstIndex, secondIndex) { 23 | let firstId = this.root(firstIndex) 24 | let secondId = this.root(secondIndex) 25 | 26 | this.arrayOfIds[firstId] = secondId 27 | } 28 | } 29 | 30 | module.exports = WeightedQuickUnion 31 | -------------------------------------------------------------------------------- /WeightedQuickUnionPathCompressed/README.md: -------------------------------------------------------------------------------- 1 | # Weight Quick Union with Path Compression 2 | 3 | ### Improvements 4 | This is yet another improvement on the Quick Union algo. 5 | 6 | Since we now know that what makes the run time go up(tree depth) we can now optimize even futher. 7 | 8 | We can do this by making the tree as flat as we can. By changing the depth of the tree and expanding the breadth. It's actually really simple to do this. It can be done by adding one line to our existing implementation. 9 | 10 | #### How do we make the tree flatter? 11 | One way we can make the tree flatter is by making as many things we can point to the a "older" root node. 12 | 13 | ### Where can we put our magical one line of code? 14 | Luckily we already have a function that finds the root node for us. So that would be a good place to add the the magic one liner. 15 | 16 | By adding this one liner to our root function every time we call it we change our tree structure to be more flat. 17 | 18 | ## Note: 19 | Also no test specs for this. Try it out. There's an explanation in the file telling you what the magic one liner does. 20 | 21 | With this improvement we now have something that is close enough to linear run time (O(n)) in practice. It's not exactly linear but for all practical purposes it is linear. 22 | 23 | If you want to try and implement and see how this affects your performance run time you can use 24 | 25 | `const timerName = "OPTINAL STRING HERE TO CALL YOUR TIMER"` 26 | 27 | `console.time(timerName)` 28 | 29 | and then to end the timer 30 | 31 | `console.timeEnd(timerName)` 32 | -------------------------------------------------------------------------------- /WeightedQuickUnionPathCompressed/weightedQuickUnionPathCompressed.js: -------------------------------------------------------------------------------- 1 | class WeightedQuickUnionPathCompressed { 2 | constructor(length) { 3 | this.arrayOfIds = Array(length); 4 | this.arrayOfSizes = Array(length); 5 | 6 | for (var i = 0; i < this.arrayOfIds.length; i++) { 7 | this.arrayOfIds[i] = i; 8 | this.arrayOfSizes[i] = 1; 9 | } 10 | } 11 | 12 | root(i) { 13 | while( i !== this.arrayOfIds[i] ) { 14 | 15 | // this is the magic one liner 16 | this.arrayOfIds[i] = this.arrayOfIds[this.arrayOfIds[i]] 17 | 18 | // So what do we actually do? 19 | 20 | // Essentially we are making every node in its path to the 21 | // root node point to the its grandparent node. 22 | // This halves the path length each time when we search through 23 | // the tree. So on the next pass of the loop we halve it again. 24 | // Nice right? 25 | 26 | i = this.arrayOfIds[i]; 27 | } 28 | 29 | return i; 30 | } 31 | 32 | areTheyConnected(firstIndex, secondIndex) { 33 | return this.root(firstIndex) === this.root(secondIndex) 34 | } 35 | 36 | connect(firstIndex, secondIndex) { 37 | let firstId = this.root(firstIndex) 38 | let secondId = this.root(secondIndex) 39 | 40 | if(firstId === secondId) return; 41 | 42 | if(this.arrayOfSizes[firstIndex] < this.arrayOfSizes[secondIndex]) { 43 | this.arrayOfIds[firstId] = secondId; 44 | this.arrayOfSizes[firstId]++; 45 | } else { 46 | this.arrayOfIds[secondId] = firstId; 47 | this.arrayOfSizes[secondId]++; 48 | } 49 | } 50 | } 51 | 52 | module.exports = WeightedQuickUnionPathCompressed 53 | -------------------------------------------------------------------------------- /WeightedQuickUnionPathCompressed/weightedQuickUnionPathCompressedStart.js: -------------------------------------------------------------------------------- 1 | class WeightedQuickUnionPathCompressed { 2 | constructor(length) { 3 | this.arrayOfIds = Array(length); 4 | this.arrayOfSizes = Array(length); 5 | 6 | for (var i = 0; i < this.arrayOfIds.length; i++) { 7 | this.arrayOfIds[i] = i; 8 | this.arrayOfSizes[i] = 1; 9 | } 10 | } 11 | 12 | root(i) { 13 | while( i !== this.arrayOfIds[i] ) { 14 | i = this.arrayOfIds[i]; 15 | } 16 | 17 | return i; 18 | } 19 | 20 | areTheyConnected(firstIndex, secondIndex) { 21 | return this.root(firstIndex) === this.root(secondIndex) 22 | } 23 | 24 | connect(firstIndex, secondIndex) { 25 | let firstId = this.root(firstIndex) 26 | let secondId = this.root(secondIndex) 27 | 28 | if(firstId === secondId) return; 29 | 30 | if(this.arrayOfSizes[firstIndex] < this.arrayOfSizes[secondIndex]) { 31 | this.arrayOfIds[firstId] = secondId; 32 | this.arrayOfSizes[firstId]++; 33 | } else { 34 | this.arrayOfIds[secondId] = firstId; 35 | this.arrayOfSizes[secondId]++; 36 | } 37 | } 38 | } 39 | 40 | module.exports = WeightedQuickUnionPathCompressed 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "QuickFindUF", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "quick-find.js", 6 | "dependencies": { 7 | "chai": "^3.5.0", 8 | "chai-properties": "^1.2.1", 9 | "chai-sinon": "^2.8.1", 10 | "chai-spies": "^0.7.1", 11 | "sinon": "^1.17.7", 12 | "sinon-chai": "^2.8.0" 13 | }, 14 | "devDependencies": { 15 | "mocha": "^3.2.0" 16 | }, 17 | "scripts": { 18 | "test": "mocha **/*.test.js -w" 19 | }, 20 | "keywords": [], 21 | "author": "Damon Ye", 22 | "license": "ISC" 23 | } 24 | --------------------------------------------------------------------------------