├── .eslintrc ├── .gitignore ├── README.md ├── chapter01 ├── 1.1 - Is Unique │ └── isUnique.js ├── 1.2 - Check Perm │ └── checkPermute.js ├── 1.3 - URLify │ ├── urlify-2.js │ ├── urlify-3.js │ └── urlify.js ├── 1.4 - PalinPerm │ ├── palinPerm.js │ └── palinPerm2.js ├── 1.5 - OneAway │ └── oneAway.js ├── 1.6 - String Compression │ └── strComp.js ├── 1.7 - Rotate Matrix │ └── rotateMatrix.js ├── 1.8 - Zero Matrix │ └── zeroMatrix.js └── 1.9 - String Rotation │ └── stringRotation.js ├── chapter02 ├── 2.1 - Remove Dups │ └── removeDups.js ├── 2.2 - Return Kth to Last │ └── returnKthToLast.js ├── 2.3 - Delete Middle Node │ └── deleteMiddleNode.js ├── 2.4 - Partition │ └── partition.js ├── 2.5 - Sum Lists │ ├── sumLists - forward.js │ ├── sumLists - recursion.js │ └── sumLists.js ├── 2.6 - Palindrome │ └── palindrome.js ├── 2.7 - Intersection │ └── intersection.js ├── 2.8 - Loop Detection │ └── loopDetection.js └── util │ ├── LinkedList.js │ ├── LinkedListX.js │ └── printList.js ├── chapter03 ├── 3.1 - Three in One │ └── threeInOne.js ├── 3.2 - Stack Min │ └── stackMin.js ├── 3.3 - Stack of Plates │ └── stackOfPlates.js ├── 3.4 - Queue via Stacks │ └── queueViaStacks.js ├── 3.5 - Sort Stack │ └── sortStack.js ├── 3.6 - Animal Shelter │ └── animalShelter.js └── util │ ├── LinkedList.js │ ├── Queue.js │ └── Stack.js ├── chapter04 ├── 4.01 - Route Between Nodes │ └── routeBetweenNodes.js ├── 4.02 - Minimal Tree │ └── minimalTree.js ├── 4.03 - List of Depths │ └── listOfDepths.js ├── 4.04 - Check Balanced │ └── checkBalanced.js ├── 4.05 - Validate BST │ └── validateBST.js ├── 4.06 - Successor │ └── successor.js ├── 4.07 - Build Order │ └── buildOrder.js ├── 4.08 - First Common Ancestor │ └── firstCommonAncestor.js ├── 4.09 - BST Sequences │ └── bstSequences.js ├── 4.10 - Check Subtree │ └── checkSubtree.js ├── 4.11 - Random Node │ └── randomNode.js ├── 4.12 - Paths with Sum │ └── pathsWithSum.js └── util │ ├── BST.js │ ├── Graph.js │ ├── LinkedList.js │ ├── Queue.js │ ├── Stack.js │ └── Tree.js ├── chapter05 ├── 5.1 - Insertion │ └── insertion.js ├── 5.2 - Binary to String │ └── binaryToString.js ├── 5.3 - Flip Bit To Win │ └── flipBitToWin.js ├── 5.4 - Next Number │ └── nextNumber.js ├── 5.5 - Debugger │ └── debugger.js ├── 5.6 - Conversion │ └── conversion.js ├── 5.7 - Pairwise Swap │ └── pairwiseSwap.js └── 5.8 - Draw Line │ └── drawLine.js ├── chapter06 ├── 6.01 - Heavy Pill │ └── heavyPill.js ├── 6.02 - Basketball │ └── basketBall.js ├── 6.03 - Dominos │ └── dominos.js ├── 6.04 - Ants on a Triangle │ └── antsOnATriangle.js ├── 6.05 - Jugs of Water │ └── jugsOfWater.js ├── 6.06 - Blue-Eyed Island │ └── blueEyedIsland.js ├── 6.07 - The Apocalypse │ └── theApocalypse.js ├── 6.08 - The Egg Drop Problem │ └── eggDrop.js ├── 6.09 - 100 Lockers │ └── hundredLockers.js ├── 6.10 - Poison │ └── poison.js └── util │ └── notes.js ├── chapter07 ├── 7.01 - Deck of Cards │ └── deckOfCards.js ├── 7.02 - Call Center │ └── callCenter.js ├── 7.03 - Jukebox │ └── jukebox.js ├── 7.04 - Parking Lot │ └── parkingLot.js ├── 7.05 - Online Book Reader │ └── onlineBookReader.js ├── 7.06 - Jigsaw │ └── jigsaw.js ├── 7.07 - Chat Server │ └── chatServer.js ├── 7.08 - Othello │ └── othello.js ├── 7.09 - Circular Array │ └── circularArray.js ├── 7.10 - Minesweeper │ └── minesweeper.js ├── 7.11 - File System │ └── fileSystem.js ├── 7.12 - Hash Table │ └── hashTable.js └── util │ ├── BST.js │ ├── Graph.js │ ├── LinkedList.js │ ├── Queue.js │ ├── Stack.js │ ├── Tree.js │ └── notes.js ├── chapter08 ├── 8.01 - Triple Step │ ├── tripleStep.js │ └── tripleStepv2.js ├── 8.02 - Robot in a Grid │ └── robotInGrid.js ├── 8.03 - Magic Index │ └── magicIndex.js ├── 8.04 - Power Set │ └── powerSet.js ├── 8.05 - Recursive Multiply │ └── recursiveMultiply.js ├── 8.06 - Towers of Hanoi │ └── towersHanoi.js ├── 8.07 - Permutations without Dups │ └── permWithoutDups.js ├── 8.08 - Permutations with Dups │ └── permWithDups.js ├── 8.09 - Parens │ └── parens.js ├── 8.10 - Paint Fill │ └── paintFill.js ├── 8.11 - Coins │ └── coins.js ├── 8.12 - Eight Queens │ └── nQueens.js ├── 8.13 - Stack Boxes │ └── stackBoxes.js └── 8.14 - Boolean Eval │ └── booleanEval.js ├── chapter09 ├── 9.1 - Stock Data │ └── stockData.md ├── 9.2 - Social Network │ └── socialNetwork.md ├── 9.3 - Web Crawler │ └── webCrawler.md ├── 9.4 - Duplicate URLs │ └── duplicateURLs.md ├── 9.5 - Cache │ └── cache.md ├── 9.6 - Sales Rank │ └── salesRank.md ├── 9.7 - Personal Financial Manager │ └── personalFinancialManager.md ├── 9.8 - Paste Bin │ └── pasteBins.md └── systemDesignNotes.md ├── chapter10 ├── 10.01 - Sorted Merge │ └── sortedMerge.js ├── 10.02 - Group Anagrams │ └── groupAnagrams.js ├── 10.03 - Search In Rotated Array │ └── searchInRotatedArray.js ├── 10.04 - Sorted Search No Size │ ├── listy.js │ └── sortedSearchNoSize.js ├── 10.05 - Sparse Search │ └── sparseSearch.js ├── 10.06 - Sort Big File │ └── sortBigFile.js ├── 10.07 - Missing Int │ └── missingInt.js ├── 10.08 - Find Duplicates │ └── findDuplicates.js ├── 10.09 - Sorted Matrix Search │ └── sortedMatrixSearch.js ├── 10.10 - Rank From Stream │ └── rankFromStream.js ├── 10.11 - Peaks And Valleys │ └── peaksAndValleys.js └── sortingAlgos │ └── quickSort.js ├── lib └── data-structures │ └── chapter-2 │ ├── 2_1.js │ ├── 2_2.js │ ├── 2_3.js │ ├── 2_8.js │ └── helper.js ├── package.json └── test └── data-structures └── chapter-2 ├── 2_1_Spec.js ├── 2_2_Spec.js ├── 2_3_Spec.js └── 2_8_Spec.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "quotes": [2, 'single'], 4 | "semi": 2, 5 | "curly": 2, 6 | "camelcase": 0, 7 | "no-unused-vars": 2, 8 | "no-use-before-define": 2, 9 | "no-underscore-dangle": 2, 10 | "strict": 0, 11 | "no-multi-spaces": 2, 12 | "semi-spacing": 2, 13 | "no-shadow": 2 14 | }, 15 | 16 | "env": { 17 | "browser": true, 18 | "node": true, 19 | "mocha": true 20 | }, 21 | "globals": { 22 | "chrome": false 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | # Logs 4 | logs 5 | *.log 6 | 7 | # Mac specific 8 | DS_Store 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cracking the Coding Interview 6th Edition - JavaScript 2 | 3 | ## Instructions 4 | 5 | - This repository contains JavaScript attempts on questions listed in the book. 6 | 7 | - So far, code or short writeups are available for the first 10 chapters. 8 | 9 | - At Gayle's request, the files only contain code, and do not contain the actual questions. For the questions, please support Gayle by purchasing the [book](https://www.amazon.com/Cracking-Coding-Interview-Programming-Questions/dp/0984782850). 10 | 11 | - Under each JavaScript file, there are tests to check that the code is working as expected. For convenience, simple logging is used to test the functions. To run the tests, just cd into the folder containing the .js file and run `node .js` to run the tests. 12 | 13 | - Contributions welcome 14 | 15 | ## List of Coding Questions: 16 | 17 | #### Chapter 1 18 | - [x] 1.1 - Is Unique 19 | - [x] 1.2 - Check Permutation 20 | - [x] 1.3 - URLify 21 | - [x] 1.4 - Palindrome Permutation 22 | - [x] 1.5 - One Away 23 | - [x] 1.6 - String Compression 24 | - [x] 1.7 - Rotate Matrix 25 | - [x] 1.8 - Zero Matrix 26 | - [x] 1.9 - String Rotation 27 | 28 | #### Chapter 2 29 | - [x] 2.1 - Remove Dups 30 | - [x] 2.2 - Return Kth to Last 31 | - [x] 2.3 - Delete Middle Node 32 | - [x] 2.4 - Partition 33 | - [x] 2.5 - Sum Lists 34 | - [x] 2.6 - Palindrome 35 | - [x] 2.7 - Intersection 36 | - [x] 2.8 - Loop Detection 37 | 38 | #### Chapter 3 39 | - [x] 3.1 - Three in One 40 | - [x] 3.2 - Stack Min 41 | - [x] 3.3 - Stack of Plates 42 | - [x] 3.4 - Queue via Stacks 43 | - [x] 3.5 - Sort Stack 44 | - [x] 3.6 - Animal Shelter 45 | 46 | #### Chapter 4 47 | - [x] 4.1 - Route Between Nodes 48 | - [x] 4.2 - Minimal Tree 49 | - [x] 4.3 - List of Depths 50 | - [x] 4.4 - Check Balanced 51 | - [x] 4.5 - Validate BST 52 | - [x] 4.6 - Successor 53 | - [x] 4.7 - Build Order 54 | - [x] 4.8 - First Common Ancestor 55 | - [x] 4.9 - BST Sequences 56 | - [x] 4.10 - Check Subtree 57 | - [x] 4.11 - Random Node 58 | - [x] 4.12 - Paths with Sum 59 | 60 | #### Chapter 5 61 | - [x] 5.1 - Insertion 62 | - [x] 5.2 - Binary to String 63 | - [x] 5.3 - Flip Bit To Win 64 | - [x] 5.4 - Next Number 65 | - [x] 5.5 - Debugger 66 | - [x] 5.6 - Conversion 67 | - [x] 5.7 - Pairwise Swap 68 | - [x] 5.8 - Draw Line 69 | 70 | #### Chapter 6 71 | - [x] 6.1 - Heavy Pill 72 | - [x] 6.2 - Basketball 73 | - [x] 6.3 - Dominos 74 | - [x] 6.4 - Ants on a Triangle 75 | - [x] 6.5 - Jugs of Water 76 | - [x] 6.6 - Blue-Eyed Island 77 | - [x] 6.7 - The Apocalypse 78 | - [x] 6.8 - The Egg Drop Problem 79 | - [x] 6.9 - 100 Lockers 80 | - [x] 6.10 - Poison 81 | 82 | #### Chapter 7 83 | - [x] 7.1 - Deck of Cards 84 | - [x] 7.2 - Call Center 85 | - [x] 7.3 - Jukebox 86 | - [x] 7.4 - Parking Lot 87 | - [x] 7.5 - Online Book Reader 88 | - [x] 7.6 - Jigsaw 89 | - [x] 7.7 - Chat Server 90 | - [x] 7.8 - Othello 91 | - [x] 7.9 - Circular Array 92 | - [x] 7.10 - Minesweeper 93 | - [x] 7.11 - File System 94 | - [x] 7.12 - Hash Table 95 | 96 | #### Chapter 8 97 | - [x] 8.1 - Triple Step 98 | - [x] 8.2 - Robot in a Grid 99 | - [x] 8.3 - Magic Index 100 | - [x] 8.4 - Power Set 101 | - [x] 8.5 - Recursive Multiply 102 | - [x] 8.6 - Towers of Hanoi 103 | - [x] 8.7 - Permutations without Dups 104 | - [x] 8.8 - Permutations with Dups 105 | - [x] 8.9 - Parens 106 | - [x] 8.10 - Paint Fill 107 | - [x] 8.11 - Coins 108 | - [x] 8.12 - Eight Queens 109 | - [x] 8.13 - Stack Boxes 110 | - [x] 8.14 - Boolean Eval 111 | 112 | #### Chapter 9 113 | - [x] 9.1 - Stock Data 114 | - [x] 9.2 - Social Network 115 | - [x] 9.3 - Web Crawler 116 | - [x] 9.4 - Duplicate URLs 117 | - [x] 9.5 - Cache 118 | - [x] 9.6 - Sales Rank 119 | - [x] 9.7 - Personal Financial Manager 120 | - [x] 9.8 - Paste Bin 121 | 122 | #### Chapter 10 123 | - [x] 10.1 - Sorted Merge 124 | - [x] 10.2 - Group Anagrams 125 | - [x] 10.3 - Search In Rotated Array 126 | - [x] 10.4 - Sorted Search No Size 127 | - [x] 10.5 - Sparse Search 128 | - [x] 10.6 - Sort Big File 129 | - [x] 10.7 - Missing Int 130 | - [x] 10.8 - Find Duplicates 131 | - [x] 10.9 - Sorted Matrix Search 132 | - [x] 10.10 - Rank From Stream 133 | - [x] 10.11 - Peaks And Valleys 134 | 135 |
136 | 137 | ## Test 138 | 139 | ```sh 140 | npm test 141 | ``` 142 | 143 | -------------------------------------------------------------------------------- /chapter01/1.1 - Is Unique/isUnique.js: -------------------------------------------------------------------------------- 1 | var allUniqueChars = function(string) { 2 | 3 | // O(n^2) approach, no additional data structures used 4 | // for each character, check remaining characters for duplicates 5 | for (var i = 0; i < string.length; i++) { 6 | for (var j = i + 1; j < string.length; j++) { 7 | if (string[i] === string[j]) { 8 | return false; // if match, return false 9 | } 10 | } 11 | } 12 | return true; // if no match, return true 13 | }; 14 | 15 | const everyCharUnique = (str, indexOffset = 'a'.charCodeAt()) => { 16 | let counterTable = Number(); 17 | for(let index of [...str].map(c => c.charCodeAt() - indexOffset)) { 18 | const mask = 1 << index; 19 | if(counterTable & mask) 20 | return false; 21 | counterTable |= mask; 22 | } 23 | return true; 24 | }; 25 | 26 | function everyCharUnique(str) { 27 | let obj = {}; 28 | for (let i = 0; i < str.length; i++) { 29 | if (obj[str[i]] && obj[str[i]] >= 1) { 30 | return false; 31 | } else { 32 | obj[str[i]] = 1; 33 | } 34 | } 35 | return true; 36 | } 37 | 38 | /* TESTS */ 39 | console.log(everyCharUnique('abcd'), 'true'); 40 | console.log(everyCharUnique('abccd'), 'false'); 41 | console.log(everyCharUnique('bhjjb'), 'false'); 42 | console.log(everyCharUnique('mdjq'), 'true'); 43 | -------------------------------------------------------------------------------- /chapter01/1.2 - Check Perm/checkPermute.js: -------------------------------------------------------------------------------- 1 | var checkPermute = function(stringOne, stringTwo) { 2 | // if different lengths, return false 3 | if (stringOne.length !== stringTwo.length) { 4 | return false; 5 | // else sort and compare 6 | // (doesnt matter how it's sorted, as long as it's sorted the same way) 7 | } else { 8 | var sortedStringOne = stringOne.split('').sort().join(''); 9 | var sortedStringTwo = stringTwo.split('').sort().join(''); 10 | return sortedStringOne === sortedStringTwo; 11 | } 12 | }; 13 | 14 | // Tests 15 | console.log(checkPermute('aba', 'aab'), true); 16 | 17 | console.log(checkPermute('aba', 'aaba'), false); 18 | 19 | console.log(checkPermute('aba', 'aa'), false); -------------------------------------------------------------------------------- /chapter01/1.3 - URLify/urlify-2.js: -------------------------------------------------------------------------------- 1 | const replaceUrlSpaces = (str) => { 2 | const convertToArray = str.trim().split(''); 3 | for(let i in convertToArray) { 4 | if(convertToArray[i] === " ") { 5 | convertToArray[i] = "%20" 6 | } 7 | } 8 | return convertToArray.join(''); 9 | } 10 | 11 | console.log(replaceUrlSpaces("Sai Charan P")); 12 | -------------------------------------------------------------------------------- /chapter01/1.3 - URLify/urlify-3.js: -------------------------------------------------------------------------------- 1 | function urlify(str, len) { 2 | let s = ""; 3 | let totalSpaces = str.length - len; 4 | let frontSpaces = 0; 5 | let flag = false; 6 | for (let i = 0; i < str.length; i++) { 7 | if (flag === false) { 8 | if (str[i] === " ") frontSpaces++; 9 | else flag = true; 10 | } 11 | if (flag === true && i < str.length - (totalSpaces - frontSpaces)) { 12 | if (str[i] === " ") s += "%20"; 13 | else s += str[i]; 14 | } 15 | } 16 | 17 | return s; 18 | } 19 | 20 | console.log(urlify("Mr John Smith ", 13)); 21 | -------------------------------------------------------------------------------- /chapter01/1.3 - URLify/urlify.js: -------------------------------------------------------------------------------- 1 | var urlify = function(str, length) { 2 | // have a pointer to check from start to end 3 | var strArr = str.split(''); 4 | var pointer = 0; 5 | while (pointer < str.length) { 6 | if (strArr[pointer] === ' ') { 7 | // *** needs more work here, a little wierd 8 | // not handling trailing spaces properly 9 | for (var i = str.length - 1; i > pointer + 3; i--) { 10 | strArr[i] = str[i - 2]; 11 | } 12 | strArr[pointer] = '%'; 13 | strArr[pointer+1] = '2'; 14 | strArr[pointer+2] = '0'; 15 | console.log(strArr, strArr.length); 16 | } 17 | pointer++; 18 | } 19 | // if character is a space, move remainder chars by two 20 | // replace following three chars with '%20' 21 | return strArr.join(''); 22 | }; 23 | 24 | console.log(urlify('Mr John Smith ', 13), 'Mr%20John%20Smith'); 25 | -------------------------------------------------------------------------------- /chapter01/1.4 - PalinPerm/palinPerm.js: -------------------------------------------------------------------------------- 1 | var palinPerm = function(string) { 2 | // create object literal to store charcount 3 | var chars = {}; 4 | var currChar; 5 | var mulligan = false; 6 | var isPerm = true; 7 | // pump characters in, spaces not counted, all lowercase 8 | string.split('').forEach((char) => { 9 | if (char !== ' ') { 10 | currChar = char.toLowerCase(); 11 | if (chars[currChar] === undefined) { 12 | chars[currChar] = 0; 13 | } 14 | chars[currChar]++; 15 | } 16 | }); 17 | // check that all chars are even count, except for one exception 18 | Object.keys(chars).forEach((char) => { 19 | if (chars[char] % 2 > 0) { 20 | // if more than one exception, return false 21 | if (mulligan) { 22 | isPerm = false; // return in a forEach statment doesn't flow out of function scope 23 | } else { 24 | mulligan = true; 25 | } 26 | } 27 | }); 28 | // if not return true 29 | return isPerm; 30 | }; 31 | 32 | // TESTS 33 | console.log(palinPerm('Tact Coa'), 'true'); 34 | console.log(palinPerm('Tact boa'), 'false'); -------------------------------------------------------------------------------- /chapter01/1.4 - PalinPerm/palinPerm2.js: -------------------------------------------------------------------------------- 1 | function palinPerm(s) { 2 | const sanitized = s.toUpperCase().split(" ").join(""); 3 | const freq = new Map(); 4 | for (let i = 0; i < sanitized.length; i++) { 5 | const char = sanitized.charAt(i); 6 | const prevFreq = freq.get(char) || 0; 7 | freq.set(char, prevFreq + 1); 8 | } 9 | let oddCount = 0; 10 | for (let char of freq) { 11 | if (char[1] % 2 !== 0) { 12 | oddCount++; 13 | } 14 | } 15 | return oddCount < 2; 16 | } 17 | 18 | // TESTS 19 | console.log(palinPerm('Tact Coa'), 'true'); 20 | console.log(palinPerm('Tact boa'), 'false'); 21 | -------------------------------------------------------------------------------- /chapter01/1.5 - OneAway/oneAway.js: -------------------------------------------------------------------------------- 1 | var oneAway = function(string1, string2) { 2 | // insert a char for str1 -> remove a char for str2 3 | var checkOneMissing = function(first, second) { 4 | if (first.length !== second.length - 1) { 5 | return false; 6 | } else { 7 | var mulligan = false; 8 | var fP = 0; // first Pointer 9 | var sP = 0; // second Pointer 10 | while (fP < first.length) { 11 | if (first[fP] !== second[sP]) { 12 | if (mulligan) { 13 | return false; 14 | } else { 15 | mulligan = true; 16 | sP++; // second length is longer 17 | } 18 | } else { 19 | fP++; 20 | sP++; 21 | } 22 | } 23 | return true; 24 | } 25 | }; 26 | 27 | var checkOneDiff = function(first, second) { 28 | if (first.length !== second.length) { 29 | return false; 30 | } else { 31 | var mulligan = false; 32 | var fP = 0; // first Pointer 33 | var sP = 0; // second Pointer 34 | while (fP < first.length) { 35 | if (first[fP] !== second[sP]) { 36 | if (mulligan) { 37 | return false; // more than one mismatch 38 | } else { 39 | mulligan = true; // use up mulligan 40 | } 41 | } 42 | fP++; 43 | sP++; 44 | } 45 | return true; 46 | } 47 | }; 48 | // insert a char for str1 -> remove a char for str2 49 | // check one diff 50 | 51 | // console log checks 52 | // console.log(string1, string2, 'checkMiss', checkOneMissing(string1, string2)); 53 | // console.log(string2, string1, 'checkMiss', checkOneMissing(string2, string1)); 54 | // console.log(string1, string2, 'checkDiff', checkOneDiff(string1, string2)); 55 | 56 | return checkOneMissing(string1, string2) || checkOneMissing(string2, string1) || checkOneDiff(string1, string2); 57 | }; 58 | 59 | 60 | // Test 61 | console.log(oneAway('pale', 'ple'), true); 62 | console.log(oneAway('pales', 'pale'), true); 63 | console.log(oneAway('pale', 'bale'), true); 64 | console.log(oneAway('pale', 'bake'), false); -------------------------------------------------------------------------------- /chapter01/1.6 - String Compression/strComp.js: -------------------------------------------------------------------------------- 1 | var strComp = function(string) { 2 | var compressed = ''; 3 | var currChar = ''; 4 | var currCount = ''; 5 | var maxCount = 1; 6 | for (var i = 0; i < string.length; i++) { 7 | if (currChar !== string[i]) { 8 | console.log(currChar, string[i], i); 9 | compressed = compressed + currChar + currCount; 10 | maxCount = Math.max(maxCount, currCount); 11 | currChar = string[i]; 12 | currCount = 1; 13 | } else { 14 | currCount++; 15 | } 16 | } 17 | compressed = compressed + currChar + currCount; 18 | maxCount = Math.max(maxCount, currCount); 19 | 20 | return maxCount === 1 ? string : compressed; 21 | }; 22 | 23 | // Test 24 | console.log('aaaaaa', strComp('aaaaaa'), 'a6'); 25 | console.log('aabcccccaaa', strComp('aabcccccaaa'), 'a2b1c5a3'); -------------------------------------------------------------------------------- /chapter01/1.7 - Rotate Matrix/rotateMatrix.js: -------------------------------------------------------------------------------- 1 | var rotateMatrix = function(matrix) { 2 | var edge = matrix.length - 1; 3 | 4 | var movePixels = function(row, col) { 5 | // starts at m[row][col] 6 | // moves to m[col][edge - row] 7 | var fromRow; 8 | var fromCol; 9 | var fromPixel; 10 | 11 | // first transformation 12 | var toRow = row; // 0 13 | var toCol = col; // 1 14 | var toPixel = matrix[row][col]; 15 | 16 | // Do rotational transformation 4 times 17 | for (var i = 0; i < 4; i++) { 18 | fromRow = toRow; 19 | fromCol = toCol; 20 | toRow = fromCol; 21 | toCol = edge - fromRow; 22 | 23 | fromPixel = toPixel; 24 | toPixel = matrix[toRow][toCol]; 25 | matrix[toRow][toCol] = fromPixel; 26 | } 27 | }; 28 | 29 | for (var i = 0; i < matrix.length / 2; i++) { 30 | for (var j = i; j < edge - i; j++) { 31 | console.log(i, j); 32 | movePixels(i, j); 33 | } 34 | } 35 | }; 36 | 37 | 38 | /* TEST */ 39 | var testMatrix = [ 40 | [1, 2, 3, 4], 41 | [0, 1, 2, 3], 42 | [0, 0, 1, 2], 43 | [1, 0, 0, 1] 44 | ]; 45 | 46 | console.log('before:'); 47 | console.log(testMatrix[0]); 48 | console.log(testMatrix[1]); 49 | console.log(testMatrix[2]); 50 | console.log(testMatrix[3]); 51 | 52 | rotateMatrix(testMatrix); 53 | 54 | console.log('after:'); 55 | console.log(testMatrix[0]); 56 | console.log(testMatrix[1]); 57 | console.log(testMatrix[2]); 58 | console.log(testMatrix[3]); 59 | 60 | /* 61 | var edge = n - 1; 62 | 63 | pattern observed: 64 | i) col value becomes row value 65 | ii) row value is subtracted off edge and becomes col value 66 | 67 | 0100 68 | 0000 69 | 0000 70 | 0000 71 | 72 | position of 1 -> m[0][1] 73 | 74 | 0000 75 | 0001 76 | 0000 77 | 0000 78 | 79 | position of 1 -> m[1][edge] 80 | 81 | 0000 82 | 0000 83 | 0000 84 | 0010 85 | 86 | position of 1 -> m[edge][edge - 1] 87 | 88 | 0000 89 | 0000 90 | 1000 91 | 0000 92 | 93 | position of 1 -> m[edge-1][0] 94 | 95 | 0100 96 | 0000 97 | 0000 98 | 0000 99 | 100 | position of 1 -> m[0][1] 101 | 102 | flow of iteration: 103 | i) start from top left corner and move diagonally down 104 | ii) for each row, iterate pixels until edge - 1 105 | (pixel at edge would have been transformed by the first pixel) 106 | iii) at each pixel iteration, iterate through 4 sides 107 | iv) do iteration in place, i.e. store a temp pixel for moving things around 108 | */ -------------------------------------------------------------------------------- /chapter01/1.8 - Zero Matrix/zeroMatrix.js: -------------------------------------------------------------------------------- 1 | /* Helper Functions */ 2 | var checkZeros = function(matrix) { 3 | var matrixHeight = matrix.length; 4 | var matrixWidth = matrix[0].length; 5 | var rowsToZeroify = {}; // use hashtables to remove duplicates 6 | var colsToZeroify = {}; 7 | 8 | for (var i = 0; i < matrixHeight; i++) { 9 | for (var j = 0; j < matrixWidth; j++) { 10 | if (matrix[i][j] === 0) { 11 | rowsToZeroify[i] = true; 12 | colsToZeroify[j] = true; 13 | } 14 | } 15 | } 16 | return { 17 | rowsToZeroify: rowsToZeroify, 18 | colsToZeroify: colsToZeroify 19 | }; 20 | }; 21 | 22 | var printMatrix = function(matrix) { 23 | for (var i = 0; i < matrix.length; i++) { 24 | console.log(matrix[i]); 25 | } 26 | }; 27 | 28 | var zeroifyCol = function(matrix, col) { 29 | for (var i = 0; i < matrix.length; i++) { 30 | matrix[i][col] = 0; 31 | } 32 | }; 33 | 34 | var zeroifyCols = function(matrix, zeroScan) { 35 | for (var col in zeroScan.colsToZeroify) { 36 | zeroifyCol(matrix, Number(col)); 37 | } 38 | }; 39 | 40 | var zeroifyRow = function(matrix, row) { 41 | for (var i = 0; i < matrix[row].length; i++) { 42 | matrix[row][i] = 0; 43 | } 44 | }; 45 | 46 | var zeroifyRows = function(matrix, zeroScan) { 47 | for (var row in zeroScan.rowsToZeroify) { 48 | zeroifyRow(matrix, Number(row)); 49 | } 50 | }; 51 | 52 | /* Main Function */ 53 | var zeroMatrix = function(matrix) { 54 | 55 | if(matrix.length === 0) { return; } 56 | 57 | var zeroScan = checkZeros(matrix); 58 | 59 | zeroifyCols(matrix, zeroScan); 60 | zeroifyRows(matrix, zeroScan); 61 | 62 | }; 63 | 64 | // Testing 65 | var testMatrix = [ 66 | [1, 1, 1, 1], 67 | [1, 1, 1, 1], 68 | [1, 0, 1, 1], 69 | [1, 1, 1, 1], 70 | [1, 1, 1, 1], 71 | [1, 1, 1, 1] 72 | ]; 73 | 74 | console.log('before'); 75 | printMatrix(testMatrix); 76 | 77 | zeroMatrix(testMatrix); 78 | 79 | console.log('after'); 80 | printMatrix(testMatrix); 81 | 82 | // function should mutate the matrix to the following: 83 | // [ 84 | // [1, 0, 1, 1], 85 | // [1, 0, 1, 1], 86 | // [0, 0, 0, 0], 87 | // [1, 0, 1, 1], 88 | // [1, 0, 1, 1], 89 | // [1, 0, 1, 1] 90 | // ] -------------------------------------------------------------------------------- /chapter01/1.9 - String Rotation/stringRotation.js: -------------------------------------------------------------------------------- 1 | var stringRotation = function(string1, string2) { 2 | if (string1.length !== string2.length) { 3 | return false; 4 | } 5 | return (string2 + string2).includes(string1); // one call of isSubString 6 | }; 7 | 8 | // Approaches: 9 | // a) sorting chars before comparing -> know if it is permutation but not know if it is in the right order 10 | // b) look for starting character before moving around and rotating -> starting characters might repeat 11 | // c) break string 2 into a front and back, and ensure that front tallies with isSubstring before doing isSubstring 12 | 13 | // Test 14 | console.log(stringRotation('waterbottle', 'erbottlewat'), true); 15 | console.log(stringRotation('waterbottle', 'erbotlewatt'), false); 16 | console.log(stringRotation('aaata', 'aataa'), true); 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /chapter02/2.1 - Remove Dups/removeDups.js: -------------------------------------------------------------------------------- 1 | // remiveDups redone 2 | const LinkedList = require("../util/LinkedListX"); 3 | 4 | function removeDuplicates(list) { 5 | const _set = new Set(); 6 | let cur = list.head; 7 | let prev = null; 8 | while (cur) { 9 | if (_set.has(cur.value)) { 10 | // duplicate found 11 | // de-link it from the list 12 | // cur jumps next but previous stays 13 | // right behind cur (as always) 14 | let elem = cur; 15 | prev.next = cur.next; 16 | cur = cur.next; 17 | elem.next = null; 18 | } 19 | else { 20 | // add to the set 21 | _set.add(cur.value); 22 | prev = cur; 23 | cur = cur.next; 24 | } 25 | } 26 | 27 | return list; 28 | } 29 | 30 | // quick test 31 | let list = new LinkedList(); 32 | for (let elem of [1, 5, 1, 6, 8, 6, 8, 8, 8, 8]) { 33 | list.append(elem); 34 | } 35 | 36 | removeDuplicates(list); 37 | 38 | console.log(list._toArray()); // [1, 5, 6, 8] 39 | -------------------------------------------------------------------------------- /chapter02/2.2 - Return Kth to Last/returnKthToLast.js: -------------------------------------------------------------------------------- 1 | const LinkedList = require("../util/LinkedListX"); 2 | 3 | var findKthToLast = function(k, list) { 4 | // do iteratively 5 | //define two pointers , fast and slow pointer 6 | let fast = list.head 7 | let slow = list.head 8 | 9 | //Move fast pointer k steps in the linkedlist while slow remains at head 10 | for(let i=0;i 5 -> 8 -> 5 -> 10 -> 2 -> 1 [partition = 5] 56 | // Output: 3 -> 2 -> 1 -> 5 -> 5 -> 8 -> 10 57 | 58 | var printList = function(a) { 59 | while (a !== null) { 60 | console.log(a.value); 61 | a = a.next; 62 | } 63 | }; 64 | 65 | var a = new LinkedList(3); 66 | var b = new LinkedList(5); 67 | var c = new LinkedList(8); 68 | var d = new LinkedList(5); 69 | var e = new LinkedList(10); 70 | var f = new LinkedList(2); 71 | var g = new LinkedList(1); 72 | 73 | a.next = b; 74 | b.next = c; 75 | c.next = d; 76 | d.next = e; 77 | e.next = f; 78 | f.next = g; 79 | 80 | var newa = partition(a, 5); 81 | printList(newa); 82 | -------------------------------------------------------------------------------- /chapter02/2.5 - Sum Lists/sumLists - forward.js: -------------------------------------------------------------------------------- 1 | const LinkedList = require('./../util/LinkedList') 2 | const printList = require('./../util/printList') 3 | 4 | function sumLinkedListsForward(list1, list2) { 5 | if (!list1 && !list2) { 6 | return null 7 | } 8 | let length1 = length(list1) 9 | let length2 = length(list2) 10 | 11 | if (length1 > length2) { 12 | list2 = padList(list2, length1 - length2) 13 | } else if (length1 < length2) { 14 | list1 = padList(list1, length2 - length1) 15 | } 16 | 17 | const { head, nextDigitValue } = carryBase10(sumAndAppendNodes(list1, list2), 0) 18 | return nextDigitValue ? appendToStart(head, new LinkedList(nextDigitValue)) : head 19 | } 20 | 21 | function length(node) { 22 | let count = 0 23 | while (node) { 24 | count++ 25 | node = node.next 26 | } 27 | return count 28 | } 29 | 30 | function padList(shortList, padding) { 31 | while (padding > 0) { 32 | shortList = appendToStart(shortList, new LinkedList(0)) 33 | padding-- 34 | } 35 | return shortList 36 | } 37 | 38 | function appendToStart(head, node) { 39 | node.next = head 40 | return node 41 | } 42 | 43 | function sumAndAppendNodes(node1, node2) { 44 | let value = (node1 ? node1.value : 0) + (node2 ? node2.value : 0) 45 | if (!node1.next && !node2.next) { 46 | return new LinkedList(value) 47 | } 48 | const { 49 | head, 50 | nextDigitValue 51 | } = carryBase10(sumAndAppendNodes(node1.next, node2.next), value) 52 | return appendToStart(head, new LinkedList(nextDigitValue)) 53 | } 54 | 55 | function carryBase10(head, nextDigitValue) { 56 | if (head.value >= 10) { 57 | head.value = head.value % 10 58 | nextDigitValue += 1 59 | } 60 | return { 61 | head, 62 | nextDigitValue 63 | } 64 | } 65 | 66 | // Input: (6 -> 1 -> 7) + (2 -> 9 -> 5). this case refers to 617 + 295 67 | // Output: 9 -> 1 -> 2. the answer refers to 912 68 | 69 | var a = new LinkedList(6) 70 | var b = new LinkedList(1) 71 | var c = new LinkedList(7) 72 | 73 | a.next = b 74 | b.next = c 75 | 76 | var d = new LinkedList(2) 77 | var e = new LinkedList(9) 78 | var f = new LinkedList(5) 79 | 80 | d.next = e 81 | e.next = f 82 | 83 | printList(sumLinkedListsForward(a, d)) 84 | -------------------------------------------------------------------------------- /chapter02/2.5 - Sum Lists/sumLists - recursion.js: -------------------------------------------------------------------------------- 1 | const LinkedList = require('./../util/LinkedList') 2 | const printList = require('./../util/printList') 3 | 4 | function sumLinkedLists(node1, node2, carry=0){ 5 | if(!node1 && !node2 && carry===0){ 6 | return null 7 | } 8 | let value = carry 9 | value += node1 ? node1.value : 0 10 | value += node2 ? node2.value : 0 11 | const node = new LinkedList(value%10) 12 | node.next = sumLinkedLists( 13 | node1 ? node1.next : null, 14 | node2 ? node2.next : null, 15 | value > 10 ? 1 : 0) 16 | return node 17 | } 18 | 19 | // Input: (7 -> 1 -> 6) + (5 -> 9 -> 2). this case refers to 617 + 295 20 | // Output: 2 -> 1 -> 9. the answer refers to 912 21 | 22 | var a = new LinkedList(7) 23 | var b = new LinkedList(1) 24 | var c = new LinkedList(6) 25 | 26 | a.next = b 27 | b.next = c 28 | 29 | var d = new LinkedList(5) 30 | var e = new LinkedList(9) 31 | var f = new LinkedList(2) 32 | 33 | d.next = e 34 | e.next = f 35 | 36 | printList(sumLinkedLists(a,d)) 37 | -------------------------------------------------------------------------------- /chapter02/2.5 - Sum Lists/sumLists.js: -------------------------------------------------------------------------------- 1 | var LinkedList = require('./../util/LinkedList'); 2 | var printList = require('./../util/printList'); 3 | 4 | var sumList = function(head1, head2) { 5 | 6 | var node1 = head1; 7 | var node2 = head2; 8 | var node3 = null; 9 | var head3 = null; 10 | 11 | var ones; 12 | var tens = 0; 13 | var sum; 14 | 15 | while (node1 !== null && node2 !== null) { 16 | if (node1 === null) { 17 | sum = node2.value; 18 | } else if (node2 === null) { 19 | sum = node1.value; 20 | } else { 21 | sum = node1.value + node2.value; 22 | } 23 | 24 | sum += tens; 25 | ones = sum % 10; 26 | if (node3 === null) { 27 | head3 = new LinkedList(ones); 28 | node3 = head3; 29 | } else { 30 | node3.next = new LinkedList(ones); 31 | node3 = node3.next; 32 | } 33 | 34 | tens = Math.floor(sum / 10); 35 | 36 | if (node1 !== null) { 37 | node1 = node1.next; 38 | } 39 | 40 | if (node2 !== null) { 41 | node2 = node2.next; 42 | } 43 | 44 | } 45 | if (tens > 0) { 46 | node3.next = new LinkedList(tens); 47 | node3 = node3.next; 48 | } 49 | 50 | return head3; 51 | }; 52 | 53 | /* TEST */ 54 | 55 | // Input: (7 -> 1 -> 6) + (5 -> 9 -> 2). this case refers to 617 + 295 56 | // Output: 2 -> 1 -> 9. the answer refers to 912 57 | 58 | var a = new LinkedList(7); 59 | var b = new LinkedList(1); 60 | var c = new LinkedList(6); 61 | 62 | a.next = b; 63 | b.next = c; 64 | 65 | var d = new LinkedList(5); 66 | var e = new LinkedList(9); 67 | var f = new LinkedList(2); 68 | 69 | d.next = e; 70 | e.next = f; 71 | 72 | var newHead = sumList(a, d); 73 | 74 | printList(newHead); 75 | 76 | // Input: (7 -> 1 -> 6) + (5 -> 9 -> 9). this case refers to 617 + 995 77 | // Output: 2 -> 1 -> 9. the answer refers to 1612 78 | 79 | var a = new LinkedList(7); 80 | var b = new LinkedList(1); 81 | var c = new LinkedList(6); 82 | 83 | a.next = b; 84 | b.next = c; 85 | 86 | var d = new LinkedList(5); 87 | var e = new LinkedList(9); 88 | var f = new LinkedList(2); 89 | 90 | d.next = e; 91 | e.next = f; 92 | 93 | var newHead = sumList(a, d); 94 | 95 | printList(newHead); 96 | -------------------------------------------------------------------------------- /chapter02/2.6 - Palindrome/palindrome.js: -------------------------------------------------------------------------------- 1 | // if doubly - move to middle, and then extend front and back to check 2 | // if singly - make a reversed linkedlist from the first half 3 | 4 | var LinkedList = require('./../util/LinkedList'); 5 | 6 | var palindrome = function(head) { 7 | var mid = head; 8 | var end = head; 9 | var isEven = true; 10 | var firstHalf = null; 11 | var frontNode = null; 12 | 13 | while (end.next !== null) { 14 | isEven = true; 15 | if (firstHalf === null) { 16 | firstHalf = new LinkedList(mid.value); 17 | } else { 18 | frontNode = firstHalf; 19 | firstHalf = new LinkedList(mid.value); 20 | firstHalf.next = frontNode; 21 | } 22 | mid = mid.next; 23 | end = end.next; 24 | if (end.next !== null) { 25 | end = end.next; 26 | isEven = false; 27 | } 28 | } 29 | 30 | if (!isEven) { 31 | mid = mid.next; 32 | } 33 | 34 | while(mid !== null) { 35 | // console.log(mid.value, firstHalf.value); 36 | if (mid.value !== firstHalf.value) { 37 | return false; 38 | } 39 | mid = mid.next; 40 | if (firstHalf!== null) { 41 | firstHalf = firstHalf.next; 42 | } 43 | } 44 | return true; 45 | 46 | }; 47 | 48 | /* TEST */ 49 | 50 | var a = new LinkedList('a'); 51 | var b = new LinkedList('b'); 52 | var c = new LinkedList('c'); 53 | var d = new LinkedList('d'); 54 | var e = new LinkedList('c'); 55 | var f = new LinkedList('b'); 56 | var g = new LinkedList('a'); 57 | 58 | a.next = b; 59 | b.next = c; 60 | c.next = d; 61 | d.next = e; 62 | e.next = f; 63 | f.next = g; 64 | 65 | console.log(palindrome(a)); 66 | -------------------------------------------------------------------------------- /chapter02/2.7 - Intersection/intersection.js: -------------------------------------------------------------------------------- 1 | var LinkedList = require('./../util/LinkedList'); 2 | 3 | var peek = function(stack) { 4 | return stack[stack.length - 1]; 5 | }; 6 | 7 | var intersection = function(head1, head2) { 8 | var stack1 = []; 9 | var stack2 = []; 10 | 11 | while (head1 !== null) { 12 | stack1.push(head1); 13 | head1 = head1.next; 14 | } 15 | 16 | while (head2 !== null) { 17 | stack2.push(head2); 18 | head2 = head2.next; 19 | } 20 | 21 | // if not intersecting return undefined. 22 | if (stack1.length === 0 || stack2.length === 0) { 23 | return undefined; 24 | } else if (peek(stack1) !== peek(stack2)) { 25 | return undefined; 26 | } else { 27 | var intersect; 28 | while (peek(stack1) === peek(stack2)) { 29 | intersect = peek(stack1); 30 | stack1.pop(); 31 | stack2.pop(); 32 | } 33 | return intersect; 34 | } 35 | // if intersecting, return intersecting node. 36 | }; 37 | 38 | /* TEST */ 39 | 40 | var a = new LinkedList('a'); 41 | var b = new LinkedList('b'); 42 | var c = new LinkedList('c'); 43 | var d = new LinkedList('d'); 44 | var e = new LinkedList('e'); 45 | var f = new LinkedList('f'); 46 | var g = new LinkedList('g'); 47 | var h = new LinkedList('h'); 48 | 49 | a.next = b; 50 | b.next = c; 51 | c.next = d; 52 | d.next = e; 53 | e.next = f; 54 | f.next = g; 55 | g.next = h; 56 | 57 | var a1 = new LinkedList('a1'); 58 | var b1 = new LinkedList('b1'); 59 | var c1 = new LinkedList('c1'); 60 | 61 | a1.next = b1; 62 | b1.next = c1; 63 | c1.next = d; 64 | 65 | var intersectNode = intersection(a, a1); 66 | 67 | console.log(intersectNode.value); -------------------------------------------------------------------------------- /chapter02/2.8 - Loop Detection/loopDetection.js: -------------------------------------------------------------------------------- 1 | var LinkedList = require('./../util/LinkedList'); 2 | 3 | var loopDetection = (head) => { 4 | // The null checking code will handle lists with no loops. 5 | if (!head || !head.next) return null 6 | 7 | var hare = head 8 | var tortoise = head 9 | 10 | do { 11 | hare = hare.next 12 | tortoise = tortoise.next 13 | if (!hare || !hare.next) return null 14 | hare = hare.next 15 | } while (hare !== tortoise) 16 | 17 | tortoise = head 18 | 19 | while (hare !== tortoise) { 20 | hare = hare.next 21 | tortoise = tortoise.next 22 | } 23 | 24 | return hare 25 | }; 26 | 27 | /* TEST */ 28 | // A -> B -> C -> D -> E -> C 29 | 30 | var a = new LinkedList(); 31 | var b = new LinkedList(); 32 | var c = new LinkedList(); 33 | var d = new LinkedList(); 34 | var e = new LinkedList(); 35 | var f = new LinkedList(); 36 | 37 | a.next = b; 38 | b.next = c; 39 | c.next = d; 40 | d.next = e; 41 | e.next = f; 42 | f.next = c; 43 | 44 | console.log(loopDetection(a) === c, true); 45 | 46 | var A = new LinkedList(); 47 | var B = new LinkedList(); 48 | var C = new LinkedList(); 49 | var D = new LinkedList(); 50 | var E = new LinkedList(); 51 | var F = new LinkedList(); 52 | 53 | A.next = B; 54 | B.next = C; 55 | C.next = D; 56 | D.next = E; 57 | E.next = F; 58 | 59 | console.log(loopDetection(A) === null, false); 60 | -------------------------------------------------------------------------------- /chapter02/util/LinkedList.js: -------------------------------------------------------------------------------- 1 | module.exports = function(value) { 2 | this.value = value; 3 | this.next = null; 4 | }; -------------------------------------------------------------------------------- /chapter02/util/LinkedListX.js: -------------------------------------------------------------------------------- 1 | // a new implementation of the linked list 2 | /* 3 | ADT: 4 | # Main operations 5 | prepend(value) -> Add a node in the beginning 6 | append(value) -> Add a node in the end 7 | pop() -> Remove a node from the end 8 | popFirst() -> Remove a node from the beginning 9 | head() -> Return the first node 10 | tail() -> Return the last node 11 | remove(Node)* -> Remove Node from the list 12 | */ 13 | 14 | // NOTE: no type-safety 15 | 16 | class Node { 17 | constructor(value) { 18 | this.value = value 19 | this.next = null; 20 | } 21 | } 22 | 23 | class LinkedList { 24 | constructor() { 25 | this.head = null; 26 | this.tail = null; 27 | } 28 | 29 | append(value) { 30 | let node = new Node(value); 31 | // if list is empty 32 | if (!this.head) { 33 | this.head = node; 34 | this.tail = node; 35 | } 36 | else { 37 | this.tail.next = node; 38 | this.tail = node; 39 | } 40 | } 41 | 42 | prepend(value) { 43 | let node = new Node(value); 44 | node.next = this.head; 45 | this.head = node; 46 | } 47 | 48 | pop() { 49 | let cur = this.head; 50 | 51 | // only one or no item exists 52 | if (!cur) return null; 53 | if (!cur.next) { 54 | this.head = null; 55 | return cur; 56 | } 57 | // move till the 2nd last 58 | while (cur.next.next) 59 | cur = cur.next; 60 | 61 | let last = this.tail; 62 | this.tail = cur; 63 | this.tail.next = null; 64 | return last; 65 | } 66 | 67 | popFirst() { 68 | let first = this.head; 69 | if (this.head && this.head.next) { 70 | this.head = this.head.next; 71 | first.next = null; 72 | } 73 | else this.head = null; 74 | return first; 75 | } 76 | 77 | head() { 78 | return this.head; 79 | } 80 | 81 | removeAt(index) { 82 | let i = 0; 83 | let cur = this.head; 84 | let prev = null; 85 | 86 | while (cur != null) { 87 | if (i == index) { 88 | // remove 89 | if (prev == null) 90 | this.head = cur.next; 91 | else prev.next = cur.next; 92 | cur.next = null; 93 | return cur.value; 94 | } 95 | else { 96 | prev = cur; 97 | cur = cur.next; 98 | i++; 99 | } 100 | } 101 | return null; 102 | } 103 | 104 | insertAt(index, value) { 105 | if (index == 0) return this.prepend(value); 106 | let cur = this.head; 107 | let i = 0; 108 | 109 | while (cur != null) { 110 | if (i == index - 1) { 111 | let node = new Node(value); 112 | node.next = cur.next; 113 | cur.next = node; 114 | return true; 115 | } 116 | else { 117 | i++; 118 | cur = cur.next; 119 | } 120 | } 121 | return false; 122 | } 123 | 124 | tail() { 125 | return this.tail; 126 | } 127 | 128 | _toArray() { 129 | let arr = []; 130 | let cur = this.head; 131 | while (cur) { 132 | arr.push(cur.value); 133 | cur = cur.next; 134 | } 135 | 136 | return arr; 137 | } 138 | } 139 | 140 | module.exports = LinkedList; 141 | 142 | /* TEST */ 143 | 144 | // let l = new LinkedList(); 145 | // l.append(3); 146 | // l.append(4); 147 | // l.append(10); 148 | // l.append(20); 149 | // l.append(5); 150 | 151 | // console.log(l.removeAt(1), 4); 152 | // console.log(l.pop().value, 5); 153 | 154 | // console.log(l._toArray()); 155 | // l.insertAt(2, 40); 156 | // console.log(l._toArray()); 157 | -------------------------------------------------------------------------------- /chapter02/util/printList.js: -------------------------------------------------------------------------------- 1 | module.exports = function(head) { 2 | while (head !== null) { 3 | console.log(head.value); 4 | head = head.next; 5 | } 6 | }; -------------------------------------------------------------------------------- /chapter03/3.1 - Three in One/threeInOne.js: -------------------------------------------------------------------------------- 1 | var ThreeInOne = function() { 2 | this.container = []; 3 | this.middleBottom = 0; 4 | this.middleTop = 0; 5 | }; 6 | 7 | ThreeInOne.prototype.push1 = function(value) { 8 | this.container.unshift(value); 9 | this.middleBottom++; 10 | this.middleTop++; 11 | }; 12 | 13 | ThreeInOne.prototype.push2 = function(value) { 14 | this.container.splice(this.middleTop, 0, value); 15 | this.middleTop++; 16 | }; 17 | 18 | ThreeInOne.prototype.push3 = function(value) { 19 | this.container.push(value); 20 | }; 21 | 22 | ThreeInOne.prototype.pop1 = function() { 23 | if (this.isEmpty1()) { 24 | return undefined; 25 | } 26 | var answer = this.container.shift(); 27 | if (this.middleBottom > 0) { 28 | this.middleBottom--; 29 | this.middleTop--; 30 | } 31 | return answer; 32 | }; 33 | 34 | ThreeInOne.prototype.pop2 = function() { 35 | if (this.isEmpty2()) { 36 | return undefined; 37 | } 38 | 39 | var answer = this.container[this.middleTop - 1]; 40 | this.container.splice(this.middleTop - 1, 1); 41 | if (this.middleBottom < this.middleTop) { 42 | this.middleTop--; 43 | } 44 | return answer; 45 | }; 46 | 47 | ThreeInOne.prototype.pop3 = function() { 48 | if (this.isEmpty3()) { 49 | return undefined; 50 | } 51 | 52 | return this.container.pop(); 53 | }; 54 | 55 | ThreeInOne.prototype.peek1 = function() { 56 | return this.isEmpty1() ? 57 | undefined : this.container[0]; 58 | }; 59 | 60 | ThreeInOne.prototype.peek2 = function() { 61 | return this.isEmpty2() ? 62 | undefined : this.container[this.middleTop - 1]; 63 | }; 64 | 65 | ThreeInOne.prototype.peek3 = function() { 66 | return this.isEmpty3() ? 67 | undefined : this.container[this.container.length - 1]; 68 | }; 69 | 70 | ThreeInOne.prototype.isEmpty1 = function() { 71 | return this.middleBottom === 0; 72 | }; 73 | 74 | ThreeInOne.prototype.isEmpty2 = function() { 75 | return this.middleBottom === this.middleTop; 76 | }; 77 | 78 | ThreeInOne.prototype.isEmpty3 = function() { 79 | return this.middleTop === this.container.length; 80 | }; 81 | 82 | /* TESTS */ 83 | 84 | var t = new ThreeInOne(); 85 | t.push1('1a'); 86 | t.push1('1b'); 87 | t.push1('1c'); 88 | t.push2('2a'); 89 | t.push2('2b'); 90 | t.push2('2c'); 91 | t.push3('3a'); 92 | t.push3('3b'); 93 | t.push3('3c'); 94 | 95 | var a1 = t.pop1(); 96 | var a2 = t.pop2(); 97 | var a3 = t.pop3(); 98 | 99 | var peek1 = t.peek1(); 100 | var peek2 = t.peek2(); 101 | var peek3 = t.peek3(); 102 | 103 | var b1 = t.pop1(); 104 | var b2 = t.pop2(); 105 | var b3 = t.pop3(); 106 | 107 | var isEmptya1 = t.isEmpty1(); 108 | var isEmptya2 = t.isEmpty2(); 109 | var isEmptya3 = t.isEmpty3(); 110 | 111 | var c1 = t.pop1(); 112 | var c2 = t.pop2(); 113 | var c3 = t.pop3(); 114 | 115 | var d1 = t.pop1(); 116 | var d2 = t.pop2(); 117 | var d3 = t.pop3(); 118 | 119 | var isEmptyb1 = t.isEmpty1(); 120 | var isEmptyb2 = t.isEmpty2(); 121 | var isEmptyb3 = t.isEmpty3(); 122 | 123 | console.log(t.container, t.middleBottom, t.middleTop); 124 | console.log(a1, a2, a3); 125 | console.log(peek1, peek2, peek3); 126 | console.log(b1, b2, b3); 127 | console.log(isEmptya1, isEmptya2, isEmptya3); 128 | console.log(c1, c2, c3); 129 | console.log(d1, d2, d3); 130 | console.log(isEmptyb1, isEmptyb2, isEmptyb3); 131 | -------------------------------------------------------------------------------- /chapter03/3.2 - Stack Min/stackMin.js: -------------------------------------------------------------------------------- 1 | var Stack = require('./../util/Stack'); 2 | 3 | // Approach, keep an additional stack that keeps the mins 4 | 5 | class StackMin extends Stack { 6 | constructor() { 7 | super(); 8 | // additional stack to track the mins 9 | this._minStack = new Stack(); 10 | this._min = null; 11 | } 12 | 13 | push(value) { 14 | super.push(value); 15 | if (this._min == null || value <= this._min) { 16 | this._min = value; 17 | this._minStack.push(value); 18 | } 19 | } 20 | 21 | pop() { 22 | let value = super.pop(); 23 | if (value == this._minStack.peek()) { 24 | this._minStack.pop(); 25 | this._min = this._minStack.peek(); 26 | } 27 | return value; 28 | } 29 | 30 | min() { 31 | return this._min; 32 | } 33 | } 34 | 35 | /* TEST */ 36 | 37 | var s = new StackMin(); 38 | s.push(9); 39 | s.push(8); 40 | s.push(1); 41 | s.push(2); 42 | s.push(1); 43 | s.push(9); 44 | 45 | console.log(s.min(), 1); 46 | s.pop(); 47 | s.pop(); 48 | console.log(s.peek(), 2); 49 | console.log(s.min(), 1); 50 | s.pop(); 51 | s.pop(); 52 | console.log(s.peek(), 8); 53 | console.log(s.min(), 8); 54 | s.pop(); 55 | s.pop(); 56 | console.log(s.isEmpty(), true); 57 | console.log(s.min(), null); 58 | -------------------------------------------------------------------------------- /chapter03/3.3 - Stack of Plates/stackOfPlates.js: -------------------------------------------------------------------------------- 1 | // implement as array of stacks 2 | const Stack = require("../util/Stack"); 3 | 4 | class SetOfStacks { 5 | constructor(capacity) { 6 | this.capacity = capacity; 7 | this.stackSet = []; 8 | } 9 | 10 | getLastStack() { 11 | return this.stackSet[this.stackSet.length - 1]; 12 | } 13 | 14 | push(value) { 15 | let last = this.getLastStack(); 16 | if (this.stackSet.length === 0 || last.size() === this.capacity) { 17 | var newStack = new Stack(); 18 | newStack.push(value); 19 | this.stackSet.push(newStack); 20 | } else { 21 | last.push(value); 22 | } 23 | } 24 | 25 | pop() { 26 | if (this.stackSet.length === 0) { 27 | return undefined; 28 | } 29 | let last = this.getLastStack(); 30 | let value = last.pop(); 31 | if (last.size() === 0) { 32 | this.stackSet.pop(); 33 | } 34 | return value; 35 | } 36 | 37 | peek() { 38 | let last = this.getLastStack(); 39 | return last.peek(); 40 | } 41 | 42 | isEmpty() { 43 | return this.stackSet.length === 0; 44 | } 45 | 46 | popAt(index) { 47 | // out of range index 48 | if (index < 0 || index >= this.stackSet.length) return false; 49 | let value = this.stackSet[index].pop(); 50 | if (this.stackSet[index].size() == 0) { 51 | // clear the stack from the set 52 | this.stackSet.splice(index, 1); 53 | } 54 | return value; 55 | } 56 | } 57 | 58 | /* TESTS */ 59 | 60 | var s = new SetOfStacks(3); 61 | s.push(1); 62 | s.push(2); 63 | s.push(3); 64 | s.push(4); 65 | s.push(5); 66 | s.push(6); 67 | s.push(7); 68 | s.push(8); 69 | s.push(9); 70 | s.push(10); 71 | s.push(11); 72 | s.push(12); 73 | s.push(13); 74 | s.push(14); 75 | 76 | console.log(s.peek(), 14); 77 | 78 | s.popAt(2); 79 | s.popAt(2); 80 | s.popAt(2); 81 | 82 | console.log(s.peek(), 14); 83 | 84 | s.pop(); 85 | s.pop(); 86 | s.pop(); 87 | s.pop(); 88 | s.pop(); 89 | s.pop(); 90 | s.pop(); 91 | s.pop(); 92 | s.pop(); 93 | 94 | console.log(s.peek(), 2); 95 | -------------------------------------------------------------------------------- /chapter03/3.4 - Queue via Stacks/queueViaStacks.js: -------------------------------------------------------------------------------- 1 | var Stack = require('./../util/Stack'); 2 | 3 | class MyQueue { 4 | constructor() { 5 | this.front = new Stack(); 6 | this.back = new Stack(); 7 | this.backUp = true; 8 | } 9 | 10 | add(value) { 11 | if (!this.backUp) { 12 | while (!this.front.isEmpty()) { 13 | this.back.push(this.front.pop()); 14 | } 15 | this.backUp = true; 16 | } 17 | this.back.push(value); 18 | } 19 | 20 | remove() { 21 | if (this.backUp) { 22 | while(!this.back.isEmpty()) { 23 | this.front.push(this.back.pop()); 24 | } 25 | this.backUp = false; 26 | } 27 | return this.front.pop(); 28 | } 29 | 30 | peek() { 31 | if (this.backUp) { 32 | while(!this.back.isEmpty()) { 33 | this.front.push(this.back.pop()); 34 | } 35 | this.backUp = false; 36 | } 37 | return this.front.peek(); 38 | } 39 | 40 | isEmpty() { 41 | return this.front.isEmpty() && this.back.isEmpty(); 42 | } 43 | } 44 | 45 | /* TEST */ 46 | var m = new MyQueue(); 47 | console.log(m.isEmpty(), true); 48 | 49 | m.add('a'); 50 | m.add('b'); 51 | m.add('c'); 52 | m.add('d'); 53 | m.add('e'); 54 | m.remove(); 55 | console.log(m.peek(), 'b'); 56 | -------------------------------------------------------------------------------- /chapter03/3.5 - Sort Stack/sortStack.js: -------------------------------------------------------------------------------- 1 | // Notes: 2 | // hold the smallest item as a variable, put the rest into the other stack. 3 | // pop back the stack, place the smallest item into the bottom, and repeat. 4 | // when completed, pop back into original stack. 5 | 6 | var Stack = require('./../util/Stack'); 7 | 8 | var sortStack = function(stack) { 9 | var tempStack = new Stack(); 10 | var currMin = Infinity; 11 | var stackDepth = 0; 12 | 13 | while (!stack.isEmpty()) { 14 | if (stack.peek() <= currMin) { 15 | if (currMin !== Infinity) { 16 | tempStack.push(currMin); 17 | } 18 | currMin = stack.pop(); 19 | } else { 20 | tempStack.push(stack.pop()); 21 | } 22 | stackDepth++; 23 | } 24 | 25 | while (!tempStack.isEmpty()) { 26 | stack.push(tempStack.pop()); 27 | } 28 | 29 | tempStack.push(currMin); 30 | currMin = Infinity; 31 | stackDepth--; 32 | 33 | while (stackDepth > 0) { 34 | 35 | while (!stack.isEmpty()) { 36 | if (stack.peek() <= currMin) { 37 | if (currMin !== Infinity) { 38 | tempStack.push(currMin); 39 | } 40 | currMin = stack.pop(); 41 | } else { 42 | tempStack.push(stack.pop()); 43 | } 44 | } 45 | 46 | for (var i = 0; i < stackDepth - 1; i++) { 47 | stack.push(tempStack.pop()); 48 | } 49 | 50 | tempStack.push(currMin); 51 | currMin = Infinity; 52 | stackDepth--; 53 | } 54 | 55 | while (!tempStack.isEmpty()) { 56 | stack.push(tempStack.pop()); 57 | } 58 | 59 | return stack; 60 | }; 61 | 62 | /* TEST */ 63 | var s = new Stack(); 64 | s.push(99); 65 | s.push(4); 66 | s.push(1); 67 | s.push(6); 68 | s.push(8); 69 | s.push(10); 70 | s.push(22); 71 | s.push(3); 72 | s.push(72); 73 | 74 | var sortS = sortStack(s); 75 | 76 | while (!sortS.isEmpty()) { 77 | console.log(sortS.pop()); 78 | } 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /chapter03/3.6 - Animal Shelter/animalShelter.js: -------------------------------------------------------------------------------- 1 | var Queue = require('./../util/Queue'); 2 | 3 | Queue.prototype.enqueue = Queue.prototype.add; 4 | 5 | var AnimalShelter = function() { 6 | this.dogQ = new Queue(); 7 | this.catQ = new Queue(); 8 | this.allQ = new Queue(); 9 | this.tempQ = new Queue(); 10 | }; 11 | 12 | AnimalShelter.prototype.enqueue = function(animal) { 13 | if (animal.type === 'dog') { 14 | this.dogQ.enqueue(animal); 15 | } else if (animal.type === 'cat') { 16 | this.catQ.enqueue(animal); 17 | } 18 | this.allQ.enqueue(animal); 19 | }; 20 | 21 | AnimalShelter.prototype.dequeueAny = function() { 22 | if (this.allQ.peek() === this.dogQ.peek()) { 23 | this.dogQ.remove(); 24 | } else if (this.allQ.peek() === this.catQ.peek()) { 25 | this.catQ.remove(); 26 | } 27 | return this.allQ.remove(); 28 | }; 29 | 30 | AnimalShelter.prototype.dequeueByType = function(type) { 31 | var yesQ; 32 | if (type === 'dog') { 33 | yesQ = this.dogQ; 34 | } else if (type === 'cat') { 35 | yesQ = this.catQ; 36 | } 37 | while (!this.allQ.isEmpty() && this.allQ.peek().type !== type) { 38 | this.tempQ.enqueue(this.allQ.remove()); 39 | } 40 | var animal = this.allQ.remove(); 41 | yesQ.remove(); 42 | 43 | while(!this.allQ.isEmpty()) { 44 | this.tempQ.enqueue(this.allQ.remove()); 45 | } 46 | 47 | while(!this.tempQ.isEmpty()) { 48 | this.allQ.enqueue(this.tempQ.remove()); 49 | } 50 | return animal; 51 | }; 52 | 53 | AnimalShelter.prototype.dequeueDog = function() { 54 | return this.dequeueByType('dog'); 55 | }; 56 | 57 | AnimalShelter.prototype.dequeueCat = function() { 58 | return this.dequeueByType('cat'); 59 | }; 60 | 61 | /* TESTS */ 62 | 63 | var a = new AnimalShelter(); 64 | a.enqueue({type:'dog', name:'machi'}); 65 | a.enqueue({type:'dog', name:'daisy'}); 66 | a.enqueue({type:'cat', name:'peanuts'}); 67 | a.enqueue({type:'dog', name:'miso'}); 68 | a.enqueue({type:'cat', name:'dada'}); 69 | a.enqueue({type:'cat', name:'xiaoxiao'}); 70 | 71 | console.log(a.dequeueAny(), 'dog machi'); 72 | 73 | console.log(a.dequeueCat(), 'cat peanuts'); 74 | 75 | console.log(a.dequeueAny(), 'dog daisy'); 76 | 77 | console.log(a.dequeueAny(), 'dog miso'); 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /chapter03/util/LinkedList.js: -------------------------------------------------------------------------------- 1 | // moved from chapter 2 (/util/LinkedListX.js) 2 | 3 | class Node { 4 | constructor(value) { 5 | this.value = value 6 | this.next = null; 7 | } 8 | } 9 | 10 | class LinkedList { 11 | constructor() { 12 | this.head = null; 13 | this.tail = null; 14 | } 15 | 16 | append(value) { 17 | let node = new Node(value); 18 | // if list is empty 19 | if (!this.head) { 20 | this.head = node; 21 | this.tail = node; 22 | } 23 | else { 24 | this.tail.next = node; 25 | this.tail = node; 26 | } 27 | } 28 | 29 | prepend(value) { 30 | let node = new Node(value); 31 | node.next = this.head; 32 | this.head = node; 33 | } 34 | 35 | pop() { 36 | let cur = this.head; 37 | 38 | // only one or no item exists 39 | if (!cur) return null; 40 | if (!cur.next) { 41 | this.head = null; 42 | return cur; 43 | } 44 | // move till the 2nd last 45 | while (cur.next.next) 46 | cur = cur.next; 47 | 48 | let last = this.tail; 49 | this.tail = cur; 50 | this.tail.next = null; 51 | return last; 52 | } 53 | 54 | popFirst() { 55 | let first = this.head; 56 | if (this.head && this.head.next) { 57 | this.head = this.head.next; 58 | first.next = null; 59 | } 60 | else this.head = null; 61 | return first; 62 | } 63 | 64 | head() { 65 | return this.head; 66 | } 67 | 68 | removeAt(index) { 69 | let i = 0; 70 | let cur = this.head; 71 | let prev = null; 72 | 73 | while (cur != null) { 74 | if (i == index) { 75 | // remove 76 | if (prev == null) 77 | this.head = cur.next; 78 | else prev.next = cur.next; 79 | cur.next = null; 80 | return cur.value; 81 | } 82 | else { 83 | prev = cur; 84 | cur = cur.next; 85 | i++; 86 | } 87 | } 88 | return null; 89 | } 90 | 91 | insertAt(index, value) { 92 | if (index == 0) return this.prepend(value); 93 | let cur = this.head; 94 | let i = 0; 95 | 96 | while (cur != null) { 97 | if (i == index - 1) { 98 | let node = new Node(value); 99 | node.next = cur.next; 100 | cur.next = node; 101 | return true; 102 | } 103 | else { 104 | i++; 105 | cur = cur.next; 106 | } 107 | } 108 | return false; 109 | } 110 | 111 | tail() { 112 | return this.tail; 113 | } 114 | 115 | _toArray() { 116 | let arr = []; 117 | let cur = this.head; 118 | while (cur) { 119 | arr.push(cur.value); 120 | cur = cur.next; 121 | } 122 | 123 | return arr; 124 | } 125 | } 126 | 127 | module.exports = LinkedList; 128 | 129 | /* TEST */ 130 | 131 | // let l = new LinkedList(); 132 | // l.append(3); 133 | // l.append(4); 134 | // l.append(10); 135 | // l.append(20); 136 | // l.append(5); 137 | 138 | // console.log(l.removeAt(1), 4); 139 | // console.log(l.pop().value, 5); 140 | 141 | // console.log(l._toArray()); 142 | // l.insertAt(2, 40); 143 | // console.log(l._toArray()); 144 | -------------------------------------------------------------------------------- /chapter03/util/Queue.js: -------------------------------------------------------------------------------- 1 | // implement a queue using linkedLists 2 | var LinkedList = require('./LinkedList'); 3 | 4 | class Queue { 5 | constructor() { 6 | this._list = new LinkedList(); 7 | } 8 | 9 | enqueue(value) { 10 | this._list.append(value); 11 | } 12 | 13 | dequeue() { 14 | let node = this._list.popFirst(); 15 | return node.value; 16 | } 17 | 18 | peek() { 19 | return this._list.head ? this._list.head.value : null; 20 | } 21 | 22 | isEmpty() { 23 | return this._list.head == null; 24 | } 25 | 26 | _toArray() { 27 | return this._list._toArray(); 28 | } 29 | } 30 | 31 | // alias 32 | Queue.prototype.add = Queue.prototype.enqueue; 33 | Queue.prototype.remove = Queue.prototype.dequeue; 34 | 35 | module.exports = Queue; 36 | 37 | /* TEST */ 38 | // var q = new Queue(); 39 | // q.add('a'); 40 | // q.add('b'); 41 | // q.add('c'); 42 | // console.log(q._toArray()); 43 | // console.log(q.remove(), 'a'); 44 | // console.log(q.peek(), 'b'); 45 | // console.log(q.remove(), 'b'); 46 | // console.log(q.remove(), 'c'); 47 | // console.log(q.isEmpty(), true); 48 | // console.log(q._toArray()); 49 | -------------------------------------------------------------------------------- /chapter03/util/Stack.js: -------------------------------------------------------------------------------- 1 | // simple implementation of a stack 2 | // using an Array 3 | 4 | /* 5 | Interface: 6 | - push(value) 7 | - pop() 8 | - peek() 9 | - isEmpty() 10 | - size() 11 | */ 12 | class Stack { 13 | constructor() { 14 | this._data = []; 15 | } 16 | 17 | size() { 18 | return this._data.length; 19 | } 20 | 21 | isEmpty() { 22 | return this.size() == 0; 23 | } 24 | 25 | push(value) { 26 | this._data.push(value); 27 | } 28 | 29 | pop() { 30 | return this._data.pop(); 31 | } 32 | 33 | peek() { 34 | if (this.isEmpty()) return null; 35 | return this._data[this.size() - 1]; 36 | } 37 | 38 | _toArray() { 39 | return this._data; 40 | } 41 | } 42 | 43 | module.exports = Stack; 44 | 45 | /* TEST */ 46 | 47 | // var s = new Stack(); 48 | // s.push('a'); 49 | // s.push('b'); 50 | // s.push('c'); 51 | // console.log(s.pop(), 'c'); 52 | // console.log(s.peek(), 'b'); 53 | // console.log(s.pop(), 'b'); 54 | // console.log(s.pop(), 'a'); 55 | // console.log(s.isEmpty(), true); 56 | -------------------------------------------------------------------------------- /chapter04/4.01 - Route Between Nodes/routeBetweenNodes.js: -------------------------------------------------------------------------------- 1 | var Graph = require('./../util/Graph'); 2 | var Queue = require('./../util/Queue'); 3 | 4 | // concurrently implement BFS on both sides of the graph 5 | // intention is to minimise the levels that the graph has to search 6 | 7 | var checkRoute = function(value1, value2, graph) { 8 | var q1 = new Queue(); 9 | var q2 = new Queue(); 10 | var visited1 = {}; 11 | var visited2 = {}; 12 | // insert values into qs 13 | visited1[value1] = true; 14 | visited2[value2] = true; 15 | if(graph.hasNode(value1)) { 16 | for (var edge in graph.findEdges(value1)) { 17 | q1.add(edge); 18 | } 19 | } 20 | if(graph.hasNode(value2)) { 21 | for (var edge in graph.findEdges(value2)) { 22 | q2.add(edge); 23 | } 24 | } 25 | // take turns dequeueing until empty 26 | var nextEdge1; 27 | var nextEdge2; 28 | while (!q1.isEmpty() || !q2.isEmpty()) { 29 | // if has queue, return true 30 | if (!q1.isEmpty()) { 31 | nextEdge1 = q1.remove(); 32 | if (nextEdge1 === value2) { 33 | return true; 34 | } 35 | if (visited1[nextEdge1] === undefined) { 36 | visited1[nextEdge1] = true; 37 | if(graph.hasNode(nextEdge1)) { 38 | for (var edge in graph.findEdges(nextEdge1)) { 39 | q1.add(edge); 40 | } 41 | } 42 | } 43 | } 44 | if (!q2.isEmpty()) { 45 | nextEdge2 = q2.remove(); 46 | if (nextEdge2 === value1) { 47 | return true; 48 | } 49 | if (visited2[nextEdge2] === undefined) { 50 | visited2[nextEdge2] = true; 51 | if(graph.hasNode(nextEdge2)) { 52 | for (var edge in graph.findEdges(nextEdge2)) { 53 | q2.add(edge); 54 | } 55 | } 56 | } 57 | } 58 | } 59 | // return false 60 | return false; 61 | }; 62 | 63 | /* TEST */ 64 | var graph = new Graph(); 65 | graph.addNode('A'); 66 | graph.addNode('B'); 67 | graph.addNode('C'); 68 | graph.addNode('D'); 69 | graph.addNode('E'); 70 | 71 | graph.addEdge('A', 'B'); 72 | graph.addEdge('A', 'C'); 73 | graph.addEdge('B', 'C'); 74 | 75 | graph.addEdge('D', 'E'); 76 | 77 | 78 | console.log(checkRoute('A', 'C', graph), true); 79 | console.log(checkRoute('A', 'E', graph), false); 80 | console.log(checkRoute('B', 'A', graph), true); 81 | console.log(checkRoute('D', 'E', graph), true); 82 | -------------------------------------------------------------------------------- /chapter04/4.02 - Minimal Tree/minimalTree.js: -------------------------------------------------------------------------------- 1 | // Approach: divide and conquer, array and insert into tree 2 | var Queue = require('./../util/Queue'); 3 | var BST = require('./../util/BST'); 4 | 5 | var insertBalanced = function(array) { 6 | var bst = new BST(); 7 | var q = new Queue(); 8 | var currArr; 9 | var floor = Math.floor; 10 | q.add(array); 11 | while (!q.isEmpty()) { 12 | currArr = q.remove(); 13 | bst.insert(currArr[floor(currArr.length/2)]); 14 | if (currArr.slice(0, floor(currArr.length/2)).length > 0) { 15 | q.add(currArr.slice(0, floor(currArr.length/2))); 16 | } 17 | if (currArr.slice(floor(currArr.length/2) + 1).length > 0) { 18 | q.add(currArr.slice(floor(currArr.length/2) + 1)); 19 | } 20 | } 21 | return bst; 22 | }; 23 | 24 | /* TEST */ 25 | var arr1 = [1, 2, 3, 4, 5, 6]; 26 | var tree1 = insertBalanced(arr1); 27 | tree1.printLevelOrder(); 28 | 29 | var arr2 = [1, 2, 3, 4, 5, 6, 7]; 30 | var tree2 = insertBalanced(arr2); 31 | tree2.printLevelOrder(); -------------------------------------------------------------------------------- /chapter04/4.03 - List of Depths/listOfDepths.js: -------------------------------------------------------------------------------- 1 | var BST = require('./../util/BST'); 2 | var LinkedList = require('./../util/LinkedList'); 3 | var Queue = require('./../util/Queue'); 4 | 5 | var listOfDepths = function(bst) { 6 | var listOfLists = []; 7 | var list = null; 8 | var newNode; 9 | var q = new Queue(); 10 | var nextq = new Queue(); 11 | var currNode = bst; 12 | 13 | q.add(currNode); 14 | while (!q.isEmpty()) { 15 | currNode = q.remove(); 16 | newNode = new LinkedList(currNode.value); 17 | newNode.next = list; 18 | list = newNode; 19 | if (currNode.left !== null) { 20 | nextq.add(currNode.left); 21 | } 22 | if (currNode.right !== null) { 23 | nextq.add(currNode.right); 24 | } 25 | if (q.isEmpty()) { 26 | listOfLists.push(list); 27 | list = null; 28 | q = nextq; 29 | nextq = new Queue(); 30 | } 31 | } 32 | return listOfLists; 33 | }; 34 | 35 | /* TEST */ 36 | // 1, 2, 3, 4, 5, 6, 7 37 | var tree = new BST(4); 38 | tree.insert(2); 39 | tree.insert(6); 40 | tree.insert(1); 41 | tree.insert(3); 42 | tree.insert(5); 43 | tree.insert(7); 44 | 45 | console.log(listOfDepths(tree)); -------------------------------------------------------------------------------- /chapter04/4.04 - Check Balanced/checkBalanced.js: -------------------------------------------------------------------------------- 1 | var BST = require('./../util/BST'); 2 | 3 | var checkBalanced = function(bst) { 4 | // case where left is null and right is not null 5 | if (bst.left === null && bst.right !== null) { 6 | if (bst.right.left !== null || bst.right.right !== null) { 7 | return false; 8 | } 9 | } 10 | // case where left is not null and right is null 11 | if (bst.left !== null && bst.right === null) { 12 | if (bst.left.left !== null || bst.left.right !== null) { 13 | return false; 14 | } 15 | } 16 | // initialize answer variable as true 17 | var answer = true; 18 | // if bst.left is not null, recursively call checkBalanced on bst.left 19 | if (bst.left !== null) { 20 | answer = answer && checkBalanced(bst.left); 21 | } 22 | // if bst.left is not null, recursively call checkBalanced on bst.left 23 | if (bst.right !== null) { 24 | answer = answer && checkBalanced(bst.right); 25 | } 26 | // return answer 27 | return answer; 28 | }; 29 | 30 | /* TEST */ 31 | 32 | var b1 = new BST(1); 33 | b1.insert(2); 34 | b1.insert(3); 35 | b1.insert(4); 36 | console.log(checkBalanced(b1), false); 37 | 38 | var b2 = new BST(4); 39 | b2.insert(2); 40 | b2.insert(6); 41 | b2.insert(1); 42 | b2.insert(3); 43 | b2.insert(5); 44 | b2.insert(7); 45 | console.log(checkBalanced(b2), true); 46 | -------------------------------------------------------------------------------- /chapter04/4.05 - Validate BST/validateBST.js: -------------------------------------------------------------------------------- 1 | var BinaryTree = function(value) { 2 | this.value = value; 3 | this.left = null; 4 | this.right = null; 5 | }; 6 | 7 | var validateBST = function(bt) { 8 | // traverse the tree depth first, while 9 | // using a array with stack like behavior to check each node's validity 10 | // * assume values do not repeat 11 | var validateRecurse = function(currBt, stackArr) { 12 | for (var i = 0; i < stackArr.length; i++) { 13 | if (stackArr[i].side === 'left' && currBt.value > stackArr[i].node.value) { 14 | return false; 15 | } else if (stackArr[i].side === 'right' && currBt.value < stackArr[i].node.value) { 16 | return false; 17 | } 18 | } 19 | var left = currBt.left === null ? true : validateRecurse(currBt.left, stackArr.concat([{ node:currBt, side:'left'}])); 20 | var right = currBt.right === null ? true : validateRecurse(currBt.right, stackArr.concat([{ node:currBt, side:'right'}])); 21 | return true && left && right; 22 | }; 23 | return validateRecurse(bt, []); 24 | }; 25 | 26 | /* TESTS */ 27 | 28 | var bt1a = new BinaryTree(5); 29 | var bt1b = new BinaryTree(4); 30 | var bt1c = new BinaryTree(6); 31 | var bt1d = new BinaryTree(1); 32 | var bt1e = new BinaryTree(100); 33 | 34 | bt1a.left = bt1b; 35 | bt1a.right = bt1c; 36 | bt1b.left = bt1d; 37 | bt1b.right = bt1e; 38 | 39 | console.log(validateBST(bt1a), false); 40 | 41 | var bt2a = new BinaryTree(5); 42 | var bt2b = new BinaryTree(3); 43 | var bt2c = new BinaryTree(6); 44 | var bt2d = new BinaryTree(1); 45 | var bt2e = new BinaryTree(4); 46 | 47 | bt2a.left = bt2b; 48 | bt2a.right = bt2c; 49 | bt2b.left = bt2d; 50 | bt2b.right = bt2e; 51 | 52 | console.log(validateBST(bt2a), true); 53 | -------------------------------------------------------------------------------- /chapter04/4.06 - Successor/successor.js: -------------------------------------------------------------------------------- 1 | var BSTp = function(value) { 2 | this.value = value; 3 | this.left = null; 4 | this.right = null; 5 | this.parent = null; 6 | }; 7 | 8 | var findSuccessor = function(node) { 9 | var successor = null; 10 | if (node.right !== null) { 11 | successor = node.right; 12 | while (successor.left !== null) { 13 | successor = successor.left; 14 | } 15 | } else if (node.parent !== null) { 16 | var currNode = node; 17 | while (currNode.parent !== null && successor === null) { 18 | if (currNode.parent.left === currNode) { 19 | successor = currNode.parent; 20 | } 21 | currNode = currNode.parent; 22 | } 23 | } 24 | return successor; 25 | }; 26 | 27 | /* TEST */ 28 | var a = new BSTp(10); 29 | var b = new BSTp(2); 30 | var c = new BSTp(3); 31 | var d = new BSTp(4); 32 | var e = new BSTp(6); 33 | var f = new BSTp(5); 34 | var g = new BSTp(7); 35 | 36 | a.left = b; b.parent = a; 37 | b.right = c; c.parent = b; 38 | c.right = d; d.parent = c; 39 | d.right = e; e.parent = d; 40 | e.left = f; f.parent = e; 41 | e.right = g; g.parent = e; 42 | 43 | 44 | console.log(findSuccessor(f).value, 6); 45 | console.log(findSuccessor(g).value, 10); -------------------------------------------------------------------------------- /chapter04/4.07 - Build Order/buildOrder.js: -------------------------------------------------------------------------------- 1 | // directed graph 2 | var Graph = require('./../util/Graph'); 3 | 4 | Graph.prototype.findNodeWithNoChildren = function() { 5 | for (var node in this.nodes) { 6 | if (Object.keys(this.nodes[node]).length === 0) { 7 | return node; 8 | } 9 | } 10 | return undefined; 11 | }; 12 | 13 | var buildOrder = function(projects, dependencies) { 14 | var graph = new Graph(); 15 | projects.forEach(project => { 16 | graph.addNode(project); 17 | }); 18 | dependencies.forEach(dependency => { 19 | graph.addEdge(dependency[1], dependency[0]); 20 | }); 21 | var answer = []; 22 | var currNode = graph.findNodeWithNoChildren(); 23 | while (currNode !== undefined) { 24 | answer.push(currNode); 25 | graph.removeNode(currNode); 26 | currNode = graph.findNodeWithNoChildren(); 27 | } 28 | if (answer.length === projects.length) { 29 | return answer; 30 | } else { 31 | throw Error; 32 | } 33 | }; 34 | 35 | /* TEST */ 36 | var projects = ['a', 'b', 'c', 'd', 'e', 'f']; 37 | var dependencies = [['a', 'd'], ['f', 'b'], ['b', 'd'], ['f', 'a'], ['d', 'c']]; 38 | 39 | console.log(buildOrder(projects, dependencies)); 40 | -------------------------------------------------------------------------------- /chapter04/4.08 - First Common Ancestor/firstCommonAncestor.js: -------------------------------------------------------------------------------- 1 | var BinaryTree = function(value) { 2 | this.value = value; 3 | this.left = null; 4 | this.right = null; 5 | this.parent = null; 6 | }; 7 | 8 | BinaryTree.prototype.isAncestor = function(node2) { 9 | if (this === node2) { 10 | return true; 11 | } else { 12 | var answer1 = false; 13 | var answer2 = false; 14 | if (this.left !== null) { 15 | answer1 = this.left.isAncestor(node2); 16 | } 17 | if (this.right !== null) { 18 | answer2 = this.right.isAncestor(node2); 19 | } 20 | return false || answer1 || answer2; 21 | } 22 | }; 23 | 24 | var firstCommonAncestor = function(node1, node2) { 25 | var currNode = node1; 26 | while (!currNode.isAncestor(node2)) { 27 | if (currNode === null) { 28 | throw Error; 29 | } else { 30 | currNode = currNode.parent; 31 | } 32 | } 33 | return currNode.value; 34 | }; 35 | 36 | /* TEST */ 37 | var a = new BinaryTree('a'); 38 | var b = new BinaryTree('b'); 39 | var c = new BinaryTree('c'); 40 | var d = new BinaryTree('d'); 41 | var e = new BinaryTree('e'); 42 | var f = new BinaryTree('f'); 43 | var g = new BinaryTree('g'); 44 | var h = new BinaryTree('h'); 45 | var i = new BinaryTree('i'); 46 | var j = new BinaryTree('j'); 47 | var k = new BinaryTree('k'); 48 | var l = new BinaryTree('l'); 49 | 50 | a.left = b; b.parent = a; 51 | a.right = c; c.parent = a; 52 | b.left = d; d.parent = b; 53 | d.left = g; g.parent = d; 54 | d.right = h; h.parent = d; 55 | h.right = k; k.parent = h; 56 | k.left = l; l.parent = k; 57 | c.left = e; e.parent = c; 58 | c.right = f; f.parent = c; 59 | f.left = i; i.parent = f; 60 | f.right = j; j.parent = f; 61 | 62 | console.log(firstCommonAncestor(g, k), 'd'); 63 | console.log(firstCommonAncestor(b, i), 'a'); 64 | -------------------------------------------------------------------------------- /chapter04/4.09 - BST Sequences/bstSequences.js: -------------------------------------------------------------------------------- 1 | var BST = require('./../util/BST'); 2 | 3 | var bstSequences = function(bst) { 4 | var sequences = []; 5 | var recurse = function(nodes, travelled) { 6 | var noChild = true; 7 | nodes.forEach((node) => { 8 | if (node.left !== null && travelled[node.left.value] === undefined) { 9 | noChild = false; 10 | travelled[node.left.value] = true; 11 | recurse(nodes.concat([node.left]), travelled); 12 | delete travelled[node.left.value]; 13 | } 14 | if (node.right !== null && travelled[node.right.value] === undefined) { 15 | noChild = false; 16 | travelled[node.right.value] = true; 17 | recurse(nodes.concat([node.right]), travelled); 18 | delete travelled[node.right.value]; 19 | } 20 | }); 21 | if (noChild) { 22 | sequences.push(nodes.map(node => node.value)); 23 | } 24 | }; 25 | var startTravelled = {}; 26 | startTravelled[bst.value] = true; 27 | recurse([bst], startTravelled); 28 | return sequences; 29 | }; 30 | 31 | /* TEST */ 32 | 33 | /* 1, 2, 3, 4, 5, 6, 7 */ 34 | 35 | var b = new BST(4); 36 | b.insert(2); 37 | b.insert(6); 38 | b.insert(1); 39 | b.insert(3); 40 | b.insert(5); 41 | b.insert(7); 42 | 43 | console.log(bstSequences(b)); 44 | -------------------------------------------------------------------------------- /chapter04/4.10 - Check Subtree/checkSubtree.js: -------------------------------------------------------------------------------- 1 | var Tree = function(value) { 2 | this.value = value; 3 | this.left = null; 4 | this.right = null; 5 | }; 6 | 7 | var isSame = function(tree1, tree2) { 8 | var answer = tree1.value === tree2.value; 9 | if (!answer) { return answer; } 10 | 11 | if (tree1.left !== null && tree2.left !== null) { 12 | answer = answer && isSame(tree1.left, tree2.left); 13 | } else if ((tree1.left === null && tree2.left !== null) || 14 | (tree1.left !== null && tree2.left === null)) { 15 | answer = answer && false; 16 | } 17 | 18 | if (tree1.right !== null && tree2.right !== null) { 19 | answer = answer && isSame(tree1.right, tree2.right); 20 | } else if ((tree1.right === null && tree2.right !== null) || 21 | (tree1.right !== null && tree2.right === null)) { 22 | answer = answer && false; 23 | } 24 | 25 | return answer; 26 | }; 27 | 28 | Tree.prototype.isSubtree = function(tree2) { 29 | if (isSame(this, tree2)) { 30 | return true; 31 | } else { 32 | var answer = false; 33 | if (this.left !== null) { answer = answer || this.left.isSubtree(tree2); } 34 | if (this.right !== null) { answer = answer || this.right.isSubtree(tree2); } 35 | return answer; 36 | } 37 | }; 38 | 39 | /* TEST */ 40 | var a1 = new Tree(1); 41 | var a2 = new Tree(2); 42 | var a3 = new Tree(3); 43 | var a4 = new Tree(4); 44 | var a5 = new Tree(5); 45 | var a6 = new Tree(6); 46 | var a7 = new Tree(7); 47 | 48 | var b3 = new Tree(3); 49 | var b6 = new Tree(6); 50 | var b7 = new Tree(7); 51 | 52 | var c3 = new Tree(3); 53 | var c6 = new Tree(6); 54 | var c8 = new Tree(8); 55 | 56 | a1.left = a2; 57 | a1.right = a3; 58 | a2.left = a4; 59 | a2.right = a5; 60 | a3.left = a6; 61 | a3.right = a7; 62 | 63 | b3.left = b6; 64 | b3.right = b7; 65 | 66 | c3.left = c6; 67 | c3.right = c8; 68 | 69 | console.log(a1.isSubtree(b3), true); 70 | console.log(a1.isSubtree(c3), false); -------------------------------------------------------------------------------- /chapter04/4.11 - Random Node/randomNode.js: -------------------------------------------------------------------------------- 1 | var Queue = require('./../util/Queue'); 2 | 3 | var BinaryTree = function(value) { 4 | this.value = value; 5 | this.left = null; 6 | this.right = null; 7 | }; 8 | 9 | BinaryTree.prototype.insert = function(value) { 10 | if (this.value === undefined) { 11 | this.value = value; 12 | } else if (value < this.value) { 13 | if (this.left === null) { 14 | this.left = new BinaryTree(value); 15 | } else { 16 | this.left.insert(value); 17 | } 18 | } else { 19 | if (this.right === null) { 20 | this.right = new BinaryTree(value); 21 | } else { 22 | this.right.insert(value); 23 | } 24 | } 25 | }; 26 | 27 | BinaryTree.prototype.find = function(value) { 28 | if (value === this.value) { 29 | return this; 30 | } else { 31 | if (value < this.value) { 32 | if (this.left === null) { 33 | return null; 34 | } else { 35 | this.left.find(value); 36 | } 37 | } else { 38 | if (this.right === null) { 39 | return null; 40 | } else { 41 | this.right.find(value); 42 | } 43 | } 44 | } 45 | }; 46 | 47 | BinaryTree.prototype.rebuild = function() { 48 | if (this.left === null && this.right === null) { 49 | return null; 50 | } 51 | var newBt = new BinaryTree(); 52 | var q = new Queue(); 53 | if (this.left !== null) { 54 | q.add(this.left); 55 | } 56 | if (this.right !== null) { 57 | q.add(this.right); 58 | } 59 | var node; 60 | while (!q.isEmpty) { 61 | node = q.remove(); 62 | newBt.insert(node.value); 63 | if (node.left !== null) { 64 | q.add(node.left); 65 | } 66 | if (this.right !== null) { 67 | q.add(node.right); 68 | } 69 | } 70 | return newBt; 71 | }; 72 | 73 | BinaryTree.prototype.delete = function(value) { 74 | if (value === this.value) { 75 | var reb = this.rebuild(); 76 | this.value = reb.value; 77 | this.left = reb.left; 78 | this.right = reb.right; 79 | } else { 80 | var q = new Queue(); 81 | var built = false; 82 | var node; 83 | q.add(this); 84 | while(!q.isEmpty() && !built) { 85 | node = q.remove(); 86 | if (node.left !== null) { 87 | if (node.left.value === value) { 88 | node.left = node.left.rebuild(); 89 | built = true; 90 | } else { 91 | q.add(node.left); 92 | } 93 | } 94 | if (node.right !== null) { 95 | if (node.right.value === value) { 96 | node.right = node.right.rebuild(); 97 | built = true; 98 | } else { 99 | q.add(node.right); 100 | } 101 | } 102 | } 103 | if (!built) { 104 | return null; 105 | } 106 | } 107 | }; 108 | 109 | BinaryTree.prototype.count = function() { 110 | var q = new Queue(); 111 | q.add(this); 112 | var node; 113 | var count = 0; 114 | while (!q.isEmpty()) { 115 | node = q.remove(); 116 | count++; 117 | if (node.left !== null) { 118 | q.add(node.left); 119 | } 120 | if (node.right !== null) { 121 | q.add(node.right); 122 | } 123 | } 124 | return count; 125 | }; 126 | 127 | BinaryTree.prototype.iterateToN = function(n) { 128 | var q = new Queue(); 129 | q.add(this); 130 | var node; 131 | var count = 0; 132 | while (!q.isEmpty()) { 133 | node = q.remove(); 134 | count++; 135 | if (count === n) { 136 | return node; 137 | } 138 | if (node.left !== null) { 139 | q.add(node.left); 140 | } 141 | if (node.right !== null) { 142 | q.add(node.right); 143 | } 144 | } 145 | return undefined; 146 | }; 147 | 148 | BinaryTree.prototype.getRandomNode = function() { 149 | // do a BFS count 150 | var count = this.count(); 151 | // use BFS to iterate random node 152 | var random = Math.floor(Math.random() * count) + 1; 153 | return this.iterateToN(random); 154 | }; 155 | 156 | /* TEST */ 157 | var bt = new BinaryTree(); 158 | bt.insert(4); 159 | bt.insert(2); 160 | bt.insert(6); 161 | bt.insert(1); 162 | bt.insert(3); 163 | bt.insert(5); 164 | bt.insert(7); 165 | console.log(bt); 166 | bt.delete(5); 167 | console.log(bt); 168 | var counts = {}; 169 | var randomnum; 170 | for (var i = 0; i < 100000; i++) { 171 | randomnum = bt.getRandomNode().value; 172 | if (counts[randomnum] === undefined) { 173 | counts[randomnum] = 1; 174 | } else { 175 | counts[randomnum]++; 176 | } 177 | } 178 | for (var num in counts) { 179 | console.log(`${num}: ${counts[num] / 100000}%`); // random enough 180 | } 181 | -------------------------------------------------------------------------------- /chapter04/4.12 - Paths with Sum/pathsWithSum.js: -------------------------------------------------------------------------------- 1 | /* helper function - sum of an array */ 2 | var SumArr = function(arr) { 3 | return arr.reduce((total, num) => total + num, 0); 4 | }; 5 | 6 | var BinaryTree = function(value) { 7 | this.value = value; 8 | this.left = null; 9 | this.right = null; 10 | }; 11 | 12 | BinaryTree.prototype.countPathsWithRoot = function(value, path) { 13 | var pathCount = 0; 14 | if (path === undefined) { 15 | path = [this.value]; 16 | } else { 17 | path = [...path, this.value]; 18 | } 19 | if (SumArr(path) === value) { 20 | pathCount++; 21 | } 22 | if (this.left !== null) { 23 | pathCount += this.left.countPathsWithRoot(value, path); 24 | } 25 | if (this.right !== null) { 26 | pathCount += this.right.countPathsWithRoot(value, path); 27 | } 28 | return pathCount; 29 | }; 30 | 31 | BinaryTree.prototype.pathsWithSum = function(value) { 32 | var pathCount = 0; 33 | pathCount += this.countPathsWithRoot(value); 34 | if (this.left !== null) { 35 | pathCount += this.left.countPathsWithRoot(value); 36 | } 37 | if (this.right !== null) { 38 | pathCount += this.right.countPathsWithRoot(value); 39 | } 40 | return pathCount; 41 | }; 42 | 43 | /* TEST */ 44 | var a = new BinaryTree(1); 45 | var b = new BinaryTree(1); 46 | var c = new BinaryTree(1); 47 | var d = new BinaryTree(10); 48 | 49 | a.left = b; 50 | a.right = c; 51 | c.left = d; 52 | 53 | console.log(a.pathsWithSum(12), 1); 54 | console.log(a.pathsWithSum(2), 2); 55 | console.log(a.pathsWithSum(1), 3); -------------------------------------------------------------------------------- /chapter04/util/BST.js: -------------------------------------------------------------------------------- 1 | var Queue = require('./Queue'); 2 | 3 | var BST = function(value) { 4 | this.value = value; 5 | this.left = null; 6 | this.right = null; 7 | }; 8 | 9 | BST.prototype.insert = function(value) { 10 | if (value < this.value) { 11 | if (this.left === null) { 12 | this.left = new BST(value); 13 | } else { 14 | this.left.insert(value); 15 | } 16 | } else { 17 | if (this.right === null) { 18 | this.right = new BST(value); 19 | } else { 20 | this.right.insert(value); 21 | } 22 | } 23 | }; 24 | 25 | BST.prototype.printLevelOrder = function() { 26 | var level = []; 27 | var q = new Queue(); 28 | var nextq = new Queue(); 29 | var currNode; 30 | 31 | q.add(this); 32 | while (!q.isEmpty()) { 33 | currNode = q.remove(); 34 | level.push(currNode.value); 35 | if (currNode.left !== null) { 36 | nextq.add(currNode.left); 37 | } 38 | if (currNode.right !== null) { 39 | nextq.add(currNode.right); 40 | } 41 | if (q.isEmpty()) { 42 | console.log(level.join(',')); 43 | level = []; 44 | q = nextq; 45 | nextq = new Queue(); 46 | } 47 | } 48 | }; 49 | 50 | module.exports = BST; 51 | 52 | /* TEST */ 53 | // 1, 2, 3, 4, 5, 6, 7 54 | // var bst = new BST(); 55 | // bst.insert(4); 56 | // bst.insert(2); 57 | // bst.insert(6); 58 | // bst.insert(1); 59 | // bst.insert(3); 60 | // bst.insert(5); 61 | // bst.insert(7); 62 | 63 | // bst.printLevelOrder(); 64 | -------------------------------------------------------------------------------- /chapter04/util/Graph.js: -------------------------------------------------------------------------------- 1 | var Graph = function() { 2 | this.nodes = {}; 3 | }; 4 | 5 | Graph.prototype.addEdge = function(node, edge) { 6 | if (this.nodes[node] === undefined) { 7 | return 'node does not exist'; 8 | } else if (this.nodes[node][edge]) { 9 | return `edge ${node}-${edge} already exists`; 10 | } else { 11 | this.nodes[node][edge] = true; 12 | } 13 | }; 14 | 15 | Graph.prototype.addNode = function(value) { 16 | if (this.nodes[value] !== undefined) { 17 | return `node of value ${value} already exists`; 18 | } else { 19 | this.nodes[value] = {}; 20 | } 21 | }; 22 | 23 | Graph.prototype.findEdges = function(node) { 24 | if (this.nodes[node] === undefined) { 25 | return 'node does not exist'; 26 | } else { 27 | return this.nodes[node]; 28 | } 29 | }; 30 | 31 | Graph.prototype.hasEdge = function(node, edge) { 32 | if (this.nodes[node] === undefined) { 33 | return false; 34 | } else { 35 | return this.nodes[node][edge] !== undefined; 36 | } 37 | }; 38 | 39 | Graph.prototype.hasNode = function(node) { 40 | return this.nodes[node] !== undefined; 41 | }; 42 | 43 | Graph.prototype.removeEdge = function(node, edge) { 44 | if (this.nodes[node] === undefined) { 45 | return 'node does not exist'; 46 | } else { 47 | delete this.nodes[node][edge]; 48 | } 49 | }; 50 | 51 | Graph.prototype.removeNode = function(node) { 52 | if (this.nodes[node] === undefined) { 53 | return 'node does not exist'; 54 | } else { 55 | delete this.nodes[node]; 56 | for (var currNode in this.nodes) { 57 | if (this.nodes[currNode][node] !== undefined) { 58 | delete this.nodes[currNode][node]; 59 | } 60 | } 61 | } 62 | }; 63 | 64 | module.exports = Graph; -------------------------------------------------------------------------------- /chapter04/util/LinkedList.js: -------------------------------------------------------------------------------- 1 | // moved from chapter 2 (/util/LinkedListX.js) 2 | 3 | class Node { 4 | constructor(value) { 5 | this.value = value 6 | this.next = null; 7 | } 8 | } 9 | 10 | class LinkedList { 11 | constructor() { 12 | this.head = null; 13 | this.tail = null; 14 | } 15 | 16 | append(value) { 17 | let node = new Node(value); 18 | // if list is empty 19 | if (!this.head) { 20 | this.head = node; 21 | this.tail = node; 22 | } 23 | else { 24 | this.tail.next = node; 25 | this.tail = node; 26 | } 27 | } 28 | 29 | prepend(value) { 30 | let node = new Node(value); 31 | node.next = this.head; 32 | this.head = node; 33 | } 34 | 35 | pop() { 36 | let cur = this.head; 37 | 38 | // only one or no item exists 39 | if (!cur) return null; 40 | if (!cur.next) { 41 | this.head = null; 42 | return cur; 43 | } 44 | // move till the 2nd last 45 | while (cur.next.next) 46 | cur = cur.next; 47 | 48 | let last = this.tail; 49 | this.tail = cur; 50 | this.tail.next = null; 51 | return last; 52 | } 53 | 54 | popFirst() { 55 | let first = this.head; 56 | if (this.head && this.head.next) { 57 | this.head = this.head.next; 58 | first.next = null; 59 | } 60 | else this.head = null; 61 | return first; 62 | } 63 | 64 | head() { 65 | return this.head; 66 | } 67 | 68 | removeAt(index) { 69 | let i = 0; 70 | let cur = this.head; 71 | let prev = null; 72 | 73 | while (cur != null) { 74 | if (i == index) { 75 | // remove 76 | if (prev == null) 77 | this.head = cur.next; 78 | else prev.next = cur.next; 79 | cur.next = null; 80 | return cur.value; 81 | } 82 | else { 83 | prev = cur; 84 | cur = cur.next; 85 | i++; 86 | } 87 | } 88 | return null; 89 | } 90 | 91 | insertAt(index, value) { 92 | if (index == 0) return this.prepend(value); 93 | let cur = this.head; 94 | let i = 0; 95 | 96 | while (cur != null) { 97 | if (i == index - 1) { 98 | let node = new Node(value); 99 | node.next = cur.next; 100 | cur.next = node; 101 | return true; 102 | } 103 | else { 104 | i++; 105 | cur = cur.next; 106 | } 107 | } 108 | return false; 109 | } 110 | 111 | tail() { 112 | return this.tail; 113 | } 114 | 115 | _toArray() { 116 | let arr = []; 117 | let cur = this.head; 118 | while (cur) { 119 | arr.push(cur.value); 120 | cur = cur.next; 121 | } 122 | 123 | return arr; 124 | } 125 | } 126 | 127 | module.exports = LinkedList; 128 | 129 | /* TEST */ 130 | 131 | // let l = new LinkedList(); 132 | // l.append(3); 133 | // l.append(4); 134 | // l.append(10); 135 | // l.append(20); 136 | // l.append(5); 137 | 138 | // console.log(l.removeAt(1), 4); 139 | // console.log(l.pop().value, 5); 140 | 141 | // console.log(l._toArray()); 142 | // l.insertAt(2, 40); 143 | // console.log(l._toArray()); 144 | -------------------------------------------------------------------------------- /chapter04/util/Queue.js: -------------------------------------------------------------------------------- 1 | // implement a queue using linkedLists 2 | var LinkedList = require('./LinkedList'); 3 | 4 | class Queue { 5 | constructor() { 6 | this._list = new LinkedList(); 7 | } 8 | 9 | enqueue(value) { 10 | this._list.append(value); 11 | } 12 | 13 | dequeue() { 14 | let node = this._list.popFirst(); 15 | return node.value; 16 | } 17 | 18 | peek() { 19 | return this._list.head ? this._list.head.value : null; 20 | } 21 | 22 | isEmpty() { 23 | return this._list.head == null; 24 | } 25 | 26 | _toArray() { 27 | return this._list._toArray(); 28 | } 29 | } 30 | 31 | // alias 32 | Queue.prototype.add = Queue.prototype.enqueue; 33 | Queue.prototype.remove = Queue.prototype.dequeue; 34 | 35 | module.exports = Queue; 36 | 37 | /* TEST */ 38 | // var q = new Queue(); 39 | // q.add('a'); 40 | // q.add('b'); 41 | // q.add('c'); 42 | // console.log(q._toArray()); 43 | // console.log(q.remove(), 'a'); 44 | // console.log(q.peek(), 'b'); 45 | // console.log(q.remove(), 'b'); 46 | // console.log(q.remove(), 'c'); 47 | // console.log(q.isEmpty(), true); 48 | // console.log(q._toArray()); 49 | -------------------------------------------------------------------------------- /chapter04/util/Stack.js: -------------------------------------------------------------------------------- 1 | // simple implementation of a stack 2 | // using an Array 3 | 4 | /* 5 | Interface: 6 | - push(value) 7 | - pop() 8 | - peek() 9 | - isEmpty() 10 | - size() 11 | */ 12 | class Stack { 13 | constructor() { 14 | this._data = []; 15 | } 16 | 17 | size() { 18 | return this._data.length; 19 | } 20 | 21 | isEmpty() { 22 | return this.size() == 0; 23 | } 24 | 25 | push(value) { 26 | this._data.push(value); 27 | } 28 | 29 | pop() { 30 | return this._data.pop(); 31 | } 32 | 33 | peek() { 34 | if (this.isEmpty()) return null; 35 | return this._data[this.size() - 1]; 36 | } 37 | } 38 | 39 | module.exports = Stack; 40 | 41 | /* TEST */ 42 | 43 | // var s = new Stack(); 44 | // s.push('a'); 45 | // s.push('b'); 46 | // s.push('c'); 47 | // console.log(s.pop(), 'c'); 48 | // console.log(s.peek(), 'b'); 49 | // console.log(s.pop(), 'b'); 50 | // console.log(s.pop(), 'a'); 51 | // console.log(s.isEmpty(), true); 52 | -------------------------------------------------------------------------------- /chapter04/util/Tree.js: -------------------------------------------------------------------------------- 1 | var Tree = function(value) { 2 | this.value = value; 3 | this.children = []; 4 | }; 5 | 6 | module.exports = Tree; -------------------------------------------------------------------------------- /chapter05/5.1 - Insertion/insertion.js: -------------------------------------------------------------------------------- 1 | const insertion = (N, M, i, j) => { 2 | const bitMask = (-1 << (j + 1)) | ((1 << i) - 1); 3 | const clearedN = N & bitMask; 4 | const shiftedM = M << i; 5 | return clearedN | shiftedM; 6 | }; 7 | 8 | /* TEST */ 9 | const N = parseInt(10000000000, 2); 10 | const M = parseInt(10011, 2); 11 | console.log(insertion(M, N, 2, 6).toString(2), 10001001100); 12 | -------------------------------------------------------------------------------- /chapter05/5.2 - Binary to String/binaryToString.js: -------------------------------------------------------------------------------- 1 | var binaryToString = function(number) { 2 | var n = 1; 3 | var str = '0.'; 4 | while (n <= 32 && number > 0) { 5 | if (number >= Math.pow(2, -n)) { 6 | number -= Math.pow(2, -n); 7 | str += '1'; 8 | } else { 9 | str += '0'; 10 | } 11 | n++; 12 | } 13 | if (n === 33 && number > 0) { 14 | return 'ERROR'; 15 | } else { 16 | return str; 17 | } 18 | }; 19 | 20 | /* TEST */ 21 | console.log(binaryToString(0.625), '0.101'); 22 | console.log(binaryToString(0.6255342856783467856932), 'ERROR'); -------------------------------------------------------------------------------- /chapter05/5.3 - Flip Bit To Win/flipBitToWin.js: -------------------------------------------------------------------------------- 1 | var flipBitToWin = function(number) { 2 | var binaryString = number.toString(2); 3 | var arrOnes = binaryString.split('0'); 4 | var longest = 0; 5 | for (var i = 0; i < arrOnes.length - 1; i++) { 6 | if (arrOnes[i].length + arrOnes[i + 1].length > longest) { 7 | longest = arrOnes[i].length + arrOnes[i + 1].length; 8 | } 9 | } 10 | longest++; // add one representing the flipped bit 11 | return longest; 12 | }; 13 | 14 | /* TEST */ 15 | console.log(flipBitToWin(1775), 8); -------------------------------------------------------------------------------- /chapter05/5.4 - Next Number/nextNumber.js: -------------------------------------------------------------------------------- 1 | var countOnes = function(string) { 2 | var strArray = string.split(''); 3 | return strArray.reduce((count, char) => { 4 | return char === '1' ? count + 1 : count; 5 | }, 0); 6 | }; 7 | 8 | var next = function(number, numOnes, iterator) { 9 | var currNum = iterator(number); 10 | while (countOnes(currNum.toString(2)) !== numOnes) { 11 | currNum = iterator(currNum); 12 | } 13 | return currNum; 14 | }; 15 | 16 | var nextSmallest = function(number, numOnes) { 17 | return next(number, numOnes, currNum => currNum - 1); 18 | }; 19 | 20 | var nextLargest = function(number, numOnes) { 21 | return next(number, numOnes, currNum => currNum + 1); 22 | }; 23 | 24 | var nextNumber = function(number) { 25 | var binary = number.toString(2); 26 | var numOnes = countOnes(binary); 27 | console.log('number', number.toString(2)); 28 | console.log('nextSmallest', nextSmallest(number, numOnes).toString(2)); 29 | console.log('nextLargest', nextLargest(number, numOnes).toString(2)); 30 | }; 31 | 32 | // shift right most bit front for next largest, back for next smallest 33 | 34 | nextNumber(10000000000000000); 35 | -------------------------------------------------------------------------------- /chapter05/5.5 - Debugger/debugger.js: -------------------------------------------------------------------------------- 1 | var mysteriousFunction = function(n) { 2 | return ((n & (n - 1)) === 0); 3 | }; 4 | 5 | // hypothesis, returns true only for 2 ^ n 6 | // only in the case of 1000, 1000 - 1 is 0111, and n & (n - 1) === 0 7 | 8 | /* TEST */ 9 | for (var i = 1; i < 10000; i++) { 10 | if (mysteriousFunction(i)) { 11 | console.log(i); 12 | } 13 | } 14 | 15 | console.log(mysteriousFunction(Math.pow(2, 1)), true); 16 | console.log(mysteriousFunction(Math.pow(2, 1) + 1), false); 17 | console.log(mysteriousFunction(Math.pow(2, 2)), true); 18 | console.log(mysteriousFunction(Math.pow(2, 2) + 1), false); 19 | console.log(mysteriousFunction(Math.pow(2, 3)), true); 20 | console.log(mysteriousFunction(Math.pow(2, 3) + 1), false); 21 | console.log(mysteriousFunction(Math.pow(2, 4)), true); 22 | console.log(mysteriousFunction(Math.pow(2, 4) + 1), false); 23 | console.log(mysteriousFunction(Math.pow(2, 5)), true); 24 | console.log(mysteriousFunction(Math.pow(2, 5) + 1), false); 25 | console.log(mysteriousFunction(Math.pow(2, 6)), true); 26 | console.log(mysteriousFunction(Math.pow(2, 6) + 1), false); 27 | console.log(mysteriousFunction(Math.pow(2, 7)), true); 28 | console.log(mysteriousFunction(Math.pow(2, 7) + 1), false); -------------------------------------------------------------------------------- /chapter05/5.6 - Conversion/conversion.js: -------------------------------------------------------------------------------- 1 | var countBits = function(number) { 2 | var string = number.toString(2); 3 | return string.split('').reduce((numBits, char) => { 4 | return char === '1' ? numBits + 1 : numBits; 5 | }, 0); 6 | }; 7 | 8 | var conversion = function(number1, number2) { 9 | return countBits(number1 ^ number2); 10 | }; 11 | 12 | console.log(conversion(29, 15), 2); -------------------------------------------------------------------------------- /chapter05/5.7 - Pairwise Swap/pairwiseSwap.js: -------------------------------------------------------------------------------- 1 | var pairwiseSwap = function(number) { 2 | var oddBits = (number >> 1).toString(2); 3 | var evenBits = (number << 1).toString(2); 4 | console.log(oddBits, evenBits); 5 | var isOdd = true; 6 | var answer = ''; 7 | for (var i = 0; i < number.toString(2).length; i++) { 8 | if (isOdd) { 9 | answer = oddBits[oddBits.length - 1 - i] === undefined ? 10 | '0' + answer : 11 | oddBits[oddBits.length - 1 - i] + answer; 12 | } else { 13 | answer = oddBits[oddBits.length - 1 - i] === undefined ? 14 | '0' + answer : 15 | evenBits[evenBits.length - 1 - i] + answer; 16 | } 17 | isOdd = !isOdd; 18 | } 19 | return answer; 20 | }; 21 | 22 | /* TEST */ 23 | console.log(pairwiseSwap(4)); 24 | -------------------------------------------------------------------------------- /chapter05/5.8 - Draw Line/drawLine.js: -------------------------------------------------------------------------------- 1 | var drawLine = function(screen, width, x1, x2, y) { 2 | var byte; 3 | var pixel; 4 | 5 | var findByte = function(x, y, width) { 6 | var start = y * width / 8; 7 | return start + Math.floor(x / 8); 8 | }; 9 | 10 | var findPixel = function(x) { 11 | return x % 8; 12 | }; 13 | 14 | for (var i = x1; i <= x2; i++) { 15 | byte = findByte(i, y, width); 16 | pixel = findPixel(i); 17 | screen[byte][pixel] = 1; 18 | } 19 | return screen; 20 | }; 21 | 22 | /* Test */ 23 | 24 | var testScreen = [[0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0], 25 | [0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0], 26 | [0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0], 27 | [0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0]]; 28 | drawLine(testScreen, 16, 4, 12, 1); 29 | console.log(testScreen); 30 | -------------------------------------------------------------------------------- /chapter06/6.01 - Heavy Pill/heavyPill.js: -------------------------------------------------------------------------------- 1 | // Step 1: For that one use, you must measure at least 19 bottles. If that one bottle is 2 | // 1.1 grams, then the other 19 bottles will be normal 3 | 4 | // Step 2: For that 19 bottles, the one measurement must be done in a way that you can 5 | // differentiate one bottle from the other bottles. The basis of measurement is weight 6 | // of each pill 7 | 8 | // Step 3: To reduce the problem, we can assume that there are less bottles, and work through 9 | // the base cases 10 | // 11 | // 1 bottle: it has pills of 1.1 grams, measure one pill on weighing scale to confirm 12 | // 2 bottles: measure one pill from one bottle on weighing scale to confirm 13 | // 3 bottles: leave out one bottle, measure pills from remaining two bottles to confirm 14 | 15 | // Step 4: To differentiate each bottle, vary the number of pills from each bottle. 16 | // 17 | // bottle 1: 1 pill 18 | // bottle 2: 2 pills 19 | // bottle 3: 3 pills 20 | // .... 21 | // bottle 19: 19 pills 22 | 23 | // Step 5: Count the number of pills 24 | 25 | // Step 6: Measure entire bunch of pills in one shot 26 | 27 | // Step 7: Excess weight reveals the bottle with overweight pills -------------------------------------------------------------------------------- /chapter06/6.02 - Basketball/basketBall.js: -------------------------------------------------------------------------------- 1 | // Probability of winning Game 1 is p 2 | // Probability of winning Game 2 is 3 | // [LWW]: (1 - p) * p * p + 4 | // [WLW]: p * (1 - p) * p + 5 | // [WWL]: p * p * (1 - p) + 6 | // [WWW]: p * p * p 7 | // = 3p^2 - 3p^3 + p^3 8 | // = 3p^2 - 2p^3 9 | 10 | // Pick Game 1 when p > 3p^2 - 2p^3 11 | // Pick Game 2 when p < 3p^2 - 2p^3 12 | 13 | // 2p^3 - 3p^2 + p 14 | 15 | // p(2p^2 - 3p + 1) 16 | 17 | // p(2p - 1)(p - 1) 18 | 19 | // intersections are p = 0, p = 0.5, p = 1 20 | 21 | // test p and 3p^2 - 2p^3 at p = 0, 0.25. 0.5, 0.75, and 1 -------------------------------------------------------------------------------- /chapter06/6.03 - Dominos/dominos.js: -------------------------------------------------------------------------------- 1 | // Step 1: Look at base assumptions of problem 2 | // 8 x 8 chessboard = 64 squares 3 | // cut off 2 squares = 62 squares 4 | // 31 dominos of 2 squares = 62 squares 5 | 6 | // Step 2: Reduce problem 7 | // 2 x 2 chessboard = 4 squares 8 | // cut off 2 squares = 2 squares 9 | // 1 domino of 2 squares = 2 squares 10 | // => impossible unless cut domino into 2 11 | 12 | // Step 3: Test suspicion that it is impossible even for 8x8 chessboard 13 | // every domino must have two adjecent squares to place on chessboard 14 | // cutting opposite corners deprived two squares of adjacent squares 15 | // 16 | // if we viewed a chessboard as alternative black and white squares, 17 | // two black squares or two white squares have been cut off 18 | // 19 | // a domino needs a black square and a white square to occupy the chessboard -------------------------------------------------------------------------------- /chapter06/6.04 - Ants on a Triangle/antsOnATriangle.js: -------------------------------------------------------------------------------- 1 | // they do not collide only if: 2 | // clockwise, clockwise, clockwise or 3 | // anticlockwise, anticlockwise, anticlockwise 4 | 5 | // since the probability of not colliding is 2 / (2 * 2 * 2) 6 | // then the probability of colliding is 1 - 2 / (2 ^ 3) 7 | 8 | // the generalized solution for non-collision is 1 - 2 / (2 ^ n) -------------------------------------------------------------------------------- /chapter06/6.05 - Jugs of Water/jugsOfWater.js: -------------------------------------------------------------------------------- 1 | // i) Fill 5-quart jug 2 | // ii) Pour from 5-quart jug to 3-quart jug (5-quart will now have 2 quarts inside) 3 | // iii) Empty 3-quart jug 4 | // iv) Pour from 5-quart jug to 3-quart jug (3-quart will now have 2 quarts) 5 | // v) Fill 5-quart jug 6 | // vi) Pour from 5-quart jug to 3-quart. be careful not to spill! 7 | // vii) 5-quart jug will now have 4-quarts of water! -------------------------------------------------------------------------------- /chapter06/6.06 - Blue-Eyed Island/blueEyedIsland.js: -------------------------------------------------------------------------------- 1 | // 1 person: sees no one having blue eyes, leaves the next day (1 day) 2 | // 2 persons: sees one other person having blue eyes, waits for a day 3 | // realises that he/she is not leaving, and hence there must be another 4 | // blue-eye which is him/herself. hence leaves the following day (2 days) 5 | // 3 persons: sees 2 other persons, waits for 2 days since they should realise by then, 6 | // if they still do not leave, will realise that there is one more person and 7 | // will leave on the following day (3 days) 8 | // ... 9 | // n persons: will leave in n days -------------------------------------------------------------------------------- /chapter06/6.07 - The Apocalypse/theApocalypse.js: -------------------------------------------------------------------------------- 1 | // (p = 1 / 2) => 0 boy 1 girl 2 | // (p = 1 / 4) => 1 boy 1 girl 3 | // (p = 1 / 8) => 2 boy 1 girl 4 | // (p = 1 / 16) => 3 boy 1 girl 5 | // (p = 1 / 32) => 4 boy 1 girl 6 | // (p = 1 / 64) => 5 boy 1 girl 7 | // ... 8 | // (p = 1 / 2 ^ (n - 1)) => n boys 1 girl 9 | 10 | // 0 / 2 ^ 1 + 1 / 2^2 + 2 / 2^3 ... geometric + arimetric series sum 11 | 12 | var findRatio = function(n) { 13 | var answer = 0; 14 | for (var i = 1; i < n; i++) { 15 | answer += (i - 1) / Math.pow(2, i); 16 | } 17 | return answer; 18 | }; 19 | 20 | console.log(findRatio(100)); 21 | console.log(findRatio(1000)); 22 | console.log(findRatio(10000)); 23 | console.log(findRatio(100000)); 24 | 25 | // the ratio seems to tend to 1 -------------------------------------------------------------------------------- /chapter06/6.08 - The Egg Drop Problem/eggDrop.js: -------------------------------------------------------------------------------- 1 | // Approach: balance out most number of drops that each egg needs to make. 2 | 3 | // better: two by two upwards, if breaks, then drop egg on floor right below (2 eggs) 4 | // (maximum of 52 drops to find floor) 5 | 6 | // best: drop first egg 10 floors by 10 floors. If egg breaks, then go back to prior multiple 7 | // of 10 and drop floor by floor until egg breaks (maximum of 20 drops to find floor) -------------------------------------------------------------------------------- /chapter06/6.09 - 100 Lockers/hundredLockers.js: -------------------------------------------------------------------------------- 1 | // Step 1: reduce the problem into 2 doors -> door 1 2 | // Step 2: 3 doors -> door 1 3 | // Step 3: 4 doors -> doors 1, 4 4 | // Step 4: 5 doors -> doors 1, 4 5 | // Step 5: 6 doors -> doors 1, 4, 9 6 | // ... 7 | // 100 doors -> doors, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100 8 | 9 | // all the square numbers are left open -> 10 doors are left open -------------------------------------------------------------------------------- /chapter06/6.10 - Poison/poison.js: -------------------------------------------------------------------------------- 1 | var genCombinations = function() { 2 | var answers = []; 3 | var recurse = function(current, pointer, array) { 4 | if (pointer === array.length) { 5 | answers.push(current); 6 | } else { 7 | recurse(current.concat([array[pointer]]), pointer + 1, array); 8 | recurse(current, pointer + 1, array); 9 | } 10 | }; 11 | recurse([], 0, ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']); 12 | return answers; 13 | }; 14 | 15 | var makeTest = function(array) { 16 | var tests = {}; 17 | for (var i = 0; i < array.length; i++) { 18 | array[i].forEach((test) => { 19 | if (tests[test] === undefined) { 20 | tests[test] = [i + 1]; 21 | } else { 22 | tests[test].push(i + 1); 23 | } 24 | }); 25 | } 26 | return tests; 27 | }; 28 | 29 | console.log(makeTest(genCombinations())); // 1024 combinations -------------------------------------------------------------------------------- /chapter06/util/notes.js: -------------------------------------------------------------------------------- 1 | // Sides Notes About Prime Numbers 2 | // 3 | // all positive integers can be decomposed into a product of primes. 4 | // 5 | // for x mod y to be zero, all primes in x's prime factorization should be in y's 6 | // prime factorization 7 | // 8 | // greatest common denominator (gcd) - min prime factorization 9 | // least common multiple (lcm) - max prime factorization 10 | // 11 | // checking for primality - iterate up to square root of n 12 | // 13 | // sieve of eratosthenes -------------------------------------------------------------------------------- /chapter07/7.01 - Deck of Cards/deckOfCards.js: -------------------------------------------------------------------------------- 1 | var Card = function(suit, number) { 2 | this.suit = suit; 3 | this.number = number; 4 | this.value = `${this.number} ${this.suit}`; 5 | }; 6 | 7 | var Deck = function() { 8 | this.cards = []; 9 | this.newDeck(); 10 | }; 11 | 12 | Deck.prototype.newDeck = function() { 13 | this.clear(); 14 | var suits = ['\u2660', '\u2663', '\u2665', '\u2666']; 15 | var numbers = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']; 16 | suits.forEach((suit) => { 17 | numbers.forEach((number) => { 18 | this.cards.push(new Card(suit, number)); 19 | }); 20 | }); 21 | }; 22 | 23 | Deck.prototype.clear = function() { 24 | while (this.cards.length > 0) { 25 | this.cards.pop(); 26 | } 27 | }; 28 | 29 | Deck.prototype.shuffle = function() { 30 | this.cards.sort(() => Math.random() > 0.5 ? 1 : -1); 31 | }; 32 | 33 | Deck.prototype.deal = function() { 34 | return this.cards.pop(); 35 | }; 36 | 37 | // dealer - hand and deck 38 | var Dealer = function() { 39 | this.deck = new Deck(); 40 | this.hand = []; 41 | }; 42 | 43 | Dealer.prototype.shuffleCards = function() { 44 | this.deck.shuffle(); 45 | this.deck.shuffle(); 46 | this.deck.shuffle(); 47 | }; 48 | 49 | Dealer.prototype.dealCard = function() { 50 | return this.deck.deal(); 51 | }; 52 | 53 | Dealer.prototype.receiveCard = function(card) { 54 | this.hand.push(card); 55 | }; 56 | 57 | var Player = function() { 58 | this.hand = []; 59 | }; 60 | 61 | Player.prototype.receiveCard = function(card) { 62 | this.hand.push(card); 63 | }; 64 | 65 | Player.prototype.discardHand = function() { 66 | this.hand = []; 67 | }; 68 | 69 | // blackjack game table 70 | 71 | var Table = function() { 72 | this.dealer = new Dealer(); 73 | this.players = []; 74 | }; 75 | 76 | Table.prototype.join = function(player) { 77 | if (this.players.length > 5) { 78 | console.log('player is full'); 79 | } else if (this.players.indexOf(player) > -1) { 80 | console.log('player is already on table'); 81 | } else { 82 | this.players.push(player); 83 | } 84 | }; 85 | 86 | Table.prototype.runGame = function() { 87 | var dealer = this.dealer; 88 | var players = this.players; 89 | 90 | if (players.length === 0) { 91 | console.log('no players on table: game did not take place'); 92 | } else { 93 | console.log('start blackjack game!'); 94 | dealer.shuffleCards(); 95 | for (var i = 0; i < 2; i++) { 96 | players.forEach((player) => { 97 | player.receiveCard(dealer.dealCard()); 98 | }); 99 | dealer.receiveCard(dealer.dealCard()); 100 | } 101 | console.log('dealer hand', dealer.hand.map((card) => card.value)); 102 | players.forEach((player) => { 103 | console.log('player hand', player.hand.map((card) => card.value)); 104 | }); 105 | } 106 | }; 107 | 108 | /* TEST */ 109 | var table = new Table(); 110 | var eugene = new Player(); 111 | var david = new Player(); 112 | var luis = new Player(); 113 | var eric = new Player(); 114 | 115 | table.join(eugene); 116 | table.join(david); 117 | table.join(luis); 118 | table.join(eric); 119 | 120 | /* build until dealing of first hand */ 121 | table.runGame(); 122 | -------------------------------------------------------------------------------- /chapter07/7.02 - Call Center/callCenter.js: -------------------------------------------------------------------------------- 1 | /* Import Queue */ 2 | var Queue = require('./../util/Queue.js'); 3 | 4 | /* Employee class */ 5 | var Employee = function(name) { 6 | this.name = name; 7 | }; 8 | 9 | Employee.prototype.dispatch = function(call, queue) { 10 | var context = this; 11 | setTimeout(function() { 12 | queue.add(context); 13 | console.log(`adding ${context.name} back to queue`); 14 | }, call.time); 15 | }; 16 | 17 | /* Call class */ 18 | var Call = function(time) { 19 | this.time = time; // time the call will take in ms 20 | }; 21 | 22 | /* Call Center class */ 23 | var CallCenter = function() { 24 | this.respondentQ = new Queue(); 25 | this.managerQ = new Queue(); 26 | this.directorQ = new Queue(); 27 | this.open = false; 28 | this.init = false; 29 | }; 30 | 31 | CallCenter.prototype.start = function() { 32 | if (this.init) { 33 | console.log('already intialized'); 34 | return; 35 | } 36 | // 3 employees of each type 37 | for (var i = 0; i < 3; i++) { 38 | this.respondentQ.add(new Employee(`resp${i}`)); 39 | this.managerQ.add(new Employee(`manager${i}`)); 40 | this.directorQ.add(new Employee(`director${i}`)); 41 | } 42 | this.init = true; 43 | }; 44 | 45 | CallCenter.prototype.dispatchCall = function(call) { 46 | var employee; 47 | if (!this.respondentQ.isEmpty()) { 48 | employee = this.respondentQ.remove(); 49 | console.log(employee, 'will be deployed'); 50 | employee.dispatch(call, this.respondentQ); 51 | console.log('a respondent will be taking your call!'); 52 | } else if (!this.managerQ.isEmpty()) { 53 | employee = this.managerQ.remove(); 54 | console.log(employee, 'will be deployed'); 55 | employee.dispatch(call, this.managerQ); 56 | console.log('a manager will be taking your call!'); 57 | } else if (!this.directorQ.isEmpty()) { 58 | employee = this.directorQ.remove(); 59 | console.log(employee, 'will be deployed'); 60 | employee.dispatch(call, this.directorQ); 61 | console.log('a director will be taking your call!'); 62 | } else { 63 | console.log('sorry, there are currently no available staff to take your call :('); 64 | } 65 | }; 66 | 67 | /* Test */ 68 | 69 | var cc = new CallCenter(); 70 | cc.start(); 71 | cc.dispatchCall(new Call(500)); 72 | cc.dispatchCall(new Call(500)); 73 | cc.dispatchCall(new Call(2000)); 74 | cc.dispatchCall(new Call(500)); 75 | cc.dispatchCall(new Call(500)); 76 | cc.dispatchCall(new Call(4000)); 77 | cc.dispatchCall(new Call(500)); 78 | cc.dispatchCall(new Call(500)); 79 | cc.dispatchCall(new Call(500)); 80 | cc.dispatchCall(new Call(6000)); 81 | cc.dispatchCall(new Call(500)); 82 | cc.dispatchCall(new Call(500)); 83 | setTimeout(function() { 84 | cc.dispatchCall(new Call(500)); 85 | cc.dispatchCall(new Call(500)); 86 | cc.dispatchCall(new Call(500)); 87 | cc.dispatchCall(new Call(500)); 88 | cc.dispatchCall(new Call(500)); 89 | cc.dispatchCall(new Call(500)); 90 | cc.dispatchCall(new Call(500)); 91 | cc.dispatchCall(new Call(500)); 92 | }, 1000); 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /chapter07/7.03 - Jukebox/jukebox.js: -------------------------------------------------------------------------------- 1 | var Player = function() { 2 | this.songQueue = []; 3 | this.currentSong = null; 4 | }; 5 | 6 | Player.prototype.shuffle = function() { 7 | this.songQueue.sort(() => { return Math.random() > 0.5 ? 1 : 0; }); 8 | }; 9 | 10 | Player.prototype.play = function() { 11 | if (this.currentSong === null) { 12 | this.currentSong = this.songQueue.unshift(); 13 | this.currentSong.play(); 14 | } else { 15 | console.log('song currently playing'); 16 | } 17 | }; 18 | 19 | Player.prototype.pause = function() { 20 | if (this.currentSong !== null) { 21 | this.currentSong.pause(); 22 | } else { 23 | console.log('no song in queue'); 24 | } 25 | }; 26 | 27 | Player.prototype.nextSong = function() { 28 | this.currentSong = this.songQueue.unshift(); 29 | this.currentSong.play(); 30 | }; 31 | 32 | Player.prototype.addSong = function(song) { 33 | this.songQueue.push(song); 34 | }; 35 | 36 | Player.prototype.currentSongName = function() { 37 | return this.currentSong.name; 38 | }; 39 | 40 | // -songs 41 | // -trackUrl 42 | // -name 43 | // -play 44 | // -pause 45 | 46 | var Song = function(name, trackFile) { 47 | this.name = name; 48 | this.track = new Audio(trackFile); 49 | }; 50 | 51 | Song.prototype.play = function() { 52 | this.track.play(); 53 | }; 54 | 55 | Song.prototype.pause = function() { 56 | this.track.play(); 57 | }; 58 | 59 | /* TEST */ 60 | -------------------------------------------------------------------------------- /chapter07/7.04 - Parking Lot/parkingLot.js: -------------------------------------------------------------------------------- 1 | var ParkingLot = function(spaces) { 2 | this.limit = spaces; 3 | this.number = 0; 4 | this.cars = {}; 5 | }; 6 | 7 | ParkingLot.prototype.park = function(car) { 8 | if (this.number <= this.limit) { 9 | if (this.cars[car.name] !== undefined) { 10 | console.log('car is already inside parking lot'); 11 | } else { 12 | this.cars[car.name] = car; 13 | this.number++; 14 | } 15 | } else { 16 | console.log('the parking lot is full!'); 17 | } 18 | }; 19 | 20 | ParkingLot.prototype.exit = function(car) { 21 | if (this.number === 0) { 22 | console.log('there are no cars in the parking lot'); 23 | } else if (this.cars[car.name] === undefined) { 24 | console.log('the car is not in the parking lot'); 25 | } else { 26 | delete this.cars[car.name]; 27 | this.number--; 28 | } 29 | }; 30 | 31 | ParkingLot.prototype.available = function() { 32 | return this.number < this.limit; 33 | }; -------------------------------------------------------------------------------- /chapter07/7.05 - Online Book Reader/onlineBookReader.js: -------------------------------------------------------------------------------- 1 | var BookReader = function() { 2 | this.currentBook = null; 3 | this.books = {}; 4 | }; 5 | 6 | BookReader.prototype.add = function(book) { 7 | this.books[book.name] = book; 8 | }; 9 | 10 | BookReader.prototype.find = function(bookname) { 11 | return this.books[bookname]; 12 | }; 13 | 14 | BookReader.prototype.open = function(bookname) { 15 | this.currentBook = this.books[bookname]; 16 | return this.currentBook.file; 17 | }; 18 | 19 | var Book = function(name, file) { 20 | this.name = name; 21 | this.file = file; 22 | }; 23 | -------------------------------------------------------------------------------- /chapter07/7.06 - Jigsaw/jigsaw.js: -------------------------------------------------------------------------------- 1 | // Approach each jigsaw pieces as a graph node with 4 edges 2 | // with the puzzle as a graph 3 | 4 | var JigsawPiece = function(row, col) { 5 | this._position = { row: row, col: col }; 6 | this.name = Math.random().toString(36).substring(7); // generate random hash 7 | this.up = null; 8 | this.down = null; 9 | this.left = null; 10 | this.right = null; 11 | }; 12 | 13 | var JigsawPuzzle = function(N) { 14 | this.jigsawGraph = this.genPieces(N); 15 | this.N = N; 16 | }; 17 | 18 | JigsawPuzzle.prototype.genPieces = function(N) { 19 | var graph = {}; 20 | var array = []; 21 | for (var i = 0; i < N; i++) { 22 | for (var j = 0; j < N; j++) { 23 | array.push(new JigsawPiece(i, j)); 24 | } 25 | } 26 | array.sort(() => Math.random() < 0.5 ? 1 : -1); 27 | array.forEach((piece) => { 28 | graph[piece.name] = piece; 29 | }); 30 | return graph; 31 | }; 32 | 33 | JigsawPuzzle.prototype.match = function(piece1, piece2, direction) { 34 | if (piece1 === null || piece2 === null) { 35 | return false; 36 | } else if (direction === 'up') { 37 | return (piece1._position.row === piece2._position.row + 1) && 38 | (piece1._position.col === piece2._position.col); 39 | } else if (direction === 'down') { 40 | return (piece1._position.row === piece2._position.row - 1) && 41 | (piece1._position.col === piece2._position.col); 42 | } else if (direction === 'left') { 43 | return (piece1._position.row === piece2._position.row) && 44 | (piece1._position.col === piece2._position.col + 1); 45 | } else if (direction === 'right') { 46 | return (piece1._position.row === piece2._position.row) && 47 | (piece1._position.col === piece2._position.col - 1); 48 | } else { 49 | throw 'error, no direction'; 50 | } 51 | }; 52 | 53 | JigsawPuzzle.prototype.checkPiece = function(piece) { 54 | // check up 55 | // check null if at top row 56 | if (piece._position.row === 0 && piece.up !== null) { 57 | return false; 58 | // match otherwise 59 | } else if (piece._position.row !== 0 && !this.match(piece, piece.up, 'up')) { 60 | return false; 61 | } 62 | 63 | // check down 64 | // check null if at bottom row 65 | if (piece._position.row === this.N - 1 && piece.down !== null) { 66 | return false; 67 | // match otherwise 68 | } else if (piece._position.row !== this.N - 1 && !this.match(piece, piece.down, 'down')) { 69 | return false; 70 | } 71 | 72 | // check left 73 | // check null if at first col 74 | if (piece._position.col === 0 && piece.left !== null) { 75 | return false; 76 | // match otherwise 77 | } else if (piece._position.col !== 0 && !this.match(piece, piece.left, 'left')) { 78 | return false; 79 | } 80 | 81 | // check right 82 | // check null if at last col 83 | if (piece._position.col === this.N - 1 && piece.right !== null) { 84 | return false; 85 | // match otherwise 86 | } else if (piece._position.col !== this.N - 1 && !this.match(piece, piece.right, 'right')) { 87 | return false; 88 | } 89 | 90 | return true; 91 | }; 92 | 93 | JigsawPuzzle.prototype.checkDone = function() { 94 | for (var piece in this.jigsawGraph) { 95 | if (!this.checkPiece(this.jigsawGraph[piece])) { 96 | return false; 97 | } 98 | } 99 | return true; 100 | }; 101 | 102 | JigsawPuzzle.prototype.fitPuzzle = function() { 103 | for (var piece1 in this.jigsawGraph) { 104 | for (var piece2 in this.jigsawGraph) { 105 | if (this.match(this.jigsawGraph[piece1], this.jigsawGraph[piece2], 'up')) { 106 | this.jigsawGraph[piece1].up = this.jigsawGraph[piece2]; 107 | this.jigsawGraph[piece2].down = this.jigsawGraph[piece1]; 108 | } else if (this.match(this.jigsawGraph[piece1], this.jigsawGraph[piece2], 'down')) { 109 | this.jigsawGraph[piece1].down = this.jigsawGraph[piece2]; 110 | this.jigsawGraph[piece2].up = this.jigsawGraph[piece1]; 111 | } else if (this.match(this.jigsawGraph[piece1], this.jigsawGraph[piece2], 'left')) { 112 | this.jigsawGraph[piece1].left = this.jigsawGraph[piece2]; 113 | this.jigsawGraph[piece2].right = this.jigsawGraph[piece1]; 114 | } else if (this.match(this.jigsawGraph[piece1], this.jigsawGraph[piece2], 'right')) { 115 | this.jigsawGraph[piece1].right = this.jigsawGraph[piece2]; 116 | this.jigsawGraph[piece2].left = this.jigsawGraph[piece1]; 117 | } 118 | } 119 | } 120 | }; 121 | 122 | /* TEST */ 123 | var jz = new JigsawPuzzle(2); 124 | console.log(jz.jigsawGraph); 125 | 126 | console.log(jz.checkDone(), false); 127 | 128 | jz.fitPuzzle(); 129 | console.log(jz.jigsawGraph); 130 | 131 | console.log(jz.checkDone(), true); 132 | -------------------------------------------------------------------------------- /chapter07/7.07 - Chat Server/chatServer.js: -------------------------------------------------------------------------------- 1 | /* frontend */ 2 | 3 | // -login 4 | // -user 5 | // -password 6 | // -dashboard 7 | // -chatrooms 8 | // -messages 9 | // -array of messages 10 | // -enter messages 11 | // -input 12 | // -websockets 13 | // -emit when message is sent 14 | // -listen to when response comes back from server 15 | 16 | /* backend */ 17 | 18 | // -server 19 | // -getMessages 20 | // -postMessage 21 | // -database 22 | // -users, chatrooms 23 | // -websockets 24 | // -listen to when messages are sent from client 25 | // -save to database 26 | // -emit back to client 27 | -------------------------------------------------------------------------------- /chapter07/7.08 - Othello/othello.js: -------------------------------------------------------------------------------- 1 | var OthPiece = function(color) { 2 | this.side = color; 3 | }; 4 | 5 | OthPiece.prototype.flip = function() { 6 | if (this.side === 'white') { 7 | this.side = 'black'; 8 | } else { 9 | this.side = 'white'; 10 | } 11 | }; 12 | 13 | var OthBoard = function(N = 8) { 14 | if (N % 2 !== 0) { throw 'error, board must be of even length'; } 15 | this.N = N; 16 | this.board = this.genBoard(N); 17 | this.board[N/2 - 1][N/2 - 1] = new OthPiece('white'); 18 | this.board[N/2][N/2] = new OthPiece('white'); 19 | this.board[N/2 - 1][N/2] = new OthPiece('black'); 20 | this.board[N/2][N/2 - 1] = new OthPiece('black'); 21 | }; 22 | 23 | OthBoard.prototype.genBoard = function(N) { 24 | var board = []; 25 | for (var i = 0; i < N; i++) { 26 | var row = []; 27 | for (var j = 0; j < N; j++) { 28 | row.push(null); 29 | } 30 | board.push(row); 31 | } 32 | return board; 33 | }; 34 | 35 | OthBoard.prototype.logBoard = function() { 36 | for (var i = 0; i < this.N; i++) { 37 | console.log(this.board[i].map((piece) => { 38 | return piece !== null ? piece.side : null; 39 | })); 40 | } 41 | }; 42 | 43 | OthBoard.prototype.placePiece = function(color, row, col) { 44 | if (this.board[row][col] === null) { 45 | console.log('piece already exists in that place'); 46 | } else if (!this.legalMove(color, row, col)) { 47 | console.log('this is not a legal move'); 48 | } else { 49 | this.board[row][col] = new OthPiece(color); 50 | this.flipPieces(color, row, col); 51 | } 52 | }; 53 | 54 | OthBoard.prototype.canFlip = function(color, row, col, direction) { 55 | var directionPos = { 56 | up: [-1, 0], 57 | down: [1, 0], 58 | left: [0, -1], 59 | right: [0, 1], 60 | upLeft: [-1, -1], 61 | upRight: [-1, 1], 62 | downLeft: [1, -1], 63 | downRight: [1, 1] 64 | }; 65 | 66 | var flipColor = { 67 | white: 'black', 68 | black: 'white' 69 | }; 70 | 71 | var movement = directionPos[direction]; 72 | var currRow = row + movement[0]; 73 | var currCol = col + movement[1]; 74 | if (currRow === this.N || currCol === this.N || currRow === -1 || currCol === - 1) { 75 | return false; 76 | } 77 | if (this.board[currRow][currCol] === null || this.board[currRow][currCol].side === color) { 78 | return false; 79 | } 80 | while(this.board[currRow][currCol] !== null && this.board[currRow][currCol].side === flipColor[color]) { 81 | currRow = currRow + movement[0]; 82 | currCol = currCol + movement[1]; 83 | } 84 | return this.board[currRow][currCol] !== null && this.board[currRow][currCol].side === color; 85 | }; 86 | 87 | OthBoard.prototype.PiecesToFlip = function(color, row, col) { 88 | return this.canFlip(color, row, col, 'up') || 89 | this.canFlip(color, row, col, 'down') || 90 | this.canFlip(color, row, col, 'left') || 91 | this.canFlip(color, row, col, 'right') || 92 | this.canFlip(color, row, col, 'upLeft') || 93 | this.canFlip(color, row, col, 'upRight') || 94 | this.canFlip(color, row, col, 'downLeft') || 95 | this.canFlip(color, row, col, 'downRight'); 96 | }; 97 | 98 | OthBoard.prototype.legalMove = function(color, row, col) { 99 | console.log('checking legal move'); 100 | if (this.board[row][col] !== null) { 101 | console.log(`${row}, ${col} is already taken up.`); 102 | return false; 103 | } else if (!this.PiecesToFlip(color, row, col)) { 104 | console.log(`${row}, ${col} - no pieces for ${color} to flip`); 105 | return false; 106 | } else { 107 | return true; 108 | } 109 | }; 110 | 111 | OthBoard.prototype.flipPiece = function(color, row, col, direction) { 112 | var directionPos = { 113 | up: [-1, 0], 114 | down: [1, 0], 115 | left: [0, -1], 116 | right: [0, 1], 117 | upLeft: [-1, -1], 118 | upRight: [-1, 1], 119 | downLeft: [1, -1], 120 | downRight: [1, 1] 121 | }; 122 | 123 | var flipColor = { 124 | white: 'black', 125 | black: 'white' 126 | }; 127 | 128 | var movement = directionPos[direction]; 129 | var currRow = row + movement[0]; 130 | var currCol = col + movement[1]; 131 | while(this.board[currRow][currCol].side === flipColor[color]) { 132 | this.board[currRow][currCol].side = color; 133 | currRow = row + movement[0]; 134 | currCol = col + movement[1]; 135 | } 136 | }; 137 | 138 | OthBoard.prototype.flipPieces = function(color, row, col) { 139 | if (this.canFlip(color, row, col, 'up')) { 140 | this.flipPiece(color, row, col, 'up'); 141 | } 142 | 143 | if (this.canFlip(color, row, col, 'down')) { 144 | this.flipPiece(color, row, col, 'down'); 145 | } 146 | 147 | if (this.canFlip(color, row, col, 'left')) { 148 | this.flipPiece(color, row, col, 'left'); 149 | } 150 | 151 | if (this.canFlip(color, row, col, 'right')) { 152 | this.flipPiece(color, row, col, 'right'); 153 | } 154 | 155 | if (this.canFlip(color, row, col, 'upLeft')) { 156 | this.flipPiece(color, row, col, 'upLeft'); 157 | } 158 | 159 | if (this.canFlip(color, row, col, 'upRight')) { 160 | this.flipPiece(color, row, col, 'upRight'); 161 | } 162 | 163 | if (this.canFlip(color, row, col, 'downLeft')) { 164 | this.flipPiece(color, row, col, 'downLeft'); 165 | } 166 | 167 | if (this.canFlip(color, row, col, 'downRight')) { 168 | this.flipPiece(color, row, col, 'downRight'); 169 | } 170 | }; 171 | 172 | OthBoard.prototype.placePiece = function(color, row, col) { 173 | if (!this.legalMove(color, row, col)) { 174 | console.log('this move is not legal'); 175 | } else { 176 | console.log('placing piece'); 177 | this.board[row][col] = new OthPiece(color); 178 | this.flipPieces(color, row, col); 179 | } 180 | }; 181 | 182 | OthBoard.prototype.checkScore = function() { 183 | var score = { 184 | black: 0, 185 | white: 0 186 | }; 187 | 188 | for (var i = 0; i < this.N; i++) { 189 | for (var j = 0; j < this.N; j++) { 190 | if (this.board[i][j] !== null) { 191 | score[this.board[i][j].side]++; 192 | } 193 | } 194 | } 195 | console.log(`the current score is: black ${score.black}, white ${score.white}`); 196 | }; 197 | 198 | /* TEST */ 199 | var ob = new OthBoard(); 200 | ob.logBoard(); 201 | ob.checkScore(); 202 | ob.placePiece('black', 2, 3); 203 | ob.logBoard(); 204 | ob.placePiece('white', 5, 5); 205 | ob.placePiece('white', 4, 2); 206 | ob.logBoard(); 207 | ob.placePiece('black', 7, 7); 208 | ob.placePiece('black', 5, 5); 209 | ob.logBoard(); 210 | -------------------------------------------------------------------------------- /chapter07/7.09 - Circular Array/circularArray.js: -------------------------------------------------------------------------------- 1 | var CircularArray = function() { 2 | this.array = []; 3 | this.front = null; 4 | this.back = null; 5 | }; 6 | 7 | CircularArray.prototype.rotate = function() { 8 | if (this.array.length > 0) { 9 | this.front = (this.front + 1) % this.array.length; 10 | this.back = (this.back + 1) % this.array.length; 11 | } 12 | }; 13 | 14 | CircularArray.prototype.push = function(value) { 15 | if (this.array.length === 0) { 16 | this.array.push(value); 17 | this.front = 0; 18 | this.back = 0; 19 | } else if (this.front <= this.back) { 20 | this.array.push(value); 21 | this.back++; 22 | } else { 23 | this.array = this.array.slice(0, this.back + 1).concat([value]).concat(this.array.slice(this.front)); 24 | this.back++; 25 | } 26 | }; 27 | 28 | CircularArray.prototype.pop = function() { 29 | if (this.array.length === 0) { 30 | return; 31 | } else if (this.front <= this.back) { 32 | var answer = this.array.pop(); 33 | this.back--; 34 | return answer; 35 | } else { 36 | var answer = this.array[this.back]; 37 | this.array = this.array.slice(0, this.back).concat(this.array.slice(this.front)); 38 | this.back--; 39 | return answer; 40 | } 41 | }; 42 | 43 | /* TEST */ 44 | var ca = new CircularArray(); 45 | ca.push(1); 46 | ca.push(2); 47 | ca.push(3); 48 | ca.push(4); 49 | ca.push(5); 50 | ca.rotate(); 51 | ca.rotate(); 52 | console.log(ca.array, ca.front, ca.back, '[1, 2, 3, 4, 5], 2, 1'); // [1, 2, 3, 4, 5], 2, 1 53 | console.log(ca.pop(), 2); // 2 54 | 55 | 56 | -------------------------------------------------------------------------------- /chapter07/7.10 - Minesweeper/minesweeper.js: -------------------------------------------------------------------------------- 1 | var Minesweeper = function(N, B) { 2 | this.board = null; 3 | this.init(N, B); 4 | }; 5 | 6 | Minesweeper.prototype.init = function(N, B) { 7 | this.board = this.createBoard(N); 8 | this.insertBombs(B); 9 | this.computeCells(); 10 | console.log('now let\' start playing minesweeper!'); 11 | }; 12 | 13 | Minesweeper.prototype.createBoard = function(N) { 14 | var board = []; 15 | for (var i = 0; i < N; i++) { 16 | board.push([]); 17 | for (var j = 0; j < N; j++) { 18 | board[i].push({ isBomb: false, reveal: false, display: null }); 19 | } 20 | } 21 | return board; 22 | }; 23 | 24 | Minesweeper.prototype.insertBombs = function(B) { 25 | var count = 0; 26 | var n = this.board.length; 27 | var row; 28 | var col; 29 | while (count < B) { 30 | row = Math.floor(Math.random() * n); 31 | col = Math.floor(Math.random() * n); 32 | if (!this.board[row][col].isBomb) { 33 | this.board[row][col].isBomb = true; 34 | count++; 35 | } 36 | } 37 | }; 38 | 39 | Minesweeper.prototype.check = function(row, col) { 40 | if (this.board[row][col].isBomb) { 41 | console.log(`boom! ${row}, ${col} is a mine`); 42 | this._printBoard(); 43 | } else { 44 | this.explore(row, col); 45 | console.log('not bad! now check your next spot'); 46 | this.printBoard(); 47 | } 48 | }; 49 | 50 | Minesweeper.prototype.explore = function(row, col) { 51 | if (this.board[row] !== undefined && this.board[row][col] !== undefined && 52 | !this.board[row][col].isBomb && !this.board[row][col].reveal) { 53 | this.board[row][col].reveal = true; 54 | if (this.board[row][col].display === 0) { 55 | this.explore(row - 1, col - 1); 56 | this.explore(row - 1, col); 57 | this.explore(row - 1, col + 1); 58 | this.explore(row, col - 1); 59 | this.explore(row, col + 1); 60 | this.explore(row + 1, col - 1); 61 | this.explore(row + 1, col); 62 | this.explore(row + 1, col + 1); 63 | } 64 | } 65 | }; 66 | 67 | Minesweeper.prototype.computeCells = function() { 68 | var n = this.board.length; 69 | for (var row = 0; row < n; row++) { 70 | for (var col = 0; col < n; col++) { 71 | this.computeCell(row, col, n); 72 | } 73 | } 74 | }; 75 | 76 | Minesweeper.prototype.computeCell = function(row, col, n) { 77 | if (this.board[row][col].isBomb) { 78 | this.board[row][col].display = 'B'; 79 | return; 80 | } 81 | var count = 0; 82 | // up row - 1, col 83 | if (row !== 0) { 84 | count += this.board[row - 1][col].isBomb ? 1 : 0; 85 | } 86 | // down row + 1, col 87 | if (row !== n - 1) { 88 | count += this.board[row + 1][col].isBomb ? 1 : 0; 89 | } 90 | // left row, col - 1 91 | if (col !== 0) { 92 | count += this.board[row][col - 1].isBomb ? 1 : 0; 93 | } 94 | // right row, col + 1 95 | if (col !== n - 1) { 96 | count += this.board[row][col + 1].isBomb ? 1 : 0; 97 | } 98 | // upleft row - 1, col - 1 99 | if (col !== 0 && row !== 0) { 100 | count += this.board[row - 1][col - 1].isBomb ? 1 : 0; 101 | } 102 | // upright row - 1, col + 1 103 | if (row !== 0 && col !== n - 1) { 104 | count += this.board[row - 1][col + 1].isBomb ? 1 : 0; 105 | } 106 | // downleft row + 1, col - 1 107 | if (row !== n - 1 && col !== 0) { 108 | count += this.board[row + 1][col - 1].isBomb ? 1 : 0; 109 | } 110 | // downright row + 1, col + 1 111 | if (row !== n - 1 && col !== n - 1) { 112 | count += this.board[row + 1][col + 1].isBomb ? 1 : 0; 113 | } 114 | this.board[row][col].display = count.toString(); 115 | }; 116 | 117 | Minesweeper.prototype._printBoard = function() { 118 | var n = this.board.length; 119 | for (var i = 0; i < n; i++) { 120 | console.log(this.board[i].map((cell) => { 121 | return cell.display; 122 | }).join('|')); 123 | } 124 | }; 125 | 126 | Minesweeper.prototype.printBoard = function() { 127 | var n = this.board.length; 128 | for (var i = 0; i < n; i++) { 129 | console.log(this.board[i].map((cell) => { 130 | return cell.reveal ? cell.display : 'x'; 131 | }).join('|')); 132 | } 133 | }; 134 | 135 | /* TEST */ 136 | var ms = new Minesweeper(10, 20); 137 | ms._printBoard(); 138 | console.log(); 139 | ms.printBoard(); 140 | -------------------------------------------------------------------------------- /chapter07/7.11 - File System/fileSystem.js: -------------------------------------------------------------------------------- 1 | var Folder = function(name) { 2 | this.type = 'folder'; 3 | this.name = name; 4 | this.filesAndFolders = []; 5 | }; 6 | 7 | Folder.prototype.findFolder = function(name) { 8 | if (this.name === name) { 9 | return this; 10 | } else { 11 | this.filesAndFolders.forEach((item) => { 12 | if (item.type === 'folder') { 13 | item.findFolder(name); 14 | } 15 | }); 16 | } 17 | }; 18 | 19 | Folder.prototype.findFile = function(name) { 20 | this.filesAndFolders.forEach((item) => { 21 | if (item.type === 'file' && item.name === name) { 22 | return item; 23 | } 24 | }); 25 | this.filesAndFolders.forEach((item) => { 26 | if (item.type === 'folder') { 27 | item.findFile(name); 28 | } 29 | }); 30 | }; 31 | 32 | var File = function(name, content) { 33 | this.type = 'file'; 34 | this.name = name; 35 | this.content = content !== undefined ? content : null; 36 | }; 37 | 38 | var FileSystem = function(name) { 39 | this.folder = new Folder(name); 40 | this.currentFolder = null; 41 | this.currentFile = null; 42 | this.index = {}; 43 | }; 44 | 45 | FileSystem.prototype.showCurrent = function() { 46 | if (this.currentFolderOrFile !== null) { 47 | return this.currentFolderOrFile.name; 48 | } else { 49 | console.log('no file'); 50 | } 51 | }; 52 | 53 | FileSystem.prototype.findFolder = function(name) { 54 | let found = this.folder.findFolder(name); 55 | this.currentFolder = found; 56 | return found; 57 | }; 58 | 59 | FileSystem.prototype.findFile = function(name) { 60 | let found = this.folder.findFile(name); 61 | this.currentFile = found; 62 | return found; 63 | }; 64 | 65 | FileSystem.prototype.addFileToCurrentFolder = function(name, content) { 66 | this.currentFolder.push(new File(name, content)); 67 | }; 68 | 69 | FileSystem.prototype.addFolderToCurrent = function(name) { 70 | this.currentFolder.push(new Folder(name)); 71 | }; 72 | 73 | FileSystem.prototype.deleteCurrentFolder = function() { 74 | // delete from parent 75 | }; 76 | 77 | FileSystem.prototype.deleteCurrentFile = function() { 78 | // delete from parent 79 | }; 80 | 81 | // folder as a tree, and file as a leaf 82 | // main algorithm will be tree search algorithm (BFS and DFS) 83 | 84 | // to speed up a folder or file search, could build an index based on the name 85 | // could have methods to create folder and create file 86 | // could find path 87 | 88 | -------------------------------------------------------------------------------- /chapter07/7.12 - Hash Table/hashTable.js: -------------------------------------------------------------------------------- 1 | var LinkedList = function(key, value) { 2 | this.keyValPair = [key, value]; 3 | this.next = null; 4 | }; 5 | 6 | LinkedList.prototype.insert = function(key, value) { 7 | if (this.next === null) { 8 | this.next = new LinkedList(key, value); 9 | } else { 10 | this.next.insert(key, value); 11 | } 12 | }; 13 | 14 | LinkedList.prototype.find = function(key) { 15 | if (this.keyValPair[0] === key) { 16 | return this.keyValPair[1]; 17 | } else if (this.next !== null) { 18 | return this.next.find(key); 19 | } else { 20 | return null; 21 | } 22 | }; 23 | 24 | LinkedList.prototype.replace = function(key, value) { 25 | if (this.keyValPair[0] === key) { 26 | this.keyValPair = [key, value]; 27 | } else if (this.next !== null) { 28 | this.next.replace(key); 29 | } 30 | }; 31 | 32 | LinkedList.prototype.delete = function(key) { 33 | if (this.keyValPair[0] === key) { 34 | // this = this.next; // need to delete node 35 | } else if (this.next !== null) { 36 | this.next.delete(key); 37 | } 38 | }; 39 | 40 | var getHash = function(key, limit) { 41 | if (typeof key !== 'string') { 42 | throw 'error, key is not a string'; 43 | } else { 44 | var answer = 0; 45 | for (var i = 0; i < key.length; i++) { 46 | answer += key[i]; 47 | } 48 | return answer % limit; 49 | } 50 | }; 51 | 52 | var HashTable = function() { 53 | this.array = []; 54 | this.limit = 8; 55 | }; 56 | 57 | HashTable.prototype.insert = function(key, value) { 58 | var hash = getHash(key, this.limit); 59 | if (this.array[hash] === undefined) { 60 | this.array[hash] = new LinkedList(key, value); 61 | } else { 62 | this.array[hash].insert(key, value); 63 | } 64 | }; 65 | 66 | HashTable.prototype.retrieve = function(key) { 67 | var hash = getHash(key, this.limit); 68 | if (this.array[hash] === undefined) { 69 | throw 'key does not exist'; 70 | } else { 71 | return this.array[hash].find(key); 72 | } 73 | }; 74 | 75 | HashTable.prototype.delete = function(key) { 76 | var hash = getHash(key, this.limit); 77 | if (this.array[hash] === undefined) { 78 | throw 'key does not exist'; 79 | } else { 80 | this.array[hash].delete(key); 81 | } 82 | }; 83 | 84 | -------------------------------------------------------------------------------- /chapter07/util/BST.js: -------------------------------------------------------------------------------- 1 | var Queue = require('./Queue'); 2 | 3 | var BST = function(value) { 4 | this.value = value; 5 | this.left = null; 6 | this.right = null; 7 | }; 8 | 9 | BST.prototype.insert = function(value) { 10 | if (value < this.value) { 11 | if (this.left === null) { 12 | this.left = new BST(value); 13 | } else { 14 | this.left.insert(value); 15 | } 16 | } else { 17 | if (this.right === null) { 18 | this.right = new BST(value); 19 | } else { 20 | this.right.insert(value); 21 | } 22 | } 23 | }; 24 | 25 | BST.prototype.printLevelOrder = function() { 26 | var level = []; 27 | var q = new Queue(); 28 | var nextq = new Queue(); 29 | var currNode; 30 | 31 | q.add(this); 32 | while (!q.isEmpty()) { 33 | currNode = q.remove(); 34 | level.push(currNode.value); 35 | if (currNode.left !== null) { 36 | nextq.add(currNode.left); 37 | } 38 | if (currNode.right !== null) { 39 | nextq.add(currNode.right); 40 | } 41 | if (q.isEmpty()) { 42 | console.log(level.join(',')); 43 | level = []; 44 | q = nextq; 45 | nextq = new Queue(); 46 | } 47 | } 48 | }; 49 | 50 | module.exports = BST; 51 | 52 | /* TEST */ 53 | // 1, 2, 3, 4, 5, 6, 7 54 | // var bst = new BST(); 55 | // bst.insert(4); 56 | // bst.insert(2); 57 | // bst.insert(6); 58 | // bst.insert(1); 59 | // bst.insert(3); 60 | // bst.insert(5); 61 | // bst.insert(7); 62 | 63 | // bst.printLevelOrder(); 64 | -------------------------------------------------------------------------------- /chapter07/util/Graph.js: -------------------------------------------------------------------------------- 1 | var Graph = function() { 2 | this.nodes = {}; 3 | }; 4 | 5 | Graph.prototype.addEdge = function(node, edge) { 6 | if (this.nodes[node] === undefined) { 7 | return 'node does not exist'; 8 | } else if (this.nodes[node][edge]) { 9 | return `edge ${node}-${edge} already exists`; 10 | } else { 11 | this.nodes[node][edge] = true; 12 | } 13 | }; 14 | 15 | Graph.prototype.addNode = function(value) { 16 | if (this.nodes[value] !== undefined) { 17 | return `node of value ${value} already exists`; 18 | } else { 19 | this.nodes[value] = {}; 20 | } 21 | }; 22 | 23 | Graph.prototype.findEdges = function(node) { 24 | if (this.nodes[node] === undefined) { 25 | return 'node does not exist'; 26 | } else { 27 | return this.nodes[node]; 28 | } 29 | }; 30 | 31 | Graph.prototype.hasEdge = function(node, edge) { 32 | if (this.nodes[node] === undefined) { 33 | return false; 34 | } else { 35 | return this.nodes[node][edge] !== undefined; 36 | } 37 | }; 38 | 39 | Graph.prototype.hasNode = function(node) { 40 | return this.nodes[node] !== undefined; 41 | }; 42 | 43 | Graph.prototype.removeEdge = function(node, edge) { 44 | if (this.nodes[node] === undefined) { 45 | return 'node does not exist'; 46 | } else { 47 | delete this.nodes[node][edge]; 48 | } 49 | }; 50 | 51 | Graph.prototype.removeNode = function(node) { 52 | if (this.nodes[node] === undefined) { 53 | return 'node does not exist'; 54 | } else { 55 | delete this.nodes[node]; 56 | for (var currNode in this.nodes) { 57 | if (this.nodes[currNode][node] !== undefined) { 58 | delete this.nodes[currNode][node]; 59 | } 60 | } 61 | } 62 | }; 63 | 64 | module.exports = Graph; -------------------------------------------------------------------------------- /chapter07/util/LinkedList.js: -------------------------------------------------------------------------------- 1 | // moved from chapter 2 (/util/LinkedListX.js) 2 | 3 | class Node { 4 | constructor(value) { 5 | this.value = value 6 | this.next = null; 7 | } 8 | } 9 | 10 | class LinkedList { 11 | constructor() { 12 | this.head = null; 13 | this.tail = null; 14 | } 15 | 16 | append(value) { 17 | let node = new Node(value); 18 | // if list is empty 19 | if (!this.head) { 20 | this.head = node; 21 | this.tail = node; 22 | } 23 | else { 24 | this.tail.next = node; 25 | this.tail = node; 26 | } 27 | } 28 | 29 | prepend(value) { 30 | let node = new Node(value); 31 | node.next = this.head; 32 | this.head = node; 33 | } 34 | 35 | pop() { 36 | let cur = this.head; 37 | 38 | // only one or no item exists 39 | if (!cur) return null; 40 | if (!cur.next) { 41 | this.head = null; 42 | return cur; 43 | } 44 | // move till the 2nd last 45 | while (cur.next.next) 46 | cur = cur.next; 47 | 48 | let last = this.tail; 49 | this.tail = cur; 50 | this.tail.next = null; 51 | return last; 52 | } 53 | 54 | popFirst() { 55 | let first = this.head; 56 | if (this.head && this.head.next) { 57 | this.head = this.head.next; 58 | first.next = null; 59 | } 60 | else this.head = null; 61 | return first; 62 | } 63 | 64 | head() { 65 | return this.head; 66 | } 67 | 68 | removeAt(index) { 69 | let i = 0; 70 | let cur = this.head; 71 | let prev = null; 72 | 73 | while (cur != null) { 74 | if (i == index) { 75 | // remove 76 | if (prev == null) 77 | this.head = cur.next; 78 | else prev.next = cur.next; 79 | cur.next = null; 80 | return cur.value; 81 | } 82 | else { 83 | prev = cur; 84 | cur = cur.next; 85 | i++; 86 | } 87 | } 88 | return null; 89 | } 90 | 91 | insertAt(index, value) { 92 | if (index == 0) return this.prepend(value); 93 | let cur = this.head; 94 | let i = 0; 95 | 96 | while (cur != null) { 97 | if (i == index - 1) { 98 | let node = new Node(value); 99 | node.next = cur.next; 100 | cur.next = node; 101 | return true; 102 | } 103 | else { 104 | i++; 105 | cur = cur.next; 106 | } 107 | } 108 | return false; 109 | } 110 | 111 | tail() { 112 | return this.tail; 113 | } 114 | 115 | _toArray() { 116 | let arr = []; 117 | let cur = this.head; 118 | while (cur) { 119 | arr.push(cur.value); 120 | cur = cur.next; 121 | } 122 | 123 | return arr; 124 | } 125 | } 126 | 127 | module.exports = LinkedList; 128 | 129 | /* TEST */ 130 | 131 | // let l = new LinkedList(); 132 | // l.append(3); 133 | // l.append(4); 134 | // l.append(10); 135 | // l.append(20); 136 | // l.append(5); 137 | 138 | // console.log(l.removeAt(1), 4); 139 | // console.log(l.pop().value, 5); 140 | 141 | // console.log(l._toArray()); 142 | // l.insertAt(2, 40); 143 | // console.log(l._toArray()); 144 | -------------------------------------------------------------------------------- /chapter07/util/Queue.js: -------------------------------------------------------------------------------- 1 | // implement a queue using linkedLists 2 | var LinkedList = require('./LinkedList'); 3 | 4 | class Queue { 5 | constructor() { 6 | this._list = new LinkedList(); 7 | } 8 | 9 | enqueue(value) { 10 | this._list.append(value); 11 | } 12 | 13 | dequeue() { 14 | let node = this._list.popFirst(); 15 | return node.value; 16 | } 17 | 18 | peek() { 19 | return this._list.head ? this._list.head.value : null; 20 | } 21 | 22 | isEmpty() { 23 | return this._list.head == null; 24 | } 25 | 26 | _toArray() { 27 | return this._list._toArray(); 28 | } 29 | } 30 | 31 | // alias 32 | Queue.prototype.add = Queue.prototype.enqueue; 33 | Queue.prototype.remove = Queue.prototype.dequeue; 34 | 35 | module.exports = Queue; 36 | 37 | /* TEST */ 38 | // var q = new Queue(); 39 | // q.add('a'); 40 | // q.add('b'); 41 | // q.add('c'); 42 | // console.log(q._toArray()); 43 | // console.log(q.remove(), 'a'); 44 | // console.log(q.peek(), 'b'); 45 | // console.log(q.remove(), 'b'); 46 | // console.log(q.remove(), 'c'); 47 | // console.log(q.isEmpty(), true); 48 | // console.log(q._toArray()); 49 | -------------------------------------------------------------------------------- /chapter07/util/Stack.js: -------------------------------------------------------------------------------- 1 | // simple implementation of a stack 2 | // using an Array 3 | 4 | /* 5 | Interface: 6 | - push(value) 7 | - pop() 8 | - peek() 9 | - isEmpty() 10 | - size() 11 | */ 12 | class Stack { 13 | constructor() { 14 | this._data = []; 15 | } 16 | 17 | size() { 18 | return this._data.length; 19 | } 20 | 21 | isEmpty() { 22 | return this.size() == 0; 23 | } 24 | 25 | push(value) { 26 | this._data.push(value); 27 | } 28 | 29 | pop() { 30 | return this._data.pop(); 31 | } 32 | 33 | peek() { 34 | if (this.isEmpty()) return null; 35 | return this._data[this.size() - 1]; 36 | } 37 | } 38 | 39 | module.exports = Stack; 40 | 41 | /* TEST */ 42 | 43 | // var s = new Stack(); 44 | // s.push('a'); 45 | // s.push('b'); 46 | // s.push('c'); 47 | // console.log(s.pop(), 'c'); 48 | // console.log(s.peek(), 'b'); 49 | // console.log(s.pop(), 'b'); 50 | // console.log(s.pop(), 'a'); 51 | // console.log(s.isEmpty(), true); 52 | -------------------------------------------------------------------------------- /chapter07/util/Tree.js: -------------------------------------------------------------------------------- 1 | var Tree = function(value) { 2 | this.value = value; 3 | this.children = []; 4 | }; 5 | 6 | module.exports = Tree; -------------------------------------------------------------------------------- /chapter07/util/notes.js: -------------------------------------------------------------------------------- 1 | // Create elegant maintainable object-oriented code 2 | // 3 | // ** Steps to handle object oriented design ** 4 | // 5 | // 1) handle ambiguity by asking clarifying questions 6 | // e.g. who is going to use it and how are they going to use it? 7 | // Know that requirements are not straightforward without extensive context 8 | // 9 | // 2) define core objects 10 | // e.g. Table, chair, etc. 11 | // 12 | // 3) analyze relationships 13 | // group to members, inheritance, one-to-many/one-to-one/many-to-many 14 | // test and check the relationships for assumptions 15 | // 16 | // 4) investigate actions 17 | // consider key actions of objects and how they relate to each other. 18 | // will likely forget some object and need to update design 19 | // 20 | // ** Design Patterns ** 21 | // 22 | // a) Singleton design pattern 23 | // 24 | // ensures that a class only has one instance and ensures access to instance through 25 | // the application 26 | // 27 | // many people dislike the Singleton design pattern 'anti-pattern' as in can interfere 28 | // with unit testing 29 | // 30 | // b) Factory Method design pattern 31 | // 32 | // offer interface creating instance of class, with subclass deciding which class to 33 | // instantiate. 34 | // 35 | // factory method takes a parameter representing which class to instantiate 36 | // -------------------------------------------------------------------------------- /chapter08/8.01 - Triple Step/tripleStep.js: -------------------------------------------------------------------------------- 1 | var numWays = function(N) { 2 | var answer = 0; 3 | var recurse = function(number) { 4 | if (number === 0) { 5 | answer++; 6 | } else if (number > 0) { 7 | recurse(number - 1); 8 | recurse(number - 2); 9 | recurse(number - 3); 10 | } 11 | }; 12 | recurse(N); 13 | return answer; 14 | }; 15 | 16 | /* TEST */ 17 | 18 | console.log(numWays(1), 1); 19 | console.log(numWays(2), 2); 20 | console.log(numWays(3), 4); 21 | -------------------------------------------------------------------------------- /chapter08/8.01 - Triple Step/tripleStepv2.js: -------------------------------------------------------------------------------- 1 | /* 2 | Recursion and Dynamic Programming (4 steps) 3 | 1. start with the recursive backtracking solution (brute force) 4 | 2. Optimize by using a memoization table (top-down dynamic programming) 5 | 3. Remove the need for recursion (bottom-up dynamic programming) 6 | 4. Apply final tricks to reduce the time / memory complexity 7 | */ 8 | 9 | // Recursive Backtracking 10 | // Time & Space O(n!) 11 | function tripleStep(n) { 12 | if (n < 0) return 0; 13 | if (n === 0) return 1; 14 | return tripleStep(n - 1) + tripleStep(n - 2) + tripleStep(n - 3); 15 | } 16 | 17 | // Top Down Memoization 18 | // Time & Space O(n) 19 | function tripleStep(n, i = 3, memo = [1, 1, 2, 4]) { 20 | if (n < 0) return 0 21 | if (n < i) return memo[n]; 22 | memo[i] = memo[i - 1] + memo[i - 2] + memo[i - 3]; 23 | return tripleStep(n, i + 1, memo); 24 | } 25 | 26 | // Bottom Up memoization 27 | // Time & Space O(n) 28 | function tripleStep(n, memo = [1, 1, 2, 4]) { 29 | for (let i = 3; i <= n; i++) { 30 | memo[i] = memo[i - 1] + memo[i - 2] + memo[i - 3]; 31 | } 32 | 33 | return memo[memo.length - 1]; 34 | } 35 | 36 | // Constant Space 37 | // Time O(n) & Space O(1) 38 | function tripleStep(n) { 39 | if (n <= 0) return 0; 40 | if (n === 1) return 1; 41 | if (n === 2) return 2; 42 | 43 | let a = 1; 44 | let b = 1; 45 | let c = 2; 46 | 47 | for (let i = 3; i < n; i++) { 48 | let d = a + b + c; 49 | a = b; 50 | b = c; 51 | c = d; 52 | } 53 | return a + b + c; 54 | } 55 | 56 | /* TEST */ 57 | console.log(tripleStep(1), 1); 58 | console.log(tripleStep(2), 2); 59 | console.log(tripleStep(3), 4); 60 | console.log(tripleStep(5), 13); 61 | console.log(tripleStep(7), 44); 62 | console.log(tripleStep(10), 274); 63 | -------------------------------------------------------------------------------- /chapter08/8.02 - Robot in a Grid/robotInGrid.js: -------------------------------------------------------------------------------- 1 | var findPaths = function(grid) { 2 | var paths = []; 3 | var endRow = grid.length - 1; 4 | var endCol = grid[0].length - 1; 5 | var recurse = function(row, col, currPath) { 6 | if (row === endRow && col === endCol) { 7 | paths.push(currPath.concat([[row, col]])); 8 | } else if (row <= endRow && col <= endCol) { 9 | if (row < endRow && grid[row+1][col] !== 'x') { 10 | recurse(row + 1, col, currPath.concat([[row, col]])); 11 | } 12 | if (col < endCol && grid[row][col+1] !== 'x') { 13 | recurse(row, col + 1, currPath.concat([[row, col]])); 14 | } 15 | } 16 | }; 17 | recurse(0, 0, []); 18 | return paths; 19 | }; 20 | 21 | /* TEST */ 22 | 23 | var grid = [ 24 | ['0', '0', '0', '0'], 25 | ['0', 'x', '0', 'x'], 26 | ['x', '0', '0', '0'], 27 | ]; 28 | 29 | console.log(findPaths(grid)); 30 | 31 | -------------------------------------------------------------------------------- /chapter08/8.03 - Magic Index/magicIndex.js: -------------------------------------------------------------------------------- 1 | var findMagic = function(array, start, end) { 2 | if (start === undefined) { 3 | start = 0; 4 | } 5 | if (end === undefined) { 6 | end = array.length - 1; 7 | } 8 | 9 | var mid = Math.floor(start + (end - start) / 2); 10 | 11 | if (mid === start && array[mid] !== mid) { 12 | return -1; 13 | } else if (array[mid] === mid) { 14 | return mid; 15 | } else if (mid < array[mid]) { 16 | return findMagic(array, start, mid); 17 | } else { 18 | return findMagic(array, mid, end); 19 | } 20 | }; 21 | 22 | /* TEST */ 23 | console.log(findMagic([-1, 0, 1, 3, 9, 100]), 3); 24 | console.log(findMagic([-1, 0, 1, 2, 3, 5, 100, 200, 300, 400, 500, 600, 700]), 5); 25 | console.log(findMagic([5, 5, 5, 5, 5, 5]), 5); // would need to be in linear runtime if values are not distinct 26 | -------------------------------------------------------------------------------- /chapter08/8.04 - Power Set/powerSet.js: -------------------------------------------------------------------------------- 1 | var returnSubsets = function(set) { 2 | var subsets = []; 3 | var recurse = function(currSet, remainingSet) { 4 | subsets.push(currSet); 5 | for (var i = 0; i < remainingSet.length; i++) { 6 | recurse(currSet.concat([remainingSet[i]]), remainingSet.slice(i + 1)); 7 | } 8 | }; 9 | recurse([], set); 10 | return subsets; 11 | }; 12 | 13 | /* TEST */ 14 | console.log(returnSubsets([1, 2, 3, 4])); -------------------------------------------------------------------------------- /chapter08/8.05 - Recursive Multiply/recursiveMultiply.js: -------------------------------------------------------------------------------- 1 | var recurseMult = function(a, b) { 2 | if (a < 0 || b < 0) { 3 | throw 'error: a and b should only be positive integers'; 4 | } 5 | if (b === 0) { 6 | return 0; 7 | } else if (b === 1) { 8 | return a; 9 | } else { 10 | return a + recurseMult(a, b - 1); 11 | } 12 | }; 13 | 14 | /* TEST */ 15 | 16 | console.log(recurseMult(2, 3)); 17 | -------------------------------------------------------------------------------- /chapter08/8.06 - Towers of Hanoi/towersHanoi.js: -------------------------------------------------------------------------------- 1 | var TowersOfHanoi = function(n) { 2 | this.first = []; 3 | this.second = []; 4 | this.third = []; 5 | for (var i = n; i >= 1; i--) { 6 | this.first.push(i); 7 | } 8 | }; 9 | 10 | TowersOfHanoi.prototype.move = function(start, mid, dest, depth) { 11 | if (depth === 0) { 12 | return; 13 | } else if (depth === 1) { 14 | dest.push(start.pop()); 15 | } else { 16 | var currDepth = depth; 17 | var shortTower; 18 | var placePiece; 19 | if (depth % 2 === 0) { 20 | shortTower = mid; 21 | placePiece = dest; 22 | } else { 23 | shortTower = dest; 24 | placePiece = mid; 25 | } 26 | var shortTowerDepth = 0; 27 | shortTower.push(start.pop()); 28 | shortTowerDepth++; 29 | currDepth--; 30 | var towerSwap1; 31 | var towerSwap2; 32 | while (currDepth > 0) { 33 | placePiece.push(start.pop()); 34 | currDepth--; 35 | this.move(shortTower, start, placePiece, shortTowerDepth); 36 | shortTowerDepth++; 37 | towerSwap1 = shortTower; 38 | towerSwap2 = placePiece; 39 | shortTower = towerSwap2; 40 | placePiece = towerSwap1; 41 | } 42 | } 43 | }; 44 | 45 | /* TEST */ 46 | var th = new TowersOfHanoi(5); 47 | console.log(th); 48 | th.move(th.first, th.second, th.third, th.first.length); 49 | console.log(th); 50 | 51 | var th2 = new TowersOfHanoi(6); 52 | console.log(th2); 53 | th.move(th2.first, th2.second, th2.third, th2.first.length); 54 | console.log(th2); 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /chapter08/8.07 - Permutations without Dups/permWithoutDups.js: -------------------------------------------------------------------------------- 1 | var permuteString = function(string) { 2 | var answers = []; 3 | var recurse = function(currPerm, remainingChars) { 4 | if (remainingChars.length === 0) { 5 | answers.push(currPerm); 6 | } else { 7 | for (var i = 0; i < remainingChars.length; i++) { 8 | recurse(currPerm + remainingChars.charAt(i), remainingChars.slice(0, i) + remainingChars.slice(i+ 1)); 9 | } 10 | } 11 | }; 12 | recurse('', string); 13 | return answers; 14 | }; 15 | 16 | /* TEST */ 17 | 18 | var testString = 'abcd'; 19 | console.log(permuteString(testString)); 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /chapter08/8.08 - Permutations with Dups/permWithDups.js: -------------------------------------------------------------------------------- 1 | var permNoDups = function(string) { 2 | var answers = []; 3 | var recurse = function(currPerm, remainingChars) { 4 | if (remainingChars.length === 0) { 5 | answers.push(currPerm); 6 | } else { 7 | var usedChars = {}; 8 | for (var i = 0; i < remainingChars.length; i++) { 9 | if (!usedChars[remainingChars.charAt(i)]) { 10 | usedChars[remainingChars.charAt(i)] = true; 11 | recurse(currPerm + remainingChars.charAt(i), remainingChars.slice(0, i) + remainingChars.slice(i+1)); 12 | } 13 | } 14 | } 15 | }; 16 | recurse('', string); 17 | return answers; 18 | }; 19 | 20 | /* TEST */ 21 | 22 | var test1 = 'aaa'; 23 | var test2 = 'abc'; 24 | var test3 = 'aba'; 25 | 26 | console.log(permNoDups(test1)); 27 | console.log(permNoDups(test2)); 28 | console.log(permNoDups(test3)); 29 | -------------------------------------------------------------------------------- /chapter08/8.09 - Parens/parens.js: -------------------------------------------------------------------------------- 1 | var parens = function(n) { 2 | var answers = []; 3 | var recurse = function(currParens, remainingPairs) { 4 | if (remainingPairs === 0) { 5 | answers.push(currParens); 6 | } else { 7 | var used = {}; 8 | if (!used[`(${currParens})`]) { 9 | used[`(${currParens})`] = true; 10 | recurse(`(${currParens})`, remainingPairs - 1); 11 | } 12 | if (!used[`()${currParens}`]) { 13 | used[`()${currParens}`] = true; 14 | recurse(`()${currParens}`, remainingPairs - 1); 15 | } 16 | if (!used[`${currParens}()`]) { 17 | used[`${currParens}()`] = true; 18 | recurse(`${currParens}()`, remainingPairs - 1); 19 | } 20 | } 21 | }; 22 | recurse('', n); 23 | return answers; 24 | }; 25 | 26 | /* TEST */ 27 | var testn = 3; 28 | console.log(parens(testn)); 29 | 30 | -------------------------------------------------------------------------------- /chapter08/8.10 - Paint Fill/paintFill.js: -------------------------------------------------------------------------------- 1 | var withinBounds = function(point, screen) { 2 | var [row, col] = point; 3 | var rowHeight = screen.length; 4 | var colWidth = screen[0].length; 5 | return row >= 0 && row < rowHeight && col >=0 && col < colWidth; 6 | }; 7 | 8 | var paintFill = function(screen, point, color) { 9 | var [row, col] = point; 10 | var currColor = screen[row][col]; 11 | if (withinBounds(point, screen) && screen[row][col] !== color) { 12 | screen[row][col] = color; 13 | 14 | if (withinBounds([row + 1, col], screen) && screen[row + 1][col] === currColor) { 15 | paintFill(screen, [row + 1, col], color); 16 | } 17 | 18 | if (withinBounds([row + 1, col + 1], screen) && screen[row + 1][col + 1] === currColor) { 19 | paintFill(screen, [row + 1, col + 1], color); 20 | } 21 | 22 | if (withinBounds([row + 1, col - 1], screen) && screen[row + 1][col - 1] === currColor) { 23 | paintFill(screen, [row + 1, col - 1], color); 24 | } 25 | 26 | if (withinBounds([row, col + 1], screen) && screen[row][col + 1] === currColor) { 27 | paintFill(screen, [row, col + 1], color); 28 | } 29 | 30 | if (withinBounds([row, col - 1], screen) && screen[row][col - 1] === currColor) { 31 | paintFill(screen, [row, col - 1], color); 32 | } 33 | 34 | if (withinBounds([row - 1, col], screen) && screen[row - 1][col] === currColor) { 35 | paintFill(screen, [row - 1, col], color); 36 | } 37 | 38 | if (withinBounds([row - 1, col + 1], screen) && screen[row - 1][col + 1] === currColor) { 39 | paintFill(screen, [row - 1, col + 1], color); 40 | } 41 | 42 | if (withinBounds([row - 1, col - 1], screen) && screen[row - 1][col - 1] === currColor) { 43 | paintFill(screen, [row - 1, col - 1], color); 44 | } 45 | } 46 | }; 47 | 48 | /* TEST */ 49 | 50 | var b = '#000000'; 51 | var w = '#ffffff'; 52 | var g = '#00ff00'; 53 | 54 | var testScreen = [ 55 | [b, b, b, b], 56 | [b, w, w, b], 57 | [b, w, w, b], 58 | [b, b, b, b] 59 | ]; 60 | var testScreen2 = [ 61 | [b, w, w, b], 62 | [b, w, w, b], 63 | [b, b, w, b], 64 | [b, w, w, b] 65 | ]; 66 | 67 | 68 | var testPoint = [1, 1]; 69 | 70 | paintFill(testScreen, testPoint, g); 71 | paintFill(testScreen2, testPoint, g); 72 | 73 | console.log(testScreen); 74 | console.log(testScreen2); 75 | -------------------------------------------------------------------------------- /chapter08/8.11 - Coins/coins.js: -------------------------------------------------------------------------------- 1 | var dp = {}; 2 | 3 | var coins = function(value, currCoin) { 4 | if (currCoin === undefined) { 5 | currCoin = 1; 6 | } 7 | if (value < 0) { 8 | return 0; 9 | } else { 10 | var key = `${value}:${currCoin}`; 11 | if (dp[key] === undefined) { 12 | if (value === 0) { 13 | dp[key] = 1; 14 | } else { 15 | var ways = 0; 16 | if (currCoin <= 1) { 17 | ways += coins(value - 1, 1); 18 | } 19 | if (currCoin <= 5) { 20 | ways += coins(value - 5, 5); 21 | } 22 | if (currCoin <= 10) { 23 | ways += coins(value - 10, 10); 24 | } 25 | if (currCoin <= 25) { 26 | ways += coins(value - 25, 25); 27 | } 28 | /* 29 | 30 | added provision if half dollars and dollar coins are added, 31 | but that would be unamerican. 32 | 33 | if (currCoin <= 50) { 34 | ways += coins(value - 50, 50); 35 | } 36 | if (currCoin <= 100) { 37 | ways += coins(value - 100, 100); 38 | } 39 | 40 | */ 41 | dp[key] = ways; 42 | } 43 | } 44 | return dp[key]; 45 | } 46 | }; 47 | 48 | /* TEST */ 49 | // there is 1 way to represent 0 cents 50 | console.log(coins(0) === 1); 51 | 52 | // there is 1 way to represent 1 cent 53 | console.log(coins(1) === 1); 54 | 55 | // there is 1 way to represent 2 cents 56 | console.log(coins(2) === 1); 57 | 58 | // there is 1 way to represent 3 cents 59 | console.log(coins(3) === 1); 60 | 61 | // there is 1 way to represent 4 cents 62 | console.log(coins(4) === 1); 63 | 64 | // there are 2 ways to represent 5 cents 65 | console.log(coins(5) === 2); 66 | 67 | // there are 6 ways to represent 17 cents 68 | console.log(coins(17) === 6); 69 | 70 | // there are 242 ways to represent 100 cents 71 | console.log(coins(100) === 242); 72 | -------------------------------------------------------------------------------- /chapter08/8.12 - Eight Queens/nQueens.js: -------------------------------------------------------------------------------- 1 | // BITWISE SOLUTION 2 | // this board will make use of a hybrid of bitwise operations and array storage to store the board 3 | // and find solutions for nqueens 4 | 5 | // for n queens, it is given that there must be one and only one queen per row 6 | 7 | // there will be three bit strings of n-length--center, leftDiag, and rightDiag 8 | 9 | // the center checks for whether a column is taken 10 | // the leftDiag checks for whether a left diagonal is taken, and shifts left bitwise per recursive step 11 | // the rightDiag checks for whether a right diagonal is taken, and shifts right bitwise per recursive step 12 | 13 | var changeChar = function(string, position, char) { // note: will not work for emoji 14 | var answer = string.split(''); 15 | answer[position] = char; 16 | return answer.join(''); 17 | }; 18 | 19 | var bitwiseOp = function(binaryString, n, callback) { 20 | var base10 = parseInt(binaryString, 2); 21 | base10 = callback(base10); 22 | var base2 = base10.toString(2); 23 | if (base2.length <= n) { 24 | var front = ''; 25 | for (var i = base2.length; i < n; i++) { 26 | front += '0'; 27 | } 28 | return front + base2; 29 | } else { 30 | return base2.slice(base2.length - n); 31 | } 32 | }; 33 | 34 | var leftShift = function(binaryString, n) { 35 | return bitwiseOp(binaryString, n, (number) => number << 1); 36 | }; 37 | 38 | var rightShift = function(binaryString, n) { 39 | return bitwiseOp(binaryString, n, (number) => number >>> 1); 40 | }; 41 | 42 | var nqueens = function(n) { 43 | var board = []; 44 | var checker = ''; 45 | var answers = []; 46 | 47 | for (var i = 0; i < n; i++) { 48 | board.push(-1); 49 | checker += '0'; 50 | } 51 | 52 | var recurse = function(currentBoard, center, leftDiag, rightDiag, currRow) { 53 | if (currRow === n) { 54 | answers.push(currentBoard); 55 | } 56 | for (var i = 0; i < n; i++) { 57 | if (center.charAt(i) === '0' && leftDiag.charAt(i) === '0' && rightDiag.charAt(i) === '0') { 58 | currentBoard[i] = currRow; 59 | recurse(currentBoard, changeChar(center, i, '1'), leftShift(changeChar(leftDiag, i, '1'), n), rightShift(changeChar(rightDiag, i, '1'), n), currRow + 1); 60 | currentBoard[i] = -1; // untoggle board to save on space complexity for arrays per recursive step 61 | } 62 | } 63 | }; 64 | recurse(board, checker, checker, checker, 0); 65 | return answers; 66 | }; 67 | 68 | /* TEST */ 69 | // leftShift works as expected 70 | console.log(leftShift('0001', 4) === '0010'); 71 | 72 | // rightShift works as expected 73 | console.log(rightShift('0100', 4) === '0010'); 74 | 75 | // there are 0 solutions to represent 2 queens 76 | console.log(nqueens(2).length === 0); 77 | 78 | // there are 0 solutions to represent 2 queens 79 | console.log(nqueens(2).length === 0); 80 | 81 | // there are 0 solutions to represent 3 queens 82 | console.log(nqueens(3).length === 0); 83 | 84 | // there are 2 solutions to represent 4 queens 85 | console.log(nqueens(4).length === 2); 86 | 87 | // there are 92 solutions to represent 8 queens 88 | console.log(nqueens(8).length === 92); 89 | -------------------------------------------------------------------------------- /chapter08/8.13 - Stack Boxes/stackBoxes.js: -------------------------------------------------------------------------------- 1 | var dp = {}; 2 | 3 | var findStackables = (boxes, basebox) => { 4 | var stackables = []; 5 | // for each box, check all other boxes for which it is dominant 6 | boxes.forEach((box) => { 7 | if (box.height < basebox.height && box.width < basebox.width && box.depth < basebox.depth) { 8 | stackables.push(box); 9 | } 10 | }); 11 | return stackables; 12 | }; 13 | 14 | var stackBoxes = function(boxes) { 15 | if (boxes === undefined) { 16 | return 'where are your boxes?'; 17 | } 18 | 19 | if (boxes.length === 0) { 20 | return 0; 21 | } 22 | boxes.sort(); 23 | var key = JSON.stringify(boxes); 24 | if (dp[key] === undefined) { 25 | var height = 0; 26 | /* find max height of stack by doing the following: 27 | -for each box 28 | -find stackables from the remaining box stack 29 | -add height of box plus a recursive call to stackables 30 | -if height is larger than the max height so far, set it as max height 31 | */ 32 | boxes.forEach((box) => { 33 | var stackables = findStackables(boxes, box); 34 | var currHeight = box.height + stackBoxes(stackables); 35 | height = Math.max(currHeight, height); 36 | }); 37 | dp[key] = height; 38 | } 39 | return dp[key]; 40 | }; 41 | 42 | /* TEST */ 43 | 44 | // 'able to call stackBoxes 45 | console.log(stackBoxes() === 'where are your boxes?'); 46 | 47 | // 'able to stack one box 48 | const box1a = { 49 | width: 1, 50 | height: 1, 51 | depth: 1 52 | }; 53 | console.log(stackBoxes([box1a]) === 1); 54 | 55 | // 'able to stack one box and return height 56 | const box1b = { 57 | width: 1, 58 | height: 100, 59 | depth: 1 60 | }; 61 | console.log(stackBoxes([box1b]) === 100); 62 | 63 | 64 | // 'able to stack two increasingly large boxes 65 | const box1c = { 66 | width: 1, 67 | height: 1, 68 | depth: 1 69 | }; 70 | const box2c = { 71 | width: 2, 72 | height: 2, 73 | depth: 2 74 | }; 75 | console.log(stackBoxes([box1c, box2c]) === 3); 76 | 77 | // 'able to stack three increasingly large boxes 78 | const box1d = { 79 | width: 1, 80 | height: 1, 81 | depth: 1 82 | }; 83 | const box2d = { 84 | width: 2, 85 | height: 2, 86 | depth: 2 87 | }; 88 | const box3d = { 89 | width: 3, 90 | height: 3, 91 | depth: 3 92 | }; 93 | console.log(stackBoxes([box1d, box2d, box3d]) === 6); 94 | 95 | // 'able to stack three increasingly large boxes out of order 96 | const box1e = { 97 | width: 1, 98 | height: 1, 99 | depth: 1 100 | }; 101 | const box2e = { 102 | width: 2, 103 | height: 2, 104 | depth: 2 105 | }; 106 | const box3e = { 107 | width: 3, 108 | height: 3, 109 | depth: 3 110 | }; 111 | console.log(stackBoxes([box2e, box3e, box1e]) === 6); 112 | 113 | // 'unable to stack three boxes, one tall, one wide, one deep 114 | const box1f = { 115 | width: 3, 116 | height: 1, 117 | depth: 1 118 | }; 119 | const box2f = { 120 | width: 1, 121 | height: 3, 122 | depth: 1 123 | }; 124 | const box3f = { 125 | width: 1, 126 | height: 1, 127 | depth: 3 128 | }; 129 | console.log(stackBoxes([box1f, box2f, box3f]) === 3); 130 | -------------------------------------------------------------------------------- /chapter08/8.14 - Boolean Eval/booleanEval.js: -------------------------------------------------------------------------------- 1 | let dp = {}; 2 | const countEval = (booleans, result) => { 3 | if (booleans === undefined) { 4 | return 'where\'s your boolean?'; 5 | } else if (booleans.length % 2 === 0) { 6 | return 'your expression is a little strange. you sure it\'s right?'; 7 | } else if (booleans.length === 1) { 8 | if (eval(booleans) === result) { 9 | return 1; 10 | } else { 11 | return 0; 12 | } 13 | } else { 14 | const key = `${booleans}:${result}`; 15 | if (dp[key] === undefined) { 16 | let count = 0; 17 | let left; 18 | let right; 19 | for (let i = 1; i < booleans.length; i = i + 2) { 20 | left = booleans.slice(0, i); 21 | right = booleans.slice(i + 1); 22 | if (eval('0' + booleans[i] + '0') === result) { 23 | count += countEval(left, 0) * countEval(right, 0); 24 | } 25 | 26 | if (eval('0' + booleans[i] + '1') === result) { 27 | count += countEval(left, 0) * countEval(right, 1); 28 | } 29 | 30 | if (eval('1' + booleans[i] + '0') === result) { 31 | count += countEval(left, 1) * countEval(right, 0); 32 | } 33 | 34 | if (eval('1' + booleans[i] + '1') === result) { 35 | count += countEval(left, 1) * countEval(right, 1); 36 | } 37 | } 38 | dp[key] = count; 39 | } 40 | return dp[key]; 41 | } 42 | }; 43 | 44 | /* TEST */ 45 | // able to call booleanEval 46 | console.log(countEval() === 'where\'s your boolean?'); 47 | 48 | // 0| throws an error 49 | console.log(countEval('0|') === 'your expression is a little strange. you sure it\'s right?'); 50 | 51 | // 0 evaluates to 1 false 52 | console.log(countEval('0', 0) === 1); 53 | 54 | // 0 evaluates to 0 true 55 | console.log(countEval('0', 1) === 0); 56 | 57 | // 1 evaluates to 1 true 58 | console.log(countEval('1', 1) === 1); 59 | 60 | // 1 evaluates to 0 false 61 | console.log(countEval('1', 0) === 0); 62 | 63 | // 1|1 evaluates to 1 true 64 | console.log(countEval('1|1', 0) === 0); 65 | 66 | // 1^0|0|1 evaluates to 2 false 67 | console.log(countEval('1^0|0|1', 0) === 2); 68 | 69 | // 0&0&0&1^1|0 evaluates to 10 true 70 | console.log(countEval('0&0&0&1^1|0', 1) === 10); 71 | -------------------------------------------------------------------------------- /chapter09/9.1 - Stock Data/stockData.md: -------------------------------------------------------------------------------- 1 | 1) Clarify and Assume 2 | 3 | -Clarifications: 4 | a) API to frontend clients via HTTP requests 5 | b) Public information, no need for user id 6 | c) API resides with the stock price databases, i.e. no need to pull data from another source 7 | d) Data will be 8 | -stock ticker 9 | -date 10 | -open 11 | -close 12 | -high 13 | -low 14 | e) Stores historical data 15 | f) Returns JSON 16 | g) Clients in different geographical locations 17 | 18 | -Assumptions 19 | a) No real-time need, used for research and records 20 | 21 | 2) Basic Setup 22 | -diagram 23 | 24 | 3) Identify and Address Problems 25 | -uneven request traffic 26 | -certain times of the day 27 | -for certain stock tickers 28 | -estimation: 29 | -updating users: half of 1,000 client applications check once per day, in order to make updates 30 | -exploring users: 1/4 of 1,000 client applications use multiple calls on various stocks 31 | -many calls at one go may overload server 32 | -place HTTP calls into a queue 33 | -horizontally scale servers with a load balancer 34 | -latency of server 35 | -speed will vary in different global locations 36 | -transmitting speed 37 | -http responses has no size limit 38 | -too much data 39 | -horizotally scaled and indexed (by alphabetical order) 40 | -have database server to direct database calls and merge database calls into one request to be returned 41 | 42 | 4) Points of Failure 43 | -database 44 | -have slave database with copy of master database on standby 45 | -server 46 | -buffer capacity for peak periods 47 | 48 | 5) Enhancements to system 49 | -analytics server that logs server and database activity 50 | -outputs report to identify points of improvement 51 | -caching of popular stock tickers in servers 52 | -when requested, don't have to make database call to retrieve data 53 | 54 | :Suggested Answer: 55 | 56 | CONSIDERATIONS 57 | 58 | --> CLIENT EASE OF USE: easy for client developers to implement 59 | --> EASE FOR US TO IMPLEMENT AND MAINTAIN: make work as efficient as possible 60 | --> FLEXIBILITY FOR FUTURE DEMANDS: can be flexible if demands change 61 | --> SCALABILITY AND EFFICIENCY: mindful of efficiency of solution, so as to not overly burden service 62 | 63 | PROPOSAL #1 64 | 65 | Keep data is simple text files and let clients download data through some sort of FTP server. 66 | Pros: Easy to maintain in some sense, as files can be easily viewed and backed up. 67 | Cons: Require complex parsing to do any sort of query. 68 | If additional data added to text file, might break client's parsing mechanism 69 | 70 | PROPOSAL #2 71 | 72 | Use standard SQL database, and let users plug directly into it. 73 | Benefits: 74 | - Structured data 75 | - Adding data would bring about structure 76 | - Allow structured queries 77 | - Enjoy benefits of an ORM, ACID, rolling back and backing up data, and security could be provided using standard database features 78 | - Easy for clients to integrate into existing applications 79 | 80 | Disadvantages: 81 | - Direct connection -> lack flexibility for any data request that is of a specific shape 82 | - Much more than we actually need in terms of features for a feed 83 | - Need to implement additional layer to maintain and view the data. Increases implementation costs. 84 | - Security - While database is generally secure, shouldn't be allowing user to make any SQL request they wish to make 85 | - Might extract excessive amounts of data 86 | - Difficult to put in place restrictions 87 | - May make inefficient queries 88 | 89 | PROPOSAL #3 90 | 91 | XML 92 | Structure data as a tree. 93 | 94 | Benefits: 95 | -Clear and easy to distribute, can be read by both machines and humans 96 | -Programming languages have packages/libraries to perform XML parsing 97 | -Easy to add data to XML, just add additional nodes 98 | -Can use existing tools to back up data 99 | 100 | Disadvantages: 101 | -Sends clients all the information, even if they only want part of it. Inefficient 102 | -Queries are parsing an entire file 103 | 104 | SOAP - messaging protocol that allows programs that run on disparate operating systems (such as Windows and Linux) to communicate using Hypertext Transfer Protocol (HTTP) and its Extensible Markup Language (XML). 105 | 106 | Regardless of solution for data storage, we could provide a web service (SOAP) for client data access. Adds a layer to our work but it can provide additional security, and it may even make it easier for clients to integrate the system. 107 | 108 | However, clients could be limited to grabbing the data only how we expect them to. By contrast, SQL implementations could allow users to make whatevery complex queries they wanted, e.g. get highest prices, counts of stocks with a certain percentage growth, etc. 109 | -------------------------------------------------------------------------------- /chapter09/9.2 - Social Network/socialNetwork.md: -------------------------------------------------------------------------------- 1 | 1) Clarifying questions 2 | -Just the data structures 3 | -Functionality, each node will contain profile content 4 | -Data to display profile picture, name, education, etc 5 | -Friends 6 | -Feed 7 | 8 | 2) Basic Setup 9 | -Profile - object with properties, containing data 10 | -Personal Info 11 | -Education 12 | -Work 13 | -Interests 14 | -Friend connections - undirected graph, by userid (because A friend of B === B friend of A) 15 | ---> PARTITION FEATURES BY PROFILE INFORMATION AND CONNECTIONS 16 | 17 | -Algorithm: 18 | -Find id of person A and person B 19 | -Find id of person A and person B from both sides, expanding out degree by degree, removing for duplicates (breath first search, to minimise the number of degrees searched) 20 | -> bidirectional breath first search 21 | 22 | 3) Identify and address problems (at scale) 23 | -Exponential -> O(n^k) where n is the number of friends a person has, and k is degree 24 | -No real way to reduce the complexity 25 | -Practically, can cache previously searched friends to remove re-processing 26 | -Too many users 27 | -Separate graph search engine from profile information database/server 28 | -In graph search engine, organize graphs in sorted order (e.g. one server stores 2 million nodes in alphabetical order, and engine can call the databases storing nodes as and when it needs it) 29 | -It would be troublesome to reindex the databases if more users starting with 'A' apply 30 | -So could index by userid numerical order (with half a billion users that's a 10-digit number) 31 | ---> SHARD DATABASE IN NUMERICAL ORDER 32 | 33 | 4) Addressing possible points of failure 34 | -traffic spike 35 | -caching, load balancers and horizontal scaling 36 | -servers -> buffer capacity 37 | -databases -> slave database 38 | 39 | ANSWER KEY 40 | 41 | Question: How is a bidirectional BFS faster than a BFS? 42 | -Unidirectional BFS: O(k^n) where k is number of edges, and n is number of levels 43 | -Bidirectional BFS: O(k^(n/2)) which is a large factor smaller 44 | 45 | e.g. BFS of 5 nodes: a -> b -> c -> d -> e , where each node has 100 edges 46 | 47 | Unidirectional would take 100 ^ 4, bidirectional would take 2 * 100 ^ 2. 48 | This would be 100 ^ 2 / 2 in computational difference. 49 | 50 | Optimization: Batch Jumps 51 | 52 | Optimization: Smart Division - by clustering, or by country, or by city, or by college and year 53 | 54 | Follow on questions: 55 | a) Do you search until the end of the graph? How do you decide when to give up? 56 | b) Some people have more friends of friends than others. How could you use this data to pick where to start traversing? sort by friends when traversing BFS, and traverse the graphs with more friends first? 57 | -------------------------------------------------------------------------------- /chapter09/9.3 - Web Crawler/webCrawler.md: -------------------------------------------------------------------------------- 1 | Clarify Assumptions 2 | -Web crawler built using Javascript 3 | -Traverse through links (a tags) 4 | -Purpose of crawling the web is to build a search index/look for data 5 | 6 | -Use case - to build a search index at scale 7 | -Base product 8 | - indexing engine that crawls the web 9 | - word -> list of urls in order 10 | 11 | a.com -> b.com 12 | 13 | b.com -> a.com 14 | 15 | Setup 16 | 17 | index database-> where the unique key is the search term, and the list of urls is the values 18 | 19 | deliverable 20 | -keep track of visited sites at scale 21 | -hashtable 22 | 23 | -service that does indexing (BFS -> 10 layers from an entry point) 24 | -in the server, maintain a hashtable for memoising 25 | -for each URL traversed: 26 | -if URL present in hashtable, skip 27 | -store URL as key with true as value (string key -> boolean value) 28 | -tip: replace '.' with '%dot%' in URLs when storing 29 | 30 | Identify and Address Problems 31 | 32 | Scale: hashtable too big to hold all the visited sites 33 | Option 1) build a separate service to store cut up versions of the hashtable 34 | Option 2) store URL keys in a sharded database. 35 | 36 | Organizing the cut up database 37 | -by alphabetical order - easy to understand but hard to adjust 38 | -hash and possibly store urls in linked list (bad idea because need to re-index if size grows too big) 39 | 40 | -> use a distributed hashtable 41 | -to scale up and prevent failure nodes 42 | 43 | Points of failure 44 | i) database -> slave duplicate 45 | ii) server -> standby server 46 | 47 | ANSWERS 48 | 49 | --> define based on URL or based on content? no easy way to do this as content can be random. 50 | --> estimate degree of similarity based on URL and content. 51 | --> generate a signature based on specific subsections of page and its URL. 52 | --> query database to see if this signature has been crawled recently 53 | --> if something with signature has been crawled, insert page back into database at a low priority 54 | --> if not, crawl links and insert into database 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /chapter09/9.4 - Duplicate URLs/duplicateURLs.md: -------------------------------------------------------------------------------- 1 | Clarifying Questions 2 | -URL -> base URLs with queries 3 | https://www.facebook.com !== http://www.facebook.com !== http://www.facebook.com/jhaski 4 | not just routes, there are also extensions. 5 | 6 | -duplicate -> content 7 | -> different URLs could point at the same document 8 | -> doesn't matter 9 | thefacebook.com !== facebook.com, even though content is the same 10 | 11 | -detect duplicate strings, where the strings are URLs 12 | 13 | Setup 14 | -hashtable, and iterate through URLs. 15 | -for each URL: 16 | -if URL is marked visted in hashtable, flag as duplicate 17 | -else mark URL as visited in hashtable 18 | 19 | Identify and Address Problems 20 | -at scale: 21 | -hashtable will have to store a lot of URLs (10 billion!?) 22 | -running through 10 billion URLs in a single thread will take too long 23 | -storing the list of 10 billion URLs is also likely going to take up too much space for one server 24 | 25 | -distributed hashtable 26 | -distribute 10 billion URLs into muliple server nodes using distributed systems 27 | -parallel server nodes 28 | 29 | Points of Failure 30 | -the distributed approach is pretty good, covers for points of failure, comes at a cost of excess capacity but I think it's worth it 31 | 32 | 33 | --SPACE SIZING 34 | -> one URL how much space? 35 | a char is 1 byte / an integer is 4 bytes 36 | a url is around 30 chars, make it 100 as buffer -> 400 bytes 37 | 10 000 000 000 URLs -> 4 000 000 000 000 (4000 billion bytes) 38 | 4 000 000 000 000 bytes -> 4 000 000 000 kb -> 4 000 000 MB --> 4 000 GB -> 4 TB 39 | 40 | One hard drive should be able to store the entire list of 10 billion URLs but it should be a little large to open as a file 41 | 42 | For speed, list of URLs can be split up into smaller files and multiple server nodes, perhaps even in RAM for faster access speeds 43 | 44 | One Machine 45 | Pros: simple 46 | Cons: slow, 47 | 48 | Many Machines 49 | Pros: Fast, can run in parallel 50 | Cons: Complex, many points of failure, need backup 51 | -------------------------------------------------------------------------------- /chapter09/9.5 - Cache/cache.md: -------------------------------------------------------------------------------- 1 | Clarifying Questions 2 | 3 | -Problem: processSearch is expensive 4 | -Use Case: 5 | -Recent queries gets results from cache, and skips processSearch 6 | -When data changes, update cache 7 | -Assumption: 8 | -Data changes when search engine rebuilds index 9 | 10 | Basic Setup 11 | 12 | LRU cache service: 13 | -Assuming multiple client server machines 14 | -Service coordinates with machines to update cache 15 | -When first set up, loaded up by query terms, and results after running processSearch 16 | -Least recently queried is discarded 17 | -Provides API to send a copy of queries to index engine when requested 18 | 19 | Index engine: 20 | -When reindexing, requests for list of popular queries from cache engine, updates results, and sends back to cache engine 21 | 22 | Identify and Address Problems 23 | -Queries at the same time by multiple machines - Queue for cache service 24 | -Need to ping LRU cache service for each search - 25 | Address latency by having CDNs 26 | -Need to update LRU cache for each reindexing - 27 | Tradeoff for not having run processSearch 28 | 29 | Points of Failure 30 | -Index engine - slave copy 31 | -Search engine - slave copy 32 | 33 | --> ASSUMPTIONS 34 | i) many queries to cache 35 | ii) calling between machines is fast 36 | iii) the processSearch function is expensive 37 | iv) globally distributed clients 38 | v) result for query is ordered list of URLs 39 | vi) most popular queries are always popular, as well as faddish queries 40 | 41 | --> SYSTEM REQURIEMENTS 42 | Primary function of cache 43 | i) Efficient lookups 44 | ii) Discard old data 45 | Cannot wait for cache to naturally expire 46 | 47 | LRU cache => linkedlist + hashtable 48 | 49 | -->Options: 50 | -Each machine has its own cache 51 | -Each machine has a copy of the cache 52 | -Each machine stores a segment of the cache 53 | 54 | -->Updating results: 55 | 56 | -->Further enhancements: 57 | 58 | 59 | -------------------------------------------------------------------------------- /chapter09/9.6 - Sales Rank/salesRank.md: -------------------------------------------------------------------------------- 1 | Clarification: 2 | i) each product has a sales figure, and a bunch of categories 3 | -e.g. a product could some under "Sports Equipment" and "Safety". 4 | 5 | ii) as it is an ecommerce company, there is a need for scale, and a need for real-time updating, or updating without too much of a delay. 6 | 7 | --> no need to be immediate, can have an hour or a day lag 8 | 9 | iii) assume that the supply chain will take care of itself, and the focus here is on categorical ranking 10 | 11 | --> frontend systems 12 | --> define what sales means: total historical, past month, past week? 13 | --> multiple categories or subcategories 14 | --> precision is important for popular items, less important for less popular items 15 | --> no need to be that precise about the time range 16 | 17 | iv) at scale, there is a need to rank efficiently 18 | 19 | Setup: 20 | 21 | -Database of products with sales figures 22 | -Database of sales index by category 23 | -Build sales rankings 24 | -sort overall and assign a rank 25 | -for each category, sort and assign a rank 26 | -assume using merge sort so O(n log n) 27 | -Build rankings index of each item 28 | -overall, safety, etc. 29 | 30 | Problems: 31 | 32 | -Inefficient to sort by categories every time 33 | -O(n log n) 34 | -Instead just sort it the first time, and everytime there's a sale, 35 | -Allow item to climb up each ranking O(n) 36 | At scale, set up a queue for sales entries 37 | 38 | --> Analytics are expensive 39 | -tables to store sales requires computation 40 | -effective way to store sales is good 41 | 42 | --> Database writes are very frequent 43 | -should batch up sales records and update periodically at one go 44 | -maybe more frequent for popular products 45 | 46 | --> Ranking data 47 | -ensure no bias in data (added orders to one product but not others) 48 | -ensure sales rank doesn't run until all stored data is processed, or by dividing up in-memory cache by some time period 49 | 50 | --> Joins are expensive 51 | -for each category, pull data from items and then sort 52 | -or one join per category and the do one walk to get rankings 53 | 54 | --> Database queries are expensive 55 | -use just log files and MapReduce 56 | -write sales to each category 57 | -overall ranking, just pairwise merge if we need to 58 | -large advantage of scaling nicely 59 | 60 | -Scale 61 | -too many products to rank (amazon has 480 million products) 62 | 63 | --> Weekly sales 64 | -circular array (only keep a week of sales) 65 | 66 | --FOLLOW UP QUESTIONS 67 | --> Next bottlenecks 68 | -black friday sales 69 | -super popular items 70 | -mapReduce method still scalable 71 | -super popular categories 72 | -mapReduce method still scalable 73 | 74 | --> subcategories 75 | -treat subcategories as the 'overall' of one category 76 | -overall will be 'overall' of subcategories 77 | -no issues with respect to scaling 78 | 79 | --> data needed to be more accurate (say within 30 minutes) 80 | -need to refresh log files every 30 minutes 81 | -maybe just for popular items 82 | -quite expensive (should not run 24/7, maybe only during the popular times) 83 | -------------------------------------------------------------------------------- /chapter09/9.7 - Personal Financial Manager/personalFinancialManager.md: -------------------------------------------------------------------------------- 1 | Core functionality: 2 | -connect to bank accounts 3 | -spending habits 4 | -recommendations 5 | 6 | Clarifying assumptions: 7 | -Front end 8 | -Back end 9 | -Connect to bank accounts 10 | -Via an API 11 | -Only information, not actions (GET requests) 12 | -Detailed transactions data 13 | -Security/authentication is an issue 14 | -Analyse your spending habits 15 | -Engine to analyse habits 16 | -Make recommendations 17 | -Rules based engine 18 | 19 | -All very personalised 20 | -Privacy - security 21 | 22 | Assumptions: 23 | -Standardized set of transaction data from different banks 24 | -Transaction data is available 25 | -Don't have to worry about bank regulations etc. from different countries 26 | -Tax regimes by country and occupation probably matter 27 | -In addition to personal data, aggregate data is also of use 28 | 29 | FRONTEND: 30 | -Overall picture of finances for each user 31 | -Connect to a bank account 32 | -Once connected, see some analytics 33 | -Ask for manual edits/additions by user (categorize spending etc.) 34 | -analyze and make recommendations 35 | 36 | BACKEND: 37 | Services architecture: 38 | -Client service -> serves frontend files to client 39 | -Database service -> stores user profile in database 40 | -Analytics service -> crunches numbers and generates profiles 41 | -Bank API service -> talks to Bank API and retrieves client's bank transactions 42 | 43 | Client is shown page to sign up or login: 44 | -sign up -> creates user account in database 45 | -log in -> brings up user profile view 46 | -enter bank credentials 47 | -calls bank API for bank transactions 48 | -place transactions into database 49 | -inputs data into analytics service to generate profile 50 | -place analysed profile into database 51 | -updates client webpage with results and recommendations 52 | 53 | Mobile and Web Desktop: 54 | -Different interfaces and features 55 | -mobile -> less granular data and graphical explainations of recommendations 56 | -web -> space for more extensive visualizations 57 | -------------------------------------------------------------------------------- /chapter09/9.8 - Paste Bin/pasteBins.md: -------------------------------------------------------------------------------- 1 | front end 2 | -enter some text 3 | -click 'Create new paste' 4 | -reloads with a URL route to access this text, i.e. 'pastbin.com/abcd123' 5 | 6 | back end 7 | -entering with the route serves the file 8 | 9 | -server storing snippets as .txt file, i.e. 'abcd123.txt' 10 | -route uses route params as the text file name to search for inside the server 11 | 12 | fileread and filewrite of text files to place text content onto frontend -------------------------------------------------------------------------------- /chapter09/systemDesignNotes.md: -------------------------------------------------------------------------------- 1 | System Design: 2 | 1) Scope the problem 3 | -Ask clarifying questions 4 | -Develop use cases 5 | 6 | 2) Make reasonable assumptions 7 | -Make assumptions 8 | -Verify assumptions 9 | 10 | 3) Draw the major components 11 | -Draw system architecture diagram 12 | -Walk through system to provide a flow 13 | 14 | 4) Identify the key issues 15 | -System bottlenecks and major challenges (in real life) 16 | -Use guidance that interviewer gives 17 | 18 | 5) Redesign for the key issues 19 | -Propose adjustments to address key issues 20 | -Update diagram on whiteboard 21 | -Mention limitations 22 | 23 | Feature and Algorithms Scaling: 24 | 1) Ask questions 25 | -Ask clarifying questions and details 26 | 27 | 2) Make believe 28 | -Solve problem without space and time complexity limitations 29 | 30 | 3) Get real 31 | -Identify problems what will occur with scale 32 | -Needing to divide work load and storage 33 | -How to divide 34 | -How to look up 35 | 36 | 4) Solve Problems 37 | -Decide on approach to solve problems 38 | -Modification or reapproaching problem/feature 39 | -Iterate and go back to step 3 again 40 | -Poke holes in your own solution 41 | 42 | Concepts to use: 43 | 44 | Scaling: A system can be scaled one of two ways: 45 | 46 | -Vertical scaling - increasing resources of a specific node. For example, you might add additional memory to a server to improve its availability to handle load changes. 47 | 48 | -Horizontal scaling means increasing the number of nodes. For example, you might add additional servers, thus decreasing the load on any one server. 49 | 50 | Vertical scaling is generally easier than horizontal scaling, but it's limited. You can only add so much memory or disk space. 51 | 52 | Load Balancer 53 | 54 | Typically, some frontend parts of a scalable website will be thrown behind a load balancer. 55 | This allows a system to distribute the load evenly so that one server doesn't crash and take down 56 | the whole system. To do so, of course, you have to build out a network of cloned servers that all have essentially the same code and access to the same data. 57 | 58 | Database Denormalization and NoSQL 59 | 60 | Joins in a relational database such as SQL can get very slow as the system grows bigger. For this reason, you would generally avoid them. 61 | 62 | Denormalization is one part of this. Denormalization means adding redundant information into a database to speed up reads. For example, imagine a database describing projects and tasks (where a project can have multiple tasks). You might need to get the project name and the taks information. Rather than doing a join across these table, you can store the project name within the task table (in addition to the project table). 63 | 64 | Or, you can go with a NoSQL database. A NoSQL database does not support joins and might structure data in a different way. It is designed to scale better. 65 | 66 | Database partitioning (Sharding) 67 | 68 | Sharding means splitting the data across multiple machines while ensuring you have a way of figuring out which data is on which machine 69 | 70 | A few common ways of partitioning include: 71 | 72 | a) Vertical partitioning: this is basically partitioning by feature. For example, if you were building a social network, you might have one partition for tables relating to profiles, another one for messages, and so on. One drawback of this is that if one of these tables gets very large, you might need to repartition that database (possibly using a different partitioning scheme). 73 | 74 | b) Key-based (or Hash-based) Parititioning: This uses some part of the data (for example an ID) to partition it. A very simple way to do this is to allocate N servers and put the data on mod(key, n). One issue with this is that the number of servers you have is effectively fixed. Adding additional servers means reallocating all the data--a very expensive task. 75 | 76 | c) Directory-based Partitioning: In this scheme, you maintain a lookup table for where the data can be found. This makes it relatively easy to add additional servers, but it comes with two major drawbacks. First the lookup table can be a single point of failure. Second, constantly accessing this table impacts performance. 77 | 78 | Many architectures actually end up using multiple partitioning schemes. 79 | 80 | Caching 81 | 82 | An in-memory cache can deliver very rapid results. It is a simple key-value pairing and typically sits between your application layer and your data store. 83 | 84 | When an application requests a piece of information, it first tries the cache. If the cache does not contain the key, it will then look up the data in the data store. (At this point, the data might-or might not-be stored in the data store) 85 | 86 | When you cache, you might cache a query and its results directly. Or, alternatively, you can cache the specific object (for example, a rendered version of a part of the website, or a list of the most recent blog posts) 87 | 88 | Asynchonous Treatment 89 | 90 | -Display old version while updating 91 | -or display notice that updating is under way. 92 | 93 | Networking Metrics 94 | 95 | Bandwidth - This is the maximum amount of data that can be transferred in a unit of time. 96 | It is typically expressed in bits per second (or some similar ways, such as gigabytes per second) 97 | 98 | Throughput - Whereas bandwidth is the maximum data that can be transferred in a unit of time, 99 | throughput is the actual amount of data that is transferred. 100 | 101 | Latency - This is how long it takes data to go from one end to the other. That is, it is the delay 102 | between the sender sending information (even a very small chunk of data) and the receiver receiving it 103 | -------------------------------------------------------------------------------- /chapter10/10.01 - Sorted Merge/sortedMerge.js: -------------------------------------------------------------------------------- 1 | const sortedMerge = (a, b) => { 2 | if (a === undefined || b === undefined) { 3 | return 'where are your arrays?'; 4 | } else { 5 | // naive solution: create new array and merge 6 | // create new array 7 | let answer = []; 8 | // declare pointers for A and B 9 | let pointerA = 0; 10 | let pointerB = 0; 11 | // use pointers to iterate through A and B and insert elements into new array 12 | /* 13 | while (pointerA < a.length || pointerB < b.length) { 14 | if (pointerA === a.length) { 15 | answer.push(b[pointerB]); 16 | pointerB++; 17 | } else if (pointerB === b.length) { 18 | answer.push(a[pointerA]); 19 | pointerA++; 20 | } else if (a[pointerA] < b[pointerB]) { 21 | answer.push(a[pointerA]); 22 | pointerA++; 23 | } else { 24 | answer.push(b[pointerB]); 25 | pointerB++; 26 | } 27 | } 28 | return answer; 29 | */ 30 | 31 | // in-place solution: merge into A in place 32 | // helper function: move back array one space, from pointer to end 33 | const moveBack = (arr, pt, end) => { 34 | let currPt = end; 35 | while (currPt > pt) { 36 | arr[currPt] = arr[currPt - 1]; 37 | currPt--; 38 | } 39 | }; 40 | 41 | let end = a.length; 42 | // while pointerB is still traversing through B 43 | while (pointerB < b.length) { 44 | // if pointerA is done traversing (should just append all of B to back of A) 45 | if (pointerA === end) { 46 | a[pointerA] = b[pointerB]; 47 | pointerA++; 48 | pointerB++; 49 | end++; 50 | // else if value at pointerA is smaller that value at pointerB (should not insert, continue traversing A) 51 | } else if (a[pointerA] < b[pointerB]) { 52 | pointerA++; 53 | } else { 54 | // otherwise move all elements from pointerA back by one space, and insert value at pointerB into a 55 | moveBack(a, pointerA, end); 56 | a[pointerA] = b[pointerB]; 57 | pointerA++; 58 | pointerB++; 59 | end++; 60 | } 61 | } 62 | return a; 63 | } 64 | }; 65 | 66 | /* TEST */ 67 | 68 | // sortedMerge is callable 69 | console.log(sortedMerge() === 'where are your arrays?'); 70 | 71 | // sortedMerge merges in order 72 | console.log(JSON.stringify(sortedMerge([0, 2, 4], [1, 3, 5])) === JSON.stringify([0, 1, 2, 3, 4, 5])); 73 | 74 | // sortedMerges sorted arrays 75 | console.log(JSON.stringify(sortedMerge([0, 1, 2], [3, 4, 5])) === JSON.stringify([0, 1, 2, 3, 4, 5])); 76 | 77 | // sortedMerge merges empty arrays 78 | console.log(JSON.stringify(sortedMerge([0, 1, 2], [])) === JSON.stringify([0, 1, 2])); 79 | -------------------------------------------------------------------------------- /chapter10/10.02 - Group Anagrams/groupAnagrams.js: -------------------------------------------------------------------------------- 1 | const groupAnagrams = (strArr) => { 2 | if (strArr === undefined) { 3 | return 'where is your anagram?'; 4 | } else { 5 | let mapStrArr = strArr.map((string) => { 6 | return { 7 | original: string, 8 | sorted: string.split('').sort().join('') 9 | }; 10 | }); 11 | 12 | mapStrArr.sort((a, b) => { 13 | return a.sorted < b.sorted ? 1 : -1; 14 | }); 15 | 16 | const answer = mapStrArr.map((mapStr) => { 17 | return mapStr.original; 18 | }); 19 | 20 | return answer; 21 | } 22 | }; 23 | 24 | /* TEST */ 25 | 26 | const anagrams = [ 27 | 'motherinlaw', 28 | 'debit card', 29 | 'dormitory', 30 | 'theearthquakes', 31 | 'astronomer', 32 | 'punishments', 33 | 'schoolmaster', 34 | 'hitlerwoman', 35 | 'badcredit', 36 | 'dirtyroom', 37 | 'thequeershakes', 38 | 'moonstarrer', 39 | 'ninethumps', 40 | 'theclassroom' 41 | ]; 42 | 43 | const anagramsSorted = ["ninethumps", "punishments", "dormitory", "dirtyroom", "astronomer", "moonstarrer", "motherinlaw", "hitlerwoman", "thequeershakes", "schoolmaster", "theclassroom", "badcredit", "theearthquakes", "debit card"]; 44 | 45 | // groupAnagrams can be called 46 | console.log(groupAnagrams() === 'where is your anagram?'); 47 | 48 | // groupAnagrams can be sorted 49 | console.log(JSON.stringify(groupAnagrams(anagrams)) === JSON.stringify(anagramsSorted)); 50 | -------------------------------------------------------------------------------- /chapter10/10.03 - Search In Rotated Array/searchInRotatedArray.js: -------------------------------------------------------------------------------- 1 | const findStartEnd = (array, front, back) => { 2 | front = front || 0; 3 | back = back || array.length; 4 | if (array[front] < array[back - 1]) { 5 | return { 6 | start: front, 7 | end: back - 1 8 | }; 9 | } else { 10 | let mid = Math.floor( (back - front) / 2); 11 | if (array[mid - 1] > array[mid]) { 12 | return { 13 | start: mid, 14 | end: mid - 1 15 | }; 16 | } else { 17 | if (array[front] < array[mid]) { 18 | return findStartEnd(array, mid, back); 19 | } else { 20 | return findStartEnd(array, front, mid); 21 | } 22 | } 23 | } 24 | }; 25 | 26 | const searchInRotatedArray = (array, value, start, end) => { 27 | if (array === undefined) { 28 | return 'where is the array?'; 29 | } 30 | if (start === undefined && end === undefined) { 31 | const startEnd = findStartEnd(array); 32 | start = startEnd.start; 33 | end = startEnd.end; 34 | } 35 | let mid; 36 | if (start === end) { 37 | return array[start] === value ? start : -1; 38 | } else if (start > end) { 39 | const half = Math.floor((array.length - (start - end - 1)) / 2); 40 | mid = (start + half) % array.length; 41 | } else { 42 | mid = start + Math.floor((end - start) / 2); 43 | } 44 | if (array[mid] === value) { 45 | return mid; 46 | } else { 47 | if (value < array[mid]) { 48 | return searchInRotatedArray(array, value, start, mid); 49 | } else { 50 | if (start === mid) { 51 | return searchInRotatedArray(array, value, end, end); // does end check if start === mid 52 | } else { 53 | return searchInRotatedArray(array, value, mid, end); 54 | } 55 | } 56 | } 57 | }; 58 | 59 | /* TEST */ 60 | 61 | // findStartEnd works 62 | console.log(JSON.stringify(findStartEnd([5, 6, 7, 8, 1, 2, 3, 4])) === JSON.stringify({ start: 4, end: 3 })); 63 | 64 | // searchInRotatedArray can be called 65 | console.log(searchInRotatedArray() === 'where is the array?'); 66 | 67 | // find in unrotated array 68 | console.log(searchInRotatedArray([1, 2, 3, 4], 3) === 2); 69 | 70 | // find in rotated array 71 | console.log(searchInRotatedArray([3, 4, 1, 2], 1) === 2); 72 | 73 | // find last item in rotated array 74 | console.log(searchInRotatedArray([3, 4, 1, 2], 4) === 1); 75 | 76 | // return -1 for values not in array 77 | console.log(searchInRotatedArray([3, 4, 1, 2], 10) === -1); 78 | -------------------------------------------------------------------------------- /chapter10/10.04 - Sorted Search No Size/listy.js: -------------------------------------------------------------------------------- 1 | class Listy { 2 | constructor(array) { 3 | this.container = array || []; 4 | } 5 | 6 | elementAt(i) { 7 | if (i >= this.container.length || i < 0) { 8 | return -1; 9 | } else { 10 | return this.container[i]; 11 | } 12 | } 13 | } 14 | 15 | module.exports = Listy; 16 | -------------------------------------------------------------------------------- /chapter10/10.04 - Sorted Search No Size/sortedSearchNoSize.js: -------------------------------------------------------------------------------- 1 | // helper function 2 | const findEnd = (listy, last, term) => { 3 | if (last === undefined && term === undefined) { 4 | last = 0; 5 | term = 1; 6 | // edge case where listy is empty 7 | if (listy.elementAt(last) === -1) { 8 | return 0; 9 | } 10 | } 11 | if (listy.elementAt(last) !== -1 && listy.elementAt(term) !== -1) { 12 | return findEnd(listy, last, term * 2); 13 | } else if (listy.elementAt(last) !== -1 && listy.elementAt(term) === -1) { 14 | if (last === term - 1) { 15 | return term; 16 | } 17 | const half = Math.floor((term - last) / 2); 18 | const mid = last + half; 19 | if (listy.elementAt(mid) === -1) { 20 | return findEnd(listy, last, mid); 21 | } else { 22 | return findEnd(listy, mid, term); 23 | } 24 | } else { 25 | throw `error: index ${last} is ${listy.elementAt(last)} and ${term} is ${listy.elementAt(term)}`; 26 | } 27 | }; 28 | 29 | const sortedSearchNoSize = (listy, value, front, back) => { 30 | if (listy === undefined) { 31 | return 'what are we searching?'; 32 | } 33 | if (front === undefined && back === undefined) { 34 | front = 0; 35 | const end = findEnd(listy); 36 | back = end; 37 | } 38 | if (front === back) { 39 | return -1; 40 | } 41 | const half = Math.floor((back - front) / 2); 42 | const mid = front + half; 43 | if (listy.elementAt(mid) === value) { 44 | return mid; 45 | } else if (value < listy.elementAt(mid)) { 46 | return sortedSearchNoSize(listy, value, front, mid); 47 | } else { 48 | if (mid === back - 1) { 49 | return -1; 50 | } else { 51 | return sortedSearchNoSize(listy, value, mid, back); 52 | } 53 | } 54 | }; 55 | 56 | /* TEST */ 57 | 58 | const Listy = require('listy.js'); 59 | 60 | // sortedSearchNoSize is available as a function 61 | console.log(sortedSearchNoSize() === 'what are we searching?'); 62 | 63 | // Listy is working 64 | let listy = new Listy([1, 2, 3, 4]); 65 | console.log(listy.length === undefined); 66 | console.log(listy[1] === undefined); 67 | console.log(listy.length === undefined); 68 | console.log(listy.elementAt(10) === -1); 69 | console.log(listy.elementAt(2) === 3); 70 | 71 | // sortedSearchNoSize works as expected 72 | listy = new Listy([2, 3, 4, 6]); 73 | console.log(sortedSearchNoSize(listy, 3) === 1); 74 | console.log(sortedSearchNoSize(listy, 2) === 0); 75 | console.log(sortedSearchNoSize(listy, 6) === 3); 76 | console.log(sortedSearchNoSize(listy, 1) === -1); 77 | console.log(sortedSearchNoSize(listy, 10) === -1); 78 | console.log(sortedSearchNoSize(listy, 5) === -1); 79 | -------------------------------------------------------------------------------- /chapter10/10.05 - Sparse Search/sparseSearch.js: -------------------------------------------------------------------------------- 1 | const findMid = (front, back) => { 2 | const half = Math.floor((back - front) / 2); 3 | const mid = front + half; 4 | return mid; 5 | }; 6 | 7 | const nonBlankMid = (array, front, back) => { 8 | let mid = findMid(front, back); 9 | let startSearch = { 10 | front: front, 11 | back: back, 12 | mid: mid 13 | }; 14 | let searchQueue = [startSearch]; 15 | let currSearch; 16 | let currFront; 17 | let currMid; 18 | let currBack; 19 | while (searchQueue.length > 0) { 20 | currSearch = searchQueue.shift(); 21 | currFront = currSearch.front; 22 | currMid = currSearch.mid; 23 | currBack = currSearch.back; 24 | if (array[currMid] !== '') { 25 | return currMid; 26 | } else { 27 | if (currFront < currMid) { 28 | searchQueue.push({ 29 | front: currFront, 30 | back: currMid, 31 | mid: findMid(currFront, currMid) 32 | }); 33 | } 34 | if (currMid < currBack) { 35 | if (currMid !== currBack - 1) { 36 | searchQueue.push({ 37 | front: currMid, 38 | back: currBack, 39 | mid: findMid(currMid, currBack) 40 | }); 41 | } 42 | } 43 | } 44 | } 45 | return -1; 46 | }; 47 | 48 | const sparseSearch = (string, array, front, back) => { 49 | // check if there are inputs 50 | if (string === undefined || array === undefined) { 51 | return 'what are you searching?'; 52 | } 53 | // set default front and back 54 | if (front === undefined && back === undefined) { 55 | front = 0; 56 | back = array.length; 57 | if (front === back) { // edge case of empty array 58 | return -1; 59 | } 60 | } 61 | // find non-blank by binary BFS 62 | const mid = nonBlankMid(array, front, back); 63 | if (mid === -1) { 64 | return -1; 65 | } else { 66 | if (string === array[mid]) { 67 | return mid; 68 | } else if (string < array[mid]) { 69 | return sparseSearch(string, array, front, mid); 70 | } else { 71 | return sparseSearch(string, array, mid, back); 72 | } 73 | } 74 | }; 75 | 76 | /* TEST */ 77 | 78 | // sparseSearch can be called 79 | console.log(sparseSearch() === 'what are you searching?'); 80 | 81 | // nonBlankMid works 82 | console.log(nonBlankMid(['', '', '', '', '', '', '', '', '', '', '', ''], 0, 12) === -1); 83 | console.log(nonBlankMid(['', '', '', '', '', '', '', '', '', 'hello', '', ''], 0, 12) === 9); 84 | console.log(nonBlankMid(['', 'hello', '', '', '', '', '', '', '', '', '', ''], 0, 12) === 1); 85 | console.log(nonBlankMid(['hello', '', '', '', '', '', '', '', '', '', '', ''], 0, 12) === 0); 86 | console.log(nonBlankMid(['', '', '', '', '', '', '', '', '', '', '', 'hello'], 0, 12) === 11); 87 | 88 | // sparseSearch works 89 | console.log(sparseSearch('ball', ['at', '', '', '', 'ball', '', '', 'car', '', '', 'dad', '', '']) === 4); 90 | console.log(sparseSearch('ball', ['', '', '', '', '', '', '', '', '', '']) === -1); 91 | -------------------------------------------------------------------------------- /chapter10/10.06 - Sort Big File/sortBigFile.js: -------------------------------------------------------------------------------- 1 | // Break up files into files containing one line each 2 | // Iterate through files two by two and merge them 3 | // Keep merging until there is one file 4 | // If there are multiple threads, can dispatch them as multiple jobs to merge files together 5 | 6 | -------------------------------------------------------------------------------- /chapter10/10.07 - Missing Int/missingInt.js: -------------------------------------------------------------------------------- 1 | // 4 000 000 000 * 8 bytes => 32 000 000 000 bytes = 32 GB 2 | 3 | // 1) sort 4 | // 2) iterate linearly 5 | // 3) return number with gap 6 | -------------------------------------------------------------------------------- /chapter10/10.08 - Find Duplicates/findDuplicates.js: -------------------------------------------------------------------------------- 1 | // We're given 4 kilobytes (4 * 2^10 * 8 = 32*2^10 bits) of main memory(RAM) to write a program to print all duplicate entries in an array 2 | // from 1 to N where N is at most 32,000 (32*2^10). 3 | 4 | // We can use an Int8Array that uses 1 Byte per element to store 0's if we haven't seen it and 1's if we have seen it. 5 | // The Int8Array typed array represents an array of twos-complement 8-bit signed integers. 6 | // 4 KB > 32,000 bits 7 | 8 | // Time Complexity - O(n) 9 | // Space Complexity - O(n) 10 | 11 | // Let's have a JavaScript Solution of this program: 12 | 13 | function FindDuplicates(arr, range = 32000) { 14 | // Initialize integer 8 bit array with 0's at every index 15 | const numbersSeen = new Int8Array(range); 16 | const duplicates = []; 17 | for (let i = 0; i < arr.length; i++) { 18 | if (numbersSeen[arr[i]] === 0) { 19 | numbersSeen[arr[i]] = 1; 20 | } else { 21 | duplicates.push(arr[i]); 22 | } 23 | } 24 | return duplicates; 25 | } 26 | 27 | const arr = [ 28 | 0, 29 | 1, 30 | 1, 31 | 2, 32 | 4, 33 | 6, 34 | 7, 35 | 9, 36 | 9, 37 | 34, 38 | 56, 39 | 78, 40 | 90, 41 | 101, 42 | 345, 43 | 789, 44 | 999, 45 | 999, 46 | 11000 47 | ]; 48 | FindDuplicates(arr); 49 | // [1, 9, 999] 50 | -------------------------------------------------------------------------------- /chapter10/10.09 - Sorted Matrix Search/sortedMatrixSearch.js: -------------------------------------------------------------------------------- 1 | const sortedMatrixSearch = (matrix, value, front, back) => { 2 | if (matrix === undefined) { 3 | return 'where is your matrix?'; 4 | } 5 | const m = matrix.length; 6 | const n = matrix[0].length; 7 | 8 | if (front === undefined && back === undefined) { 9 | front = 0; 10 | back = m * n; 11 | } 12 | console.log(front, back); 13 | 14 | if (front > back) { return -1; } 15 | 16 | let mid = Math.floor((front + back) / 2); 17 | const row = Math.floor(mid / n); 18 | const col = mid % n; 19 | 20 | if (matrix[row][col] === value) { 21 | return [row, col]; 22 | } else if (value < matrix[row][col]) { 23 | return sortedMatrixSearch(matrix, value, front, mid); 24 | } else { 25 | return sortedMatrixSearch(matrix, value, mid + 1, back); 26 | } 27 | }; 28 | 29 | // sortedMatrixSearch can be called 30 | console.log(sortedMatrixSearch() === 'where is your matrix?'); 31 | 32 | // sortedMatrixSearch finds existing values 33 | console.log(JSON.stringify(sortedMatrixSearch([[1, 2], [3, 4]], 3)) === JSON.stringify([1, 0])); 34 | console.log(JSON.stringify(sortedMatrixSearch([[1, 2, 3], [4, 5, 6], [7, 8, 9]], 9)) === JSON.stringify([2, 2])); 35 | console.log(JSON.stringify(sortedMatrixSearch([[1, 2, 3], [4, 5, 6], [7, 8, 9]], 1)) === JSON.stringify([0, 0])); 36 | console.log(JSON.stringify(sortedMatrixSearch([[1, 2, 3], [4, 5, 6], [7, 8, 9]], 5)) === JSON.stringify([1, 1])); 37 | 38 | // sortedMatrixSearch returns -1 for value greater than range 39 | console.log(sortedMatrixSearch([[1, 2, 3], [4, 5, 6], [7, 8, 9]], 100) === -1); 40 | 41 | // sortedMatrixSearch returns -1 for value absent from range 42 | console.log(sortedMatrixSearch([[1, 2, 3], [5, 6, 7], [8, 9, 10]], 4) === -1); 43 | 44 | // sortedMatrixSearch returns -1 for value lower than range 45 | console.log(sortedMatrixSearch([[1, 2, 3], [4, 5, 6], [7, 8, 9]], 0) === -1); 46 | -------------------------------------------------------------------------------- /chapter10/10.10 - Rank From Stream/rankFromStream.js: -------------------------------------------------------------------------------- 1 | // use a heap (cheap insertion) 2 | // get rank => keep ejecting until find item, and then put back 3 | 4 | class Heap { 5 | constructor() { 6 | this.values = []; 7 | } 8 | 9 | insert(value) { 10 | this.values.push(value); 11 | this.floatUp(); 12 | } 13 | 14 | remove() { 15 | const front = 0; 16 | const back = this.values.length - 1; 17 | this.swap(front, back); 18 | let removed = this.values.pop(); 19 | this.sinkDown(); 20 | return removed; 21 | } 22 | 23 | sinkDown(index) { 24 | let currIndex = index || 0; 25 | let left = currIndex * 2 + 1; 26 | let right = currIndex * 2 + 2; 27 | let sinkTo = this.values[left] < this.values[right] ? left : right; 28 | if (sinkTo < this.values.length && this.values[currIndex] > this.values[sinkTo]) { 29 | this.swap(currIndex, sinkTo); 30 | this.sinkDown(sinkTo); 31 | } 32 | } 33 | 34 | floatUp(index) { 35 | let currIndex = index || this.values.length - 1; 36 | let parent = Math.floor((currIndex - 1) / 2); 37 | if (parent >= 0 && this.values[parent] > this.values[currIndex]) { 38 | this.swap(parent, currIndex); 39 | this.floatUp(parent); 40 | } 41 | } 42 | 43 | swap(index1, index2) { 44 | const temp1 = this.values[index1]; 45 | const temp2 = this.values[index2]; 46 | this.values[index1] = temp2; 47 | this.values[index2] = temp1; 48 | } 49 | 50 | isEmpty() { 51 | return this.values.length === 0; 52 | } 53 | 54 | peek() { 55 | return this.values[0]; 56 | } 57 | } 58 | 59 | const heap = new Heap(); 60 | 61 | function getRankOfNumber(x) { 62 | const stack = []; 63 | let rank = 0; 64 | let value; 65 | while (!heap.isEmpty()) { 66 | value = heap.remove(); 67 | if (value === x && heap.peek() !== x) { 68 | heap.insert(value); 69 | while (stack.length > 0) { 70 | heap.insert(stack.pop()); 71 | } 72 | return rank; 73 | } else { 74 | rank++; 75 | stack.push(value); 76 | } 77 | } 78 | while (stack.length > 0) { 79 | heap.insert(stack.pop()); 80 | } 81 | return -1; 82 | } 83 | 84 | function track(x) { 85 | heap.insert(x); 86 | } 87 | 88 | /* TEST */ 89 | // insert 5, 1, 4, 4, 5, 9, 13, 7, 3 90 | track(5); 91 | track(1); 92 | track(4); 93 | track(4); 94 | track(5); 95 | track(9); 96 | track(13); 97 | track(7); 98 | track(3); 99 | 100 | // getRankOfNumber(1) = 0 101 | // getRankOfNumber(3) = 1 102 | // getRankOfNumber(4) = 3 103 | console.log(getRankOfNumber(1) === 0); 104 | console.log(getRankOfNumber(3) === 1); 105 | console.log(getRankOfNumber(4) === 3); 106 | -------------------------------------------------------------------------------- /chapter10/10.11 - Peaks And Valleys/peaksAndValleys.js: -------------------------------------------------------------------------------- 1 | // sorting algorithm 2 | 3 | // one pass 4 | 5 | // for each member, do a comparison with left and right 6 | 7 | // alternately check if it is a peak or a valley 8 | 9 | // time complexity: O(N) 10 | // space complexity: O(1) 11 | 12 | function swap(array, i, j) { 13 | const valueI = array[i]; 14 | const valueJ = array[j]; 15 | array[i] = valueJ; 16 | array[j] = valueI; 17 | } 18 | 19 | function peaksAndValleys(array) { 20 | if (!Array.isArray(array)) { return 'where is your array?'; } 21 | let isPeak = true; 22 | let curr; 23 | let right; 24 | for (let i = 0; i < array.length - 1; i++) { 25 | curr = array[i]; 26 | right = array[i+1]; 27 | if (isPeak) { 28 | if (curr < right) { 29 | swap(array, i, i+1); 30 | } 31 | } else { 32 | if (curr > right) { 33 | swap(array, i, i+1); 34 | } 35 | } 36 | isPeak = !isPeak; 37 | } 38 | return array; 39 | } 40 | 41 | /* TEST */ 42 | // function can be called 43 | console.log(peaksAndValleys() === 'where is your array?'); 44 | 45 | // more tests needed 46 | -------------------------------------------------------------------------------- /chapter10/sortingAlgos/quickSort.js: -------------------------------------------------------------------------------- 1 | // write the quickSort algorithm 2 | 3 | const swap = (array, left, right) => { 4 | const temp1 = array[left]; 5 | const temp2 = array[right]; 6 | array[left] = temp2; 7 | array[right] = temp1; 8 | }; 9 | 10 | const partition = (array, left, right) => { 11 | const pivotIndex = Math.floor((left + right) / 2); 12 | const pivot = array[pivotIndex]; 13 | while(left <= right) { 14 | while(array[left] < pivot) { 15 | left++; 16 | } 17 | while(pivot < array[right]) { 18 | right--; 19 | } 20 | if (left <= right) { 21 | console.log(`swap ${array[left]} and ${array[right]}`); 22 | swap(array, left, right); 23 | console.log(array); 24 | left++; 25 | right--; 26 | } 27 | } 28 | return left; 29 | }; 30 | 31 | const quickSort = (array, left, right) => { 32 | const index = partition(array, left, right); 33 | if (left < index - 1) { 34 | quickSort(array, left, index - 1); 35 | } 36 | if (index < right) { 37 | quickSort(array, index, right); 38 | } 39 | }; 40 | 41 | let array = [4, 7, 1, 9, 3, 8, 0, 2]; 42 | quickSort(array, 0, array.length - 1); 43 | console.log('quicksorted array is', array); 44 | -------------------------------------------------------------------------------- /lib/data-structures/chapter-2/2_1.js: -------------------------------------------------------------------------------- 1 | var SLL = require('./helper.js'); 2 | 3 | SLL.prototype.removeDups = function(head) { 4 | 5 | if (head === null || head.next === null) { 6 | return null; 7 | } 8 | 9 | var current = head; 10 | 11 | while(current !== null) { 12 | var runner = current; 13 | while (runner.next !== null) { 14 | if (runner.next.data === current.data) { 15 | runner.next = runner.next.next; 16 | this.length--; 17 | } else { 18 | runner = runner.next; 19 | } 20 | } 21 | current = current.next; 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /lib/data-structures/chapter-2/2_2.js: -------------------------------------------------------------------------------- 1 | var SLL = require('./helper.js'); 2 | 3 | SLL.prototype.nthToLast = function(head, k) { 4 | var p1 = head, 5 | p2 = head; 6 | 7 | for (var i = 0; i < k; i++) { 8 | if (p1 === null) { 9 | return null; 10 | } 11 | p1 = p1.next; 12 | } 13 | 14 | while(p1 !== null) { 15 | p1 = p1.next; 16 | p2 = p2.next; 17 | } 18 | return p2; 19 | }; 20 | -------------------------------------------------------------------------------- /lib/data-structures/chapter-2/2_3.js: -------------------------------------------------------------------------------- 1 | var SLL = require('./helper.js'); 2 | 3 | function Node(data, next) { 4 | this.data = data, 5 | this.next = next; 6 | } 7 | 8 | SLL.prototype.addNode = function(data, next) { 9 | var node = new Node(data, next), 10 | current = this.head; 11 | 12 | if (this.head === null) { 13 | this.head = node; 14 | this.length++; 15 | return; 16 | } 17 | while (current.next) { 18 | current = current.next; 19 | } 20 | current.next = node; 21 | this.length++; 22 | }; 23 | 24 | //solution for delete middle node with access to the head of LinkedList 25 | SLL.prototype.findMiddleAndRemove = function(head) { 26 | ( head === null || head.next === null ) ? null : head; 27 | 28 | var slowPointer = head, 29 | fastPointer = head, 30 | previous = null; 31 | 32 | while(fastPointer.next !== null && fastPointer.next.next !== null) { 33 | fastPointer = fastPointer.next.next; 34 | previous = slowPointer; 35 | slowPointer = slowPointer.next; 36 | } 37 | 38 | previous.next = slowPointer.next; 39 | this.length--; 40 | }; 41 | 42 | //solution for delete middle node with access to the node ( middle node ) not the head of LinkedList 43 | SLL.prototype.removeMiddleNode = function(currentNode) { 44 | if ( currentNode === null || currentNode.next === null) { 45 | return false; 46 | } 47 | //copy next node data to current node 48 | var nextNode = currentNode.next; 49 | currentNode.data = nextNode.data; 50 | currentNode.next = nextNode.next; 51 | 52 | //remove next node 53 | currentNode.next = nextNode.next; 54 | this.length--; 55 | }; 56 | 57 | //return MyLinkedList; 58 | -------------------------------------------------------------------------------- /lib/data-structures/chapter-2/2_8.js: -------------------------------------------------------------------------------- 1 | var SLL = require('./helper.js'); 2 | 3 | SLL.prototype.findBeginning = function(head) { 4 | var slow = head, 5 | fast = head; 6 | 7 | //find meeting point 8 | while (fast !== null && fast.next !== null) { 9 | slow = slow.next; 10 | fast = fast.next.next; 11 | if (slow === fast) { 12 | break; 13 | } 14 | } 15 | 16 | // no loop 17 | if (fast === null || fast.next === null) { 18 | return null; 19 | } 20 | 21 | // move slow to head. keep fast at meeting point. if they move at the same pace, they must meet at the loop start. 22 | slow = head; 23 | while (slow !== fast) { 24 | slow = slow.next; 25 | fast = fast.next; 26 | } 27 | return fast; 28 | }; 29 | -------------------------------------------------------------------------------- /lib/data-structures/chapter-2/helper.js: -------------------------------------------------------------------------------- 1 | module.exports = MyLinkedList = (function(){ 2 | 3 | function MyLinkedList() { 4 | this.head = null; 5 | this.length = 0; 6 | } 7 | 8 | return MyLinkedList; 9 | })(); 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CtCI", 3 | "version": "0.0.1", 4 | "description": "Cracking the Coding Interview - Answers in Javascript", 5 | "scripts": { 6 | "test": "eslint ." 7 | }, 8 | "dependencies": {}, 9 | "devDependencies": { 10 | "eslint": "^6.6.0" 11 | }, 12 | "license": "MIT" 13 | } 14 | -------------------------------------------------------------------------------- /test/data-structures/chapter-2/2_1_Spec.js: -------------------------------------------------------------------------------- 1 | require('../../test_helper'); 2 | 3 | describe('2.1 #removeDups', function () { 4 | describe('remove duplicate from linkedlist', function () { 5 | var sll; 6 | beforeEach(function() { 7 | sll = new MyLinkedList(); 8 | for(var i=1; i < 6; i++) { 9 | sll.addNode( i + 'Node', null); 10 | } 11 | //create duplicate in linkedlist 12 | sll.addNode('5Node', null); 13 | }); 14 | 15 | afterEach(function() { 16 | sll = null; 17 | }); 18 | 19 | it('should delete duplicate node from SLL', function () { 20 | sll.removeDups(sll.head); 21 | expect(sll).length.to.be(5); 22 | }); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/data-structures/chapter-2/2_2_Spec.js: -------------------------------------------------------------------------------- 1 | require('../../test_helper'); 2 | 3 | describe('2.2 #nthToLast', function () { 4 | describe('return Kth element to last', function () { 5 | var sll; 6 | beforeEach(function() { 7 | sll = new MyLinkedList(); 8 | for(var i=1; i < 6; i++) { 9 | sll.addNode( i + 'Node', null); 10 | } 11 | }); 12 | 13 | afterEach(function() { 14 | sll = null; 15 | }); 16 | 17 | it('should return Kth element to last', function () { 18 | var kthNode = sll.nthToLast(sll.head, 3); 19 | expect(kthNode).to.not.be.null; 20 | expect(kthNode).to.have.property('data').and.be.equal('3Node'); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/data-structures/chapter-2/2_3_Spec.js: -------------------------------------------------------------------------------- 1 | require('../../test_helper'); 2 | 3 | describe('2.3 #FindMiddle', function () { 4 | describe('return middle Node', function () { 5 | var sll; 6 | beforeEach(function() { 7 | sll = new MyLinkedList(); 8 | for(var i=1; i < 6; i++) { 9 | sll.addNode( i + 'Node', null); 10 | } 11 | }); 12 | 13 | afterEach(function() { 14 | sll = null; 15 | }); 16 | 17 | it('should return correct length of LinkedList', function () { 18 | expect(sll).length.to.be(5); 19 | }); 20 | 21 | it('should remove the middle node with aceess to the head of SLL', function () { 22 | sll.findMiddleAndRemove(sll.head); 23 | expect(sll).length.to.be(4); 24 | }); 25 | 26 | it('should remove the middle node with access to the middle_node not head', function () { 27 | sll.removeMiddleNode(sll.head.next.next); 28 | expect(sll).length.to.be(4); 29 | }); 30 | 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /test/data-structures/chapter-2/2_8_Spec.js: -------------------------------------------------------------------------------- 1 | require('../../test_helper'); 2 | 3 | describe('2.8 #findBeginning', function () { 4 | describe('find loop in Singly Linkedlist', function () { 5 | var sll; 6 | beforeEach(function() { 7 | sll = new MyLinkedList(); 8 | for(var i=1; i < 6; i++) { 9 | sll.addNode( i + 'Node', null); 10 | } 11 | //create loop in linkedlist 12 | sll.addNode('6Node', sll.head.next.next.next); 13 | }); 14 | 15 | afterEach(function() { 16 | sll = null; 17 | }); 18 | 19 | it('should return loop start (node) in SLL', function () { 20 | var loopPoint = sll.findBeginning(sll.head); 21 | expect(loopPoint).not.be.null; 22 | expect(loopPoint).to.have.property('data').and.be.equal('4Node'); 23 | }); 24 | }); 25 | }); 26 | --------------------------------------------------------------------------------