├── .gitignore ├── CTCI ├── 1-1-is-unique.js ├── 1-2-check-permutation.js ├── 1-3-URLify.js ├── 1-4-palindrome-permutation.js ├── 1-5-one-away.js ├── 1-6-string-compression.js ├── 16-10-living-people.js ├── 16-20-phoney-words.js ├── 16-21-sum-swap.js ├── 17-26-sparse-similarity.js ├── 2-1-remove-dups.js ├── 4-1-route-between-nodes.js ├── 4-2-minimal-tree.js └── 6-1-the-heavy-pill.js ├── README.md ├── babel.config.js ├── datastructure ├── BSTUtils.js ├── Dequeue.js ├── FakeQueue.js ├── FakeQueue_immutable.js ├── LinkedList.js ├── MinHeap.js ├── Node │ ├── BSTNode.js │ ├── DoublyNode.js │ ├── GraphNode.js │ ├── SinglyNode.js │ └── TreeNode.js ├── Queue.js ├── TriePrefix.js ├── UnionFind.js └── test │ └── minHeap.test.js ├── leetcode ├── 1-two-sum.js ├── 1008-construct-binary-search-tree-from-preorder-traversal.js ├── 1010-pairs-of-songs-with-durations-divisible-by-60.js ├── 1022-sum-of-root-to-leaf-binary-numbers.js ├── 1026-maximum-difference-between-node-and-ancestor.js ├── 1029-two-city-scheduling.js ├── 1035-uncrossed-lines.js ├── 104-max-depth-of-binary-tree.js ├── 1041-robot-bounded-in-circle.js ├── 107-binary-tree-level-order-traversal-ii.js ├── 110-balanced-binary-tree.js ├── 1138-alphabet-board-path.js ├── 116-populating-next-right-pointers-in-each-node.js ├── 117-populating-next-right-pointers-in-each-node-ii.js ├── 121-best-time-to-buy-and-sell-stocks.js ├── 1217-minimum-cost-to-move-chips-to-the-same-position.js ├── 122-best-time-to-buy-and-sell-stocks-ii.js ├── 1232-check-if-it-is-a-straight-line.js ├── 125-valid-palindrome.js ├── 1276-number-of-burgers-with-no-waste-of-ingredients.js ├── 1277-count-square-submatrices-with-all-ones.js ├── 1283-find-the-smallest-divisor-given-a-threshold.js ├── 129-sum-root-to-leaf-numbers.js ├── 1290-convert-binary-number-in-a-linked-list-to-integer.js ├── 1291-sequential-digit.js ├── 130-surrounded-regions.js ├── 1305-all-elements-in-two-binary-search-trees.js ├── 1306-jump-game-iii.js ├── 135-candy.js ├── 136-single-number.js ├── 137-single-number-ii.js ├── 1379-find-a-corresponding-node-of-a-binary-tree-in-a-clone-of-that-tree.js ├── 1386-cinema-seat-allocation.js ├── 146-lru-cache.js ├── 1464-maximum-product-of-two-elements-in-an-array.js ├── 1465-maximum-area-a-piece-of-cake.js ├── 1466-consecutive-characters.js ├── 1470-shuffle-the-array.js ├── 1471-the-k-strongest-values-in-an-array.js ├── 1472-design-browser-history.js ├── 152-maximum-product-subarray.js ├── 1640-check-array-formation-through-concatenation.js ├── 165-compare-version-numbers.js ├── 169-majority-element.js ├── 198-house-robber.js ├── 2-add-two-numbers.js ├── 20-valid-parentheses.js ├── 202-happy-number.js ├── 207-course-schedule.js ├── 208-implement-trie-prefix-tree.js ├── 21-merge-two-sorted-lists.js ├── 216-combination-sum-iii.js ├── 220-contains-duplicate-iii.js ├── 226-invert-binary-tree.js ├── 230-kth-smallest-element-in-a-bst.js ├── 231-power-of-two.js ├── 237-delete-node-in-a-linked-list.js ├── 239-sliding-window-maximum.js ├── 24-swap-nodes-in-pairs.js ├── 275-h-index-ii.js ├── 278-first-bad-version.js ├── 283-move-zeroes.js ├── 290-word-pattern.js ├── 299-bulls-and-cows.js ├── 3-longest-substring-without-repeating-characters.js ├── 300-longest-increasing-subsequence.js ├── 312-burst-balloons.js ├── 328-odd-even-linked-list.js ├── 334-increasing-triplet-subsequence.js ├── 337-house-robber-iii.js ├── 338-counting-bits.js ├── 344-reverse-string.js ├── 347-top-k-frequent-elements.js ├── 35-search-insert-position.js ├── 367-valid-perfect-square.js ├── 380-insert-delete-getrandom-o1.js ├── 383-ransom-note.js ├── 387-first-unique-character-in-a-string.js ├── 39-combination-sum.js ├── 392-is-subsequence.js ├── 394-decode-string.js ├── 40-combination-sum-ii.js ├── 402-remove-k-digits.js ├── 406-queue-reconstruction-by-height.js ├── 41-first-missing-positive.js ├── 42-trapping-rain-water.js ├── 421-maximum-xor-of-two-numbers-in-an-array.js ├── 43-multiply-strings.js ├── 438-find-all-anagrams-in-a-string.js ├── 441-arranging-coins.js ├── 445-add-two-numbers-ii.js ├── 451-sort-characters-by-frequency.js ├── 452-minimum-number-of-arrows-to-burst-balloons.js ├── 459-repeated-substring-pattern.js ├── 468-valid-ip-address.js ├── 47-permutations-ii.js ├── 476-number-complement.js ├── 520-detect-capital.js ├── 525-contiguous-array.js ├── 528-random-pick-with-weight.js ├── 53-maximum-subarray.js ├── 540-single-element-in-a-sorted-array.js ├── 56-merge-intervals.js ├── 563-binary-tree-tilt.js ├── 567-permutation-in-string.js ├── 57-insert-interval.js ├── 58-length-of-last-word.js ├── 59-spiral-matrix-ii.js ├── 593-valid-square.js ├── 594-longest-harmonious-subsequence.js ├── 605-can-place-flowers.js ├── 66-plus-one.js ├── 690-employee-importance.js ├── 700-search-in-a-binary-search-tree.js ├── 705-design-hashset.js ├── 72-edit-distance.js ├── 721-accounts-merge.js ├── 733-flood-fill.js ├── 75-sort-colors.js ├── 771-jewels-and-stones.js ├── 787-cheapest-flight-with-k-stops.js ├── 80-remove-duplicates-from-sorted-array-ii.js ├── 804-unique-morse-code-words.js ├── 820-short-encoding-of-words.js ├── 832-flipping-an-image.js ├── 845-longest-mountain-in-array.js ├── 858-mirror-reflection.js ├── 865-smallest-subtree-with-all-the-deepest-nodes.js ├── 88-merge-sorted-array.js ├── 880-decoded-string-at-index.js ├── 886-possible-bipartition.js ├── 901-online-stock-span.js ├── 918-maximum-sum-circular-subarray.js ├── 941-valid-mountain-array.js ├── 945-minimum-increments-to-make-array-unique.js ├── 949-largest-time-for-given-digits.js ├── 953-verifying-an-alien-dictionary.js ├── 973-k-closest-points-to-origin.js ├── 977-squares-of-a-sorted-array.js ├── 98-validate-binary-search-tree.js ├── 986-interval-list-intersections.js ├── 993-cousins-in-binary-tree.js ├── 997-find-the-town-judge.js └── problems │ ├── 342-power-of-four.js │ └── 442-find-all-duplicates-in-an-array.js ├── misc ├── deserialize-1.js ├── flood-fill.js ├── least-frequent-array-element.js ├── shuffle.js └── sort2DArr.js ├── package.json ├── random └── splitStrs.js ├── whiteboarding └── leetcode │ ├── 1029-two-city-scheduling.png │ ├── 1276-number-of-burgers-with-no-waste-of-ingredients.png │ └── 820-short-encoding-of-words.png └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | *.swp 10 | 11 | pids 12 | logs 13 | results 14 | tmp 15 | coverage 16 | 17 | # API keys 18 | .env 19 | 20 | # Dependency directory 21 | node_modules 22 | bower_components 23 | 24 | # builds 25 | public 26 | 27 | # Private 28 | resources 29 | 30 | # Editors 31 | .idea 32 | *.iml 33 | 34 | # OS metadata 35 | .DS_Store 36 | Thumbs.db 37 | -------------------------------------------------------------------------------- /CTCI/1-1-is-unique.js: -------------------------------------------------------------------------------- 1 | // determine if the string has unique characters 2 | // use array and use hash table 3 | function isUnique(str) { 4 | const seen = {} 5 | for(let i=0; i val===1); 24 | 25 | let strNoWhitespace = str.replace(/\s/g, '') 26 | 27 | if(strNoWhitespace.length%2===0) { 28 | // if even number of characters, having any unbalanced 29 | // char is unacceptable 30 | return numUnbalanced.length === 0 31 | } 32 | 33 | // if odd number of characters, only allowed to have 34 | // exactly 1 unbalanced char 35 | return numUnbalanced.length === 1 36 | } -------------------------------------------------------------------------------- /CTCI/1-5-one-away.js: -------------------------------------------------------------------------------- 1 | function replaceOneChar(s1, s2) { 2 | // s1 and s2 and equal lengths 3 | let numDiff = 0; 4 | for(let i=0; i1) { 12 | return false 13 | } 14 | } 15 | return numDiff === 1 16 | } 17 | // use recursion 18 | function oneExtraCharRecursion(s1, s2) { 19 | // base case 20 | if(s1 === s2) return true 21 | if(s1==='' && s2==='') return false 22 | if(s1==='' || s2==='') return true 23 | 24 | let first1 = s1.charAt(0) 25 | let rest1 = s1.substring(1, s1.length) 26 | let first2 = s2.charAt(0) 27 | let rest2 = s2.substring(1, s2.length) 28 | 29 | if(first1 === first2) { 30 | return oneExtraChar(rest1, rest2) 31 | } 32 | // else if the firsts do not match 33 | return oneExtraChar(s1, rest2) || oneExtraChar(rest1, s2) 34 | } 35 | function oneExtraChar(s1, s2) { 36 | // find which string is longer 37 | let long = (s1.length>=s2.length) ? s1 : s2 38 | let short = (s1.length 1) { 57 | return false 58 | } 59 | 60 | // if the strings are identical, then no way they are 61 | // one away 62 | if(s1===s2) return false 63 | 64 | // if diffLen === 0, then we are looking for a 65 | // replace char case 66 | if(diffLen === 0) { 67 | return replaceOneChar(s1, s2) 68 | } 69 | 70 | // if diffLen === 1, then we are looking for an 71 | // insert/delete char case. 72 | return oneExtraChar(s1,s2) 73 | } -------------------------------------------------------------------------------- /CTCI/1-6-string-compression.js: -------------------------------------------------------------------------------- 1 | // assume string only has upper or lower case letters 2 | function stringCompression(str) { 3 | if(str.length<3) return str 4 | let count = 0 5 | let trackLtr = str.charAt(0) 6 | let compressedStr = '' 7 | for(let i=0; i=str.length) { 19 | return str 20 | } 21 | } 22 | if(count>0) { 23 | compressedStr += `${trackLtr}${count}` 24 | } 25 | // if compressed str is no shorter than the original 26 | // str, return original str 27 | return compressedStr.length >= str.length ? str : compressedStr 28 | } -------------------------------------------------------------------------------- /CTCI/16-10-living-people.js: -------------------------------------------------------------------------------- 1 | export default function maxAliveYear(people) { 2 | // O(N) 3 | let birthYears = people.map(p => p.birth) 4 | 5 | // O(N) 6 | let deathYears = people.map(p => p.death) 7 | .filter(year => typeof year !== 'undefined') 8 | let maxBirthYear = Math.max(...birthYears) 9 | let maxDeathYear = Math.max(...deathYears) 10 | 11 | let minYear = Math.min(...birthYears) 12 | let maxYear = maxDeathYear ? maxDeathYear : maxBirthYear 13 | let aliveYears = Array(maxYear-minYear+1).fill(0) 14 | let maxAliveIndex = 0 15 | 16 | deathYears.forEach((deathYear, i) => { 17 | if(typeof deathYear !== 'undefined') { 18 | // calculate the starting index of the aliveYear 19 | // to modify for death (the "DC offset") 20 | 21 | let deathIndex = deathYear - minYear 22 | 23 | while(deathIndex < aliveYears.length) { 24 | aliveYears[deathIndex] -= 1 25 | deathIndex++ 26 | } 27 | } 28 | }) 29 | 30 | birthYears.forEach((birthYear, i) => { 31 | // calculate starting index of the aliveYears to modify 32 | // for birth 33 | let birthIndex = birthYears[i] - minYear 34 | 35 | while(birthIndex < aliveYears.length) { 36 | aliveYears[birthIndex] += 1 37 | if(aliveYears[birthIndex] > aliveYears[maxAliveIndex]) { 38 | maxAliveIndex = birthIndex 39 | } 40 | birthIndex++ 41 | } 42 | }) 43 | 44 | return minYear + maxAliveIndex 45 | } -------------------------------------------------------------------------------- /CTCI/16-20-phoney-words.js: -------------------------------------------------------------------------------- 1 | // Our dictionary 2 | const num2Ltrs = { 3 | '2': ['a', 'b', 'c'], 4 | '3': ['d', 'e', 'f'], 5 | '4': ['g', 'h', 'i'], 6 | '5': ['j', 'k', 'l'], 7 | '6': ['m', 'n', 'o'], 8 | '7': ['p', 'q', 'r', 's'], 9 | '8': ['t', 'u', 'v'], 10 | '9': ['w', 'x', 'y', 'z'] 11 | } 12 | 13 | function canMakeWordFromNum(ltrArr, numArr) { 14 | // for each number in numArr, find the corresponding chars 15 | // then if one of the chars matches the head of the ltrsArr 16 | // remove the head. Otherwise, keep looking through the rest 17 | // of the nums. When we finish looking through all the nums 18 | // but there are still ltrs left in the ltrArr, return false 19 | // otherwise, return true. 20 | // O(N) where N is size of numsArr 21 | let ltrArrCpy = [...ltrArr] // make local copy. avoid side effects 22 | 23 | for(let i in numArr) { 24 | let num = numArr[i] 25 | 26 | let ltrs = num2Ltrs[num] 27 | if(!ltrs) continue 28 | 29 | // O(1) for this example because only a few ltrs 30 | // mapped to the num 31 | ltrs.forEach(ltr => { 32 | if(ltrArrCpy[0]===ltr) { 33 | ltrArrCpy.shift() 34 | return 35 | } 36 | }) 37 | } 38 | return ltrArrCpy.length === 0 39 | } 40 | 41 | function phoneyWords(nums) { 42 | let numArr = [] 43 | 44 | // We pre-process the phone number to remove dash, 1 and 0 45 | // O(1) - phone number length 14 always. Constant work 46 | for(let i=0; i { 60 | let ltrArr = word.split('').map(c => c.toLowerCase()) 61 | 62 | if(canMakeWordFromNum(ltrArr, numArr)) { 63 | res.push(word) 64 | } 65 | }) 66 | return res 67 | } -------------------------------------------------------------------------------- /CTCI/16-21-sum-swap.js: -------------------------------------------------------------------------------- 1 | function sumSwap(arrs) { 2 | const [arr1, arr2] = arrs 3 | 4 | // sort mutates arr1 and arr2 5 | arr1.sort((a,b) => a-b) 6 | arr2.sort((a,b) => a-b) 7 | 8 | const arr1Sum = arr1.reduce((acc,curr) => acc+curr, 0) 9 | const arr2Sum = arr2.reduce((acc,curr) => acc+curr, 0) 10 | 11 | let bigArr, smallArr, bigSum, smallSum 12 | 13 | if(arr1Sum < arr2Sum) { 14 | smallArr = arr1 15 | smallSum = arr1Sum 16 | bigArr = arr2 17 | bigSum = arr2Sum 18 | } else if(arr1Sum > arr2Sum) { 19 | smallArr = arr2 20 | smallSum = arr2Sum 21 | bigArr = arr1 22 | bigSum = arr1Sum 23 | } else { 24 | // edge case. We don't need to swap. arrays already have equal sum 25 | return [] 26 | } 27 | 28 | //Try swapping a big elem from bigArr with the small 29 | //elem from the smallArr until they are equal or you 30 | // run out of elem 31 | let s = 0; 32 | let b = bigArr.length-1 33 | 34 | while(s=0) { 35 | // revise the sum 36 | let bigSumRev = bigSum-bigArr[b]+smallArr[s] 37 | let smallSumRev = smallSum-smallArr[s]+bigArr[b] 38 | 39 | if(bigSumRev===smallSumRev) { 40 | // order matters! 41 | return arr1Sum a-b) 35 | 36 | while(docIds.length>0) { 37 | let second = docIds.pop() 38 | let first = docIds.pop() 39 | let pair = `${first},${second}` 40 | let currEntry = interDocs[pair] 41 | let newEntry = [num] 42 | if(currEntry) { 43 | interDocs[pair] = currEntry.concat(newEntry) 44 | } else { 45 | interDocs[pair] = newEntry 46 | } 47 | } 48 | } 49 | return interDocs 50 | } 51 | function similarityScores(interDocs, docs) { 52 | let keys = Object.keys(interDocs) 53 | let output = '' 54 | 55 | for(i in keys) { 56 | let pairIds = keys[i] 57 | let [firstId, secondId] = pairIds.split(',') 58 | let intersects = interDocs[pairIds].length 59 | let union = docs[firstId].length + docs[secondId].length - intersects 60 | let similarityScore = intersects/union 61 | output += `${pairIds} : ${similarityScore}` + '\n' 62 | } 63 | return output 64 | } 65 | function sparseSimilarity(docs) { 66 | let num2docs = createNum2Docs(docs) 67 | let interDocs = createInterDocs(num2docs) 68 | 69 | // calculate similarity score 70 | return similarityScores(interDocs, docs) 71 | } 72 | -------------------------------------------------------------------------------- /CTCI/2-1-remove-dups.js: -------------------------------------------------------------------------------- 1 | function removeDups(ll) { 2 | let curr = ll.head 3 | let prev = null 4 | let seen = {} 5 | while(curr) { 6 | if (seen[curr.val]) { 7 | prev.next = curr.next 8 | } else { 9 | prev = curr 10 | seen[curr.val] = true 11 | } 12 | curr = curr.next 13 | } 14 | return ll 15 | } 16 | 17 | function removeDupsNoBuffer(ll) { 18 | let curr = ll.head 19 | while(curr) { 20 | let runner = curr 21 | while(runner.next) { 22 | // walk runner through the nodes that come after the curr node 23 | // to check for any duplicates. remove all dups 24 | if(runner.next.val === curr.val) { 25 | runner.next = runner.next.next 26 | } else { 27 | runner = runner.next 28 | } 29 | } 30 | curr = curr.next 31 | } 32 | return ll 33 | } -------------------------------------------------------------------------------- /CTCI/4-1-route-between-nodes.js: -------------------------------------------------------------------------------- 1 | import FakeQueue from '../datastructure/FakeQueue'; 2 | 3 | // Enum 4 | const VISITED = 'VISITED'; 5 | const UNVISITED = 'UNVISITED'; 6 | const VISITING = 'VISITING'; 7 | 8 | function unvisitGraph(node) { 9 | if(node === null) return 10 | node.state = UNVISITED 11 | for(i in node.children) { 12 | let child = node.children[i] 13 | if(child.state !== UNVISITED) { 14 | unvisitGraph(child) 15 | } 16 | } 17 | } 18 | 19 | // Input: start and end are both nodes in the graph. 20 | // Output: true if there is a path between start and end 21 | function routeBetweenNodes({graph, start, end}) { 22 | // there is a path between a node and itself 23 | if(start === end) { 24 | return true 25 | } 26 | // initialize all graph's nodes as UNVISITED 27 | unvisitGraph(graph) 28 | 29 | // initialize Queue 30 | let q = new FakeQueue(); 31 | q.enqueue(start) 32 | 33 | // Step 1: traverse the graph until you find start 34 | // For every node we visit, mark it as VISITED 35 | start.state = VISITING 36 | 37 | while(!q.isEmpty()) { 38 | let u = q.dequeue() 39 | // console.log('u=',u.val) 40 | if(u.children) { 41 | for(i in u.children) { 42 | let v = u.children[i] 43 | if(v.state === UNVISITED) { 44 | if(v === end) { 45 | return true 46 | } else { 47 | v.state = VISITING 48 | q.enqueue(v) 49 | } 50 | } 51 | } 52 | u.state = VISITED 53 | } 54 | } 55 | return false 56 | } 57 | -------------------------------------------------------------------------------- /CTCI/4-2-minimal-tree.js: -------------------------------------------------------------------------------- 1 | import * as Node from '../datastructure/Node/BSTNode'; 2 | 3 | function createBST(arr) { 4 | if(arr.length === 0) return null 5 | 6 | let mid = Math.floor(arr.length/2) 7 | 8 | let leftArr = arr.slice(0,mid) 9 | let rightArr = arr.slice(mid+1, arr.length) 10 | 11 | let root = new Node(arr[mid]) 12 | 13 | root.left = createBST(leftArr) 14 | root.right = createBST(rightArr) 15 | return root 16 | } 17 | -------------------------------------------------------------------------------- /CTCI/6-1-the-heavy-pill.js: -------------------------------------------------------------------------------- 1 | function Bottle(weight=1.0) { 2 | let pillWeight = weight 3 | this.setPillWeight = (weight) => { 4 | pillWeight = weight 5 | } 6 | this.getPillWeight = () => { 7 | return pillWeight 8 | } 9 | } 10 | 11 | // generic function used by generateBottles function 12 | function getRandomInteger(min, max) { 13 | return Math.floor(Math.random() * (max - min)) + min; 14 | } 15 | function roundToNearestInt(num) { 16 | return Math.floor(num+0.5) 17 | } 18 | 19 | // Helper utility function 20 | function printBottleWeights(bottles) { 21 | let weights = bottles.map(b => { 22 | return b.getPillWeight() 23 | }) 24 | } 25 | 26 | function generateBottles(numBottles) { 27 | let bottles = Array.from(Array(numBottles).keys()) 28 | bottles = bottles.map(b => { 29 | return new Bottle() 30 | }) 31 | 32 | printBottleWeights(bottles) 33 | 34 | let rand = getRandomInteger(0,numBottles) 35 | let heavyBottle = bottles[rand] 36 | heavyBottle.setPillWeight(1.1) 37 | 38 | printBottleWeights(bottles) 39 | return bottles 40 | } 41 | 42 | 43 | function measureWeight(pills) { 44 | return pills.flatMap(x => x) 45 | .reduce((acc, curr) => acc+curr, 0) 46 | } 47 | 48 | function findHeavyBottle(bottles) { 49 | let pills = bottles.map((b, i) => { 50 | let numPills = i+1 51 | let pillWeights = Array(numPills).fill(b.getPillWeight()) 52 | return pillWeights 53 | }) 54 | 55 | let measuredWeight = measureWeight(pills) 56 | // If all bottles contain 1 gram pills, we expect total weight 57 | // to be 1*1 + 1*2 + ... + 1*20 = 1 + 2 + 3 + ... + 20 58 | // n * (n+1) / 2 59 | let numBottles = bottles.length 60 | let baseWeight = numBottles * (numBottles+1)/2 61 | let heavyIndex = (measuredWeight - baseWeight)/0.1 62 | return roundToNearestInt(heavyIndex)-1 63 | } -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | // babel.config.js 2 | module.exports = { 3 | presets: [ 4 | [ 5 | '@babel/preset-env', 6 | { 7 | targets: { 8 | node: 'current', 9 | }, 10 | }, 11 | ], 12 | ], 13 | }; -------------------------------------------------------------------------------- /datastructure/BSTUtils.js: -------------------------------------------------------------------------------- 1 | import TreeNode from "./Node/BSTNode"; 2 | 3 | /* 4 | For every BST problem, you are given a BST as an input. But to manually create the BST for test cases is time consuming. It's easy to represent a BST as an array and build the BST before each test. Write a function that creates the BST from the array 5 | 6 | Examples: 7 | [3,1,4,null,2] 8 | 3 9 | / \ 10 | 1 4 11 | \ 12 |   2 13 | 14 | [5,3,6,2,4,null,null,1] 15 | 5 16 | / \ 17 | 3 6 18 | / \ 19 | 2 4 20 | / 21 | 1 22 | 23 | [8,5,10,1,7,null,12] 24 | 8 25 | / \ 26 | 5 10 27 | / \ \ 28 | 1 7 12 29 | 30 | [1,2,3,4] 31 | 8 32 | / \ 33 | 2 3 34 | / 35 | 4 36 | 37 | [1,2,3,null,4,null,5] 38 | 8 39 | / \ 40 | 2 3 41 | \ \ 42 | 4 5 43 | 44 | [3,4,5,-7,-6,null,null,-7,null,-5,null,null,null,-4] 45 | 3 46 | / \ 47 | 4 5 48 | / \ 49 | -7 -6 50 | / / 51 | -7 -5 52 | / 53 | -4 54 | 55 | */ 56 | 57 | 58 | // TODO: bstFromArray fails for this case: [3,4,5,-7,-6,null,null,-7,null,-5,null,null,null,-4] 59 | // The -4 leaf node does not get added to the tree 60 | export function bstFromArray(arr) { 61 | function buildTree(i) { 62 | if(i>=arr.length || arr[i] === null) return null 63 | const node = new TreeNode(arr[i], null, null) 64 | node.left = buildTree(2*i+1) 65 | node.right = buildTree(2*i+2) 66 | return node 67 | } 68 | return buildTree(0) 69 | } 70 | 71 | export function arrayFromBst(root) { 72 | const ARRAY_MAX_SIZE = 1000 73 | const arr = Array(ARRAY_MAX_SIZE).fill(null) 74 | let maxInd = -1 75 | function traverse(node, arrInd) { 76 | if(node===null) return 77 | if(arrInd>maxInd) maxInd = arrInd 78 | arr[arrInd] = node.val 79 | 80 | traverse(node.left, arrInd*2+1) 81 | traverse(node.right, arrInd*2+2) 82 | } 83 | traverse(root, 0) 84 | return arr.slice(0,maxInd+1) 85 | } 86 | 87 | export function arrayFromBstBFS(root) { 88 | const arr = [] 89 | const queue = [root] 90 | while(queue.length) { 91 | const node = queue.shift() 92 | if(!node) { 93 | arr.push(null) 94 | } else { 95 | arr.push(node.val) 96 | queue.push(node.left) 97 | queue.push(node.right) 98 | } 99 | } 100 | let endIndex = arr.length - 1 101 | while(arr[endIndex] === null) endIndex-- 102 | 103 | return arr.slice(0, endIndex+1) 104 | } 105 | 106 | export function isValidBST(root) { 107 | function checkTree(node) { 108 | if(!node) { 109 | return [true, null, null] 110 | } 111 | if(!node.left && !node.right) { 112 | return [true, node.val, node.val] 113 | } 114 | 115 | const [lValid, lMin, lMax] = checkTree(node.left) 116 | if(!lValid) return [false, 0, 0] 117 | 118 | const [rValid, rMin, rMax] = checkTree(node.right) 119 | if(!rValid) return [false, 0, 0] 120 | 121 | if((lMax && lMax>=node.val) || (rMin && rMin<=node.val)) { 122 | return [false, node.val, node.val] 123 | } 124 | 125 | const newMin = lMin===null ? node.val : lMin 126 | const newMax = rMax===null ? node.val : rMax 127 | return [true, newMin, newMax] 128 | } 129 | const res = checkTree(root) 130 | return res[0] 131 | } 132 | -------------------------------------------------------------------------------- /datastructure/Dequeue.js: -------------------------------------------------------------------------------- 1 | export function Dequeue() { 2 | this.data = [] 3 | this.pushBack = (item) => { 4 | this.data.push(item) 5 | } 6 | this.pushFront = (item) => { 7 | this.data.unshift(item) 8 | } 9 | this.popFront = () => { 10 | this.data.shift() 11 | } 12 | this.popBack = () => { 13 | this.data.pop() 14 | } 15 | this.peekFront = () => { 16 | return this.data[0] 17 | } 18 | this.peekBack = () => { 19 | return this.data[this.data.length-1] 20 | } 21 | this.isEmpty = () => { 22 | return this.data.length === 0 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /datastructure/FakeQueue.js: -------------------------------------------------------------------------------- 1 | export default function FakeQueue() { 2 | let arr = [] // private variable 3 | 4 | this.enqueue = val => arr.push(val) 5 | this.dequeue = () => arr.shift() 6 | this.isEmpty = () => arr.length === 0 7 | this.getData = () => arr 8 | this.print = () => console.log(arr) 9 | } 10 | -------------------------------------------------------------------------------- /datastructure/FakeQueue_immutable.js: -------------------------------------------------------------------------------- 1 | /* FakeQueue_functional: 2 | A functional datastructure for Queue implemented using the array 3 | see repl: 4 | https://repl.it/@xiaoyunyang/queue 5 | */ 6 | 7 | function Queue(dataIn = []) { // data is an optional parameter 8 | let data = dataIn // private variable 9 | this.enqueue = item => { 10 | let newData = data.concat([item]) // concat returns a new arr 11 | return new Queue(newData) 12 | } 13 | this.dequeue = () => { 14 | let [head, ...rest] = data 15 | return [head, new Queue(rest)] 16 | } 17 | this.getData = () => data 18 | this.isEmpty = () => data.length === 0 19 | } 20 | 21 | export default Queue; -------------------------------------------------------------------------------- /datastructure/LinkedList.js: -------------------------------------------------------------------------------- 1 | import SinglyNode from './Node/SinglyNode' 2 | 3 | export default function LinkedList(arr) { 4 | this.head = arr ? createLL(arr) : null 5 | 6 | function createLL(arr) { 7 | let ll = null; 8 | for(let i=arr.length-1; i>=0; i-=1) { 9 | let newNode = new SinglyNode(arr[i]) 10 | if(!ll) { 11 | ll = newNode 12 | continue 13 | } 14 | newNode.next = ll 15 | ll = newNode 16 | } 17 | return ll 18 | } 19 | this.push = item => { 20 | // O(1) operation, push to head of LL 21 | let newNode = newSignlyNode(item) 22 | newNode.next = this.head 23 | this.head = newNode 24 | } 25 | this.pop = () => { 26 | // O(1) operation, remove head of LL 27 | // error checking. If list is empty, just return 28 | if(this.isEmpty()) return null 29 | 30 | let firstNode = this.head 31 | this.head = this.head.next 32 | } 33 | this.isEmpty = () => { 34 | return !this.head 35 | } 36 | this.printLL = () => { 37 | let curr = this.head 38 | let vals = '' 39 | while(curr) { 40 | vals += `${curr.val} -> ` 41 | curr = curr.next 42 | } 43 | return `${vals}null` 44 | } 45 | this.removeTail = () => { 46 | // If there's only one thing in the list 47 | if(!this.head || !this.head.next) { 48 | this.head = null 49 | return 50 | } 51 | 52 | const loop = (prev, curr) => { 53 | if(!curr) return 54 | let next = curr.next 55 | if(!next) { // curr is the last one 56 | prev.next = null 57 | return 58 | } 59 | // if there is a next, we haven't found 60 | // the tail yet. so 61 | loop(curr, curr.next) 62 | } 63 | 64 | loop(null, this.head) 65 | } 66 | } 67 | 68 | // Utility functions for Leetcode testing -------------------- 69 | function ListNode(val, next) { 70 | this.val = (val===undefined ? 0 : val) 71 | this.next = (next===undefined ? null : next) 72 | } 73 | 74 | export function createLL(nodeVals) { 75 | let head = null 76 | for(let i=nodeVals.length-1; i>=0; i--) { 77 | const node = new ListNode(nodeVals[i]) 78 | node.next = head 79 | head = node 80 | } 81 | return head 82 | } 83 | 84 | export function serializeLL(head) { 85 | let arr = [] 86 | let node = head 87 | while(node) { 88 | arr.push(node.val) 89 | node = node.next 90 | } 91 | return JSON.stringify(arr) 92 | } 93 | -------------------------------------------------------------------------------- /datastructure/MinHeap.js: -------------------------------------------------------------------------------- 1 | export default class MinHeap { 2 | constructor() { 3 | this.arr = Array(1).fill(null); 4 | } 5 | swap(i, j) { 6 | const temp = this.arr[i]; 7 | this.arr[i] = this.arr[j]; 8 | this.arr[j] = temp; 9 | } 10 | insert(elem) { 11 | // For min heap, when we insert a new element, we begin with adding it to the 12 | // end of the array. Then we keep swapping with its parent until the parent's 13 | // priority is smaller than both its children. This process is called bubble up. 14 | // The worst-case runtime of the algorithm is O(log N) since that's the height 15 | // of the binary tree and the most swaps we have to do. 16 | 17 | this.arr.push(elem); 18 | const insertedIndex = this.arr.length - 1; 19 | this.bubbleUp(insertedIndex); 20 | } 21 | getHeap() { 22 | return this.arr; 23 | } 24 | isEmpty() { 25 | return this.arr.length < 2; 26 | } 27 | getMin() { 28 | if (this.isEmpty()) { 29 | return undefined; 30 | } 31 | return this.arr[1]; 32 | } 33 | deleteMin() { 34 | // For min Heap, when we delete min, we fill the void with the last element in the heap. 35 | // Then we swap that element with its children until both its children 36 | // are greater in priority than the element. This process is called bubble down. 37 | // The worst-case runtime of the algorithm is O(log N), which is the height of 38 | // the binary tree. 39 | 40 | if (this.isEmpty()) { 41 | return undefined; 42 | } 43 | 44 | const parentIndex = this.arr.length - 1; 45 | this.swap(parentIndex, 1); 46 | const min = this.arr.pop(); 47 | this.bubbleDown(1); 48 | 49 | return min; 50 | } 51 | bubbleUp(insertedIndex) { 52 | const parentIndex = Math.floor(insertedIndex / 2); 53 | 54 | if (this.arr[parentIndex] === null) { 55 | return; 56 | } 57 | 58 | if (this.arr[parentIndex].priority <= this.arr[insertedIndex].priority) { 59 | return; 60 | } 61 | 62 | this.swap(parentIndex, insertedIndex); 63 | this.bubbleUp(parentIndex); 64 | } 65 | bubbleDown(parentIndex) { 66 | const leftChildIndex = parentIndex * 2; 67 | const rightChildIndex = (parentIndex * 2) + 1; 68 | 69 | const smallerChildIndex = [leftChildIndex, rightChildIndex] 70 | .filter(i => this.arr[i] !== undefined) 71 | .sort((a, b) => this.arr[a].priority - this.arr[b].priority)[0]; 72 | 73 | if (!smallerChildIndex) return; 74 | 75 | if (this.arr[parentIndex].priority < this.arr[smallerChildIndex].priority) { 76 | return; 77 | } 78 | 79 | this.swap(parentIndex, smallerChildIndex); 80 | this.bubbleDown(leftChildIndex); 81 | } 82 | } -------------------------------------------------------------------------------- /datastructure/Node/BSTNode.js: -------------------------------------------------------------------------------- 1 | export default function BSTNode(val, left, right) { 2 | this.val = (val===undefined ? 0 : val) 3 | this.left = (left===undefined ? null : left) 4 | this.right = (right===undefined ? null : right) 5 | } 6 | -------------------------------------------------------------------------------- /datastructure/Node/DoublyNode.js: -------------------------------------------------------------------------------- 1 | export default function DoublyNode(val) { 2 | this.val = val; 3 | this.prev = null 4 | this.next = null 5 | } -------------------------------------------------------------------------------- /datastructure/Node/GraphNode.js: -------------------------------------------------------------------------------- 1 | export default function Node(val) { 2 | this.val = val; 3 | this.children = null; 4 | this.state = "UNVISITED"; 5 | } 6 | -------------------------------------------------------------------------------- /datastructure/Node/SinglyNode.js: -------------------------------------------------------------------------------- 1 | export default function SinglyNode(val) { 2 | this.val = val 3 | this.next = null 4 | } 5 | -------------------------------------------------------------------------------- /datastructure/Node/TreeNode.js: -------------------------------------------------------------------------------- 1 | // general tree node 2 | export default function TreeNode(data) { 3 | this.data = data 4 | this.children = [] 5 | } -------------------------------------------------------------------------------- /datastructure/Queue.js: -------------------------------------------------------------------------------- 1 | import DoublyNode from './Node/DoublyNode' 2 | 3 | export default function Queue() { 4 | this.front = null 5 | this.back = null 6 | 7 | this.enqueue = item => { 8 | let newNode = new DoublyNode(item) 9 | if(!this.front) { 10 | this.front = newNode 11 | this.back = newNode 12 | return 13 | } 14 | this.back.next = newNode 15 | newNode.prev = this.back 16 | this.back = newNode 17 | } 18 | this.printForward = () => { 19 | let vals = '' 20 | let n = this.front 21 | while(n) { 22 | vals += `${n.val} -> ` 23 | n = n.next 24 | } 25 | return `${vals}null` 26 | } 27 | this.printBackward = () => { 28 | let vals = '' 29 | let n = this.back 30 | while(n) { 31 | vals = ` <- ${n.val}`.concat(vals) 32 | n = n.prev 33 | } 34 | return `null${vals}` 35 | } 36 | this.dequeue = () => { 37 | // remove from the front and return the removed node's val 38 | if(!this.front) return null 39 | let firstNode = this.front 40 | this.front = this.front.next 41 | // edge case: if this.front.next is null, the next line of 42 | // code will fail because can't dereference null pointer 43 | if(this.front) { 44 | this.front.prev = null 45 | } else { 46 | this.back = null 47 | } 48 | firstNode.next = null 49 | return firstNode.val 50 | } 51 | 52 | this.peek = () => { 53 | if(!this.front) return null 54 | return this.front.val 55 | } 56 | this.isEmpty = () => { 57 | return !this.front 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /datastructure/TriePrefix.js: -------------------------------------------------------------------------------- 1 | class Trie { 2 | constructor() { 3 | this.root = {} 4 | } 5 | insert(word) { 6 | let node = this.root 7 | for(let letter of word) { 8 | let nextNode = node[letter] 9 | if(!nextNode) { 10 | nextNode = {} 11 | node[letter] = nextNode 12 | } 13 | node = nextNode 14 | } 15 | node.end = true 16 | } 17 | traverse(word) { 18 | let node = this.root 19 | for(let letter of word) { 20 | let nextNode = node[letter] 21 | if(!nextNode) { 22 | return undefined 23 | } 24 | node = nextNode 25 | } 26 | return node 27 | } 28 | search(word) { 29 | const node = this.traverse(word) 30 | return node !==undefined && node.end === true 31 | } 32 | startsWith(prefix) { 33 | let node = this.traverse(prefix) 34 | return node !== undefined 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /datastructure/UnionFind.js: -------------------------------------------------------------------------------- 1 | class UnionFind { 2 | constructor(aLen) { 3 | // Note, if this data structure were implemented in TS, all these would 4 | // be private class variables 5 | this.leader = Array.from(Array(aLen).keys()) 6 | this.rank = Array(aLen).fill(0) 7 | this.numSets = aLen 8 | this.setSize = Array(aLen).fill(1) 9 | } 10 | // find the representative item (leader) of a set 11 | findSet(i) { 12 | if(this.leader[i] === i) return i 13 | const root = this.findSet(this.leader[i]) 14 | this.leader[i] = root 15 | return root 16 | } 17 | // determines if elements at indices i and j belong to the same set 18 | isSameSet(i, j) { 19 | return this.findSet(i) === this.findSet(j) 20 | } 21 | // union by rank heuristic 22 | unionSet(i, j) { 23 | if(this.isSameSet(i,j)) return 24 | 25 | const iLeader = this.findSet(i) 26 | const jLeader = this.findSet(j) 27 | 28 | if(this.rank[iLeader] > this.rank[jLeader]) { 29 | this.leader[jLeader] = iLeader 30 | this.setSize[iLeader] += this.setSize[jLeader] 31 | } else { 32 | this.leader[iLeader] = jLeader 33 | this.setSize[jLeader] += this.setSize[iLeader] 34 | } 35 | if(this.rank[iLeader] === this.rank[jLeader]) { 36 | this.rank[jLeader]++ 37 | } 38 | 39 | this.numSets-- 40 | } 41 | // returns the number of disjoint sets currently in the structure 42 | numDisjointSets() { 43 | return this.numSets 44 | } 45 | // returns the size of set that currently contains item i 46 | sizeOfSet(i) { 47 | this.setSize[this.findSet(i)] 48 | } 49 | } -------------------------------------------------------------------------------- /datastructure/test/minHeap.test.js: -------------------------------------------------------------------------------- 1 | import { sort } from "ramda"; 2 | import MinHeap from "../MinHeap"; 3 | 4 | // get random int between 0 and max-1 5 | const getRandomInt = (max) => 6 | Math.floor(Math.random() * Math.floor(max)); 7 | 8 | // Fisher-Yates Shuffle 9 | const shuffle = (arr) => { 10 | const a = [...arr]; 11 | 12 | function swap(i, j) { 13 | const temp = a[i]; 14 | a[i] = a[j]; 15 | a[j] = temp; 16 | } 17 | 18 | let j = 0; 19 | for (let i = 0; i < arr.length - 2; i += 1) { 20 | j = getRandomInt(i); 21 | swap(i, j); 22 | } 23 | return a; 24 | }; 25 | 26 | const verifyInvariant = (heapArr, currInd) => { 27 | if (heapArr.length < 2) return true; 28 | 29 | const curr = heapArr[currInd]; 30 | const leftChildInd = currInd * 2; 31 | const rightChildInd = (currInd * 2) + 1; 32 | const leftChild = heapArr[leftChildInd]; 33 | const rightChild = heapArr[rightChildInd]; 34 | 35 | if (!leftChild && !rightChild) return true; 36 | let leftHeapValid = true; 37 | let rightHeapValid = true; 38 | if (leftChild) { 39 | if (leftChild.priority < curr.priority) return false; 40 | leftHeapValid = verifyInvariant(heapArr, leftChildInd); 41 | } 42 | if (rightChild) { 43 | if (rightChild.priority < curr.priority) return false; 44 | rightHeapValid = verifyInvariant(heapArr, leftChildInd); 45 | } 46 | 47 | return leftHeapValid && rightHeapValid; 48 | }; 49 | 50 | describe("verifyInvariant", () => { 51 | it("should verify min heap properties correctly for not a min heap", () => { 52 | const heap = [ 53 | { val: "a", priority: 0 }, 54 | { val: "a", priority: 12 }, 55 | { val: "a", priority: 13 }, 56 | { val: "a", priority: 1 } 57 | ]; 58 | expect(verifyInvariant(heap, 1)).toBe(false); 59 | }); 60 | it("should verify min heap properties correctly for a min heap", () => { 61 | const heap = [ 62 | { val: "a", priority: 0 }, 63 | { val: "a", priority: 1 }, 64 | { val: "a", priority: 2 }, 65 | { val: "a", priority: 3 } 66 | ]; 67 | expect(verifyInvariant(heap, 1)).toBe(true); 68 | }); 69 | }); 70 | 71 | describe("MinHeap", () => { 72 | describe("Insert and remove randomized testing", () => { 73 | 74 | const elems = [ 75 | { val: "a", priority: 0 }, 76 | { val: "a1", priority: 0 }, 77 | { val: "b", priority: 1 }, 78 | { val: "c", priority: 2 }, 79 | { val: "c1", priority: 2 }, 80 | { val: "d", priority: 3 }, 81 | { val: "e", priority: 4 }, 82 | { val: "f", priority: 5 } 83 | ]; 84 | 85 | // create 1000 tests using shuffled elems 86 | const testCases = Array(100).fill(shuffle(elems)); 87 | 88 | for(let i=0; i { 96 | h.insert(elem); 97 | expect(verifyInvariant(h.getHeap(), 1)).toBe(true); 98 | }); 99 | 100 | test.each( 101 | testCase 102 | )(`test #${i} maintain min heap invariant after removing element`, () => { 103 | const min = h.deleteMin(); 104 | mins = mins.concat(min); 105 | expect(verifyInvariant(h.getHeap(), 1)).toBe(true); 106 | }); 107 | 108 | it(`test #${i} should remove min priority element from heap`, () => { 109 | const prios = mins.map(min => (min && min.priority)); 110 | const sortBy = (a, b) => a - b; 111 | expect(prios).toEqual(sort(sortBy, prios)); 112 | }); 113 | } 114 | }); 115 | 116 | describe("Edge Cases", () => { 117 | let h; 118 | let mins; 119 | 120 | beforeEach(() => { 121 | h = new MinHeap(); 122 | mins = []; 123 | }); 124 | it("should remove in the same order as insertion for elements of same priority", () => { 125 | const elems = [ 126 | { val: "a", priority: 0 }, 127 | { val: "a1", priority: 0 }, 128 | { val: "a2", priority: 0 } 129 | ]; 130 | 131 | elems.forEach(elem => h.insert(elem)); 132 | for (let i = 0; i < elems.length; i += 1) { 133 | mins = mins.concat(h.deleteMin()); 134 | } 135 | expect(mins).toEqual(elems); 136 | }); 137 | it("should return undefined if deleting min from empty heap", () => { 138 | const min = h.deleteMin(); 139 | expect(min).toBe(undefined); 140 | }); 141 | it("should return undefined when getMin from empty heap", () => { 142 | expect(h.getMin()).toBe(undefined); 143 | }); 144 | }); 145 | }); -------------------------------------------------------------------------------- /leetcode/1-two-sum.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @param {number} target 4 | * @return {number[]} 5 | */ 6 | var twoSum = function(nums, target) { 7 | let compMap = {} 8 | for(let i=0; i= preorder.length) return null 17 | const val = preorder[i] 18 | let root = null 19 | if(val<=min || val>=max) return null 20 | 21 | root = new TreeNode(val, null, null) 22 | i++ 23 | root.left = build(min, val) 24 | root.right = build(val, max) 25 | return root 26 | } 27 | return build(-Infinity, Infinity) 28 | }; 29 | -------------------------------------------------------------------------------- /leetcode/1010-pairs-of-songs-with-durations-divisible-by-60.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} time 3 | * @return {number} 4 | */ 5 | var numPairsDivisibleBy60 = function(time) { 6 | const compCounts = Array(60).fill(0) 7 | let res = 0 8 | for(let i=0; i { 2 | let sum = 0 3 | for(let i=0; i !node.left && !node.right 10 | 11 | /** 12 | * @param {TreeNode} root 13 | * @return {number} 14 | */ 15 | var sumRootToLeaf = function(root) { 16 | // pre-order traversal 17 | function traverse(node, bin) { 18 | if(node===null) return 0 19 | if(isLeaf(node)) { 20 | return bin2Num(bin+node.val) 21 | } 22 | 23 | return traverse(node.left, bin+node.val) + 24 | traverse(node.right, bin+node.val) 25 | } 26 | return traverse(root, "") 27 | }; 28 | -------------------------------------------------------------------------------- /leetcode/1026-maximum-difference-between-node-and-ancestor.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | /** 10 | * @param {TreeNode} root 11 | * @return {number} 12 | */ 13 | var maxAncestorDiff = function(root) { 14 | function getMaxDiffOfTree(node, max, min) { 15 | if(!node) return max-min 16 | const newMax = Math.max(node.val, max) 17 | const newMin = Math.min(node.val, min) 18 | const leftDiff = getMaxDiffOfTree(node.left, newMax, newMin) 19 | const rightDiff = getMaxDiffOfTree(node.right, newMax, newMin) 20 | return Math.max(leftDiff, rightDiff) 21 | } 22 | 23 | return getMaxDiffOfTree(root, -Infinity, Infinity) 24 | }; 25 | -------------------------------------------------------------------------------- /leetcode/1029-two-city-scheduling.js: -------------------------------------------------------------------------------- 1 | const twoCitySchedCost = (costs) => { 2 | 3 | const costDiffs = costs.map((cost, i) => { 4 | const [aCost,bCost] = cost 5 | return [aCost - bCost, i] 6 | }).sort((a,b) => a[0] - b[0]); 7 | 8 | let a = 0 9 | let b = costDiffs.length-1 10 | let total = 0; 11 | while(a { 15 | let top = (i-1<0) ? 0 : matrix[i-1][j] 16 | let left = (j-1<0) ? 0 : matrix[i][j-1] 17 | return Math.max(top, left) 18 | } 19 | const getDiag = (i,j) => { 20 | return (i-1 >=0 && j-1 >= 0) ? matrix[i-1][j-1] : 0 21 | } 22 | const getSelf = (i,j) => { 23 | return A[i] === B[j] ? 1 : 0 24 | } 25 | 26 | for(let i=0; i { 3 | let newDir = dir 4 | let newX = x 5 | let newY = y 6 | switch(move) { 7 | case "G": 8 | if(dir === "N") newY++ 9 | else if(dir === "W") newX-- 10 | else if(dir === "S") newY-- 11 | else if(dir === "E") newX++ 12 | break 13 | case "L": 14 | if(dir === "N") newDir = "W" 15 | else if(dir === "W") newDir = "S" 16 | else if(dir === "S") newDir = "E" 17 | else if(dir === "E") newDir = "N" 18 | break 19 | case "R": 20 | if(dir === "N") newDir = "E" 21 | else if(dir === "W") newDir = "N" 22 | else if(dir === "S") newDir = "W" 23 | else if(dir === "E") newDir = "S" 24 | break 25 | } 26 | return { 27 | dir: newDir, 28 | x: newX, 29 | y: newY 30 | } 31 | } 32 | 33 | /** 34 | * @param {string} instructions 35 | * @return {boolean} 36 | */ 37 | var isRobotBounded = function(instructions) { 38 | // Approach: loop through the instructions and calculate the (x,y) 39 | // after executing the move. Then return true if final (x,y) is (0,0). 40 | // false otherwise 41 | 42 | // initial conditions 43 | let x = 0 44 | let y = 0 45 | let dir = "N" 46 | for(let i=0; i1) return -1 22 | return Math.max(leftHeight, rightHeight) 23 | } 24 | const h = getHeight(root, 0) 25 | return (h!==-1) 26 | }; 27 | -------------------------------------------------------------------------------- /leetcode/1138-alphabet-board-path.js: -------------------------------------------------------------------------------- 1 | // map letter to board position 2 | const board = ["abcde", "fghij", "klmno", "pqrst", "uvwxy", "z"] 3 | const h = {} 4 | for(let i=0; i { 11 | // edge case: need to consider if start or end letter is z 12 | 13 | const vertDist = start[0]-end[0] 14 | const vertMove = (vertDist>0) ? "U" : "D" 15 | 16 | const horizDist = start[1]-end[1] 17 | const horizMove = (horizDist>0) ? "L" : "R" 18 | const horiz = Array(Math.abs(horizDist)).fill(horizMove).reduce((acc, m) => acc+m, "") 19 | const vert = Array(Math.abs(vertDist)).fill(vertMove).reduce((acc, m) => acc+m, "") 20 | return isEndZ ? horiz+vert+"!" : vert+horiz+"!" 21 | } 22 | /** 23 | * @param {string} target 24 | * @return {string} 25 | */ 26 | var alphabetBoardPath = function(target) { 27 | if(target.length === 0) return "" 28 | // approach: graph search to find the next letter. Then calculate path 29 | 30 | let start = [0,0] 31 | let end 32 | let res = "" 33 | for(let i=0; imax) { 15 | max = price 16 | } 17 | profit = Math.max(profit, max-min) 18 | } 19 | return profit 20 | }; 21 | -------------------------------------------------------------------------------- /leetcode/1217-minimum-cost-to-move-chips-to-the-same-position.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} position 3 | * @return {number} 4 | */ 5 | var minCostToMoveChips = function(position) { 6 | let cost = 0 7 | let cost2 = 0 8 | for(let i=0; i=last) { 11 | res+=price-last 12 | } 13 | last = price 14 | } 15 | return res 16 | }; 17 | -------------------------------------------------------------------------------- /leetcode/1232-check-if-it-is-a-straight-line.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[][]} coordinates 3 | * @return {boolean} 4 | */ 5 | var checkStraightLine = function(coordinates) { 6 | let first = coordinates[0] 7 | let second = coordinates[1] 8 | const getSlope = (first, second) => (second[1]-first[1]) / (second[0]-first[0]) 9 | const m = getSlope(first, second) 10 | let mTest 11 | first = second 12 | for (let i=2; i (c >= "A" && c <= "Z") || (c >= "a" && c <= "z") || (c >= "0" && c <= "9") 7 | let i = 0 8 | let j = s.length-1 9 | while(i { 2 | const jumbos = tomatoSlices / 2 - cheeseSlices 3 | const smalls = cheeseSlices - jumbos 4 | if (jumbos !== Math.floor(jumbos) || jumbos < 0 || smalls < 0) return [] 5 | 6 | return [jumbos, smalls] 7 | }; -------------------------------------------------------------------------------- /leetcode/1277-count-square-submatrices-with-all-ones.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[][]} matrix 3 | * @return {number} 4 | */ 5 | var countSquares = function(matrix) { 6 | const numRows = matrix.length 7 | const rowLen = matrix[0].length 8 | const sumMatrix = Array(numRows) 9 | for(let i=0; i { 14 | const top = (i-1<0) ? 0 : sumMatrix[i-1][j] 15 | const left = (j-1<0) ? 0 : sumMatrix[i][j-1] 16 | const diag = (i-1>=0 && j-1>=0) ? sumMatrix[i-1][j-1] : 0 17 | return Math.min(top, left, diag) 18 | } 19 | let res = 0 20 | for(let i=0; i>1) 14 | const sum = nums.reduce((acc,num) => acc+Math.ceil(num/mid),0) 15 | if(sum>threshold) { 16 | left = mid + 1 17 | } else { 18 | right = mid 19 | } 20 | } 21 | 22 | return right 23 | }; 24 | -------------------------------------------------------------------------------- /leetcode/129-sum-root-to-leaf-numbers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val) { 4 | * this.val = val; 5 | * this.left = this.right = null; 6 | * } 7 | */ 8 | /** 9 | * @param {TreeNode} root 10 | * @return {number} 11 | */ 12 | var sumNumbers = function(root) { 13 | function isLeaf(node) { 14 | return node.left === null && node.right === null 15 | } 16 | function traverse(node, numAsStr) { 17 | if(node === null) return 0 18 | if(isLeaf(node)) { 19 | return Number(`${numAsStr}${node.val}`) 20 | } 21 | 22 | return traverse(node.left, `${numAsStr}${node.val}`) + 23 | traverse(node.right, `${numAsStr}${node.val}`) 24 | } 25 | return traverse(root, "") 26 | }; -------------------------------------------------------------------------------- /leetcode/1290-convert-binary-number-in-a-linked-list-to-integer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for singly-linked list. 3 | * function ListNode(val, next) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.next = (next===undefined ? null : next) 6 | * } 7 | */ 8 | /** 9 | * @param {ListNode} head 10 | * @return {number} 11 | */ 12 | var getDecimalValue = function(head) { 13 | let numAsStr = "" 14 | let node = head 15 | while(node) { 16 | numAsStr = `${numAsStr}${node.val}` 17 | node = node.next 18 | } 19 | 20 | return Number.parseInt(numAsStr, 2) 21 | }; 22 | -------------------------------------------------------------------------------- /leetcode/1291-sequential-digit.js: -------------------------------------------------------------------------------- 1 | const getNewNum = (start, len) => { 2 | let numStr = "" 3 | for(let i=start; i<=9; i++) { 4 | if(numStr.length=low && newNum<=high) res.push(newNum) 25 | } 26 | } 27 | return res 28 | }; 29 | -------------------------------------------------------------------------------- /leetcode/130-surrounded-regions.js: -------------------------------------------------------------------------------- 1 | const encodeCoord = (i,j) => `${i},${j}` 2 | const decodeCoord = (s) => s.split(',').map(s => parseInt(s, 10)) 3 | 4 | /** 5 | * @param {character[][]} board 6 | * @return {void} Do not return anything, modify board in-place instead. 7 | */ 8 | var solve = function(board) { 9 | if(board.length===0) return 10 | let h = board.length 11 | let w = board[0].length 12 | let doNotTurn = new Set() 13 | 14 | // Start with the outer edges 15 | // if it is an O, identify all connected O's on the 16 | // board and add them to a "do not turn to X" list 17 | let explored = new Set() 18 | let queue = [] 19 | let edges = [] 20 | for(let i=0; i0) { 39 | let encoded = queue.shift() 40 | let [i,j] = decodeCoord(encoded) 41 | let neighbors = [ 42 | [i+1, j], [i, j+1], [i-1, j], [i, j-1] 43 | ].filter(coord => 44 | coord[0]>=0 && coord[0]=0 && coord[1] { 10 | if(node===null) return 11 | traverse(node.left, arr) 12 | arr.push(node.val) 13 | traverse(node.right, arr) 14 | } 15 | /** 16 | * @param {TreeNode} root1 17 | * @param {TreeNode} root2 18 | * @return {number[]} 19 | */ 20 | var getAllElements = function(root1, root2) { 21 | // approach: do a in-order traversal of both trees to generate two 22 | // arrays sorted in asc order. Then merge these two arrays (similar to 23 | // the merge step of merge-sort) 24 | let arr1 = [] 25 | let arr2 = [] 26 | traverse(root1, arr1) 27 | traverse(root2, arr2) 28 | let i = 0 29 | let j = 0 30 | const res = [] 31 | while(i=arr1.length) { 33 | res.push(arr2[j]) 34 | j++ 35 | } else if(j>=arr2.length) { 36 | res.push(arr1[i]) 37 | i++ 38 | } else if(arr1[i]<=arr2[j]) { 39 | res.push(arr1[i]) 40 | i++ 41 | } else { 42 | res.push(arr2[j]) 43 | j++ 44 | } 45 | } 46 | return res 47 | }; 48 | 49 | -------------------------------------------------------------------------------- /leetcode/1306-jump-game-iii.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} arr 3 | * @param {number} start 4 | * @return {boolean} 5 | */ 6 | var canReach = function(arr, start) { 7 | const explored = new Set() 8 | 9 | function dfs(i) { 10 | if(i<0 || i>=arr.length) return false 11 | if(arr[i]===0) return true 12 | if(explored.has(i)) return false 13 | explored.add(i) 14 | return dfs(i-arr[i]) || dfs(i+arr[i]) 15 | } 16 | return dfs(start) 17 | }; 18 | 19 | /** 20 | * @param {number[]} arr 21 | * @param {number} start 22 | * @return {boolean} 23 | */ 24 | var canReachIter = function(arr, start) { 25 | let ind 26 | let elem 27 | const s = [start] 28 | const explored = new Set() 29 | 30 | while(s.length>0) { 31 | ind = s.pop() 32 | if(ind<0 || ind>=arr.length) continue 33 | elem = arr[ind] 34 | if(elem === 0) return true 35 | if(explored.has(ind)) continue 36 | explored.add(ind) 37 | s.push(ind-elem) 38 | s.push(ind+elem) 39 | } 40 | return false 41 | }; 42 | -------------------------------------------------------------------------------- /leetcode/135-candy.js: -------------------------------------------------------------------------------- 1 | var candy = function(ratings) { 2 | let l2r = Array(ratings.length).fill(1) 3 | let r2l = Array(ratings.length).fill(1) 4 | 5 | for(let i=1; iratings[l-1]) { 10 | l2r[l] = l2r[l-1]+1 11 | } 12 | if(ratings[r]>ratings[r+1]) { 13 | r2l[r] = r2l[r+1]+1 14 | } 15 | } 16 | // combine l2r and r2l 17 | let sum = 0 18 | for(let i=0;i acc ^ curr, 0); 7 | }; 8 | 9 | /** 10 | * @param {number[]} nums 11 | * @return {number} 12 | */ 13 | var singleNumberSet = function(nums) { 14 | const s = new Set() 15 | for(let num of nums) { 16 | if(s.has(num)) { 17 | s.delete(num) 18 | } else { 19 | s.add(num) 20 | } 21 | } 22 | return Array.from(s)[0] 23 | }; -------------------------------------------------------------------------------- /leetcode/137-single-number-ii.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {number} 4 | */ 5 | var singleNumber = function(nums) { 6 | // twi counters 7 | let lo = 0 8 | let hi = 0 9 | for(let num of nums) { 10 | lo = (~hi) & (lo^num) 11 | hi = (~lo) & (hi^num) 12 | } 13 | return lo 14 | }; 15 | -------------------------------------------------------------------------------- /leetcode/1379-find-a-corresponding-node-of-a-binary-tree-in-a-clone-of-that-tree.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val) { 4 | * this.val = val; 5 | * this.left = this.right = null; 6 | * } 7 | */ 8 | /** 9 | * @param {TreeNode} original 10 | * @param {TreeNode} cloned 11 | * @param {TreeNode} target 12 | * @return {TreeNode} 13 | */ 14 | 15 | var getTargetCopy = function(original, cloned, target) { 16 | let res = null 17 | function traverse(node) { 18 | if(!node) return 19 | if(node.val===target.val) { 20 | res = node 21 | return 22 | } 23 | traverse(node.left) 24 | traverse(node.right) 25 | } 26 | traverse(cloned) 27 | return res 28 | }; 29 | -------------------------------------------------------------------------------- /leetcode/1386-cinema-seat-allocation.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} n 3 | * @param {number[][]} reservedSeats 4 | * @return {number} 5 | */ 6 | var maxNumberOfFamilies = function(n, reservedSeats) { 7 | const newRow = 0x00 8 | const rowToBits = {} 9 | for(let r of reservedSeats) { 10 | if(r[1]>1 && r[1]<10) { 11 | rowToBits[r[0]] = (rowToBits[r[0]] || newRow) | (1 << (9-r[1])) 12 | } 13 | } 14 | 15 | let famEvicted = 0 16 | const masks = [ 17 | 0x0f, // 00 0011 11 18 | 0xc3, // 11 0000 11 19 | 0xf0 // 11 1100 00 20 | ] 21 | const rowsOfReservedSeats = Object.keys(rowToBits) 22 | for(let row of rowsOfReservedSeats) { 23 | rowBits = rowToBits[row] 24 | if((rowBits | masks[0]) === masks[0] && (rowBits | masks[2]) === masks[2]) { 25 | continue // can seat both families 26 | } 27 | const canSeatOneFam = masks.some(mask => (rowBits | mask) === mask) 28 | famEvicted += canSeatOneFam ? 1 : 2 29 | } 30 | return n * 2 - famEvicted 31 | }; -------------------------------------------------------------------------------- /leetcode/146-lru-cache.js: -------------------------------------------------------------------------------- 1 | class LRUCache { 2 | constructor(capacity) { 3 | this.capacity = capacity; 4 | 5 | // Doubly linked list: maintains an ordered list and we can remove an element 6 | // anywhere in the list in O(1) time and add to end of list in O(1) time 7 | this.head = {}; 8 | this.tail = {}; 9 | this.head.next = this.tail; // head points to the lru element 10 | this.tail.prev = this.head; 11 | 12 | // type: key -> { key, value, prev, next } 13 | this.cache = new Map(); 14 | } 15 | 16 | addAtTail(key, value) { 17 | const prevLastNode = this.tail.prev; 18 | const newNode = { 19 | key, 20 | value, 21 | }; 22 | 23 | // make old last node and 24 | // new last node point to each other 25 | newNode.prev = prevLastNode; 26 | prevLastNode.next = newNode; 27 | 28 | // make new last node the tail 29 | newNode.next = this.tail; 30 | this.tail.prev = newNode; 31 | 32 | // add new tail node to cache 33 | this.cache.set(key, newNode); 34 | } 35 | delete(key) { 36 | const node = this.cache.get(key); 37 | 38 | // update all the references 39 | node.prev.next = node.next; 40 | node.next.prev = node.prev; 41 | 42 | // delete from the cache 43 | this.cache.delete(key); 44 | } 45 | deleteFromHead() { 46 | const headNodeKey = this.head.next.key; 47 | this.delete(headNodeKey); 48 | } 49 | put(key, value) { 50 | // corner case: already in the cache 51 | if (this.cache.has(key)) { 52 | // move the node to tail 53 | this.delete(key); 54 | this.addAtTail(key, value); 55 | return; 56 | } 57 | 58 | // check if we have space in the cache 59 | const hasSpace = this.cache.size < this.capacity; 60 | 61 | if (hasSpace) { 62 | // If so, just insert 63 | this.addAtTail(key, value); 64 | return; 65 | } 66 | 67 | // otherwise, evict lru then add the new 68 | this.deleteFromHead(); 69 | this.addAtTail(key, value); 70 | } 71 | 72 | get(key) { 73 | if (!this.cache.has(key)) return -1; 74 | 75 | const { value } = this.cache.get(key); 76 | this.delete(key); 77 | this.addAtTail(key, value); 78 | 79 | return value; 80 | } 81 | } 82 | /** 83 | * Your LRUCache object will be instantiated and called as such: 84 | * var obj = new LRUCache(capacity) 85 | * var param_1 = obj.get(key) 86 | * obj.put(key,value) 87 | */ 88 | -------------------------------------------------------------------------------- /leetcode/1464-maximum-product-of-two-elements-in-an-array.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {number} 4 | */ 5 | var maxProduct = function(nums) { 6 | const sortedNums = nums.map(a=>a-1).sort((a,b) => b-a) 7 | return sortedNums[0]*sortedNums[1] 8 | }; -------------------------------------------------------------------------------- /leetcode/1465-maximum-area-a-piece-of-cake.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} h 3 | * @param {number} w 4 | * @param {number[]} horizontalCuts 5 | * @param {number[]} verticalCuts 6 | * @return {number} 7 | */ 8 | var maxArea = function(h, w, horizontalCuts, verticalCuts) { 9 | horizontalCuts.sort((a,b) => a-b).push(h) 10 | verticalCuts.sort((a,b) => a-b).push(w) 11 | let last = 0 12 | let maxH = 0 13 | for(let i=0; i a-b) 8 | const median = arr[(arr.length-1)>>1] 9 | let i=0 10 | let j=arr.length-1 11 | let res = [] 12 | while(res.length < k) { 13 | const left = arr[i] 14 | const right = arr[j] 15 | if(median-left > right-median) { 16 | res.push(left) 17 | i++ 18 | } else { 19 | // median-left <= right-median 20 | res.push(right) 21 | j-- 22 | } 23 | } 24 | return res 25 | }; 26 | -------------------------------------------------------------------------------- /leetcode/1472-design-browser-history.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} homepage 3 | */ 4 | var BrowserHistory = function(homepage) { 5 | this.history = [homepage] 6 | this.curr = 0 7 | }; 8 | 9 | /** 10 | * @param {string} url 11 | * @return {void} 12 | */ 13 | BrowserHistory.prototype.visit = function(url) { 14 | while(this.history.length>this.curr+1) { 15 | this.history.pop() 16 | } 17 | this.history.push(url) 18 | this.curr++ 19 | }; 20 | 21 | /** 22 | * @param {number} steps 23 | * @return {string} 24 | */ 25 | BrowserHistory.prototype.back = function(steps) { 26 | let back = 0 27 | while(back0) { 28 | this.curr-- 29 | back++ 30 | } 31 | 32 | return this.history[this.curr] 33 | 34 | }; 35 | 36 | /** 37 | * @param {number} steps 38 | * @return {string} 39 | */ 40 | BrowserHistory.prototype.forward = function(steps) { 41 | let forward = 0 42 | while(forward +d) 12 | const ds2 = version2.split(".").map((d,i) => +d) 13 | const maxDigits = Math.max(ds1.length, ds2.length) 14 | 15 | const ws1 = ds1.reduce((acc, curr, i) => { 16 | const weight = Math.pow(10, maxDigits-i) 17 | return acc + curr*weight 18 | }, 0) 19 | const ws2 = ds2.reduce((acc, curr, i) => { 20 | const weight = Math.pow(10, maxDigits-i) 21 | return acc + curr*weight 22 | }, 0) 23 | 24 | // edge case: ignore leading zeros 25 | // if same return 0 26 | if(ws1===ws2) return 0 27 | return ws1>ws2 ? 1 : -1 28 | }; 29 | -------------------------------------------------------------------------------- /leetcode/169-majority-element.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {number} 4 | */ 5 | var majorityElement = function(nums) { 6 | let maj = nums[0] 7 | let count = 1 8 | for(let i=1; i0) { 35 | let nextNode = new ListNode(carry) 36 | if(!tail) { 37 | res = nextNode 38 | } else { 39 | tail.next = nextNode 40 | } 41 | } 42 | return res 43 | }; 44 | -------------------------------------------------------------------------------- /leetcode/20-valid-parentheses.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} s 3 | * @return {boolean} 4 | */ 5 | var isValid = function(s) { 6 | const openCloseMap = { 7 | "[": "]", 8 | "(": ")", 9 | "{": "}" 10 | } 11 | const opens = new Set(Object.keys(openCloseMap)) 12 | const stack = [] 13 | for(let i=0; i 7 | String(number).split('').reduce((acc, curr) => { 8 | const digitSq = Math.pow(Number(curr), 2) 9 | return acc+digitSq 10 | }, 0) 11 | 12 | const seen = new Set() 13 | const getHappy = (number) => { 14 | if(number === 1) return true 15 | if(seen.has(number)) return false 16 | seen.add(number) 17 | return getHappy(getSumOfSquares(number)) 18 | } 19 | 20 | return getHappy(n) 21 | }; 22 | -------------------------------------------------------------------------------- /leetcode/207-course-schedule.js: -------------------------------------------------------------------------------- 1 | function isCycle(graph, u, seen, recStack) { 2 | if(recStack[u]) return true 3 | if(seen[u]) return false // For optimization, not required for correct solution 4 | seen[u] = true 5 | recStack[u] = true 6 | const vs = graph[u] || [] 7 | for(let v of vs) { 8 | if(isCycle(graph, v, seen, recStack)) { 9 | return true 10 | } 11 | } 12 | recStack[u] = false 13 | return false 14 | } 15 | /** 16 | * @param {number} numCourses 17 | * @param {number[][]} prerequisites 18 | * @return {boolean} 19 | */ 20 | var canFinish = function(numCourses, prerequisites) { 21 | const graph = {} 22 | // build the graph 23 | for(let prereq of prerequisites) { 24 | const [v,u] = prereq 25 | graph[u] = graph[u] || [] 26 | graph[u].push(v) 27 | } 28 | 29 | // detect if graph has cycles 30 | const seen = {} 31 | const recStack = {} 32 | for(let i=0; i=0; i--) { 4 | const node = new ListNode(nodeVals[i]) 5 | node.next = head 6 | head = node 7 | } 8 | return head 9 | } 10 | 11 | /** 12 | * Definition for singly-linked list. 13 | * function ListNode(val, next) { 14 | * this.val = (val===undefined ? 0 : val) 15 | * this.next = (next===undefined ? null : next) 16 | * } 17 | */ 18 | /** 19 | * @param {ListNode} l1 20 | * @param {ListNode} l2 21 | * @return {ListNode} 22 | */ 23 | var mergeTwoLists = function(l1, l2) { 24 | let merged = [] 25 | let n1 = l1 26 | let n2 = l2 27 | while(n1 || n2) { 28 | if(!n2 || (n1 && n2 && n1.val<=n2.val)) { 29 | merged.push(n1.val) 30 | n1 = n1.next 31 | } else { 32 | merged.push(n2.val) 33 | n2 = n2.next 34 | } 35 | } 36 | // post-processing step: create the LL from merged List 37 | return createLL(merged) 38 | }; 39 | -------------------------------------------------------------------------------- /leetcode/216-combination-sum-iii.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} k 3 | * @param {number} n 4 | * @return {number[][]} 5 | */ 6 | var combinationSum3 = function(k, n) { 7 | // approach: backtracking 8 | const res = [] 9 | 10 | const search = (num, sum, nums, kLeft) => { 11 | if(sum===n && kLeft===0) { 12 | res.push(nums) 13 | return 14 | } 15 | if(num>9 || num>n || sum>n || k<0) return 16 | search(num+1, sum+num, nums.concat(num), kLeft-1) 17 | search(num+1, sum, nums, kLeft) 18 | } 19 | search(1, 0, [], k) 20 | return res 21 | }; 22 | -------------------------------------------------------------------------------- /leetcode/220-contains-duplicate-iii.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @param {number} k 4 | * @param {number} t 5 | * @return {boolean} 6 | */ 7 | var containsNearbyAlmostDuplicate = function(nums, k, t) { 8 | const sorted = nums.map((num,i) => ({num,i})).sort((a,b) => a.num-b.num) 9 | let i=0 10 | let j=1 11 | while(ik) j++ 16 | else if (diff>t) i++ 17 | if(i===j) j++ 18 | } 19 | return false 20 | }; -------------------------------------------------------------------------------- /leetcode/226-invert-binary-tree.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val) { 4 | * this.val = val; 5 | * this.left = this.right = null; 6 | * } 7 | */ 8 | /** 9 | * @param {TreeNode} root 10 | * @return {TreeNode} 11 | */ 12 | var invertTree = function(root) { 13 | function traverse(node) { 14 | if(node===null) return 15 | // swap left and right node of current 16 | const temp = node.left 17 | node.left = node.right 18 | node.right = temp 19 | traverse(node.left) 20 | traverse(node.right) 21 | } 22 | traverse(root) 23 | return root 24 | }; -------------------------------------------------------------------------------- /leetcode/230-kth-smallest-element-in-a-bst.js: -------------------------------------------------------------------------------- 1 | var kthSmallest = function(root, k) { 2 | // in-order traversal 3 | function traverse(node, arr) { 4 | if(!node) return 5 | 6 | let numLeft = traverse(node.left, arr) 7 | if(arr.length===k) return 8 | arr.push(node.val) 9 | let numRight = traverse(node.right, arr) 10 | } 11 | let arr = [] 12 | traverse(root, arr) 13 | return arr[arr.length-1] 14 | }; -------------------------------------------------------------------------------- /leetcode/231-power-of-two.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} n 3 | * @return {boolean} 4 | */ 5 | var isPowerOfTwo = function(n) { 6 | if(n<1) return false 7 | let num = n 8 | while(num>1) { 9 | if(num%2!==0) return false 10 | num = num>>1 11 | } 12 | return true 13 | }; 14 | -------------------------------------------------------------------------------- /leetcode/237-delete-node-in-a-linked-list.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for singly-linked list. 3 | * function ListNode(val) { 4 | * this.val = val; 5 | * this.next = null; 6 | * } 7 | */ 8 | /** 9 | * @param {ListNode} node 10 | * @return {void} Do not return anything, modify node in-place instead. 11 | */ 12 | var deleteNode = function(node) { 13 | const next = node.next 14 | if(next===null) return 15 | node.val = next.val 16 | node.next = next.next 17 | }; 18 | -------------------------------------------------------------------------------- /leetcode/239-sliding-window-maximum.js: -------------------------------------------------------------------------------- 1 | import Dequeue from "../datastructure/Dequeue"; 2 | 3 | /** 4 | * @param {number[]} nums 5 | * @param {number} k 6 | * @return {number[]} 7 | */ 8 | var maxSlidingWindow = function(nums, k) { 9 | const res = [] 10 | const q = new Dequeue() 11 | for(let i=0; i=k-1) { 26 | res.push(nums[q.peekFront()]) 27 | } 28 | } 29 | 30 | return res 31 | }; 32 | -------------------------------------------------------------------------------- /leetcode/24-swap-nodes-in-pairs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for singly-linked list. 3 | * function ListNode(val, next) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.next = (next===undefined ? null : next) 6 | * } 7 | */ 8 | /** 9 | * @param {ListNode} head 10 | * @return {ListNode} 11 | */ 12 | var swapPairs = function(head) { 13 | let curr = head 14 | let next 15 | let prev = head 16 | if(head && head.next) { 17 | head = head.next 18 | } 19 | while(curr) { 20 | next = curr.next 21 | if(!next) return head 22 | prev.next = next 23 | curr.next = next.next 24 | next.next = curr 25 | 26 | prev = curr 27 | curr = prev.next 28 | 29 | } 30 | return head 31 | }; 32 | -------------------------------------------------------------------------------- /leetcode/275-h-index-ii.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} citations 3 | * @return {number} 4 | */ 5 | var hIndex = function(citations) { 6 | let tot = citations.length 7 | let left = 0 8 | let right = tot 9 | let mid 10 | while(left<=right) { 11 | mid = left + ((right-left)>>1) 12 | const h = tot-mid 13 | if(h === citations[mid]) return h 14 | else if(h>citations[mid]) left = mid+1 15 | else right = mid-1 16 | } 17 | return tot - left 18 | }; 19 | 20 | -------------------------------------------------------------------------------- /leetcode/278-first-bad-version.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for isBadVersion() 3 | * 4 | * @param {integer} version number 5 | * @return {boolean} whether the version is bad 6 | * isBadVersion = function(version) { 7 | * ... 8 | * }; 9 | */ 10 | 11 | /** 12 | * @param {function} isBadVersion() 13 | * @return {function} 14 | */ 15 | var solution = function(isBadVersion) { 16 | /** 17 | * @param {integer} n Total versions 18 | * @return {integer} The first bad version 19 | */ 20 | return function(n) { 21 | let left = 1 22 | let right = n 23 | let mid 24 | while(left < right) { 25 | mid = left + ((right-left) >> 1) 26 | 27 | if(isBadVersion(mid)) { 28 | right = mid 29 | } else { 30 | left = mid+1 31 | } 32 | } 33 | return left 34 | }; 35 | }; 36 | -------------------------------------------------------------------------------- /leetcode/283-move-zeroes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {void} Do not return anything, modify nums in-place instead. 4 | */ 5 | var moveZeroes = function(nums) { 6 | let i = 0 7 | let j = 1 8 | while(i0) B++ 21 | h[g] = (h[g] || 0) - 1 22 | if(h[s]<0) B++ 23 | h[s] = (h[s] || 0) + 1 24 | } 25 | } 26 | return `${A}A${B}B` 27 | }; 28 | -------------------------------------------------------------------------------- /leetcode/3-longest-substring-without-repeating-characters.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} s 3 | * @return {number} 4 | */ 5 | var lengthOfLongestSubstring = function(s) { 6 | let localMax = 0 7 | let globalMax = 0 8 | const map = {} 9 | for(let i=0; i> 1) 13 | if(tails[mid]=tails.length) tails.push(nums[i]) 17 | else { 18 | tails[left]=nums[i] 19 | } 20 | } 21 | return tails.length 22 | }; 23 | 24 | /** 25 | * @param {number[]} nums 26 | * @return {number} 27 | */ 28 | var lengthOfLIS_DP = function(nums) { 29 | const LIS = Array(nums.length).fill(undefined) 30 | LIS[0] = 1 31 | let res = 1 32 | for(let i=1; imaxLIS) { 36 | maxLIS = LIS[j] 37 | } 38 | } 39 | LIS[i] = maxLIS + 1 40 | res = Math.max(res, LIS[i]) 41 | } 42 | 43 | return res 44 | }; -------------------------------------------------------------------------------- /leetcode/312-burst-balloons.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {number} 4 | */ 5 | var maxCoins = function(nums) { 6 | let eNums = [...nums] // extended nums 7 | eNums.unshift(1) 8 | eNums.push(1) 9 | const n = eNums.length 10 | 11 | // DP table 12 | let dp = Array(n).fill(undefined) 13 | for(let i=0; i map[b] - map[a]) 13 | return arr.slice(0, k).map(a=>Number(a)) 14 | }; -------------------------------------------------------------------------------- /leetcode/35-search-insert-position.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @param {number} target 4 | * @return {number} 5 | */ 6 | var searchInsert = function(nums, target) { 7 | for(let i=0; i=target) return i 9 | } 10 | return nums.length 11 | }; 12 | -------------------------------------------------------------------------------- /leetcode/367-valid-perfect-square.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} num 3 | * @return {boolean} 4 | */ 5 | var isPerfectSquare = function(num) { 6 | if(num===0 || num===1) return true 7 | if(num<0) return false 8 | let left = 0 9 | let right = num 10 | let mid 11 | let guess 12 | while(left>1) 14 | guess = mid*mid 15 | if(guess === num) { 16 | return true 17 | } else if(guess>num) { 18 | right = mid 19 | } else { 20 | left = mid+1 21 | } 22 | } 23 | return false 24 | }; -------------------------------------------------------------------------------- /leetcode/380-insert-delete-getrandom-o1.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Initialize your data structure here. 3 | */ 4 | var RandomizedSet = function() { 5 | this.map = new Map() 6 | this.arr = [] 7 | }; 8 | 9 | /** 10 | * Inserts a value to the set. Returns true if the set did not already contain the specified element. 11 | * @param {number} val 12 | * @return {boolean} 13 | */ 14 | RandomizedSet.prototype.insert = function(val) { 15 | if(this.map.has(val)) return false 16 | this.arr.push(val) 17 | this.map.set(val, this.arr.length-1) 18 | return true 19 | }; 20 | 21 | /** 22 | * Removes a value from the set. Returns true if the set contained the specified element. 23 | * @param {number} val 24 | * @return {boolean} 25 | */ 26 | RandomizedSet.prototype.remove = function(val) { 27 | const {map, arr} = this 28 | if(!map.has(val)) return false 29 | 30 | // fill the void of the deleted val in the array with the last val 31 | // swap them so the val you want to delete is at the end of the arr 32 | const i = map.get(val) 33 | const j = arr.length-1 34 | const last = arr[j] 35 | arr[j] = val 36 | arr[i] = last 37 | map.set(last, i) 38 | 39 | this.map.delete(val) 40 | arr.pop() 41 | return true 42 | }; 43 | 44 | /** 45 | * Get a random element from the set. 46 | * @return {number} 47 | */ 48 | RandomizedSet.prototype.getRandom = function() { 49 | const randInd = Math.floor(Math.random() * this.arr.length) 50 | return this.arr[randInd] 51 | }; 52 | 53 | /** 54 | * Your RandomizedSet object will be instantiated and called as such: 55 | * var obj = new RandomizedSet() 56 | * var param_1 = obj.insert(val) 57 | * var param_2 = obj.remove(val) 58 | * var param_3 = obj.getRandom() 59 | */ 60 | -------------------------------------------------------------------------------- /leetcode/383-ransom-note.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} ransomNote 3 | * @param {string} magazine 4 | * @return {boolean} 5 | */ 6 | var canConstruct = function(ransomNote, magazine) { 7 | const rs = ransomNote.split("") 8 | const ms = magazine.split("") 9 | for(let r of rs) { 10 | if(!ms.includes(r)) { 11 | return false 12 | } 13 | ms[ms.indexOf(r)] = "" 14 | } 15 | return true 16 | }; 17 | -------------------------------------------------------------------------------- /leetcode/387-first-unique-character-in-a-string.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} s 3 | * @return {number} 4 | */ 5 | var firstUniqChar = function(s) { 6 | let dups = new Set() 7 | for(let i=0; i a - b); 10 | const res = []; 11 | const length = candidates.length; 12 | const search = (i, sum, nums) => { 13 | if (i === length || sum > target) return; 14 | if (sum === target) { 15 | res.push(nums); 16 | return; 17 | } 18 | const c = candidates[i]; 19 | if (c > target) return; 20 | search(i, sum + c, nums.concat(c)); 21 | search(i + 1, sum, nums); 22 | }; 23 | 24 | search(0, 0, []); 25 | return res; 26 | }; 27 | -------------------------------------------------------------------------------- /leetcode/392-is-subsequence.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} s 3 | * @param {string} t 4 | * @return {boolean} 5 | */ 6 | var isSubsequence = function(s, t) { 7 | if(s.length>t.length) return false 8 | if(s.length===0) return true 9 | const cs = s.split('') 10 | for(let i=0; i { 2 | return Array(rep).fill(pattern).join("") 3 | } 4 | 5 | /** 6 | * @param {string} s 7 | * @return {string} 8 | */ 9 | var decodeString = function(s) { 10 | const stack = [] 11 | 12 | let isLastCharNum = false 13 | for(let i=0; i a-b) 10 | const res = [] 11 | const length = candidates.length 12 | 13 | const search = (i, sum, nums) => { 14 | if(sum===target) { 15 | res.push(nums) 16 | return 17 | } 18 | if(i >= length || sum>target) return 19 | const c = candidates[i] 20 | if(c>target) return // optimization 21 | 22 | search(i+1, sum+c, nums.concat(c)) 23 | 24 | // since candidates are sorted, we skip until we find next uniq 25 | let nextI = i+1 26 | while(candidates[nextI]===c) nextI++ 27 | search(nextI, sum, nums) 28 | } 29 | 30 | search(0, 0, []) 31 | return res 32 | }; 33 | -------------------------------------------------------------------------------- /leetcode/402-remove-k-digits.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} num 3 | * @param {number} k 4 | * @return {string} 5 | */ 6 | var removeKdigits = function(num, k) { 7 | if(num.length===k) return "0" 8 | if(k===0) return num 9 | let res = [] 10 | let left = k 11 | 12 | // get rid of everything larger than curr from tail 13 | for(let i=0; i0 && res.length>0 && res[res.length-1]>curr) { 16 | res.pop() 17 | left-- 18 | } 19 | res.push(curr) 20 | } 21 | // trim the tail 22 | while(left>0) { 23 | res.pop() 24 | left-- 25 | } 26 | // trim the leading zeros 27 | while(res[0]===0 && res.length>1) { 28 | res.shift(1) 29 | } 30 | return res.reduce((acc, curr) => acc+curr, "") 31 | } -------------------------------------------------------------------------------- /leetcode/406-queue-reconstruction-by-height.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[][]} people 3 | * @return {number[][]} 4 | */ 5 | var reconstructQueue = function(people) { 6 | people.sort((a, b) => a[0] == b[0] ? a[1] - b[1] : b[0] - a[0]); 7 | const res = [] 8 | for(let p of people) { 9 | res.splice(p[1], 0, p) 10 | } 11 | return res 12 | }; 13 | -------------------------------------------------------------------------------- /leetcode/41-first-missing-positive.js: -------------------------------------------------------------------------------- 1 | var firstMissingPos = function(nums) { 2 | let maxSeen = 0; //initialize 3 | // build dictionary of what numbers you've seen 4 | let seens = {}; 5 | for(let i=0; i maxSeen ? curr : maxSeen 9 | } 10 | // edge case: your have an array of negative numbers or 0 11 | // or you have an empty array 12 | if (maxSeen === 0) { 13 | return 1; 14 | } 15 | 16 | // iterate from 1 until maxSeen. The first number you see in 17 | // num which does not exist in the seens dictionary is your answer 18 | // unless your first missing positive integer is greater 19 | // than maxSeen 20 | for(let i=1; i=leftMax) { 13 | leftMax = height[left] 14 | } else { 15 | totArea+=leftMax-height[left] 16 | } 17 | left +=1 18 | } else { 19 | if(height[right]>=rightMax) { 20 | rightMax = height[right] 21 | } else { 22 | totArea+=rightMax-height[right] 23 | } 24 | right-=1 25 | } 26 | } 27 | return totArea; 28 | }; -------------------------------------------------------------------------------- /leetcode/421-maximum-xor-of-two-numbers-in-an-array.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {number} 4 | */ 5 | var findMaximumXOR = function(nums) { 6 | let max = 0 7 | for(let i=0; imax) max = curr 11 | } 12 | } 13 | return max 14 | }; 15 | -------------------------------------------------------------------------------- /leetcode/43-multiply-strings.js: -------------------------------------------------------------------------------- 1 | var multiply = function(num1, num2) { 2 | const m = num1.length 3 | const n = num2.length 4 | const pos = new Array(m+n) 5 | pos.fill(0) 6 | 7 | for(let i = m - 1; i >= 0; i--) { 8 | for(let j = n - 1; j >= 0; j--) { 9 | let mul = (num1.charAt(i)-'0') * (num2.charAt(j)-'0') 10 | let p1 = i + j, p2 = i + j + 1 11 | let sum = mul + pos[p2] 12 | pos[p1] += Math.floor(sum / 10) 13 | pos[p2] = (sum) % 10; 14 | } 15 | } 16 | 17 | let sb = '' 18 | for(let i=0; i c.charCodeAt(0) - 'a'.charCodeAt(0) 8 | const arrEqual = (a,b) => { 9 | for(let i=0; ii) { 9 | left-=i 10 | if(left0) { 41 | res = new ListNode(carry, res) 42 | } 43 | return res 44 | }; 45 | -------------------------------------------------------------------------------- /leetcode/451-sort-characters-by-frequency.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} s 3 | * @return {string} 4 | */ 5 | var frequencySort = function(s) { 6 | let map = {} 7 | for(let i=0; i map[b].length-map[a].length) 13 | let res = '' 14 | for(key of keys) { 15 | res+=map[key].join('') 16 | } 17 | return res 18 | }; -------------------------------------------------------------------------------- /leetcode/452-minimum-number-of-arrows-to-burst-balloons.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[][]} points 3 | * @return {number} 4 | 5 | [[1,6],[2,8],[7,12],[10,16]] 6 | */ 7 | var findMinArrowShots = function (points) { 8 | points.sort((a, b) => a[1] - b[1]); 9 | // assume: at least one balloon 10 | let arrows = 1; 11 | let prevEnd = points[0][1]; 12 | 13 | for (let i = 1; i < points.length; i++) { 14 | const [start, end] = points[i]; 15 | // if start <= prevEnd, there's overlap. The last arrow 16 | // can be used to pop the current balloon. Do nothing 17 | if (start > prevEnd) { 18 | prevEnd = end; 19 | arrows++; 20 | } 21 | } 22 | return arrows; 23 | }; 24 | -------------------------------------------------------------------------------- /leetcode/459-repeated-substring-pattern.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} s 3 | * @return {boolean} 4 | */ 5 | var repeatedSubstringPattern = function(s) { 6 | // approach: sliding window 7 | const halfLen = s.length>>1 8 | for(let windowSize=1; windowSize<=halfLen; windowSize++) { 9 | const pattern = s.substring(0,windowSize) 10 | for(let i=windowSize; i255) return false 12 | } 13 | return true 14 | } 15 | function isValidIPv6(IP) { 16 | const nums = IP.split(":") 17 | if(nums.length!==8) return false 18 | for(num of nums) { 19 | if(!is16BitHex(num)) return false 20 | } 21 | return true 22 | } 23 | /** 24 | * @param {string} IP 25 | * @return {string} 26 | */ 27 | var validIPAddress = function(IP) { 28 | if(isValidIPv4(IP)) { 29 | return "IPv4" 30 | } 31 | if(isValidIPv6(IP)) { 32 | return "IPv6" 33 | } 34 | return "Neither" 35 | }; 36 | -------------------------------------------------------------------------------- /leetcode/47-permutations-ii.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {number[][]} 4 | */ 5 | var permuteUnique = function(nums) { 6 | nums.sort((a,b) => a-b) 7 | let res = [] 8 | const dfs = (newPerm, visited) => { 9 | if(newPerm.length === nums.length) { 10 | res.push(newPerm.slice()) 11 | return 12 | } 13 | for(let i=0; i0 && !visited.has(i-1) && nums[i] === nums[i-1]) continue 15 | if(visited.has(i)) continue 16 | 17 | newPerm.push(nums[i]) 18 | visited.add(i) 19 | dfs(newPerm, visited) 20 | visited.delete(i) 21 | newPerm.pop() 22 | } 23 | } 24 | 25 | dfs([], new Set()) 26 | return res 27 | }; 28 | 29 | /** 30 | * @param {number[]} nums 31 | * @return {number[][]} 32 | */ 33 | var permuteUniqueSlow = function(nums) { 34 | let seen = new Set() 35 | let res = [] 36 | const permute = (arr, newPerm = []) => { 37 | for(let i=0; i>i !==0) { 9 | res ^= (1< 7 | c >= "A" && c <= "Z" 8 | ) 9 | const isAllCaps = capLetters.length === word.length 10 | const isOnlyFirstCap = capLetters.length === 1 && capLetters[0]=== word[0] 11 | const isAllLowercase = capLetters.length === 0 12 | return isAllCaps || isOnlyFirstCap || isAllLowercase 13 | }; 14 | -------------------------------------------------------------------------------- /leetcode/525-contiguous-array.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {number} 4 | */ 5 | var findMaxLength = function(nums) { 6 | let localMax = 0 7 | let globalMax = 0 8 | let balance = 0 9 | const balances = {0: -1} 10 | for(let i=0; i acc+curr, 0) 8 | for(let i=0; i>1) 30 | if(rand>this.cdf[mid]) { 31 | left = mid+1 32 | } else { 33 | right = mid 34 | } 35 | } 36 | return left 37 | }; 38 | 39 | /** 40 | * Your Solution object will be instantiated and called as such: 41 | * var obj = new Solution(w) 42 | * var param_1 = obj.pickIndex() 43 | */ 44 | -------------------------------------------------------------------------------- /leetcode/53-maximum-subarray.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {number} 4 | */ 5 | var maxSubArray = function(nums) { 6 | let bestSum = -Infinity 7 | let currSum = -Infinity 8 | for (let i=0; i>1) 11 | if(nums[mid]==nums[mid-1]) { 12 | mid+=1 // include mid with left 13 | } 14 | if((mid-left)%2 === 1) { 15 | right = mid 16 | } else { 17 | left = mid 18 | } 19 | } 20 | return nums[left] 21 | }; -------------------------------------------------------------------------------- /leetcode/56-merge-intervals.js: -------------------------------------------------------------------------------- 1 | const getNewInterval = (prev, curr) => { 2 | const [prevLo, prevHi] = prev 3 | const [currLo, currHi] = curr 4 | if(prevHi>=currLo) { 5 | const lo = Math.min(prevLo, currLo) 6 | const hi = Math.max(prevHi, currHi) 7 | return [[lo, hi]] 8 | } 9 | return [prev, curr] 10 | } 11 | 12 | /** 13 | * @param {number[][]} intervals 14 | * @return {number[][]} 15 | */ 16 | var merge = function(intervals) { 17 | // intervals must be sorted based 18 | // on start time 19 | intervals.sort((a,b) => a[0]-b[0]) 20 | 21 | let res = [] 22 | let prev = intervals[0] 23 | for(let i=1; i c.charCodeAt(0) - 'a'.charCodeAt(0) 8 | const arrEqual = (a,b) => { 9 | for(let i in a) { 10 | if(a[i]!==b[i]) return false 11 | } 12 | return true 13 | } 14 | let cFreq = Array(26).fill(0) 15 | let cSeen = Array(26).fill(0) 16 | let s1Chars = new Set() 17 | for(let c of s1) { 18 | cFreq[getCharInd(c)]++ 19 | s1Chars.add(c) 20 | } 21 | let c, i 22 | let toCheck = 0 23 | for(let j=0; j { 2 | if(interval1[1]=0; i--) { 8 | const word = words[i] 9 | if(word.length>0) return word.length 10 | } 11 | return 0 12 | }; 13 | -------------------------------------------------------------------------------- /leetcode/59-spiral-matrix-ii.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} n 3 | * @return {number[][]} 4 | */ 5 | var generateMatrix = function(n) { 6 | 7 | // Build Matrix 8 | const row = Array(n).fill(undefined) 9 | const matrix = Array(n).fill(undefined) 10 | for(let i=0; i { 14 | if(dir==="RIGHT") { 15 | if(j+1=0 && !matrix[i][j-1]) { 28 | return [i,j-1, dir] 29 | } 30 | return [i-1, j, "UP"] 31 | } 32 | // dir === UP 33 | if(i-1>=0 && !matrix[i-1][j]) { 34 | return [i-1, j, dir] 35 | } 36 | return [i, j+1, "RIGHT"] 37 | 38 | } 39 | 40 | let dir = "RIGHT" 41 | const lastToFill = n*n 42 | let i = 0 43 | let j = 0 44 | for(let toFill=1; toFill<=lastToFill; toFill++) { 45 | matrix[i][j] = toFill 46 | // calculate next i and j 47 | const [nexti, nextj, nextDir] = getNext(i,j,dir) 48 | i = nexti 49 | j = nextj 50 | dir = nextDir 51 | 52 | } 53 | return matrix 54 | }; 55 | 56 | -------------------------------------------------------------------------------- /leetcode/593-valid-square.js: -------------------------------------------------------------------------------- 1 | const getDist = (p1, p2) => { 2 | const [x1, y1] = p1 3 | const [x2, y2] = p2 4 | return Math.sqrt(Math.pow((x2-x1),2) + Math.pow((y2-y1),2)) 5 | } 6 | /** 7 | * @param {number[]} p1 8 | * @param {number[]} p2 9 | * @param {number[]} p3 10 | * @param {number[]} p4 11 | * @return {boolean} 12 | */ 13 | var validSquare = function(p1, p2, p3, p4) { 14 | const points = [p1,p2,p3,p4] 15 | 16 | let ds = new Set() 17 | for(let i=0; i 0 && dsArr[1] > 0 29 | }; 30 | -------------------------------------------------------------------------------- /leetcode/594-longest-harmonious-subsequence.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {number} 4 | */ 5 | var findLHS = function(nums) { 6 | let res = 0 7 | let h = {} 8 | for(let i=0; i=n) return true 14 | } 15 | return false 16 | }; 17 | 18 | 19 | /** 20 | * @param {number[]} flowerbed 21 | * @param {number} n 22 | * @return {boolean} 23 | */ 24 | var canPlaceFlowers = function(flowerbed, n) { 25 | let zeros = 0 26 | let canPlant = 0 27 | let prevOne = -1 28 | for(let i=0; i>1) 33 | } else { 34 | const numZeros = (i-prevOne-1) 35 | canPlant+= (numZeros-1)>>1 36 | } 37 | if(canPlant>=n) return true 38 | prevOne = i 39 | 40 | } 41 | if(prevOne===-1) { 42 | canPlant+= (flowerbed.length+1)>>1 43 | } else if(prevOne>1 45 | } 46 | return canPlant>=n 47 | }; 48 | -------------------------------------------------------------------------------- /leetcode/66-plus-one.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} digits 3 | * @return {number[]} 4 | */ 5 | var plusOne = function(digits) { 6 | if(digits.length<1) return [] 7 | let newNum = digits[digits.length-1] + 1 8 | let carry = (newNum>9) ? 1 : 0 9 | const res = [newNum%10] 10 | for(let i=digits.length-2; i>=0; i--) { 11 | newNum = digits[i]+carry 12 | res.unshift(newNum%10) 13 | carry = (newNum>=10) ? 1 : 0 14 | } 15 | if(carry===1) res.unshift(carry) 16 | return res 17 | }; 18 | -------------------------------------------------------------------------------- /leetcode/690-employee-importance.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for Employee. 3 | * function Employee(id, importance, subordinates) { 4 | * this.id = id; 5 | * this.importance = importance; 6 | * this.subordinates = subordinates; 7 | * } 8 | */ 9 | 10 | /** 11 | * @param {Employee[]} employees 12 | * @param {number} id 13 | * @return {number} 14 | */ 15 | var GetImportance = function(employees, id) { 16 | 17 | // approach: start with curr employee and hop to their subordinates 18 | // and sub-subordinates until we reach an employee with no subordinates 19 | 20 | // pre-processing: create an easy way to look up employees by their id 21 | const h = {} 22 | for(let i=0; i0) { 32 | curr = h[toExplore.shift()] 33 | curr.subordinates.forEach(id => { 34 | if(!seen[id]) { 35 | toExplore.push(id) 36 | } 37 | }) 38 | res += curr.importance 39 | } 40 | return res 41 | }; 42 | -------------------------------------------------------------------------------- /leetcode/700-search-in-a-binary-search-tree.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | /** 10 | * @param {TreeNode} root 11 | * @param {number} val 12 | * @return {TreeNode} 13 | */ 14 | var searchBST = function(root, val) { 15 | function traverse(node) { 16 | if(node===null) return null 17 | if(node.val===val) return node 18 | const left = traverse(node.left) 19 | const right = traverse(node.right) 20 | return left || right 21 | } 22 | return traverse(root) 23 | }; 24 | -------------------------------------------------------------------------------- /leetcode/705-design-hashset.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Initialize your data structure here. 3 | */ 4 | var MyHashSet = function() { 5 | this.h = {} 6 | }; 7 | 8 | /** 9 | * @param {number} key 10 | * @return {void} 11 | */ 12 | MyHashSet.prototype.add = function(key) { 13 | this.h[key] = true 14 | }; 15 | 16 | /** 17 | * @param {number} key 18 | * @return {void} 19 | */ 20 | MyHashSet.prototype.remove = function(key) { 21 | this.h[key] = false 22 | }; 23 | 24 | /** 25 | * Returns true if this set contains the specified element 26 | * @param {number} key 27 | * @return {boolean} 28 | */ 29 | MyHashSet.prototype.contains = function(key) { 30 | return this.h[key] === true // could be undefined 31 | }; 32 | 33 | /** 34 | * Your MyHashSet object will be instantiated and called as such: 35 | * var obj = new MyHashSet() 36 | * obj.add(key) 37 | * obj.remove(key) 38 | * var param_3 = obj.contains(key) 39 | */ 40 | -------------------------------------------------------------------------------- /leetcode/72-edit-distance.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} word1 3 | * @param {string} word2 4 | * @return {number} 5 | */ 6 | var minDistance = function(word1, word2) { 7 | const n = word1.length 8 | const m = word2.length 9 | if(n===0) return word2.length 10 | if(m===0) return word1.length 11 | const matrix = Array(n+1).fill(undefined) 12 | const row = Array(m+1).fill(undefined) 13 | for(let i in matrix) { 14 | matrix[i] = Array.from(row) 15 | } 16 | const getTop = (i,j) => matrix[i-1][j] 17 | const getLeft = (i,j) => matrix[i][j-1] 18 | const getDiag = (i,j) => matrix[i-1][j-1] 19 | for(let i=0; i 0) { 33 | u = toExplore.pop() 34 | emails.push(u) 35 | if(graph[u]) { 36 | for(let j=0; j 0) { 46 | emails = emails.sort() 47 | const name = emailToNames[emails[0]] 48 | res.push([name, ...emails]) 49 | } 50 | } 51 | 52 | return res 53 | }; -------------------------------------------------------------------------------- /leetcode/733-flood-fill.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[][]} image 3 | * @param {number} sr 4 | * @param {number} sc 5 | * @param {number} newColor 6 | * @return {number[][]} 7 | */ 8 | var floodFill_DFS = function(image, sr, sc, newColor) { 9 | const oldColor = image[sr][sc] 10 | function dfs(row, col) { 11 | if(row<0 || row>=image.length || col<0 || col>=image[0].length) return 12 | if(image[row][col]!==oldColor) return 13 | image[row][col] = newColor 14 | dfs(row-1, col) 15 | dfs(row+1, col) 16 | dfs(row, col-1) 17 | dfs(row, col+1) 18 | } 19 | if(oldColor===newColor) return image 20 | dfs(sr, sc) 21 | return image 22 | }; 23 | 24 | var floodFill_BFS = function(image, sr, sc, newColor) { 25 | const getNeighbors = (row, col, color) => { 26 | const above = (row-1) >= 0 ? [row-1, col] : [] 27 | const below = (row+1) < image.length ? [row+1, col] : [] 28 | const left = (col-1) >= 0 ? [row, col-1] : [] 29 | const right = (col+1) < image[0].length ? [row, col+1] : [] 30 | return [above, below, left, right].filter(a => 31 | a.length > 0 && image[a[0]][a[1]] === color 32 | ) 33 | } 34 | let oldColor = image[sr][sc] 35 | const toExplore = [] 36 | let u = [sr, sc] 37 | let seen = {} 38 | toExplore.push(u) 39 | seen[String(u)] = true 40 | let cs 41 | while(toExplore.length>0) { 42 | u = toExplore.shift() 43 | image[u[0]][u[1]] = newColor 44 | cs = getNeighbors(u[0], u[1], oldColor) 45 | for(let c of cs) { 46 | if(!seen[String(c)]) { 47 | toExplore.push(c) 48 | seen[String(c)] = true 49 | } 50 | } 51 | } 52 | return image 53 | }; -------------------------------------------------------------------------------- /leetcode/75-sort-colors.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {void} Do not return anything, modify nums in-place instead. 4 | */ 5 | var sortColors = function(nums) { 6 | const swap = (i,j) => { 7 | const temp = nums[i] 8 | nums[i] = nums[j] 9 | nums[j] = temp 10 | } 11 | let tail0 = 0 12 | let head2 = nums.length 13 | let i = 0 14 | 15 | while(i0) { 26 | const [u, k, uCost] = q.shift() 27 | if(u===dst || k=== K) continue 28 | if(graph[u]===undefined) continue 29 | for(let neighbor of graph[u]) { 30 | const [v, vCost] = neighbor 31 | let alt = uCost+vCost 32 | if(alt < dist[v]) { 33 | dist[v] = alt 34 | q.push([v, k+1, alt]) 35 | } 36 | } 37 | } 38 | return dist[dst] < Infinity ? dist[dst] : -1 39 | }; 40 | -------------------------------------------------------------------------------- /leetcode/80-remove-duplicates-from-sorted-array-ii.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {number} 4 | */ 5 | var removeDuplicates = function(nums) { 6 | let i=1; 7 | while(i { 4 | const offset = letter.charCodeAt(0) - "a".charCodeAt(0) 5 | return MorseAlpha[offset] 6 | } 7 | /** 8 | * @param {string[]} words 9 | * @return {number} 10 | */ 11 | var uniqueMorseRepresentations = function(words) { 12 | let uniqPattern = new Set() 13 | for(let word of words) { 14 | const pattern = word.split("").reduce((acc, letter) => acc+getMorseCodeFromLetter(letter), "") 15 | uniqPattern.add(pattern) 16 | } 17 | return uniqPattern.size 18 | }; 19 | -------------------------------------------------------------------------------- /leetcode/820-short-encoding-of-words.js: -------------------------------------------------------------------------------- 1 | import TreeNode from "../datastructure/Node/TreeNode"; 2 | 3 | const updateTrie = (node, newWord) => { 4 | if(newWord.length === 0) return 5 | const letter = newWord.slice(-1) 6 | const nextNode = node.children.find(child => child.data === letter); 7 | if(!nextNode) { 8 | nextNode = new TreeNode(letter) 9 | node.children.push(nextNode) 10 | } 11 | updateTrie(nextNode, newWord.substring(0, newWord.length-1)) 12 | } 13 | const traverseTrie = (node, refString, string) => { 14 | if(node.children.length === 0) { 15 | return refString + node.data + string 16 | } 17 | let newRefString = "" 18 | for(let i=0; i { 25 | const trie = new TreeNode("#") 26 | for (let i=0; i bin === 0 ? 1 : 0 8 | 9 | for(let i=0; iA[i+1])) i++ 24 | 25 | if(i===startDown) { 26 | i++ 27 | continue 28 | } 29 | 30 | localMax = i-startUp+1 31 | globalMax = Math.max(globalMax, localMax) 32 | } 33 | return globalMax 34 | }; 35 | -------------------------------------------------------------------------------- /leetcode/858-mirror-reflection.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} p 3 | * @param {number} q 4 | * @return {number} 5 | */ 6 | var mirrorReflection = function(p, q) { 7 | let m = q, n = p; 8 | while(m % 2 == 0 && n % 2 == 0){ 9 | m /= 2; 10 | n /= 2; 11 | } 12 | if (m % 2 == 0 && n % 2 == 1) return 0; 13 | if (m % 2 == 1 && n % 2 == 1) return 1; 14 | if (m % 2 == 1 && n % 2 == 0) return 2; 15 | return -1; 16 | }; 17 | -------------------------------------------------------------------------------- /leetcode/865-smallest-subtree-with-all-the-deepest-nodes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | /** 10 | * @param {TreeNode} root 11 | * @return {TreeNode} 12 | */ 13 | var subtreeWithAllDeepest = function(root) { 14 | let res = null 15 | let max = 0 16 | function traverse(node, depth) { 17 | if(node===null) return depth 18 | const leftDepth = traverse(node.left, depth+1) 19 | const rightDepth = traverse(node.right, depth+1) 20 | if(leftDepth>rightDepth) { 21 | if(leftDepth>max) { 22 | res = node.left 23 | max = leftDepth 24 | } 25 | return leftDepth 26 | } else if(rightDepth>leftDepth) { 27 | if(rightDepth>max) { 28 | res = node.right 29 | max = rightDepth 30 | } 31 | return rightDepth 32 | } else { 33 | if(leftDepth >= max) { 34 | res = node 35 | max = leftDepth 36 | 37 | } 38 | return leftDepth 39 | } 40 | } 41 | traverse(root, 0) 42 | return res 43 | }; 44 | -------------------------------------------------------------------------------- /leetcode/88-merge-sorted-array.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums1 3 | * @param {number} m 4 | * @param {number[]} nums2 5 | * @param {number} n 6 | * @return {void} Do not return anything, modify nums1 in-place instead. 7 | */ 8 | var merge = function(nums1, m, nums2, n) { 9 | let i = 0 10 | let j = 0 11 | while(i=m) { 13 | nums1[i] = nums2[j] 14 | i++ 15 | j++ 16 | } else if(i<=m && nums2[j]<=nums1[i]) { 17 | nums1.splice(i, 0, 0) 18 | nums1.splice(-1) 19 | nums1[i] = nums2[j] 20 | m++ 21 | i++ 22 | j++ 23 | } else { 24 | i++ 25 | } 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /leetcode/880-decoded-string-at-index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} S 3 | * @param {number} K 4 | * @return {string} 5 | */ 6 | var decodeAtIndex = function(S, K) { 7 | let decodedLen = 0 8 | // pre-processing step 9 | for(let i=0; i=0; i--) { 17 | K %= decodedLen 18 | const c = S[i] 19 | if(K===0 && isNaN(c)) { 20 | return c 21 | } 22 | if(!isNaN(c)) { 23 | decodedLen = Math.ceil(decodedLen / (+c)) 24 | continue 25 | } 26 | decodedLen-- 27 | } 28 | return null 29 | }; 30 | -------------------------------------------------------------------------------- /leetcode/886-possible-bipartition.js: -------------------------------------------------------------------------------- 1 | function Node(val) { 2 | this.val = val 3 | this.color = null 4 | this.children = [] 5 | this.parents = [] 6 | } 7 | /** 8 | * @param {number} N 9 | * @param {number[][]} dislikes 10 | * @return {boolean} 11 | */ 12 | var possibleBipartition = function(N, dislikes) { 13 | const graph = {} 14 | 15 | // Build the graph 16 | for(let dislike of dislikes) { 17 | const [u,v] = dislike 18 | graph[u] = graph[u] || new Node(u) 19 | graph[u].children.push(v) 20 | graph[v] = graph[v] || new Node(v) 21 | graph[v].parents.push(u) 22 | } 23 | 24 | // color all the nodes 25 | let seen = {} 26 | let toExplore = [] 27 | const keys = Object.keys(graph) 28 | let color = 1 29 | 30 | for(let i=0; i 0 && this.prices[this.prices.length-1] <= price) { 14 | result+=this.results.pop() 15 | this.prices.pop() 16 | } 17 | this.results.push(result) 18 | this.prices.push(price) 19 | return result 20 | } 21 | } -------------------------------------------------------------------------------- /leetcode/918-maximum-sum-circular-subarray.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} A 3 | * @return {number} 4 | */ 5 | var maxSubarraySumCircular = function(A) { 6 | let currMax = A[0] 7 | let bestMax = A[0] 8 | let currMin = A[0] 9 | let bestMin = A[0] 10 | let total = A[0] 11 | 12 | for(let i=1; iA[i+1]) i++ 15 | 16 | return i===A.length-1 17 | }; 18 | -------------------------------------------------------------------------------- /leetcode/945-minimum-increments-to-make-array-unique.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} A 3 | * @return {number} 4 | */ 5 | var minIncrementForUnique = function(A) { 6 | A = A.sort((a,b) =>a-b) 7 | let last = A[0] 8 | let numIncs = 0 9 | let diff 10 | for(let i=1; i { 2 | const s = String(num) 3 | if(s.length===1) return "0" + s 4 | return s 5 | } 6 | const removeFromArr = (nums, toRemove) => { 7 | let res = [...nums] 8 | for(let i=0; i=0) { 12 | res.splice(index, 1) 13 | } 14 | } 15 | return res 16 | } 17 | const makeMinute = (d1, d2) => { 18 | const min = Number(`${d1}${d2}`) 19 | if(min>=0 && min<60) return min 20 | return -1 21 | } 22 | /** 23 | * @param {number[]} A 24 | * @return {string} 25 | */ 26 | var largestTimeFromDigits = function(A) { 27 | // look for digits for a,b,c,d where ab:cd is largest valid time possible 28 | // ab <-- 00, 01, ...09, 10, 11, ... 19, 20, 21, 22, 23 29 | // cd: 00, 01, ..., 09, 10, 11, ..., 19, 20,..., 29, ...59 30 | for(let h=23; h>=0; h--) { 31 | // find largest minutes from remaining digits 32 | let hs = stringifyTime(h) 33 | let a = +hs[0] 34 | let b = +hs[1] 35 | let nums = A 36 | const toRemove = [a,b] 37 | nums = removeFromArr(A, toRemove) 38 | if(nums.length!==2) continue 39 | 40 | // Try all permutations of the remaining 2 digits 41 | // to determine if valid minute can be made 42 | const m = Math.max( 43 | makeMinute(nums[0], nums[1]), 44 | makeMinute(nums[1], nums[0]) 45 | ) 46 | if(m>=0) { 47 | const ms = stringifyTime(m) 48 | return `${hs}:${ms}` 49 | } 50 | 51 | } 52 | return "" 53 | 54 | }; -------------------------------------------------------------------------------- /leetcode/953-verifying-an-alien-dictionary.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string[]} words 3 | * @param {string} order 4 | * @return {boolean} 5 | */ 6 | var isAlienSorted = function(words, order) { 7 | // we can translate the words from alien alphabet to 8 | // normal alphabet. Then we can use the built-in sort function 9 | const map = {} 10 | const aCharCode = 'a'.charCodeAt(0) 11 | for(let i=0; i { 17 | let res = "" 18 | for(let i=0; i p[0]*p[0] + p[1]*p[1] 8 | points.sort((a,b) => getRsq(a)-getRsq(b)) 9 | return points.slice(0,K) 10 | }; 11 | -------------------------------------------------------------------------------- /leetcode/977-squares-of-a-sorted-array.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {number[]} 4 | */ 5 | var sortedSquares = function (nums) { 6 | const res = [] 7 | let i = 0 8 | const s = [] // stack 9 | for (let i = 0; i < nums.length; i++) { 10 | const num = nums[i] 11 | const numSq = num * num 12 | if (num < 0) { 13 | s.push(numSq) 14 | continue 15 | } 16 | // num is positive 17 | while (s.length) { 18 | if (s[s.length - 1] > numSq) break 19 | res.push(s.pop()) 20 | } 21 | res.push(numSq) 22 | } 23 | // post processing step 24 | while (s.length) { 25 | res.push(s.pop()) 26 | } 27 | return res 28 | }; 29 | -------------------------------------------------------------------------------- /leetcode/98-validate-binary-search-tree.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | /** 10 | * @param {TreeNode} root 11 | * @return {boolean} 12 | */ 13 | var isValidBST = function(root) { 14 | function checkTree(node) { 15 | if(!node) { 16 | return [true, null, null] 17 | } 18 | if(!node.left && !node.right) { 19 | return [true, node.val, node.val] 20 | } 21 | 22 | const [lValid, lMin, lMax] = checkTree(node.left) 23 | if(!lValid) return [false, 0, 0] 24 | 25 | const [rValid, rMin, rMax] = checkTree(node.right) 26 | if(!rValid) return [false, 0, 0] 27 | 28 | if((lMax && lMax>=node.val) || (rMin && rMin<=node.val)) { 29 | return [false, node.val, node.val] 30 | } 31 | 32 | const newMin = lMin===null ? node.val : lMin 33 | const newMax = rMax===null ? node.val : rMax 34 | return [true, newMin, newMax] 35 | } 36 | const res = checkTree(root) 37 | return res[0] 38 | }; 39 | -------------------------------------------------------------------------------- /leetcode/986-interval-list-intersections.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[][]} A 3 | * @param {number[][]} B 4 | * @return {number[][]} 5 | */ 6 | var intervalIntersection = function(A, B) { 7 | if(A.length===0 || B.length === 0) return [] 8 | let i = 0 9 | let j = 0 10 | let res = [] 11 | while(i=lo) res.push([lo,hi]) 18 | if(aHi maxRank) { 16 | maxRank = rank[l] 17 | guess = l 18 | } 19 | } 20 | if(maxRank < N-1) return -1 21 | if(leader[guess]) return -1 22 | return guess 23 | }; -------------------------------------------------------------------------------- /leetcode/problems/342-power-of-four.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} n 3 | * @return {boolean} 4 | */ 5 | var isPowerOfFour = function(n) { 6 | let left = n 7 | while(left>1) { 8 | if(n % 4!==0) return false 9 | left /= 4 10 | } 11 | return left===1 12 | }; 13 | -------------------------------------------------------------------------------- /leetcode/problems/442-find-all-duplicates-in-an-array.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {number[]} 4 | */ 5 | var findDuplicates = function(nums) { 6 | const dups = [] 7 | for(let i=0; i a-b) 23 | const dups = [] 24 | let prev = nums[0] 25 | for(let i=1; i 0) { 45 | let data = dataArr.shift() 46 | let newNode = new Node(data) 47 | let inserted = insertIntoTree(newNode, tree) 48 | if(!inserted) { 49 | dataArr.push(data) 50 | } 51 | } 52 | return tree 53 | } 54 | 55 | function printTree(treeNode, prepend) { 56 | let children = treeNode.children 57 | for(let i in children) { 58 | console.log(`${prepend}${children[i].data.name}`) 59 | printTree(children[i], prepend+'-') 60 | } 61 | } -------------------------------------------------------------------------------- /misc/flood-fill.js: -------------------------------------------------------------------------------- 1 | import Queue from '../../datastructure/FakeQueue_immutable' 2 | import sort2DArr from './flood-fill' 3 | 4 | function neighbors(p) { 5 | let top = [p[0]-1, p[1]] 6 | let bottom = [p[0]+1, p[1]] 7 | let left = [p[0], p[1]-1] 8 | let right = [p[0], p[1]+1] 9 | return {top, bottom, left, right} 10 | } 11 | 12 | function isSameColor(grid, p1, p2) { 13 | // also ensures the points are valid locations within the grid 14 | if(isNotInGrid(grid, p1) || isNotInGrid(grid, p2)) { 15 | return false 16 | } 17 | return grid[p1[0]][p1[1]] === grid[p2[0]][p2[1]] 18 | } 19 | 20 | function isNotInGrid(grid, p) { 21 | if(typeof p === 'undefined') return true 22 | if (typeof grid[p[0]] === 'undefined') return true 23 | return false 24 | } 25 | 26 | function findMaxConnectedDFS(grid, p) { 27 | let explored = {} 28 | explored[p] = true 29 | let res = [p] 30 | 31 | // explore neighbors via DFS using recursion 32 | function explore(p) { 33 | Object.values(neighbors(p)).forEach(neighbor => { 34 | if(isSameColor(grid, p, neighbor) && !explored[neighbor]) { 35 | res.push(neighbor) 36 | explored[neighbor] = true 37 | return explore(neighbor) 38 | } 39 | }) 40 | } 41 | 42 | explore(p,res, explored) 43 | return sort2DArr(res) 44 | } 45 | 46 | function findMaxConnectedBFS(grid, p) { 47 | let explored = {} 48 | let u = p 49 | let res = [u] 50 | 51 | let q = new Queue() 52 | q = q.enqueue(u) 53 | explored[u] = true 54 | 55 | // explore neighbors via BFS using a queue and a while-loop 56 | while(!q.isEmpty()) { 57 | [u, q] = q.dequeue() 58 | Object.values(neighbors(u)).forEach(v => { 59 | if (!explored[v]) { 60 | explored[v] = true 61 | if(isSameColor(grid, u, v)) { 62 | res.push(v) 63 | q = q.enqueue(v) 64 | } 65 | } 66 | }) 67 | } 68 | return sort2DArr(res) 69 | } 70 | 71 | export { 72 | findMaxConnectedDFS, 73 | findMaxConnectedBFS 74 | } 75 | -------------------------------------------------------------------------------- /misc/least-frequent-array-element.js: -------------------------------------------------------------------------------- 1 | function leastFrequent(nums) { 2 | let map = {} 3 | for(let num of nums) { 4 | map[num] = (map[num] || 0) + 1 5 | } 6 | const arr = Object.keys(map).sort((a,b) => map[a]-map[b]) 7 | let min = map[arr[0]] 8 | let res = [] 9 | for(let num of arr) { 10 | if(map[num] > min) break 11 | res.push(Number(num)) 12 | } 13 | return res.sort((a,b)=>a-b) 14 | } 15 | -------------------------------------------------------------------------------- /misc/shuffle.js: -------------------------------------------------------------------------------- 1 | /* 2 | repl: 3 | https://repl.it/@xiaoyunyang/knuth-shuffle-and-sort 4 | 5 | From wikipedia's Fisher-Yates Shuffle 6 | To shuffle an array a of n elements (indices 0..n-1): 7 | for i from 0 to n−2 do 8 | j ← random integer such that i ≤ j < n 9 | exchange a[i] and a[j] 10 | */ 11 | 12 | function getRandomInt(max) { 13 | // get random int between 0 and max-1 14 | return Math.floor(Math.random() * Math.floor(max)); 15 | } 16 | 17 | function shuffle(arr) { 18 | let a = [...arr] 19 | 20 | function swap(i,j) { 21 | let temp = a[i] 22 | a[i] = a[j] 23 | a[j] = temp 24 | } 25 | // knuth shuffle 26 | let j = 0 27 | for (let i=0; i { 23 | return compare(a,b) 24 | }) 25 | return sortedArr 26 | } 27 | 28 | export default sort2DArr -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "coding-challenges", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "jest" 7 | }, 8 | "repository": "https://github.com/xiaoyunyang/coding-challenges.git", 9 | "author": "Xiaoyun Yang ", 10 | "license": "MIT", 11 | "bugs": { 12 | "url": "https://github.com/xiaoyunyang/coding-challenges/issues" 13 | }, 14 | "homepage": "https://github.com/xiaoyunyang/coding-challenges#readme", 15 | "dependencies": { 16 | "ramda": "^0.26.1" 17 | }, 18 | "devDependencies": { 19 | "@babel/preset-env": "^7.6.3", 20 | "jest": "^24.9.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /random/splitStrs.js: -------------------------------------------------------------------------------- 1 | // https://repl.it/@xiaoyunyang/dynamic-programming-example 2 | 3 | function splitWords(str, regex) { 4 | let subStr = str.substring(1,str.length) 5 | let splitStrs = str.split(regex) 6 | return splitStrs.filter(str => str.length>0) 7 | } 8 | 9 | // Given a sequence of words belonging to a dictionary concatenated 10 | // together without space, return the sequence of words joined 11 | // together with space 12 | function stringPartition(str, dict) { 13 | // replace val every entry in dict with regex 14 | let keys = Object.keys(dict) 15 | 16 | let regexStr = Object.keys(dict).reduce((acc, curr) => { 17 | return `${acc}${curr}|` 18 | }, '(').slice(0,-1)+')' 19 | 20 | let regex = new RegExp(regexStr, 'g') 21 | 22 | let splitStrs = splitWords(str, regex) 23 | 24 | let res = splitStrs.join(' ') 25 | 26 | return res 27 | } 28 | -------------------------------------------------------------------------------- /whiteboarding/leetcode/1029-two-city-scheduling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyunyang/coding-challenges/12d4727058b299d80c37d3ac2156a25dba4eb58d/whiteboarding/leetcode/1029-two-city-scheduling.png -------------------------------------------------------------------------------- /whiteboarding/leetcode/1276-number-of-burgers-with-no-waste-of-ingredients.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyunyang/coding-challenges/12d4727058b299d80c37d3ac2156a25dba4eb58d/whiteboarding/leetcode/1276-number-of-burgers-with-no-waste-of-ingredients.png -------------------------------------------------------------------------------- /whiteboarding/leetcode/820-short-encoding-of-words.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyunyang/coding-challenges/12d4727058b299d80c37d3ac2156a25dba4eb58d/whiteboarding/leetcode/820-short-encoding-of-words.png --------------------------------------------------------------------------------