From a56762cb9def69df3c5c10bc9a610cf622569cfb Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Mon, 12 Oct 2015 12:22:05 -0700 Subject: [PATCH 01/33] Add info about properties --- sorting-algorithms/src/bubble.js | 13 +++++++++++++ sorting-algorithms/src/insertion.js | 15 +++++++++++++++ sorting-algorithms/src/merge.js | 16 ++++++++++++++-- sorting-algorithms/src/quick.js | 15 ++++++++++++++- sorting-algorithms/src/selection.js | 16 ++++++++++++++++ 5 files changed, 72 insertions(+), 3 deletions(-) diff --git a/sorting-algorithms/src/bubble.js b/sorting-algorithms/src/bubble.js index b890b5e..9f09e6d 100644 --- a/sorting-algorithms/src/bubble.js +++ b/sorting-algorithms/src/bubble.js @@ -9,6 +9,7 @@ Iterate over array, comparing adjacent items and swap if in incorrect order. Lar - Implement bubble sort - Identify time complexity +- Identify space complexity Optimizations: - Make algorithm adaptive (if at any point array is already sorted, exit function early). After doing this, what is time complexity for nearly sorted arrays? @@ -20,6 +21,18 @@ Variants: */ +/* +Properties: +O(1) extra space +Time complexity: +- worst: O(n2) comparisons and swaps +- best: O(n) when nearly sorted +not stable +adaptive - O(n) time when nearly sorted + +Use cases: +Similar to insertion sort (many properties are the same for insertion and bubble sort) - when the data is nearly sorted (since it's adaptive) or when the problem size is small (because it has low memory overhead) +*/ var bubbleSort = function(array) { // while wall > 0 diff --git a/sorting-algorithms/src/insertion.js b/sorting-algorithms/src/insertion.js index 4af8af8..afdec0b 100644 --- a/sorting-algorithms/src/insertion.js +++ b/sorting-algorithms/src/insertion.js @@ -20,6 +20,7 @@ now repeat for next unsorted element - Implement insertion sort for array of numbers - Identify time complexity +- Identify space complexity - Modify function to take comparator function. specify default if not provided (check out native Array.sort comparator function for reference) - Use your comparator function to verify that your sort is stable by taking input: [{value: 15}, {value: 10, order: 1}, {value: 10, order: 2}] @@ -30,6 +31,20 @@ now repeat for next unsorted element */ + +/* +Properties: +O(1) extra space +Time complexity: +- worst: O(n^2) comparisons and swaps +- best: O(n) when nearly sorted +stable +adaptive - O(n) time when nearly sorted + +Use cases: +When the data is nearly sorted (since it's adaptive) or when the problem size is small (because it has low memory overhead) +*/ + var insertionSort = function(array, comparator) { comparator = comparator || defaultComparator; diff --git a/sorting-algorithms/src/merge.js b/sorting-algorithms/src/merge.js index e79493d..fc659aa 100644 --- a/sorting-algorithms/src/merge.js +++ b/sorting-algorithms/src/merge.js @@ -16,6 +16,7 @@ Split array into sublists of size 1, merge adjacent sublists into sorted lists, - Implement recursive merge sort (you might want to write a helper function to handle the merge step) - Implement iterative merge sort - Identify time complexity +- Identify space complexity - Modify function to take comparator function. specify default if not provided (check out native Array.sort comparator function for reference) - Use your comparator function to verify that your sort is stable by taking input: [{value: 15}, {value: 10, order: 1}, {value: 10, order: 2}] @@ -29,7 +30,18 @@ subarrays for natural merge sort: [ [1,2], [4,5], [9] ] */ -var mergeSortRecursive = function(array) { + +/* +Properties: +O(n) extra space for iterative solution +O(n·log(n)) time (for worst and best) +stable - the only stable O(n·log(n)) sorting algorithm +not adaptive + +Use cases: +If stabilty is a requirement and using extra space is no concern, merge sort is great because it's simple to implement, it's the only stable O(nlog(n)) sorting algorithm. +*/ +function mergeSortRecursive (array) { // base case if (array.length <= 1) return array; @@ -43,7 +55,7 @@ var mergeSortRecursive = function(array) { return merge(leftSorted, rightSorted); }; -var mergeSortIterative = function(array) { +function mergeSortIterative (array) { // create array of subarrays with each element var splitArr = array.map(function(element) { return [element]; }); diff --git a/sorting-algorithms/src/quick.js b/sorting-algorithms/src/quick.js index 76b22b5..d685ba5 100644 --- a/sorting-algorithms/src/quick.js +++ b/sorting-algorithms/src/quick.js @@ -12,6 +12,7 @@ It has a partitioning step, in which you pick an element (called a pivot) and pa - Write a partition helper function. For choice of pivot, for a basic implementation, we recommend choosing either the first or last element in the subarray. If you need hints, look up the Lumoto partiton scheme. Test this out before moving forward! - Implement quicksort - Identify time complexity +- Identify space complexity - Consider implications for choice of pivot (https://en.wikipedia.org/wiki/Quicksort#Choice_of_pivot) @@ -22,13 +23,25 @@ Variants: */ + + +/* +Properties: +O(n) extra space +O(n^2) time (for few unique keys), but typically O(n·log(n)) if recursion is balanced +not stable +not adaptive + +Use cases: +Quicksort is in place and has low overhead. If a stable sort is not necessary. It has a higher worstcase time complexity than merge sort (if pivot is not in center of array) +*/ function quicksort(array, lo, hi) { if (lo === undefined) lo = 0; if (hi === undefined) hi = array.length-1; if (lo < hi) { // partition array - p = partition(array, lo, hi); + var p = partition(array, lo, hi); console.log('partitioning from', lo, 'to', hi, '=> partition:', p); // sort subarrays quicksort(array, lo, p-1); diff --git a/sorting-algorithms/src/selection.js b/sorting-algorithms/src/selection.js index 686b970..2db7b5c 100644 --- a/sorting-algorithms/src/selection.js +++ b/sorting-algorithms/src/selection.js @@ -19,6 +19,7 @@ sorted portion has now grown: - Implement selection sort - Identify time complexity +- Identify space complexity Stable Variant - Implement as a stable sort - rather than swapping, the minimum value is inserted into the first position and all other items are shifted one to the right. How does this impact performance? @@ -29,6 +30,21 @@ Stable Variant */ + +/* +Properties: +O(1) extra space +O(n^2) comparisons (for worst and best) +O(n) swaps +not stable +not adaptive + +Use cases: +Since selection sort minimizes the number of swaps, it's useful when the cost of swapping items is high. + +Comparison to other algorithms: + +*/ var selectionSort = function (array, comparator) { comparator = comparator || defaultComparator; array.forEach(function(element, index) { From d18b895ae9446f2bafa5a2d803880d9959e5835c Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Mon, 12 Oct 2015 12:26:50 -0700 Subject: [PATCH 02/33] Move files --- sorting-algorithms/{src => }/bubble.js | 0 sorting-algorithms/{src => }/insertion.js | 0 sorting-algorithms/{src => }/merge.js | 0 sorting-algorithms/{src => }/quick.js | 0 sorting-algorithms/{src => }/selection.js | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename sorting-algorithms/{src => }/bubble.js (100%) rename sorting-algorithms/{src => }/insertion.js (100%) rename sorting-algorithms/{src => }/merge.js (100%) rename sorting-algorithms/{src => }/quick.js (100%) rename sorting-algorithms/{src => }/selection.js (100%) diff --git a/sorting-algorithms/src/bubble.js b/sorting-algorithms/bubble.js similarity index 100% rename from sorting-algorithms/src/bubble.js rename to sorting-algorithms/bubble.js diff --git a/sorting-algorithms/src/insertion.js b/sorting-algorithms/insertion.js similarity index 100% rename from sorting-algorithms/src/insertion.js rename to sorting-algorithms/insertion.js diff --git a/sorting-algorithms/src/merge.js b/sorting-algorithms/merge.js similarity index 100% rename from sorting-algorithms/src/merge.js rename to sorting-algorithms/merge.js diff --git a/sorting-algorithms/src/quick.js b/sorting-algorithms/quick.js similarity index 100% rename from sorting-algorithms/src/quick.js rename to sorting-algorithms/quick.js diff --git a/sorting-algorithms/src/selection.js b/sorting-algorithms/selection.js similarity index 100% rename from sorting-algorithms/src/selection.js rename to sorting-algorithms/selection.js From b062427bfd1840adbc28930b0f1b3c2a84a32d03 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Mon, 12 Oct 2015 12:40:36 -0700 Subject: [PATCH 03/33] Rename folder --- .../doublyLinkedList.js | 0 {data-structure-implementations => data-structures}/hashTable.js | 0 {data-structure-implementations => data-structures}/linkedList.js | 0 {data-structure-implementations => data-structures}/queue.js | 0 {data-structure-implementations => data-structures}/set.js | 0 {data-structure-implementations => data-structures}/stack.js | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename {data-structure-implementations => data-structures}/doublyLinkedList.js (100%) rename {data-structure-implementations => data-structures}/hashTable.js (100%) rename {data-structure-implementations => data-structures}/linkedList.js (100%) rename {data-structure-implementations => data-structures}/queue.js (100%) rename {data-structure-implementations => data-structures}/set.js (100%) rename {data-structure-implementations => data-structures}/stack.js (100%) diff --git a/data-structure-implementations/doublyLinkedList.js b/data-structures/doublyLinkedList.js similarity index 100% rename from data-structure-implementations/doublyLinkedList.js rename to data-structures/doublyLinkedList.js diff --git a/data-structure-implementations/hashTable.js b/data-structures/hashTable.js similarity index 100% rename from data-structure-implementations/hashTable.js rename to data-structures/hashTable.js diff --git a/data-structure-implementations/linkedList.js b/data-structures/linkedList.js similarity index 100% rename from data-structure-implementations/linkedList.js rename to data-structures/linkedList.js diff --git a/data-structure-implementations/queue.js b/data-structures/queue.js similarity index 100% rename from data-structure-implementations/queue.js rename to data-structures/queue.js diff --git a/data-structure-implementations/set.js b/data-structures/set.js similarity index 100% rename from data-structure-implementations/set.js rename to data-structures/set.js diff --git a/data-structure-implementations/stack.js b/data-structures/stack.js similarity index 100% rename from data-structure-implementations/stack.js rename to data-structures/stack.js From ba9ec63197233d5299a81c47d1eaaf70c9bddcf9 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Mon, 12 Oct 2015 17:54:55 -0700 Subject: [PATCH 04/33] Remove empty line --- data-structures/stack.js | 1 - 1 file changed, 1 deletion(-) diff --git a/data-structures/stack.js b/data-structures/stack.js index a695a99..74bb7a5 100644 --- a/data-structures/stack.js +++ b/data-structures/stack.js @@ -93,4 +93,3 @@ console.log(myStack.pop(), 'should be c'); console.log(myStack.count(), 'should be 2'); console.log(myStack.peek(), 'should be b'); console.log(myStack.count(), 'should be 2'); - From b5b97d0873db66652daddde5f2aa99eece82d240 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Mon, 12 Oct 2015 17:55:03 -0700 Subject: [PATCH 05/33] Implement solution --- data-structures/tree.js | 102 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 data-structures/tree.js diff --git a/data-structures/tree.js b/data-structures/tree.js new file mode 100644 index 0000000..89f1c27 --- /dev/null +++ b/data-structures/tree.js @@ -0,0 +1,102 @@ +/* + +TREES + +Abstract data type + +General Tree: +A tree has a root node. +The root node has 0 or more children. +Each child node has 0 or more children. +(each node in the tree can be seen as a subtree) + +Constraints: +A child has only one parent and the root node has no parent. +Note: A tree is a special type of graph. A tree is a graph without cycles. + + +*** Operations: + +tree.addChild(value) +=> child node (new tree) +add child to tree/subtree and return child node (which should be a tree instance) + +tree.contains(value) +=> true/false +Return true if value is in tree, false if not + +tree.traverseDepthFirst(callback) +=> undefined +Invoke the callback for every node in a depth-first order + +tree.traverseBreadthFirst(callback) +=> undefined +Invoke the callback for every node in a breadth-first order + +*** Nightmare mode: +Given treeA and treeB, check if treeB is a subtree of treeA (meaning that there exists a node n in treeA such that the subtree of n is identical to treeB). + +*/ + +// N-ary Tree (any number of children) +function Tree (value) { + this.value = value; + this.children = []; +} + +// Adds child to tree or subtree bound to this keyword +// O(1) +Tree.prototype.addChild = function(value) { + var child = new Tree(value); + this.children.push(child); + return child; +}; + +var tree = new Tree(1); +var branch1 = tree.addChild(2); +var branch2 = tree.addChild(3); +var branch3 = tree.addChild(4); +branch1.addChild(5); +branch1.addChild(6); +branch3.addChild(7).addChild(8); + +// O(n) +Tree.prototype.contains = function(value) { + if (this.value === value) return true; + for (var i=0; i Date: Mon, 12 Oct 2015 17:55:10 -0700 Subject: [PATCH 06/33] Implement solution --- data-structures/binarySearchTree.js | 189 ++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 data-structures/binarySearchTree.js diff --git a/data-structures/binarySearchTree.js b/data-structures/binarySearchTree.js new file mode 100644 index 0000000..93b3d42 --- /dev/null +++ b/data-structures/binarySearchTree.js @@ -0,0 +1,189 @@ +/* + +BINARY SEARCH TREES + +Abstract data type + +A binary search tree is a tree with the additional constraints: +- each node has only two child nodes (node.left and node.right) +- all the values in the left subtree of a node are less than or equal to the value of the node +- all the values in the right subtree of a node are greater than the value of the node + + +*** Operations: +bsTree.insert(value) +=> bsTree (return for chaining purposes) +Insert value into correct position within tree + +bsTree.contains(value) +=> true/false +Return true if value is in tree, false if not + +bsTree.traverseDepthFirst_inOrder(callback) +=> undefined +Invoke the callback for every node in a depth-first in-order (visit left branch, then current node, than right branch) +Note: In-Order traversal is most common type for binary trees. For binary search tree, this visits the nodes in ascending order (hence the name). + +bsTree.traverseDepthFirst_preOrder(callback) +=> undefined +Invoke the callback for every node in a depth-first pre-order (visits current node before its child nodes) + +bsTree.traverseDepthFirst_postOrder(callback) +=> undefined +Invoke the callback for every node in a depth-first post-order (visit the current node after its child nodes) + +bsTree.traverseBreadthFirst(callback) +=> undefined +Invoke the callback for every node in a breadth-first order + +bsTree.checkIfFull() +=> true/false +A binary tree is full if every node has either zero or two children (no nodes have only one child) + +bsTree.checkIfBalanced() +=> true/false +For this exercise, let's say that a tree is balanced if the minimum height and the maximum height differ by no more than 1. The height for a branch is the number of levels below the root. + +*** Nightmare mode: +A binary search tree was created by iterating over an array and inserting each element into the tree. Given a binary search tree with no duplicates, how many different arrays would result in the creation of this tree. + +*/ + + +// Binary Search Tree +function BinarySearchTree (value) { + this.value = value; + this.left = null; + this.right = null; +} + +// O(log(n)) +BinarySearchTree.prototype.insert = function(value) { + if (value <= this.value) { + if (this.left) this.left.insert(value); + else this.left = new BinarySearchTree(value); + } + else { + if (this.right) this.right.insert(value); + else this.right = new BinarySearchTree(value); + } + return this; +}; + +// O(log(n)); +BinarySearchTree.prototype.contains = function(value) { + if (this.value === value) return true; + if (value < this.value) { + // if this.left doesn't exist return false + // if it does exist, check if its subtree contains the value + return !!this.left && this.left.contains(value); + } + if (value > this.value) { + // if this.right doesn't exist return false + // if it does exist, check if its subtree contains the value + return !!this.right && this.right.contains(value); + } + return false; +}; + +var bsTree = new BinarySearchTree(10); +bsTree.insert(5).insert(15).insert(8).insert(3).insert(7).insert(20).insert(17).insert(9).insert(14); + +// In-Order traversal is most common +// visit left branch, then current node, than right branch +// For binary search tree, this visits the nodes in ascending order (hence the name) +// O(n) +BinarySearchTree.prototype.traverseDepthFirst_inOrder = function(fn) { + if (!this.left && !this.right) return fn(this); + if (this.left) this.left.traverseDepthFirst_inOrder(fn); + fn(this); + if (this.right) this.right.traverseDepthFirst_inOrder(fn); +}; + +var result_traverseDepthFirst_inOrder = []; +bsTree.traverseDepthFirst_inOrder(function(node) { + result_traverseDepthFirst_inOrder.push(node.value); +}); +console.log(result_traverseDepthFirst_inOrder, 'should be [3,5,7,8,9,10,14,15,17,20]'); + +// Pre-Order traversal +// visits current node before its child nodes +// O(n) +BinarySearchTree.prototype.traverseDepthFirst_preOrder = function(fn) { + fn(this); + if (this.left) this.left.traverseDepthFirst_preOrder(fn); + if (this.right) this.right.traverseDepthFirst_preOrder(fn); +}; + +var result_traverseDepthFirst_preOrder = []; +bsTree.traverseDepthFirst_preOrder(function(node) { + result_traverseDepthFirst_preOrder.push(node.value); +}); +console.log(result_traverseDepthFirst_preOrder, 'should be [10,5,3,8,7,9,15,14,20,17]'); + +// Post-Order traversal +// visit the current node after its child nodes +// O(n) +BinarySearchTree.prototype.traverseDepthFirst_postOrder = function(fn) { + if (this.left) this.left.traverseDepthFirst_postOrder(fn); + if (this.right) this.right.traverseDepthFirst_postOrder(fn); + fn(this); +}; + +var result_traverseDepthFirst_postOrder = []; +bsTree.traverseDepthFirst_postOrder(function(node) { + result_traverseDepthFirst_postOrder.push(node.value); +}); +console.log(result_traverseDepthFirst_postOrder, 'should be [3,7,9,8,5,14,17,20,15,10]'); + +// O(n) +BinarySearchTree.prototype.traverseBreadthFirst = function(fn) { + var queue = [this]; + while (queue.length) { + var node = queue.shift(); + fn(node); + node.left && queue.push(node.left); + node.right && queue.push(node.right); + } +}; + +var result_traverseBreadthFirst = []; +bsTree.traverseBreadthFirst(function(node) { + result_traverseBreadthFirst.push(node.value); +}); +console.log(result_traverseBreadthFirst, 'should be [10,5,15,3,8,14,20,7,9,17]'); + +// O(n) +// A binary tree is full if every node has either zero or two children (no nodes have only one child) +BinarySearchTree.prototype.checkIfFull = function() { + var result = true; + this.traverseBreadthFirst(function(node) { + if (!node.left && node.right) result = false; + else if (node.left && !node.right) result = false; + }); + return result; +}; + +console.log(bsTree.checkIfFull(), 'should be false'); + +var fullBSTree = new BinarySearchTree(10); +fullBSTree.insert(5).insert(20).insert(15).insert(21).insert(16).insert(13); +console.log(fullBSTree.checkIfFull(), 'should be true'); + +// For this exercise, let's say that a tree is balanced if the minimum height and the maximum height differ by no more than 1. The height for a branch is the number of levels below the root. +// O(n) +BinarySearchTree.prototype.checkIfBalanced = function() { + var heights = []; + var recurse = function(node, height) { + if (!node.left && !node.right) return heights.push(height); + node.left && recurse(node.left, height+1); + node.right && recurse(node.right, height+1); + }; + recurse(this, 1); + var min = Math.min.apply(null, heights); + var max = Math.max.apply(null, heights); + return max-min <= 1; +}; + +console.log(bsTree.checkIfBalanced(), 'should be true'); +console.log(fullBSTree.checkIfBalanced(), 'should be false'); From 305848ccbe53b277edec1c7209611c46c969b54e Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Mon, 12 Oct 2015 18:10:32 -0700 Subject: [PATCH 07/33] Add exercise for prefix tree --- data-structures/tree.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data-structures/tree.js b/data-structures/tree.js index 89f1c27..8e02d4a 100644 --- a/data-structures/tree.js +++ b/data-structures/tree.js @@ -36,6 +36,8 @@ Invoke the callback for every node in a breadth-first order *** Nightmare mode: Given treeA and treeB, check if treeB is a subtree of treeA (meaning that there exists a node n in treeA such that the subtree of n is identical to treeB). +Given a dictionary, create a prefix tree (commonly known as a trie) +https://en.wikipedia.org/wiki/Trie */ // N-ary Tree (any number of children) From bbc51d35639bc402c694af679b33aff5e434b2c4 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Sat, 31 Oct 2015 22:58:36 -0700 Subject: [PATCH 08/33] Add solution for binary search in an array --- searching-algorithms/binarySearchArray.js | 47 +++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 searching-algorithms/binarySearchArray.js diff --git a/searching-algorithms/binarySearchArray.js b/searching-algorithms/binarySearchArray.js new file mode 100644 index 0000000..9d88400 --- /dev/null +++ b/searching-algorithms/binarySearchArray.js @@ -0,0 +1,47 @@ +/* +BINARY SEARCH ARRAY + +*** Description + +Given a sorted array and a value, determine if the value is in the array using the binary search (divide and conquer) method. + +*** Exercises + +Write a function that takes a sorted array and a value and returns the index of the value in the array. Return null if the value is not found in the array. What is the time complexity? + +Extra credit: Implement the function both iteratively and recursively. + +*/ + +// // Iterative solution +// function binarySearch(array, target) { +// var lo = 0; +// var hi = array.length-1; +// while (lo <= hi) { +// var mid = Math.floor((hi-lo)/2) + lo; +// if (target === array[mid]) return mid; +// else if (target < array[mid]) hi = mid-1; +// else lo = mid+1; +// } +// return null; +// } + +// Recursive solution +function binarySearch(array, target) { + return (function recurse(lo, hi) { + if (lo > hi) return null; + var mid = Math.floor((hi-lo)/2) + lo; + if (target === array[mid]) return mid; + else if (target < array[mid]) return recurse(lo, mid-1); + else return recurse(mid+1, hi); + })(0, array.length-1); +} + +var arr = [0,1,2,3,4,5]; +console.log(binarySearch(arr, 0), 'should be', 0); +console.log(binarySearch(arr, 1), 'should be', 1); +console.log(binarySearch(arr, 2), 'should be', 2); +console.log(binarySearch(arr, 3), 'should be', 3); +console.log(binarySearch(arr, 4), 'should be', 4); +console.log(binarySearch(arr, 5), 'should be', 5); +console.log(binarySearch(arr, 8), 'should be', null); From a6eb9319233e21087e9b7ad3163db74008569633 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Sat, 31 Oct 2015 22:59:27 -0700 Subject: [PATCH 09/33] Add time complexity --- searching-algorithms/binarySearchArray.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/searching-algorithms/binarySearchArray.js b/searching-algorithms/binarySearchArray.js index 9d88400..b593b04 100644 --- a/searching-algorithms/binarySearchArray.js +++ b/searching-algorithms/binarySearchArray.js @@ -13,6 +13,8 @@ Extra credit: Implement the function both iteratively and recursively. */ +// Time complexity: O(log(n)) + // // Iterative solution // function binarySearch(array, target) { // var lo = 0; From acec33d017d9fcedb8ffa36b97ed9715ca97c935 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Sun, 8 Nov 2015 17:48:02 -0800 Subject: [PATCH 10/33] Return node rather than just value, fix bug --- data-structures/linkedList.js | 50 ++++++++++++++++------------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/data-structures/linkedList.js b/data-structures/linkedList.js index f838b1f..1cc2460 100644 --- a/data-structures/linkedList.js +++ b/data-structures/linkedList.js @@ -16,35 +16,35 @@ invoke callback function with the value of each node myList.print() => string with all values in list (ex: '0, 1, 2, 3') -myList.insertAfter(refNode, value) -=> value of new node +myList.insertAfter(refNode, value) +=> new node insert new node associated with value passed in after refNode -myList.removeAfter(refNode) -=> value of removed node +myList.removeAfter(refNode) +=> removed node remove node after the refNode myList.insertHead(value) -=> value associated with new head +=> new head insert new head node at the beginning of the list with the value passed in myList.removeHead() -=> value of removed head node +=> removed head node remove the head node of the linked list myList.findNode(value) => first node that has a value matching what was passed in -* Optimization: +* Optimization: Say we have a linked list that has 100 items and we want to add an item to the very end. How would you do that with your current implementation? How can you modify the data structure to add an item to the end in constant time? myList.appendToTail(value) -=> value of tail node +=> tail node add a new tail node at the end of the list with the associated value passed in myList.removeTail() -=> value of removed tail node +=> removed tail node remove the tail node from the list @@ -52,18 +52,18 @@ remove the tail node from the list Now let's think about creating insertBefore and removeBefore methods for the nodes in our list. Can you think of an efficient way to do so? -Think about time complexity. What would it be for your current implementation of a linked list? +Think about time complexity. What would it be for your current implementation of a linked list? How can we modify our data structures (Node and Linked List classes) so that we can make these O(1) operations? Once you've come up with a plan, implement the following methods. -myList.insertBefore(refNode, value) -=> value of new node inserted +myList.insertBefore(refNode, value) +=> new node inserted insert new node with associated value before refNode -myList.removeBefore(refNode) -=> value of removed node +myList.removeBefore(refNode) +=> removed node remove node before the refNode passed in @@ -110,7 +110,7 @@ LinkedList.prototype.print = function() { LinkedList.prototype.insertAfter = function(node, value) { // get reference to former next var oldNext = node.next; - // create new node + // create new node var newNext = new Node(value); // store it as the new next node.next = newNext; @@ -118,14 +118,14 @@ LinkedList.prototype.insertAfter = function(node, value) { newNext.next = oldNext; // if reference node is tail, set tail to newNext if (this.tail === node) this.tail = newNext; - return newNext.value; + return newNext; }; LinkedList.prototype.removeAfter = function(node) { - // if node is tail, then there's nothing to remove - if (!removedNode) return 'Nothing to remove'; // store reference to removed node var removedNode = node.next; + // if node is tail, then there's nothing to remove + if (!removedNode) return 'Nothing to remove'; // get reference to node after removed node var newNext = removedNode.next; // set newNext as the next node @@ -134,7 +134,7 @@ LinkedList.prototype.removeAfter = function(node) { removedNode.next = null; // if removedNode is tail, set tail to node if (removedNode === this.tail) this.tail = node; - return removedNode.value; + return removedNode; }; LinkedList.prototype.insertHead = function(value) { @@ -142,7 +142,7 @@ LinkedList.prototype.insertHead = function(value) { var oldHead = this.head; this.head = newHead; newHead.next = oldHead; - return this.head.value; + return this.head; }; LinkedList.prototype.removeHead = function() { @@ -150,7 +150,7 @@ LinkedList.prototype.removeHead = function() { var newHead = oldHead.next; this.head = newHead; oldHead.next = null; - return oldHead.value; + return oldHead; } LinkedList.prototype.findNode = function(value) { @@ -164,7 +164,7 @@ LinkedList.prototype.findNode = function(value) { LinkedList.prototype.appendToTail = function(value) { var newTail = new Node(value); - + // // without myList.tail property: O(n) // var node = this.head; // while(node.next) { @@ -176,7 +176,7 @@ LinkedList.prototype.appendToTail = function(value) { this.tail.next = newTail; this.tail = newTail; - return newTail.value; + return newTail; }; @@ -203,7 +203,3 @@ myList.insertAfter(myList.findNode(2), 2.5); console.log(myList.print(), 'should be 0, 2, 2.5, 3, 4'); myList.removeAfter(myList.findNode(2)); console.log(myList.print(), 'should be 0, 2, 3, 4'); - - - - From d5691bfc83b48c5dffb402ca6ba9c345eeabb8e7 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Sun, 8 Nov 2015 17:49:05 -0800 Subject: [PATCH 11/33] Implement graph data structure --- data-structures/graph.js | 113 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 data-structures/graph.js diff --git a/data-structures/graph.js b/data-structures/graph.js new file mode 100644 index 0000000..5807ed2 --- /dev/null +++ b/data-structures/graph.js @@ -0,0 +1,113 @@ +/* +GRAPHS + +Abstract data type + +Basic Graph: +Stores nodes (represented by any primitive value) and the neighbors for each node. This implementation represents a graph as an adjacency list (https://en.wikipedia.org/wiki/Adjacency_list). + +Here's an example: +1---2---3 + \ / + 4 +graph = { + 1: [2, 4], + 2: [1, 3, 4], + 3: [2], + 4: [1, 2] +} + +Constraints: +This graph implementation is undirected and can have unconnected nodes. + +*** Operations: + + + +*** Nightmare mode: + + +*/ + +function Graph () { + this._nodes = {}; +} + +/* +This implementation represents a graph with an adjacency list. +Example: +{ + 1: [2, 4], + 2: [1, 3, 4], + 3: [2], + 4: [1, 2] +} +*/ + +Graph.prototype.addNode = function(value) { + if (value === undefined) return; + this._nodes[value] = this._nodes[value] || []; +}; + +Graph.prototype.removeNode = function(value) { + this._nodes[value].forEach(function(neighbor) { + var neighborEdges = this._nodes[neighbor]; + var index = neighborEdges.indexOf(value); + neighborEdges.splice(index, 1); + }) + delete this._nodes[value]; +}; + +Graph.prototype.contains = function(value) { + return this._nodes[value] !== undefined; +}; + +Graph.prototype.addEdge = function(value1, value2) { + if (!this._nodes[value1] || !this._nodes[value2]) return 'Invalid node value'; + this._nodes[value1].push(value2); + this._nodes[value2].push(value1); +}; + +Graph.prototype.removeEdge = function(value1, value2) { + if (!this._nodes[value1] || !this._nodes[value2]) return 'Invalid node value'; + var value1Neighbors = this._nodes[value1]; + value1Neighbors.splice(value1Neighbors.indexOf(value2), 1); + var value2Neighbors = this._nodes[value2]; + value2Neighbors.splice(value2Neighbors.indexOf(value1), 1); +}; + +Graph.prototype.hasEdge = function(value1, value2) { + return this._nodes[value1].indexOf(value2) > -1; +}; + +Graph.prototype.forEach = function(fn) { + for (var node in this._nodes) { + fn(node, this._nodes[node], this._nodes); + } +}; + +var graph = new Graph(); + +graph.addNode(1); +graph.addNode(2); +graph.addNode(3); +graph.addNode(4); +graph.addNode(5); +console.log(graph._nodes, 'should have 5'); +graph.removeNode(5); +console.log(graph._nodes, 'should NOT have 5'); +console.log(graph.contains(4), 'should be true'); +console.log(graph.contains(7), 'should be false'); +graph.addEdge(1,2); +graph.addEdge(1,4); +graph.addEdge(3,2); +graph.addEdge(2,4); +graph.addEdge(3,4); +console.log(graph._nodes); +graph.removeEdge(4,3); +console.log(graph._nodes); +console.log(graph.hasEdge(1,2), 'should be true'); +console.log(graph.hasEdge(1,3), 'should be false'); +graph.forEach(function(node, neighbors) { + console.log(node, 'has neighbors:', neighbors); +}); From 2f7b8a4744e25fc44ce4fbfa121df78ea1513aae Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Sun, 8 Nov 2015 17:54:40 -0800 Subject: [PATCH 12/33] Return node instead of just value --- data-structures/doublyLinkedList.js | 31 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/data-structures/doublyLinkedList.js b/data-structures/doublyLinkedList.js index c5e1657..4254b7e 100644 --- a/data-structures/doublyLinkedList.js +++ b/data-structures/doublyLinkedList.js @@ -17,19 +17,19 @@ myList.print() => string with all values in list (ex: '0, 1, 2, 3') myList.insertAfter(refNode, value) -=> value of new node +=> new node insert new node associated with value passed in after refNode myList.removeAfter(refNode) -=> value of removed node +=> removed node remove node after the refNode myList.insertHead(value) -=> value associated with new head +=> new head insert new head node at the beginning of the list with the value passed in myList.removeHead() -=> value of removed head node +=> removed head node remove the head node of the linked list myList.findNode(value) @@ -40,11 +40,11 @@ myList.findNode(value) Say we have a linked list that has 100 items and we want to add an item to the very end. How would you do that with your current implementation? How can you modify the data structure to add an item to the end in constant time? myList.appendToTail(value) -=> value of tail node +=> tail node add a new tail node at the end of the list with the associated value passed in myList.removeTail() -=> value of removed tail node +=> removed tail node remove the tail node from the list @@ -59,11 +59,11 @@ How can we modify our data structures (Node and Linked List classes) so that we Once you've come up with a plan, implement the following methods. myList.insertBefore(refNode, value) -=> value of new node inserted +=> new node inserted insert new node with associated value before refNode myList.removeBefore(refNode) -=> value of removed node +=> removed node remove node before the refNode passed in @@ -122,7 +122,7 @@ LinkedList.prototype.insertAfter = function(node, value) { if (this.tail === node) this.tail = newNext; // set prev properties - return newNext.value; + return newNext; }; LinkedList.prototype.removeAfter = function(node) { @@ -144,7 +144,7 @@ LinkedList.prototype.removeAfter = function(node) { // if removedNode is tail, set tail to node if (removedNode === this.tail) this.tail = node; - return removedNode.value; + return removedNode; }; LinkedList.prototype.insertHead = function(value) { @@ -153,7 +153,7 @@ LinkedList.prototype.insertHead = function(value) { this.head = newHead; newHead.next = oldHead; oldHead.prev = newHead; - return this.head.value; + return this.head; }; LinkedList.prototype.removeHead = function() { @@ -162,7 +162,7 @@ LinkedList.prototype.removeHead = function() { this.head = newHead; newHead.prev = null; oldHead.next = null; - return oldHead.value; + return oldHead; } LinkedList.prototype.findNode = function(value) { @@ -191,7 +191,7 @@ LinkedList.prototype.appendToTail = function(value) { newTail.prev = oldTail; this.tail = newTail; - return newTail.value; + return newTail; }; LinkedList.prototype.insertBefore = function(node, value) { @@ -207,7 +207,7 @@ LinkedList.prototype.insertBefore = function(node, value) { // if node is head, set newPrev as head if (node === this.head) this.head = newPrev; - return newPrev.value; + return newPrev; }; LinkedList.prototype.removeBefore = function(node) { @@ -227,8 +227,7 @@ LinkedList.prototype.removeBefore = function(node) { removedNode.next = null; removedNode.prev = null; - - return removedNode.value; + return removedNode; }; var myList = new LinkedList(0); From b22d12da59debd240444ee09d5279c1f23fc6243 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Sun, 8 Nov 2015 21:29:22 -0800 Subject: [PATCH 13/33] Implement traversal methods --- data-structures/graph.js | 119 ++++++++++++++++++++++++++++++++++----- 1 file changed, 104 insertions(+), 15 deletions(-) diff --git a/data-structures/graph.js b/data-structures/graph.js index 5807ed2..da6f766 100644 --- a/data-structures/graph.js +++ b/data-structures/graph.js @@ -18,32 +18,80 @@ graph = { } Constraints: -This graph implementation is undirected and can have unconnected nodes. +This graph implementation is undirected and can have unconnected nodes. The nodes are represented by unique primitive values. *** Operations: +graph.addNode(value) // value must be a primitive +=> undefined +Add node to graph +graph.removeNode(value) +=> undefined +Remove node from graph +graph.contains(value) +=> true/false +Returns true if value is found in graph, false otherwise + +graph.addEdge(value1, value2) +=> undefined +Create connection between two nodes if they're both present in the graph + +graph.removeEdge(value1, value2) +=> undefined +Remove connection between two nodes + +graph.hasEdge(value1, value2) +=> true/false +Returns true if edge exists, false otherwise + +graph.forEach(callback) +=> undefined +Traverse the graph and invoke the passed callback once for each node. The callback function receives the following for each node: node value, node Neighbors, all nodes. *** Nightmare mode: +Implement traversal methods for depth-first and breadth-first traversal. The methods take a starting node and a callback that gets invoked for each node. The callback should receive two arguments: the node value and the distance (number of edges that separate the node from the starting node). See example usage below. -*/ +graph.traverseDepthFirst(value1, callback) +=> undefined +Starting at the node with the value passed in, traverse the graph and invoke the callback for each node in a depth-first fashion. -function Graph () { - this._nodes = {}; -} +graph.traverseBreadthFirst(value, callback) +=> undefined +Starting at the node with the value passed in, traverse the graph and invoke the callback for each node in a breadth-first fashion. -/* -This implementation represents a graph with an adjacency list. -Example: +Example Usage: +1---2---3---5 + \ / + 4 { - 1: [2, 4], - 2: [1, 3, 4], - 3: [2], - 4: [1, 2] + '1': [ 2, 4 ], + '2': [ 1, 3, 4 ], + '3': [ 2, 5 ], + '4': [ 1, 2 ], + '5': [ 3 ] } + +var traverseDF = []; +graph.traverseDepthFirst(1, function(val, distance) { traverseDF.push([val, distance]) }); +traverseDF should be [ [ 1, 0 ], [ 2, 1 ], [ 3, 2 ], [ 5, 3 ], [ 4, 2 ] ] + +var traverseBF = []; +graph.traverseBreadthFirst(1, function(val, distance) { traverseBF.push([val, distance]) }); +traverseBF should be [ [ 1, 0 ], [ 2, 1 ], [ 4, 1 ], [ 3, 2 ], [ 5, 3 ] ] + + +*** Exercises: + +Given a directed graph and two nodes in the graph, write a function that indicates whether there is a route between the two nodes. Bonus: rather than returning a boolean, have your function return the shortest distance between the two nodes (the number of edges that separate them). + */ +function Graph () { + this._nodes = {}; +} + Graph.prototype.addNode = function(value) { if (value === undefined) return; this._nodes[value] = this._nodes[value] || []; @@ -51,9 +99,9 @@ Graph.prototype.addNode = function(value) { Graph.prototype.removeNode = function(value) { this._nodes[value].forEach(function(neighbor) { - var neighborEdges = this._nodes[neighbor]; - var index = neighborEdges.indexOf(value); - neighborEdges.splice(index, 1); + var neighborsNeighbors = this._nodes[neighbor]; + var index = neighborsNeighbors.indexOf(value); + neighborsNeighbors.splice(index, 1); }) delete this._nodes[value]; }; @@ -86,6 +134,38 @@ Graph.prototype.forEach = function(fn) { } }; +Graph.prototype.traverseDepthFirst = function(value, fn, visited, distance) { + if (!this._nodes[value] || typeof fn !== 'function') return 'Invalid value or function'; + visited = visited || {}; + distance = distance || 0; + fn(value, distance); + visited[value] = true; + this._nodes[value].forEach(function(neighbor) { + if (visited[neighbor]) return; + this.traverseDepthFirst(neighbor, fn, visited, distance+1); + }, this); +}; + +Graph.prototype.traverseBreadthFirst = function(value, fn) { + if (!this._nodes[value] || typeof fn !== 'function') return 'Invalid value or function'; + var visited = {}; + var queue = [value]; + visited[value] = 0; + while (queue.length) { + var node = queue.shift(); + fn(node, visited[node]); + var neighbors = this._nodes[node].filter(function(neighbor) { + if (visited[neighbor] === undefined) { + visited[neighbor] = visited[node]+1; + return true; + } + }); + queue = queue.concat(neighbors); + } +}; + + + var graph = new Graph(); graph.addNode(1); @@ -111,3 +191,12 @@ console.log(graph.hasEdge(1,3), 'should be false'); graph.forEach(function(node, neighbors) { console.log(node, 'has neighbors:', neighbors); }); +graph.addNode(5); +graph.addEdge(3,5); +console.log(graph._nodes); +var traverseDF = []; +graph.traverseDepthFirst(1, function(val, dist) { traverseDF.push([val, dist]) }); +console.log(traverseDF, 'should be [ [ 1, 0 ], [ 2, 1 ], [ 3, 2 ], [ 5, 3 ], [ 4, 2 ] ]'); +var traverseBF = []; +graph.traverseBreadthFirst(1, function(val, dist) { traverseBF.push([val, dist]) }); +console.log(traverseBF, 'should be [ [ 1, 0 ], [ 2, 1 ], [ 4, 1 ], [ 3, 2 ], [ 5, 3 ] ]'); From 07b67d4a1cea1b1aacabefd89f67d56fa4b17a5c Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Sun, 8 Nov 2015 21:29:41 -0800 Subject: [PATCH 14/33] Change title of exercise section --- data-structures/linkedList.js | 1 + data-structures/queue.js | 2 +- data-structures/set.js | 2 +- data-structures/stack.js | 2 +- data-structures/tree.js | 4 +++- 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/data-structures/linkedList.js b/data-structures/linkedList.js index 1cc2460..638b3d5 100644 --- a/data-structures/linkedList.js +++ b/data-structures/linkedList.js @@ -66,6 +66,7 @@ myList.removeBefore(refNode) => removed node remove node before the refNode passed in +*SOLUTION: See doublyLinkedList.js file* *** Extra Credit: diff --git a/data-structures/queue.js b/data-structures/queue.js index d784307..663c549 100644 --- a/data-structures/queue.js +++ b/data-structures/queue.js @@ -28,7 +28,7 @@ myQueue.count() => number of elements in queue -*** Nightmare mode: +*** Exercises: Modify your queue to take a max capacity and return a string if you try to add an element when there's no more room: myQueue.enqueue(value) diff --git a/data-structures/set.js b/data-structures/set.js index 22ac80e..a147b5e 100644 --- a/data-structures/set.js +++ b/data-structures/set.js @@ -27,7 +27,7 @@ mySet.forEach(callbackFn) calls callbackFn once for each value in the set -*** Extra Credit: +*** Exercises: Modify your set to take a max capacity and return a string if you try to add an element when there's no more room mySet.add(value) diff --git a/data-structures/stack.js b/data-structures/stack.js index 74bb7a5..cbae554 100644 --- a/data-structures/stack.js +++ b/data-structures/stack.js @@ -29,7 +29,7 @@ myStack.count() => number of elements in stack -*** Nightmare mode: +*** Exercises: Modify your stack to take a max capacity and return a string if you try to add an element when there's no more room: myStack.push(value) diff --git a/data-structures/tree.js b/data-structures/tree.js index 8e02d4a..a6a2a1e 100644 --- a/data-structures/tree.js +++ b/data-structures/tree.js @@ -33,11 +33,13 @@ tree.traverseBreadthFirst(callback) => undefined Invoke the callback for every node in a breadth-first order -*** Nightmare mode: + +*** Exercises: Given treeA and treeB, check if treeB is a subtree of treeA (meaning that there exists a node n in treeA such that the subtree of n is identical to treeB). Given a dictionary, create a prefix tree (commonly known as a trie) https://en.wikipedia.org/wiki/Trie + */ // N-ary Tree (any number of children) From bef1fd1c495e1aeaa358f8d4b76fefb63d483c61 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Sun, 8 Nov 2015 21:30:42 -0800 Subject: [PATCH 15/33] Rename exercise section --- data-structures/binarySearchTree.js | 2 +- data-structures/doublyLinkedList.js | 4 +--- data-structures/hashTable.js | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/data-structures/binarySearchTree.js b/data-structures/binarySearchTree.js index 93b3d42..aaa631b 100644 --- a/data-structures/binarySearchTree.js +++ b/data-structures/binarySearchTree.js @@ -44,7 +44,7 @@ bsTree.checkIfBalanced() => true/false For this exercise, let's say that a tree is balanced if the minimum height and the maximum height differ by no more than 1. The height for a branch is the number of levels below the root. -*** Nightmare mode: +*** Additional Exercises: A binary search tree was created by iterating over an array and inserting each element into the tree. Given a binary search tree with no duplicates, how many different arrays would result in the creation of this tree. */ diff --git a/data-structures/doublyLinkedList.js b/data-structures/doublyLinkedList.js index 4254b7e..2d98697 100644 --- a/data-structures/doublyLinkedList.js +++ b/data-structures/doublyLinkedList.js @@ -68,7 +68,7 @@ remove node before the refNode passed in -*** Nightmare mode: +*** Additional Exercises: Implement a circularly linked list: https://en.wikipedia.org/wiki/Linked_list#Circularly_linked_list @@ -77,8 +77,6 @@ https://en.wikipedia.org/wiki/Linked_list#Circularly_linked_list */ -// 40 min - function Node(value) { this.value = value; this.next = null; diff --git a/data-structures/hashTable.js b/data-structures/hashTable.js index 66d688c..61d32b0 100644 --- a/data-structures/hashTable.js +++ b/data-structures/hashTable.js @@ -45,7 +45,7 @@ myMap.forEach(callbackFn) Invokes callback function once for each key-value pair in the hash table -*** Nightmare mode: +*** Exercises: Resize the hash table: - if the count becomes greater than 75% of the table size, double the table size and redistribute the key/value pairs From 297d223d46a3facad21001ea3ca8017fc557763d Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Mon, 16 Nov 2015 19:13:52 -0800 Subject: [PATCH 16/33] Add recursion questions and solutions --- recursion/fibonacci.js | 30 +++++++++++++++ recursion/paintFill.js | 5 +++ recursion/practice.js | 0 recursion/robotPaths.js | 65 +++++++++++++++++++++++++++++++++ recursion/stringPermutations.js | 5 +++ 5 files changed, 105 insertions(+) create mode 100644 recursion/fibonacci.js create mode 100644 recursion/paintFill.js create mode 100644 recursion/practice.js create mode 100644 recursion/robotPaths.js create mode 100644 recursion/stringPermutations.js diff --git a/recursion/fibonacci.js b/recursion/fibonacci.js new file mode 100644 index 0000000..23ef40b --- /dev/null +++ b/recursion/fibonacci.js @@ -0,0 +1,30 @@ +/* + +Write a function that outputs the nth Fibonnaci number. A number in this sequence is found by adding up the two numbers before it. + +Fibonnaci's sequence: +input 0 1 2 3 4 5 6 7 8 9 ... +output 0 1 1 2 3 5 8 13 21 34 ... + +What is the time complexity? Can you think of optimizing your solution? (Hint: look up dynamic programming) + +*/ + +// O(2^n) implementation +function fibonnaci (n) { + if (n === 0 || n === 1) return n; + return fibonnaci(n-1) + fibonnaci(n-2); +} + + +// O(n) implementation using dynamic programming +function fibonnaciDP (n) { + var memo = { + 0: 0, + 1: 1 + }; + function recurse(m) { + return memo[m] !== undefined ? memo[m] : recurse(m-1) + recurse(m-2); + } + return recurse(n); +} diff --git a/recursion/paintFill.js b/recursion/paintFill.js new file mode 100644 index 0000000..3b9691f --- /dev/null +++ b/recursion/paintFill.js @@ -0,0 +1,5 @@ +/* + +Implement a function that takes in a two-dimensional array of colors that represents a screen, a point in the array, and a color. The function will change the original color of the point to the new color and will fill the surrounding area with the original color in the same fashion. + +*/ diff --git a/recursion/practice.js b/recursion/practice.js new file mode 100644 index 0000000..e69de29 diff --git a/recursion/robotPaths.js b/recursion/robotPaths.js new file mode 100644 index 0000000..53dee7b --- /dev/null +++ b/recursion/robotPaths.js @@ -0,0 +1,65 @@ +/* + +Version 1: +Given the size of a grid (X rows and Y columns), write a function that returns the number of possible paths for a robot to take starting at the top left of the grid and ending at the bottom right? The robot can only move to the right and down. + +Version 2: +Now, imagine that the robot can move up, down, left, or right but cannot visit a spot that has already been visited. How many unique paths can the robot take? +Hint: it may be useful to create a grid class and use it to keep track of the state as the robot traverses the grid. What useful methods can you put on your grid class? Can you write an implementation that only uses a single grid? + +*/ + +// Version 1 +function robotPathsV1 (rowCount, columnCount) { + var pathCount = 0; + function recurse(i, j) { + if (i === rowCount-1 && j === columnCount-1) pathCount++; + else { + if (i= rowCount || j >= colCount) { return; } + if (grid.hasBeenVisited(i, j)) { return; } + grid.togglePiece(i, j); + recurse(grid, i+1, j); + recurse(grid, i, j+1); + recurse(grid, i-1, j); + recurse(grid, i, j-1); + grid.togglePiece(i, j); + } + recurse(grid, 0, 0); + return count; +} diff --git a/recursion/stringPermutations.js b/recursion/stringPermutations.js new file mode 100644 index 0000000..5c63284 --- /dev/null +++ b/recursion/stringPermutations.js @@ -0,0 +1,5 @@ +/* + +Write a function that takes a string and returns all permutations of the string. + +/* From 3fecb157b51d6e800b3006dc1e04901951e55cb3 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Tue, 17 Nov 2015 01:12:12 -0800 Subject: [PATCH 17/33] add questions --- data-structures/queue.js | 6 ++++ data-structures/stack.js | 36 ++++++++++++++++++++++- recursion/greatestCommonDivisor.js | 5 ++++ recursion/mathematicalOperations.js | 5 ++++ recursion/paintFill.js | 13 ++++++++ recursion/{robotPaths.js => pathCount.js} | 10 +++---- recursion/practice.js | 0 recursion/stringPermutations.js | 18 ++++++++++-- 8 files changed, 85 insertions(+), 8 deletions(-) create mode 100644 recursion/greatestCommonDivisor.js create mode 100644 recursion/mathematicalOperations.js rename recursion/{robotPaths.js => pathCount.js} (68%) delete mode 100644 recursion/practice.js diff --git a/data-structures/queue.js b/data-structures/queue.js index 663c549..e79a737 100644 --- a/data-structures/queue.js +++ b/data-structures/queue.js @@ -45,8 +45,14 @@ myQueue.until(7) => 3 What's the time complexity? +Implement a queue using two stacks. + +Implement a queue using one stack. + + */ + function Queue(capacity) { this._capacity = capacity; this._storage = {}; diff --git a/data-structures/stack.js b/data-structures/stack.js index cbae554..6860fd3 100644 --- a/data-structures/stack.js +++ b/data-structures/stack.js @@ -46,7 +46,41 @@ myStack.until(7) => 4 What's the time complexity? - */ +Create a min method that returns the minimum value in the stack in constant time. + +Towers of Hanoi: +You have three vertical rods and N disks of different sizes that can slide onto the rods. Initially the disks are arranged in a stack in ascending order of size on the first rod and your goal is to move all the disks to the last rod given +... + +Implement k stacks in a single array of fixed length. Start by first implementing two stacks in an array. + +Balanced parens. + +function balanced (str) { + return Array.prototype.reduce.call(str, function(acc, el){ + if (el === '(') { + return { + left: acc.left + 1, + right: acc.right, + valid: acc.valid + } + } else if (el === ')') { + return { + left: acc.left, + right: acc.right + 1, + valid: ((acc.left <= acc.right + 1) && acc.valid) + } + } else { + return acc + } + }, { + left: 0, + right: 0, + valid: true + }) +} + +*/ function Stack(capacity) { this._capacity = capacity; diff --git a/recursion/greatestCommonDivisor.js b/recursion/greatestCommonDivisor.js new file mode 100644 index 0000000..0156479 --- /dev/null +++ b/recursion/greatestCommonDivisor.js @@ -0,0 +1,5 @@ +/* + +Write a function that takes two numbers and returns the greatest common divisor. + +*/ diff --git a/recursion/mathematicalOperations.js b/recursion/mathematicalOperations.js new file mode 100644 index 0000000..5ef1ffb --- /dev/null +++ b/recursion/mathematicalOperations.js @@ -0,0 +1,5 @@ +/* + +Given an array of numbers and a target value, return a string of arithmetic operations (addition, subtraction, multiplication, division) using the values in the array that results in the target value. If it's not possible to compute the target value, return null. + +*/ diff --git a/recursion/paintFill.js b/recursion/paintFill.js index 3b9691f..3c4277a 100644 --- a/recursion/paintFill.js +++ b/recursion/paintFill.js @@ -3,3 +3,16 @@ Implement a function that takes in a two-dimensional array of colors that represents a screen, a point in the array, and a color. The function will change the original color of the point to the new color and will fill the surrounding area with the original color in the same fashion. */ + +function paintFill(screen, point, newColor) { + var originalColor = screen[point.row][point.column]; + function recurse(row, column) { + screen[row][column] = newColor; + if (screen[row-1][column] === originalColor) recurse(row-1, column); + if (screen[row+1][column] === originalColor) recurse(row+1, column); + if (screen[row][column-1] === originalColor) recurse(row, column-1); + if (screen[row][column+1] === originalColor) recurse(row, column+1); + } + recurse(point.row, point.column); + return screen; +} diff --git a/recursion/robotPaths.js b/recursion/pathCount.js similarity index 68% rename from recursion/robotPaths.js rename to recursion/pathCount.js index 53dee7b..ab278a7 100644 --- a/recursion/robotPaths.js +++ b/recursion/pathCount.js @@ -1,16 +1,16 @@ /* Version 1: -Given the size of a grid (X rows and Y columns), write a function that returns the number of possible paths for a robot to take starting at the top left of the grid and ending at the bottom right? The robot can only move to the right and down. +Given the size of a grid (X rows and Y columns), write a function that returns the number of possible paths one can take starting at the top left of the grid and ending at the bottom right, assuming you can only move to the right and down. Version 2: -Now, imagine that the robot can move up, down, left, or right but cannot visit a spot that has already been visited. How many unique paths can the robot take? -Hint: it may be useful to create a grid class and use it to keep track of the state as the robot traverses the grid. What useful methods can you put on your grid class? Can you write an implementation that only uses a single grid? +Now, imagine that the you can move up, down, left, or right but cannot visit a spot that has already been visited. How many unique paths can the you take? +Hint: it may be useful to create a grid class and use it to keep track of the state as the you traverses the grid. What useful methods can you put on your grid class? Can you write an implementation that only uses a single grid? */ // Version 1 -function robotPathsV1 (rowCount, columnCount) { +function pathCountV1 (rowCount, columnCount) { var pathCount = 0; function recurse(i, j) { if (i === rowCount-1 && j === columnCount-1) pathCount++; @@ -44,7 +44,7 @@ Grid.prototype.hasBeenVisited = function(i, j) { }; -var robotPathsV2 = function(rowCount, colCount) { +var pathCountV2 = function(rowCount, colCount) { var grid = new Grid(rowCount, colCount); var count = 0; function recurse(grid, i, j) { diff --git a/recursion/practice.js b/recursion/practice.js deleted file mode 100644 index e69de29..0000000 diff --git a/recursion/stringPermutations.js b/recursion/stringPermutations.js index 5c63284..7ba7517 100644 --- a/recursion/stringPermutations.js +++ b/recursion/stringPermutations.js @@ -1,5 +1,19 @@ /* -Write a function that takes a string and returns all permutations of the string. +Write a function that takes a string and returns all permutations of the string. Ensure that there are no duplicates in the output. -/* +*/ + +function permutations(str) { + var results = {}; + function recurse(word, remainder) { + if (remainder.length === 0) { + return results[word] = true; + } + for (var i=0; i Date: Tue, 17 Nov 2015 04:44:57 -0800 Subject: [PATCH 18/33] Add exercise solutions --- data-structures/queue.js | 121 ++++++++++++++++++++++++---- data-structures/stack.js | 103 +++++++++++++---------- recursion/greatestCommonDivisor.js | 17 +++- recursion/mathematicalOperations.js | 5 -- 4 files changed, 182 insertions(+), 64 deletions(-) delete mode 100644 recursion/mathematicalOperations.js diff --git a/data-structures/queue.js b/data-structures/queue.js index e79a737..a3c1586 100644 --- a/data-structures/queue.js +++ b/data-structures/queue.js @@ -45,16 +45,13 @@ myQueue.until(7) => 3 What's the time complexity? -Implement a queue using two stacks. +Implement a queue using two stacks (include enqueue, dequeue, peek, and count). -Implement a queue using one stack. - - - */ +*/ function Queue(capacity) { - this._capacity = capacity; + this._capacity = capacity || Infinity; this._storage = {}; this._head = 0; this._tail = 0; @@ -87,13 +84,107 @@ Queue.prototype.count = function() { return this._tail - this._head; }; +// O(n) +Queue.prototype.contains = function(value) { + for (var i = this._head; i < this._tail; i++) { + if (this._storage[i] === value) return true; + } + return false; +}; + +// O(n) +Queue.prototype.until = function(value) { + for (var i = this._head; i < this._tail; i++) { + if (this._storage[i] === value) return i-this._head+1; + } + return null; +}; + +// var myQueue = new Queue(3); +// console.log(myQueue.enqueue('a'), 'should be 1'); +// console.log(myQueue.enqueue('b'), 'should be 2'); +// console.log(myQueue.enqueue('c'), 'should be 3'); +// console.log(myQueue.enqueue('d'), 'should be Max capacity reached'); +// console.log(myQueue.dequeue(), 'should be a'); +// console.log(myQueue.count(), 'should be 2'); +// console.log(myQueue.peek(), 'should be b'); +// console.log(myQueue.count(), 'should be 2'); +// console.log(myQueue.contains('b'), 'should be true'); +// console.log(myQueue.contains('d'), 'should be false'); +// console.log(myQueue._storage, myQueue._head); +// console.log(myQueue.until('b'), 'should be 1'); +// console.log(myQueue.until('c'), 'should be 2'); +// console.log(myQueue.until('d'), 'should be null'); + +// ____________________________________________ +// EXERCISES +// Implement a queue using two stacks +function Stack(capacity) { + this._capacity = capacity || Infninty; + this._storage = {}; + this._count = 0; +} + +Stack.prototype.push = function(value) { + if (this._count < this._capacity) { + this._storage[this._count++] = value; + return this._count; + } + return 'Max capacity already reached. Remove element before adding a new one.'; +}; + +Stack.prototype.pop = function() { + var value = this._storage[--this._count]; + delete this._storage[this._count]; + if (this._count < 0) { + this._count = 0; + } + return value; +}; + +Stack.prototype.peek = function() { + return this._storage[this._count-1]; +} + +Stack.prototype.count = function() { + return this._count; +}; + +function Queue_TwoStacks() { + this._stackIn = new Stack(); + this._stackOut = new Stack(); +} + +Queue_TwoStacks.prototype.enqueue = function(val) { + this.storageIn.push(val); +}; + +Queue_TwoStacks.prototype._transferStacks = function() { + while (this._stackIn.count() > 0) { + this._stackOut.push(this._stackIn.pop()); + } +}; + +Queue_TwoStacks.prototype.dequeue = function() { + if (this._stackOut.count() === 0) this._transferStacks(); + return this._stackOut.pop(); +}; + +Queue_TwoStacks.prototype.count = function() { + return this._stackIn.count() + this._stackOut.count(); +}; + +Queue_TwoStacks.prototype.peek = function() { + if (this._stackOut.count() === 0) this._transferStacks(); + return this._stackOut.peek(); +}; -var myQueue = new Queue(3); -console.log(myQueue.enqueue('a'), 'should be 1'); -console.log(myQueue.enqueue('b'), 'should be 2'); -console.log(myQueue.enqueue('c'), 'should be 3'); -console.log(myQueue.enqueue('d'), 'should be Max capacity reached'); -console.log(myQueue.dequeue(), 'should be a'); -console.log(myQueue.count(), 'should be 2'); -console.log(myQueue.peek(), 'should be b'); -console.log(myQueue.count(), 'should be 2'); +// var myQueue_TwoStacks = new Queue(3); +// console.log(myQueue_TwoStacks.enqueue('a'), 'should be 1'); +// console.log(myQueue_TwoStacks.enqueue('b'), 'should be 2'); +// console.log(myQueue_TwoStacks.enqueue('c'), 'should be 3'); +// console.log(myQueue_TwoStacks.enqueue('d'), 'should be Max capacity reached'); +// console.log(myQueue_TwoStacks.dequeue(), 'should be a'); +// console.log(myQueue_TwoStacks.count(), 'should be 2'); +// console.log(myQueue_TwoStacks.peek(), 'should be b'); +// console.log(myQueue_TwoStacks.count(), 'should be 2'); diff --git a/data-structures/stack.js b/data-structures/stack.js index 6860fd3..ecfd43d 100644 --- a/data-structures/stack.js +++ b/data-structures/stack.js @@ -46,44 +46,12 @@ myStack.until(7) => 4 What's the time complexity? -Create a min method that returns the minimum value in the stack in constant time. - -Towers of Hanoi: -You have three vertical rods and N disks of different sizes that can slide onto the rods. Initially the disks are arranged in a stack in ascending order of size on the first rod and your goal is to move all the disks to the last rod given -... - -Implement k stacks in a single array of fixed length. Start by first implementing two stacks in an array. - -Balanced parens. - -function balanced (str) { - return Array.prototype.reduce.call(str, function(acc, el){ - if (el === '(') { - return { - left: acc.left + 1, - right: acc.right, - valid: acc.valid - } - } else if (el === ')') { - return { - left: acc.left, - right: acc.right + 1, - valid: ((acc.left <= acc.right + 1) && acc.valid) - } - } else { - return acc - } - }, { - left: 0, - right: 0, - valid: true - }) -} +Implement a MinStack that has a min method which will return the minimum value in the stack in constant time. */ function Stack(capacity) { - this._capacity = capacity; + this._capacity = capacity || Infinity; this._storage = {}; this._count = 0; } @@ -118,12 +86,61 @@ Stack.prototype.count = function() { }; -var myStack = new Stack(3); -console.log(myStack.push('a'), 'should be 1'); -console.log(myStack.push('b'), 'should be 2'); -console.log(myStack.push('c'), 'should be 3'); -console.log(myStack.push('d'), 'should be Max capacity reached'); -console.log(myStack.pop(), 'should be c'); -console.log(myStack.count(), 'should be 2'); -console.log(myStack.peek(), 'should be b'); -console.log(myStack.count(), 'should be 2'); +// var myStack = new Stack(3); +// console.log(myStack.push('a'), 'should be 1'); +// console.log(myStack.push('b'), 'should be 2'); +// console.log(myStack.push('c'), 'should be 3'); +// console.log(myStack.push('d'), 'should be Max capacity reached'); +// console.log(myStack.pop(), 'should be c'); +// console.log(myStack.count(), 'should be 2'); +// console.log(myStack.peek(), 'should be b'); +// console.log(myStack.count(), 'should be 2'); + +//____________________________________________ +// Implement a min stack +function MinStack(capacity) { + this._capacity = capacity; + this._storage = {}; + this._count = 0; + this._min = new Stack(); +} + +// O(1) +MinStack.prototype.push = function(value) { + if (this._count < this._capacity) { + if (this._min.peek() < value) { + this._min.push(this._min.peek()); + } else { + this._min.push(value); + } + this._storage[this._count++] = value; + return this._count; + } + return 'Max capacity already reached. Remove element before adding a new one.'; +}; + +// O(1) +MinStack.prototype.pop = function() { + this._min.pop(); + var value = this._storage[--this._count]; + delete this._storage[this._count]; + if (this._count < 0) { + this._count = 0; + } + return value; +}; + +// O(1) +MinStack.prototype.peek = function() { + return this._storage[this._count-1]; +}; + +// O(1) +MinStack.prototype.count = function() { + return this._count; +}; + +// O(1) +MinStack.prototype.min = function() { + return this._min.peek(); +}; diff --git a/recursion/greatestCommonDivisor.js b/recursion/greatestCommonDivisor.js index 0156479..470110c 100644 --- a/recursion/greatestCommonDivisor.js +++ b/recursion/greatestCommonDivisor.js @@ -1,5 +1,20 @@ /* -Write a function that takes two numbers and returns the greatest common divisor. +Write a function that takes two numbers and returns the greatest common divisor. */ + +// Euclid's algorithm +function gcd(num1, num2) { + var min = Math.min(num1, num2); + var max = Math.max(num1, num2); + if (max % min === 0) return min; + else return gcd(min, max % min); +} + +// Dijkstra's algorithm +function gcd(num1, num2) { + if (num1 === num2) return num1; + else if (num1 > num2) return gcd(num1-num2, num2); + else return gcd(num1, num2-num1); +} diff --git a/recursion/mathematicalOperations.js b/recursion/mathematicalOperations.js deleted file mode 100644 index 5ef1ffb..0000000 --- a/recursion/mathematicalOperations.js +++ /dev/null @@ -1,5 +0,0 @@ -/* - -Given an array of numbers and a target value, return a string of arithmetic operations (addition, subtraction, multiplication, division) using the values in the array that results in the target value. If it's not possible to compute the target value, return null. - -*/ From fadfdc605c34e230c577342f8d93d884b504de0d Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Tue, 17 Nov 2015 09:32:22 -0800 Subject: [PATCH 19/33] Add test for solution --- recursion/paintFill.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/recursion/paintFill.js b/recursion/paintFill.js index 3c4277a..aba64d8 100644 --- a/recursion/paintFill.js +++ b/recursion/paintFill.js @@ -16,3 +16,13 @@ function paintFill(screen, point, newColor) { recurse(point.row, point.column); return screen; } + +var screen = [ + [1,1,1,1,1,1,1], + [1,2,2,2,2,1,1], + [3,3,3,2,2,2,1], + [1,1,2,2,2,3,3], + [1,1,1,1,3,3,3] +]; + +console.log(paintFill(screen, {row: 2, column: 4}, 5)); From 52eb253f68b03383f3461db83c61c4a4a2164d04 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Tue, 17 Nov 2015 09:35:17 -0800 Subject: [PATCH 20/33] Fix bug --- recursion/paintFill.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/recursion/paintFill.js b/recursion/paintFill.js index aba64d8..df0c72e 100644 --- a/recursion/paintFill.js +++ b/recursion/paintFill.js @@ -8,8 +8,8 @@ function paintFill(screen, point, newColor) { var originalColor = screen[point.row][point.column]; function recurse(row, column) { screen[row][column] = newColor; - if (screen[row-1][column] === originalColor) recurse(row-1, column); - if (screen[row+1][column] === originalColor) recurse(row+1, column); + if (screen[row-1] && screen[row-1][column] === originalColor) recurse(row-1, column); + if (screen[row+1] && screen[row+1][column] === originalColor) recurse(row+1, column); if (screen[row][column-1] === originalColor) recurse(row, column-1); if (screen[row][column+1] === originalColor) recurse(row, column+1); } @@ -25,4 +25,4 @@ var screen = [ [1,1,1,1,3,3,3] ]; -console.log(paintFill(screen, {row: 2, column: 4}, 5)); +console.log(paintFill(screen, {row: 4, column: 4}, 5)); From 25b53867f55a3a6c71e82319b0b371c8db1a7238 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Wed, 18 Nov 2015 01:23:04 -0800 Subject: [PATCH 21/33] Implement heap --- data-structures/heap.js | 123 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 data-structures/heap.js diff --git a/data-structures/heap.js b/data-structures/heap.js new file mode 100644 index 0000000..052ca24 --- /dev/null +++ b/data-structures/heap.js @@ -0,0 +1,123 @@ +/* +HEAPS + +Abstract data type + +A max heap is a special type of binary tree that satisfies two properties: + +1. Shape property – All nodes which are at the same depth of the tree from the root node are said to be on the same level. Each level of a max heap must be filled with nodes before any nodes can appear on the next level of the max heap. When nodes are added to the next level of the max heap they must be added from left to right. +2. Heap property – In a max heap all nodes are greater than or equal to each of its children nodes. + +Heaps are usually implemented in an array. The first element is the root, the next two are the children of the root, and the next four are their children. The children of the node at position n are at positions 2n+1 and 2n+2. + + +View visualization here: https://presentpath.github.io/heap-visualizer/ + +*** Operations: + +heap.insert(value) +=> undefined +Add value to heap according to the shape and heap property + +heap.removeMax() +=> max value +Remove the max value from the heap, reorder the heap, and return the max value + +*/ + + +var Heap = function() { + this.storage = []; +}; + +Heap.prototype.insert = function(value) { + // Push to storage array + this.storage.push(value); + + var that = this; + + // Recursive function to handle swaps, input index + var reheapify = function(index) { + + // Get parent index + var parentInd = Math.ceil(index/2-1); + // Base Case : value < parent or parent is null + if (parentInd < 0 || that.storage[index] <= that.storage[parentInd]) { + return 'value added to index '+index; + } + // Recursive Case: swap with parent and make recursive call + that.storage[index] = that.storage[index] ^ that.storage[parentInd]; + that.storage[parentInd] = that.storage[index] ^ that.storage[parentInd]; + that.storage[index] = that.storage[index] ^ that.storage[parentInd]; + + return reheapify(parentInd); + }; + return reheapify(that.storage.length-1); +}; + +// Heap remove max method on prototype +// Remove the max value from a heap, reorder the heap, and return the max value +Heap.prototype.removeMax = function() { + // Check if heap is currently empty + if (this.storage.length === 0) { + // If nothing to remove then return null + return null; + } else if (this.storage.length === 1) { + // If heap only has one element in it then pop off the lone element in the storage array and return it + var removed = this.storage.pop(); + + return removed; + } + + // Handle all other cases where heap has more than one node + // Preserve the max value in order to return it + var maxValue = this.storage[0]; + // Replace the root node with the last node of the heap and remove the last node + this.storage[0] = this.storage.pop(); + + // Preserve context for inner recursive helper function + var that = this; + + // Recursive function to restore the heap property of the heap + var reheapify = function(index) { + // Set index of max value to current node's index + var maxIndex = index; + + // Check first child node's value against current node + if ((2*index + 1 < that.storage.length) && (that.storage[2*index + 1] > that.storage[index])) { + // If greater then set index of max value to first child node's index + maxIndex = 2*index + 1; + } + // Check second child node's value against current max node + if ((2*index + 2 < that.storage.length) && (that.storage[2*index + 2] > that.storage[maxIndex])) { + // If greater then set index of max value to second child node's index + maxIndex = 2*index + 2; + } + // If the index of the max value is not equal to the index of the current node + // Then swap the nodes and reheapify at the new index of the current node + if (maxIndex !== index) { + // Swap node values + that.storage[index] = that.storage[index] ^ that.storage[maxIndex]; + that.storage[maxIndex] = that.storage[index] ^ that.storage[maxIndex]; + that.storage[index] = that.storage[index] ^ that.storage[maxIndex]; + + // Reheapify at new index of current node + reheapify(maxIndex); + } + }; + + // Recursively move the swapped node down the heap until it's greater than both of its children + reheapify(0); + + // Return the removed max value from the heap + return maxValue; +}; + +// Instantiate heap +var heap = new Heap(); + +heap.insert(1); +heap.insert(2); +heap.insert(3); +heap.insert(5); +console.log(heap.storage); From 5da6bfaa6cf868efa38ce0c987512a252913551e Mon Sep 17 00:00:00 2001 From: Bianca Elizabeth Date: Wed, 18 Nov 2015 06:31:20 -0600 Subject: [PATCH 22/33] Update quick.js --- sorting-algorithms/quick.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sorting-algorithms/quick.js b/sorting-algorithms/quick.js index d685ba5..d580ba6 100644 --- a/sorting-algorithms/quick.js +++ b/sorting-algorithms/quick.js @@ -10,7 +10,8 @@ It has a partitioning step, in which you pick an element (called a pivot) and pa *** Exercises - Write a partition helper function. For choice of pivot, for a basic implementation, we recommend choosing either the first or last element in the subarray. If you need hints, look up the Lumoto partiton scheme. Test this out before moving forward! -- Implement quicksort +- Implement quicksort iteratively +- Implement quicksort recursively - Identify time complexity - Identify space complexity From 3f53a95f6399478ff817f50a8c23773178fe4d73 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Wed, 18 Nov 2015 09:59:17 -0800 Subject: [PATCH 23/33] Use temp var for swap --- data-structures/heap.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data-structures/heap.js b/data-structures/heap.js index 052ca24..236f470 100644 --- a/data-structures/heap.js +++ b/data-structures/heap.js @@ -46,9 +46,9 @@ Heap.prototype.insert = function(value) { return 'value added to index '+index; } // Recursive Case: swap with parent and make recursive call - that.storage[index] = that.storage[index] ^ that.storage[parentInd]; - that.storage[parentInd] = that.storage[index] ^ that.storage[parentInd]; - that.storage[index] = that.storage[index] ^ that.storage[parentInd]; + var temp = that.storage[index]; + that.storage[index] = that.storage[parentInd]; + that.storage[parentInd] = temp; return reheapify(parentInd); }; @@ -96,7 +96,7 @@ Heap.prototype.removeMax = function() { // If the index of the max value is not equal to the index of the current node // Then swap the nodes and reheapify at the new index of the current node if (maxIndex !== index) { - // Swap node values + // Swap node values (here's a nifty way to do so "in place" using the XOR bitwise operator) that.storage[index] = that.storage[index] ^ that.storage[maxIndex]; that.storage[maxIndex] = that.storage[index] ^ that.storage[maxIndex]; that.storage[index] = that.storage[index] ^ that.storage[maxIndex]; From 650903421feb1194964ab1fe85352c8abb04f691 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Wed, 18 Nov 2015 09:59:30 -0800 Subject: [PATCH 24/33] Implement heapsort solution --- sorting-algorithms/heap.js | 146 +++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 sorting-algorithms/heap.js diff --git a/sorting-algorithms/heap.js b/sorting-algorithms/heap.js new file mode 100644 index 0000000..f710ad7 --- /dev/null +++ b/sorting-algorithms/heap.js @@ -0,0 +1,146 @@ +/* +HEAP SORT + +*** Description + +Heap sort consists of two parts: +1) Build a heap out of the data from the input array +2) Create sorted array by iteratively removing the largest element from the heap and inserting it into the sorted array + +*** Exercise + +Implement heap sort using the Heap constructor provided. + +*** Extra Credit + +Now try building heapsort from scratch, without using the Heap constructor. See if you can do everything in place. +(for hints see https://en.wikipedia.org/wiki/Heapsort#Algorithm) + +Example: +Note that the array is separated into two portions - + heap|sorted +[ 7 2 5 1|8 9 10] +swap first value (max) with element at end of heap +[ 1 2 5 7|8 9 10] +heap size has decreased and sorted portion has now grown +[ 1 2 5|7 8 9 10] +heap portion is re-heapified so that the first value is the new max +[ 5 1 2|7 8 9 10] + heap|sorted +repeat... + +*/ + +var Heap = function() { + this.storage = []; +}; + +Heap.prototype.insert = function(value) { + // Push to storage array + this.storage.push(value); + + var that = this; + + // Recursive function to handle swaps, input index + var reheapify = function(index) { + + // Get parent index + var parentInd = Math.ceil(index/2-1); + // Base Case : value < parent or parent is null + if (parentInd < 0 || that.storage[index] <= that.storage[parentInd]) { + return 'value added to index '+index; + } + // Recursive Case: swap with parent and make recursive call + var temp = that.storage[index]; + that.storage[index] = that.storage[parentInd]; + that.storage[parentInd] = temp; + + return reheapify(parentInd); + }; + return reheapify(that.storage.length-1); +}; + +// Heap remove max method on prototype +// Remove the max value from a heap, reorder the heap, and return the max value +Heap.prototype.removeMax = function() { + // Check if heap is currently empty + if (this.storage.length === 0) { + // If nothing to remove then return null + return null; + } else if (this.storage.length === 1) { + // If heap only has one element in it then pop off the lone element in the storage array and return it + var removed = this.storage.pop(); + + return removed; + } + + // Handle all other cases where heap has more than one node + // Preserve the max value in order to return it + var maxValue = this.storage[0]; + // Replace the root node with the last node of the heap and remove the last node + this.storage[0] = this.storage.pop(); + + // Preserve context for inner recursive helper function + var that = this; + + // Recursive function to restore the heap property of the heap + var reheapify = function(index) { + // Set index of max value to current node's index + var maxIndex = index; + + // Check first child node's value against current node + if ((2*index + 1 < that.storage.length) && (that.storage[2*index + 1] > that.storage[index])) { + // If greater then set index of max value to first child node's index + maxIndex = 2*index + 1; + } + // Check second child node's value against current max node + if ((2*index + 2 < that.storage.length) && (that.storage[2*index + 2] > that.storage[maxIndex])) { + // If greater then set index of max value to second child node's index + maxIndex = 2*index + 2; + } + // If the index of the max value is not equal to the index of the current node + // Then swap the nodes and reheapify at the new index of the current node + if (maxIndex !== index) { + // Swap node values (here's a nifty way to do so "in place" using the XOR bitwise operator) + that.storage[index] = that.storage[index] ^ that.storage[maxIndex]; + that.storage[maxIndex] = that.storage[index] ^ that.storage[maxIndex]; + that.storage[index] = that.storage[index] ^ that.storage[maxIndex]; + + // Reheapify at new index of current node + reheapify(maxIndex); + } + }; + + // Recursively move the swapped node down the heap until it's greater than both of its children + reheapify(0); + + // Return the removed max value from the heap + return maxValue; +}; + +// ******* HEAPSORT IMPLEMENTATION ******* +function heapSort(arr) { + // create a heap + var heap = new Heap(); + // iterate over input array and add values to heap + arr.forEach(heap.insert.bind(heap)); + // remove max value from heap until empty + var result = []; + while (heap.storage.length) { + result.push(heap.removeMax()); + } + // reverse the result array to sort from smallest to largest + return result.reduceRight(function(acc, val) { + acc.push(val); + return acc; + }, []); +} +console.log(heapSort([1,2,5,9,4,1,6,3])); + +/* +Properties: +O(1) extra space if done in place +O(nlog(n)) time +not stable +not adaptive +*/ From b6759817af244846e3fa7337e26543ff5cfcaa58 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 19 Nov 2015 00:08:55 -0800 Subject: [PATCH 25/33] Implement solution --- recursion/flatten.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 recursion/flatten.js diff --git a/recursion/flatten.js b/recursion/flatten.js new file mode 100644 index 0000000..5b6124b --- /dev/null +++ b/recursion/flatten.js @@ -0,0 +1,21 @@ +/* +Implement a function that flattens a nested array. + +flatten([1,[2],[3, [[4]]]]); +=> [1,2,3,4] +*/ + +function flatten(arr) { + var result = []; + arr.forEach(function(element) { + if (!Array.isArray(element)) { + result.push(element); + } else { + result = result.concat(flatten(element)); + } + }); + return result; +} + + +console.log(flatten([1,[2],[3, [[4]]]])); From 947e66a7c58aa6d93d1fadc9483682ef7d4b9fab Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 19 Nov 2015 00:09:20 -0800 Subject: [PATCH 26/33] Implement solution --- recursion/reverse.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 recursion/reverse.js diff --git a/recursion/reverse.js b/recursion/reverse.js new file mode 100644 index 0000000..cb11bb7 --- /dev/null +++ b/recursion/reverse.js @@ -0,0 +1,13 @@ +/* +Implement a function that will reverse a string recursively. + +reverse('abcdefg') +=> 'gfedcba' +*/ + +function reverse(str) { + if (str.length === 0) return ''; + return str[str.length-1] + reverse(str.substr(0,str.length-1)); +} + +console.log(reverse('abcdefg')); From e4ac79235a4cfdb3b62c4cbff5039b25302a811a Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 19 Nov 2015 00:10:00 -0800 Subject: [PATCH 27/33] Implement factorial --- recursion/factorial.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 recursion/factorial.js diff --git a/recursion/factorial.js b/recursion/factorial.js new file mode 100644 index 0000000..7b4f08c --- /dev/null +++ b/recursion/factorial.js @@ -0,0 +1,12 @@ +/* +Implement factorial. + +factorial(5) => 5*4*3*2*1 => 120 +*/ + +function factorial(n) { + if (n === 1) return 1; + return n*factorial(n-1); +} + +console.log(factorial(5)); From 197c638c277b585e92bc4a0bea8a0970f2f34d9a Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 19 Nov 2015 21:43:41 -0800 Subject: [PATCH 28/33] Add table resizing --- data-structures/hashTable.js | 46 ++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/data-structures/hashTable.js b/data-structures/hashTable.js index 61d32b0..4f13f42 100644 --- a/data-structures/hashTable.js +++ b/data-structures/hashTable.js @@ -93,6 +93,21 @@ HashTable.prototype.find = function(key) { }; }; +// O(n) +HashTable.prototype.resize = function(newSize) { + var oldStorage = this._storage; + this._size = newSize; + this._count = 0; + this._storage = []; + var that = this; + oldStorage.forEach(function(bucket) { + bucket.forEach(function(item) { + var key = Object.keys(item)[0]; + that.set(key, item[key]); + }); + }); +}; + // O(1) HashTable.prototype.set = function(key, value) { @@ -108,6 +123,9 @@ HashTable.prototype.set = function(key, value) { newItem[key] = value; this._count++; bucket.push(newItem); + if (this._count > 0.75*this._size) { + this.resize(2*this._size); + } } return this; }; @@ -115,6 +133,7 @@ HashTable.prototype.set = function(key, value) { var myMap = new HashTable(10); console.log(myMap.set('key', 'value'), 'should be HT object'); + // O(1) HashTable.prototype.get = function(key) { var match = this.find(key).match; @@ -140,10 +159,15 @@ console.log(myMap.has('foo'), 'should be false'); // O(1) HashTable.prototype.delete = function(key) { var match = this.find(key).match; - var bucket = this.find(key).bucket; - var matchIndex = this.find(key).matchIndex; - match && bucket.splice(matchIndex, 1); - if (this._count > 0) this._count--; + if (match) { + var bucket = this.find(key).bucket; + var matchIndex = this.find(key).matchIndex; + bucket.splice(matchIndex, 1); + this._count--; + if (this._count < 0.25*this._size) { + this.resize(0.5*this._size); + } + } return !!match; }; @@ -169,8 +193,20 @@ HashTable.prototype.forEach = function(callback) { callback(item); }); }); -} +}; +console.log('count', myMap._count, 'should be 0'); +console.log('size', myMap._size, 'should be 5'); myMap.set('foo', 'bar'); myMap.set('fooAgain', 'barAgain'); +myMap.set('a', 1); +myMap.set('b', 2); myMap.forEach(console.log); +console.log('count', myMap._count, 'should be 4'); +console.log('size', myMap._size, 'should be 10 (doubled)'); +myMap.delete('a'); +console.log('count', myMap._count); +console.log('size', myMap._size); +myMap.delete('b'); +console.log('count', myMap._count); +console.log('size', myMap._size, 'should be 5 (halved)'); From 5868df82daceab89f9603a38da2d010b33acb503 Mon Sep 17 00:00:00 2001 From: Torsten Muller Date: Wed, 25 Nov 2015 08:11:17 -0600 Subject: [PATCH 29/33] Added the memoization of Fibonacci results to the memoization algorithm --- recursion/fibonacci.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/recursion/fibonacci.js b/recursion/fibonacci.js index 23ef40b..4d271f6 100644 --- a/recursion/fibonacci.js +++ b/recursion/fibonacci.js @@ -24,7 +24,10 @@ function fibonnaciDP (n) { 1: 1 }; function recurse(m) { - return memo[m] !== undefined ? memo[m] : recurse(m-1) + recurse(m-2); + if (memo[m] === undefined) { + memo[m] = recurse(m-1) + recurse(m-2); + } + return memo[m]; } return recurse(n); } From 5b2fddeec22cc279b8642180607e4f10ef663a3a Mon Sep 17 00:00:00 2001 From: Lindsay Elia Date: Mon, 13 Jun 2016 17:51:12 -0700 Subject: [PATCH 30/33] changed variable name in enqueue function to correspond to Queue_TwoStacks function variable names --- data-structures/queue.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data-structures/queue.js b/data-structures/queue.js index a3c1586..25573ae 100644 --- a/data-structures/queue.js +++ b/data-structures/queue.js @@ -156,7 +156,7 @@ function Queue_TwoStacks() { } Queue_TwoStacks.prototype.enqueue = function(val) { - this.storageIn.push(val); + this._stackIn.push(val); }; Queue_TwoStacks.prototype._transferStacks = function() { From 02499708145af17e4a22ec66685cb4a98d2e77ee Mon Sep 17 00:00:00 2001 From: Abbie Tuckner Date: Tue, 14 Jun 2016 08:58:28 -0500 Subject: [PATCH 31/33] recursionIntro solutions --- recursion/recursionIntro.js | 73 +++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 recursion/recursionIntro.js diff --git a/recursion/recursionIntro.js b/recursion/recursionIntro.js new file mode 100644 index 0000000..ccfc00f --- /dev/null +++ b/recursion/recursionIntro.js @@ -0,0 +1,73 @@ +//1. Write a function that loops through the numbers n down to 0. If you haven't done so try using a while loop to do this. + +var countDown = function(n) { + while (n > 0) { + console.log(n); + n--; + } +} + +//2. Next, try looping just like above except using recursion + +var recursiveCountDown = function(n) { + while (n > 0) { + console.log(n); + return recursiveCountDown(--n); + } +} + +//3.Write a function 'exponent' that takes two arguments base, and expo, uses a while loop to return the exponenet value of the base. + +var exponent = function(base, expo) { + var val = base; + + while (expo > 1) { + val *= base + expo--; + } + + return val; +} + +//4. Write a function 'RecursiveExponent' that takes two arguments base, and expo, recursively returns exponent value of the base. + +var recursiveExponent = function(base, expo) { + if (expo === 1) { + return base; + } + + return base * recursiveExponent(base, --expo) +} + +//5. Write a function 'recursiveMultiplier' that takes two arguments, 'arr and num', and multiplies each arr value into by num and returns an array of the values. + +var recursiveMultiplier = function(arr, num) { + if(arr.length === 0){ + return arr; + } + + var last = arr.pop(); + + recursiveMultiplier(arr, num); + + arr.push(last * num); + + return arr; +} + +//6. Write a function 'recursiveReverse' that takes an array and uses recursion to return its contents in reverse + +var recursiveReverse = function(arr) { + var reversedArr = []; + var addItems = function(orderedArr) { + if (orderedArr.length > 0) { + reversedArr.push(orderedArr.pop()); + addItems(orderedArr); + } + return; + } + + addItems(arr); + + return reversedArr; +} From 3092c6b2e5f080aefce9043729545091d9baf567 Mon Sep 17 00:00:00 2001 From: Francis Ngo Date: Thu, 19 Jan 2017 13:46:44 -0800 Subject: [PATCH 32/33] fix typo 'Infinity' --- data-structures/queue.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data-structures/queue.js b/data-structures/queue.js index 25573ae..52d9464 100644 --- a/data-structures/queue.js +++ b/data-structures/queue.js @@ -120,7 +120,7 @@ Queue.prototype.until = function(value) { // EXERCISES // Implement a queue using two stacks function Stack(capacity) { - this._capacity = capacity || Infninty; + this._capacity = capacity || Infinity; this._storage = {}; this._count = 0; } From 6416d81e1a0721dbc7fcbdc21a36eb95043a224b Mon Sep 17 00:00:00 2001 From: Li Xinyang Date: Sun, 2 Apr 2017 11:49:22 +0800 Subject: [PATCH 33/33] fix: Handle pop empty stack --- data-structures/stack.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data-structures/stack.js b/data-structures/stack.js index ecfd43d..8c856cd 100644 --- a/data-structures/stack.js +++ b/data-structures/stack.js @@ -67,6 +67,10 @@ Stack.prototype.push = function(value) { // O(1) Stack.prototype.pop = function() { + if (this._count === 0) { + return 'No element inside the stack. Add element before poping.' + } + var value = this._storage[--this._count]; delete this._storage[this._count]; if (this._count < 0) {