├── README.md ├── printMultiplicationTable └── printMultiplicationTable.js ├── identicalBinaryTree └── identicalBinaryTree.js ├── squareRoot └── squareRoot.js ├── largestSumContiguousArray └── largestSumContiguousArray.js ├── binarySearchArray └── binarySearchArray.js ├── allPossibleParens └── allPossibleParens.js ├── subStringPalindrome └── subStringPalindrome.js ├── hexToDec └── hexToDec.js ├── findSiblings └── findSiblings.js ├── findAllAnagrams └── findAllAnagrams.js ├── parens └── parens.js ├── zerosToLeft └── zerosToLeft.js ├── sumArray └── sumArray.js ├── evenOccurence └── evenOccurence.js ├── permsWithoutDups └── persWithoutDups.js ├── coverDistance └── coverDistance.js ├── kthPermutation └── kthPermutation.js ├── appearsTwice └── appearsTwice.js ├── nthFibonacci └── nthFibonacci.js ├── maxSellProfit └── maxSellProfit.js ├── smallestCommonNumber └── smallestCommonNumber.js ├── npm-debug.log ├── powerSet └── powerSet.js ├── twoEggProblem └── twoEggProblem.js ├── highLowIndex └── highLowIndex.js ├── mergeOverlappingTimes └── mergeOverlappingTimes.js ├── fibonacciModified └── fibonacciModified.js ├── sumOfThree └── sumOfThree.js ├── isSubsetOf └── isSubsetOf.js ├── reverseWords └── reverseWords.js ├── rotatedArraySearch └── rotatedArraySearch.js ├── closestMeetingPoint └── closestMeetingPoint.js ├── magicIndex └── magicIndex.js ├── peaksAndValleys └── peaksAndValleys.js ├── queueStack └── queueStack.js ├── sortedSearch └── sortedSearch.js ├── longestRun └── longestRun.js ├── printTreePerimeter └── printTreePerimeter.js ├── maxSumSubsequence └── maxSumSubsequence.js ├── validBinaryTree └── validBinaryTree.js ├── linkedListCycles └── linkedListCycles.js ├── characterFrequency └── characterFrequency.js ├── findADuplicate └── findADuplicate.js ├── coinChange └── coinChange.js ├── allAnagrams └── allAnagrams.js ├── primeTester └── primeTester.js ├── recursiveMultiply └── recursiveMultiply.js ├── spiralTraversal └── spiralTraversal.js ├── secondLargestItemBinaryTree └── secondLargestItemBinaryTree.js ├── deepEquality └── deepEquality.js ├── asyncMap └── asyncMap.js ├── composePipe └── composePipe.js ├── linkedList └── linkedList.js ├── commonCharacters └── commonCharacters.js ├── reverseWordsInSentence └── reverseWordsInSentence.js ├── fractionConverter └── fractionConverter.js ├── coinSums └── coinSums.js ├── depthFirstSearch └── depthFirstSearch.js ├── telephoneWords └── telephoneWords.js ├── gameScoring └── gameScoring.js ├── subSort └── subSort.js ├── rotateMatrix └── rotateMatrix.js ├── breadthFirstSearch └── breadthFirstSearch.js ├── patternMatching └── patternMatching.js ├── awardBudgetCap └── awardBudgetCap.js ├── rectangleLove └── rectangleLove.js ├── pathsWithSum └── pathsWithSum.js ├── boggle └── boggle.js ├── quickSort └── quickSort.js ├── stackOfBoxes └── stackOfBoxes.js ├── levenshteinDistance └── levenshteinDistance.js ├── textJustification └── textJustification.js ├── treeBFSelect └── treeBFSelect.js └── mergeSort └── mergeSort.js /README.md: -------------------------------------------------------------------------------- 1 | # toy-problems 2 | -------------------------------------------------------------------------------- /printMultiplicationTable/printMultiplicationTable.js: -------------------------------------------------------------------------------- 1 | // Print out the grade-school multiplication table up to nxn 2 | 3 | function multiplicationTable(n) { 4 | var result = []; 5 | for (var i = 0; i < n; i++) { 6 | result.push([]); 7 | for (var j = 0; j < n; j++) { 8 | result[i][j] = (i+1) * (j+1); 9 | } 10 | } 11 | return result; 12 | } 13 | -------------------------------------------------------------------------------- /identicalBinaryTree/identicalBinaryTree.js: -------------------------------------------------------------------------------- 1 | /* 2 | Write a function that returns a boolean telling us whether two 3 | binary trees are identical. 4 | */ 5 | 6 | function areIdentical(treeA, treeB) { 7 | if (treeA.root === null && treeB === null) { 8 | return true; 9 | } 10 | 11 | if (treeA && treeB) { 12 | return (treeA.data === treeB.data && areIdentical(treeA.left, treeB.left) && areIdentical(treeA.right && treeA.right)); 13 | } 14 | 15 | return false; 16 | } 17 | -------------------------------------------------------------------------------- /squareRoot/squareRoot.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a double number, write a function to calculate its square root. 3 | */ 4 | 5 | var epsilon = 0.00001; 6 | 7 | function findSqaureRoot(num) { 8 | var low = 0.0; 9 | var high = num / 2.0 + 1.0; 10 | while (low < high) { 11 | var mid = (low + high) / 2; 12 | var sqr = mid * mid; 13 | var diff = Math.abs(num - sqr); 14 | if (diff <= epsilon) { 15 | return mid; 16 | } 17 | 18 | if (sqr > num) { 19 | high = mid; 20 | } else { 21 | low = mid; 22 | } 23 | } 24 | return -1; 25 | } 26 | -------------------------------------------------------------------------------- /largestSumContiguousArray/largestSumContiguousArray.js: -------------------------------------------------------------------------------- 1 | /* 2 | Write an efficient program to find the sum of contiguous subarray 3 | within a one-dimensional array of numbers which has the largest sum. 4 | 5 | */ 6 | 7 | function largestContiguousSum(array) { 8 | var maxSoFar = 0; 9 | var maxEndingHere = 0; 10 | for (var i = 0; i < array.length; i++) { 11 | maxEndingHere = maxEndingHere + array[i]; 12 | if (maxEndingHere < 0) { 13 | maxEndingHere = 0; 14 | } 15 | 16 | if (maxEndingHere > maxSoFar) { 17 | maxSoFar = maxEndingHere; 18 | } 19 | } 20 | return maxSoFar; 21 | } 22 | -------------------------------------------------------------------------------- /binarySearchArray/binarySearchArray.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Given a sorted array, find the index of an element 3 | * using a binary search algorithm. 4 | * 5 | * Example usage: 6 | * 7 | * var index = binarySearch([1, 2, 3, 4, 5], 4); 8 | * console.log(index); // 3 9 | */ 10 | 11 | function binarySearch(array, element) { 12 | function innerRecurse(low, high) { 13 | var mid = Math.floor((low + high) * 0.5); 14 | if (element === array[mid]) { 15 | return element; 16 | } 17 | if (element < array[mid]) { 18 | return innerRecurse(low, mid); 19 | } else { 20 | return innerRecurse(mid, high); 21 | } 22 | } 23 | 24 | innerRecurse(0, array.length - 1); 25 | } 26 | -------------------------------------------------------------------------------- /allPossibleParens/allPossibleParens.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Print all parentheses combinations for a given value n such that 4 | they are balanced. 5 | */ 6 | 7 | function printAllParens(n) { 8 | var output = []; 9 | printAllParensRecurse(n, 0, 0, output); 10 | 11 | return output; 12 | } 13 | 14 | function printAllParensRecurse(n, right, left, output) { 15 | if (right >= n && left >= n) { 16 | console.log(output); 17 | } 18 | 19 | if (left < n) { 20 | output.push('{'); 21 | printAllParensRecurse(n, right, left + 1, output); 22 | output.pop(); 23 | } 24 | 25 | if (right < left) { 26 | output.push('}'); 27 | printAllParensRecurse(n, right + 1, left, output); 28 | output.pop(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /subStringPalindrome/subStringPalindrome.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a string find all substrings that are palindromes. 3 | aabbbaa 4 | */ 5 | 6 | function subStringPalindrome(string) { 7 | var count = 0; 8 | 9 | function innerRecurse(string, j, k) { 10 | var countInner = 0; 11 | while (j >= 0 && k < string.length) { 12 | if (input[j] !== input[k]) { 13 | break; 14 | } 15 | console.log(input.substr(j, k + 1)); 16 | countInner++; 17 | j--; 18 | k++; 19 | } 20 | return countInner; 21 | } 22 | 23 | for (var i = 0; i < string.length; i++) { 24 | count += innerRecurse(string, i - 1, i + 1); 25 | count += innerRecurse(string, i, i + 1); 26 | } 27 | 28 | return count; 29 | } 30 | -------------------------------------------------------------------------------- /hexToDec/hexToDec.js: -------------------------------------------------------------------------------- 1 | // Convert HEX to DECIMAL (Base 16 to Base 10) 2 | 3 | var hexToDecObject= { 4 | 0: 0, 5 | 1: 1, 6 | 2: 2, 7 | 3: 3, 8 | 4: 4, 9 | 5: 5, 10 | 6: 6, 11 | 7: 7, 12 | 8: 8, 13 | 9: 9, 14 | 'A': 10, 15 | 'B': 11, 16 | 'C': 12, 17 | 'D': 13, 18 | 'E': 14, 19 | 'F': 15 20 | }; 21 | 22 | function hexToDec(hex) { 23 | var hexString = hex.split(''); 24 | var result = 0; 25 | var length = hexString.length; 26 | for (var i = length - 1; i >= 0; i--) { 27 | if (typeof hexString[i] === 'string') { 28 | hexString[i] = hexString[i].toUpperCase(); 29 | } 30 | current = hexString[i]; 31 | result += hexToDecObject[current] * Math.pow(16, length - i - 1); 32 | } 33 | return result; 34 | } 35 | -------------------------------------------------------------------------------- /findSiblings/findSiblings.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root to a binary tree where each node has an additional pointer called sibling (or next), connect the sibling pointer to next node in the same level. Last node in each level should point to the first node of next level in the tree. 3 | */ 4 | 5 | function findSiblings(root) { 6 | if (!root || !root.right || !root.left) { 7 | return; 8 | } 9 | 10 | var current = root; 11 | var last = root; 12 | while (current) { 13 | if (current.left) { 14 | last.next = current.left; 15 | last = current.left; 16 | } 17 | 18 | if (current.right) { 19 | last.next = current.right; 20 | last = current.right; 21 | } 22 | last.next = null; 23 | current = current.next; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /findAllAnagrams/findAllAnagrams.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an array of strings, find and groups all the anagrams 3 | 4 | */ 5 | 6 | function findAllAnagrams(array) { 7 | var sortedArray = []; 8 | var hashMap = {}; 9 | var results = []; 10 | for (var i = 0; i < array.length; i++) { 11 | sortedArray.push(array[i].toLowerCase().split('').sort().join('')); 12 | } 13 | 14 | for (var j = 0; j < sortedArray.length; j++) { 15 | if (hashMap[sortedArray[j]]) { 16 | hashMap[sortedArray[j]].push(array[j]); 17 | } else { 18 | hashMap[sortedArray[j]] = [array[j]]; 19 | } 20 | } 21 | 22 | for (var key in hashMap) { 23 | if (hashMap[key].length > 1) { 24 | results.push(hashMap[key]); 25 | } 26 | } 27 | 28 | return results; 29 | } 30 | -------------------------------------------------------------------------------- /parens/parens.js: -------------------------------------------------------------------------------- 1 | /* 2 | Write an algorithm to print all valid combinations of n pairs of parentheses 3 | */ 4 | 5 | // APPROACH 1 6 | function printAllParens(n) { 7 | var set = new Set(); 8 | if (n === 0) { 9 | set.add(''); 10 | } else { 11 | var previous = printAllParens(n - 1); 12 | for (let item of previous) { 13 | for (var i = 0; i < item.length; i++) { 14 | if (item.charAt(i) === '(') { 15 | var s = insertInside(item, i); 16 | set.add(a); 17 | } 18 | } 19 | set.add('()' + item); 20 | } 21 | } 22 | return set; 23 | } 24 | 25 | function insertInside(str, i) { 26 | var left = str.substr(0, i + 1); 27 | var right = str.substr(i + 1, str.length); 28 | return left + '()' + right; 29 | } 30 | -------------------------------------------------------------------------------- /zerosToLeft/zerosToLeft.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an integer array, move all elements containing '0' to the left while maintaining the order of other elements in the array. 3 | */ 4 | 5 | function zerosToLeft(array) { 6 | if (array.length < 1) { 7 | throw new Error('Please enter a valid array'); 8 | } 9 | 10 | var readIndex = array.length - 1; 11 | var writeIndex = array.length - 1; 12 | var currentItemToWrite = array[readIndex]; 13 | while (readIndex >= 0) { 14 | if (array[readIndex] !== 0) { 15 | currentItemToWrite = array[readIndex]; 16 | array[writeIndex] = currentItemToWrite; 17 | writeIndex--; 18 | } 19 | readIndex--; 20 | } 21 | 22 | while(writeIndex >= 0) { 23 | array[writeIndex] = 0; 24 | writeIndex--; 25 | } 26 | 27 | return array; 28 | } 29 | -------------------------------------------------------------------------------- /sumArray/sumArray.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Given an array of numbers, calculate the greatest contiguous sum of numbers in it. 3 | * A single array item will count as a contiguous sum. 4 | * 5 | * example 1: sumArray([1, 2, 3]); // => 6 6 | * example 2: sumArray([1, 2, 3, -4]); // 6 7 | * example 3: sumArray([1, 2, 3, -4, 5]); // 7 8 | * example 4: sumArray([4, -1, 5]); // => 8 9 | * example 5: sumArray([10, -11, 11]); // 11 10 | */ 11 | 12 | function sumArray(array) { 13 | var currentSum = 0; 14 | var allTimeSum = 0; 15 | var length = array.length; 16 | for (var i = 0; i < length; i++) { 17 | currentSum += array[i]; 18 | if (currentSum > allTimeSum) { 19 | allTimeSum = currentSum; 20 | } 21 | if (currentSum < 0) { 22 | currentSum = 0; 23 | } 24 | } 25 | return allTimeSum; 26 | } 27 | -------------------------------------------------------------------------------- /evenOccurence/evenOccurence.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Find the first item that occurs an even number of times in an array. 3 | * Remember to handle multiple even-occurrence items and return the first one. 4 | * Return null if there are no even-occurrence items. 5 | */ 6 | 7 | /* 8 | * example usage: 9 | * var onlyEven = evenOccurrence([1, 7, 2, 4, 5, 6, 8, 9, 6, 4]); 10 | * console.log(onlyEven); // 4 11 | */ 12 | 13 | function evenOccurence(array) { 14 | var index; 15 | var hash = {}; 16 | var length = array.length; 17 | var current; 18 | for (var i = 0; i < length; i++) { 19 | current = array[i]; 20 | hash[current] = hash[current] + 1 || 1; 21 | } 22 | for (var i = 0; i < length; i++) { 23 | if (hash[array[i]] % 2 === 0) { 24 | return array[i] 25 | } 26 | } 27 | return null; 28 | } 29 | -------------------------------------------------------------------------------- /permsWithoutDups/persWithoutDups.js: -------------------------------------------------------------------------------- 1 | /* 2 | Write a method to compute all permutation of a string of unique characters 3 | */ 4 | 5 | function findPermsWithoutDups(string) { 6 | var perms = []; 7 | 8 | if (string === null) { 9 | return null; 10 | } 11 | 12 | if (string.length === 1) { 13 | perms.push(string); 14 | return perms; 15 | } 16 | 17 | var firstChar = string.substr(0, 1); 18 | var remainder = string.substr(1); 19 | var words = findPermsWithoutDups(remainder); 20 | for (var i = 0; i < words.length; i++) { 21 | for (var j = 0; j <= words[i].length; j++) { 22 | var s = insertCharAt(words[i], firstChar, j); 23 | perms.push(s); 24 | } 25 | } 26 | return perms; 27 | } 28 | 29 | function insertCharAt(word, char, index) { 30 | return word.substr(0, index) + char + word.substr(index); 31 | } 32 | -------------------------------------------------------------------------------- /coverDistance/coverDistance.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a distance ‘dist, count total number of ways to cover the distance 3 | with 1, 2 and 3 steps. 4 | */ 5 | 6 | // METHOD 1: 7 | function coverDistance(d) { 8 | if (d === 0) { 9 | return 1; 10 | } 11 | 12 | if (d < 0) { 13 | return 0; 14 | } 15 | 16 | return coverDistance(d - 1) + coverDistance(d - 2) + coverDistance(d - 3); 17 | } 18 | 19 | // METHOD 2: Reduce space complexity significantly 20 | function coverDistance2(d) { 21 | var count = []; 22 | count[0] = 1; 23 | count[1] = 1; 24 | count[2] = 2; 25 | if (d < 3) { 26 | return count[d - 1]; 27 | } 28 | d -= 2; 29 | while(d) { 30 | var temp = count[2]; 31 | count[2] = count[0] + count[1] + count[2]; 32 | count[0] = count[1]; 33 | count[1] = temp; 34 | d--; 35 | } 36 | 37 | return count[2]; 38 | } 39 | -------------------------------------------------------------------------------- /kthPermutation/kthPermutation.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a set of n elements find their kth permutation. Consider the following set of elements: 3 | NUMBER: 4 | 1 5 | 2 6 | 3 7 | All permutations of the above elements are (with ordering): 8 | 9 | 1st - 123 10 | 2nd - 132 11 | 3rd - 213 12 | 4th - 231 13 | 5th - 312 14 | 6th - 321 15 | 16 | */ 17 | function factorial(n) { 18 | if (n === 0 || n === 1) { 19 | return 1; 20 | } 21 | return n * factorial(n - 1); 22 | } 23 | 24 | function findKthPermutation(v, k, result) { 25 | if (!v || v.length === 0) { 26 | return +result; 27 | } 28 | 29 | var n = v.length; 30 | var count = factorial(n - 1); 31 | var selected = Math.floor((k - 1) / count); 32 | result += '' + v[selected]; 33 | v.splice(selected, 1); 34 | k = k - (count * selected); 35 | 36 | return findKthPermutation(v, k, result); 37 | } 38 | -------------------------------------------------------------------------------- /appearsTwice/appearsTwice.js: -------------------------------------------------------------------------------- 1 | /* 2 | I have a list where every number in the range 1....n appears 3 | once except for one number which appears twice. 4 | Write a function for finding the number that appears twice. 5 | */ 6 | // METHOD 1: runtime = O(n) & memory = O(n) 7 | function findDup(list, n) { 8 | var obj = {}; 9 | var curElement; 10 | for (var i = 0; i < list.length; i++) { 11 | curElement = list[i]; 12 | if (obj[curElement]) { 13 | return curElement; 14 | } else { 15 | obj[curElement] = true; 16 | } 17 | } 18 | } 19 | 20 | // METHOD 2: runtime = O(n) & memory = O(1) 21 | // Using the triangular series theorem 22 | function findDup2(list, n) { 23 | var triangularSum = ((n * n) + n) * 0.5; 24 | var listSum = 0; 25 | for (var i = 0; i < list.length; i++) { 26 | listSum += list[i]; 27 | } 28 | return listSum - triangularSum; 29 | } 30 | -------------------------------------------------------------------------------- /nthFibonacci/nthFibonacci.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A Fibonacci sequence is a list of numbers that begins with 0 and 1, and each 3 | * subsequent number is the sum of the previous two. 4 | * 5 | * For example, the first five Fibonacci numbers are: 6 | * 7 | * 0 1 1 2 3 8 | * 9 | * If n were 4, your function should return 3; for 5, it should return 5. 10 | * 11 | * Write a function that accepts a number, n, and returns the nth Fibonacci 12 | * number. Use a recursive solution to this problem; if you finish with time 13 | * left over, implement an iterative solution. 14 | * 15 | * example usage: 16 | * nthFibonacci(2); // => 1 17 | * nthFibonacci(3); // => 2 18 | * nthFibonacci(4); // => 3 19 | * etc... 20 | * 21 | */ 22 | 23 | function nthFibonacci(n) { 24 | var fibs = [0, 1]; 25 | for (; n > 1; n--) { 26 | fibs.push(fibs.shift() + fibs[0]); 27 | } 28 | return fibs[n]; 29 | } 30 | -------------------------------------------------------------------------------- /maxSellProfit/maxSellProfit.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a list of stock prices for n days, find the maximum profit with a single buy/sell activity. 3 | 4 | We need to maximize the single buy/sell profit and in case we can't make any profit, we'll try to minimize the loss. 5 | */ 6 | 7 | function maxProfit(array) { 8 | if (array.length < 2 || !array) { 9 | throw new Error('Please enter a valid array'); 10 | } 11 | 12 | var currentBuy = array[0]; 13 | var globalSell = array[1]; 14 | var globalProfit = globalSell - currentBuy; 15 | var currentProfit = 0; 16 | for (var i = 1; i < array.length; i++) { 17 | var currentProfit = array[i] - currentBuy; 18 | if (currentProfit > globalProfit) { 19 | globalSell = array[i]; 20 | globalProfit = currentProfit; 21 | } 22 | 23 | if (currentBuy > array[i]) { 24 | currentBuy = array[i]; 25 | } 26 | } 27 | return globalProfit; 28 | } 29 | -------------------------------------------------------------------------------- /smallestCommonNumber/smallestCommonNumber.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given three integer arrays sorted in ascending order, find the smallest 3 | number that is common in all three arrays. For example, let's look at the 4 | below three arrays. Here 6 is the smallest number that's common in all the 5 | arrays. 6 | */ 7 | function smallestCommonNumber(a, b, c) { 8 | var index1 = 0; 9 | var index2 = 0; 10 | var index3 = 0; 11 | var minValue = -1; 12 | 13 | while (index1 < a.length && index2 < b.length && index3 < c.length) { 14 | if (a[index1] === b[index2] && b[index2] === c[index3]) { 15 | minValue = a[index1]; 16 | break; 17 | } 18 | 19 | if (a[index1] < b[index2] && a[index1] < c[index3]) { 20 | index1++; 21 | } else if (b[index2] < a[index1] && b[index2] < c[index3]) { 22 | index2++; 23 | } else { 24 | index3++; 25 | } 26 | } 27 | return minValue; 28 | } 29 | -------------------------------------------------------------------------------- /npm-debug.log: -------------------------------------------------------------------------------- 1 | 0 info it worked if it ends with ok 2 | 1 verbose cli [ '/usr/local/bin/node', '/usr/local/bin/npm', 'run', 'start-p' ] 3 | 2 info using npm@2.14.20 4 | 3 info using node@v4.4.1 5 | 4 verbose stack Error: ENOENT: no such file or directory, open '/Users/preethi/Desktop/preethi-code/toy-problems/package.json' 6 | 4 verbose stack at Error (native) 7 | 5 verbose cwd /Users/preethi/Desktop/preethi-code/toy-problems 8 | 6 error Darwin 14.3.0 9 | 7 error argv "/usr/local/bin/node" "/usr/local/bin/npm" "run" "start-p" 10 | 8 error node v4.4.1 11 | 9 error npm v2.14.20 12 | 10 error path /Users/preethi/Desktop/preethi-code/toy-problems/package.json 13 | 11 error code ENOENT 14 | 12 error errno -2 15 | 13 error syscall open 16 | 14 error enoent ENOENT: no such file or directory, open '/Users/preethi/Desktop/preethi-code/toy-problems/package.json' 17 | 14 error enoent This is most likely not a problem with npm itself 18 | 14 error enoent and is related to npm not being able to find a file. 19 | 15 verbose exit [ -2, true ] 20 | -------------------------------------------------------------------------------- /powerSet/powerSet.js: -------------------------------------------------------------------------------- 1 | /* 2 | write a method to return all subsets of a set 3 | */ 4 | 5 | function getSubsets(set) { 6 | var allSubsets, index; 7 | 8 | function getSubs(set, index, currentSubsets) { 9 | index = index || 0; 10 | 11 | if (index === set.length) { 12 | allSubsets = currentSubsets; 13 | return; 14 | } 15 | 16 | if (index > set.length) { 17 | return; 18 | } 19 | 20 | currentSubsets = currentSubsets || []; 21 | if (index === 0) { 22 | currentSubsets.push([set[index]]); 23 | getSubs(set, index + 1, currentSubsets); 24 | } else { 25 | item = set[index]; 26 | copy = currentSubsets.slice(); 27 | for (var i = 0; i < copy.length; i++) { 28 | moreSubsets = []; 29 | moreSubsets = moreSubsets.concat(copy[i].concat(item)); 30 | currentSubsets = currentSubsets.concat([moreSubsets]); 31 | } 32 | getSubs(set, index + 1, currentSubsets); 33 | } 34 | } 35 | 36 | getSubs(set); 37 | 38 | return allSubsets; 39 | } 40 | -------------------------------------------------------------------------------- /twoEggProblem/twoEggProblem.js: -------------------------------------------------------------------------------- 1 | /* 2 | A building has 100 floors. One of the floors is the highest floor an egg 3 | can be dropped from without breaking. 4 | If an egg is dropped from above that floor, it will break. If it is 5 | dropped from that floor or below, it will be completely undamaged and 6 | you can drop the egg again. 7 | 8 | Given two eggs, find the highest floor an egg can be dropped from without breaking, with as few drops as possible. 9 | */ 10 | /* 11 | SOLUTION 1: 12 | n + (n−1) + (n−2) + … + 1 = 100 13 | n * (n + 1)/2 = 100; 14 | n = ~14 15 | 16 | So we drop the first egg at the 14th floor, then 13th floor, then 12th floor and so on. When the first egg breaks, we know we have to go 14 - x (the number of drops we made) to find the floor it breaks at 17 | 18 | SOLUTION 2: 19 | We drop the egg at every 10th floor. Once it breaks, you go up by 2 until the second one breaks. So if it breaks at 90, we go back to 80. Then drop at 82, 84, 86, 88. Whichever once it breaks at, we know that the max floor is the floor right below 20 | */ 21 | -------------------------------------------------------------------------------- /highLowIndex/highLowIndex.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a sorted array of integers, return the low and high index of the given key. Return -1 if not found. The array length can be in millions with lots of duplicates. 3 | */ 4 | 5 | function findLowIndex(array, key) { 6 | var low = 0; 7 | var high = array.length - 1; 8 | var midPoint = Math.floor((high + low) * 0.5); 9 | while (low <= high) { 10 | if (array[midPoint] >= key) { 11 | high = midPoint - 1; 12 | } else { 13 | low = midPoint + 1; 14 | } 15 | midPoint = Math.floor((high + low) * 0.5); 16 | } 17 | if (array[low] === key) { 18 | return low; 19 | } else { 20 | return -1; 21 | } 22 | } 23 | 24 | function findHighIndex(array, key) { 25 | var low = 0; 26 | var high = array.length - 1; 27 | var midPoint = Math.floor((high + low) * 0.5); 28 | while (low <= high) { 29 | if (array[midPoint] <= key) { 30 | low = midPoint + 1; 31 | } else { 32 | high = midPoint - 1; 33 | } 34 | midPoint = Math.floor((high + low) * 0.5); 35 | } 36 | if (array[high] === key) { 37 | return high; 38 | } else { 39 | return -1; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /mergeOverlappingTimes/mergeOverlappingTimes.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an array (list) of intervals as input where each interval has a 3 | start and end timestamps. Input array is sorted by starting timestamps. 4 | You are required to merge overlapping intervals and return output array (list). 5 | 6 | Consider below input array. Intervals (1, 5), (3, 7), (4, 6), (6, 8) 7 | are overlapping so should be merged to one big interval (1, 8). Similarly, intervals (10, 12) and (11, 15) are also overlapping intervals and should 8 | be merged to (10, 15). 9 | */ 10 | 11 | function mergeIntervals(arrays) { 12 | var finalMergedTimes = []; 13 | var currentStart = arrays[0][0]; 14 | var currentEnd = arrays[0][1]; 15 | for (var i = 1; i < arrays.length; i++) { 16 | if (arrays[i][0] <= currentEnd) { 17 | currentEnd = Math.max(arrays[i][1], currentEnd); 18 | } else { 19 | finalMergedTimes.push([currentStart, currentEnd]); 20 | currentStart = arrays[i][0]; 21 | currentEnd = Math.max(arrays[i][1], currentEnd); 22 | } 23 | } 24 | 25 | finalMergedTimes.push([currentStart, currentEnd]); 26 | return finalMergedTimes; 27 | } 28 | -------------------------------------------------------------------------------- /fibonacciModified/fibonacciModified.js: -------------------------------------------------------------------------------- 1 | /* 2 | Implement an algorithm to get the nth i-Fibonacci number. 3 | In other words, if i is 5, then the sequence is: 4 | [0, 0, 0, 0, 1, 1, 2, 4, 8, 16, 31]... 5 | */ 6 | // METHOD 1 7 | function iFibonacci(n, i) { 8 | var solution = []; 9 | for (var p = 0; p < i; p++) { 10 | solution.push(0); 11 | } 12 | solution[i - 1] = 1; 13 | var curSum = 0; 14 | for (var j = 1; j <= n; j++) { 15 | for (var k = 0; k < i; k++) { 16 | curSum += solution[k]; 17 | } 18 | for (var m = 0; m < i - 1; m++) { 19 | solution[m] = solution[m + 1]; 20 | } 21 | solution[i - 1] = curSum; 22 | curSum = 0; 23 | } 24 | return solution[i - 1]; 25 | } 26 | 27 | // METHOD 2 28 | function iFibonacci2(n, i) { 29 | var solution = []; 30 | for (var p = 0; p <= i; p++) { 31 | solution.push(0); 32 | } 33 | solution[i] = 1; 34 | var curSum = 1; 35 | var previousSum = 0; 36 | for (var j = 1; j <= n; j++) { 37 | curSum = curSum + previousSum - solution[0]; 38 | previousSum = curSum; 39 | solution.push(curSum); 40 | solution.splice(0, 1); 41 | } 42 | return solution[i]; 43 | } 44 | -------------------------------------------------------------------------------- /sumOfThree/sumOfThree.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an array of integers and a value, determine if there are any three integers in the array that sum equal to the given value. 3 | 4 | target = 20; 5 | array = [1, 5, 2, 9, 24, 14, 6, 9, 4, 10]; 6 | sorted = [1, 2, 4, 5, 6, 9, 9, 10, 14, 24] 7 | 11, 12, 13, 14, 14, 14, 15, 15, 16, 19, 19, 18, 19, 20, 23, 23, 24, 26, 28, 29, 30, 33, 33, 38 8 | */ 9 | 10 | // BRUTE FORCE = 3 nested loops. O(n^3) 11 | // More optimized - use Sum Of two to find sum of three 12 | function sumOfThree(array, target) { 13 | var sortedArray = array.sort(function(a, b) { 14 | return a - b; 15 | }); 16 | 17 | for (var i = 0; i < array.length; i++) { 18 | if (sumOfTwo(array, target - array[i], i + 1)) { 19 | return true; 20 | } 21 | } 22 | return false; 23 | } 24 | 25 | function sumOfTwo(array, target, startIndex) { 26 | 27 | for (var i = startIndex, j = array.length - 1; i < j) { 28 | var currentSum = array[i] + array[j]; 29 | if (currentSum === target) { 30 | return true; 31 | } 32 | 33 | if (currentSum < target) { 34 | i++; 35 | } else { 36 | j--; 37 | } 38 | } 39 | return false; 40 | } 41 | -------------------------------------------------------------------------------- /isSubsetOf/isSubsetOf.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Make an array method that can return whether or not a context array is a 3 | * subset of an input array. To simplify the problem, you can assume that both 4 | * arrays will contain only strings. 5 | * 6 | * 7 | * var a = ['commit','push'] 8 | * a.isSubsetOf(['commit','rebase','push','blame']) // true 9 | * 10 | * NOTE: You should disregard duplicates in the set. 11 | * 12 | * var b = ['merge','reset','reset'] 13 | * 14 | * b.isSubsetOf(['reset','merge','add','commit']) // true 15 | * 16 | * See http://en.wikipedia.org/wiki/Subset for more on the definition of a 17 | * subset. 18 | */ 19 | 20 | /* 21 | * Extra credit: Make the method work for arrays that contain any value, 22 | * including non-strings. 23 | */ 24 | 25 | Array.prototype.isSubsetOf = function(array) { 26 | function arrayContains(array, item) { 27 | for (var i = 0; i < array.length; i++) { 28 | if (array[i] === item) { 29 | return true; 30 | } 31 | } 32 | return false; 33 | } 34 | for (var i = 0; i < this.length; i++) { 35 | if (!arrayContains(array, this[i])) { 36 | return false; 37 | } 38 | } 39 | return true; 40 | } 41 | -------------------------------------------------------------------------------- /reverseWords/reverseWords.js: -------------------------------------------------------------------------------- 1 | /* 2 | You're working on a secret team solving coded transmissions. 3 | Your team is scrambling to decipher a recent message, worried it's a plot 4 | to break into a major European National Cake Vault. The message has been 5 | mostly deciphered, but all the words are backwards! Your colleagues have 6 | handed off the last step to you. 7 | 8 | Write a function reverseWords() that takes a string message and reverses 9 | the order of the words in-place. 10 | 11 | Since strings in JavaScript are immutable, we'll first convert the string 12 | into an array of characters, do the in-place word reversal on that array, 13 | and re-join that array into a string before returning it. 14 | But keep in mind that this isn't technically "in-place," and the array of 15 | characters will cost O(n) additional space! 16 | */ 17 | 18 | function reverseWords(string) { 19 | var stringArray = string.split(' '); 20 | for (var i = 0; i < Math.floor(stringArray.length / 1); i++) { 21 | var temp = stringArray[i]; 22 | stringArray[i] = stringArray[stringArray.length - 1 - i]; 23 | stringArray[stringArray.length - 1 - i] = temp; 24 | } 25 | return stringArray.join(' '); 26 | } 27 | -------------------------------------------------------------------------------- /rotatedArraySearch/rotatedArraySearch.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Given a sorted array that has been rotated some number of items right or 3 | * left, i.e. [0, 1, 2, 3, 4, 5, 6, 7] might become [4, 5, 6, 7, 0, 1, 2, 3] 4 | * how can you efficiently find the rotation point? For simplicity, you can assume 5 | * that there are no duplicate elements in the array. 6 | * 7 | * Target time complexity: O(log(array.length)) 8 | */ 9 | 10 | function rotatedArraySearch(array) { 11 | var rotationPoint; 12 | var midPoint = Math.floor(array.length * 0.5); 13 | var start, end; 14 | 15 | function findRotationRecursively(array, midPoint, start, end) { 16 | if (array[midPoint] > array[midPoint + 1] && array[midPoint] > array[midPoint -1]) { 17 | rotationPoint = midPoint; 18 | return; 19 | } else if (array[midPoint] < array[midPoint + 1] && array[midPoint] > array[midPoint - 1]) { 20 | findRotationRecursively(array, Math.floor((end + midPoint) * 0.5), midPoint, end); 21 | } else { 22 | findRotationRecursively(array, Math.floor(midPoint * 0.5), start, midPoint); 23 | } 24 | } 25 | 26 | findRotationRecursively(array, midPoint, 0, array.length -1); 27 | return rotationPoint; 28 | } 29 | -------------------------------------------------------------------------------- /closestMeetingPoint/closestMeetingPoint.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given N people on MxM grid, find the point that requires the least total distance covered by all the people to meet at that point. 3 | */ 4 | 5 | class Point { 6 | constructor(x, y) { 7 | this.x = x; 8 | this.y = y; 9 | } 10 | 11 | calculateDistance(point) { 12 | return Math.sqrt((point.x - this.x) * (point.x - this.x) + (point.y - this.y) * (point.y - this.y)) 13 | } 14 | 15 | calculateSumOfDistances(points) { 16 | var total = 0; 17 | for (var i = 0; i < points.length; i++) { 18 | total += this.calculateDistance(points[i]); 19 | } 20 | return total; 21 | } 22 | } 23 | 24 | function shortesDistance(m, points) { 25 | var point = new Point(1, 1); 26 | var minDistance = point.calculateSumOfDistances(points); 27 | var currentPoint, currentDistance, finalPoint; 28 | for (var i = 0; i < m; i++) { 29 | for (var j = 0; j < m; j++) { 30 | currentPoint = new Point(i, j); 31 | currentDistance = currentPoint.calculateSumOfDistances(points); 32 | if (currentDistance < minDistance) { 33 | finalPoint = currentPoint; 34 | minDistance = currentDistance; 35 | } 36 | } 37 | } 38 | return finalPoint; 39 | } 40 | -------------------------------------------------------------------------------- /magicIndex/magicIndex.js: -------------------------------------------------------------------------------- 1 | /* 2 | A magic index in an array A[1, 2, ... n-1] is defined to be an index such that A[i] = i. Given a sorted array of distinct integers, write a method to find a magic index, if one exists, in array A. Assume the values are NOT distint 3 | */ 4 | 5 | // APPROACH #1: BRUTE FORCE 6 | function findMagicIndex(array) { 7 | for (var i = 0; i < array.length; i++) { 8 | if (array[i] === i) { 9 | return index; 10 | } 11 | } 12 | return -1; 13 | } 14 | 15 | // APPROACH #2: SINCE IT'S SORTED, WE SHOULD TAKE ADVANTAGE OF THIS 16 | function findMagicIndex(array) { 17 | 18 | function findMagic(array, start, end) { 19 | if (end < start) { 20 | return -1; 21 | } 22 | 23 | var midIndex = Math.floor((start + end) / 2); 24 | if (array[midIndex] === midIndex) { 25 | return midIndex; 26 | } 27 | 28 | var leftIndex = Math.min(midIndex - 1, array[midIndex]); 29 | var left = findMagic(array, start, leftIndex); 30 | if (left > 0) { 31 | return left; 32 | } 33 | 34 | var rightIndex = Math.max(midIndex + 1, midValue); 35 | var right = findMagic(array, rightIndex, end); 36 | return right; 37 | } 38 | 39 | return findMagic(array, 0, array.length - 1); 40 | } 41 | -------------------------------------------------------------------------------- /peaksAndValleys/peaksAndValleys.js: -------------------------------------------------------------------------------- 1 | /* 2 | In an array of integers, a "peak" is an element which is greater than 3 | or equal to adjacent integers and a "valley" is an elemnent which is 4 | less than or equal to the adjacent integers. For example, in the 5 | array [5, 8, 6, 2, 3, 4, 6], 8 & 6 are peaks and 5 & 2 are valleys. 6 | 7 | Given an array of integers, sort the array into alternating sequence of peaks and valleys. 8 | */ 9 | 10 | function peaksAndValleys(array) { 11 | for (var i = 1; i < array.length; i+= 2) { 12 | var maxIndex = findMaxIndex(array, i - 1, i, i + 1); 13 | if (i !== maxIndex) { 14 | swap(array, i, maxIndex); 15 | } 16 | } 17 | return array; 18 | } 19 | 20 | function findMaxIndex(array, a, b, c) { 21 | 22 | var length = array.length; 23 | var aValue = a < length ? array[a] : -1; 24 | var bValue = b < length ? array[b] : -1; 25 | var cValue = c < length ? array[c] : -1; 26 | 27 | if (bValue > aValue && bValue > cValue) { 28 | return b; 29 | } else if (cValue > aValue && cValue > bValue) { 30 | return c; 31 | } else { 32 | return a; 33 | } 34 | } 35 | 36 | function swap(array, a, b) { 37 | var temp = array[b]; 38 | array[b] = array[a]; 39 | array[a] = temp; 40 | } 41 | -------------------------------------------------------------------------------- /queueStack/queueStack.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Write a stack using your preferred instantiation pattern. 3 | * Avoid using native array methods i.e., push, pop, and length. 4 | * Once you're done, implement a queue using two stacks. 5 | */ 6 | 7 | function Stack() { 8 | var storage = []; 9 | var length = 0; 10 | this.push = function() { 11 | storage[length] = arguments[0]; 12 | length++; 13 | } 14 | this.pop = function() { 15 | if (length) { 16 | var result = storage[length - 1]; 17 | delete storage[length - 1]; 18 | length--; 19 | return result; 20 | } 21 | return undefined; 22 | } 23 | this.size = function() { 24 | return length; 25 | } 26 | } 27 | 28 | function Queue() { 29 | var storage = []; 30 | var length = 0; 31 | var inbox = new Stack(); 32 | var outbox = new Stack(); 33 | this.enqueue = function() { 34 | inbox.push.apply(arguments); 35 | } 36 | 37 | this.dequeue = function() { 38 | if (outbox.size() === 0) { 39 | while(inbox.size() !== 0) { 40 | outbox.push(inbox.pop()); 41 | } 42 | } 43 | return outbox.pop(); 44 | } 45 | 46 | this.size = function() { 47 | return inbox.size() + outbox.size(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /sortedSearch/sortedSearch.js: -------------------------------------------------------------------------------- 1 | /* 2 | You are given an array-like data structure Listy which lacks a size method. 3 | It does however have an "elementAt(i)" method that returns the element at 4 | index i in O(1) time. If i is beyond the bounds of the data structure, it 5 | returns -1. For this reason, the data structure only supports positive integers. 6 | Given an Listy which contains sorted, positive integers, find the index 7 | at which the element x occures. If x occurs multiple times, you 8 | may return any index 9 | */ 10 | 11 | function search(Listy, value) { 12 | // Since we dont' know the lenght, to do binary search, we should 13 | // find the lower index we can use to begin binary search with 14 | var index = 1; 15 | while (Listy.elementAt(index) !== -1 && Listy.elementAt(index) < value) { 16 | index = index *= 2; 17 | } 18 | return binarySearchListy(Listy, value, index / 2, index); 19 | } 20 | 21 | function binarySearchListy(Listy, value, low, high) { 22 | var mid; 23 | while (low <= high) { 24 | mid = (low + high) * 0.5; 25 | var middleInt = Listy.elementAt(mid); 26 | if (middleInt > value || middleInt === -1) { 27 | high = mid - 1; 28 | } else if (middleInt < value) { 29 | low = mid + 1; 30 | } else { 31 | return mid; 32 | } 33 | } 34 | return -1; 35 | } 36 | -------------------------------------------------------------------------------- /longestRun/longestRun.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Write a function that, given a string, Finds the longest run 3 | * of identical characters and returns an array containing the 4 | * start and end indices of that run. If there are two runs 5 | * of equal length, return the first one. 6 | * For example: 7 | * 8 | * longestRun("abbbcc") // [1, 3] 9 | * longestRun("aabbc") // [0, 1] 10 | * longestRun("abcd") // [0, 0] 11 | * 12 | * Try your function with long, random strings to make sure 13 | * it handles large inputs well. 14 | */ 15 | 16 | function longestRun(string) { 17 | var currentCount = 1; 18 | var currentStart = 0; 19 | var topCount = 0; 20 | var topStart = 0; 21 | var topEnd = 0; 22 | for (var i = 1; i < string.length; i++) { 23 | if (string[i] === string[i - 1]) { 24 | currentCount++; 25 | if (currentCount > topCount) { 26 | topCount = currentCount; 27 | topStart = currentStart; 28 | topEnd = i; 29 | } 30 | } else { 31 | currentCount = 1; 32 | currentStart = i; 33 | } 34 | } 35 | 36 | return [topStart, topEnd]; 37 | } 38 | 39 | var randomStringGenerator = function (len) { 40 | var text = ""; 41 | var possible = "abcdefghijklmnopqrstuvwxyz"; 42 | 43 | for(var i = 0; i < len; i++) { 44 | text += possible.charAt(Math.floor(Math.random() * possible.length)); 45 | } 46 | 47 | return text; 48 | }; 49 | -------------------------------------------------------------------------------- /printTreePerimeter/printTreePerimeter.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root node of a binary tree, print nodes forming the boundary (perimeter). 3 | */ 4 | 5 | function printLeftPerimeter(root) { 6 | while (root) { 7 | var current = root.value; 8 | if (root.left) { 9 | root = root.left; 10 | } else if (root.right) { 11 | root = root.right; 12 | } else { 13 | break; // leaf node 14 | } 15 | console.log(current + ' '); 16 | } 17 | } 18 | 19 | function printRightPerimeter(root) { 20 | var stack = []; 21 | while (root) { 22 | var current = root.value; 23 | if (root.right) { 24 | root = root.right; 25 | } else if (root.left) { 26 | root = root.left; 27 | } else { 28 | break; // leaf node 29 | } 30 | stack.push(current); 31 | } 32 | 33 | while (stack.length) { 34 | console.log(stack.pop() + ' '); 35 | } 36 | } 37 | 38 | function printLeaves(root) { 39 | if (root) { 40 | printLeaves(root.left); 41 | if (!root.left && !root.right) { 42 | console.log(root.data + ' '); 43 | } 44 | printLeaves(root.right); 45 | } 46 | } 47 | 48 | function printPerimeter(root) { 49 | if (root) { 50 | console.log(root.data + ' '); 51 | printLeftPerimeter(root.left); 52 | 53 | if (root.left || root.right) { 54 | printLeaves(root); 55 | } 56 | 57 | printRightPerimeter(root.right); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /maxSumSubsequence/maxSumSubsequence.js: -------------------------------------------------------------------------------- 1 | /* 2 | Find an efficient algorithm to find maximum sum of a subsequence in 3 | an array such that no consecutive elements are part of this subsequence. 4 | Consider the following examples, the max sums of subsequence with no consecutive elements in below examples are 20 and 9 respectively. 5 | */ 6 | 7 | // METHOD 1: 8 | function findMaxSumNonAdjacent(array) { 9 | if (array.length < 0) { 10 | return 0; 11 | } else if (array.length === 1) { 12 | return array[0]; 13 | } 14 | 15 | var results = []; 16 | var length = array.length; 17 | results.push(array[0]); 18 | for (var i = 1; i < length; i++) { 19 | results[i] = Math.max(results[i - 1], array[i]); 20 | if (i - 2 >= 0) { 21 | results[i] = Math.max(results[i], results[i - 2] + array[i]); 22 | } 23 | } 24 | return results[length - 1]; 25 | } 26 | 27 | // METHOD 2: 28 | function findMaxSumNonAdjacent2(array) { 29 | if (array.length < 0) { 30 | return 0; 31 | } else if (array.length === 1) { 32 | return array[0]; 33 | } 34 | 35 | var previousSum = array[0]; 36 | var currentSum; 37 | var length = array.length; 38 | for (var i = 1; i < length; i++) { 39 | if (i - 2 >= 0) { 40 | currentSum = Math.max(previousSum, previousSum + array[i]); 41 | previousSum = currentSum; 42 | } else { 43 | currentSum = array[i]; 44 | } 45 | } 46 | return currentSum; 47 | } 48 | -------------------------------------------------------------------------------- /validBinaryTree/validBinaryTree.js: -------------------------------------------------------------------------------- 1 | /* 2 | Write a function to check that a binary tree is a valid binary search tree 3 | 4 | function BinaryTreeNode(value) { 5 | this.value = value; 6 | this.left = null; 7 | this.right = null; 8 | } 9 | 10 | BinaryTreeNode.prototype.insertLeft = function(value) { 11 | this.left = new BinaryTreeNode(value); 12 | return this.left; 13 | }; 14 | 15 | BinaryTreeNode.prototype.insertRight = function(value) { 16 | this.right = new BinaryTreeNode(value); 17 | return this.right; 18 | }; 19 | */ 20 | 21 | function validator(node) { 22 | if (node.right >= node.value && node.left <= node.value) { 23 | return true; 24 | } 25 | 26 | return false; 27 | } 28 | 29 | function validTree(root) { 30 | if (!root) { 31 | return false; 32 | } 33 | 34 | var current = root; 35 | var stack = []; 36 | stack.push({node: root, lowerBound: Infinity, upperBound: Infinity}); 37 | while (stack.length) { 38 | var lastItem = stack.pop(); 39 | var lastNode = lastItem.node; 40 | var lowerBound = lastItem.lowerBound; 41 | var upperBound = lastItem.upperBound; 42 | 43 | if (lastNode.value < lowerBound || lastNode.value > upperBound) { 44 | return false; 45 | } 46 | 47 | if (lastNode.left) { 48 | stack.push({node: lastNode.left, lowerBound: lowerBound, upperBound: lastNode.value}); 49 | } 50 | 51 | if (lastNode.right) { 52 | stack.push({node: lastNode.right, lowerBound: lastNode.right, upperBound: upperBound}); 53 | } 54 | } 55 | return true; 56 | } 57 | -------------------------------------------------------------------------------- /linkedListCycles/linkedListCycles.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Assignment: Write a function that returns true if a linked 3 | * list contains a cycle, or false if it terminates somewhere 4 | * 5 | * Explanation: 6 | * 7 | * Generally, we assume that a linked list will terminate in 8 | * a null next pointer, as follows: 9 | * 10 | * A -> B -> C -> D -> E -> null 11 | * 12 | * A 'cycle' in a linked list is when traversing the list 13 | * would result in visiting the same nodes over and over 14 | * This is caused by pointing a node in the list to another 15 | * node that already appeared earlier in the list. Example: 16 | * 17 | * A -> B -> C 18 | * ^ | 19 | * | v 20 | * E <- D 21 | * 22 | * Example code: 23 | * 24 | * var nodeA = Node('A'); 25 | * var nodeB = nodeA.next = Node('B'); 26 | * var nodeC = nodeB.next = Node('C'); 27 | * var nodeD = nodeC.next = Node('D'); 28 | * var nodeE = nodeD.next = Node('E'); 29 | * hasCycle(nodeA); // => false 30 | * nodeE.next = nodeB; 31 | * hasCycle(nodeA); // => true 32 | * 33 | * Constraint 1: Do this in linear time 34 | * Constraint 2: Do this in constant space 35 | * Constraint 3: Do not mutate the original nodes in any way 36 | */ 37 | var Node = function(value){ 38 | return { value: value, next: null }; 39 | } 40 | 41 | function linkedListCycle(node) { 42 | var slow = node; 43 | var fast = node; 44 | var pause = true; 45 | while (fast = fast.next) { 46 | if (slow === fast) { 47 | return true; 48 | } 49 | slow = pause ? slow : slow.next; 50 | pause = !pause; 51 | } 52 | return false; 53 | } 54 | -------------------------------------------------------------------------------- /characterFrequency/characterFrequency.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Write a function that takes as its input a string and returns an array of 3 | * arrays as shown below sorted in descending order by frequency and then by 4 | * ascending order by character. 5 | * 6 | * :: Example :: 7 | * 8 | * characterFrequency('mississippi') === 9 | * [ 10 | * ['i', 4], 11 | * ['s', 4], 12 | * ['p', 2], 13 | * ['m', 1] 14 | * ] 15 | * 16 | * :: Example2 :: 17 | * 18 | * characterFrequency('miaaiaaippi') === 19 | * [ 20 | * ['a', 4], 21 | * ['i', 4], 22 | * ['p', 2], 23 | * ['m', 1] 24 | * ] 25 | * 26 | * :: Example3 :: 27 | * 28 | * characterFrequency('mmmaaaiiibbb') === 29 | * [ 30 | * ['a', 3], 31 | * ['b', 3], 32 | * ['i', 3], 33 | * ['m', 3] 34 | * ] 35 | * 36 | */ 37 | 38 | function characterFrequency(string) { 39 | var result = []; 40 | var storage = {}; 41 | var letter; 42 | var letters; 43 | for (var i = 0; i < string.length; i++) { 44 | letter = string.substr(i, 1); 45 | storage[letter] = storage[letter] + 1 || 1; 46 | } 47 | letters = Object.keys(storage); 48 | for (var i = 0; i < letters.length; i++) { 49 | result.push([letters[i], storage[letters[i]]]); 50 | } 51 | 52 | result.sort(function(a, b) { 53 | if (a[1] > b[1]) { 54 | return -1; 55 | } else if (a[1] < b[1]) { 56 | return 1; 57 | } else if (a[0] < b[0]) { 58 | return -1; 59 | } else if (a[0] > b[0]) { 60 | return 1; 61 | } else { 62 | return 0; 63 | } 64 | }) 65 | } 66 | -------------------------------------------------------------------------------- /findADuplicate/findADuplicate.js: -------------------------------------------------------------------------------- 1 | /* 2 | We have an array of integers, where: 3 | 4 | The integers are in the range 1 ... n 5 | The array has a length of n+1 6 | 7 | It follows that our array has at least one integer which appears at least twice. But it may have several duplicates, and each duplicate may appear more than twice. 8 | 9 | Write a function which finds any integer that appears more than once in our array. 10 | 11 | We're going to run this function on our new, super-hip Macbook Pro With Retina Display. Thing is, the damn thing came with the RAM soldered right to the motherboard, so we can't upgrade our RAM. So we need to optimize for space! 12 | */ 13 | 14 | function findDuplicate(array) { 15 | var floor = 0; 16 | var ceiling = array.length - 1; 17 | while (floor < ceiling) { 18 | var midpoint = Math.floor((floor + ceiling) * 0.5); 19 | var lowerFloorRange = floor; 20 | var lowerCeilingRange = midpoint; 21 | var upperFloorRange = midpoint + 1; 22 | var upperCeilingRange = ceiling; 23 | 24 | var distinctPossibleIntegersInLowerRange = lowerCeilingRange - lowerFloorRange + 1; 25 | var itemsInLowerRange = 0; 26 | array.forEach(function(item) { 27 | if (item >= lowerFloorRange && item <= lowerCeilingRange) { 28 | itemsInLowerRange++; 29 | } 30 | }); 31 | 32 | if (itemsInLowerRange > distinctPossibleIntegersInLowerRange) { 33 | floor = lowerFloorRange; 34 | ceiling = lowerCeilingRange; 35 | } else { 36 | floor = upperFloorRange; 37 | ceiling = upperCeilingRange; 38 | } 39 | } 40 | 41 | return floor; 42 | } 43 | -------------------------------------------------------------------------------- /coinChange/coinChange.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given coins denominations and total amount, find out the number of ways to make the change. 3 | 4 | For example, we have coin denominations 1, 2 and 5 and the total amount is 7. We can make its change in the following six ways: 5 | - 1, 1, 1, 1, 1, 1, 1 6 | - 1, 1, 1, 1, 1, 2 7 | - 1, 1, 1, 2, 2 8 | - 1, 2, 2, 2 9 | - 1, 1, 5 10 | - 2, 5 11 | */ 12 | 13 | // To count total number solutions, we can divide all set solutions in two sets. 14 | // 1) Solutions that do not contain mth coin (or Sm). 15 | // 2) Solutions that contain at least one Sm. 16 | 17 | function coinChange(amount, denoms) { 18 | var result = []; 19 | for (var i = 0; i <= amount; i++) { 20 | result.push(0); 21 | } 22 | // Initialize our results array with 1 (there is only 1 way to get 0) 23 | result[0] = 1; 24 | var curDenom; 25 | for (var k = 0; k < denoms.length; k++) { 26 | // For each denom 27 | curDenom = denoms[k]; 28 | for (var j = denoms[k]; j <= amount; j++) { 29 | // We count the number of ways to get to target amount, 30 | // adding solution that do not use this denom * do use this denom 31 | result[j] = result[j] + result[j - curDenom]; 32 | } 33 | } 34 | return result[amount]; 35 | } 36 | 37 | // METHOD 2: 38 | function coinChange(amount, denoms, index) { 39 | if (amount === 0) { 40 | return 1; 41 | } 42 | 43 | if (amount < 0) { 44 | return 0; 45 | } 46 | 47 | if (amount > 0 && index === denoms.length) { 48 | return 0; 49 | } 50 | 51 | return coinChange(amount - denoms[index], denoms, index) + coinChange(amount, denoms, index + 1); 52 | } 53 | -------------------------------------------------------------------------------- /allAnagrams/allAnagrams.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given a single input string, write a function that produces all 3 | * possible anagrams of a string and outputs them as an array. 4 | * At first, don't worry about repeated strings. What time complexity 5 | * is your solution? 6 | * 7 | * Extra credit: Deduplicate your return array without using uniq(). 8 | */ 9 | 10 | /** 11 | 12 | * example usage: 13 | * var anagrams = allAnagrams('abc'); 14 | * console.log(anagrams); // [ 'abc', 'acb', 'bac', 'bca', 'cab', 'cba' ] 15 | */ 16 | // APPROACH 1 17 | 18 | function allAnagrams(string) { 19 | var uniqOutput = {}; 20 | 21 | (function anagram(ana, string) { 22 | if (string === '') { 23 | uniqOutput[ana] = 1; 24 | } 25 | 26 | for (var i = 0; i < string.length; i++) { 27 | anagram(ana + string[i], string.slice(0, i) + string.slice(i + 1)) 28 | } 29 | })('', string) 30 | 31 | return Object.keys(uniqOutput); 32 | } 33 | 34 | 35 | // APPROACH 2 36 | // Time complexity: O(n^2) 37 | function allAnagrams(string) { 38 | var stringArray = string.split(''); 39 | var anagrams = []; 40 | var current; 41 | var rest; 42 | var restAnagrams; 43 | var next; 44 | 45 | if (string.length === 1) { 46 | return [string]; 47 | } 48 | 49 | for (var i = 0; i < string.length; i++) { 50 | rest = Object.create(stringArray); 51 | current = rest.splice(i, 1); 52 | restAnagrams = allAnagrams(rest.join('')) 53 | for (var j = 0; j < restAnagrams.length; j++) { 54 | next = current.concat(restAnagrams[j]); 55 | anagrams.push(next.join('')); 56 | } 57 | } 58 | 59 | return anagrams; 60 | } 61 | -------------------------------------------------------------------------------- /primeTester/primeTester.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A prime number is a whole number that has no other 3 | * divisors other than itself and 1. Write a function 4 | * that accepts a number and returns true if it's 5 | * a prime number, false if it's not. 6 | */ 7 | 8 | function primeTester(n) { 9 | if (typeof n !== 'number' || n < 1 || n % 1 !== 0) { 10 | return false; 11 | } 12 | if (n === 1) { 13 | return false; 14 | } 15 | 16 | var upperLimit = Math.sqrt(Math.abs(n)); 17 | for (var i = 2; i < upperLimit; i++) { 18 | if (n % i === 0) { 19 | return false; 20 | } 21 | } 22 | return true; 23 | } 24 | 25 | /* Write a function that generates a list of all prime numbers 26 | * in a user-specified range (inclusive). Check out the Sieve 27 | * of Eratosthenes on Wikipedia. (And if you're feeling 28 | * saucy, check out the Sieve of Atkin.) 29 | */ 30 | 31 | function primeSieve(start, end) { 32 | var current = 2; 33 | var primes = range(0, end + 1); 34 | while (current < end) { 35 | // mark all multiples of current as not prime 36 | for (var i = current + current; i <= end; i += current) { 37 | primes[i] = null; 38 | } 39 | // find the next current 40 | do { 41 | current += 1; 42 | // continue to advance it until we hit a prime number 43 | // or we are out of range. 44 | } while (!primes[current] && current <= end) 45 | } 46 | return primes.slice(2).filter(function(val) { 47 | return val && val >= start; 48 | }); 49 | } 50 | 51 | var range = function (start, end) { 52 | var result = []; 53 | for (var i = start; i < end; i++) { result.push(i); } 54 | return result; 55 | }; 56 | -------------------------------------------------------------------------------- /recursiveMultiply/recursiveMultiply.js: -------------------------------------------------------------------------------- 1 | /* 2 | Write a recursive function to multiply two numbers without using 3 | the * operator or / operator. You can use addition, subtraction and 4 | bit shifting, but you should minimize the number of those operation. 5 | */ 6 | 7 | // Approach 1: iterative 8 | function multiply(a, b) { 9 | var result = 0; 10 | var negative = (a < 0 && b > 0) || (a > 0 && b < 0); 11 | var min = Math.min(Math.abs(a), Math.abs(b)); 12 | var max = min === a ? b : a; 13 | var halfOfMin = Math.floor(min / 2); 14 | var minIsOdd = min % 2 === 1; 15 | 16 | for (var i = 0; i < halfOfMin; i++) { 17 | result += max; 18 | } 19 | 20 | result += result; 21 | 22 | if (minIsOdd) { 23 | result += max; 24 | } 25 | 26 | result = negative ? result *= -1 : result; 27 | 28 | return result; 29 | } 30 | 31 | 32 | 33 | // Approach 2: recursive 34 | function recursiveMultiple(a, b) { 35 | var min = Math.abs(a) < Math.abs(b) ? Math.abs(a) : Math.abs(b); 36 | var max = Math.abs(a) < Math.abs(b) ? Math.abs(b) : Math.abs(a); 37 | var negative = (a < 0 && b > 0) || (a > 0 && b < 0); 38 | 39 | function innerRecurse(min, max) { 40 | if (min === 0) { 41 | return 0; 42 | } 43 | 44 | if (min === 1) { 45 | return max; 46 | } 47 | 48 | var half = min >> 1; 49 | var halfProduct = innerRecurse(half, max); 50 | if (min % 2 === 0) { 51 | return halfProduct + halfProduct; 52 | } else { 53 | return halfProduct + halfProduct + max; 54 | } 55 | } 56 | 57 | var result = innerRecurse(min, max); 58 | if (negative) { 59 | result *= -1; 60 | } 61 | return result; 62 | } 63 | -------------------------------------------------------------------------------- /spiralTraversal/spiralTraversal.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Write a function that accepts a 2-dimensional array 3 | * (that is, an array containing many same-length arrays), 4 | * and prints out every value found, but in a spiral from 5 | * the upper left in to the center 6 | * Please write some form of specs, tests, or assertions for 7 | * your code, and check as many edge cases as you can think of 8 | * Example: 9 | 10 | spiralTraversal([ 11 | [1,2,3], 12 | [4,5,6], 13 | [7,8,9] 14 | ]); 15 | 16 | returns [1, 2, 3, 6, 9, 8, 7, 4, 5] 17 | */ 18 | 19 | function spiralTraversal(array) { 20 | var finalOutput = []; 21 | var startRowIndex = 0; 22 | var endRowIndex = array.length - 1; 23 | var startColIndex = 0; 24 | var endColIndex = array[0].length - 1; 25 | 26 | while (startRowIndex <= endRowIndex && startColIndex <= endColIndex) { 27 | 28 | for (var i = startColIndex; i <= endColIndex; i++) { 29 | finalOutput.push(array[startRowIndex][i]); 30 | } 31 | startRowIndex++; 32 | 33 | for (var i = startRowIndex; i <= endRowIndex; i++) { 34 | finalOutput.push(array[i][endColIndex]); 35 | } 36 | endColIndex--; 37 | 38 | if (startRowIndex <= endRowIndex) { 39 | for (var i = endColIndex; i >= startColIndex; i--) { 40 | finalOutput.push(array[endRowIndex][i]); 41 | } 42 | endRowIndex--; 43 | } 44 | 45 | if (startColIndex <= endColIndex) { 46 | for (var i = endRowIndex; i >= startRowIndex; i--) { 47 | finalOutput.push(array[i][startColIndex]); 48 | } 49 | startColIndex++; 50 | } 51 | } 52 | return finalOutput; 53 | } 54 | -------------------------------------------------------------------------------- /secondLargestItemBinaryTree/secondLargestItemBinaryTree.js: -------------------------------------------------------------------------------- 1 | /* 2 | Write a function to find the 2nd largest element in a binary search tree 3 | 4 | Same binary node class: 5 | function BinaryTreeNode(value) { 6 | this.value = value; 7 | this.left = null; 8 | this.right = null; 9 | } 10 | 11 | BinaryTreeNode.prototype.insertLeft = function(value) { 12 | this.left = new BinaryTreeNode(value); 13 | return this.left; 14 | }; 15 | 16 | BinaryTreeNode.prototype.insertRight = function(value) { 17 | this.right = new BinaryTreeNode(value); 18 | return this.right; 19 | }; 20 | 21 | 10 22 | 7 9 11 12 23 | 3 8 9 10 15 24 | */ 25 | 26 | function findLargest(root) { 27 | var current = root.right; 28 | while(current.right) { 29 | current = current.right; 30 | } 31 | 32 | return current.value; 33 | } 34 | 35 | function findSecondLargestRecursive(root) { 36 | if (!root) { 37 | return null; 38 | } 39 | 40 | if (root.left && !root.right) { 41 | return findLargest(root); 42 | } 43 | 44 | if (root.right && !root.right.right && !root.right.left) { 45 | return root.value; 46 | } 47 | 48 | return findSecondLargest(root.right); 49 | } 50 | 51 | 52 | function findSecondLargestIterative(root) { 53 | if (!root || !root.right || !root.left) { 54 | throw new Error('whoops, need at least two nodes'); 55 | } 56 | 57 | var current = root; 58 | while (current) { 59 | if (!current.right && current.left) { 60 | return findLargest(current.left); 61 | } 62 | 63 | if (current.right && !current.right.left && !current.right.right) { 64 | return current.value; 65 | } 66 | current = current.right; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /deepEquality/deepEquality.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Write a function that, given two objects, returns whether 3 | * or not the two are deeply equivalent--meaning the structure 4 | * of the two objects is the same, and so is the structure 5 | * of each of their corresponding descendants. 6 | * 7 | * Examples: 8 | * 9 | * deepEquals({a:1, b: {c:3}},{a:1, b: {c:3}}); // true 10 | * deepEquals({a:1, b: {c:5}},{a:1, b: {c:6}}); // false 11 | * 12 | * Don't worry about handling cyclical object structures. 13 | * 14 | */ 15 | 16 | function deepEquals(a, b) { 17 | // If deep equal, return true 18 | if (a === b) { 19 | return true; 20 | } 21 | 22 | // if either item exists but the other doesn't, return false 23 | if (b && !a || a & !b) { 24 | return false; 25 | } 26 | 27 | // if either isn't an Object, return false 28 | if ((a instanceof Object) || !(b instanceof Object)) { 29 | return false; 30 | } 31 | 32 | var aKeys = Object.keys(a); 33 | var bKeys = Object.keys(b); 34 | 35 | if (aKeys.length !== bKeys.length) { 36 | return false; 37 | } 38 | 39 | // two empty objects are equal 40 | if (aKeys.length === 0) { 41 | return true; 42 | } 43 | 44 | for (var i = 0; i < aKeys.length; i++) { 45 | if (bJeys[i] !== aKeys[i]) { 46 | return false; 47 | } 48 | 49 | // if one of the keys is an object and other ins't, exit 50 | if (a[aKeys[i]] instanceof Object && !(b[aKeys[i]] instanceof Object) || b[aKeys[i]] instanceof Object && !(a[aKeys[i]] instanceof Object)) { 51 | return false; 52 | } 53 | if (!deepEquals(a[aKeys[i]], b[aKeys[i]])) { 54 | return false; 55 | } 56 | } 57 | return true; 58 | } 59 | -------------------------------------------------------------------------------- /asyncMap/asyncMap.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* Implement the function asyncMap: 4 | * 5 | * asyncMap has two parameters, an array of asynchronous 6 | * functions (tasks) and a callback. 7 | * Each of the tasks takes a separate callback and invokes 8 | * that callback when complete. 9 | * 10 | * The callback passed to asyncMap is then performed on the 11 | * results of the callbacks of the tasks. 12 | * 13 | * The order of these results should be the same as the order 14 | * of the tasks. 15 | * It is important to note that this is not the order in which 16 | * the tasks return, but the order in which they are passed to 17 | * asyncMap. 18 | * 19 | * Once all the callbacks of the tasks are returned, asyncMap 20 | * should invoke the callback on the results array. 21 | * 22 | * Example: 23 | * 24 | * asyncMap([ 25 | * function(cb){ 26 | * setTimeout(function(){ 27 | * cb('one'); 28 | * }, 200); 29 | * }, 30 | * function(cb){ 31 | * setTimeout(function(){ 32 | * cb('two'); 33 | * }, 100); 34 | * } 35 | * ], 36 | * function(results){ 37 | * // the results array will equal ['one','two'] even though 38 | * // the second function had a shorter timeout. 39 | * console.log(results); // ['one', 'two'] 40 | * }); 41 | * 42 | */ 43 | 44 | function asyncMap(tasks, callback) { 45 | var resultsArray = []; 46 | var resultsCount = 0; 47 | 48 | for (var i = 0; i < tasks.length; i++) { 49 | (function(i) { 50 | tasks[i](function(val) { 51 | resultsArray[i] = val; 52 | resultsCount++; 53 | if (resultsCount === tasks.length) { 54 | callback(resultsArray); 55 | } 56 | }); 57 | })(i); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /composePipe/composePipe.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Write Compose and Pipe functions. 3 | * 4 | * Step 1: Implement the function Compose: 5 | * 6 | * Compose should return a function that is the composition 7 | * of a list of functions of arbitrary length. 8 | * 9 | * Each function is called on the return value of the function 10 | * that follows. 11 | * 12 | * You can view compose as moving right to left through its 13 | * arguments. 14 | * Compose Example: 15 | * var greet = function(name){ return 'hi: ' + name;} 16 | * var exclaim = function(statement) { return statement.toUpperCase() + '!';} 17 | * var welcome = compose(greet, exclaim); 18 | * welcome('phillip'); // 'hi: PHILLIP!' 19 | * 20 | * Step 2: Implement the function Pipe: 21 | * 22 | * Pipe composes a series of functions and returns the 23 | * resulting function. 24 | * 25 | * Each function is called on the return value of the 26 | * preceding function. 27 | * 28 | * You can view pipe as moving left to right through its 29 | * arguments. 30 | * 31 | * Pipe Example: 32 | * var add2 = function(number){ return number + 2; } 33 | * var multiplyBy3 = function(number){ return number * 3; } 34 | * pipe(add2, multiplyBy3)(5) // 21 35 | * pipe(add2, multiplyBy3, multiplyBy3)(5) // 63 36 | */ 37 | 38 | function compose() { 39 | var args = Array.prototype.slice.call(arguments); 40 | return function(val) { 41 | return args.reduceRight(function(memo, func) { 42 | return func(memo) 43 | }, val); 44 | } 45 | } 46 | 47 | function pipe() { 48 | var args = Array.prototype.slice.call(arguments); 49 | return function(val) { 50 | return args.reduce(function(memo, func) { 51 | return func(memo); 52 | }, val); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /linkedList/linkedList.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Implement a linked list using the pseudoclassical instantiation pattern. 3 | * 4 | * Your linked list should have methods called "addToTail", "removeHead", and "contains." 5 | * 6 | */ 7 | 8 | // EXAMPLE USAGE: 9 | // var list = new LinkedList(); 10 | // list.tail; //yields 'null' 11 | // list.addToTail(4); 12 | // list.addToTail(5); 13 | // list.head.value; //yields '4'; 14 | // list.contains(5); //yields 'true'; 15 | // list.contains(6); //yields 'false'; 16 | // list.removeHead(); //yields '4' 17 | // list.tail.value; //yields '5'; 18 | var LinkedList = function() { 19 | this.head = null; 20 | this.tail = null; 21 | } 22 | 23 | LinkedList.prototype.addToTail = function(value) { 24 | var newTail = this.makeNode(value); 25 | if (!this.head) { 26 | this.head = newTail; 27 | } 28 | if (this.tail) { 29 | this.tail.next = newTail; 30 | } 31 | this.tail = newTail; 32 | } 33 | 34 | LinkedList.prototype.removeHead = function() { 35 | var currentHead = this.head; 36 | if (!this.head) { 37 | return null; 38 | } 39 | if (this.head === this.tail) { 40 | this.head = null; 41 | this.tail = null; 42 | } else { 43 | this.head = this.head.next; 44 | } 45 | return currentHead.value; 46 | } 47 | 48 | LinkedList.prototype.makeNode = function(value) { 49 | var node = {}; 50 | node.value = value; 51 | node.next = null; 52 | return node; 53 | } 54 | 55 | LinkedList.prototype.contains = function(value) { 56 | if (!this.head) { 57 | return false; 58 | } 59 | var current = this.head; 60 | while(current) { 61 | if (current.value === value) { 62 | return true; 63 | break; 64 | } 65 | current = current.next; 66 | } 67 | return false; 68 | } 69 | -------------------------------------------------------------------------------- /commonCharacters/commonCharacters.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Write a function `f(a, b)` which takes two strings as arguments and 3 | * returns a string containing the characters found in both strings 4 | * (without duplication), in the 5 | * order that they appeared in `a`. Remember to skip spaces and characters you 6 | * have already encountered! 7 | * 8 | * Example: commonCharacters('acexivou', 'aegihobu') 9 | * Returns: 'aeiou' 10 | * Extra credit: Extend your function to handle more than two 11 | * input strings. 12 | * 13 | */ 14 | 15 | function commonCharacters(string1, string2) { 16 | var otherStrings = Array.prototype.slice.call(arguments, 1); 17 | var common = otherStrings.reduce(function(obj, string) { 18 | obj = intersection(obj, objectify(string)); 19 | return obj; 20 | }, objectify(string1)); 21 | 22 | return string1.split('').reduce(function(output, char) { 23 | if (common[char]) { 24 | output += char; 25 | common[char] = false; 26 | } 27 | return output; 28 | }); 29 | } 30 | // HELPER FUNCTIONS: 31 | 32 | // Given two objects, intersection() uses reduce to create 33 | // an object with only the common keys 34 | var intersection = function (set1, set2) { 35 | return Object.keys(set1).reduce(function (out, val) { 36 | if (val in set2) { out[val] = true; } 37 | return out; 38 | }, {}); 39 | }; 40 | 41 | // Takes a string and makes an object with each alphabetical 42 | // character in the string represented by a key with the 43 | // value 'true' 44 | var objectify = function (string) { 45 | return string.split('').reduce(function (obj, char) { 46 | // regex that matches only alphabetical characters of either case 47 | if (char.match(/[a-z]/i)) { obj[char] = true; } 48 | return obj; 49 | }, {}); 50 | 51 | }; 52 | -------------------------------------------------------------------------------- /reverseWordsInSentence/reverseWordsInSentence.js: -------------------------------------------------------------------------------- 1 | var stringReverse = function(string, start, end) { 2 | if (!string || string.length < 2) { 3 | return string; 4 | } 5 | 6 | var stringArray = string.split(''); 7 | while (start < end) { 8 | var temp = stringArray[start]; 9 | stringArray[start] = stringArray[end]; 10 | stringArray[end] = temp; 11 | start++; 12 | end--; 13 | } 14 | return stringArray.join(''); 15 | } 16 | 17 | var replaceAt = function(word, index, character) { 18 | return word.substr(0, index) + character + word.substr(index + character.length); 19 | } 20 | 21 | var reverseStringInPlace = function(string, start, end) { 22 | if (!string || string.length < 2) { 23 | return; 24 | } 25 | while (start < end) { 26 | var temp = string[start]; 27 | string = replaceAt(string, start, string[end]); 28 | string = replaceAt(string, end, temp); 29 | 30 | start++; 31 | end--; 32 | } 33 | return string; 34 | } 35 | 36 | 37 | var reverseWords = function(sentence) { 38 | if (!sentence || sentence.length === 0) { 39 | return null; 40 | } 41 | var stringLength = sentence.length; 42 | var reversedSentence = reverseStringInPlace(sentence, 0, stringLength - 1); 43 | 44 | var start = 0; 45 | var end = 0; 46 | 47 | 48 | while (true) { 49 | while (reversedSentence[start] === ' ') { 50 | start++; 51 | } 52 | 53 | if (start >= stringLength) { 54 | break; 55 | } 56 | 57 | end = start + 1; 58 | while (end < stringLength && reversedSentence[end] !== ' ') { 59 | end++; 60 | } 61 | 62 | reversedSentence = reverseStringInPlace(reversedSentence, start, end - 1); 63 | 64 | start = end; 65 | } 66 | return reversedSentence; 67 | } 68 | -------------------------------------------------------------------------------- /fractionConverter/fractionConverter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Write a function that takes a number as its argument and 3 | * returns a string that represents that number's simplified fraction. 4 | * 5 | * Example: toFraction(0.5) === '1/2' 6 | * 7 | * Whole numbers and mixed fractions should be returned as irregular fractions 8 | * 9 | * Example: toFraction(3.0) === '3/1' 10 | * 11 | * Example: toFraction(2.5) === '5/2' 12 | * 13 | */ 14 | 15 | function fractionConverter(fraction) { 16 | var isNegative = fraction < 1; 17 | if (isNegative) { 18 | fraction = fraction * -1; 19 | } 20 | var fractionString = fraction.toString(); 21 | var decimal = fractionString.split('.')[1]; 22 | var whole = fractionString.split('.')[0]; 23 | var output; 24 | // If we have a decimal value of 0, just return it formatted 25 | // properly as a fraction string 26 | if (decimal === undefined) { 27 | return `${whole}/1`; 28 | } 29 | 30 | // Helper function to simplify a fraction to it's least value 31 | function reduce(numerator, denominator) { 32 | var lowerNum = Math.min(numerator, denominator); 33 | for (var i = lowerNum; i > 0; i--) { 34 | if (i > numerator || i > denominator) { 35 | return [numerator, denominator]; 36 | } 37 | 38 | if (numerator % i === 0 && denominator % i === 0) { 39 | numerator = numerator / i; 40 | denominator = denominator / i; 41 | } 42 | } 43 | return [numerator, denominator]; 44 | } 45 | 46 | var length = decimal.length; 47 | var denom = Math.pow(10, length); 48 | var reduced = reduce(+decimal, denom); 49 | var top = +whole * reduced[1] + +reduced[0]; 50 | var bottom = reduced[1]; 51 | output = isNegative ? `-${top}/${bottom}` : `${top}/${bottom}` 52 | return output; 53 | } 54 | -------------------------------------------------------------------------------- /coinSums/coinSums.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | In England the currency is made up of pound, £, and pence, p, 4 | and there are eight coins in general circulation: 5 | 6 | 1p piece 7 | 2p piece 8 | 5p piece 9 | 10p piece 10 | 20p piece 11 | 50p piece 12 | £1 (100p) 13 | £2 (200p) 14 | 15 | It is possible to make £2 in the following way: 16 | 17 | 1 * £1 + 1 * 50p + 2 * 20p + 1 * 5p + 1 * 2p + 3 * 1p 18 | How many different ways can £2 be made using any number of coins? 19 | 20 | example usage of `makeChange`: 21 | 22 | // aka, there's only one way to make 1p. that's with a single 1p piece 23 | makeChange(1) === 1 24 | // aka, there's only two ways to make 2p. that's with two, 1p pieces or with a single 2p piece 25 | makeChange(2) === 2 26 | */ 27 | 28 | function makeChange(target) { 29 | var output; 30 | var denoms = [200, 100, 50, 20, 10, 5, 1]; 31 | var totalWays = 0 ; 32 | var length = denoms.length - 1; 33 | function innerRecurse(denoms, index, target) { 34 | if (index === denoms.length - 1) { 35 | return 1; 36 | } 37 | 38 | var denomAmount = denoms[index]; 39 | var amountRemaining; 40 | for (var i = 0; i * denomAmount <= target; i++) { 41 | amountRemaining = target - (i * denomAmount); 42 | totalWays += innerRecurse(denoms, index + 1, amountRemaining); 43 | } 44 | return totalWays; 45 | } 46 | return innerRecurse(denoms, 0, target) 47 | } 48 | 49 | // METHOD # 2 50 | function makeChange(n) { 51 | var output; 52 | var denoms = [1, 2, 5, 10, 20, 50, 100, 200]; 53 | 54 | (function innerRecurse(index, total) { 55 | var currentDenom = denom[index]; 56 | if (index === 0) { 57 | total % currentDemon === 0 && output++; 58 | return; 59 | } 60 | while (total >= 0) { 61 | innerRecurse(index - 1, total); 62 | total -= currentDenom; 63 | } 64 | 65 | })(demons.length - 1, n); 66 | return output; 67 | } 68 | -------------------------------------------------------------------------------- /depthFirstSearch/depthFirstSearch.js: -------------------------------------------------------------------------------- 1 | 2 | var Stack = function() { 3 | this.storage = []; 4 | this.length = 0; 5 | } 6 | 7 | Stack.prototype.push = function(val) { 8 | this.storage.push(val); 9 | this.length++; 10 | } 11 | 12 | Stack.prototype.pop = function() { 13 | var val = this.storage.pop(); 14 | this.length--; 15 | return val; 16 | } 17 | 18 | Stack.prototype.peek = function() { 19 | return this.storage[this.length - 1]; 20 | } 21 | 22 | Stack.prototype.isEmpty = function() { 23 | return this.length === 0; 24 | } 25 | 26 | function Node(val){ 27 | this.value = val; 28 | this.left = null; 29 | this.right = null; 30 | } 31 | 32 | 33 | function BinarySearchTree(){ 34 | this.root = null; 35 | } 36 | 37 | BinarySearchTree.prototype.push = function(val) { 38 | var root = this.root; 39 | var newNode = new Node(val); 40 | if(!root) { 41 | this.root = newNode; 42 | return; 43 | } 44 | var current = root; 45 | 46 | while(current) { 47 | if (val < current.value) { 48 | if (!current.left) { 49 | current.left = newNode; 50 | break; 51 | } else { 52 | current = current.left; 53 | } 54 | } else { 55 | if (!current.right) { 56 | current.right = newNode; 57 | break; 58 | } else { 59 | current = current.right; 60 | } 61 | } 62 | } 63 | } 64 | 65 | var tree = new BinarySearchTree(); 66 | tree.push(4); 67 | tree.push(7); 68 | tree.push(7); 69 | tree.push(1); 70 | tree.push(10); 71 | 72 | function DFS(tree) { 73 | if (tree.root === null) { 74 | return; 75 | } 76 | 77 | var current = tree.root; 78 | var stack = new Stack(); 79 | var n; 80 | stack.push(current); 81 | while (!stack.isEmpty()) { 82 | n = stack.pop(); 83 | if (n.left) { 84 | stack.push(n.left); 85 | } 86 | 87 | if (n.right) { 88 | stack.push(n.right); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /telephoneWords/telephoneWords.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Each number key on a standard phone keypad has a set 3 | * of Latin letters written on it as well: http://en.wikipedia.org/wiki/File:Telephone-keypad2.svg 4 | * 5 | * Businesses often try to come up with clever ways to 6 | * spell out their phone number in advertisements to make it more memorable. But there are a lot of combinations! 7 | * 8 | * Write a function that takes up to four digits of a phone 9 | * number, and returns a list of all of the words that can 10 | * be written on the phone with that number. (You should 11 | * return all permutations, not only English words.) 12 | * 13 | * Example: 14 | * telephoneWords('2745'); 15 | * => ['APGJ', 16 | * 'APGK', 17 | * 'APGL', 18 | * ..., // many many more of these 19 | * 'CSIL'] 20 | * 21 | * Tips: 22 | * - Phone numbers are strings! (A phone number can start 23 | * with a zero.) 24 | * - The digits 0 and 1 do not have letters associated 25 | * with them, so they should be left as numbers. 26 | * - Don't return every combination of those digits 27 | * in any order, just the order given. 28 | * 29 | */ 30 | var phoneDigitsToLetters = { 31 | 0: '0', 32 | 1: '1', 33 | 2: 'ABC', 34 | 3: 'DEF', 35 | 4: 'GHI', 36 | 5: 'JKL', 37 | 6: 'MNO', 38 | 7: 'PQRS', 39 | 8: 'TUV', 40 | 9: 'WXYZ' 41 | }; 42 | 43 | function telephoneWords(digitString) { 44 | var words = []; 45 | function innerRecurse(currentWord, index) { 46 | if (currentWord.length === digitString.length) { 47 | words.push(currentWord); 48 | return; 49 | } 50 | var currentLetters = phoneDigitsToLetters[digitString[index]]; 51 | for (var i = 0; i < currentLetters.length; i++) { 52 | innerRecurse(currentWord + currentLetters[i], index + 1); 53 | } 54 | } 55 | innerRecurse('', 0); 56 | return words; 57 | } 58 | -------------------------------------------------------------------------------- /gameScoring/gameScoring.js: -------------------------------------------------------------------------------- 1 | /* 2 | Imagine a game (like baseball) where a player can score 1,2 or 4 runs. Given a score "n", find the total number of ways score "n" can be reached. 3 | */ 4 | 5 | // METHOD 1: 6 | function scoreOptionsRecurse(n, result) { 7 | // If there isn't a path to this n, return 0 8 | if (n < 0) { 9 | return 0; 10 | } 11 | 12 | // If we've calculated & stored the value previously, return it 13 | // (memoization) 14 | if (result[n] > 0) { 15 | return result[n]; 16 | } 17 | 18 | result[n] = scoreOptionsRecurse(n - 1, result) + scoreOptionsRecurse(n - 2, result) + scoreOptionsRecurse(n - 4, result); 19 | 20 | return result[n]; 21 | } 22 | 23 | function scoreOptions(n) { 24 | // return 0 ways if n is less than or equal to 0 25 | if (n <= 0) { 26 | return 0; 27 | } 28 | 29 | var result = {}; 30 | result[0] = 1; 31 | for (var i = 1; i <= n; i++) { 32 | result[i] = 0; 33 | } 34 | 35 | scoreOptionsRecurse(n,result); 36 | return result[n]; 37 | } 38 | 39 | // METHOD 2: 40 | function scoreOptions(n) { 41 | if (n <= 0) { 42 | return 0; 43 | } 44 | 45 | var result = [0, 0, 0, 1]; 46 | var curSum; 47 | for (var i = 1; i <= n; i++) { 48 | // S(n) = S(n - 1) + S(n - 2) + S(n - 4) 49 | curSum = result[3] + result[2] + result[0]; 50 | result[0] = result[1]; 51 | result[1] = result[2]; 52 | result[2] = result[3]; 53 | result[3] = curSum; 54 | } 55 | return result[3]; 56 | } 57 | 58 | // NOW, what if the possible scores were 2, 3, 5? 59 | function scoreOptions(n) { 60 | if (n <= 0) { 61 | return 0; 62 | } 63 | 64 | var result = [0, 0, 0, 0, 1]; 65 | var curSum; 66 | for (var i = 1; i <= n; i++) { 67 | // S(n) = S(n - 2) + S(n - 3) + S(n - 5) 68 | curSum = result[3] + result[2] + result[0]; 69 | result[0] = result[1]; 70 | result[1] = result[2]; 71 | result[2] = result[3]; 72 | result[3] = result[4]; 73 | result[4] = curSum; 74 | } 75 | return result[4]; 76 | } 77 | -------------------------------------------------------------------------------- /subSort/subSort.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an array of integers, write a mehod to find indices m and n 3 | such that if you sorted elements m through n, the entire 4 | array would be sorted. Minimize n - m (that is, find the smallest such 5 | sequence) 6 | 7 | Example: 8 | INPUT: 1, 2, 4, 7, 10, 11, 7, 12, 6, 7, 16, 18, 19 9 | OUTPUT: [3, 9] 10 | */ 11 | 12 | function subSort(array) { 13 | var leftIndex = 0; 14 | var rightIndex = array.length - 1; 15 | 16 | for (var i = 1; i < array.length; i++) { 17 | if (array[i] > array[leftIndex]) { 18 | leftIndex++; 19 | } else { 20 | break; 21 | } 22 | } 23 | 24 | for (var i = array.length - 2; i >=0; i--) { 25 | if (array[i] < array[rightIndex]) { 26 | rightIndex--; 27 | } else { 28 | break; 29 | } 30 | } 31 | 32 | while(rightIndex < array.length && leftIndex >= 0) { 33 | var left = array.slice(0, leftIndex + 1); 34 | var right = array.slice(rightIndex); 35 | var mid = array.slice(leftIndex + 1, rightIndex); 36 | if (!conditionRight(left, mid, right)) { 37 | rightIndex++; 38 | } else if (!conditionLeft(left, mid, right)) { 39 | leftIndex--; 40 | } else { 41 | break; 42 | } 43 | } 44 | return [leftIndex + 1, rightIndex - 1]; 45 | } 46 | 47 | function conditionRight(left, mid, right) { 48 | if (findMin(right) > findMax(mid)) { 49 | return true; 50 | } else { 51 | return false; 52 | } 53 | } 54 | 55 | function conditionLeft(left, mid, right) { 56 | if (findMin(mid) > findMax(left)) { 57 | return true; 58 | } else { 59 | return false; 60 | } 61 | } 62 | 63 | function findMin(array) { 64 | var min = array[0]; 65 | array.forEach(function(item) { 66 | if (item < min) { 67 | min = item; 68 | } 69 | }); 70 | 71 | return min; 72 | } 73 | 74 | function findMax(array) { 75 | var max = array[0]; 76 | array.forEach(function(item) { 77 | if (item > max) { 78 | max = item; 79 | } 80 | }); 81 | 82 | return max; 83 | } 84 | -------------------------------------------------------------------------------- /rotateMatrix/rotateMatrix.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Write a function that rotates a NxN matrix 90 degrees. 3 | * 4 | * A matrix, also called a 2-D array, is simply an array of arrays of values. 5 | * 6 | * Example 1x1 matrix: 7 | * [ [1] ] 8 | * 9 | * Example 2x2 matrix: 10 | * [ [1,2], 11 | * [3,4] ] 12 | * 13 | * Important note: 14 | * Matrices are identified as m-by-n, where m is 15 | * the number of *rows* and n is the number of *columns*. So an [i][j] address in a matrix 16 | * will be i places down, and j places over. This usually matches the way arrays are 17 | * addressed in code, but keep in mind that it differs from use in geometry and computer 18 | * graphics, where coordinates of the form (x,y) are usually x units over, and y units down. 19 | * 20 | * Example rotation of a 4x4 matrix: 21 | * 22 | * var matrix = [ 23 | * [1,2,3,4], 24 | * [5,6,7,8], 25 | * [9,'A','B','C'], 26 | * ['D','E','F','G'] 27 | * ]; 28 | * matrix[0][0]; // 1 29 | * matrix[3][2]; // 'F' 30 | * 31 | * var rotatedMatrix = rotateMatrix(matrix); // Rotate 90 degrees clockwise 32 | * // rotatedMatrix is: 33 | * [ ['D',9,5,1], 34 | * ['E','A',6,2], 35 | * ['F','B',7,3], 36 | * ['G','C',8,4] 37 | * ] 38 | * rotatedMatrix[0][0]; // 'D' 39 | * rotatedMatrix[3][2]; // 8 40 | * 41 | * Extra credit: 42 | * - Make your function operate on rectangular matrices (MxN rather than NxN). 43 | * - Make your function accept a parameter for the direction of rotation (1 = clockwise, -1 = counterclockwise) 44 | */ 45 | 46 | function rotationMartix(matrix, direction) { 47 | direction = direction || 1; 48 | var m = matrix.length; 49 | var n = matrix[0].length; 50 | var output = []; 51 | 52 | for (var i = 0; i < n; i++) { 53 | output[i] = []; 54 | for (var j = 0; j < m; j++) { 55 | if (direction === 1) { 56 | output[i][j] = matrix[m - j - 1][i]; 57 | } else { 58 | output[i][j] = matrix[j][n - i - 1]; 59 | } 60 | } 61 | } 62 | return output; 63 | } 64 | -------------------------------------------------------------------------------- /breadthFirstSearch/breadthFirstSearch.js: -------------------------------------------------------------------------------- 1 | /* 2 | Implement Breadth First Search: https://en.wikipedia.org/wiki/Breadth-first_search 3 | */ 4 | 5 | var Queue = function() { 6 | this.storage = []; 7 | this.length = 0; 8 | } 9 | 10 | Queue.prototype.enqueue = function(val) { 11 | this.storage.push(val); 12 | this.length++; 13 | } 14 | 15 | Queue.prototype.dequeue = function() { 16 | var val = this.storage.shift(); 17 | this.length--; 18 | return val; 19 | } 20 | 21 | Queue.prototype.isEmpty = function() { 22 | return this.length === 0; 23 | } 24 | 25 | function Node(val){ 26 | this.value = val; 27 | this.left = null; 28 | this.right = null; 29 | } 30 | 31 | 32 | function BinarySearchTree(){ 33 | this.root = null; 34 | } 35 | 36 | BinarySearchTree.prototype.push = function(val) { 37 | var root = this.root; 38 | var newNode = new Node(val); 39 | if(!root) { 40 | this.root = newNode; 41 | return; 42 | } 43 | var current = root; 44 | 45 | while(current) { 46 | if (val < current.value) { 47 | if (!current.left) { 48 | current.left = newNode; 49 | break; 50 | } else { 51 | current = current.left; 52 | } 53 | } else { 54 | if (!current.right) { 55 | current.right = newNode; 56 | break; 57 | } else { 58 | current = current.right; 59 | } 60 | } 61 | } 62 | } 63 | 64 | var tree = new BinarySearchTree(); 65 | tree.push(4); 66 | tree.push(7); 67 | tree.push(7); 68 | tree.push(1); 69 | tree.push(10); 70 | 71 | function BFS(tree) { 72 | // if there is no root, return 73 | if (tree.root === null) { 74 | return; 75 | } 76 | var queue = new Queue(); 77 | var current = tree.root; 78 | var n; 79 | queue.enqueue(current); 80 | while(!queue.isEmpty()) { 81 | n = queue.dequeue(); 82 | 83 | console.log(n.value); 84 | 85 | if (n.left !== null) { 86 | queue.enqueue(n.left); 87 | } 88 | if (n.right !== null) { 89 | queue.enqueue(n.right); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /patternMatching/patternMatching.js: -------------------------------------------------------------------------------- 1 | /* 2 | You are given two strings, pattern & value. The pattern string consissts of 3 | just the letters a & b, describing a pattern within a string. For 4 | example, the string catcatgocatgo matches the pattern aabab. it also matches 5 | patterns like a, ab, and b. 6 | 7 | Write a method to determine if value matches pattern. 8 | */ 9 | 10 | function findPattern(v, p) { 11 | if (p.length === 0 || p.length === 1) { 12 | return v.length === p.length; 13 | } 14 | 15 | var size = v.length; 16 | var mainChar = p[0]; 17 | var altChar = mainChar === 'a' ? 'b': 'a'; 18 | var mainCount = countOf(mainChar, p); 19 | var altCount = p.length - mainCount; 20 | var firstAlt = p.indexOf(altChar); 21 | var maxMainSize = Math.floor((v.length - altCount) / mainCount); 22 | 23 | for (var mainSize = 1; mainSize <= maxMainSize; mainSize++) { 24 | var remainingLength = size - (mainSize * mainCount); 25 | var first = v.substr(0, mainSize); 26 | if (altCount === 0 || remainingLength % altCount === 0) { 27 | var altIndex = firstAlt * mainSize; 28 | var altSize = altCount === 0 ? 0 : remainingLength / altCount; 29 | var second = altCount === 0 ? '' : v.substring(altIndex, altSize + altIndex); 30 | var cand = buildFromPatterm(p, first, second); 31 | if (cand === v) { 32 | return true; 33 | } 34 | } 35 | } 36 | return false; 37 | } 38 | 39 | function buildFromPatterm(pattern, main, alt) { 40 | var first = pattern.charAt(0); 41 | var substring = ''; 42 | for (var i = 0; i < pattern.length; i++) { 43 | if (pattern[i] === first) { 44 | substring += main; 45 | } else { 46 | substring += alt; 47 | } 48 | } 49 | return substring; 50 | } 51 | 52 | 53 | function countOf(char, str) { 54 | var count = 0; 55 | for (var i = 0; i < str.length; i++) { 56 | if (str[i] === char) { 57 | count++; 58 | } 59 | } 60 | return count; 61 | } 62 | -------------------------------------------------------------------------------- /awardBudgetCap/awardBudgetCap.js: -------------------------------------------------------------------------------- 1 | /* 2 | The awards committee had planned to give n research grants this year, 3 | out of a its total yearly budget. 4 | 5 | However, the budget was reduced to b dollars. The committee members has 6 | decided to affect the minimal number of highest grants, by applying a 7 | maximum cap c on all grants: every grant that was planned to be higher 8 | than c will now be c dollars. 9 | 10 | Help the committee to choose the right value of c that would make the 11 | total sum of grants equal to the new budget. 12 | 13 | Given an array of grants g and a new budget b, explain and code an 14 | efficient method to find the cap c. Assume that each grant is unique. 15 | 16 | Solution: If we sort the array, we can get linear complexity. 17 | */ 18 | 19 | function findCap(g, b) { 20 | if (g === null || g.length === 0) { 21 | return 0; 22 | } 23 | var gSorted = g.sort(function(a, b) { 24 | if (a < b) { 25 | return -1; 26 | } else if (a > b) { 27 | return 1; 28 | } 29 | return 0; 30 | }); 31 | 32 | var partialSum = []; 33 | var tempSum = 0; 34 | 35 | // Find partialSum up to each index in sorted array 36 | for (var i = 0; i < gSorted.length; i++) { 37 | tempSum += gSorted[i]; 38 | partialSum[i] = tempSum; 39 | } 40 | 41 | // If the sum of entire array is less than budget, return the largest 42 | // award amount (i.e. last element in array) 43 | if (partialSum[gSorted.length - 1] <= b) { 44 | return gSorted[n - 1]; 45 | } 46 | 47 | // Helper function to find capped sum if current index were the cap 48 | function cappedSum(i) { 49 | return partialSum[i - 1] * gSorted[i]*(gSorted.length - i); 50 | } 51 | 52 | var start = 0; 53 | var end = gSorted.length - 1; 54 | var mid, result; 55 | 56 | while (end > start) { 57 | mid = Math.floor((end + start) * 0.5); 58 | if (mid > 0) { 59 | if (cappedSum(mid) > b) { 60 | if (cappedSum(mid - 1) < b) { 61 | break; 62 | } else { 63 | end = mid - 1; 64 | } 65 | } else { 66 | start = mid + 1; 67 | } 68 | } 69 | } 70 | result = (b - partialSum[mid - 1]) / (gSorted.length - mid); 71 | return result; 72 | } 73 | -------------------------------------------------------------------------------- /rectangleLove/rectangleLove.js: -------------------------------------------------------------------------------- 1 | /* 2 | A crack team of love scientists from OkEros (a hot new dating site) have 3 | devised a way to represent dating profiles as rectangles on a two-dimensional 4 | plane. 5 | 6 | They need help writing an algorithm to find the intersection of two users' 7 | love rectangles. They suspect finding that intersection is the key to a 8 | matching algorithm so powerful it will cause an immediate acquisition by 9 | Google or Facebook or Obama or something. 10 | 11 | Write a function to find the rectangular intersection of two given love 12 | rectangles. 13 | 14 | As with the example above, love rectangles are always "straight" and 15 | never "diagonal." More rigorously: each side is parallel with either the 16 | x-axis or the y-axis. 17 | 18 | They are defined as objects like this: 19 | 20 | var myRectangle = { 21 | 22 | // coordinates of bottom-left corner 23 | leftX: 1, 24 | bottomY: 5, 25 | 26 | // width and height 27 | width: 10, 28 | height: 4, 29 | 30 | }; 31 | */ 32 | 33 | function findIntersection(a, b) { 34 | var xOverlap = findXOverlap(a, b); 35 | var yOverlap = findYOverlap(a, b); 36 | if (!xOverlap.width || !yOverlap.width) { 37 | return 0; 38 | } 39 | 40 | return { 41 | leftX: xOverlap.startPoint, 42 | width: xOverlap.width, 43 | bottomY: yOverlap.startPoint, 44 | height: yOverlap.height, 45 | } 46 | } 47 | 48 | function findXOverlap(a, b) { 49 | var highestStartPoint = Math.max(a.leftX, b.leftX); 50 | var lowestEndPoint = Math.min(a.leftX + a.width, b.leftX, b.width); 51 | if (highestStartPoint > lowestEndPoint) { 52 | return { 53 | startPoint: null, 54 | width: null, 55 | }; 56 | } else { 57 | return { 58 | startPoint: highestStartPoint, 59 | width: lowestEndPoint - highestStartPoint 60 | }; 61 | } 62 | } 63 | 64 | function findYOverlap(a, b) { 65 | var highestStartPoint = Math.max(a.bottomY, b.bottomY); 66 | var lowestEndPoint = Math.min(a.bottomY + a.height, b.leftX, b.height); 67 | if (highestStartPoint > lowestEndPoint) { 68 | return { 69 | startPoint: null, 70 | width: null, 71 | }; 72 | } else { 73 | return { 74 | startPoint: highestStartPoint, 75 | width: lowestEndPoint - highestStartPoint 76 | }; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /pathsWithSum/pathsWithSum.js: -------------------------------------------------------------------------------- 1 | /* 2 | You are given a binary tree in which each node contains an integer value which might be positive or negative. Design an algorithm to count the number of paths that sum to a fiven value. 3 | The path does not need to start of end at the root or leaf, but it much go downwards (traveling from parent to child node) 4 | */ 5 | 6 | // APPROACH #1: BRUTE FORCE 7 | function pathsWithSum(tree, value) { 8 | if (tree.data === null) { 9 | return 0; 10 | } 11 | // Count paths starting from root 12 | var pathsFromRoot = pathsWithSumFromNode(root, value, 0); 13 | var pathsOnLeft = pathsWithSum(root.left, value); 14 | var pathsOnRight = pathsWithSum(root.right, value); 15 | 16 | return pathsFromRoot + pathsOnLeft + pathsOnRight; 17 | } 18 | 19 | function pathsWithSumFromNode(node, value, currentSum) { 20 | if (node === null) { 21 | return 0; 22 | } 23 | 24 | currentSum += node.data; 25 | 26 | var totolPaths = 0; 27 | if (currentSum === value) { 28 | totalPaths++; 29 | } 30 | 31 | totalPaths += pathsWithSumFromNode(node.right, value, currentSum); 32 | totalPaths += pathsWithSumFromNode(node.left, value, currentSum); 33 | 34 | return totalPaths; 35 | } 36 | 37 | // APPROACH #2: OPTIMIZED 38 | function pathsWithSum(tree, value) { 39 | if (tree.data === null) { 40 | return 0; 41 | } 42 | var pathCount = {}; 43 | incrementHashTable(pathCount, 0, 1); 44 | return pathsWithSumFromNode(tree, value, 0, pathCount); 45 | } 46 | 47 | function pathsWithSumFromNode(node, value, currentSum, pathCount) { 48 | currentSum += node.data; 49 | // Adding running sum to hash table 50 | incrementHashTable(pathCount, currentSum, 1); 51 | 52 | // Count # of paths with sum ending at the current node 53 | var sum = currentSum - value; 54 | var totalPaths = pathCount[sum] !== undefined ? pathCount[sum] : 0; 55 | 56 | // Count paths with sum on the left and right 57 | totalPaths += pathsWithSum(node.left, value, currentSum, pathCount); 58 | totalPaths += pathsWithSum(node.right, value, currentSum, pathCount); 59 | 60 | incrementHashTable(pathCount, currentSum, -1); 61 | return totalPaths; 62 | } 63 | 64 | function incrementHashTable(pathCount, key, delta) { 65 | if (pathCount[key]) { 66 | pathCount[key] = 0; 67 | } 68 | pathCount[key] = pathCount[key] + delta; 69 | } 70 | -------------------------------------------------------------------------------- /boggle/boggle.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an NxN grid of characters and a dictionary, find all words which can be made from the characters in grid and present in the given dictionary. A word can start and end at any character in the grid. Next character must be adjacent to previous character in any of the directions i.e. up, down, left, right and diagonal. Character at each position in grid can be used only once while making a word. 3 | */ 4 | 5 | // Assume we have a pre-existing implementation of dictionary 6 | class Boggle { 7 | constructor(grid, dictionary) { 8 | this.grid = grid; 9 | this.dictionary = dictionary; 10 | this.state = []; 11 | 12 | for (var i = 0; i < this.grid.length; i++) { 13 | var temp = []; 14 | for (var j = 0; j < this.grid.length; j++) { 15 | temp.push(false); 16 | } 17 | this.state.push(temp); 18 | } 19 | } 20 | 21 | findAllNeighbors(x, y) { 22 | var neighbors = []; 23 | var start_x = Math.max(0, x - 1); 24 | var start_y = Math.max(0, y - 1); 25 | var end_x = Math.min(this.grid.length - 1, x + 1); 26 | var end+y = Math.min(this.grid.length - 1, y + 1); 27 | for (var i = start_x; i <= end_x; i++) { 28 | for (j = start_y; j <= end_y; j++) { 29 | if (i === x && j === y) { 30 | continue; 31 | } 32 | if(this.state[i][j] === false) { 33 | neighbors.push([i, j]); 34 | } 35 | } 36 | } 37 | return neighbors; 38 | } 39 | 40 | findWordsRect(i, j, current, words) { 41 | if (current.length > 0 && this.dictionary.has(current)) { 42 | words.add(current); 43 | } 44 | 45 | var neighbors = this.findAllNeighbors(i, j); 46 | for (var k = 0; k < neighbors.length; k++) { 47 | var first = neighbors[k][0]; 48 | var second = neighbors[k][1]; 49 | current += this.grid[first][second]; 50 | this.state[first][second] = true; 51 | this.findWordsRect(first, second, current, words); 52 | current = current.substr(0, current.length - 1); 53 | this.state[first][second] = false; 54 | } 55 | } 56 | 57 | findAllWords() { 58 | var words = new Set([]); 59 | for (var i = 0; i < this.grid.length; i++) { 60 | for (var k = 0; k < this.grid.length; k++) { 61 | this.findWordsRect(i, k, '', words); 62 | } 63 | } 64 | return words; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /quickSort/quickSort.js: -------------------------------------------------------------------------------- 1 | /* 2 | Implement Quick sort: 3 | - The divide-and-conquer strategy is used in quicksort. Below the recursion step is described: 4 | - Choose a pivot value. We take the value of the middle element 5 | as pivot value, but it can be any value, which is in range of 6 | sorted values, even if it doesn't present in the array. 7 | - Partition. Rearrange elements in such a way, that all elements which are lesser than the pivot go to the left part of the array 8 | and all elements greater than the pivot, go to the right part of 9 | the array. Values equal to the pivot can stay in any part of the array. Notice, that array may be divided in non-equal parts. 10 | - Sort both parts. Apply quicksort algorithm recursively to the 11 | left and the right parts. 12 | 13 | */ 14 | 15 | function quickSort(array, low, high) { 16 | // At first round, assume we aren't given the low and high values 17 | if(typeof low !== 'number') { 18 | low = 0; 19 | } 20 | 21 | if(typeof high !== 'number') { 22 | high = array.length - 1; 23 | } 24 | 25 | var pivotIndex = null; 26 | if (low < high) { 27 | // Set pivot index to be midpoint (can be anything) 28 | pivotIndex = Math.ceil((high - low) * 0.5) + low; 29 | // Calc the new pivot by moving everything lower than 30 | // current pivot to left of it and everything higher 31 | // than current pivot to right of it 32 | newPivot = partition(array, pivotIndex, low, high); 33 | // Repeat for the sub array to left and right of new pivot 34 | quickSort(array, low, newPivot - 1); 35 | quickSort(array, newPivot + 1, high); 36 | } 37 | } 38 | 39 | // Utility function to swap values in an array 40 | function swap(array, indexA, indexB) { 41 | var temp = array[indexA]; 42 | array[indexA] = array[indexB]; 43 | array[indexB] = temp; 44 | } 45 | 46 | function partition(array, pivot, low, high) { 47 | // Start our new pivot point from the low index 48 | var storeIndex = low; 49 | var pivotValue = array[pivot]; 50 | // put our pivot value at the right end 51 | swap(array, pivot, high); 52 | // For each value lower than current pivot value, 53 | // put it to the left of the current pivot point 54 | // and increment current pivot point by 1 55 | for (var j = low; j < high; j++) { 56 | if (array[j] < pivotValue) { 57 | swap(array, j, storeIndex); 58 | storeIndex++; 59 | } 60 | } 61 | // Put our pivot value in the right place 62 | swap(array, high, storeIndex); 63 | return storeIndex; 64 | } 65 | -------------------------------------------------------------------------------- /stackOfBoxes/stackOfBoxes.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | You have a stack of n boxes with width wi, height hi 4 | and depth di. The boxes cannot be rotated and can only be stacked on top of one another if each box in the stack 5 | is strictly larger than the boxed above it in width, height, 6 | and depth. 7 | 8 | Implement a method to compute the height of the tallest possible stack. The height of a stack is the sum of heights 9 | of each box 10 | 11 | */ 12 | var boxes = [ 13 | { height: 10, width: 5, depth: 5 }, 14 | { height: 2, width: 5, depth: 5 }, 15 | { height: 8, width: 3, depth: 2 }, 16 | ]; 17 | 18 | // Sort function for ordering boxed from largest to smallest 19 | // by height 20 | function boxComparator(box1, box2) { 21 | return box2.height - box1.height; 22 | } 23 | 24 | // BRUTE FORCE 25 | function createStack(boxesArray) { 26 | var maxHeight = 0; 27 | var sortedBoxesByHeight = boxesArray.sort(boxComparator); 28 | for (var i = 0; i < boxesArray.length; i++) { 29 | currentHeight = createStackRecursively(boxes, i); 30 | maxHeight = Math.max(currentHeight, maxHeight); 31 | } 32 | return maxHeight; 33 | } 34 | 35 | 36 | function createStackRecursively(boxes, bottomIndex) { 37 | var bottomBox = boxes[bottomIndex]; 38 | var maxHeight = 0; 39 | for (var i = bottomIndex + 1; i < boxes.length; i++) { 40 | if (boxes[i].height < bottomBox.height && boxes[i].width < bottomBox.width && boxes[i].depth < bottomBox.depth) { 41 | currentHeight = createStackRecursively(boxes, i); 42 | maxHeight = Math.max(currentHeight, maxHeight); 43 | } 44 | } 45 | maxHeight = maxHeight + bottomBox.height; 46 | return maxHeight; 47 | } 48 | 49 | // MEMOIZED 50 | function createStackMemo(boxesArray) { 51 | var maxHeight = 0; 52 | var memo = {}; 53 | var sortedBoxesByHeight = boxesArray.sort(boxComparator); 54 | for (var i = 0; i < boxesArray.length; i++) { 55 | currentHeight = createStackRecursivelyMemo(boxes, i, memo); 56 | maxHeight = Math.max(currentHeight, maxHeight); 57 | } 58 | return maxHeight; 59 | } 60 | 61 | function createStackRecursivelyMemo(boxes, bottomIndex, memo) { 62 | var bottomBox = boxes[bottomIndex]; 63 | 64 | if (memo[bottomIndex]) { 65 | return memo[bottomIndex]; 66 | } 67 | 68 | var maxHeight = 0; 69 | var current; 70 | for (var i = bottomIndex + 1; i < boxes.length; i++) { 71 | if (boxes[i].height < bottomBox.height && boxes[i].width < bottomBox.width && boxes[i].depth < bottomBox.depth) { 72 | currentHeight = createStackRecursivelyMemo(boxes, i, memo); 73 | maxHeight = Math.max(currentHeight, maxHeight); 74 | } 75 | } 76 | maxHeight = maxHeight + bottomBox.height; 77 | memo[bottomIndex] = maxHeight; 78 | return maxHeight; 79 | } 80 | -------------------------------------------------------------------------------- /levenshteinDistance/levenshteinDistance.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given two strings, compute the Levenshtein distance between them i.e. the minimum number of edits required to convert one string into the other. 3 | 4 | For example, the Levenshtein distance between "kitten" and "sitting" is 3. 5 | 6 | The minimum steps required to transform the former into latter are: 7 | 8 | kitten → sitten (substitution of "s" for "k") 9 | sitten → sittin (substitution of "i" for "e") 10 | sittin → sitting (insertion of "g" at the end) 11 | 12 | Mathematically, the Levenshtein distance between two strings a, b (of length i and j respectively) is given by leva,b (i, j) where 13 | 14 | leva,b(i, j) = max(i, j) if min(i, j) = 0, 15 | min( lev a,b(i-1, j) + 1, lev a,b(i, j-1) + 1, lev a,b(i-1, j-1) + cost ) otherwise. 16 | 17 | where cost is 0 when a = b and 1 otherwise. 18 | */ 19 | function levenshteinDistance(string1, string2) { 20 | if (string1 === string2) { 21 | return 0; 22 | } 23 | 24 | if (string1.length === 0) { 25 | return string2.length; 26 | } 27 | 28 | if (string2.length === 0) { 29 | return string1.length; 30 | } 31 | 32 | var matrix = []; 33 | for (var i = 0; i <= string1.length; i++) { 34 | matrix.push([]); 35 | for (var j = 0; j <= string2.length; j++) { 36 | if (i === 0) { 37 | matrix[i][j] = j; 38 | } else if (j === 0) { 39 | matrix[i][j] = i; 40 | } else { 41 | matrix[i][j] = 0; 42 | } 43 | } 44 | } 45 | 46 | var cost; 47 | for (var k = 1; k <= string1.length; k++) { 48 | for (var n = 1; n <= string2.length; n++) { 49 | if (string1[k - 1] === string2[n - 1]) { 50 | cost = 0; 51 | } else { 52 | cost = 1; 53 | } 54 | matrix[k][n] = Math.min(matrix[k - 1][n] + 1, matrix[k][n - 1] + 1, matrix[k - 1][n - 1] + cost); 55 | } 56 | } 57 | return matrix[string1.length][string2.length]; 58 | } 59 | 60 | // MORE OPTIMIZED 61 | function levenshteinDistance2(string1, string2) { 62 | if (string1 === string2) { 63 | return 0; 64 | } 65 | 66 | if (string1.length === 0) { 67 | return string2.length; 68 | } 69 | 70 | if (string2.length === 0) { 71 | return string1.length; 72 | } 73 | 74 | var row1 = []; 75 | var row2 = []; 76 | for (var i = 0; i <= string2.length; i++) { 77 | row1.push(i); 78 | row2.push(0); 79 | } 80 | 81 | var cost; 82 | for (var k = 0; k < string1.length; k++) { 83 | row2[0] = k + 1; 84 | for (var n = 0; n < string2.length; n++) { 85 | if (string1[k] === string2[n]) { 86 | cost = 0; 87 | } else { 88 | cost = 1; 89 | } 90 | row2[n + 1] = Math.min(row2[n] + 1, row1[n + 1] + 1, row1[n] + cost); 91 | } 92 | for (var j = 0; j < string2.length; j++) { 93 | row1[j] = row2[j]; 94 | } 95 | } 96 | return row2[string2.length]; 97 | } 98 | -------------------------------------------------------------------------------- /textJustification/textJustification.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an array of words and a length L, format the text such that each 3 | line has exactly L characters and is fully (left and right) justified. 4 | You should pack your words in a greedy approach; that is, pack as many 5 | words as you can in each line. Pad extra spaces ' ' when necessary so 6 | that each line has exactly L characters. 7 | 8 | Extra spaces between words should be distributed as evenly as possible. 9 | If the number of spaces on a line do not divide evenly between words, 10 | the empty slots on the left will be assigned more spaces than the slots 11 | on the right. 12 | 13 | For the last line of text, it should be left justified and no extra space 14 | is inserted between words. 15 | 16 | For example, 17 | Words: ["This", "is", "an", "example", "of", "text", "justification."] 18 | L: 16 19 | 20 | Return the formatted lines as: 21 | [ 22 | "This is an", 23 | "example of text", 24 | "justification. " 25 | ] 26 | */ 27 | 28 | function textJustification(words, L) { 29 | var lines = []; 30 | var newLine = ''; 31 | var i = 0; 32 | var currentCount = 0; 33 | var leftOver = 0; 34 | var spacesInBetween = 0; 35 | var spaces = ''; 36 | while (i < words.length) { 37 | // If we can add more words do current line, add them and increment 38 | // the character count for current line 39 | if (currentCount + words[i].length <= L) { 40 | newLine += words[i]; 41 | newLine += ' '; 42 | currentCount += words[i].length + 1; 43 | i++; 44 | } else { 45 | // If we exceeded the max character count (L) for current line 46 | // Calculate the spaces in between each word in current line that 47 | // need to be added to justify text. Then add these spaces 48 | newLine = newLine.split('').splice(0, newLine.length - 1).join(''); 49 | leftOver = L - currentCount; 50 | spacesInBetween = leftOver / (newLine.split(' ').length - 1); 51 | if (spacesInBetween % 1 !== 0) { 52 | spacesInBetween = Math.floor(leftOver / (newLine.split(' ').length - 1)); 53 | for (var k = 0; k < spacesInBetween; k++) { 54 | spaces += ' '; 55 | } 56 | for (var j = 0; j < newLine.length; j++) { 57 | newLine[j] += spaces; 58 | } 59 | } else { 60 | for (var m = 0; m < spacesInBetween; m++) { 61 | spaces += ' '; 62 | } 63 | for (var n = 0; n < newLine.length; n++) { 64 | newLine[n] += spaces; 65 | } 66 | } 67 | // Push the new updated line to our lines 68 | lines.push(newLine); 69 | // reset our variables 70 | spaces = ''; 71 | newLine = ''; 72 | leftOver = 0; 73 | spacesInBetween = 0; 74 | currentCount = 0; 75 | // if we are at the last word, we don't need to do any more work. 76 | // Just pus it to the end of the line 77 | if (i === words.length - 1) { 78 | lines.push(words[words.length - 1]); 79 | break; 80 | } 81 | } 82 | } 83 | return lines; 84 | } 85 | -------------------------------------------------------------------------------- /treeBFSelect/treeBFSelect.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Implement a `BFSelect` method on this Tree class. 4 | * 5 | * BFSelect accepts a filter function, calls that function on each of the nodes 6 | * in Breadth First order, and returns a flat array of node values of the tree 7 | * for which the filter returns true. 8 | * 9 | * Example: 10 | * var root1 = new Tree(1); 11 | * var branch2 = root1.addChild(2); 12 | * var branch3 = root1.addChild(3); 13 | * var leaf4 = branch2.addChild(4); 14 | * var leaf5 = branch2.addChild(5); 15 | * var leaf6 = branch3.addChild(6); 16 | * var leaf7 = branch3.addChild(7); 17 | * root1.BFSelect(function (value, depth) { 18 | * return value % 2; 19 | * }) 20 | * // [1, 3, 5, 7] 21 | * 22 | * root1.BFSelect(function (value, depth) { 23 | * return depth === 1; 24 | * }) 25 | * // [2, 3] 26 | * 27 | */ 28 | 29 | /* 30 | * Basic tree that stores a value. 31 | */ 32 | 33 | var Tree = function(value){ 34 | this.value = value; 35 | this.children = []; 36 | }; 37 | 38 | // Super basic queue 39 | var Queue = function() { 40 | var storage = []; 41 | 42 | this.push = function(item) { 43 | storage.push(item); 44 | }; 45 | 46 | this.pop = function() { 47 | return storage.shift(); 48 | }; 49 | 50 | this.length = function() { 51 | return storage.length; 52 | } 53 | }; 54 | 55 | 56 | Tree.prototype.BFSelect = function(filter) { 57 | var queue = new Queue(); 58 | var results = []; 59 | queue.push({tree: this, depth: 0}); 60 | while (queue.length()) { 61 | var item = queue.pop(); 62 | var tree = item.tree; 63 | var depth = item.depth; 64 | if (filter(tree, depth)) { 65 | results.push(item); 66 | } 67 | for (var i = 0; i < tree.children.length; i++) { 68 | queue.push({tree: tree.children[i], depth: depth + 1}); 69 | } 70 | } 71 | 72 | return results; 73 | }; 74 | 75 | 76 | /** 77 | * add an immediate child 78 | * (wrap values in Tree nodes if they're not already) 79 | */ 80 | Tree.prototype.addChild = function(child){ 81 | if (!child || !(child instance of Tree)) { 82 | child = new Tree(child); 83 | } 84 | 85 | if (!this.descendant(child)) { 86 | this.children.push(child); 87 | } else { 88 | throw new Error('This child is already a child of this tree!'); 89 | } 90 | return child; 91 | }; 92 | 93 | /** 94 | * check to see if the provided tree is already a child of this tree or any of its sub trees 95 | */ 96 | Tree.prototype.isDescendant = function(child){ 97 | if (this.children.indexOf(child) > -1) { 98 | return true; 99 | } else { 100 | for (var i = 0; i < this.children.length; i++) { 101 | if(this.children[i].isDescendant(child) { 102 | return true; 103 | } 104 | } 105 | return false; 106 | } 107 | 108 | }; 109 | 110 | /** 111 | * remove an immediate child 112 | */ 113 | Tree.prototype.removeChild = function(child){ 114 | var index = this.children.indexOf(child); 115 | if (index === -1) { 116 | throw new Error('child is not an immediate child of this node'); 117 | } 118 | 119 | this.children.splice(index, 1); 120 | }; 121 | -------------------------------------------------------------------------------- /mergeSort/mergeSort.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Implement a function that sorts an array of numbers using 3 | * the "mergesort" algorithm. 4 | * 5 | * Mergesort is an optimized sorting algorithm which is a common 6 | * choice to implement `sort` methods in standard libraries as an 7 | * alternative to quicksort or heapsort. (For example, 8 | * Firefox's Array.sort method uses a tuned mergesort; the WebKit 9 | * engine used by Chrome and Safari uses quicksort for numeric 10 | * arrays, and mergesort for arrays of strings.) 11 | * 12 | * Mergesort uses a divide-and-conquer strategy. It begins by 13 | * treating the input list of length N as a set of N "sublists" 14 | * of length 1, which are considered to be sorted. Adjacent 15 | * sublists are then "merged" into sorted sublists of length 2, 16 | * which are merged into sorted sublists of length 4, and so 17 | * on, until only a single sorted list remains. (Note, if N is 18 | * odd, an extra sublist of length 1 will be left after the first 19 | * merge, and so on.) 20 | * 21 | * This can be implemented using either a recursive ("top-down") 22 | * or an iterative ("bottom-up") approach. 23 | * 24 | * Illustration of an iterative approach: 25 | * 26 | * Initial step: Input array is split into "sorted" sublists 27 | * [4,7,4,3,9,1,2] -> [[4],[7],[4],[3],[9],[1],[2]] 28 | * 29 | * Merge step: Adjacent sublists are merged into sorted sublists 30 | * [[4],[7],[4],[3],[9],[1],[2]] -> [[4,7],[3,4],[1,9],[2]] 31 | * 32 | * Repeat merge step: 33 | * [[4,7],[3,4],[1,9],[2]] -> [[3,4,4,7], [1,2,9]] 34 | * 35 | * Repeat merge step: 36 | * [[3,4,4,7], [1,2,9]] -> [[1,2,3,4,4,7,9]] 37 | * 38 | * Done! Return the sorted array: 39 | * [1,2,3,4,4,7,9] 40 | * Illustration of a recursive approach: 41 | * 42 | * 1. Split the input array in half 43 | * [4, 7, 4, 3, 9, 1, 2] -> [4, 7, 4], [3, 9, 1, 2] 44 | * 45 | * 2. Both sides are sorted recursively: 46 | * [4, 7, 4] -> [4, 4, 7] 47 | * [3, 9, 1, 2] -> [1, 2, 3, 9] 48 | * 49 | * 3. Both halves are merged: 50 | * [4, 7, 4], [3, 9, 1, 2] -> [1, 2, 3, 4, 4, 7, 9] 51 | * 52 | * Step 2 might seem a bit mystical - how do we sort both sides? The 53 | * simple answer is that we use mergesort! After all, mergesort sorts 54 | * arrays, right? We can test this on [4, 7, 4] by just following the 55 | * steps above but imagining that [4, 7, 4] is the whole array, which 56 | * is what happens when you call mergesort on it. 57 | * 58 | * 1. Split the input array in half 59 | * [4, 7, 4] -> [4], [7, 4] 60 | * 61 | * 2. Both sides are sorted recursively: 62 | * [4] -> [4] 63 | * [7, 4] -> [4, 7] 64 | * 65 | * 3. Both halves are merged: 66 | * [4], [4, 7] -> [4, 4, 7] 67 | * 68 | * I cheated again by going directly from [7, 4] to [4, 7], 69 | * but that's really just: 70 | * 71 | * 1. Split the input array in half 72 | * [7, 4] -> [7], [4] 73 | * 74 | * 2. Both sides are sorted recursively: 75 | * [7] -> [7] 76 | * [4] -> [4] 77 | * 78 | * 3. Both halves are merged: 79 | * [7], [4] -> [4, 7] 80 | * 81 | * As you can see, all the work actually gets done in step 3, 82 | * the merge step. Everything else is just splitting and recursing. 83 | * 84 | * 85 | * Complexity: 86 | * What is the complexity of your algorithm in time and space? 87 | * The merge step can be implemented using what is conceptually 88 | * an insertion sort, and yet its time 89 | * complexity is (spoiler alert!) much lower. Why is that? 90 | * 91 | * 92 | * Extra credit: 93 | * One of the benefits of mergesort over e.g. quicksort is that 94 | * it is "stable"; assuming the merge step is properly implemented, 95 | * list items with the same value will remain in the same order 96 | * they were in in the input. (This is academic in the case of 97 | * sorting integers, but it is an important consideration when 98 | * sorting more complex objects.) Is your implementation a stable sort? 99 | * 100 | * Extra credit: 101 | * The naive mergesort assumes that the input array is completely 102 | * unsorted, but in practice even random data will have "runs" of 103 | * sorted integers. The "natural mergesort" takes advantage of this 104 | * by splitting the input not into sublists of length 1, but into 105 | * whatever sublists are already sorted in the input. 106 | * Implement natural splitting into your mergesort. How much does 107 | * it improve your average-case runtime? 108 | * 109 | */ 110 | 111 | function merge(arrayR, arrayL) { 112 | var merged = []; 113 | indexR = 0; 114 | indexL = 0; 115 | while (merged.length < arrayR.length + arrayL.length) { 116 | if (indexL >= arrayL.length || arrayR[indexR] < arrayL[indexL]) { 117 | merged.push(arrayR[indexR]); 118 | indexR++; 119 | } else { 120 | merged.push(arrayL[indexL]); 121 | indexL++; 122 | } 123 | } 124 | return merged; 125 | } 126 | 127 | function mergeSort(array) { 128 | var lists = []; 129 | var currentList = []; 130 | // Create sublists out of the original list, 131 | // with each list sublist being a list that is 132 | // already sorted 133 | for (var i = 0; i < array.length; i++) { 134 | if (currentList.length && array[i] < currentList[currentList.length - 1]) { 135 | lists.push(currentList); 136 | currentList = []; 137 | } 138 | currentList.push(array[i]); 139 | } 140 | lists.push(currentList); 141 | // Until we merge into one list 142 | while (lists.length > 1) { 143 | var newLists = []; 144 | // Merge all adjacent lists 145 | for (var i = 0; i < Math.floor(lists.length * 0.5); i++) { 146 | newLists.push(merge(lists[i* 2], lists[i * 2 + 1])); 147 | } 148 | 149 | // if we have an off number of lists, push the last one 150 | // to the end 151 | if (lists.length % 2) { 152 | newLists.push(lists[lists.length - 1]); 153 | } 154 | lists = newLists; 155 | } 156 | return lists[0]; 157 | } 158 | --------------------------------------------------------------------------------