├── 10-regular-expression-matching.md ├── 100-same-tree.md ├── 101-symmetric-tree.md ├── 102-binary-tree-level-order-traversal.md ├── 1026-maximum-difference-between-node-and-ancestor.md ├── 1027-longest-arithmetic-subsequence.md ├── 1029-two-city-scheduling.md ├── 103-binary-tree-zigzag-level-order-traversal.md ├── 1030-matrix-cells-in-distance-order.md ├── 1031-maximum-sum-of-two-non-overlapping-subarrays.md ├── 1033-moving-stones-until-consecutive.md ├── 1034-coloring-a-border.md ├── 1035-uncrossed-lines.md ├── 1036-escape-a-large-maze.md ├── 1037-valid-boomerang.md ├── 1038-binary-search-tree-to-greater-sum-tree.md ├── 1039-minimum-score-triangulation-of-polygon.md ├── 104-maximum-depth-of-binary-tree.md ├── 1040-moving-stones-until-consecutive-ii.md ├── 1041-robot-bounded-in-circle.md ├── 1042-flower-planting-with-no-adjacent.md ├── 1043-partition-array-for-maximum-sum.md ├── 1047-remove-all-adjacent-duplicates-in-string.md ├── 105-construct-binary-tree-from-preorder-and-inorder-traversal.md ├── 1053-previous-permutation-with-one-swap.md ├── 1057-campus-bikes.md ├── 106-construct-binary-tree-from-inorder-and-postorder-traversal.md ├── 1060-missing-element-in-sorted-array.md ├── 1062-longest-repeating-substring.md ├── 107-binary-tree-level-order-traversal-ii.md ├── 108-convert-sorted-array-to-binary-search-tree.md ├── 1091-shortest-path-in-binary-matrix.md ├── 11-container-with-most-water.md ├── 1108-defanging-an-ip-address.md ├── 111-minimum-depth-of-binary-tree.md ├── 112-path-sum.md ├── 1122-relative-sort-array.md ├── 1123-lowest-common-ancestor-of-deepest-leaves.md ├── 114-flatten-binary-tree-to-linked-list.md ├── 117-populating-next-right-pointers-in-each-node-ii.md ├── 119-pascals-triangle-ii.md ├── 1197-minimum-knight-moves.md ├── 12-integer-to-roman.md ├── 121-best-time-to-buy-and-sell-stock.md ├── 1213-intersection-of-three-sorted-arrays.md ├── 1219-path-with-maximum-gold.md ├── 122-best-time-to-buy-and-sell-stock-ii.md ├── 123-best-time-to-buy-and-sell-stock-iii.md ├── 124-binary-tree-maximum-path-sum.md ├── 1245-tree-diameter.md ├── 1249-minimum-remove-to-make-valid-parentheses.md ├── 125-valid-palindrome.md ├── 1269-number-of-ways-to-stay-in-the-same-place-after-some-steps.md ├── 127-word-ladder.md ├── 128-longest-consecutive-sequence.md ├── 129-sum-root-to-leaf-numbers.md ├── 13-roman-to-integer.md ├── 130-surrounded-regions.md ├── 1326-minimum-number-of-taps-to-open-to-water-a-garden.md ├── 133-clone-graph.md ├── 134-gas-station.md ├── 1347-minimum-number-of-steps-to-make-two-strings-anagram.md ├── 1348-tweet-counts-per-frequency.md ├── 135-candy.md ├── 136-single-number.md ├── 137-single-number-ii.md ├── 1379-find-a-corresponding-node-of-a-binary-tree-in-a-clone-of-that-tree.md ├── 138-copy-list-with-random-pointer.md ├── 139-word-break.md ├── 1394-find-lucky-integer-in-an-array.md ├── 14-longest-common-prefix.md ├── 140-word-break-ii.md ├── 141-linked-list-cycle.md ├── 1424-diagonal-traverse-ii.md ├── 1428-leftmost-column-with-at-least-a-one.md ├── 1436-destination-city.md ├── 1437-check-if-all-1s-are-at-least-length-k-places-away.md ├── 1438-longest-continuous-subarray-with-absolute-diff-less-than-or-equal-to-limit.md ├── 144-binary-tree-preorder-traversal.md ├── 145-binary-tree-postorder-traversal.md ├── 1458-max-dot-product-of-two-subsequences.md ├── 146-lru-cache.md ├── 1460-make-two-arrays-equal-by-reversing-sub-arrays.md ├── 1461-check-if-a-string-contains-all-binary-codes-of-size-k.md ├── 1464-aximum-product-of-two-elements-in-an-array.md ├── 1465-maximum-area-of-a-piece-of-cake-after-horizontal-and-vertical-cuts.md ├── 1466-reorder-routes-to-make-all-paths-lead-to-the-city-zero.md ├── 1469-find-all-the-lonely-nodes.md ├── 1470-shuffle-the-array.md ├── 1471-the-k-strongest-values-in-an-array.md ├── 1472-design-browser-history.md ├── 1473-paint-house-iii.md ├── 148-sort-list.md ├── 1481-least-number-of-unique-integers-after-k-removals.md ├── 1482-minimum-number-of-days-to-make-m-bouquets.md ├── 1488-avoid-flood-in-the-city.md ├── 149-max-points-on-a-line.md ├── 1496-path-crossing.md ├── 1497-check-if-array-pairs-are-divisible-by-k.md ├── 15-3sum.md ├── 150-evaluate-reverse-polish-notation.md ├── 151-reverse-words-in-a-string.md ├── 1514-path-with-maximum-probability.md ├── 1518-water-bottles.md ├── 152-maximum-product-subarray.md ├── 153-find-minimum-in-rotated-sorted-array.md ├── 155-min-stack.md ├── 158-read-n-characters-given-read4-ii-call-multiple-times.md ├── 1582-special-positions-in-a-binary-matrix.md ├── 1583-count-unhappy-friends.md ├── 1584-min-cost-to-connect-all-points.md ├── 16-3sum-closest.md ├── 161-one-edit-distance.md ├── 162-find-peak-element.md ├── 1658-minimum-operations-to-reduce-x-to-zero.md ├── 167-two-sum-ii-input-array-is-sorted.md ├── 169-majority-element.md ├── 17-letter-combinations-of-a-phone-number.md ├── 173-binary-search-tree-iterator.md ├── 1752-check-if-array-is-sorted-and-rotated.md ├── 1753-maximum-score-from-removing-stones.md ├── 18-4sum.md ├── 1817-finding-the-users-active-minutes.md ├── 1868-product-of-two-run-length-encoded-arrays.md ├── 188-best-time-to-buy-and-sell-stock-iv.md ├── 189-rotate-array.md ├── 19-remove-nth-node-from-end-of-list.md ├── 190-reverse-bits.md ├── 1905-count-sub-islands.md ├── 191-number-of-1-bits.md ├── 198-house-robber.md ├── 199-binary-tree-right-side-view.md ├── 2-add-two-numbers.md ├── 20-valid-parentheses.md ├── 200-number-of-islands.md ├── 202-happy-number.md ├── 204-count-primes.md ├── 205-isoporphic-strings.md ├── 206-reverse-linked-list.md ├── 207-course-schedule.md ├── 2073-time-needed-to-buy-tickets.md ├── 208-implement-trie-prefix-tree.md ├── 209-minimum-size-subarray-sum.md ├── 21-merge-two-sorted-lists.md ├── 210-course-schedule-ii.md ├── 211-design-add-and-search-words-data-structure.md ├── 212-word-search-ii.md ├── 215-kth-largest-element-in-an-array.md ├── 219-contains-duplicate-ii.md ├── 22-generate-parentheses.md ├── 222-count-complete-tree-nodes.md ├── 224-basic-calculator.md ├── 226-invert-binary-tree.md ├── 228-summary-ranges.md ├── 23-merge-k-sorted-lists.md ├── 230-kth-smallest-element-in-a-bst.md ├── 232-implement-queue-using-stacks.md ├── 236-lowest-common-ancestor-of-a-binary-tree.md ├── 238-product-of-array-except-self.md ├── 239-sliding-window-maximum.md ├── 24-swap-nodes-in-pairs.md ├── 242-valid-anagram.md ├── 246-strobogrammatic-number.md ├── 247-strobogrammatic-number-ii.md ├── 249-group-shifted-strings.md ├── 25-reverse-nodes-in-k-group.md ├── 252-meeting-rooms.md ├── 257-binary-tree-paths.md ├── 259-3sum-smaller.md ├── 26-remove-duplicates-from-sorted-array.md ├── 266-palindrome-permutation.md ├── 269-alien-dictionary.md ├── 27-remove-element.md ├── 270-closest-binary-search-tree-value.md ├── 273-integer-to-english-words.md ├── 274-h-index.md ├── 2768-number-of-black-blocks.md ├── 278-first-bad-version.md ├── 28-implement-strstr.md ├── 282-expression-add-operators.md ├── 283-move-zeroes.md ├── 286-walls-and-gates.md ├── 289-game-of-life.md ├── 29-divide-two-integers.md ├── 290-word-pattern.md ├── 296-best-meeting-point.md ├── 297-serialize-and-deserialize-binary-tree.md ├── 3-longest-substring-without-repeating-characters.md ├── 30-substring-with-concatenation-of-all-words.md ├── 300-longest-increasing-subsequence.md ├── 304-range-sum-query-2d-immutable.md ├── 31-next-permutation.md ├── 311-sparse-matrix-multiplication.md ├── 314-binary-tree-vertical-order-traversal.md ├── 315-count-of-smaller-numbers-after-self.md ├── 316-remove-duplicate-letters.md ├── 3162-find-the-number-of-good-pairs-i.md ├── 3163-find-the-number-of-good-pairs-ii.md ├── 3163-string-compression-iii.md ├── 3168-minimum-number-of-chairs-in-a-waiting-room.md ├── 3169-count-days-without-meetings.md ├── 317-shortest-distance-from-all-buildings.md ├── 3170-lexicographically-minimum-string-after-removing-stars.md ├── 32-longest-valid-parentheses.md ├── 322-coin-change.md ├── 323-number-of-connected-components-in-an-undirected-graph.md ├── 33-search-in-rotated-sorted-array.md ├── 339-nested-list-weight-sum.md ├── 34-find-first-and-last-position-of-element-in-sorted-array.md ├── 340-longest-substring-with-at-most-k-distinct-characters.md ├── 341-flatten-nested-list-iterator.md ├── 344-reverse-string.md ├── 347-top-k-frequent-elements.md ├── 348-design-tic-tac-toe.md ├── 349-intersection-of-two-arrays.md ├── 35-search-insert-position.md ├── 350-intersection-of-two-arrays-ii.md ├── 359-logger-rate-limiter.md ├── 36-valid-sudoku.md ├── 362-design-hit-counter.md ├── 37-sudoku-solver.md ├── 373-find-k-pairs-with-smallest-sums.md ├── 374-guess-number-higher-or-lower.md ├── 38-count-and-say.md ├── 380-insert-delete-getrandom-o1.md ├── 383-ransom-note.md ├── 39-combination-sum.md ├── 392-is-subsequence.md ├── 398-random-pick-index.md ├── 4-median-of-two-sorted-arrays.md ├── 40-combination-sum-ii.md ├── 41-first-missing-positive.md ├── 415-add-strings.md ├── 419-battleships-in-a-board.md ├── 42-trapping-rain-water.md ├── 427-construct-quad-tree.md ├── 43-multiply-strings.md ├── 433-minimum-genetic-mutation.md ├── 438-find-all-anagrams-in-a-string.md ├── 44-wildcard-matching.md ├── 442-find-all-duplicates-in-an-array.md ├── 443-string-compression.md ├── 45-jump-game-ii.md ├── 452-minimum-number-of-arrows-to-burst-balloons.md ├── 453-minimum-moves-to-equal-array-elements.md ├── 46-permutations.md ├── 461-hamming-distance.md ├── 463-island-perimeter.md ├── 47-permutations-ii.md ├── 48-rotate-image.md ├── 485-max-consecutive-ones.md ├── 487-max-consecutive-ones-ii.md ├── 49-group-anagrams.md ├── 498-diagonal-traverse.md ├── 5-longest-palindromic-substring.md ├── 50-powx-n.md ├── 509-fibonacci-number.md ├── 51-n-queens.md ├── 52-n-queens-ii.md ├── 520-detect-capital.md ├── 523-continuous-subarray-sum.md ├── 528-random-pick-with-weight.md ├── 53-maximum-subarray.md ├── 530-minimum-absolute-difference-in-bst.md ├── 532-k-diff-pairs-in-an-array.md ├── 54-spiral-matrix.md ├── 543-diameter-of-binary-tree.md ├── 55-jump-game.md ├── 56-merge-intervals.md ├── 560-subarray-sum-equals-k.md ├── 563-binary-tree-tilt.md ├── 57-insert-interval.md ├── 58-length-of-last-word.md ├── 59-spiral-matrix-ii.md ├── 6-zigzag-conversion.md ├── 60-permutation-sequence.md ├── 61-rotate-list.md ├── 616-add-bold-tag-in-string.md ├── 62-unique-paths.md ├── 621-task-scheduler.md ├── 63-unique-paths-ii.md ├── 635-design-log-storage-system.md ├── 636-exclusive-time-of-functions.md ├── 637-average-of-levels-in-binary-tree.md ├── 64-minimum-path-sum.md ├── 647-palindromic-substrings.md ├── 65-valid-number.md ├── 658-find-k-closest-elements.md ├── 66-plus-one.md ├── 67-add-binary.md ├── 670-maximum-swap.md ├── 674-longest-continuous-increasing-subsequence.md ├── 68-text-justification.md ├── 680-valid-palindrome-ii.md ├── 689-maximum-sum-of-3-non-overlapping-subarrays.md ├── 69-sqrtx.md ├── 692-top-k-frequent-words.md ├── 7-reverse-integer.md ├── 70-climbing-stairs.md ├── 702-search-in-a-sorted-array-of-unknown-size.md ├── 704-binary-search.md ├── 708-insert-into-a-sorted-circular-linked-list.md ├── 71-simplify-path.md ├── 718-maximum-length-of-repeated-subarray.md ├── 72-edit-distance.md ├── 721-accounts-merge.md ├── 724-find-pivot-index.md ├── 73-set-matrix-zeroes.md ├── 74-search-a-2d-matrix.md ├── 748-shortest-completing-word.md ├── 75-sort-colors.md ├── 76-minimum-window-substring.md ├── 766-toeplitz-matrix.md ├── 767-reorganize-string.md ├── 77-combinations.md ├── 779-k-th-symbol-in-grammar.md ├── 78-subsets.md ├── 783-minimum-distance-between-bst-node.md ├── 785-is-graph-bipartite.md ├── 79-word-search.md ├── 791-custom-sort-string.md ├── 8-string-to-integer-atoi.md ├── 80-remove-duplicates-from-sorted-array-ii.md ├── 81-search-in-rotated-sorted-array-ii.md ├── 82-remove-duplicates-from-sorted-list-ii.md ├── 824-goat-latin.md ├── 825-friends-of-appropriate-ages.md ├── 83-remove-duplicates-from-sorted-list.md ├── 839-similar-string-groups.md ├── 84-largest-rectangle-in-histogram.md ├── 844-backspace-string-compare.md ├── 85-maximal-rectangle.md ├── 86-partition-list.md ├── 863-all-nodes-distance-k-in-binary-tree.md ├── 869-reordered-power-of-2.md ├── 87-scramble-string.md ├── 88-merge-sorted-array.md ├── 89-gray-code.md ├── 896-monotonic-array.md ├── 9-palindrome-number.md ├── 90-subsets-ii.md ├── 905-sort-array-by-parity.md ├── 909-snakes-and-ladders.md ├── 91-decode-ways.md ├── 918-maximum-sum-circular-subarray.md ├── 92-reverse-linked-list-ii.md ├── 921-minimum-add-to-make-parentheses-valid.md ├── 93-restore-ip-addresses.md ├── 938-range-sum-of-bst.md ├── 94-binary-tree-inorder-traversal.md ├── 95-unique-binary-search-trees-ii.md ├── 953-verifying-an-alien-dictionary.md ├── 958-check-completeness-of-a-binary-tree.md ├── 96-unique-binary-search-trees.md ├── 97-interleaving-string.md ├── 98-validate-binary-search-tree.md ├── 985-sum-of-even-numbers-after-queries.md ├── 986-interval-list-intersections.md ├── 987-vertical-order-traversal-of-a-binary-tree.md ├── 99-recover-binary-search-tree.md └── README.md /100-same-tree.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * Definition for a binary tree node. 4 | * function TreeNode(val, left, right) { 5 | * this.val = (val===undefined ? 0 : val) 6 | * this.left = (left===undefined ? null : left) 7 | * this.right = (right===undefined ? null : right) 8 | * } 9 | */ 10 | /** 11 | * @param {TreeNode} p 12 | * @param {TreeNode} q 13 | * @return {boolean} 14 | */ 15 | var isSameTree = function (p, q) { 16 | if (p == null && q == null) return true; 17 | if (p == null || q == null || p.val !== q.val) return false; 18 | return isSameTree(p.left, q.left) && isSameTree(p.right, q.right); 19 | }; 20 | ``` 21 | -------------------------------------------------------------------------------- /102-binary-tree-level-order-traversal.md: -------------------------------------------------------------------------------- 1 | me explaining this on youtube: https://youtu.be/4Lm0p0A2t1w 2 | 3 | ```js 4 | /** 5 | * Definition for a binary tree node. 6 | * function TreeNode(val, left, right) { 7 | * this.val = (val===undefined ? 0 : val) 8 | * this.left = (left===undefined ? null : left) 9 | * this.right = (right===undefined ? null : right) 10 | * } 11 | */ 12 | /** 13 | * @param {TreeNode} root 14 | * @return {number[][]} 15 | */ 16 | 17 | // Time: O(n) 18 | // Space: O(n/2) => O(n) 19 | var levelOrder = function (root) { 20 | if (root === null) return []; 21 | 22 | const result = []; 23 | // BFS 24 | const queue = [root]; 25 | 26 | while (queue.length > 0) { 27 | queue.push(null); 28 | const level = []; 29 | let head; 30 | while ((head = queue.shift())) { 31 | level.push(head.val); 32 | if (head.left) queue.push(head.left); 33 | if (head.right) queue.push(head.right); 34 | } 35 | result.push(level); 36 | } 37 | 38 | return result; 39 | }; 40 | ``` 41 | -------------------------------------------------------------------------------- /103-binary-tree-zigzag-level-order-traversal.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * Definition for a binary tree node. 4 | * function TreeNode(val, left, right) { 5 | * this.val = (val===undefined ? 0 : val) 6 | * this.left = (left===undefined ? null : left) 7 | * this.right = (right===undefined ? null : right) 8 | * } 9 | */ 10 | /** 11 | * @param {TreeNode} root 12 | * @return {number[][]} 13 | */ 14 | var zigzagLevelOrder = function (root) { 15 | if (root == null) return []; 16 | 17 | const queue = [root]; 18 | let direction = "right"; 19 | const result = []; 20 | while (queue.length > 0) { 21 | let count = queue.length; 22 | let levelList = []; 23 | while (count > 0) { 24 | const head = queue.shift(); 25 | levelList.push(head.val); 26 | if (head.left) { 27 | queue.push(head.left); 28 | } 29 | if (head.right) { 30 | queue.push(head.right); 31 | } 32 | count -= 1; 33 | } 34 | if (direction === "left") { 35 | levelList.reverse(); 36 | } 37 | result.push(levelList); 38 | direction = direction === "right" ? "left" : "right"; 39 | } 40 | return result; 41 | }; 42 | ``` 43 | -------------------------------------------------------------------------------- /1033-moving-stones-until-consecutive.md: -------------------------------------------------------------------------------- 1 | Youtube video explaining this: https://youtu.be/Mqd_ZPcljT8 2 | 3 | 4 | ```js 5 | 6 | const minMoves = (a, b, c) => { 7 | return Math.min(b - a - 1, 1) + Math.min(c - b - 1, 1); 8 | } 9 | 10 | const maxMoves = (a, b, c) => { 11 | return b - a - 1 + c - b - 1; 12 | } 13 | 14 | /** 15 | * @param {number} a 16 | * @param {number} b 17 | * @param {number} c 18 | * @return {number[]} 19 | */ 20 | var numMovesStones = function(a, b, c) { 21 | const input = [a,b,c].sort((a, b) => a - b) 22 | const result = [ 23 | minMoves(input[0], input[1], input[2]), 24 | maxMoves(input[0], input[1], input[2]) 25 | ] 26 | 27 | if (input[2] > input[1] + 1) { 28 | result[0] = Math.min(result[0], minMoves(input[1], input[1] + 1, input[2]) + 1); 29 | } 30 | 31 | if (input[1] > input[0] + 1) { 32 | result[0] = Math.min(result[0], minMoves(input[0], input[0] + 1, input[1]) + 1); 33 | } 34 | 35 | return result; 36 | }; 37 | ``` 38 | -------------------------------------------------------------------------------- /1035-uncrossed-lines.md: -------------------------------------------------------------------------------- 1 | Youtube video explaining this: https://youtu.be/659LrloKNdw 2 | 3 | 4 | ```js 5 | /** 6 | * @param {number[]} A 7 | * @param {number[]} B 8 | * @return {number} 9 | */ 10 | var maxUncrossedLines = function(A, B) { 11 | const results = new Array(A.length + 1); 12 | for (let i = 0; i < A.length + 1; i++) { 13 | results[i] = new Array(B.length + 1).fill(0); 14 | } 15 | for (let i = 1, totalA = A.length; i < totalA + 1; i++) { 16 | for (let j = 1, totalB = B.length; j < totalB + 1; j++) { 17 | if (A[i - 1] === B[j - 1]) { 18 | results[i][j] = results[i - 1][j - 1] + 1; 19 | } else { 20 | results[i][j] = Math.max(results[i][j - 1], results[i - 1][j]); 21 | } 22 | } 23 | } 24 | return results[A.length][B.length]; 25 | }; 26 | ``` 27 | -------------------------------------------------------------------------------- /1037-valid-boomerang.md: -------------------------------------------------------------------------------- 1 | A video explaining this: https://youtu.be/kfjbiYD6nUc 2 | 3 | ```js 4 | /** 5 | * @param {number[][]} points 6 | * @return {boolean} 7 | */ 8 | var isBoomerang = function(points) { 9 | return (points[1][1] - points[0][1]) * (points[2][0] - points[0][0]) !== 10 | (points[2][1] - points[0][1]) * (points[1][0] - points[0][0]) 11 | }; 12 | ``` 13 | -------------------------------------------------------------------------------- /1038-binary-search-tree-to-greater-sum-tree.md: -------------------------------------------------------------------------------- 1 | A video explaining this problem: https://youtu.be/WAmjkVqH2go 2 | 3 | ```js 4 | /** 5 | * Definition for a binary tree node. 6 | * function TreeNode(val) { 7 | * this.val = val; 8 | * this.left = this.right = null; 9 | * } 10 | */ 11 | 12 | const traverse = (node, currentSum) => { 13 | let sum = currentSum 14 | if (node.right) { 15 | sum = traverse(node.right, currentSum) 16 | } 17 | 18 | node.val = sum + node.val 19 | 20 | if (node.left) { 21 | sum = traverse(node.left, node.val) 22 | return sum 23 | } 24 | 25 | return node.val 26 | } 27 | 28 | /** 29 | * @param {TreeNode} root 30 | * @return {TreeNode} 31 | */ 32 | var bstToGst = function(root) { 33 | traverse(root, 0) 34 | return root 35 | }; 36 | ``` 37 | -------------------------------------------------------------------------------- /1039-minimum-score-triangulation-of-polygon.md: -------------------------------------------------------------------------------- 1 | A video explaining this: https://youtu.be/NhgAG7QWk8k 2 | 3 | ```js 4 | const minTri = (A, start, end, cache) => { 5 | const key = start * 100 + end 6 | if (cache[key]) { 7 | return cache[key] 8 | } 9 | 10 | const total = A.length 11 | 12 | const length = end - start + 1 13 | if (length < 3) return 0 14 | if (length === 3) return A[start] * A[start + 1] * A[end] 15 | 16 | let min = Infinity 17 | for (let i = start + 1; i < end; i++) { 18 | min = Math.min( 19 | min, 20 | minTri(A, start, i, cache) + 21 | minTri(A, i, end, cache) + 22 | A[start] * A[i] * A[end] 23 | ) 24 | } 25 | cache[key] = min 26 | return min 27 | } 28 | /** 29 | * @param {number[]} A 30 | * @return {number} 31 | */ 32 | var minScoreTriangulation = function(A) { 33 | return minTri(A, 0, A.length - 1, {}) 34 | }; 35 | ``` 36 | -------------------------------------------------------------------------------- /1041-robot-bounded-in-circle.md: -------------------------------------------------------------------------------- 1 | youtube video explaining this: https://youtu.be/HTDfnhZHNN8 2 | 3 | ```js 4 | /** 5 | * @param {string} instructions 6 | * @return {boolean} 7 | */ 8 | var isRobotBounded = function(instructions) { 9 | let direction = [0, 1] 10 | let pos = [0, 0] 11 | 12 | for (let i = 0, total = instructions.length; i < total; i++) { 13 | const instruction = instructions[i] 14 | if (instruction === 'G') { 15 | pos[0] += direction[0] 16 | pos[1] += direction[1] 17 | } else if (instruction === 'L') { 18 | [direction[0], direction[1]] = [-direction[1], direction[0]] 19 | } else { 20 | [direction[0], direction[1]] = [direction[1], -direction[0]] 21 | } 22 | } 23 | 24 | // if we are still at the origin 25 | if (pos[0] === 0 && pos[1] === 0) { 26 | return true 27 | } 28 | 29 | // check if direction changes bigger than 90 degrees 30 | // cos(theta) = A * B / (|A| * |B|) 31 | // A => direction 32 | // B => [0, 1] 33 | return direction[1] <= 0 34 | }; 35 | ``` 36 | -------------------------------------------------------------------------------- /1042-flower-planting-with-no-adjacent.md: -------------------------------------------------------------------------------- 1 | Youtube video explaining this: https://youtu.be/IXk3kdXWp5M 2 | 3 | 4 | ```js 5 | var gardenNoAdj = function(N, paths) { 6 | let map = {}; 7 | for(let i=1; i<=N; i++){ 8 | map[i] = []; 9 | } 10 | 11 | for(let path of paths){ 12 | map[path[0]].push(path[1]); 13 | map[path[1]].push(path[0]); 14 | } 15 | let res = new Array(N).fill(0); 16 | for(let i=1; i<=N; i++){ 17 | let set = new Set([1,2,3,4]); 18 | 19 | for(let next of map[i]){ 20 | if(set.has(res[next-1])) set.delete(res[next-1]); 21 | } 22 | 23 | res[i-1] = set.values().next().value; 24 | } 25 | 26 | return res; 27 | }; 28 | ``` 29 | -------------------------------------------------------------------------------- /1043-partition-array-for-maximum-sum.md: -------------------------------------------------------------------------------- 1 | Youtube video explaining this: https://youtu.be/VAEqcS77iLo 2 | 3 | 4 | ```js 5 | /** 6 | * @param {number[]} A 7 | * @param {number} K 8 | * @return {number} 9 | */ 10 | var maxSumAfterPartitioning = function(A, K) { 11 | const result = [A[0]] 12 | for (let i = 1; i < A.length; i++) { 13 | let temp = A[i] + result[i - 1] // [ A[i] ] 14 | if (i > 0) { 15 | let maxItem = A[i] 16 | for (let j = i - 1; j >= 0 && j > i - K; j--) { 17 | maxItem = Math.max(maxItem, A[j]) 18 | temp = Math.max(temp, (i - j + 1) * maxItem + (j > 0 ? result[j - 1] : 0)) 19 | } 20 | } 21 | result.push(temp) 22 | } 23 | return result[A.length - 1] 24 | }; 25 | ``` 26 | -------------------------------------------------------------------------------- /1047-remove-all-adjacent-duplicates-in-string.md: -------------------------------------------------------------------------------- 1 | Here is my explanation on youtube: https://youtu.be/IXXPT9kmf0g 2 | 3 | ```js 4 | /** 5 | * @param {string} S 6 | * @return {string} 7 | */ 8 | var removeDuplicates = function(S) { 9 | const stack = [] 10 | 11 | for (let i = 0; i < S.length; i++) { 12 | if (stack[stack.length - 1] === S[i]) { 13 | stack.pop() 14 | } else { 15 | stack.push(S[i]) 16 | } 17 | } 18 | 19 | return stack.join('') 20 | }; 21 | ``` 22 | -------------------------------------------------------------------------------- /1053-previous-permutation-with-one-swap.md: -------------------------------------------------------------------------------- 1 | Me explaining this problem on youtube: https://youtu.be/yJGdxk5t3I8 2 | 3 | 4 | ```js 5 | /** 6 | * @param {number[]} A 7 | * @return {number[]} 8 | */ 9 | var prevPermOpt1 = function(A) { 10 | const swap = (i, j) => { 11 | const temp = A[i] 12 | A[i] = A[j] 13 | A[j] = temp 14 | } 15 | 16 | // 1. from right to left, find the first upward slope 17 | for (let i = A.length; i >= 0; i--) { 18 | if (A[i] < A[i - 1]) { 19 | // 2. from left to right, search for largest number which is not larger than the slope 20 | let target = i 21 | let j = i + 1 22 | while (A[j] < A[i - 1]) { 23 | if (A[j] !== A[j - 1]) { 24 | target = j 25 | } 26 | j += 1 27 | } 28 | 29 | // 3. swap 30 | swap(i - 1, target) 31 | break 32 | } 33 | } 34 | 35 | return A 36 | 37 | }; 38 | ``` 39 | -------------------------------------------------------------------------------- /1060-missing-element-in-sorted-array.md: -------------------------------------------------------------------------------- 1 | Me explaining this on youtube: https://youtu.be/YMSCmC-srzc 2 | ```js 3 | /** 4 | * @param {number[]} nums 5 | * @param {number} k 6 | * @return {number} 7 | */ 8 | 9 | // Time: O(n) 10 | // space: O(1) 11 | var missingElement = function(nums, k) { 12 | // loop through the element, and find the diff 13 | // if diff is enough, return result 14 | // if not, continue 15 | 16 | nums.push(Infinity) 17 | 18 | for (let i = 1; i < nums.length; i++) { 19 | const diff = nums[i] - nums[i - 1] 20 | if (diff - 1 >= k) { 21 | return nums[i - 1] + k 22 | } else { 23 | k -= diff - 1 24 | } 25 | } 26 | 27 | }; 28 | ``` 29 | -------------------------------------------------------------------------------- /107-binary-tree-level-order-traversal-ii.md: -------------------------------------------------------------------------------- 1 | Here is me explaining : https://youtu.be/-0i4BUs79ck 2 | 3 | 4 | ```js 5 | /** 6 | * Definition for a binary tree node. 7 | * function TreeNode(val, left, right) { 8 | * this.val = (val===undefined ? 0 : val) 9 | * this.left = (left===undefined ? null : left) 10 | * this.right = (right===undefined ? null : right) 11 | * } 12 | */ 13 | /** 14 | * @param {TreeNode} root 15 | * @return {number[][]} 16 | */ 17 | var levelOrderBottom = function(root) { 18 | if (root === null) return [] 19 | const result = [] 20 | 21 | const queue = [root] 22 | 23 | while (queue.length > 0) { 24 | queue.push(null) 25 | const layer = [] 26 | let head 27 | while (head = queue.shift()){ 28 | layer.push(head.val) 29 | if (head.left) queue.push(head.left) 30 | if (head.right) queue.push(head.right) 31 | } 32 | result.unshift(layer) 33 | } 34 | 35 | return result 36 | }; 37 | ``` 38 | -------------------------------------------------------------------------------- /108-convert-sorted-array-to-binary-search-tree.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * Definition for a binary tree node. 4 | * function TreeNode(val, left, right) { 5 | * this.val = (val===undefined ? 0 : val) 6 | * this.left = (left===undefined ? null : left) 7 | * this.right = (right===undefined ? null : right) 8 | * } 9 | */ 10 | /** 11 | * @param {number[]} nums 12 | * @return {TreeNode} 13 | */ 14 | var sortedArrayToBST = function (nums) { 15 | // isn't enough we just form a triangle? 16 | // nope. 17 | // but it makes sens to choose the center as root 18 | // recursive 19 | 20 | // return the root 21 | function impl(i, j) { 22 | if (i > j) return null; 23 | const m = Math.floor((i + j) / 2); 24 | const root = new TreeNode(nums[m]); 25 | root.left = impl(i, m - 1); 26 | root.right = impl(m + 1, j); 27 | return root; 28 | } 29 | return impl(0, nums.length - 1); 30 | }; 31 | ``` 32 | -------------------------------------------------------------------------------- /1108-defanging-an-ip-address.md: -------------------------------------------------------------------------------- 1 | me explaining this on youtube: https://youtu.be/kXYdQ3SHAx0 2 | 3 | 4 | ```js 5 | /** 6 | * @param {string} address 7 | * @return {string} 8 | */ 9 | // var defangIPaddr = function(address) { 10 | // return address.replace(/\./g, '[.]') 11 | // }; 12 | 13 | // Time: O(n) 14 | // var defangIPaddr = function(address) { 15 | // return address.split('.').join('[.]') 16 | // }; 17 | 18 | 19 | // Time: O(n) 20 | // Space: O(1) 21 | var defangIPaddr = function(address) { 22 | let result = '' 23 | let prevDotIndex = -1 24 | for (let i = 0; i < address.length + 1; i++) { 25 | if (i === address.length) { 26 | result += address.slice(prevDotIndex + 1, i) 27 | } else if (address[i] === '.') { 28 | result += address.slice(prevDotIndex + 1, i) 29 | result += '[.]' 30 | prevDotIndex = i 31 | } 32 | } 33 | return result 34 | }; 35 | ``` 36 | -------------------------------------------------------------------------------- /111-minimum-depth-of-binary-tree.md: -------------------------------------------------------------------------------- 1 | A video explaining this: https://youtu.be/GFcKai8Ra8s 2 | 3 | ```js 4 | /** 5 | * Definition for a binary tree node. 6 | * function TreeNode(val) { 7 | * this.val = val; 8 | * this.left = this.right = null; 9 | * } 10 | */ 11 | /** 12 | * @param {TreeNode} root 13 | * @return {number} 14 | */ 15 | var minDepth = function(root) { 16 | 17 | if (!root) { 18 | return 0 19 | } 20 | 21 | const queue = [[root, 1]] 22 | 23 | while (queue.length > 0) { 24 | const [node, depth] = queue.shift() 25 | 26 | if (!node.left && !node.right) { 27 | return depth 28 | } 29 | 30 | if (node.left) { 31 | queue.push([node.left, depth + 1]) 32 | } 33 | 34 | if (node.right) { 35 | queue.push([node.right, depth + 1]) 36 | } 37 | } 38 | }; 39 | ``` 40 | -------------------------------------------------------------------------------- /112-path-sum.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * Definition for a binary tree node. 4 | * function TreeNode(val, left, right) { 5 | * this.val = (val===undefined ? 0 : val) 6 | * this.left = (left===undefined ? null : left) 7 | * this.right = (right===undefined ? null : right) 8 | * } 9 | */ 10 | /** 11 | * @param {TreeNode} root 12 | * @param {number} targetSum 13 | * @return {boolean} 14 | */ 15 | var hasPathSum = function (root, targetSum) { 16 | if (root == null) return false; 17 | // DFS with a Sum 18 | const stack = [[root, 0]]; 19 | while (stack.length > 0) { 20 | const [node, sum] = stack.pop(); 21 | if (node.left != null) { 22 | stack.push([node.left, sum + node.val]); 23 | } 24 | if (node.right != null) { 25 | stack.push([node.right, sum + node.val]); 26 | } 27 | 28 | if ( 29 | node.left == null && 30 | node.right == null && 31 | node.val + sum === targetSum 32 | ) { 33 | return true; 34 | } 35 | } 36 | return false; 37 | }; 38 | ``` 39 | -------------------------------------------------------------------------------- /1122-relative-sort-array.md: -------------------------------------------------------------------------------- 1 | Here is my video explaining this: https://youtu.be/FvOau8OlngI 2 | 3 | ```js 4 | /** 5 | * @param {number[]} arr1 6 | * @param {number[]} arr2 7 | * @return {number[]} 8 | */ 9 | var relativeSortArray = function(arr1, arr2) { 10 | // 1. count numbers in arr1 11 | // 2. collect numbers in arr1 based on arr2 12 | // 3. append the rest numbers 13 | 14 | const count = new Map() 15 | for (let num of arr1) { 16 | if (count.has(num)) { 17 | count.set(num, count.get(num) + 1) 18 | } else { 19 | count.set(num, 1) 20 | } 21 | } 22 | 23 | const result = [] 24 | 25 | for (let num of arr2) { 26 | if (count.has(num)) { 27 | let amount = count.get(num) 28 | while (amount > 0) { 29 | result.push(num) 30 | amount -= 1 31 | } 32 | count.delete(num) 33 | } 34 | } 35 | 36 | // the rest numbers 37 | for (let num = 0; num <= 1000; num++) { 38 | if (count.has(num)) { 39 | let amount = count.get(num) 40 | while (amount > 0) { 41 | result.push(num) 42 | amount -= 1 43 | } 44 | count.delete(num) 45 | } 46 | } 47 | 48 | return result 49 | }; 50 | ``` 51 | -------------------------------------------------------------------------------- /1123-lowest-common-ancestor-of-deepest-leaves.md: -------------------------------------------------------------------------------- 1 | me explaining this on youtube: https://youtu.be/mnNKUC7gOd8 2 | 3 | 4 | ```js 5 | /** 6 | * Definition for a binary tree node. 7 | * function TreeNode(val, left, right) { 8 | * this.val = (val===undefined ? 0 : val) 9 | * this.left = (left===undefined ? null : left) 10 | * this.right = (right===undefined ? null : right) 11 | * } 12 | */ 13 | /** 14 | * @param {TreeNode} root 15 | * @return {TreeNode} 16 | */ 17 | 18 | /* 19 | 20 | 1 0 21 | 2 3 1 22 | 4 2 23 | 24 | 25 | */ 26 | 27 | // Time: O(n) 28 | // Space: worst call stack O(n) 29 | var lcaDeepestLeaves = function(root) { 30 | 31 | // [height of leaf node, node] 32 | let result = [0, root] 33 | 34 | // return depth (how many layers of node the tree has) 35 | const walk = (node, depth) => { 36 | 37 | if (node === null) return 0 38 | 39 | const leftHeight = walk(node.left, depth + 1) 40 | const rightHeight = walk(node.right, depth + 1) 41 | 42 | // it is a possible candiate 43 | if (leftHeight === rightHeight) { 44 | // compare the depth of leaf node 45 | const depthOfLeafNode = depth + leftHeight 46 | if (depthOfLeafNode >= result[0]) { 47 | result[0] = depthOfLeafNode 48 | result[1] = node 49 | } 50 | } 51 | return Math.max(leftHeight, rightHeight) + 1 52 | } 53 | 54 | walk(root, 0) 55 | 56 | return result[1] 57 | 58 | }; 59 | 60 | 61 | 62 | 63 | 64 | 65 | ``` 66 | -------------------------------------------------------------------------------- /117-populating-next-right-pointers-in-each-node-ii.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * // Definition for a Node. 4 | * function Node(val, left, right, next) { 5 | * this.val = val === undefined ? null : val; 6 | * this.left = left === undefined ? null : left; 7 | * this.right = right === undefined ? null : right; 8 | * this.next = next === undefined ? null : next; 9 | * }; 10 | */ 11 | 12 | /** 13 | * @param {Node} root 14 | * @return {Node} 15 | */ 16 | var connect = function (root) { 17 | if (root == null) return root; 18 | // BFS 19 | // each time we process layer by layer 20 | // and set up next 21 | 22 | const queue = [root]; 23 | while (queue.length > 0) { 24 | let count = queue.length; 25 | let prev = null; 26 | while (count > 0) { 27 | const head = queue.shift(); 28 | if (prev == null) { 29 | prev = head; 30 | } else { 31 | prev.next = head; 32 | prev = head; 33 | } 34 | 35 | if (head.left) { 36 | queue.push(head.left); 37 | } 38 | 39 | if (head.right) { 40 | queue.push(head.right); 41 | } 42 | count -= 1; 43 | } 44 | } 45 | return root; 46 | }; 47 | ``` 48 | -------------------------------------------------------------------------------- /119-pascals-triangle-ii.md: -------------------------------------------------------------------------------- 1 | a video explaining this: https://youtu.be/_1QNutln2G4 2 | 3 | 4 | ```js 5 | /** 6 | * @param {number} rowIndex 7 | * @return {number[]} 8 | */ 9 | var getRow = function(rowIndex) { 10 | if (rowIndex === 0) { 11 | return [1] 12 | } 13 | 14 | const prevRow = getRow(rowIndex - 1) 15 | const result = [1] 16 | prevRow.reduce((prev, item) => { 17 | if (prev !== 0) { 18 | result.push(prev + item) 19 | } 20 | return item 21 | }, 0) 22 | result.push(1) 23 | return result 24 | }; 25 | ``` 26 | -------------------------------------------------------------------------------- /121-best-time-to-buy-and-sell-stock.md: -------------------------------------------------------------------------------- 1 | Here is my video explaining it: https://youtu.be/l15PJ62EhSk 2 | 3 | ```js 4 | /** 5 | * @param {number[]} prices 6 | * @return {number} 7 | */ 8 | var maxProfit = function (prices) { 9 | // buy at min, sell at max, max is after min 10 | // to buy at i, search sell at j cost n -i 11 | // O(n ^ 2) 12 | 13 | // loop through the price 14 | // keep track of the min 15 | // get profit at each index, collect the max 16 | 17 | let minPrice = prices[0]; 18 | 19 | let maxProfit = 0; 20 | 21 | for (let price of prices) { 22 | if (price < minPrice) { 23 | minPrice = price; 24 | } 25 | 26 | // best profit selling at this price 27 | const profit = Math.max(price - minPrice, 0); 28 | maxProfit = Math.max(maxProfit, profit); 29 | } 30 | 31 | return maxProfit; 32 | }; 33 | ``` 34 | -------------------------------------------------------------------------------- /1213-intersection-of-three-sorted-arrays.md: -------------------------------------------------------------------------------- 1 | Me explaining this on youtube: https://youtu.be/LY1DuGOH-gg 2 | 3 | 4 | ```js 5 | /** 6 | * @param {number[]} arr1 7 | * @param {number[]} arr2 8 | * @param {number[]} arr3 9 | * @return {number[]} 10 | */ 11 | var arraysIntersection = function(arr1, arr2, arr3) { 12 | // brute force 13 | // suppose all n integers 14 | // O(n * lg(n) *2) => O(n * lg(n)) 15 | 16 | // check the head until one is dead 17 | // pop(shift) the smaller ones 18 | 19 | // O(n) 20 | // O(n) 21 | const result = [] 22 | 23 | while (arr1.length > 0 && arr2.length > 0 && arr3.length > 0) { 24 | const head1 = arr1[0] 25 | const head2 = arr2[0] 26 | const head3 = arr3[0] 27 | 28 | if (head1 === head2 && head1 === head3) { 29 | result.push(head1) 30 | arr1.shift() 31 | arr2.shift() 32 | arr3.shift() 33 | } else { 34 | // 1 1 2 35 | // 1 2 2 36 | if (head1 <= head2 && head1 <= head3) { 37 | arr1.shift() 38 | } 39 | 40 | if (head2 <= head1 && head2 <= head3) { 41 | arr2.shift() 42 | } 43 | 44 | if (head3 <= head1 && head3 <= head2) { 45 | arr3.shift() 46 | } 47 | } 48 | } 49 | 50 | return result 51 | }; 52 | ``` 53 | -------------------------------------------------------------------------------- /1219-path-with-maximum-gold.md: -------------------------------------------------------------------------------- 1 | Me explaining this on youtube: https://youtu.be/CBediynjFxE 2 | ```js 3 | /** 4 | * @param {number[][]} grid 5 | * @return {number} 6 | */ 7 | 8 | // Time: worst (m * n)! 9 | // space: O(m * n + m * n) => O(m * n) 10 | var getMaximumGold = function(grid) { 11 | // use flag to keep track of the availitity of each cells 12 | const isAvaiable = grid.map(row => row.map(cell => cell !== 0)) 13 | 14 | let max = 0 15 | 16 | let sum = 0 17 | 18 | const rows = grid.length 19 | const cols = grid[0].length 20 | // recursion try to choose a cell 21 | const walk = (i, j) => { 22 | if (i < 0 || i > rows - 1 || j < 0 || j > cols - 1 || !isAvaiable[i][j]) return 23 | 24 | // add to sum 25 | sum += grid[i][j] 26 | 27 | // update max 28 | max = Math.max(sum, max) 29 | 30 | // update availibity 31 | isAvaiable[i][j] = false 32 | 33 | // walk around 34 | walk(i, j + 1) 35 | walk(i + 1, j) 36 | walk(i, j - 1) 37 | walk(i - 1, j) 38 | 39 | // update sum 40 | sum -= grid[i][j] 41 | 42 | // update availibity 43 | isAvaiable[i][j] = true 44 | } 45 | 46 | // start on all the possible gold cell 47 | grid.forEach((row, i) => { 48 | row.forEach((cell, j) => walk(i, j)) 49 | }) 50 | 51 | return max 52 | }; 53 | ``` 54 | -------------------------------------------------------------------------------- /122-best-time-to-buy-and-sell-stock-ii.md: -------------------------------------------------------------------------------- 1 | Here is my video of explaining it: https://youtu.be/2AhTLGxptlA 2 | 3 | ```js 4 | /** 5 | * @param {number[]} prices 6 | * @return {number} 7 | */ 8 | var maxProfit = function (prices) { 9 | // point Buy => soon to go up, next is bigger 10 | // point Sell => soon to go down, next is smaller 11 | 12 | let profit = 0; 13 | 14 | for (let i = 0; i < prices.length; i++) { 15 | // if it the point Buy, we search next to the point sell 16 | if (i === 0 || prices[i + 1] > prices[i]) { 17 | // this is a point to buy 18 | // search for the point sell, it might be itself 19 | let j = i; 20 | while (prices[j + 1] >= prices[j]) { 21 | j += 1; 22 | } 23 | 24 | // i j might be the same, skip 25 | if (j > i) { 26 | profit += prices[j] - prices[i]; 27 | } 28 | 29 | i = j; 30 | } 31 | } 32 | 33 | return profit; 34 | }; 35 | ``` 36 | 37 | since we can sell immediately, following code is simpler 38 | 39 | ```js 40 | /** 41 | * @param {number[]} prices 42 | * @return {number} 43 | */ 44 | var maxProfit = function (prices) { 45 | // basically get all the profit from 2 consecutive increasing price 46 | 47 | // if a price is larger than previous one collect the profit 48 | // otherise do nothing 49 | let profit = 0; 50 | for (let i = 1; i < prices.length; i++) { 51 | if (prices[i] > prices[i - 1]) { 52 | profit += prices[i] - prices[i - 1]; 53 | } 54 | } 55 | return profit; 56 | }; 57 | ``` 58 | -------------------------------------------------------------------------------- /123-best-time-to-buy-and-sell-stock-iii.md: -------------------------------------------------------------------------------- 1 | 2 | Here is a video explaining it: https://youtu.be/TF_8mA6jw7A 3 | 4 | 5 | ```js 6 | /** 7 | * @param {number[]} prices 8 | * @return {number} 9 | */ 10 | var maxProfit = function(prices) { 11 | // [7 1 5 3 6 4 8] 12 | 13 | // Array 14 | // Array 15 | 16 | const maxProfitFromLeftAt = new Array(prices.length).fill(0) 17 | const maxProfitFromRightAt = new Array(prices.length + 1).fill(0) 18 | 19 | let minPrice = prices[0] 20 | for (let i = 1; i < prices.length; i++) { 21 | const price = prices[i] 22 | if (price < minPrice) { 23 | minPrice = price 24 | } 25 | 26 | const profit = price - minPrice 27 | maxProfitFromLeftAt[i] = Math.max(maxProfitFromLeftAt[i - 1], profit) 28 | } 29 | 30 | 31 | let result = prices.length > 0 ? maxProfitFromLeftAt[prices.length - 1] : 0 32 | 33 | let maxPrice = prices[prices.length - 1] 34 | for (let i = prices.length - 2; i > 0; i--) { 35 | const price = prices[i] 36 | if (price > maxPrice) { 37 | maxPrice = price 38 | } 39 | 40 | const profit = maxPrice - price 41 | const maxProfit = Math.max(maxProfitFromRightAt[i + 1], profit) 42 | maxProfitFromRightAt[i] = maxProfit 43 | 44 | 45 | result = Math.max(result, maxProfit + maxProfitFromLeftAt[i - 1]) 46 | } 47 | 48 | 49 | return result 50 | }; 51 | ``` 52 | -------------------------------------------------------------------------------- /1245-tree-diameter.md: -------------------------------------------------------------------------------- 1 | watch my video of explanation: https://youtu.be/sLEaLSA4KOg 2 | 3 | ```js 4 | /** 5 | * @param {number[][]} edges 6 | * @return {number} 7 | */ 8 | 9 | // recursion 10 | 11 | // Time: O(n) 12 | // Space: O(n) 13 | var treeDiameter = function(edges) { 14 | // process the input first 15 | const children = [] 16 | 17 | for (let [from, to] of edges) { 18 | if (!children[from]) children[from] = [] 19 | children[from].push(to) 20 | } 21 | 22 | // return [diameter, height] 23 | const walk = (index) => { 24 | if (children[index] === undefined) return [0, 1] 25 | 26 | // loop through children to find 27 | // 1. max dimater 28 | // 2. top 2 heights 29 | 30 | let maxDiameter = 0 31 | let top2Heights = [0, 0] // desc 32 | 33 | for (let childIndex of children[index]) { 34 | const [diameter, height] = walk(childIndex) 35 | maxDiameter = Math.max(diameter, maxDiameter) 36 | 37 | if (height >= top2Heights[0]) { 38 | top2Heights[1] = top2Heights[0] 39 | top2Heights[0] = height 40 | } else if (height > top2Heights[1]) { 41 | top2Heights[1] = height 42 | } 43 | } 44 | 45 | return [ 46 | Math.max(maxDiameter, top2Heights[0] + top2Heights[1]), 47 | Math.max(...top2Heights) + 1 48 | ] 49 | } 50 | 51 | return walk(0)[0] 52 | 53 | }; 54 | ``` 55 | -------------------------------------------------------------------------------- /1249-minimum-remove-to-make-valid-parentheses.md: -------------------------------------------------------------------------------- 1 | Here is my video of explaining it on youtube: https://youtu.be/TvAVIJnfmVA 2 | 3 | ```js 4 | const minRemoveToMakeValid = (s) => { 5 | // two way parse 6 | let countOfLeft = 0 7 | let countOfRight = 0 8 | let resultFor1Pass = '' 9 | 10 | for (let i = 0; i < s.length; i++) { 11 | if (s[i] === '(') { 12 | countOfLeft += 1 13 | } else if (s[i] === ')') { 14 | if (countOfRight === countOfLeft) { 15 | continue 16 | } 17 | countOfRight += 1 18 | } 19 | resultFor1Pass += s[i] 20 | } 21 | 22 | // when it is done, extra left parentheses are to be removed 23 | let extraLeftParenCount = countOfLeft - countOfRight 24 | 25 | if (extraLeftParenCount === 0) return resultFor1Pass 26 | 27 | let result = '' 28 | 29 | for (let j = resultFor1Pass.length - 1; j > -1 ; j--) { 30 | if (resultFor1Pass[j] === '(') { 31 | extraLeftParenCount -= 1 32 | 33 | if (extraLeftParenCount === 0) { 34 | result = resultFor1Pass.slice(0, j) + result 35 | break 36 | } 37 | } else { 38 | result = resultFor1Pass[j] + result 39 | } 40 | 41 | } 42 | 43 | return result 44 | } 45 | ``` 46 | -------------------------------------------------------------------------------- /125-valid-palindrome.md: -------------------------------------------------------------------------------- 1 | A video explaining this: https://youtu.be/AB1r7Xf7lVU 2 | 3 | ```js 4 | /** 5 | * @param {string} s 6 | * @return {boolean} 7 | */ 8 | var isPalindrome = function (s) { 9 | if (s.length === 0) return true; 10 | const regAlphanumeric = /[0-9a-zA-Z]/; 11 | 12 | let i = 0; 13 | let j = s.length - 1; 14 | 15 | while (i < j) { 16 | while (!regAlphanumeric.test(s[i]) && i < s.length - 1) { 17 | i += 1; 18 | } 19 | 20 | while (!regAlphanumeric.test(s[j]) && j > 0) { 21 | j -= 1; 22 | } 23 | 24 | if (i >= j) return true; 25 | 26 | if (s[i].toLowerCase() !== s[j].toLowerCase()) return false; 27 | 28 | i += 1; 29 | j -= 1; 30 | } 31 | 32 | return true; 33 | }; 34 | ``` 35 | -------------------------------------------------------------------------------- /128-longest-consecutive-sequence.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {number[]} nums 4 | * @return {number} 5 | */ 6 | // O(N) -> for each move we'll remove one item from the set 7 | // O(N) 8 | var longestConsecutive = function (nums) { 9 | // for each number 10 | // we can get the streak it belongs 11 | // by +1/-1 12 | const set = new Set(nums); 13 | let streak = 0; 14 | 15 | for (const num of nums) { 16 | set.delete(num); 17 | let start = num; 18 | let end = num; 19 | while (set.has(end + 1)) { 20 | end += 1; 21 | set.delete(end); 22 | } 23 | while (set.has(start - 1)) { 24 | start -= 1; 25 | set.delete(start); 26 | } 27 | 28 | streak = Math.max(streak, end - start + 1); 29 | } 30 | 31 | return streak; 32 | }; 33 | ``` 34 | -------------------------------------------------------------------------------- /129-sum-root-to-leaf-numbers.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * Definition for a binary tree node. 4 | * function TreeNode(val, left, right) { 5 | * this.val = (val===undefined ? 0 : val) 6 | * this.left = (left===undefined ? null : left) 7 | * this.right = (right===undefined ? null : right) 8 | * } 9 | */ 10 | /** 11 | * @param {TreeNode} root 12 | * @return {number} 13 | */ 14 | var sumNumbers = function (root) { 15 | // we can track the path during DFS 16 | // collect result when found leaf node 17 | let sum = 0; 18 | let currentSum = 0; 19 | const walk = (node) => { 20 | if (node == null) return; 21 | 22 | currentSum = currentSum * 10 + node.val; 23 | 24 | if (node.left == null && node.right == null) { 25 | sum += currentSum; 26 | } 27 | 28 | walk(node.left); 29 | walk(node.right); 30 | currentSum = (currentSum - node.val) / 10; 31 | }; 32 | 33 | walk(root); 34 | 35 | return sum; 36 | }; 37 | ``` 38 | -------------------------------------------------------------------------------- /13-roman-to-integer.md: -------------------------------------------------------------------------------- 1 | ## 1. 2 | 3 | A video explaining this: https://youtu.be/fE3vsgt2bC8 4 | 5 | ```js 6 | const map = { 7 | I: 1, 8 | V: 5, 9 | X: 10, 10 | L: 50, 11 | C: 100, 12 | D: 500, 13 | M: 1000, 14 | }; 15 | 16 | /** 17 | * @param {string} s 18 | * @return {number} 19 | */ 20 | var romanToInt = function (s) { 21 | let sum = 0; 22 | 23 | for (let i = 0; i < s.length; i++) { 24 | let sign = 1; 25 | if ( 26 | i < s.length - 1 && 27 | (map[s[i + 1]] === map[s[i]] * 5 || map[s[i + 1]] === map[s[i]] * 10) 28 | ) { 29 | sign *= -1; 30 | } 31 | 32 | sum += sign * map[s[i]]; 33 | } 34 | 35 | return sum; 36 | }; 37 | ``` 38 | 39 | ## 2 40 | 41 | ```js 42 | /** 43 | * @param {string} s 44 | * @return {number} 45 | */ 46 | var romanToInt = function (s) { 47 | // just sume them up, except the case of 4 and 9 48 | const map = { 49 | I: 1, 50 | V: 5, 51 | X: 10, 52 | L: 50, 53 | C: 100, 54 | D: 500, 55 | M: 1000, 56 | }; 57 | 58 | let result = 0; 59 | for (let i = 0; i < s.length; i++) { 60 | const char = s[i]; 61 | const charNext = s[i + 1]; 62 | const num = map[char]; 63 | const numNext = map[charNext]; 64 | 65 | if (num < numNext) { 66 | result += numNext - num; 67 | i += 1; 68 | } else { 69 | result += num; 70 | } 71 | } 72 | 73 | return result; 74 | }; 75 | ``` 76 | -------------------------------------------------------------------------------- /1347-minimum-number-of-steps-to-make-two-strings-anagram.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {string} s 4 | * @param {string} t 5 | * @return {number} 6 | */ 7 | var minSteps = function (s, t) { 8 | // count? and differentiate? 9 | const countS = count(s); 10 | const countT = count(t); 11 | let coutOfCharToAdd = 0; 12 | let coutOfCharToDelete = 0; 13 | for (let i = 0; i < countS.length; i++) { 14 | if (countT[i] > countS[i]) { 15 | coutOfCharToDelete += countT[i] - countS[i]; 16 | } 17 | 18 | if (countT[i] < countS[i]) { 19 | coutOfCharToAdd += countS[i] - countT[i]; 20 | } 21 | } 22 | 23 | // we can replace the character from add to remove 24 | //coutOfCharToDelete must be the same as coutOfCharToAdd 25 | // because s and t are of same length 26 | return coutOfCharToAdd; 27 | }; 28 | 29 | function count(s) { 30 | const result = new Array(26).fill(0); 31 | const charCodeA = "a".charCodeAt(0); 32 | for (let i = 0; i < s.length; i++) { 33 | const charCode = s.charCodeAt(i); 34 | result[charCode - charCodeA] += 1; 35 | } 36 | return result; 37 | } 38 | ``` 39 | -------------------------------------------------------------------------------- /136-single-number.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {number[]} nums 4 | * @return {number} 5 | */ 6 | var singleNumber = function (nums) { 7 | return nums.reduce((result, num) => result ^ num); 8 | }; 9 | ``` 10 | -------------------------------------------------------------------------------- /137-single-number-ii.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {number[]} nums 4 | * @return {number} 5 | */ 6 | var singleNumber = function (nums) { 7 | let visited = new Set(); 8 | let candidates = new Set(); 9 | for (const num of nums) { 10 | if (visited.has(num)) { 11 | candidates.delete(num); 12 | } else { 13 | candidates.add(num); 14 | visited.add(num); 15 | } 16 | } 17 | return candidates.values().next().value; 18 | }; 19 | ``` 20 | -------------------------------------------------------------------------------- /1379-find-a-corresponding-node-of-a-binary-tree-in-a-clone-of-that-tree.md: -------------------------------------------------------------------------------- 1 | 2 | Me explaining it on youtube: https://youtu.be/tP2nCq9L1PQ 3 | 4 | ```js 5 | /** 6 | * Definition for a binary tree node. 7 | * function TreeNode(val) { 8 | * this.val = val; 9 | * this.left = this.right = null; 10 | * } 11 | */ 12 | /** 13 | * @param {TreeNode} original 14 | * @param {TreeNode} cloned 15 | * @param {TreeNode} target 16 | * @return {TreeNode} 17 | */ 18 | // Time: O(n) 19 | // Space: height of tree O(height) 20 | var getTargetCopy = function(original, cloned, target) { 21 | // 1. need to search target => O(n) 22 | // 2. pass down the corresponding node to the recursion 23 | 24 | const walk = (node, clonedNode, target) => { 25 | if (node === target) return clonedNode 26 | 27 | let result = null 28 | 29 | if (node.left !== null) { 30 | result = walk(node.left, clonedNode.left, target) 31 | } 32 | 33 | if (result !== null) return result 34 | 35 | if (node.right !== null) { 36 | result = walk(node.right, clonedNode.right, target) 37 | } 38 | 39 | return result 40 | } 41 | 42 | return walk(original, cloned, target) 43 | }; 44 | ``` 45 | -------------------------------------------------------------------------------- /1394-find-lucky-integer-in-an-array.md: -------------------------------------------------------------------------------- 1 | Me explaining it on youtube: https://youtu.be/wSqQmPBN8uY 2 | 3 | ```js 4 | /** 5 | * @param {number[]} arr 6 | * @return {number} 7 | */ 8 | 9 | // Time: O(n) 10 | // Space: O(n) 11 | // var findLucky = function(arr) { 12 | // // count all and find the largest luck number 13 | // const countMap = new Map() 14 | // for (let num of arr) { 15 | // if (countMap.has(num)) { 16 | // countMap.set(num, countMap.get(num) + 1) 17 | // } else { 18 | // countMap.set(num, 1) 19 | // } 20 | // } 21 | 22 | // let max = -1 23 | 24 | // for (let [num, count] of countMap) { 25 | // if (num === count && num > max) { 26 | // max = num 27 | // } 28 | // } 29 | 30 | // return max 31 | // }; 32 | 33 | // sort first 34 | // Time: O(n log n) 35 | // Space: O(1) 36 | var findLucky = function(arr) { 37 | arr.sort((a, b) => b - a) 38 | 39 | // 1, 2, 2, 3, 3 40 | // check at arr[arr[0]] !== , and arr[arr[0] - 1] === arr[0] 41 | 42 | // 4 3 2 2 43 | let max = -1 44 | let i = 0 45 | while (i < arr.length) { 46 | const num = arr[i] 47 | if (arr[i + num - 1] === num && arr[i + num] !== num) { 48 | max = num 49 | break 50 | } 51 | // if not move to the next different number 52 | i += 1 53 | while (arr[i] === num) { 54 | i += 1 55 | } 56 | } 57 | 58 | return max 59 | }; 60 | ``` 61 | -------------------------------------------------------------------------------- /14-longest-common-prefix.md: -------------------------------------------------------------------------------- 1 | ## 1 2 | 3 | A video explaining this: https://youtu.be/enprkdKA7uU 4 | 5 | ```js 6 | /** 7 | * @param {string[]} strs 8 | * @return {string} 9 | */ 10 | var longestCommonPrefix = function (strs) { 11 | if (strs.length === 0) { 12 | return ""; 13 | } 14 | 15 | let isShortestStrMet = false; 16 | let i = 0; 17 | for (; i < strs[0].length; i++) { 18 | if (isShortestStrMet) { 19 | break; 20 | } 21 | 22 | const char = strs[0][i]; 23 | let isSame = true; 24 | for (let j = 0; j < strs.length; j++) { 25 | isShortestStrMet = strs[j][i] === undefined; 26 | 27 | if (strs[j][i] !== char) { 28 | isSame = false; 29 | break; 30 | } 31 | } 32 | 33 | if (!isSame) { 34 | break; 35 | } 36 | } 37 | 38 | return strs[0].slice(0, i); 39 | }; 40 | ``` 41 | 42 | ## 2 43 | 44 | ```js 45 | var longestCommonPrefix = function (strs) { 46 | let result = ""; 47 | 48 | let i = 0; 49 | while (true) { 50 | let char; 51 | for (const str of strs) { 52 | if (i >= str.length) { 53 | return result; 54 | } 55 | if (char == null) { 56 | char = str[i]; 57 | } else if (char !== str[i]) { 58 | return result; 59 | } 60 | } 61 | result += char; 62 | i += 1; 63 | } 64 | }; 65 | ``` 66 | -------------------------------------------------------------------------------- /140-word-break-ii.md: -------------------------------------------------------------------------------- 1 | Here is me explaining this on youtube: https://youtu.be/sa3MVdy42MU 2 | 3 | ```js 4 | /** 5 | * @param {string} s 6 | * @param {string[]} wordDict 7 | * @return {string[]} 8 | */ 9 | 10 | // Time: 11 | // T(i) = T(i - 1) + T(i - 2) + ....T(1) 12 | // = 2T(i - 1) 13 | // = O(2^n) 14 | // Space 15 | 16 | // S(i) = S(i - 1) + s(i - 2) + .. S(1) 17 | // = 2 ^ 1(s( i - 2) + s(i - 3) ... S(1)) 18 | // = 2 ^ 2(s( i - 3) + ....S(1)) 19 | // = 2 ^ (i - 2) (S(1)) 20 | // = O(2 ^ n) 21 | // 0 22 | // 1 23 | // 2 24 | // 3 25 | // 2 26 | // 3 27 | // aaaaaaaaaaab 28 | // a aa aaa aaaaaa 29 | var wordBreak = function(s, wordDict) { 30 | const cache = new Map() 31 | // return string[] 32 | const walk = (i) => { 33 | if (cache.has(i)) return cache.get(i) 34 | if (i >= s.length) return [] 35 | 36 | const matches = [] 37 | // search for words to match 38 | for (let word of wordDict) { 39 | const length = word.length 40 | const nextIndex = i + length 41 | if (s.slice(i, i + length) === word) { 42 | if (nextIndex >= s.length) { 43 | matches.push(word) 44 | } else { 45 | const nextMatches = walk(i + length) 46 | for (let sentence of nextMatches) { 47 | matches.push(word + ' ' + sentence) 48 | } 49 | } 50 | } 51 | } 52 | cache.set(i, matches) 53 | return matches 54 | } 55 | 56 | return walk(0) 57 | }; 58 | ``` 59 | -------------------------------------------------------------------------------- /141-linked-list-cycle.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * Definition for singly-linked list. 4 | * function ListNode(val) { 5 | * this.val = val; 6 | * this.next = null; 7 | * } 8 | */ 9 | 10 | /** 11 | * @param {ListNode} head 12 | * @return {boolean} 13 | */ 14 | var hasCycle = function (head) { 15 | if (head == null) return false; 16 | let pSlow = head; 17 | let pFast = head; 18 | while (pFast != null) { 19 | pSlow = pSlow.next; 20 | pFast = pFast.next?.next; 21 | if (pSlow === pFast) { 22 | return true; 23 | } 24 | } 25 | return false; 26 | }; 27 | ``` 28 | -------------------------------------------------------------------------------- /1424-diagonal-traverse-ii.md: -------------------------------------------------------------------------------- 1 | Me explaining this on youtube: https://youtu.be/4I9TFHyFUNY 2 | 3 | ```js 4 | /** 5 | * @param {number[][]} nums 6 | * @return {number[]} 7 | */ 8 | 9 | 10 | // Time: O(element count) + O(diagonal count) => worst O(m n) 11 | // Space: worst O(m n) 12 | var findDiagonalOrder = function(matrix) { 13 | const rows = matrix.length 14 | const cols = matrix[0].length 15 | const diagonals = [] 16 | 17 | for (let i = 0; i < rows; i++) { 18 | for (let j = 0; j < matrix[i].length; j++) { 19 | const num = matrix[i][j] 20 | if (num === undefined) break 21 | const key = i + j 22 | if (diagonals[key]) { 23 | diagonals[key].unshift(num) 24 | } else { 25 | diagonals[key] = [num] 26 | } 27 | } 28 | } 29 | 30 | const result = [] 31 | for (let diagonal of diagonals) { 32 | if (diagonal) { 33 | result.push(...diagonal) 34 | } 35 | } 36 | return result 37 | }; 38 | ``` 39 | -------------------------------------------------------------------------------- /1436-destination-city.md: -------------------------------------------------------------------------------- 1 | My explaination here: https://youtu.be/aktWSwqhYRQ 2 | ```js 3 | /** 4 | * @param {string[][]} paths 5 | * @return {string} 6 | */ 7 | 8 | // merge the paths 9 | // Time: O(n^2) 10 | // Space: O(1) 11 | // var destCity = function(paths) { 12 | // while (paths.length > 1) { 13 | // const head = paths.shift() 14 | 15 | // for (let i = 0; i < paths.length; i++) { 16 | // // if they could be merged 17 | // if (head[0] === paths[i][1]) { 18 | // paths[i][1] = head[1] 19 | // break 20 | // } 21 | 22 | // if (head[1] === paths[i][0]) { 23 | // paths[i][0] = head[0] 24 | // break 25 | // } 26 | // } 27 | // } 28 | // return paths[0][1] 29 | // }; 30 | 31 | 32 | // collect the result directly 33 | 34 | // O(n) + worst O(n) => O(n) 35 | // Space: O(n) 36 | var destCity = function(paths) { 37 | 38 | const map = new Map(paths) 39 | 40 | for (let anyPath of map) { 41 | let start = anyPath[1] 42 | while (map.has(start)) { 43 | start = map.get(start) 44 | } 45 | 46 | return start 47 | break 48 | } 49 | }; 50 | ``` 51 | -------------------------------------------------------------------------------- /1437-check-if-all-1s-are-at-least-length-k-places-away.md: -------------------------------------------------------------------------------- 1 | Me explaining this on youtube: https://youtu.be/6hYEANDwRTw 2 | ```js 3 | /** 4 | * @param {number[]} nums 5 | * @param {number} k 6 | * @return {boolean} 7 | */ 8 | // Time: O(n) 9 | // Space: O(1) 10 | var kLengthApart = function(nums, k) { 11 | let prevOneIndex = -Infinity 12 | 13 | for (let i = 0; i < nums.length; i++) { 14 | if (nums[i] === 1) { 15 | const distance = i - prevOneIndex - 1 16 | if (distance < k) { 17 | return false 18 | } 19 | 20 | prevOneIndex = i 21 | } 22 | } 23 | 24 | return true 25 | }; 26 | ``` 27 | -------------------------------------------------------------------------------- /1438-longest-continuous-subarray-with-absolute-diff-less-than-or-equal-to-limit.md: -------------------------------------------------------------------------------- 1 | Me explaining this on youtube: https://youtu.be/fVW5pa2JMwA 2 | 3 | ```js 4 | /** 5 | * @param {number[]} nums 6 | * @param {number} limit 7 | * @return {number} 8 | */ 9 | var longestSubarray = function(nums, limit) { 10 | // f(i) => longest subarray which ends at i 11 | // f(i + 1) 12 | // = 1 + K: maximum length from i(backword0), for i - K = 1 <= j <= i , (j, i + 1) is valid, break at failure or k === f(i) 13 | 14 | const longestSubarrayEndsAt = Array(nums.length).fill(1) 15 | 16 | let max = 1 17 | 18 | for (let i = 1; i < nums.length; i++) { 19 | const prev = longestSubarrayEndsAt[i - 1] 20 | let k = i - 1 21 | while (k >= i - 1 - prev + 1) { 22 | const diff = Math.abs(nums[i] - nums[k]) 23 | if (diff > limit) { 24 | break 25 | } 26 | 27 | k -= 1 28 | } 29 | 30 | max = Math.max(max, i - k) 31 | longestSubarrayEndsAt[i] = i - k 32 | } 33 | return max 34 | }; 35 | ``` 36 | -------------------------------------------------------------------------------- /144-binary-tree-preorder-traversal.md: -------------------------------------------------------------------------------- 1 | Here is my video of explainining it: https://youtu.be/u4O5XqBti2I 2 | ```js 3 | /** 4 | * Definition for a binary tree node. 5 | * function TreeNode(val, left, right) { 6 | * this.val = (val===undefined ? 0 : val) 7 | * this.left = (left===undefined ? null : left) 8 | * this.right = (right===undefined ? null : right) 9 | * } 10 | */ 11 | /** 12 | * @param {TreeNode} root 13 | * @return {number[]} 14 | */ 15 | var preorderTraversal = function(root) { 16 | const result = [] 17 | 18 | if (root === null) return result 19 | 20 | const stack = [root] 21 | 22 | while (stack.length) { 23 | const top = stack.pop() 24 | result.push(top.val) 25 | if (top.right) stack.push(top.right) 26 | if (top.left) stack.push(top.left) 27 | } 28 | 29 | return result 30 | } 31 | ``` 32 | -------------------------------------------------------------------------------- /145-binary-tree-postorder-traversal.md: -------------------------------------------------------------------------------- 1 | My youtube video explaining this: https://youtu.be/3rVjpFVvVA8 2 | 3 | ```js 4 | /** 5 | * Definition for a binary tree node. 6 | * function TreeNode(val, left, right) { 7 | * this.val = (val===undefined ? 0 : val) 8 | * this.left = (left===undefined ? null : left) 9 | * this.right = (right===undefined ? null : right) 10 | * } 11 | */ 12 | /** 13 | * @param {TreeNode} root 14 | * @return {number[]} 15 | */ 16 | var postorderTraversal = function(root) { 17 | const result = [] 18 | 19 | if (root === null) return result 20 | 21 | const stack = [] 22 | 23 | const pushStack = (node) => { 24 | while (node) { 25 | stack.push(node) 26 | node = node.left 27 | } 28 | } 29 | 30 | pushStack(root) 31 | 32 | while (stack.length > 0) { 33 | let top = stack[stack.length - 1] 34 | 35 | if (top === null) { 36 | stack.pop() 37 | top = stack.pop() 38 | result.push(top.val) 39 | } else { 40 | if (top.right) { 41 | stack.push(null) 42 | pushStack(top.right) 43 | } else { 44 | stack.pop() 45 | result.push(top.val) 46 | } 47 | } 48 | } 49 | 50 | return result 51 | }; 52 | ``` 53 | -------------------------------------------------------------------------------- /1458-max-dot-product-of-two-subsequences.md: -------------------------------------------------------------------------------- 1 | 2 | Here is me explaining on youtube: https://youtu.be/M39Ap4euAQI 3 | 4 | ```js 5 | /** 6 | * @param {number[]} nums1 7 | * @param {number[]} nums2 8 | * @return {number} 9 | */ 10 | // m n 11 | // O(m ^ 2 * n) 12 | // O(m n) 13 | var maxDotProduct = function(nums1, nums2) { 14 | // [2,1,-2,5] [3,0,-6] 15 | // product(i, j) = 16 | // if nums2[j] is not used, product(i, j - 1) 17 | // if nums2[j] is used, max { 18 | // 0<= k <= i, nums2[j] * nums1[k] + max(0, product[k - 1][j - 1]) 19 | // } 20 | const length1 = nums1.length 21 | const length2 = nums2.length 22 | const dp = new Array(length1).fill(0).map(_ => new Array(length2)) 23 | 24 | for (let i = 0; i < length1; i++) { 25 | for (let j = 0; j < length2; j++) { 26 | if (i === 0 && j === 0) { 27 | dp[i][j] = nums1[i] * nums2[j] 28 | } else if (i === 0) { 29 | dp[i][j] = Math.max(dp[i][j - 1], nums1[i] * nums2[j]) 30 | } else if (j === 0) { 31 | dp[i][j] = Math.max(dp[i - 1][j], nums1[i] * nums2[j]) 32 | } else { 33 | let maxProductIfUsed = -Infinity 34 | for (let k = i; k >= 0; k--) { 35 | maxProductIfUsed = Math.max( 36 | maxProductIfUsed, 37 | nums1[k] * nums2[j] + Math.max(0, k > 0 ? dp[k - 1][j - 1] : 0) 38 | ) 39 | } 40 | 41 | dp[i][j] = Math.max(maxProductIfUsed, dp[i][j - 1]) 42 | } 43 | } 44 | } 45 | return dp[length1 - 1][length2 - 1] 46 | }; 47 | ``` 48 | -------------------------------------------------------------------------------- /1460-make-two-arrays-equal-by-reversing-sub-arrays.md: -------------------------------------------------------------------------------- 1 | Me explaining this: https://youtu.be/bC4H5GYwUYI 2 | 3 | ```js 4 | /** 5 | * @param {number[]} target 6 | * @param {number[]} arr 7 | * @return {boolean} 8 | */ 9 | 10 | // Time: O(m + n) 11 | // Space: worst O(m + n) 12 | // var canBeEqual = function(target, arr) { 13 | // // count 14 | // const count1 = new Map() 15 | // const count2 = new Map() 16 | 17 | // for (let i of target) { 18 | // if (count1.has(i)) { 19 | // count1.set(i, count1.get(i) + 1) 20 | // } else { 21 | // count1.set(i, 1) 22 | // } 23 | // } 24 | 25 | // for (let i of arr) { 26 | // if (count2.has(i)) { 27 | // count2.set(i, count2.get(i) + 1) 28 | // } else { 29 | // count2.set(i, 1) 30 | // } 31 | // } 32 | 33 | // for (let [i, count] of count1) { 34 | // if (count2.get(i) !== count) { 35 | // return false 36 | // } 37 | // } 38 | 39 | // return true 40 | // }; 41 | 42 | // sort 43 | // Time: O(2mlogm + m) => O(mlom) 44 | // Space: O(1) 45 | var canBeEqual = function (target, arr) { 46 | if (target.length !== arr.length) return false; 47 | // count 48 | target.sort((a, b) => a - b); 49 | arr.sort((a, b) => a - b); 50 | 51 | for (let i = 0; i < target.length; i++) { 52 | if (arr[i] !== target[i]) { 53 | return false; 54 | } 55 | } 56 | 57 | return true; 58 | }; 59 | ``` 60 | -------------------------------------------------------------------------------- /1461-check-if-a-string-contains-all-binary-codes-of-size-k.md: -------------------------------------------------------------------------------- 1 | Here is me explaining it: https://youtu.be/Pd_N0MKdQtQ 2 | 3 | ```js 4 | /** 5 | * @param {string} s 6 | * @param {number} k 7 | * @return {boolean} 8 | */ 9 | 10 | // Time: O(nk) 11 | // Space: O(2^k) 12 | var hasAllCodes = function (s, k) { 13 | // brute force 14 | // O(2^k * n) 15 | 16 | // get all substrings (Set) then check existence by O(1) 17 | // check Set.size O(1) 18 | 19 | const substrings = new Set(); 20 | 21 | for (let i = 0; i + k - 1 < s.length; i++) { 22 | substrings.add(s.slice(i, i + k - 1 + 1)); 23 | } 24 | 25 | return substrings.size === 2 ** k; 26 | }; 27 | ``` 28 | -------------------------------------------------------------------------------- /1465-maximum-area-of-a-piece-of-cake-after-horizontal-and-vertical-cuts.md: -------------------------------------------------------------------------------- 1 | Here is my youtube video explaining it: https://youtu.be/lvKfbNt5EII 2 | 3 | 4 | ```js 5 | /** 6 | * @param {number} h 7 | * @param {number} w 8 | * @param {number[]} horizontalCuts 9 | * @param {number[]} verticalCuts 10 | * @return {number} 11 | */ 12 | // Time: O(nlogn) 13 | // space: O(1) 14 | var maxArea = function(h, w, horizontalCuts, verticalCuts) { // both at length n 15 | // O(nlogn) 16 | horizontalCuts.sort((a, b) => a - b) 17 | // O(nlogn) 18 | verticalCuts.sort((a, b) => a - b) 19 | 20 | horizontalCuts.push(h) 21 | verticalCuts.push(w) 22 | 23 | let maxH = 0 24 | let prev = 0 25 | // O(n) 26 | for (let cut of horizontalCuts) { 27 | maxH = Math.max(maxH, cut - prev) 28 | prev = cut 29 | } 30 | 31 | let maxV = 0 32 | prev = 0 33 | // O(n) 34 | for (let cut of verticalCuts) { 35 | maxV = Math.max(maxV, cut - prev) 36 | prev = cut 37 | } 38 | 39 | return (maxH * maxV) % (10 ** 9 + 7) 40 | }; 41 | ``` 42 | -------------------------------------------------------------------------------- /1466-reorder-routes-to-make-all-paths-lead-to-the-city-zero.md: -------------------------------------------------------------------------------- 1 | me explaining it on youtube: https://youtu.be/LVuQFBTev58 2 | 3 | 4 | ```js 5 | /** 6 | * @param {number} n 7 | * @param {number[][]} connections 8 | * @return {number} 9 | */ 10 | var minReorder = function(n, connections) { 11 | // const 12 | // two Maps> 13 | 14 | const inboundMap = new Map() 15 | const outboundMap = new Map() 16 | 17 | for (let [from, to] of connections) { 18 | if (outboundMap.has(from)) { 19 | outboundMap.get(from).add(to) 20 | } else { 21 | outboundMap.set(from, new Set([to])) 22 | } 23 | 24 | if (inboundMap.has(to)) { 25 | inboundMap.get(to).add(from) 26 | } else { 27 | inboundMap.set(to, new Set([from])) 28 | } 29 | } 30 | 31 | const traversed = new Set() 32 | 33 | let result = 0 34 | 35 | const walk = (city) => { 36 | traversed.add(city) 37 | 38 | // 1. look at the inbound 39 | if (inboundMap.has(city)) { 40 | for (let from of inboundMap.get(city)) { 41 | if (!traversed.has(from)) { 42 | walk(from) 43 | } 44 | } 45 | } 46 | 47 | // 2. look at the outbound 48 | if (outboundMap.has(city)) { 49 | for (let to of outboundMap.get(city)) { 50 | if (!traversed.has(to)) { 51 | result += 1 52 | walk(to) 53 | } 54 | } 55 | } 56 | } 57 | 58 | walk(0) 59 | 60 | return result 61 | }; 62 | ``` 63 | -------------------------------------------------------------------------------- /1470-shuffle-the-array.md: -------------------------------------------------------------------------------- 1 | Hear me explaining it on youtube: https://youtu.be/R-nXA1rzlzI 2 | 3 | 4 | ```js 5 | /** 6 | * @param {number[]} nums 7 | * @param {number} n 8 | * @return {number[]} 9 | */ 10 | var shuffle = function(nums, n) { 11 | const result = [] 12 | for (let i = 0; i < n; i++) { 13 | result.push(nums[i]) 14 | result.push(nums[i + n]) 15 | } 16 | return result 17 | }; 18 | ``` 19 | -------------------------------------------------------------------------------- /1472-design-browser-history.md: -------------------------------------------------------------------------------- 1 | Hear me expalaining it on youtube: https://youtu.be/FcXFVLyjU0k 2 | 3 | ```js 4 | /** 5 | * @param {string} homepage 6 | */ 7 | var BrowserHistory = function(homepage) { 8 | // [1] 9 | // [1,2] 10 | // [1,2,3] 11 | // [1,2(o), 3] 12 | // [1,2, 4(0)] 13 | 14 | this.histories = [homepage] 15 | this.currentIndex = 0 16 | }; 17 | 18 | /** 19 | * @param {string} url 20 | * @return {void} 21 | */ 22 | BrowserHistory.prototype.visit = function(url) { 23 | this.histories.length = this.currentIndex + 1 24 | this.histories.push(url) 25 | this.currentIndex += 1 26 | }; 27 | 28 | /** 29 | * @param {number} steps 30 | * @return {string} 31 | */ 32 | BrowserHistory.prototype.back = function(steps) { 33 | const newIndex = Math.max(0, this.currentIndex - steps) 34 | this.currentIndex = newIndex 35 | return this.histories[newIndex] 36 | }; 37 | 38 | /** 39 | * @param {number} steps 40 | * @return {string} 41 | */ 42 | BrowserHistory.prototype.forward = function(steps) { 43 | const newIndex = Math.min(this.histories.length - 1, this.currentIndex + steps) 44 | this.currentIndex = newIndex 45 | return this.histories[newIndex] 46 | }; 47 | 48 | /** 49 | * Your BrowserHistory object will be instantiated and called as such: 50 | * var obj = new BrowserHistory(homepage) 51 | * obj.visit(url) 52 | * var param_2 = obj.back(steps) 53 | * var param_3 = obj.forward(steps) 54 | */ 55 | ``` 56 | -------------------------------------------------------------------------------- /1481-least-number-of-unique-integers-after-k-removals.md: -------------------------------------------------------------------------------- 1 | Hear me explaining it on youtube: https://youtu.be/CNCCQ4UndDs 2 | 3 | ```js 4 | /** 5 | * @param {number[]} arr 6 | * @param {number} k 7 | * @return {number} 8 | */ 9 | 10 | // Time: O(NlogN) 11 | // space: O(N) 12 | var findLeastNumOfUniqueInts = function(arr, k) { 13 | // maximize the uniqueness from k element 14 | // by couting the numbers => sort 15 | 16 | // space: O(N) 17 | const count = new Map() 18 | 19 | // O(n) 20 | for (let num of arr) { 21 | if (count.has(num)) { 22 | count.set(num, count.get(num) + 1) 23 | } else { 24 | count.set(num, 1) 25 | } 26 | } 27 | 28 | const uniqueCounts = [...count.values()] 29 | 30 | // O(NlogN) 31 | uniqueCounts.sort((a, b) => a - b) 32 | 33 | let removedCount = 0 34 | let removedUniqueCount = 0 35 | // O(N) 36 | for (let count of uniqueCounts) { 37 | removedCount += count 38 | if (removedCount <= k) { 39 | removedUniqueCount += 1 40 | } else { 41 | break 42 | } 43 | } 44 | 45 | return uniqueCounts.length - removedUniqueCount; 46 | }; 47 | ``` 48 | -------------------------------------------------------------------------------- /149-max-points-on-a-line.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {number[][]} points 4 | * @return {number} 5 | */ 6 | var maxPoints = function (points) { 7 | // brute force 8 | 9 | // choose two points to determine a line O(N^2) 10 | // then check other points if they are on it O(N^3) 11 | 12 | // for a point, we constantly check if it blongs to O(N^2) lines 13 | // is it possible we somehow hash? 14 | 15 | // if we fix a point, we can easily find the longest line 16 | // by calculating the angles with all other points => O(n) 17 | // thus improving the algorithm into O(n^2) 18 | let max = 1; 19 | for (let i = 0; i < points.length; i++) { 20 | const tanCount = new Map(); 21 | for (let j = i + 1; j < points.length; j++) { 22 | let tan = (points[j][1] - points[i][1]) / (points[j][0] - points[i][0]); 23 | // normalize if Infinity 24 | if (tan === -Infinity) { 25 | tan = Infinity; 26 | } 27 | const count = (tanCount.get(tan) ?? 0) + 1; 28 | max = Math.max(max, count + 1); 29 | tanCount.set(tan, count); 30 | } 31 | } 32 | return max; 33 | }; 34 | ``` 35 | -------------------------------------------------------------------------------- /1496-path-crossing.md: -------------------------------------------------------------------------------- 1 | A video of this: https://youtu.be/NnlbI98AkCM 2 | 3 | ```js 4 | /** 5 | * @param {string} path 6 | * @return {boolean} 7 | */ 8 | var isPathCrossing = function(path) { 9 | // use a set to keep track of the walked points 10 | // once found crossed, return immediately 11 | 12 | const directions = { 13 | N: [0, 1], 14 | E: [1, 0], 15 | S: [0, -1], 16 | W: [-1, 0] 17 | } 18 | // Set 19 | const walked = new Set() 20 | let currentCoord = null 21 | 22 | const walk = (point) => { 23 | const key = point.join('_') 24 | if (walked.has(key)) { 25 | return true 26 | } 27 | walked.add(point.join('_')) 28 | currentCoord = point 29 | return false 30 | } 31 | 32 | walk([0, 0]) 33 | 34 | for (let step of path) { 35 | const direction = directions[step] 36 | const next = [currentCoord[0] + direction[0], currentCoord[1] + direction[1]] 37 | if (walk(next)) { 38 | return true 39 | } 40 | } 41 | 42 | return false 43 | }; 44 | ``` 45 | -------------------------------------------------------------------------------- /1497-check-if-array-pairs-are-divisible-by-k.md: -------------------------------------------------------------------------------- 1 | A video explaining: https://youtu.be/ntE-tgQC988 2 | 3 | ```js 4 | /** 5 | * @param {number[]} arr 6 | * @param {number} k 7 | * @return {boolean} 8 | */ 9 | var canArrange = function(arr, k) { 10 | // 1,2,3,4,5,10,6,7,8,9, -1 11 | 12 | // 2 3 4 0 0 1 2 , 4 13 | // 14 | 15 | // ( a + b ) % k === 0 16 | // (a % k + b % k) % k === 0 17 | // 18 | 19 | // about the count of each number's modulelo? 20 | // find the pair by direct access map 21 | // if no single element left, then OK 22 | 23 | // Map 24 | 25 | const countMap = new Map() 26 | for (let num of arr) { 27 | 28 | const mod = num > 0 ? num % k : (k - (-num) % k) % k 29 | if (countMap.has(mod)) { 30 | countMap.set(mod, countMap.get(mod) + 1) 31 | } else { 32 | countMap.set(mod, 1) 33 | } 34 | } 35 | // remove the pairs 36 | // -1,1,-2,2,-3,3,-4,4 37 | // 2 1 1 2 0 0 38 | for (let [mod, count] of countMap) { 39 | if (mod === 0) { 40 | if (count % 2 !== 0) { 41 | return false 42 | } 43 | countMap.delete(mod) 44 | } else { 45 | const counterpart = k - mod 46 | 47 | if (countMap.get(counterpart) !== count) { 48 | // not match 49 | return false 50 | } 51 | countMap.delete(mod) 52 | countMap.delete(counterpart) 53 | } 54 | } 55 | 56 | return true 57 | }; 58 | ``` 59 | -------------------------------------------------------------------------------- /15-3sum.md: -------------------------------------------------------------------------------- 1 | A video explaining this: https://youtu.be/QtbjaHkUzNo 2 | 3 | ```js 4 | /** 5 | * @param {number[]} nums 6 | * @return {number[][]} 7 | */ 8 | var threeSum = function(nums) { 9 | if (nums.length < 3) return [] 10 | 11 | const result = [] 12 | const hashes = new Set() 13 | 14 | // fix the left, the rest is the 2 sum problem 15 | for (let i = 0; i + 2 < nums.length; i++) { 16 | if (nums[i] === nums[i - 1]) continue 17 | const set = new Set() 18 | for (let j = i + 1; j < nums.length; j++) { 19 | const counterpart = -nums[i] - nums[j] 20 | if (set.has(counterpart)) { 21 | const min = Math.min(nums[i], nums[j], counterpart) 22 | const max = Math.max(nums[i], nums[j], counterpart) 23 | const hash = `${min}_${max}` 24 | if (!hashes.has(hash)) { 25 | result.push([min, -min-max,max]) 26 | hashes.add(hash) 27 | } 28 | } 29 | set.add(nums[j]) 30 | } 31 | } 32 | 33 | return result 34 | }; 35 | ``` 36 | -------------------------------------------------------------------------------- /150-evaluate-reverse-polish-notation.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {string[]} tokens 4 | * @return {number} 5 | */ 6 | var evalRPN = function (tokens) { 7 | const stack = []; 8 | for (const token of tokens) { 9 | switch (token) { 10 | case "+": { 11 | const operand2 = stack.pop(); 12 | const operand1 = stack.pop(); 13 | stack.push(operand1 + operand2); 14 | break; 15 | } 16 | case "-": { 17 | const operand2 = stack.pop(); 18 | const operand1 = stack.pop(); 19 | stack.push(operand1 - operand2); 20 | break; 21 | } 22 | case "*": { 23 | const operand2 = stack.pop(); 24 | const operand1 = stack.pop(); 25 | stack.push(operand1 * operand2); 26 | break; 27 | } 28 | case "/": { 29 | const operand2 = stack.pop(); 30 | const operand1 = stack.pop(); 31 | stack.push(Math.trunc(operand1 / operand2)); 32 | break; 33 | } 34 | default: 35 | stack.push(parseInt(token, 10)); 36 | } 37 | } 38 | return stack[0]; 39 | }; 40 | ``` 41 | -------------------------------------------------------------------------------- /151-reverse-words-in-a-string.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {string} s 4 | * @return {string} 5 | */ 6 | // var reverseWords = function(s) { 7 | // return s.trim().split(/\s+/).reverse().join(' ') 8 | // }; 9 | 10 | var reverseWords = function (s) { 11 | return s.trim().split(/\s+/).reverse().join(" "); 12 | }; 13 | ``` 14 | 15 | ```js 16 | /** 17 | * @param {string} s 18 | * @return {string} 19 | */ 20 | // var reverseWords = function(s) { 21 | // return s.trim().split(/\s+/).reverse().join(' ') 22 | // }; 23 | 24 | var reverseWords = function (s) { 25 | const words = []; 26 | let isWord = false; 27 | let buffer = ""; 28 | 29 | for (let i = 0; i < s.length; i++) { 30 | const char = s[i]; 31 | if (isWord) { 32 | if (char != " ") { 33 | buffer += char; 34 | } else { 35 | isWord = false; 36 | words.push(buffer); 37 | buffer = ""; 38 | } 39 | } else { 40 | if (char != " ") { 41 | isWord = true; 42 | buffer += char; 43 | } 44 | } 45 | } 46 | if (buffer !== "") { 47 | words.push(buffer); 48 | } 49 | 50 | return words.reverse().join(" "); 51 | }; 52 | ``` 53 | -------------------------------------------------------------------------------- /1518-water-bottles.md: -------------------------------------------------------------------------------- 1 | Here is a video explainig it: https://youtu.be/DpipzCreVMU 2 | 3 | ```js 4 | /** 5 | * @param {number} numBottles 6 | * @param {number} numExchange 7 | * @return {number} 8 | */ 9 | var numWaterBottles = function(numBottles, numExchange) { 10 | let fullBottleCount = numBottles 11 | let emptyBottlCount = 0 12 | let result = 0 13 | 14 | while (fullBottleCount > 0) { 15 | result += fullBottleCount 16 | emptyBottlCount += fullBottleCount 17 | 18 | fullBottleCount = Math.floor(emptyBottlCount / numExchange) 19 | emptyBottlCount %= numExchange 20 | } 21 | 22 | return result 23 | }; 24 | ``` 25 | -------------------------------------------------------------------------------- /153-find-minimum-in-rotated-sorted-array.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {number[]} nums 4 | * @return {number} 5 | */ 6 | var findMin = function (nums) { 7 | // it means to find the first item right? 8 | // [ m ][ ] => on the right 9 | // [ ][ m ] => on the left 10 | 11 | let i = 0; 12 | let j = nums.length - 1; 13 | while (i < j) { 14 | console.log(i, j); 15 | const m = Math.floor((i + j) / 2); 16 | // m -> close to i 17 | // itself might be the smallet number 18 | if (nums[m] > nums[j]) { 19 | i = m + 1; 20 | } else { 21 | j = m; 22 | } 23 | } 24 | // [ ] 25 | // stops when i == j 26 | return nums[i]; 27 | }; 28 | ``` 29 | -------------------------------------------------------------------------------- /155-min-stack.md: -------------------------------------------------------------------------------- 1 | ```js 2 | var MinStack = function () { 3 | // Map, Set, Array 4 | this.stack = []; 5 | this.minStack = []; 6 | }; 7 | 8 | /** 9 | * @param {number} val 10 | * @return {void} 11 | */ 12 | MinStack.prototype.push = function (val) { 13 | this.stack.push(val); 14 | if (this.minStack.length === 0) { 15 | this.minStack.push(val); 16 | } else { 17 | this.minStack.push(Math.min(this.minStack.at(-1), val)); 18 | } 19 | }; 20 | 21 | /** 22 | * @return {void} 23 | */ 24 | MinStack.prototype.pop = function () { 25 | // all are fine 26 | this.stack.pop(); 27 | this.minStack.pop(); 28 | }; 29 | 30 | /** 31 | * @return {number} 32 | */ 33 | MinStack.prototype.top = function () { 34 | return this.stack.at(-1); 35 | }; 36 | 37 | /** 38 | * @return {number} 39 | */ 40 | MinStack.prototype.getMin = function () { 41 | // min 42 | // when to update minimum? 43 | // if to update when pop, we need to know next minimum 44 | // it means we need a sorted data structure? 45 | // or we can just store the next minimum because 46 | // the popping of the list is just one direction. 47 | return this.minStack.at(-1); 48 | }; 49 | 50 | /** 51 | * Your MinStack object will be instantiated and called as such: 52 | * var obj = new MinStack() 53 | * obj.push(val) 54 | * obj.pop() 55 | * var param_3 = obj.top() 56 | * var param_4 = obj.getMin() 57 | */ 58 | ``` 59 | -------------------------------------------------------------------------------- /158-read-n-characters-given-read4-ii-call-multiple-times.md: -------------------------------------------------------------------------------- 1 | Here is me explaining it on youtube: https://youtu.be/OE-quj_pBeE 2 | 3 | ```js 4 | /** 5 | * Definition for read4() 6 | * 7 | * @param {character[]} buf Destination buffer 8 | * @return {number} The number of characters read 9 | * read4 = function(buf) { 10 | * ... 11 | * }; 12 | */ 13 | 14 | /** 15 | * @param {function} read4() 16 | * @return {function} 17 | */ 18 | var solution = function(read4) { 19 | /** 20 | * @param {character[]} buf Destination buffer 21 | * @param {number} n Number of characters to read 22 | * @return {number} The number of actual characters read 23 | */ 24 | const lastUnread = [] 25 | 26 | return function(buf, n) { 27 | // use lastUnread first 28 | 29 | while (buf.length < n && lastUnread.length > 0) { 30 | buf.push(lastUnread.shift()) 31 | } 32 | 33 | while (buf.length < n) { 34 | const bufferFor4 = [] 35 | read4(bufferFor4) 36 | if (bufferFor4.length === 0) { 37 | break 38 | } 39 | buf.push(...bufferFor4) 40 | } 41 | 42 | // if we read too much 43 | while (buf.length > n) { 44 | lastUnread.unshift(buf.pop()) 45 | } 46 | 47 | return buf.length 48 | }; 49 | }; 50 | ``` 51 | -------------------------------------------------------------------------------- /1582-special-positions-in-a-binary-matrix.md: -------------------------------------------------------------------------------- 1 | Here is a video explaining: https://www.youtube.com/watch?v=rvrbBIUBsKs 2 | 3 | ```js 4 | /** 5 | * @param {number[][]} mat 6 | * @return {number} 7 | */ 8 | 9 | // Time: O(2mn) O(mn) 10 | // Space: O(m + n) 11 | var numSpecial = function(mat) { 12 | const rows = mat.length 13 | const cols = mat[0].length 14 | 15 | const count1Rows = new Array(rows).fill(0) 16 | const count1Cols = new Array(cols).fill(0) 17 | 18 | for (let row = 0; row < rows; row++) { 19 | for (let col = 0; col < cols; col++) { 20 | if (mat[row][col] === 1) { 21 | count1Rows[row] += 1 22 | count1Cols[col] += 1 23 | } 24 | } 25 | } 26 | 27 | let result = 0 28 | for (let row = 0; row < rows; row++) { 29 | for (let col = 0; col < cols; col++) { 30 | if (mat[row][col] === 1 && count1Rows[row] === 1 && count1Cols[col] === 1) { 31 | result += 1 32 | } 33 | } 34 | } 35 | 36 | return result 37 | }; 38 | ``` 39 | -------------------------------------------------------------------------------- /1583-count-unhappy-friends.md: -------------------------------------------------------------------------------- 1 | here is a video explaining this : https://youtu.be/aUQbwFQQa0U 2 | ```js 3 | /** 4 | * @param {number} n 5 | * @param {number[][]} preferences 6 | * @param {number[][]} pairs 7 | * @return {number} 8 | */ 9 | var unhappyFriends = function(n, preferences, pairs) { 10 | // [0, 1], [2,3] 11 | // 1 -> 3, 1 -> 2 12 | // Map> 13 | // 1 -> 3 14 | let unhappyFriends = new Set() 15 | const unhappyMap = new Map() 16 | 17 | for (let [a, b] of pairs) { 18 | for (let target of preferences[a]) { 19 | if (target === b) break 20 | 21 | // check if target -> b exist 22 | if (unhappyMap.has(target) && unhappyMap.get(target).has(a)) { 23 | unhappyFriends.add(target) 24 | unhappyFriends.add(a) 25 | } 26 | 27 | if (unhappyMap.has(a)){ 28 | unhappyMap.get(a).add(target) 29 | } else { 30 | unhappyMap.set(a, new Set([target])) 31 | } 32 | } 33 | 34 | for (let target of preferences[b]) { 35 | if (target === a) break 36 | // check if target -> b exist 37 | if (unhappyMap.has(target) && unhappyMap.get(target).has(b)) { 38 | unhappyFriends.add(target) 39 | unhappyFriends.add(b) 40 | } 41 | if (unhappyMap.has(b)){ 42 | unhappyMap.get(b).add(target) 43 | } else { 44 | unhappyMap.set(b, new Set([target])) 45 | } 46 | } 47 | } 48 | 49 | return unhappyFriends.size 50 | }; 51 | ``` 52 | -------------------------------------------------------------------------------- /16-3sum-closest.md: -------------------------------------------------------------------------------- 1 | A video explaining this: https://youtu.be/C2mT-EgNwMk 2 | ```js 3 | /** 4 | * @param {number[]} nums 5 | * @param {number} target 6 | * @return {number} 7 | */ 8 | var threeSumClosest = function(nums, target) { 9 | let result = Number.POSITIVE_INFINITY 10 | 11 | 12 | // sort the nums O(lgN) 13 | nums.sort((a, b) => a - b) 14 | 15 | 16 | // [-4, -1, -1, 0, 1, 2] 17 | 18 | // n * n , total + O(n^2) => O(n^2) 19 | // Space: 3Cn 20 | 21 | for (let i = 0; i < nums.length - 2; i++) { 22 | // fix ith number as left one 23 | let j = i + 1 24 | let k = nums.length - 1 25 | 26 | while (j < k) { 27 | const sum = nums[i] + nums[j] + nums[k] 28 | 29 | const currentDelta = sum - target 30 | 31 | if (currentDelta === 0) return target 32 | 33 | if (currentDelta < 0) { 34 | // move to the next different number 35 | j += 1 36 | while (nums[j] === nums[j - 1]) { 37 | j += 1 38 | } 39 | } 40 | 41 | if (currentDelta > 0) { 42 | // move to the next different number 43 | k -= 1 44 | while (nums[k] === nums[k + 1]) { 45 | k -= 1 46 | } 47 | } 48 | if (Math.abs(currentDelta) < Math.abs(result - target)) { 49 | result = sum 50 | } 51 | } 52 | 53 | // move to the next different number 54 | while (nums[i + 1] === nums[i]) { 55 | i += 1 56 | } 57 | } 58 | 59 | return result 60 | }; 61 | ``` 62 | -------------------------------------------------------------------------------- /167-two-sum-ii-input-array-is-sorted.md: -------------------------------------------------------------------------------- 1 | ## 1. Map 2 | 3 | ```js 4 | // /** 5 | // * @param {number[]} numbers 6 | // * @param {number} target 7 | // * @return {number[]} 8 | // */ 9 | var twoSum = function (numbers, target) { 10 | const map = new Map(); 11 | 12 | for (let i = 0; i < numbers.length; i++) { 13 | if (map.has(target - numbers[i])) { 14 | return [i + 1, map.get(target - numbers[i]) + 1].sort((a, b) => a - b); 15 | } 16 | map.set(numbers[i], i); 17 | } 18 | }; 19 | ``` 20 | 21 | ## 2. Narrow the border 22 | 23 | ```js 24 | // we didn't take a adavantage of the info "sorted" 25 | // suppose we have i, j, if the sum is smaller, then we need to move i, j to the right 26 | // if bigger we have to move to the left 27 | // the widest range would be 0 j 28 | // so if we start from both ends, if smaller, move i to right, if bigger, move j to the left 29 | 30 | var twoSum = function (numbers, target) { 31 | let i = 0; 32 | let j = numbers.length - 1; 33 | // no i <= j because we cannot use the same index 34 | while (i < j) { 35 | const sum = numbers[i] + numbers[j]; 36 | if (sum === target) { 37 | return [i + 1, j + 1]; 38 | } else if (sum < target) { 39 | i += 1; 40 | } else { 41 | j -= 1; 42 | } 43 | } 44 | }; 45 | ``` 46 | -------------------------------------------------------------------------------- /1752-check-if-array-is-sorted-and-rotated.md: -------------------------------------------------------------------------------- 1 | Here is my explaining video: https://youtu.be/0nhjhVv1VLs 2 | ```js 3 | /** 4 | * @param {number[]} nums 5 | * @return {boolean} 6 | */ 7 | var check = function(nums) { 8 | // [a, b][c, d] => [c,d][a,b] 9 | 10 | // c -> d 11 | // a -> b 12 | // b <= c 13 | 14 | // a <= d => 1 15 | 16 | // 1. turning point is maximum 1 17 | // 2. if has turning point, b <= c 18 | 19 | let turningPoints = 0 20 | for (let i = 1; i < nums.length; i++) { 21 | if (nums[i] < nums[i - 1]) { 22 | turningPoints += 1 23 | } 24 | 25 | if (turningPoints > 1) { 26 | return false 27 | } 28 | } 29 | 30 | return turningPoints === 0 || nums[nums.length - 1] <= nums[0] 31 | }; 32 | ``` 33 | -------------------------------------------------------------------------------- /1753-maximum-score-from-removing-stones.md: -------------------------------------------------------------------------------- 1 | Here is the video explaining: https://youtu.be/21MLoK0zZBs 2 | 3 | ```js 4 | /** 5 | * @param {number} a 6 | * @param {number} b 7 | * @param {number} c 8 | * @return {number} 9 | */ 10 | var maximumScore = function(...args) { 11 | const [a, b, c] = args.sort((x, y) => y - x) 12 | 13 | if (b + c <= a) return b + c 14 | 15 | // 6 4 4 => empty 6 because 4 + 4 > 6, 16 | // while empty 6, we could empty 4 or ther other 4, because 6 is the largest number 17 | // could balance the rest 2 piles while empty the largest pile 18 | // a, b, c 19 | // a >= b, a >= c, a >= b > b - c 20 | // empty a first 21 | return a + Math.floor((b + c - a) / 2) 22 | 23 | }; 24 | ``` 25 | -------------------------------------------------------------------------------- /18-4sum.md: -------------------------------------------------------------------------------- 1 | A video explaining the details: https://youtu.be/Xc-GIwvq4Rs 2 | ```js 3 | /** 4 | * @param {number[]} nums 5 | * @param {number} target 6 | * @return {number[][]} 7 | */ 8 | 9 | // Time: O(n^3) 10 | // Space: 3Cn ?? 11 | var fourSum = function(nums, target) { 12 | const result = [] 13 | 14 | if (nums.length < 4) return result 15 | 16 | // O(logN) 17 | nums.sort((a,b) => a - b) 18 | 19 | // O(n^3) 20 | for (let i = 0; i < nums.length - 3; i++) { 21 | // skip the same numbers 22 | if (nums[i] === nums[i - 1]) continue 23 | 24 | for (let j = i + 1; j < nums.length - 2; j++) { 25 | if (j > i + 1 && nums[j] == nums[j - 1]) continue 26 | 27 | let k = j + 1 28 | let l = nums.length - 1 29 | 30 | while (k < l) { 31 | const sum = nums[i] + nums[j] + nums[k] + nums[l] 32 | 33 | if (sum === target) { 34 | result.push([nums[i], nums[j], nums[k], nums[l]]) 35 | } 36 | 37 | if (sum <= target) { 38 | k += 1 39 | while (nums[k] === nums[k - 1]) { 40 | k += 1 41 | } 42 | } 43 | 44 | if (sum >= target) { 45 | l -= 1 46 | while (nums[l] === nums[l + 1]) { 47 | l -= 1 48 | } 49 | } 50 | } 51 | } 52 | } 53 | 54 | return result 55 | }; 56 | ``` 57 | -------------------------------------------------------------------------------- /1817-finding-the-users-active-minutes.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {number[][]} logs 4 | * @param {number} k 5 | * @return {number[]} 6 | */ 7 | var findingUsersActiveMinutes = function (logs, k) { 8 | // [[0,5],[1,2],[0,2],[0,5],[1,3]] 9 | // 0 -> [5, 2] 10 | // 1 -> [2, 3] 11 | // Map> 12 | const map = new Map(); 13 | 14 | for (const [user, min] of logs) { 15 | if (!map.has(user)) { 16 | map.set(user, new Set()); 17 | } 18 | map.get(user).add(min); 19 | } 20 | 21 | const result = new Array(k).fill(0); 22 | for (const [user, mins] of map) { 23 | result[mins.size - 1] += 1; 24 | } 25 | 26 | return result; 27 | }; 28 | ``` 29 | -------------------------------------------------------------------------------- /189-rotate-array.md: -------------------------------------------------------------------------------- 1 | ### 1. brute force 2 | 3 | ```js 4 | /** 5 | * @param {number[]} nums 6 | * @param {number} k 7 | * @return {void} Do not return anything, modify nums in-place instead. 8 | */ 9 | // O(k * N) 10 | var rotate = function (nums, k) { 11 | while (k > 0) { 12 | nums.unshift(nums.pop()); 13 | k -= 1; 14 | } 15 | }; 16 | ``` 17 | 18 | ### 2. reverse 19 | 20 | ```js 21 | // [1,2,3,4,5,6,7] 22 | // [1,2 3,1,5,6,7] 4 at 3 23 | // [1,2,3,1,5,3,4] 6 at 5 24 | // [1,6,3,1,5,3,4] 2 at 1 25 | 26 | // so we can update the item and try put the next item at right location 27 | 28 | // we can keep swaping the nums and update the indices as well. 29 | var rotate = function (nums, k) { 30 | k %= nums.length; 31 | if (k === 0) return; 32 | reverse(0, nums.length - 1); 33 | reverse(0, k - 1); 34 | reverse(k, nums.length - 1); 35 | 36 | function reverse(i, j) { 37 | while (i < j) { 38 | [nums[i], nums[j]] = [nums[j], nums[i]]; 39 | i += 1; 40 | j -= 1; 41 | } 42 | } 43 | }; 44 | ``` 45 | -------------------------------------------------------------------------------- /190-reverse-bits.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {number} n - a positive integer 4 | * @return {number} - a positive integer 5 | */ 6 | var reverseBits = function (n) { 7 | const bits = [...n.toString(2).padStart(32, "0")]; 8 | let i = 0; 9 | let j = bits.length - 1; 10 | while (i < j) { 11 | [bits[i], bits[j]] = [bits[j], bits[i]]; 12 | i += 1; 13 | j -= 1; 14 | } 15 | return parseInt(bits.join(""), 2); 16 | }; 17 | ``` 18 | 19 | Or we can just get the bits from right to left 20 | 21 | ```js 22 | var reverseBits = function (n) { 23 | let i = 0; 24 | let result = 0; 25 | while (i < 32) { 26 | const lastBit = n % 2; 27 | result += lastBit * 2 ** (31 - i); 28 | n = (n - lastBit) / 2; 29 | i += 1; 30 | } 31 | return result; 32 | }; 33 | ``` 34 | -------------------------------------------------------------------------------- /191-number-of-1-bits.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {number} n 4 | * @return {number} 5 | */ 6 | var hammingWeight = function (n) { 7 | // keep getting the bits 8 | let count = 0; 9 | while (n > 0) { 10 | const lastBit = n % 2; 11 | if (lastBit > 0) { 12 | count += 1; 13 | } 14 | n = (n - lastBit) / 2; 15 | } 16 | return count; 17 | }; 18 | ``` 19 | -------------------------------------------------------------------------------- /198-house-robber.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {number[]} nums 4 | * @return {number} 5 | */ 6 | var rob = function (nums) { 7 | // sum of sub list without consecutive number 8 | // suppose [a, b, c] are the result 9 | // for any number, it could be in the list or not 10 | // look at the last item 11 | // Max = Max{ last item chosen, last item not chosen} 12 | // Max{last item chosen} = Max{prev item not chosen} 13 | // Max{last item not chosen} = Max{prev item chosen, prev item not chosen} 14 | 15 | // max sum that ends at index 16 | const maxChosen = new Array(nums.length).fill(0); 17 | const maxNotChosen = new Array(nums.length).fill(0); 18 | let max = 0; 19 | 20 | for (let i = 0; i < nums.length; i++) { 21 | maxChosen[i] = (maxNotChosen[i - 1] ?? 0) + nums[i]; 22 | maxNotChosen[i] = Math.max(maxNotChosen[i - 1] ?? 0, maxChosen[i - 1] ?? 0); 23 | max = Math.max(max, maxChosen[i], maxNotChosen[i]); 24 | } 25 | 26 | return max; 27 | }; 28 | ``` 29 | -------------------------------------------------------------------------------- /2-add-two-numbers.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * Definition for singly-linked list. 4 | * function ListNode(val, next) { 5 | * this.val = (val===undefined ? 0 : val) 6 | * this.next = (next===undefined ? null : next) 7 | * } 8 | */ 9 | /** 10 | * @param {ListNode} l1 11 | * @param {ListNode} l2 12 | * @return {ListNode} 13 | */ 14 | var addTwoNumbers = function (l1, l2) { 15 | let carryover = 0; 16 | let p1 = l1; 17 | let p2 = l2; 18 | let falseHead = new ListNode(0); 19 | let p3 = falseHead; 20 | while (p1 != null || p2 != null) { 21 | let sum = (p1?.val ?? 0) + (p2?.val ?? 0) + carryover; 22 | if (sum > 9) { 23 | sum -= 10; 24 | carryover = 1; 25 | } else { 26 | carryover = 0; 27 | } 28 | const next = new ListNode(sum); 29 | p3.next = next; 30 | p3 = next; 31 | 32 | p1 = p1?.next; 33 | p2 = p2?.next; 34 | } 35 | 36 | if (carryover) { 37 | const next = new ListNode(1); 38 | p3.next = next; 39 | } 40 | 41 | return falseHead.next; 42 | }; 43 | ``` 44 | -------------------------------------------------------------------------------- /20-valid-parentheses.md: -------------------------------------------------------------------------------- 1 | A video explaining this: https://youtu.be/nagl6LE62xg 2 | 3 | ```js 4 | /** 5 | * @param {string} s 6 | * @return {boolean} 7 | */ 8 | 9 | // TIME: O(n) 10 | // Space: O(n) 11 | 12 | var isValid = function (s) { 13 | const pair = { 14 | "]": "[", 15 | ")": "(", 16 | "}": "{", 17 | }; 18 | 19 | const stack = []; 20 | 21 | for (let i = 0; i < s.length; i++) { 22 | const char = s[i]; 23 | if (["(", "[", "{"].includes(char)) { 24 | stack.push(char); 25 | } else { 26 | const top = stack.pop(); 27 | if (top !== pair[char]) { 28 | return false; 29 | } 30 | } 31 | } 32 | 33 | return stack.length === 0; 34 | }; 35 | ``` 36 | 37 | Another try to make the map a bit cleaner 38 | 39 | ```js 40 | /** 41 | * @param {string} s 42 | * @return {boolean} 43 | */ 44 | const pairs = [ 45 | ["(", ")"], 46 | ["[", "]"], 47 | ["{", "}"], 48 | ]; 49 | 50 | const map = new Map(pairs); 51 | const reverseMap = new Map(pairs.map(([a, b]) => [b, a])); 52 | 53 | var isValid = function (s) { 54 | const stack = []; 55 | for (const char of s) { 56 | if (map.has(char)) { 57 | stack.push(char); 58 | } else { 59 | const top = stack.pop(); 60 | if (top !== reverseMap.get(char)) { 61 | return false; 62 | } 63 | } 64 | } 65 | return stack.length === 0; 66 | }; 67 | ``` 68 | -------------------------------------------------------------------------------- /202-happy-number.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {number} n 4 | * @return {boolean} 5 | */ 6 | var isHappy = function (n) { 7 | const nums = new Set(); 8 | let current = n; 9 | while (true) { 10 | next = getNext(current); 11 | if (next === 1) { 12 | return true; 13 | } 14 | if (nums.has(next)) { 15 | return false; 16 | } 17 | nums.add(next); 18 | current = next; 19 | } 20 | }; 21 | 22 | function getNext(num) { 23 | let next = 0; 24 | while (num > 0) { 25 | const modulo = num % 10; 26 | next += modulo ** 2; 27 | num = (num - modulo) / 10; 28 | } 29 | return next; 30 | } 31 | ``` 32 | -------------------------------------------------------------------------------- /204-count-primes.md: -------------------------------------------------------------------------------- 1 | Here is my video explaining this: https://youtu.be/j2EV6sbYHnE 2 | 3 | ```js 4 | /** 5 | * @param {number} n 6 | * @return {number} 7 | */ 8 | var countPrimes = function(n) { 9 | // to check if a number i is prime 10 | // divide i with all number [0, sqrt[i]] 11 | // O(n * sqrt(n)) 12 | 13 | // 2: O 14 | // 4: x 15 | // 6: x 16 | // get a prime, and we could filter out a lot of non-prime numbers 17 | 18 | const isPrime = new Array(n).fill(true) 19 | isPrime[0] = false 20 | isPrime[1] = false 21 | 22 | for (let i = 2; i * i < n; i++) { 23 | if (!isPrime[i]) continue 24 | 25 | for (let j = i; i * j < n; j++) { 26 | isPrime[i * j] = false 27 | } 28 | } 29 | 30 | return isPrime.reduce((count, isPrime) => { 31 | if (isPrime) count += 1 32 | return count 33 | }, 0) 34 | }; 35 | ``` 36 | -------------------------------------------------------------------------------- /205-isoporphic-strings.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {string} s 4 | * @param {string} t 5 | * @return {boolean} 6 | */ 7 | var isIsomorphic = function (s, t) { 8 | if (s.length !== t.length) return false; 9 | 10 | let i = 0; 11 | 12 | const map = new Map(); 13 | const mappedValues = new Set(); 14 | 15 | while (i < s.length) { 16 | if (map.has(s[i])) { 17 | if (t[i] !== map.get(s[i])) { 18 | return false; 19 | } 20 | } else { 21 | if (mappedValues.has(t[i])) { 22 | return false; 23 | } 24 | mappedValues.add(t[i]); 25 | map.set(s[i], t[i]); 26 | } 27 | i += 1; 28 | } 29 | return true; 30 | }; 31 | ``` 32 | -------------------------------------------------------------------------------- /206-reverse-linked-list.md: -------------------------------------------------------------------------------- 1 | Youtube explanation on this: https://youtu.be/JfWYJolU7RQ 2 | 3 | ```js 4 | /** 5 | * Definition for singly-linked list. 6 | * function ListNode(val) { 7 | * this.val = val; 8 | * this.next = null; 9 | * } 10 | */ 11 | /** 12 | * @param {ListNode} head 13 | * @return {ListNode} 14 | */ 15 | var reverseList = function (head) { 16 | let start = null; 17 | 18 | let p = head; 19 | while (p) { 20 | const next = p; 21 | p = p.next; 22 | 23 | next.next = start; 24 | start = next; 25 | } 26 | 27 | return start; 28 | }; 29 | ``` 30 | 31 | ```js 32 | /** 33 | * Definition for singly-linked list. 34 | * function ListNode(val, next) { 35 | * this.val = (val===undefined ? 0 : val) 36 | * this.next = (next===undefined ? null : next) 37 | * } 38 | */ 39 | /** 40 | * @param {ListNode} head 41 | * @return {ListNode} 42 | */ 43 | var reverseList = function (head) { 44 | // 1 2 3 4 5 45 | // p 46 | // 2 1 3 4 5 47 | 48 | let start = head; 49 | let p = head; 50 | while (p?.next != null) { 51 | const next = p.next; 52 | p.next = next.next; 53 | next.next = start; 54 | start = next; 55 | } 56 | return start; 57 | }; 58 | ``` 59 | -------------------------------------------------------------------------------- /207-course-schedule.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {number} numCourses 4 | * @param {number[][]} prerequisites 5 | * @return {boolean} 6 | */ 7 | var canFinish = function (numCourses, prerequisites) { 8 | // a -> b 9 | // b => c 10 | 11 | // Map> 12 | // the problem is to check for each course 13 | // if there is a circle 14 | 15 | // DFS 16 | // use a Set visited to track the visited node 17 | // there might be duplicate checks, memo the check result 18 | const map = new Map(); 19 | for (const [from, to] of prerequisites) { 20 | if (map.has(from)) { 21 | map.get(from).add(to); 22 | } else { 23 | map.set(from, new Set([to])); 24 | } 25 | } 26 | const cache = new Map(); 27 | 28 | function hasCircle(node, visited = new Set()) { 29 | if (cache.has(node)) { 30 | return cache.get(node); 31 | } 32 | 33 | if (visited.has(node)) { 34 | cache.set(node, true); 35 | return true; 36 | } 37 | 38 | const pres = map.get(node) ?? new Set(); 39 | if (pres.size === 0) { 40 | cache.set(node, false); 41 | return false; 42 | } 43 | 44 | visited.add(node); 45 | for (const pre of pres) { 46 | if (hasCircle(pre, visited)) { 47 | cache.set(node, true); 48 | return true; 49 | } 50 | } 51 | visited.delete(node); 52 | cache.set(node, false); 53 | return false; 54 | } 55 | 56 | for (const [from, to] of map.entries()) { 57 | if (hasCircle(from)) { 58 | return false; 59 | } 60 | } 61 | return true; 62 | }; 63 | ``` 64 | -------------------------------------------------------------------------------- /2073-time-needed-to-buy-tickets.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {number[]} tickets 4 | * @param {number} k 5 | * @return {number} 6 | */ 7 | var timeRequiredToBuy = function (tickets, k) { 8 | // since it goes level by level 9 | // it basically means 10 | // the sum of all numbers <= n - 1 11 | // and the count of number >= n but before k 12 | 13 | // so we can just calculate the sum in one pass 14 | let result = 0; 15 | for (let i = 0; i < tickets.length; i++) { 16 | if (tickets[i] >= tickets[k]) { 17 | if (i <= k) { 18 | result += tickets[k]; 19 | } else { 20 | result += tickets[k] - 1; 21 | } 22 | } else { 23 | result += tickets[i]; 24 | } 25 | } 26 | return result; 27 | }; 28 | ``` 29 | -------------------------------------------------------------------------------- /209-minimum-size-subarray-sum.md: -------------------------------------------------------------------------------- 1 | Here is my youtube explaining this: https://youtu.be/eUdoZnemqFQ 2 | 3 | ```js 4 | /** 5 | * @param {number} s 6 | * @param {number[]} nums 7 | * @return {number} 8 | */ 9 | 10 | // Time: O(2N) => O(N) 11 | // space: O(1) 12 | var minSubArrayLen = function (s, nums) { 13 | if (nums.length === 0) return 0; 14 | // 2 15 | // 2 3 16 | // 2 3 1 17 | // 2 3 1 2 18 | // 3 1 2 4 19 | // 1 2 4 20 | // 2 4 3 21 | // 4 3 22 | 23 | // sliing window 24 | 25 | let currentLen = 0; 26 | let currentSum = 0; 27 | 28 | let minLen = Infinity; 29 | 30 | let start = 0; 31 | let end = 0; 32 | 33 | while (end < nums.length) { 34 | currentLen += 1; 35 | currentSum += nums[end]; 36 | if (currentSum >= s) { 37 | do { 38 | minLen = Math.min(minLen, end - start + 1); 39 | currentSum -= nums[start]; 40 | start += 1; 41 | } while (currentSum >= s); 42 | } 43 | end += 1; 44 | } 45 | 46 | return minLen === Infinity ? 0 : minLen; 47 | }; 48 | ``` 49 | 50 | Maybe following is easier to understand. 51 | 52 | ```js 53 | var minSubArrayLen = function (target, nums) { 54 | // [2,3,1,2,4,3] 55 | // [] 56 | 57 | let i = 0; // 5 58 | let j = 0; // 6 59 | let sum = nums[0]; // 7 60 | let result = Infinity; // 2 61 | 62 | while (i < nums.length && j < nums.length) { 63 | if (sum >= target) { 64 | result = Math.min(result, j - i + 1); 65 | sum -= nums[i]; 66 | i += 1; 67 | } else { 68 | j += 1; 69 | sum += nums[j]; 70 | } 71 | } 72 | return Number.isFinite(result) ? result : 0; 73 | }; 74 | ``` 75 | -------------------------------------------------------------------------------- /21-merge-two-sorted-lists.md: -------------------------------------------------------------------------------- 1 | A video explaining this: https://youtu.be/TDihxl5hs3E 2 | 3 | ```js 4 | /** 5 | * Definition for singly-linked list. 6 | * function ListNode(val) { 7 | * this.val = val; 8 | * this.next = null; 9 | * } 10 | */ 11 | /** 12 | * @param {ListNode} l1 13 | * @param {ListNode} l2 14 | * @return {ListNode} 15 | */ 16 | 17 | // Recursion 18 | // Time: O(n) 19 | // Space: O(n) 20 | // var mergeTwoLists = function(l1, l2) { 21 | // if (l1 === null) return l2 22 | // if (l2 === null) return l1 23 | 24 | // if (l1.val <= l2.val) { 25 | // l1.next = mergeTwoLists(l1.next, l2) 26 | // return l1 27 | // } else { 28 | // l2.next = mergeTwoLists(l1, l2.next) 29 | // return l2 30 | // } 31 | // }; 32 | 33 | // Iteration 34 | // Time O(n) 35 | // Space O(1) 36 | var mergeTwoLists = function(l1, l2) { 37 | let head = new ListNode() 38 | let p = head 39 | 40 | while (l1 && l2) { 41 | console.log(l1.val, l2.val) 42 | if (l1.val <= l2.val) { 43 | p.next = l1 44 | l1 = l1.next 45 | } else { 46 | p.next = l2 47 | l2 = l2.next 48 | } 49 | p = p.next 50 | } 51 | 52 | if (l1) p.next = l1 53 | if (l2) p.next = l2 54 | 55 | return head.next 56 | }; 57 | ``` 58 | -------------------------------------------------------------------------------- /219-contains-duplicate-ii.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {number[]} nums 4 | * @param {number} k 5 | * @return {boolean} 6 | */ 7 | var containsNearbyDuplicate = function (nums, k) { 8 | // same numbers but within range of K 9 | // use map to keep track of the latest index 10 | const map = new Map(); 11 | for (let i = 0; i < nums.length; i++) { 12 | const num = nums[i]; 13 | if (map.has(num) && i - map.get(num) <= k) { 14 | return true; 15 | } 16 | map.set(num, i); 17 | } 18 | return false; 19 | }; 20 | ``` 21 | -------------------------------------------------------------------------------- /226-invert-binary-tree.md: -------------------------------------------------------------------------------- 1 | My video explaining this: https://youtu.be/B8QkJDF4WZw 2 | 3 | ```js 4 | /** 5 | * Definition for a binary tree node. 6 | * function TreeNode(val, left, right) { 7 | * this.val = (val===undefined ? 0 : val) 8 | * this.left = (left===undefined ? null : left) 9 | * this.right = (right===undefined ? null : right) 10 | * } 11 | */ 12 | /** 13 | * @param {TreeNode} root 14 | * @return {TreeNode} 15 | */ 16 | var invertTree = function (root) { 17 | if (root === null) return root; 18 | [root.left, root.right] = [root.right, root.left]; 19 | 20 | invertTree(root.left); 21 | invertTree(root.right); 22 | 23 | return root; 24 | }; 25 | ``` 26 | -------------------------------------------------------------------------------- /228-summary-ranges.md: -------------------------------------------------------------------------------- 1 | Youtube video explaining this: https://youtu.be/H1zJFRITZgA 2 | 3 | ```js 4 | /** 5 | * @param {number[]} nums 6 | * @return {string[]} 7 | */ 8 | var summaryRanges = function (nums) { 9 | const result = []; 10 | 11 | for (let i = 0; i < nums.length; i++) { 12 | let end = i; 13 | while (nums[end + 1] === nums[end] + 1) { 14 | end += 1; 15 | } 16 | 17 | if (end > i) { 18 | result.push(`${nums[i]}->${nums[end]}`); 19 | } else { 20 | result.push(`${nums[i]}`); 21 | } 22 | 23 | i = end; 24 | } 25 | 26 | return result; 27 | }; 28 | ``` 29 | 30 | Another try 31 | 32 | ```js 33 | /** 34 | * @param {number[]} nums 35 | * @return {string[]} 36 | */ 37 | var summaryRanges = function (nums) { 38 | if (nums.length === 0) return []; 39 | 40 | const intervals = []; 41 | let buffer = [nums[0], nums[0]]; 42 | for (let i = 1; i < nums.length; i++) { 43 | const num = nums[i]; 44 | if (num === buffer[1] + 1) { 45 | buffer[1] = num; 46 | } else { 47 | intervals.push(buffer); 48 | buffer = [num, num]; 49 | } 50 | } 51 | intervals.push(buffer); 52 | 53 | return intervals.map(([start, end]) => 54 | start === end ? `${start}` : `${start}->${end}` 55 | ); 56 | }; 57 | ``` 58 | -------------------------------------------------------------------------------- /230-kth-smallest-element-in-a-bst.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * Definition for a binary tree node. 4 | * function TreeNode(val, left, right) { 5 | * this.val = (val===undefined ? 0 : val) 6 | * this.left = (left===undefined ? null : left) 7 | * this.right = (right===undefined ? null : right) 8 | * } 9 | */ 10 | /** 11 | * @param {TreeNode} root 12 | * @param {number} k 13 | * @return {number} 14 | */ 15 | var kthSmallest = function (root, k) { 16 | // traverse it in an array 17 | const list = []; 18 | const walk = (node) => { 19 | if (node == null) return; 20 | if (node.left) { 21 | walk(node.left); 22 | } 23 | list.push(node.val); 24 | if (node.right) { 25 | walk(node.right); 26 | } 27 | }; 28 | walk(root); 29 | return list[k - 1]; 30 | }; 31 | ``` 32 | -------------------------------------------------------------------------------- /24-swap-nodes-in-pairs.md: -------------------------------------------------------------------------------- 1 | A video explaining the details: https://youtu.be/issaJfZ82EU 2 | 3 | ```js 4 | /** 5 | * Definition for singly-linked list. 6 | * function ListNode(val) { 7 | * this.val = val; 8 | * this.next = null; 9 | * } 10 | */ 11 | /** 12 | * @param {ListNode} head 13 | * @return {ListNode} 14 | */ 15 | var swapPairs = function(head) { 16 | // reverse the nodes after head 17 | const swap = (node) => { 18 | if (node && node.next && node.next.next) { 19 | const first = node.next 20 | const second = node.next.next 21 | first.next = second.next 22 | second.next = first 23 | node.next = second 24 | 25 | swap(first) 26 | } 27 | } 28 | 29 | const start = new ListNode(null) 30 | start.next = head 31 | swap(start) 32 | 33 | return start.next 34 | }; 35 | ``` 36 | -------------------------------------------------------------------------------- /246-strobogrammatic-number.md: -------------------------------------------------------------------------------- 1 | Here is a video explaining: https://www.youtube.com/watch?v=h0MTVIgbhH0 2 | 3 | 4 | ```js 5 | /** 6 | * @param {string} num 7 | * @return {boolean} 8 | */ 9 | // Time: O(n) 10 | // Space: O(1) 11 | var isStrobogrammatic = function(num) { 12 | if (num.length === 0) return true 13 | // 0 14 | // 1 15 | // 1 1 16 | // 6 9 17 | // 9 6 18 | // 8 19 | // 8 8 20 | // 0 0 21 | 22 | let i = 0 23 | let j = num.length - 1 24 | 25 | const strogoSingles = new Set(['0', '1','8']) 26 | const strogoPairs = new Set(['00', '11','69','96','88']) 27 | 28 | const isStrobo = (i, j) => { 29 | if (i === j) { 30 | return strogoSingles.has(num[i]) 31 | } 32 | 33 | return strogoPairs.has(`${num[i]}${num[j]}`) 34 | } 35 | 36 | 37 | while (i <= j) { 38 | 39 | if (!isStrobo(i, j)) { 40 | return false 41 | } 42 | 43 | i += 1 44 | j -= 1 45 | } 46 | 47 | return true 48 | }; 49 | ``` 50 | -------------------------------------------------------------------------------- /247-strobogrammatic-number-ii.md: -------------------------------------------------------------------------------- 1 | Me explaining it on youtube: https://youtu.be/THMY52gMtdo 2 | 3 | ```js 4 | /** 5 | * @param {number} n 6 | * @return {string[]} 7 | */ 8 | 9 | // Time: An = An-2 * 5 => 5^n 10 | // space: O(1) 11 | var findStrobogrammatic = function(n) { 12 | if (n === 0) return [] 13 | // 0 14 | // 1 15 | // 6 - 9 16 | // 8 17 | // 0 0 18 | // 1 1 19 | // 8 8 20 | // 9 - 6 21 | 22 | // 0 1 8 => singles 23 | // 0 0 , 1 1, 8 8, 6 9, 9 6 => pairs 24 | 25 | 26 | // if n is 1 => singles 27 | // if n is 2 => pairs 28 | // if n is odd, put each pair to combinations at n - 2 29 | 30 | const singles = ['0', '1', '8'] 31 | const pairs = ['00', '11', '88', '69', '96'] 32 | 33 | let result = n % 2 === 1 ? singles.slice(0) : [''] 34 | 35 | let length = n % 2 === 1 ? 1 : 0 36 | 37 | while (length < n) { 38 | // push the pairs in to the current combinations 39 | const appended = [] 40 | for (let combination of result) { 41 | for (let pair of pairs) { 42 | if (pair === '00' && length + 2 === n) continue 43 | appended.push(pair[0] + combination + pair[1]) 44 | } 45 | } 46 | result = appended 47 | length += 2 48 | } 49 | 50 | return result 51 | }; 52 | ``` 53 | -------------------------------------------------------------------------------- /249-group-shifted-strings.md: -------------------------------------------------------------------------------- 1 | Here is me explaining it on youtube: https://youtu.be/7dbJCbWGwnc 2 | ```js 3 | /** 4 | * @param {string[]} strings 5 | * @return {string[][]} 6 | */ 7 | 8 | // Time: O(all letters) 9 | // Space: worst O(strings length) 10 | var groupStrings = function(strings) { 11 | // a b d => a (1) b (2) d => b (1) c (2) e => 12 => 1_2 12 | // a (25) z => b (-1) a 25 => 25_1 13 | // a => OK => '' 14 | const LENGTH_ALL_LETTERS = 26 15 | // create a string joined by distances between letters 16 | const getHash = (str) => { 17 | if (!str.length) return 'empty'; 18 | if (str.length === 1) return '' 19 | const distances = [] 20 | for (let i = 1; i < str.length; i++) { 21 | const distance = (str.charCodeAt(i) - str.charCodeAt(i - 1) + LENGTH_ALL_LETTERS) % LENGTH_ALL_LETTERS 22 | distances.push(distance) 23 | } 24 | 25 | return distances.join('') 26 | } 27 | 28 | // Map 29 | const resultMap = new Map() 30 | 31 | for (let string of strings) { 32 | const hash = getHash(string) 33 | let group = resultMap.get(hash) 34 | 35 | if (!group) { 36 | group = [] 37 | resultMap.set(hash, group) 38 | } 39 | 40 | group.push(string) 41 | } 42 | 43 | return [...resultMap.values()] 44 | }; 45 | ``` 46 | -------------------------------------------------------------------------------- /25-reverse-nodes-in-k-group.md: -------------------------------------------------------------------------------- 1 | Youtube video to explain this : https://youtu.be/okUJJo2jV5k 2 | ```js 3 | /** 4 | * Definition for singly-linked list. 5 | * function ListNode(val) { 6 | * this.val = val; 7 | * this.next = null; 8 | * } 9 | */ 10 | /** 11 | * @param {ListNode} head 12 | * @param {number} k 13 | * @return {ListNode} 14 | */ 15 | // TIME: O(2N) 16 | // Space: O(1) 17 | var reverseKGroup = function(head, k) { 18 | // reverse the list except the head 19 | const reverse = (node) => { 20 | // 1 - 2, 3, 4 21 | // 1 - 3, 2, 4 22 | // 1 - 4, 3, 2 23 | let p = node.next 24 | 25 | while (p.next) { 26 | const next = p.next.next 27 | 28 | p.next.next = node.next 29 | node.next = p.next 30 | p.next = next 31 | } 32 | 33 | return p 34 | } 35 | 36 | // swap k nodes, starting from node.next 37 | const swap = (node) => { 38 | if (node === null) return 39 | 40 | let count = 0 41 | let end = node.next 42 | while (end && count < k - 1) { 43 | count += 1 44 | end = end.next 45 | } 46 | 47 | // if there is enough k nodes 48 | if (end) { 49 | let next = end.next 50 | end.next = null 51 | 52 | // reverse the nodes and concat 53 | const newEnd = reverse(node) 54 | newEnd = next 55 | 56 | // continue 57 | swap(newEnd) 58 | } 59 | } 60 | 61 | const start = new ListNode() 62 | start.next = head 63 | 64 | swap(start) 65 | 66 | return start.next 67 | }; 68 | ``` 69 | -------------------------------------------------------------------------------- /252-meeting-rooms.md: -------------------------------------------------------------------------------- 1 | Here is a video explaining it : https://youtu.be/Qd7LaGypkS0 2 | 3 | 4 | ```js 5 | /** 6 | * @param {number[][]} intervals 7 | * @return {boolean} 8 | */ 9 | 10 | // Time: O(nlogn + n) => O(nlogn) 11 | // space: O(1) 12 | var canAttendMeetings = function(intervals) { 13 | // [ ] [ ] 14 | // [ ] [] 15 | 16 | // 1. sort first 17 | // 2. loop though the intervals and check against previous one (adjasent interval) 18 | 19 | const isOverlapping = (interval1, interval2) => { 20 | return !(interval1[1] <= interval2[0] || interval1[0] >= interval2[1]) 21 | } 22 | 23 | intervals.sort((a, b) => a[0] - b[0]) 24 | 25 | for (let i = 1; i < intervals.length; i++) { 26 | if (isOverlapping(intervals[i], intervals[i - 1])) { 27 | return false 28 | } 29 | } 30 | 31 | return true 32 | }; 33 | ``` 34 | -------------------------------------------------------------------------------- /259-3sum-smaller.md: -------------------------------------------------------------------------------- 1 | Here is my youtube video to explain this: https://youtu.be/W2c5NjpEnX8 2 | ```js 3 | /** 4 | * @param {number[]} nums 5 | * @param {number} target 6 | * @return {number} 7 | */ 8 | var threeSumSmaller = function(nums, target) { 9 | // brute force O(N^3) 10 | 11 | // 1. sort first O(nlogn) 12 | // 2. first the left number => 2 sum problem O(n) => O(n^2) 13 | 14 | if (nums.length < 3) return 0 15 | 16 | nums.sort((a, b) => a - b) 17 | 18 | let result = 0 19 | // -2 0 0 2 2 20 | for (let i = 0; i + 2 < nums.length; i++) { 21 | let start = i + 1 22 | let end = nums.length - 1 23 | while (start < end) { 24 | const sum = nums[i] + nums[start] + nums[end] 25 | if (sum < target) { 26 | result += end - start 27 | } 28 | 29 | if (sum < target) { 30 | start += 1 31 | } else { 32 | end -= 1 33 | } 34 | } 35 | } 36 | 37 | return result 38 | }; 39 | ``` 40 | -------------------------------------------------------------------------------- /26-remove-duplicates-from-sorted-array.md: -------------------------------------------------------------------------------- 1 | A video for this problem: https://youtu.be/41PEg1iEZhc 2 | 3 | ```js 4 | /** 5 | * @param {number[]} nums 6 | * @return {number} 7 | */ 8 | 9 | // TIME: O(N) 10 | // Space: O(1) 11 | var removeDuplicates = function (nums) { 12 | let p1 = 0; 13 | let p2 = 0; 14 | 15 | while (p2 < nums.length) { 16 | if (nums[p2] !== nums[p2 - 1]) { 17 | nums[p1] = nums[p2]; 18 | p1 += 1; 19 | } 20 | 21 | p2 += 1; 22 | } 23 | 24 | return p1; 25 | }; 26 | ``` 27 | -------------------------------------------------------------------------------- /266-palindrome-permutation.md: -------------------------------------------------------------------------------- 1 | Here is my youtube video: https://youtu.be/roRCxBmstHQ 2 | 3 | ```js 4 | /** 5 | * @param {string} s 6 | * @return {boolean} 7 | */ 8 | var canPermutePalindrome = function(s) { 9 | // aba abba 10 | // 1 odd count, all even else 11 | // all even 12 | 13 | // count the character, odd count must be <= 1 14 | 15 | const count = new Map() 16 | 17 | for (let char of s) { 18 | if (count.has(char)) { 19 | count.set(char, count.get(char) + 1) 20 | } else { 21 | count.set(char, 1) 22 | } 23 | } 24 | 25 | let countOfOddOccurance = 0 26 | for (let occurance of count.values()) { 27 | if (occurance % 2 === 1) { 28 | countOfOddOccurance += 1 29 | } 30 | 31 | if (countOfOddOccurance > 1) { 32 | return false 33 | } 34 | } 35 | 36 | return true 37 | }; 38 | ``` 39 | -------------------------------------------------------------------------------- /27-remove-element.md: -------------------------------------------------------------------------------- 1 | ## 1. Put the item at correct position 2 | 3 | A video explaining this: https://youtu.be/PTLuyqH2VkM 4 | 5 | ```js 6 | /** 7 | * @param {number[]} nums 8 | * @param {number} val 9 | * @return {number} 10 | */ 11 | var removeElement = function (nums, val) { 12 | let p1 = 0; 13 | let p2 = 0; 14 | 15 | while (p2 < nums.length) { 16 | if (nums[p2] !== val) { 17 | nums[p1] = nums[p2]; 18 | p1 += 1; 19 | } 20 | 21 | p2 += 1; 22 | } 23 | 24 | return p1; 25 | }; 26 | ``` 27 | 28 | ## 2. Swap with the last item 29 | 30 | ```js 31 | var removeElement = function (nums, val) { 32 | // swap the val with last element 33 | let i = 0; 34 | let j = nums.length - 1; 35 | while (i <= j) { 36 | if (nums[i] === val) { 37 | swap(i, j); 38 | j -= 1; 39 | } else { 40 | i += 1; 41 | } 42 | } 43 | 44 | // j is to be non val 45 | // i is to be the val 46 | 47 | function swap(i, j) { 48 | [nums[i], nums[j]] = [nums[j], nums[i]]; 49 | } 50 | 51 | return j + 1; 52 | }; 53 | ``` 54 | -------------------------------------------------------------------------------- /270-closest-binary-search-tree-value.md: -------------------------------------------------------------------------------- 1 | Here is me explaining it on youtube: https://youtu.be/gm-x2JHB2DQ 2 | ```js 3 | /** 4 | * Definition for a binary tree node. 5 | * function TreeNode(val, left, right) { 6 | * this.val = (val===undefined ? 0 : val) 7 | * this.left = (left===undefined ? null : left) 8 | * this.right = (right===undefined ? null : right) 9 | * } 10 | */ 11 | /** 12 | * @param {TreeNode} root 13 | * @param {number} target 14 | * @return {number} 15 | */ 16 | 17 | // Time: O(log N) worst O(N) 18 | // Space: O(1) 19 | var closestValue = function(root, target) { 20 | // binary search target 21 | // update closest value along the path 22 | 23 | let result = Infinity 24 | 25 | let node = root 26 | 27 | while (node !== null) { 28 | if (node.val === target) { 29 | result = target 30 | break 31 | } 32 | 33 | if (Math.abs(node.val - target) < Math.abs(result - target)) { 34 | result = node.val 35 | } 36 | 37 | if (target > node.val) { 38 | node = node.right 39 | } else { 40 | node = node.left 41 | } 42 | } 43 | 44 | return result 45 | }; 46 | ``` 47 | -------------------------------------------------------------------------------- /278-first-bad-version.md: -------------------------------------------------------------------------------- 1 | Here is my video explaining it: https://youtu.be/uHEvS8VO0o4 2 | 3 | ```js 4 | /** 5 | * Definition for isBadVersion() 6 | * 7 | * @param {integer} version number 8 | * @return {boolean} whether the version is bad 9 | * isBadVersion = function(version) { 10 | * ... 11 | * }; 12 | */ 13 | 14 | /** 15 | * @param {function} isBadVersion() 16 | * @return {function} 17 | */ 18 | var solution = function (isBadVersion) { 19 | /** 20 | * @param {integer} n Total versions 21 | * @return {integer} The first bad version 22 | */ 23 | return function (n) { 24 | let i = 1; 25 | let j = n; 26 | while (i <= j) { 27 | const middle = Math.floor((i + j) / 2); 28 | if (isBadVersion(middle)) { 29 | j = middle - 1; 30 | } else { 31 | i = middle + 1; 32 | } 33 | } 34 | return i; 35 | }; 36 | }; 37 | ``` 38 | -------------------------------------------------------------------------------- /283-move-zeroes.md: -------------------------------------------------------------------------------- 1 | Here is my video explaining it: https://youtu.be/ex8k9RThrOQ 2 | 3 | 4 | ```js 5 | /** 6 | * @param {number[]} nums 7 | * @return {void} Do not return anything, modify nums in-place instead. 8 | */ 9 | var moveZeroes = function(nums) { 10 | // 1 0 0 3 11 | // 1 3 0 0 12 12 | // 1 3 12 0 0 13 | 14 | 15 | // 0 1 0 3 12 16 | // 12 1 0 3 0 17 | // 12 1 3 0 0 18 | 19 | let positionToSwap = 0 20 | 21 | for (let i = 0; i < nums.length; i++) { 22 | if (nums[i] === 0) continue 23 | 24 | ;[nums[i], nums[positionToSwap]] = [nums[positionToSwap], nums[i]] 25 | positionToSwap += 1 26 | } 27 | }; 28 | ``` 29 | -------------------------------------------------------------------------------- /290-word-pattern.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {string} pattern 4 | * @param {string} s 5 | * @return {boolean} 6 | */ 7 | var wordPattern = function (pattern, s) { 8 | const words = s.split(" "); 9 | const chars = pattern.split(""); 10 | if (words.length !== chars.length) { 11 | return false; 12 | } 13 | const map = new Map(); 14 | const mappedWords = new Set(); 15 | while (words.length > 0) { 16 | const word = words.pop(); 17 | const char = chars.pop(); 18 | if (map.has(char)) { 19 | if (map.get(char) !== word) { 20 | return false; 21 | } 22 | } else { 23 | map.set(char, word); 24 | if (mappedWords.has(word)) { 25 | return false; 26 | } 27 | mappedWords.add(word); 28 | } 29 | } 30 | return true; 31 | }; 32 | ``` 33 | -------------------------------------------------------------------------------- /3-longest-substring-without-repeating-characters.md: -------------------------------------------------------------------------------- 1 | sliding window. 2 | 3 | ```js 4 | /** 5 | * @param {string} s 6 | * @return {number} 7 | */ 8 | var lengthOfLongestSubstring = function (s) { 9 | if (s.length === 0) return 0; 10 | // [i, j] 11 | // keep track of the index. 12 | const indexMap = new Map(); 13 | 14 | // for each step 15 | // move the right boundary to the right 16 | // if found the duplicate, move i to that index 17 | 18 | // abcabcbb 19 | let i = 0; // 1 20 | let j = 0; // 3 21 | let result = 0; 22 | 23 | // for each move, it is valid 24 | while (j < s.length) { 25 | const char = s[j]; 26 | if (indexMap.has(char)) { 27 | while (i <= indexMap.get(char)) { 28 | indexMap.delete(s[i]); 29 | i += 1; 30 | } 31 | } 32 | indexMap.set(char, j); 33 | result = Math.max(result, j - i + 1); 34 | j += 1; 35 | } 36 | 37 | return result; 38 | }; 39 | ``` 40 | -------------------------------------------------------------------------------- /304-range-sum-query-2d-immutable.md: -------------------------------------------------------------------------------- 1 | Me explaining it on youtube: https://youtu.be/8caqneyUoWs 2 | 3 | ```js 4 | /** 5 | * @param {number[][]} matrix 6 | */ 7 | 8 | // Time: O(N) 9 | // Space: O(MN) 10 | var NumMatrix = function(matrix) { 11 | // sum(i, j, k, l) === sum(0, 0, k, l) - sum(0, 0, i - , l) - sum(0, 0, k, j - 1) + sum(0, 0, i - 1, j - 1) 12 | const rows = matrix.length 13 | if (rows === 0) return 0 14 | const cols = matrix[0].length 15 | if (cols === 0) return 0 16 | const sumFromOrigin = Array(rows + 1).fill(0).map(_ => Array(cols + 1).fill(0)) 17 | 18 | for (let i = 0; i < rows; i++) { 19 | for (let j = 0; j < cols; j++) { 20 | const row = i + 1 21 | const col = j + 1 22 | sumFromOrigin[row][col] = sumFromOrigin[row][col - 1] - sumFromOrigin[row - 1][col - 1] + sumFromOrigin[row - 1][col] + matrix[i][j] 23 | } 24 | } 25 | 26 | this.sumFromOrigin = sumFromOrigin 27 | }; 28 | 29 | /** 30 | * @param {number} row1 31 | * @param {number} col1 32 | * @param {number} row2 33 | * @param {number} col2 34 | * @return {number} 35 | */ 36 | 37 | // Time: O(1) 38 | // Space: O(1) 39 | NumMatrix.prototype.sumRegion = function(row1, col1, row2, col2) { 40 | const sumFromOrigin = this.sumFromOrigin 41 | return sumFromOrigin[row2 + 1][col2 + 1] - sumFromOrigin[row2 + 1][col1 + 1 - 1] - sumFromOrigin[row1 + 1 - 1][col2 + 1] + sumFromOrigin[row1 + 1 - 1][col1 + 1 - 1] 42 | }; 43 | 44 | /** 45 | * Your NumMatrix object will be instantiated and called as such: 46 | * var obj = new NumMatrix(matrix) 47 | * var param_1 = obj.sumRegion(row1,col1,row2,col2) 48 | */ 49 | ``` 50 | -------------------------------------------------------------------------------- /3162-find-the-number-of-good-pairs-i.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {number[]} nums1 4 | * @param {number[]} nums2 5 | * @param {number} k 6 | * @return {number} 7 | */ 8 | 9 | // O(mn) 10 | var numberOfPairs = function (nums1, nums2, k) { 11 | let result = 0; 12 | nums1.forEach((a) => { 13 | nums2.forEach((b) => { 14 | if (b * k !== 0 && a % (b * k) === 0) { 15 | result += 1; 16 | } 17 | }); 18 | }); 19 | return result; 20 | }; 21 | ``` 22 | -------------------------------------------------------------------------------- /3163-string-compression-iii.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {string} word 4 | * @return {string} 5 | */ 6 | var compressedString = function (word) { 7 | // maintain a buffer 8 | // flush on new character, or last one, or buffer is overflowed 9 | let result = ""; 10 | let buffer = ""; 11 | let count = 0; 12 | for (const char of word) { 13 | if (buffer === "") { 14 | buffer = char; 15 | count += 1; 16 | } else if (char !== buffer[0]) { 17 | result += count + buffer; 18 | buffer = char; 19 | count = 1; 20 | } else { 21 | if (count === 9) { 22 | result += count + buffer; 23 | count = 1; 24 | } else { 25 | count += 1; 26 | } 27 | } 28 | } 29 | if (count > 0) { 30 | result += count + buffer; 31 | } 32 | return result; 33 | }; 34 | ``` 35 | -------------------------------------------------------------------------------- /3168-minimum-number-of-chairs-in-a-waiting-room.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {string} s 4 | * @return {number} 5 | */ 6 | var minimumChairs = function (s) { 7 | let count = 0; 8 | let max = 0; 9 | for (const char of s) { 10 | if (char === "E") { 11 | count += 1; 12 | max = Math.max(max, count); 13 | } else { 14 | count -= 1; 15 | } 16 | } 17 | return max; 18 | }; 19 | ``` 20 | -------------------------------------------------------------------------------- /3169-count-days-without-meetings.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {number} days 4 | * @param {number[][]} meetings 5 | * @return {number} 6 | */ 7 | var countDays = function (days, meetings) { 8 | // sor the meetings and merge them 9 | // then just collect in one pass 10 | meetings.sort((a, b) => a[0] - b[0]); 11 | const merged = []; 12 | let buffer = null; 13 | for (const meeting of meetings) { 14 | // meetings are sorted 15 | 16 | // if no overlap 17 | if (buffer == null) { 18 | buffer = meeting; 19 | continue; 20 | } 21 | if (buffer[1] < meeting[0]) { 22 | merged.push(buffer); 23 | buffer = meeting; 24 | } else { 25 | // overlap 26 | buffer[1] = Math.max(buffer[1], meeting[1]); 27 | } 28 | } 29 | if (buffer != null) { 30 | merged.push(buffer); 31 | } 32 | 33 | let result = 0; 34 | let prevMeetingEndDay = 0; 35 | for (const meeting of merged) { 36 | result += meeting[0] - prevMeetingEndDay - 1; 37 | prevMeetingEndDay = meeting[1]; 38 | } 39 | result += days - prevMeetingEndDay; 40 | 41 | return result; 42 | }; 43 | ``` 44 | -------------------------------------------------------------------------------- /3170-lexicographically-minimum-string-after-removing-stars.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {string} s 4 | * @return {string} 5 | */ 6 | var clearStars = function (s) { 7 | // aaba**zz* 8 | // remove the smallest, if multiple, choose the right most one 9 | // array 26 to hold the right most index? 10 | 11 | // a [0] 12 | // b [2] 13 | // z [2] 14 | 15 | // deleted Set to hold the index 16 | 17 | const indicesArr = []; 18 | const deletedIndices = new Set(); 19 | const charCodeA = "a".charCodeAt(0); 20 | 21 | for (let i = 0; i < s.length; i++) { 22 | const char = s[i]; 23 | if (char === "*") { 24 | for (let i = 0; i < 26; i++) { 25 | if (indicesArr[i]?.length > 0) { 26 | deletedIndices.add(indicesArr[i].pop()); 27 | break; 28 | } 29 | } 30 | } else { 31 | const j = s.charCodeAt(i) - charCodeA; 32 | if (indicesArr[j] == null) { 33 | indicesArr[j] = []; 34 | } 35 | indicesArr[j].push(i); 36 | } 37 | } 38 | let result = ""; 39 | for (let i = 0; i < s.length; i++) { 40 | const char = s[i]; 41 | if (char !== "*" && !deletedIndices.has(i)) { 42 | result += char; 43 | } 44 | } 45 | return result; 46 | }; 47 | ``` 48 | -------------------------------------------------------------------------------- /32-longest-valid-parentheses.md: -------------------------------------------------------------------------------- 1 | Youtube video explaining this: https://youtu.be/tyLRMPlrvno 2 | 3 | ```js 4 | /** 5 | * @param {string} s 6 | * @return {number} 7 | */ 8 | 9 | // Time: O(n) 10 | // Space: O(n) 11 | var longestValidParentheses = function(s) { 12 | if (s.length === 0) return 0 13 | // e.g. )()()(( 14 | // XOOOOXXXOOOOOXX 15 | // ))))((( 16 | 17 | // solution is use a stack to track the invalid parenthese index 18 | 19 | const stack = [] 20 | let result = 0 21 | for(let i = 0; i < s.length + 1; i++) { 22 | if (s[i] === '(') { 23 | // if they are not matched, then check the substring 24 | result = Math.max(result, i - (stack.length ? stack[stack.length - 1] : -1) - 1) 25 | stack.push(i) 26 | } else { 27 | // if there is none element in the stack, it is from the beginning 28 | // get the length of strings to the left side 29 | if (stack.length === 0) { 30 | result = Math.max(result, i + 1 - 1) 31 | stack.push(i) 32 | } else { 33 | const topIndex = stack.pop() 34 | if (s[topIndex] !== '(' || i >= s.length ) { 35 | // if they are not matched, then check the substring 36 | result = Math.max(result, i - topIndex - 1) 37 | stack.push(i) 38 | } 39 | } 40 | } 41 | } 42 | 43 | return result 44 | }; 45 | ``` 46 | -------------------------------------------------------------------------------- /322-coin-change.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {number[]} coins 4 | * @param {number} amount 5 | * @return {number} 6 | */ 7 | // O(C*A) 8 | var coinChange = function (coins, amount) { 9 | const numOfCoins = new Array(amount + 1).fill(Infinity); 10 | numOfCoins[0] = 0; 11 | for (let i = 0; i < amount; i++) { 12 | for (const coin of coins) { 13 | numOfCoins[i + coin] = Math.min(numOfCoins[i + coin], numOfCoins[i] + 1); 14 | } 15 | } 16 | return numOfCoins[amount] === Infinity ? -1 : numOfCoins[amount]; 17 | }; 18 | ``` 19 | 20 | Above is very slow though, can we do better? 21 | -------------------------------------------------------------------------------- /323-number-of-connected-components-in-an-undirected-graph.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {number} n 4 | * @param {number[][]} edges 5 | * @return {number} 6 | */ 7 | var countComponents = function (n, edges) { 8 | // DFS or BFS, count, visited 9 | const edgeMap = new Map(); 10 | for (const [from, to] of edges) { 11 | if (edgeMap.has(from)) { 12 | edgeMap.get(from).add(to); 13 | } else { 14 | edgeMap.set(from, new Set([to])); 15 | } 16 | 17 | if (edgeMap.has(to)) { 18 | edgeMap.get(to).add(from); 19 | } else { 20 | edgeMap.set(to, new Set([from])); 21 | } 22 | } 23 | 24 | let count = 0; 25 | const visited = new Set(); 26 | const walk = (i) => { 27 | if (visited.has(i)) { 28 | return; 29 | } 30 | 31 | visited.add(i); 32 | if (edgeMap.has(i)) { 33 | for (const to of edgeMap.get(i)) { 34 | walk(to); 35 | } 36 | } 37 | }; 38 | 39 | for (let i = 0; i < n; i++) { 40 | if (!visited.has(i)) { 41 | count += 1; 42 | walk(i); 43 | } 44 | } 45 | return count; 46 | }; 47 | ``` 48 | -------------------------------------------------------------------------------- /340-longest-substring-with-at-most-k-distinct-characters.md: -------------------------------------------------------------------------------- 1 | Me explaining this on youtube: https://youtu.be/YORTb_6JxYQ 2 | 3 | ```js 4 | /** 5 | * @param {string} s 6 | * @param {number} k 7 | * @return {number} 8 | */ 9 | 10 | // Time: O(2N) => O(n) 11 | // space: O(2) => O(1) 12 | var lengthOfLongestSubstringKDistinct = function(s, k) { 13 | if (k === 0) return 0 14 | // eceba 15 | // eba 16 | 17 | // slide window 18 | // keep track of the occurence with a MAP 19 | // when add one, and overflowed, remove one from left 20 | 21 | const count = new Map() 22 | let max = 0 23 | 24 | let i = 0 25 | let j = 0 26 | 27 | while (j < s.length) { 28 | const charJ = s[j] 29 | 30 | if (count.has(charJ)) { 31 | count.set(charJ, count.get(charJ) + 1) 32 | } else { 33 | if (count.size === k) { 34 | // need to remove chars from left 35 | while (count.size > k - 1) { 36 | const charI = s[i] 37 | const countI = count.get(charI) 38 | if (countI === 1) { 39 | count.delete(charI) 40 | } else { 41 | count.set(charI, countI - 1) 42 | } 43 | 44 | i += 1 45 | } 46 | } 47 | 48 | // safely add it to hashmap 49 | count.set(charJ, 1) 50 | } 51 | 52 | max = Math.max(max, j - i + 1) 53 | j += 1 54 | } 55 | 56 | return max 57 | }; 58 | ``` 59 | -------------------------------------------------------------------------------- /341-flatten-nested-list-iterator.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @constructor 4 | * @param {NestedInteger[]} nestedList 5 | */ 6 | var NestedIterator = function (nestedList) { 7 | this.stack = []; 8 | this.flatten(nestedList); 9 | }; 10 | 11 | /** 12 | * @this NestedIterator 13 | * @returns {boolean} 14 | */ 15 | NestedIterator.prototype.hasNext = function () { 16 | return this.stack.length > 0; 17 | }; 18 | 19 | NestedIterator.prototype.flatten = function (nestedList) { 20 | const queue = [nestedList]; 21 | while (queue.length > 0) { 22 | const top = queue.pop(); 23 | if (Array.isArray(top)) { 24 | queue.push(...top); 25 | } else if (top.isInteger()) { 26 | this.stack.push(top.getInteger()); 27 | } else { 28 | queue.push(...top.getList()); 29 | } 30 | } 31 | }; 32 | 33 | /** 34 | * @this NestedIterator 35 | * @returns {integer} 36 | */ 37 | NestedIterator.prototype.next = function () { 38 | return this.stack.pop(); 39 | }; 40 | 41 | /** 42 | * Your NestedIterator will be called like this: 43 | * var i = new NestedIterator(nestedList), a = []; 44 | * while (i.hasNext()) a.push(i.next()); 45 | */ 46 | ``` 47 | -------------------------------------------------------------------------------- /344-reverse-string.md: -------------------------------------------------------------------------------- 1 | Me explaining this : https://youtu.be/-j0iiF2Qlmo 2 | 3 | 4 | ```js 5 | /** 6 | * @param {character[]} s 7 | * @return {void} Do not return anything, modify s in-place instead. 8 | */ 9 | 10 | // Time: O(n) 11 | var reverseString = function(s) { 12 | let start = 0 13 | let end = s.length - 1 14 | 15 | while (start < end) { 16 | [s[start], s[end]] = [s[end], s[start]] 17 | start += 1 18 | end -= 1 19 | } 20 | 21 | return s 22 | }; 23 | ``` 24 | -------------------------------------------------------------------------------- /35-search-insert-position.md: -------------------------------------------------------------------------------- 1 | Youtube video explaining this: https://youtu.be/6EDD1vSSgOk 2 | 3 | ```js 4 | /** 5 | * @param {number[]} nums 6 | * @param {number} target 7 | * @return {number} 8 | */ 9 | var searchInsert = function(nums, target) { 10 | let start = 0 11 | let end = nums.length - 1 12 | 13 | while (start <= end) { 14 | const middle = Math.floor((start + end) / 2) 15 | if (nums[middle] === target) { 16 | return middle 17 | } else if (nums[middle] < target) { 18 | start = middle + 1 19 | } else { 20 | end = middle - 1 21 | } 22 | } 23 | // end target start 24 | return start 25 | }; 26 | ``` 27 | -------------------------------------------------------------------------------- /350-intersection-of-two-arrays-ii.md: -------------------------------------------------------------------------------- 1 | Here is my youtube video link explaining this: https://youtu.be/AEYOwT_-p8o 2 | 3 | 4 | ```js 5 | /** 6 | * @param {number[]} nums1 7 | * @param {number[]} nums2 8 | * @return {number[]} 9 | */ 10 | var intersect = function(nums1, nums2) { 11 | // nums1 = [1,2,2,1], nums2 = [2,2] 12 | 13 | // n m 14 | // O(nm) + space O(min(n, m)) 15 | 16 | // preprocess nums2 to a Map 17 | // O(n + m) + space(min(n, m)) 18 | // process the shorter one 19 | 20 | let preprocessTarget = nums1 21 | let loopTarget = nums2 22 | 23 | if (nums1.length > nums2.length) { 24 | preprocessTarget = nums2 25 | loopTarget = nums1 26 | } 27 | 28 | // Map 29 | const countMap = new Map() 30 | for (let num of preprocessTarget) { 31 | if (countMap.has(num)) { 32 | countMap.set(num, countMap.get(num) + 1) 33 | } else { 34 | countMap.set(num, 1) 35 | } 36 | } 37 | 38 | 39 | const result = [] 40 | 41 | for (let num of loopTarget) { 42 | if (countMap.has(num)) { 43 | result.push(num) 44 | 45 | const count = countMap.get(num) 46 | if (count === 1) { 47 | countMap.delete(num) 48 | } else { 49 | countMap.set(num, count - 1) 50 | } 51 | } 52 | } 53 | 54 | return result 55 | }; 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | ``` 69 | -------------------------------------------------------------------------------- /359-logger-rate-limiter.md: -------------------------------------------------------------------------------- 1 | Here is my video explaining it : https://youtu.be/nei1-vf2WmU 2 | 3 | ```js 4 | /** 5 | * Initialize your data structure here. 6 | */ 7 | var Logger = function() { 8 | // Map 9 | this.printed = new Map() 10 | }; 11 | 12 | /** 13 | * Returns true if the message should be printed in the given timestamp, otherwise returns false. 14 | If this method returns false, the message will not be printed. 15 | The timestamp is in seconds granularity. 16 | * @param {number} timestamp 17 | * @param {string} message 18 | * @return {boolean} 19 | */ 20 | Logger.prototype.shouldPrintMessage = function(timestamp, message) { 21 | if (this.printed.has(message)) { 22 | const last = this.printed.get(message) 23 | if (timestamp - last < 10) { 24 | return false 25 | } 26 | } 27 | 28 | this.printed.set(message, timestamp) 29 | return true 30 | }; 31 | 32 | /** 33 | * Your Logger object will be instantiated and called as such: 34 | * var obj = new Logger() 35 | * var param_1 = obj.shouldPrintMessage(timestamp,message) 36 | */ 37 | ``` 38 | -------------------------------------------------------------------------------- /36-valid-sudoku.md: -------------------------------------------------------------------------------- 1 | A video explaining this: https://youtu.be/1-6VjaJg2pM 2 | 3 | ```js 4 | /** 5 | * @param {character[][]} board 6 | * @return {boolean} 7 | */ 8 | 9 | 10 | // Brute Force, with 3 passes 9 * 9 * 3 11 | 12 | // let's improve it to 1 pass 13 | var isValidSudoku = function(board) { 14 | const isUsedInRow = new Array(9).fill(0).map(_ => new Array()) 15 | const isUsedInCol = new Array(9).fill(0).map(_ => new Array()) 16 | const isUsedInSub = new Array(9).fill(0).map(_ => new Array()) 17 | 18 | for (let i = 0; i < 9; i++) { 19 | for (let j = 0; j < 9; j++) { 20 | const num = board[i][j] 21 | if (num === '.') continue 22 | const subBoxIndex = Math.floor(i / 3) + Math.floor(j / 3) * 3 23 | if (isUsedInRow[i][num] || isUsedInCol[j][num] || isUsedInSub[subBoxIndex][num]) { 24 | return false 25 | } 26 | isUsedInRow[i][num] = true 27 | isUsedInCol[j][num] = true 28 | isUsedInSub[subBoxIndex][num] = true 29 | } 30 | } 31 | 32 | return true 33 | }; 34 | ``` 35 | -------------------------------------------------------------------------------- /362-design-hit-counter.md: -------------------------------------------------------------------------------- 1 | ```js 2 | var HitCounter = function () { 3 | // use a sliding window to keep track of the hit count in the last 5 minutes 4 | // since the timestamp i monotonic 5 | // we only need to maintain 5 items in an array 6 | 7 | this.buffer = []; 8 | this.count = 0; 9 | }; 10 | 11 | /** 12 | * @param {number} timestamp 13 | * @return {void} 14 | */ 15 | HitCounter.prototype.hit = function (timestamp) { 16 | const buffer = this.buffer; 17 | if (buffer.length === 0) { 18 | buffer.push([timestamp, 1]); 19 | this.count = 1; 20 | } else if (timestamp === buffer[buffer.length - 1][0]) { 21 | buffer[buffer.length - 1][1] += 1; 22 | this.count += 1; 23 | } else { 24 | this.shift(timestamp); 25 | buffer.push([timestamp, 1]); 26 | this.count += 1; 27 | } 28 | }; 29 | 30 | HitCounter.prototype.shift = function (timestamp) { 31 | const buffer = this.buffer; 32 | while (buffer.length > 0 && buffer[0][0] <= timestamp - 300) { 33 | this.count -= buffer[0][1]; 34 | buffer.shift(); 35 | } 36 | }; 37 | 38 | /** 39 | * @param {number} timestamp 40 | * @return {number} 41 | */ 42 | HitCounter.prototype.getHits = function (timestamp) { 43 | this.shift(timestamp); 44 | return this.count; 45 | }; 46 | 47 | /** 48 | * Your HitCounter object will be instantiated and called as such: 49 | * var obj = new HitCounter() 50 | * obj.hit(timestamp) 51 | * var param_2 = obj.getHits(timestamp) 52 | */ 53 | ``` 54 | -------------------------------------------------------------------------------- /374-guess-number-higher-or-lower.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * Forward declaration of guess API. 4 | * @param {number} num your guess 5 | * @return -1 if num is higher than the picked number 6 | * 1 if num is lower than the picked number 7 | * otherwise return 0 8 | * var guess = function(num) {} 9 | */ 10 | 11 | /** 12 | * @param {number} n 13 | * @return {number} 14 | */ 15 | var guessNumber = function (n) { 16 | let i = 1; 17 | let j = n; 18 | while (i <= j) { 19 | const m = Math.floor((i + j) / 2); 20 | const guessResult = guess(m); 21 | if (guessResult === 0) { 22 | return m; 23 | } else if (guessResult < 0) { 24 | j = m - 1; 25 | } else { 26 | i = m + 1; 27 | } 28 | } 29 | // should not come here 30 | throw new Error("there should be a result"); 31 | }; 32 | ``` 33 | -------------------------------------------------------------------------------- /38-count-and-say.md: -------------------------------------------------------------------------------- 1 | A video explaining this: 2 | https://youtu.be/KKmM4weS5x0 3 | 4 | 5 | ```js 6 | /** 7 | * @param {number} n 8 | * @return {string} 9 | */ 10 | 11 | // Time: n 12 | var countAndSay = function(n) { 13 | let result = '1' 14 | 15 | while (n-- > 1) { 16 | let next = '' 17 | let count = 1 18 | let current = result[0] 19 | 20 | for (let i = 1; i < result.length + 1; i++) { 21 | if (result[i] !== current) { 22 | next += `${count}${current}` 23 | current = result[i] 24 | count = 1 25 | } else { 26 | count += 1 27 | } 28 | } 29 | 30 | result = next 31 | } 32 | 33 | return result 34 | }; 35 | ``` 36 | -------------------------------------------------------------------------------- /383-ransom-note.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {string} ransomNote 4 | * @param {string} magazine 5 | * @return {boolean} 6 | */ 7 | var canConstruct = function (ransomNote, magazine) { 8 | const countMagazine = count(magazine); 9 | for (const char of ransomNote) { 10 | const count = (countMagazine.get(char) ?? 0) - 1; 11 | if (count < 0) { 12 | return false; 13 | } 14 | countMagazine.set(char, count); 15 | } 16 | return true; 17 | }; 18 | 19 | function count(str) { 20 | const map = new Map(); 21 | for (const char of str) { 22 | map.set(char, (map.get(char) ?? 0) + 1); 23 | } 24 | return map; 25 | } 26 | ``` 27 | -------------------------------------------------------------------------------- /392-is-subsequence.md: -------------------------------------------------------------------------------- 1 | Here is my video explaining it :https://youtu.be/LIpGKvF3Qz0 2 | 3 | ```js 4 | /** 5 | * @param {string} s 6 | * @param {string} t 7 | * @return {boolean} 8 | */ 9 | 10 | // two cursors 11 | var isSubsequence = function (s, t) { 12 | // "abc", "ahbgdc" 13 | 14 | // abc a(h)b(gd)c 15 | 16 | let i = 0; 17 | let j = 0; 18 | 19 | while (i < s.length && j < t.length) { 20 | if (s[i] === t[j]) { 21 | i += 1; 22 | j += 1; 23 | } else { 24 | j += 1; 25 | } 26 | } 27 | 28 | return i === s.length; 29 | }; 30 | ``` 31 | 32 | ```js 33 | /** 34 | * @param {string} s 35 | * @param {string} t 36 | * @return {boolean} 37 | */ 38 | var isSubsequence = function (s, t) { 39 | if (s.length === "") return true; 40 | 41 | let i = 0; 42 | let j = 0; 43 | while (i < s.length) { 44 | // for each char in s 45 | // we try to match one in t 46 | while (j < t.length && t[j] !== s[i]) { 47 | j += 1; 48 | } 49 | 50 | if (j >= t.length) { 51 | return false; 52 | } 53 | 54 | i += 1; 55 | j += 1; 56 | } 57 | 58 | return true; 59 | }; 60 | ``` 61 | -------------------------------------------------------------------------------- /398-random-pick-index.md: -------------------------------------------------------------------------------- 1 | Here is me explaining it: https://youtu.be/ySrinuKlHP4 2 | 3 | ```js 4 | /** 5 | * @param {number[]} nums 6 | */ 7 | // O(n) and O(n) 8 | var Solution = function(nums) { 9 | // Space O(n) 10 | // Map 11 | const indexMap = new Map() 12 | 13 | for (let i = 0; i < nums.length; i++) { 14 | if (!indexMap.has(nums[i])) { 15 | indexMap.set(nums[i], []) 16 | } 17 | 18 | indexMap.get(nums[i]).push(i) 19 | } 20 | 21 | this.indexMap = indexMap 22 | }; 23 | 24 | /** 25 | * @param {number} target 26 | * @return {number} 27 | */ 28 | // O(1) 29 | Solution.prototype.pick = function(target) { 30 | const indices = this.indexMap.get(target) 31 | const randomI = Math.floor(indices.length * Math.random()) 32 | return indices[randomI] 33 | }; 34 | 35 | /** 36 | * Your Solution object will be instantiated and called as such: 37 | * var obj = new Solution(nums) 38 | * var param_1 = obj.pick(target) 39 | */ 40 | ``` 41 | -------------------------------------------------------------------------------- /40-combination-sum-ii.md: -------------------------------------------------------------------------------- 1 | Video explaining this: https://youtu.be/0gY2ji6K66c 2 | 3 | 4 | ```js 5 | // Time: worst O(2^n) 6 | // Space: worst O(2^N)??? 7 | var combinationSum2 = function(candidates, target) { 8 | // sort the numbers to avoid unecessary calculations 9 | candidates.sort((a, b) => a - b) 10 | 11 | const result = [] 12 | // Back Tracking 13 | // sum -> current sum of temp result 14 | // index -> starting point, avoid duplicate checks 15 | // tmp -> temporary result 16 | const walk = (sum, index, temp) => { 17 | // check if it satisify the condition 18 | // if bigger ,the terminate the process 19 | if (sum >= target) { 20 | if (sum === target) { 21 | result.push(temp) 22 | } 23 | return true 24 | } 25 | 26 | // if not, we could add more numbers in it 27 | // 1 1 1 1 1 1 => 3 28 | for (let i = index; i < candidates.length; i++) { 29 | const num = candidates[i] 30 | // avoid duplicate results 31 | if (i > index && num === candidates[i-1]) continue 32 | const shouldTerminate = walk(sum + num, i + 1, temp.concat(num)) 33 | if (shouldTerminate) { 34 | break 35 | } 36 | } 37 | 38 | return false 39 | } 40 | 41 | walk(0,0,[]) 42 | 43 | return result 44 | }; 45 | ``` 46 | -------------------------------------------------------------------------------- /415-add-strings.md: -------------------------------------------------------------------------------- 1 | Here is my video of explaining it: https://youtu.be/SZKe-EwXB68 2 | ```js 3 | /** 4 | * @param {string} num1 5 | * @param {string} num2 6 | * @return {string} 7 | */ 8 | var addStrings = function(num1, num2) { 9 | const length1 = num1.length 10 | const length2 = num2.length 11 | 12 | const maxLength = Math.max(length1, length2) 13 | const resultArr = new Array().fill(0) 14 | 15 | let carry = 0 16 | // from right to left 17 | for (let i = 0; i < maxLength; i++) { 18 | const digit1 = (num1[length1 - 1 - i] || 0) - 0 19 | const digit2 = (num2[length2 - 1 - i] || 0) - 0 20 | 21 | let sum = digit1 + digit2 + carry 22 | if (sum > 9) { 23 | sum -= 10 24 | carry = 1 25 | } else { 26 | carry = 0 27 | } 28 | 29 | resultArr[maxLength - 1 - i] = sum 30 | } 31 | let resultStr = resultArr.join('') 32 | return carry > 0 ? '1' + resultStr : resultStr 33 | }; 34 | ``` 35 | -------------------------------------------------------------------------------- /427-construct-quad-tree.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * // Definition for a QuadTree node. 4 | * function Node(val,isLeaf,topLeft,topRight,bottomLeft,bottomRight) { 5 | * this.val = val; 6 | * this.isLeaf = isLeaf; 7 | * this.topLeft = topLeft; 8 | * this.topRight = topRight; 9 | * this.bottomLeft = bottomLeft; 10 | * this.bottomRight = bottomRight; 11 | * }; 12 | */ 13 | 14 | /** 15 | * @param {number[][]} grid 16 | * @return {Node} 17 | */ 18 | var construct = function (grid) { 19 | // map reduce? 20 | // for a grid defined by (x, y, size) 21 | // we can divide it into 4 areas 22 | // then create 4 nodes from them 23 | 24 | // divide and conquer 25 | // until size === 1 26 | // then it is very easy. 27 | 28 | const impl = (x, y, size) => { 29 | if (size === 1) { 30 | return new Node(grid[x][y], 1); 31 | } 32 | 33 | const subGridSize = size / 2; 34 | 35 | const topLeft = impl(x, y, size / 2); 36 | const topRight = impl(x, y + size / 2, size / 2); 37 | const bottomLeft = impl(x + size / 2, y, size / 2); 38 | const bottomRight = impl(x + size / 2, y + size / 2, size / 2); 39 | if ( 40 | topRight.val === topLeft.val && 41 | bottomLeft.val === topLeft.val && 42 | bottomRight.val === topLeft.val 43 | ) { 44 | return new Node(topLeft.val, 1); 45 | } 46 | // attention here needs to be not 0 nor 1 47 | return new Node(NaN, 0, topLeft, topRight, bottomLeft, bottomRight); 48 | }; 49 | 50 | const result = impl(0, 0, grid.length); 51 | return result; 52 | }; 53 | ``` 54 | -------------------------------------------------------------------------------- /43-multiply-strings.md: -------------------------------------------------------------------------------- 1 | A video explaining this: https://youtu.be/TFlv8yZBtDo 2 | 3 | ```js 4 | /** 5 | * @param {string} num1 6 | * @param {string} num2 7 | * @return {string} 8 | */ 9 | 10 | // Time: O(n * m) 11 | // Space: O(n + m) 12 | var multiply = function(num1, num2) { 13 | const result = Array(num1.length + num2.length).fill(0) 14 | 15 | for (let j = num2.length - 1; j > -1; j--) { 16 | for (let i = num1.length - 1; i > -1; i--) { 17 | const product = num1[i] * num2[j] 18 | const index = num1.length + num2.length - 1 - (num2.length - 1 - j + num1.length - 1 - i) 19 | result[index] += product 20 | if (result[index] > 9) { 21 | result[index - 1] += Math.floor(result[index] / 10) 22 | result[index] %= 10 23 | } 24 | } 25 | } 26 | 27 | // 99 * 9 => carry digit 28 | // 11 * 1 => no carry digt 29 | // skip the leading zeros 30 | while (result[0] === 0) { 31 | result.shift() 32 | } 33 | 34 | return result.length === 0 ? '0' : result.join('') 35 | }; 36 | ``` 37 | -------------------------------------------------------------------------------- /442-find-all-duplicates-in-an-array.md: -------------------------------------------------------------------------------- 1 | Here is my video explaining it : https://youtu.be/IUm4KFMiMoQ 2 | 3 | ```js 4 | 5 | /** 6 | * @param {number[]} nums 7 | * @return {number[]} 8 | */ 9 | // O(n) 10 | // O(1) 11 | var findDuplicates = function(nums) { 12 | // [4,3,2,7,8,2,3,1] 13 | // [7,3,2,4,8,2,3,1] 14 | // [3,3,2,4,8,2,7,1] 15 | // [2,3,3,4,8,2,7,1] 16 | // [3,2,3,4,8,2,7,1] 17 | // [3,2,3,4,1,2,7,8] 18 | // [1,2,3,4,3,2,7,8] 19 | 20 | // 1. keep swapping to the right position, if same number meets, then duplicate, do nothing 21 | 22 | const result = new Set() 23 | for (let i = 0; i < nums.length; i++) { 24 | while (nums[i] !== i + 1) { 25 | // try to swap 26 | const rightIndex = nums[i] - 1 27 | if (nums[rightIndex] === nums[i]) { 28 | result.add(nums[i]) 29 | break 30 | } else { 31 | [nums[i], nums[rightIndex]] = [nums[rightIndex], nums[i]] 32 | } 33 | } 34 | } 35 | return [...result] 36 | }; 37 | ``` 38 | -------------------------------------------------------------------------------- /443-string-compression.md: -------------------------------------------------------------------------------- 1 | Here is my video explaing: https://youtu.be/ClXso8GXiXs 2 | 3 | ```js 4 | /** 5 | * @param {character[]} chars 6 | * @return {number} 7 | */ 8 | var compress = function(chars) { 9 | // "a","a","b","b","c","c","c" 10 | 11 | // there are 3 c 12 | // "a", "2", "b", "2", "c", "3" 13 | 14 | 15 | // 1. current letter 16 | // 2. current letter count 17 | // 3. index to put letter/number 18 | 19 | let currentLetter = null 20 | let currentLetterCount = 0 21 | let indexToPut = 0 22 | 23 | for (let i = 0; i < chars.length + 1; i++) { 24 | const char = chars[i] 25 | 26 | if (char !== currentLetter) { 27 | 28 | if (currentLetter !== null) { 29 | chars[indexToPut] = currentLetter 30 | indexToPut += 1 31 | if (currentLetterCount > 1) { 32 | const countInStr = `${currentLetterCount}` 33 | for (let digit of countInStr) { 34 | chars[indexToPut] = digit 35 | indexToPut += 1 36 | } 37 | } 38 | } 39 | 40 | currentLetter = char 41 | currentLetterCount = 1 42 | } else { 43 | currentLetterCount += 1 44 | } 45 | } 46 | 47 | return indexToPut 48 | }; 49 | ``` 50 | -------------------------------------------------------------------------------- /45-jump-game-ii.md: -------------------------------------------------------------------------------- 1 | A video explaining this: https://youtu.be/NgZrnWZIEkA 2 | 3 | ```js 4 | /** 5 | * @param {number[]} nums 6 | * @return {number} 7 | */ 8 | // Time: O(n^2) 9 | var jump = function (nums) { 10 | if (nums.length === 1) return 0; 11 | 12 | const dp = Array(nums.length).fill(Number.POSITIVE_INFINITY); 13 | 14 | dp[0] = 0; 15 | 16 | // [3,3,2,1,1,4] 17 | // [0] 18 | // [0, 1, 1, 1] 19 | // [0, 1, 2, 2, 2] 20 | for (let i = 0; i < nums.length; i++) { 21 | if (i > 0 && i + nums[i] <= i - 1 + nums[i - 1]) { 22 | continue; 23 | } 24 | for (let k = i + 1; k <= i + nums[i] && k < nums.length; k++) { 25 | dp[k] = Math.min(dp[k], dp[i] + 1); 26 | if (k === nums.length - 1) { 27 | return dp[k]; 28 | } 29 | } 30 | } 31 | }; 32 | ``` 33 | 34 | or 35 | 36 | ```js 37 | var jump = function (nums) { 38 | const stepsTo = new Array(nums.length).fill(Infinity); 39 | stepsTo[0] = 0; 40 | 41 | for (let i = 0; i < nums.length; i++) { 42 | const steps = nums[i]; 43 | for (let j = i + 1; j <= i + steps; j++) { 44 | stepsTo[j] = Math.min(stepsTo[j], stepsTo[i] + 1); 45 | } 46 | } 47 | 48 | return stepsTo[nums.length - 1]; 49 | }; 50 | ``` 51 | -------------------------------------------------------------------------------- /452-minimum-number-of-arrows-to-burst-balloons.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {number[][]} points 4 | * @return {number} 5 | */ 6 | var findMinArrowShots = function (points) { 7 | // [ ] 8 | /// [ ] 9 | // [ ] 10 | // [ ] 11 | 12 | // after sorting it 13 | // we can actually merge interval by choosing the narrow one 14 | // [ ] 15 | // [ ] 16 | // for above case, when the 2nd one is shot, the 1st is shot too 17 | // so it is equal this 18 | // [ ] 19 | // For blow 20 | // [ ] 21 | // [ ] 22 | // equals to 23 | // [ ] 24 | // because if the 3rd one is 25 | // [ ] 26 | // [ ] 27 | // [ ] 28 | // then we can narrow it further 29 | // [ ] 30 | // [ ] 31 | // [ ] 32 | // then the first must shot, meaning we should shot the 2nd as well. 33 | // the problem comes, how many times we need ot flush the buffer 34 | // while merging the intervals 35 | points.sort((a, b) => a[0] - b[0]); 36 | let buffer = null; 37 | let count = 0; 38 | for (const interval of points) { 39 | if (buffer == null) { 40 | buffer = interval; 41 | } else { 42 | if (interval[0] > buffer[1]) { 43 | // shoot 44 | count += 1; 45 | buffer = interval; 46 | } else { 47 | buffer[0] = Math.max(buffer[0], interval[0]); 48 | buffer[1] = Math.min(buffer[1], interval[1]); 49 | } 50 | } 51 | } 52 | if (buffer != null) { 53 | count += 1; 54 | } 55 | return count; 56 | }; 57 | ``` 58 | -------------------------------------------------------------------------------- /453-minimum-moves-to-equal-array-elements.md: -------------------------------------------------------------------------------- 1 | A video explaining this: https://youtu.be/Hl3TivVZ9ME 2 | 3 | ```js 4 | /* 5 | * @param {number[]} nums 6 | * @return {number} 7 | */ 8 | var minMoves = function(nums) { 9 | let min = Number.POSITIVE_INFINITY 10 | let sum = 0 11 | 12 | for (let num of nums) { 13 | sum += num 14 | if (num < min) { 15 | min = num 16 | } 17 | } 18 | 19 | return sum - nums.length * min 20 | }; 21 | ``` 22 | -------------------------------------------------------------------------------- /46-permutations.md: -------------------------------------------------------------------------------- 1 | Youtube video explaining this: https://youtu.be/kONcHqWCmBo 2 | 3 | ```js 4 | /** 5 | * @param {number[]} nums 6 | * @return {number[][]} 7 | */ 8 | // Time: n! 9 | var permute = function (nums) { 10 | const result = []; 11 | 12 | const walk = (temp, rest) => { 13 | if (rest.length === 0) { 14 | result.push(temp); 15 | return; 16 | } 17 | 18 | for (let i = 0; i < rest.length; i++) { 19 | const newRest = rest.slice(0); 20 | newRest.splice(i, 1); 21 | walk(temp.concat(rest[i]), newRest); 22 | } 23 | }; 24 | 25 | walk([], nums); 26 | 27 | return result; 28 | }; 29 | ``` 30 | 31 | Another try in 2024 32 | 33 | ```js 34 | /** 35 | * @param {number[]} nums 36 | * @return {number[][]} 37 | */ 38 | var permute = function (nums) { 39 | // for each pick we choose an item from the nums 40 | // use a set to pevent duplicate 41 | const result = []; 42 | const pick = (temp, set, rest) => { 43 | if (rest.size === 0) { 44 | result.push(temp); 45 | } else { 46 | // be careful that the manipulating of set on the fly 47 | // affects the for loop 48 | for (const item of [...rest]) { 49 | if (!set.has(item)) { 50 | set.add(item); 51 | rest.delete(item); 52 | pick([...temp, item], set, rest); 53 | set.delete(item); 54 | rest.add(item); 55 | } 56 | } 57 | } 58 | }; 59 | pick([], new Set(), new Set(nums)); 60 | return result; 61 | }; 62 | ``` 63 | -------------------------------------------------------------------------------- /461-hamming-distance.md: -------------------------------------------------------------------------------- 1 | Here is my youtube video explaining it : https://youtu.be/X-96S8qg0Hk 2 | 3 | 4 | ```js 5 | /** 6 | * @param {number} x 7 | * @param {number} y 8 | * @return {number} 9 | */ 10 | // Time: O(31) => O(1) 11 | // space: O(1) 12 | var hammingDistance = function(x, y) { 13 | let result = 0 14 | 15 | while (x > 0 || y > 0) { 16 | const right1 = x % 2 17 | const right2 = y % 2 18 | 19 | if (right1 !== right2) { 20 | result += 1 21 | } 22 | 23 | x = x >> 1 24 | y = y >> 1 25 | } 26 | 27 | return result 28 | }; 29 | // 0 1 0 0 1 0 1 1 1 30 | // 1 0 0 1 0 1 1 1 1 31 | // 1 1 0 1 1 1 0 0 0 32 | // 1 1 0 1 1 0 1 1 1 (-1) 33 | // 1 1 0 1 1 0 0 0 0 (&) 34 | // 0 1 0 1 35 | // 0 1 0 0 36 | // 0 1 0 0 37 | // 0 0 1 1 38 | // 0 39 | var hammingDistance = function(x, y) { 40 | let result = 0 41 | 42 | let xor = x ^ y 43 | 44 | while (xor !== 0) { 45 | result += 1 46 | xor = xor & (xor - 1) 47 | } 48 | 49 | return result 50 | }; 51 | ``` 52 | -------------------------------------------------------------------------------- /463-island-perimeter.md: -------------------------------------------------------------------------------- 1 | Here is a video explaining this: https://youtu.be/8YWIFymZYGA 2 | 3 | ```js 4 | /** 5 | * @param {number[][]} grid 6 | * @return {number} 7 | */ 8 | 9 | // Time: O(MN) 10 | // Space: O(1) 11 | var islandPerimeter = function(grid) { 12 | let result = 0 13 | 14 | const rows = grid.length 15 | if (rows === 0) return result 16 | 17 | const cols = grid[0].length 18 | 19 | const getPerimeterOfCell = (i, j) => { 20 | let count = 0 21 | 22 | if (grid[i][j] === 0) return count 23 | 24 | if (i === 0 || grid[i - 1][j] === 0) count += 1 25 | if (i === rows - 1 || grid[i + 1][j] === 0) count += 1 26 | if (j === 0 || grid[i][j - 1] === 0) count += 1 27 | if (j === cols - 1 || grid[i][j + 1] === 0) count += 1 28 | 29 | return count 30 | } 31 | 32 | for (let i = 0; i < rows; i++) { 33 | for (let j = 0; j < cols; j++) { 34 | result += getPerimeterOfCell(i, j) 35 | } 36 | } 37 | 38 | return result 39 | }; 40 | ``` 41 | -------------------------------------------------------------------------------- /47-permutations-ii.md: -------------------------------------------------------------------------------- 1 | Youtube video explaining this: https://youtu.be/KiOkoeZROLo 2 | 3 | 4 | ```js 5 | /** 6 | * @param {number[]} nums 7 | * @return {number[][]} 8 | */ 9 | var permuteUnique = function(nums) { 10 | const result = [] 11 | 12 | nums.sort((a, b) => a - b) 13 | 14 | const walk = (temp, rest) => { 15 | if (rest.length === 0) { 16 | result.push(temp) 17 | return 18 | } 19 | 20 | for (let i = 0; i < rest.length; i++) { 21 | if (rest[i] === rest[i - 1]) continue 22 | const newRest = rest.slice(0) 23 | newRest.splice(i, 1) 24 | walk(temp.concat(rest[i]), newRest) 25 | } 26 | } 27 | 28 | walk([], nums) 29 | 30 | return result 31 | }; 32 | ``` 33 | -------------------------------------------------------------------------------- /485-max-consecutive-ones.md: -------------------------------------------------------------------------------- 1 | My youtube explaining video: https://youtu.be/D6GnFWfs7c8 2 | 3 | 4 | ```js 5 | /** 6 | * @param {number[]} nums 7 | * @return {number} 8 | */ 9 | var findMaxConsecutiveOnes = function(nums) { 10 | // 1,1,0,1,1,1 11 | // 1 1 0 1 1 1 12 | 13 | // keep track of the non-1 right before the start of several 1s 14 | 15 | let prev = -1 16 | let result = 0 17 | for (let i = 0; i < nums.length + 1; i++) { 18 | if (nums[i] !== 1) { 19 | result = Math.max(result, i - prev - 1) 20 | prev = i 21 | } 22 | } 23 | return result 24 | }; 25 | ``` 26 | -------------------------------------------------------------------------------- /487-max-consecutive-ones-ii.md: -------------------------------------------------------------------------------- 1 | My youtube explanation video: https://youtu.be/LaEzdmVkUB0 2 | 3 | ```js 4 | /** 5 | * @param {number[]} nums 6 | * @return {number} 7 | */ 8 | 9 | // Time: O(n) 10 | // space: O(1) 11 | var findMaxConsecutiveOnes = function(nums) { 12 | // 1,0,1,1,0 13 | // 1,0,0,1, 1, 0 14 | 15 | // keep track of prev sub-array-1 length 16 | // length: 1 2 17 | // prev non-1 index: 2 5 18 | 19 | let prevNon1Index = -1 20 | let prevSubArray1Length = 0 21 | let result = 0 22 | 23 | for (let i = 0; i < nums.length + 1; i++) { 24 | if (nums[i] !== 1) { 25 | 26 | const currentSubArray1Length = i - prevNon1Index - 1 27 | 28 | 29 | result = Math.max(result, 30 | currentSubArray1Length + (nums[prevNon1Index] === 0 ? 1 : 0) + prevSubArray1Length) 31 | 32 | prevNon1Index = i 33 | prevSubArray1Length = currentSubArray1Length 34 | } 35 | } 36 | 37 | return result 38 | }; 39 | ``` 40 | -------------------------------------------------------------------------------- /49-group-anagrams.md: -------------------------------------------------------------------------------- 1 | Youtube video explaining this: https://youtu.be/LINRCoEoyFM 2 | 3 | ```js 4 | /** 5 | * @param {string[]} strs 6 | * @return {string[][]} 7 | */ 8 | 9 | // Sort the strings first 10 | // k letters N words 11 | // Time: O(N * k * logk) 12 | // Space: O(N) 13 | // var groupAnagrams = function(strs) { 14 | // const result = new Map() 15 | 16 | // for (let str of strs) { 17 | // const sorted = str.split('').sort().join('') 18 | // if (result.has(sorted)) { 19 | // result.get(sorted).push(str) 20 | // } else { 21 | // result.set(sorted, [str]) 22 | // } 23 | // } 24 | 25 | // return Array.from(result.values()) 26 | // }; 27 | 28 | // counting the existence of letters 29 | // Time: O(N * k) ? 30 | // Space: O(N + 26) 31 | var groupAnagrams = function (strs) { 32 | const result = new Map(); 33 | 34 | for (let str of strs) { 35 | const count = Array(26).fill(0); 36 | for (let i = 0; i < str.length; i++) { 37 | count[str.charCodeAt(i) - 97] += 1; 38 | } 39 | const somewhatHash = count.join("|"); // 20000001000000005 40 | if (result.has(somewhatHash)) { 41 | result.get(somewhatHash).push(str); 42 | } else { 43 | result.set(somewhatHash, [str]); 44 | } 45 | } 46 | 47 | return Array.from(result.values()); 48 | }; 49 | ``` 50 | -------------------------------------------------------------------------------- /498-diagonal-traverse.md: -------------------------------------------------------------------------------- 1 | me explaining this on youtube: https://youtu.be/WPsWQJTTqfg 2 | 3 | ```js 4 | /** 5 | * @param {number[][]} matrix 6 | * @return {number[]} 7 | */ 8 | 9 | // Time: O(m * n) 10 | // Space: O(m * n) 11 | var findDiagonalOrder = function(matrix) { 12 | const rows = matrix.length 13 | if (rows === 0) return [] 14 | 15 | const cols = matrix[0].length 16 | const diagonals = new Array(rows + cols - 1).fill(0).map(_ => []) 17 | 18 | for (let i = 0; i < rows; i++) { 19 | for (let j = 0; j < cols; j++) { 20 | const key = i + j 21 | const num = matrix[i][j] 22 | if (key % 2 === 0) { 23 | diagonals[key].unshift(num) 24 | } else { 25 | diagonals[key].push(num) 26 | } 27 | } 28 | } 29 | 30 | // collect 31 | return diagonals.reduce((result, arr) => { 32 | result.push(...arr) 33 | return result 34 | }, []) 35 | }; 36 | ``` 37 | -------------------------------------------------------------------------------- /5-longest-palindromic-substring.md: -------------------------------------------------------------------------------- 1 | A video for this problem: https://youtu.be/9sIvG8x7FKg 2 | 3 | ```js 4 | const checkPalindromiceAt = (i, j, s, result) => { 5 | while (i >= 0 && j < s.length && s[i] === s[j]) { 6 | i--; 7 | j++; 8 | } 9 | if (j - 1 - (i + 1) > result[1] - result[0]) { 10 | result[0] = i + 1; 11 | result[1] = j - 1; 12 | } 13 | }; 14 | 15 | /** 16 | * @param {string} s 17 | * @return {string} 18 | */ 19 | var longestPalindrome = function (s) { 20 | if (s.length === 0) { 21 | return ""; 22 | } 23 | 24 | let i = 0; 25 | const result = [0, 0]; 26 | while (i < s.length) { 27 | checkPalindromiceAt(i, i, s, result); 28 | if (i < s.length - 1) { 29 | checkPalindromiceAt(i, i + 1, s, result); 30 | } 31 | i++; 32 | } 33 | 34 | return s.slice(result[0], result[1] + 1); 35 | }; 36 | ``` 37 | -------------------------------------------------------------------------------- /509-fibonacci-number.md: -------------------------------------------------------------------------------- 1 | Video explanation on this: https://youtu.be/gPuXCOrYIpg 2 | 3 | ```js 4 | /** 5 | * @param {number} N 6 | * @return {number} 7 | */ 8 | 9 | const memo = (func) => { 10 | const cache = new Map() 11 | return function(...args) { 12 | const key = args.join('_') 13 | if (cache.has(key)) { 14 | return cache.get(key) 15 | } 16 | const result = func(...args) 17 | cache.set(key, result) 18 | return result 19 | } 20 | } 21 | 22 | var fib = memo((N) => { 23 | if (N === 0) return 0 24 | if (N === 1) return 1 25 | const result = fib(N - 1) + fib(N - 2) 26 | return result 27 | }) 28 | ``` 29 | -------------------------------------------------------------------------------- /51-n-queens.md: -------------------------------------------------------------------------------- 1 | Youtube video explaining this from me: https://youtu.be/IY8uVrt_jlM 2 | 3 | 4 | ```js 5 | /** 6 | * @param {number} n 7 | * @return {string[][]} 8 | */ 9 | 10 | // 11 | var solveNQueens = function(n) { 12 | const result = [] 13 | const board = Array(n).fill(0).map(_ => Array(n).fill('.')) 14 | 15 | const colsUsed = Array(n).fill(false) 16 | const diago135Used = Array(n * 2 - 1).fill(false) 17 | const diago45Used = Array(n * 2 - 1).fill(false) 18 | 19 | const put = (i, j) => { 20 | const indexOfDiago135 = i + n - j - 1 21 | const indexOfDiago45 = i + j 22 | // if not available 23 | if (colsUsed[j] || diago135Used[indexOfDiago135] || diago45Used[indexOfDiago45]) { 24 | return 25 | } 26 | 27 | // if possible, put Queen in 28 | board[i][j] = 'Q' 29 | colsUsed[j] = true 30 | diago135Used[indexOfDiago135] = true 31 | diago45Used[indexOfDiago45] = true 32 | 33 | // last Queen is put 34 | if (i === n - 1) { 35 | result.push(board.map(row => row.join(''))) 36 | } else { 37 | for (let k = 0; k < n ; k ++) { 38 | put(i + 1, k) 39 | } 40 | } 41 | 42 | board[i][j] = '.' 43 | colsUsed[j] = false 44 | diago135Used[indexOfDiago135] = false 45 | diago45Used[indexOfDiago45] = false 46 | } 47 | 48 | for (let k = 0; k < n ; k ++) { 49 | put(0, k) 50 | } 51 | 52 | return result 53 | }; 54 | ``` 55 | -------------------------------------------------------------------------------- /520-detect-capital.md: -------------------------------------------------------------------------------- 1 | Here is my youtube explanation: https://youtu.be/Fj24xroI-S4 2 | ```js 3 | /** 4 | * @param {string} word 5 | * @return {boolean} 6 | */ 7 | var detectCapitalUse = function(word) { 8 | // if lower case char: invalid case is prev is upper case & not the first letter 9 | // if upper case: if have lower case before it, invalid 10 | 11 | let hasUpper = false 12 | let hasLower = false 13 | 14 | for (let i = 0, total = word.length; i < total; i++) { 15 | if (/[a-z]/.test(word[i])) { 16 | if (hasUpper && !hasLower && i > 1) { 17 | return false 18 | } 19 | 20 | hasLower = true 21 | } else { 22 | if (hasLower) { 23 | return false 24 | } 25 | hasUpper = true 26 | } 27 | } 28 | 29 | return true 30 | }; 31 | ``` 32 | -------------------------------------------------------------------------------- /530-minimum-absolute-difference-in-bst.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * Definition for a binary tree node. 4 | * function TreeNode(val, left, right) { 5 | * this.val = (val===undefined ? 0 : val) 6 | * this.left = (left===undefined ? null : left) 7 | * this.right = (right===undefined ? null : right) 8 | * } 9 | */ 10 | /** 11 | * @param {TreeNode} root 12 | * @return {number} 13 | */ 14 | var getMinimumDifference = function (root) { 15 | // traverse it in an array 16 | // get the diff in O(n^2) 17 | 18 | // but we didn't use the fact it is BST 19 | // it is already ordered? 20 | 21 | // For BST 22 | // Inorder traversal leads to an ordered array 23 | 24 | // then the minimum diff should be two consecutive 25 | const list = []; 26 | const walk = (node) => { 27 | if (node == null) return; 28 | if (node.left) { 29 | walk(node.left); 30 | } 31 | list.push(node.val); 32 | if (node.right) { 33 | walk(node.right); 34 | } 35 | }; 36 | walk(root); 37 | let min = Infinity; 38 | for (let i = 1; i < list.length; i++) { 39 | min = Math.min(list[i] - list[i - 1], min); 40 | } 41 | return min; 42 | }; 43 | ``` 44 | -------------------------------------------------------------------------------- /543-diameter-of-binary-tree.md: -------------------------------------------------------------------------------- 1 | Watch my video on youtube for this problem: https://youtu.be/PYvqkhp5-48 2 | ```js 3 | /** 4 | * Definition for a binary tree node. 5 | * function TreeNode(val) { 6 | * this.val = val; 7 | * this.left = this.right = null; 8 | * } 9 | */ 10 | /** 11 | * @param {TreeNode} root 12 | * @return {number} 13 | */ 14 | 15 | // Time: O(n) 16 | // Space: worst O(n) for call stack 17 | var diameterOfBinaryTree = function(root) { 18 | // return [diameter, height] 19 | const walk = (node) => { 20 | 21 | if (node === null) return [0, 0] 22 | const [leftDiameter, leftHeight] = walk(node.left) 23 | const [rightDiameter, rightHeight] = walk(node.right) 24 | 25 | return [ 26 | Math.max(leftDiameter, rightDiameter, leftHeight + rightHeight), 27 | Math.max(leftHeight, rightHeight) + 1 28 | ] 29 | } 30 | 31 | return walk(root)[0] 32 | }; 33 | ``` 34 | -------------------------------------------------------------------------------- /55-jump-game.md: -------------------------------------------------------------------------------- 1 | ## 1. 2 | 3 | Youtube explaining this: https://youtu.be/f4mevxUz_mE 4 | 5 | ```js 6 | /** 7 | * @param {number[]} nums 8 | * @return {boolean} 9 | */ 10 | // expand while looping 11 | // Time: O(n) worst 12 | // Space: O(1) 13 | var canJump = function (nums) { 14 | if (nums.length <= 1) return true; 15 | 16 | let maxIndex = 0; 17 | 18 | let i = 0; 19 | 20 | while (i <= maxIndex) { 21 | maxIndex = Math.max(maxIndex, i + nums[i]); 22 | 23 | if (maxIndex >= nums.length - 1) { 24 | return true; 25 | } 26 | i += 1; 27 | } 28 | 29 | return false; 30 | }; 31 | ``` 32 | 33 | ## 2 34 | 35 | ```js 36 | /** 37 | * @param {number[]} nums 38 | * @return {boolean} 39 | */ 40 | var canJump = function (nums) { 41 | let canJumpTo = 0; 42 | for (let i = 0; i < nums.length; i++) { 43 | if (canJumpTo >= nums.length - 1) { 44 | return true; 45 | } 46 | if (canJumpTo < i) { 47 | return false; 48 | } 49 | canJumpTo = Math.max(canJumpTo, i + nums[i]); 50 | } 51 | return canJumpTo[nums.length - 1]; 52 | }; 53 | ``` 54 | -------------------------------------------------------------------------------- /560-subarray-sum-equals-k.md: -------------------------------------------------------------------------------- 1 | A video explaining this: https://youtu.be/gdgx4HHBmKg 2 | 3 | ```js 4 | /** 5 | * @param {number[]} nums 6 | * @param {number} k 7 | * @return {number} 8 | */ 9 | var subarraySum = function(nums, k) { 10 | let result = 0 11 | const countMap = new Map([[0, 1]]) 12 | 13 | let sum = 0 14 | 15 | for (let num of nums) { 16 | sum += num 17 | 18 | if (countMap.has(sum - k)) { 19 | result += countMap.get(sum - k) 20 | } 21 | 22 | if (countMap.has(sum)) { 23 | countMap.set(sum, countMap.get(sum) + 1) 24 | } else { 25 | countMap.set(sum, 1) 26 | } 27 | } 28 | 29 | return result 30 | }; 31 | ``` 32 | -------------------------------------------------------------------------------- /563-binary-tree-tilt.md: -------------------------------------------------------------------------------- 1 | Youtube video explaining this: https://youtu.be/n7iOrbGF4_g 2 | ```js 3 | /** 4 | * Definition for a binary tree node. 5 | * function TreeNode(val) { 6 | * this.val = val; 7 | * this.left = this.right = null; 8 | * } 9 | */ 10 | /** 11 | * @param {TreeNode} root 12 | * @return {number} 13 | */ 14 | var findTilt = function(root) { 15 | const getTiltAndSum = (node) => { 16 | 17 | if (node === null) { 18 | return [0, 0] 19 | } 20 | const rightTiltAndSum = getTiltAndSum(node.right) 21 | const leftTiltAndSum = getTiltAndSum(node.left) 22 | 23 | return [Math.abs(rightTiltAndSum[1] - leftTiltAndSum[1]) + rightTiltAndSum[0] + leftTiltAndSum[0], rightTiltAndSum[1] + leftTiltAndSum[1] + node.val] 24 | } 25 | 26 | return getTiltAndSum(root)[0] 27 | }; 28 | ``` 29 | -------------------------------------------------------------------------------- /59-spiral-matrix-ii.md: -------------------------------------------------------------------------------- 1 | A video explaining this: https://youtu.be/vPNX6AWiTqA 2 | 3 | 4 | ```js 5 | /** 6 | * @param {number} n 7 | * @return {number[][]} 8 | */ 9 | var generateMatrix = function(n) { 10 | if (n === 0) return [] 11 | if (n === 1) return [[1]] 12 | 13 | const directions = [ 14 | [0, 1], 15 | [1, 0], 16 | [0, -1], 17 | [-1, 0], 18 | ] 19 | 20 | let currentDirectionIndex = 0 21 | 22 | const turnRight = () => { 23 | currentDirectionIndex = (currentDirectionIndex + 1) % directions.length 24 | } 25 | 26 | const result = Array(n).fill(0).map(item => Array(n).fill(-1)) 27 | 28 | let i = 0 29 | let j = 0 30 | const total = n ** 2 31 | let count = 1 32 | 33 | while (count <= total) { 34 | result[i][j] = count 35 | 36 | // check if we need to turn right 37 | const nextPossible = [i + directions[currentDirectionIndex][0], j + directions[currentDirectionIndex][1]]; 38 | 39 | // if it is overflowed, or it is already set, turn right 40 | if (nextPossible[0] < 0 || nextPossible[0] >= n || 41 | nextPossible[1] < 0 || nextPossible[1] >= n || 42 | result[nextPossible[0]][nextPossible[1]] !== -1) { 43 | turnRight() 44 | } 45 | 46 | i += directions[currentDirectionIndex][0] 47 | j += directions[currentDirectionIndex][1] 48 | 49 | count += 1 50 | } 51 | 52 | 53 | return result 54 | }; 55 | ``` 56 | -------------------------------------------------------------------------------- /6-zigzag-conversion.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {string} s 4 | * @param {number} numRows 5 | * @return {string} 6 | */ 7 | var convert = function (s, numRows) { 8 | if (numRows === 1) return s; 9 | const rows = new Array(numRows).fill(""); 10 | 11 | let direction = 1; 12 | let nextRow = 0; 13 | for (const char of s) { 14 | // for each char place at nextRow 15 | rows[nextRow] += char; 16 | 17 | const [newDirection, row] = getNextRow(nextRow, direction); 18 | nextRow = row; 19 | direction = newDirection; 20 | } 21 | 22 | function getNextRow(row, direction) { 23 | const next = row + direction; 24 | if (next < 0 || next >= numRows) { 25 | return [-direction, row - direction]; 26 | } else { 27 | return [direction, next]; 28 | } 29 | } 30 | 31 | return rows.join(""); 32 | }; 33 | ``` 34 | 35 | Another try 36 | 37 | ```js 38 | /** 39 | * @param {string} s 40 | * @param {number} numRows 41 | * @return {string} 42 | */ 43 | var convert = function (s, numRows) { 44 | if (s.length === 0) return s; 45 | if (numRows === 1) return s; 46 | 47 | const rows = new Array(numRows).fill(""); 48 | let currentRow = -1; 49 | let direction = 1; 50 | let i = 0; 51 | while (i < s.length) { 52 | let nextRow = currentRow + direction; 53 | if (nextRow >= numRows || nextRow < 0) { 54 | direction *= -1; 55 | nextRow = currentRow + direction; 56 | } 57 | currentRow = nextRow; 58 | rows[currentRow] += s[i]; 59 | i += 1; 60 | } 61 | return rows.join(""); 62 | }; 63 | ``` 64 | -------------------------------------------------------------------------------- /62-unique-paths.md: -------------------------------------------------------------------------------- 1 | Here is a video explaining this: https://youtu.be/F7dgkLuJV-4 2 | 3 | ```js 4 | /** 5 | * @param {number} m 6 | * @param {number} n 7 | * @return {number} 8 | */ 9 | 10 | // Dynamic programming 11 | // generate the steps for each row 12 | // use 1 array and do the iteration 13 | 14 | // Time: O(n * m) 15 | // Space: O(m) 16 | var uniquePaths = function(m, n) { 17 | const dp = Array(m).fill(1) 18 | 19 | while (n > 1) { 20 | for (let i = 1; i < m; i++) { 21 | dp[i] += dp[i - 1] 22 | } 23 | 24 | n -= 1 25 | } 26 | 27 | return dp[m - 1] 28 | }; 29 | ``` 30 | -------------------------------------------------------------------------------- /621-task-scheduler.md: -------------------------------------------------------------------------------- 1 | me explaining it on youtube: https://youtu.be/RrnDKOh2jbM 2 | 3 | 4 | ```js 5 | /** 6 | * @param {character[]} tasks 7 | * @param {number} n 8 | * @return {number} 9 | */ 10 | var leastInterval = function(tasks, n) { 11 | // "A","A","A","B","B","B" 12 | // A B 0 A B 0 A B 13 | 14 | // A A A A B B 15 | // A (B C D E 0 0 ) A ( B C D E 0 0 ) A (B C E 0 0 ) A (B) 16 | 17 | // count of each task 18 | const countOfTask = new Array(26).fill(0) 19 | 20 | let maxCount = 0 21 | const codeForA = 'A'.charCodeAt(0) 22 | 23 | for (let task of tasks) { 24 | const index = task.charCodeAt(0) - codeForA 25 | countOfTask[index] += 1 26 | if (countOfTask[index] > maxCount) { 27 | maxCount = countOfTask[index] 28 | } 29 | } 30 | 31 | // determine how many tasks are to the maxCount 32 | let taskCountOfMaxCount = 0 33 | for (let count of countOfTask) { 34 | if (count === maxCount) { 35 | taskCountOfMaxCount += 1 36 | } 37 | } 38 | 39 | const intervalsRequred = (maxCount - 1) * (n + 1) + taskCountOfMaxCount 40 | return Math.max(intervalsRequred, tasks.length) 41 | }; 42 | ``` 43 | -------------------------------------------------------------------------------- /63-unique-paths-ii.md: -------------------------------------------------------------------------------- 1 | A video explaining this: https://youtu.be/guMv7BKIKCg 2 | 3 | ```js 4 | /** 5 | * @param {number[][]} obstacleGrid 6 | * @return {number} 7 | */ 8 | // Time: O(n * m) 9 | // Space: O(m) 10 | var uniquePathsWithObstacles = function(obstacleGrid) { 11 | 12 | const n = obstacleGrid.length 13 | const m = obstacleGrid[0].length 14 | const dp = Array(m).fill(0) 15 | dp[0] = 1 16 | 17 | for (let i = 0; i < n; i++) { 18 | for (let j = 0; j < m; j++) { 19 | // only update when it is empty 20 | if (obstacleGrid[i][j] === 0) { 21 | dp[j] += j > 0 ? dp[j - 1] : 0 22 | } else { 23 | dp[j] = 0 24 | } 25 | } 26 | } 27 | 28 | return dp[m - 1] 29 | }; 30 | ``` 31 | -------------------------------------------------------------------------------- /636-exclusive-time-of-functions.md: -------------------------------------------------------------------------------- 1 | Here is a video explaining it: https://youtu.be/pO3EQ5y4V34 2 | ```js 3 | /** 4 | * @param {number} n 5 | * @param {string[]} logs 6 | * @return {number[]} 7 | */ 8 | 9 | // Time: O(count of logs) 10 | // Space: worst O(n) 11 | var exclusiveTime = function(n, logs) { 12 | // use a stack to keep track of the top most function 13 | // upate the time of each function by segement 14 | 15 | const callStack = [] // Array 16 | 17 | const result = new Array(n).fill(0) 18 | 19 | let prevTimestamp = 0 20 | for (let log of logs) { 21 | const [functionId, flag, _timestamp] = log.split(':') 22 | const timestamp = parseInt(_timestamp, 10) 23 | 24 | if (callStack.length === 0) { 25 | callStack.push(functionId) // better confirm with interviewer 26 | } else { 27 | if (flag === 'end') { 28 | const length = timestamp - prevTimestamp + 1 29 | const currentFunction = callStack.pop() 30 | result[currentFunction] += length 31 | prevTimestamp = timestamp + 1 32 | } else { 33 | const length = timestamp - prevTimestamp 34 | result[callStack[callStack.length - 1]] += length 35 | callStack.push(functionId) 36 | prevTimestamp = timestamp 37 | } 38 | } 39 | } 40 | 41 | return result 42 | }; 43 | ``` 44 | -------------------------------------------------------------------------------- /64-minimum-path-sum.md: -------------------------------------------------------------------------------- 1 | A video explaining this: https://youtu.be/A95LVX0R5K0 2 | 3 | ```js 4 | /** 5 | * @param {number[][]} grid 6 | * @return {number} 7 | */ 8 | 9 | // Time: O(n * m) 10 | // Space: O(m) 11 | var minPathSum = function(grid) { 12 | const n = grid.length 13 | const m = grid[0].length 14 | // just like 62, we store not the count 15 | // but [sum, path] 16 | const dp = Array(m).fill(0).map(_ => Number.POSITIVE_INFINITY) 17 | dp[0] = 0 18 | 19 | 20 | 21 | for (let i = 0; i < n; i++) { 22 | for (let j = 0; j < m; j++) { 23 | const left = j === 0 ? Number.POSITIVE_INFINITY : dp[j - 1] 24 | const top = dp[j] 25 | 26 | dp[j] = grid[i][j] + Math.min(left, top) 27 | } 28 | } 29 | 30 | return dp[m - 1] 31 | }; 32 | ``` 33 | -------------------------------------------------------------------------------- /66-plus-one.md: -------------------------------------------------------------------------------- 1 | A video explaining this: https://youtu.be/y4t6kQ2fSDM 2 | 3 | ```js 4 | /** 5 | * @param {number[]} digits 6 | * @return {number[]} 7 | */ 8 | // Time: O(n) 9 | // Space: O(1) 10 | var plusOne = function (digits) { 11 | let carryover = 1; 12 | let i = digits.length - 1; 13 | 14 | while (carryover > 0 && i > -1) { 15 | digits[i] = digits[i] + 1; 16 | if (digits[i] > 9) { 17 | digits[i] %= 10; 18 | carryover = 1; 19 | } else { 20 | carryover = 0; 21 | } 22 | 23 | i -= 1; 24 | } 25 | 26 | // for cases like [9, 9, 9] 27 | if (carryover > 0) { 28 | digits.unshift(1); 29 | } 30 | 31 | return digits; 32 | }; 33 | ``` 34 | 35 | Another try 36 | 37 | ```js 38 | /** 39 | * @param {number[]} digits 40 | * @return {number[]} 41 | */ 42 | var plusOne = function (digits) { 43 | let carryOverIndex = digits.length - 1; 44 | while (carryOverIndex > -2) { 45 | if (carryOverIndex === -1) { 46 | digits.unshift(1); 47 | break; 48 | } 49 | digits[carryOverIndex] = digits[carryOverIndex] + 1; 50 | if (digits[carryOverIndex] > 9) { 51 | digits[carryOverIndex] = digits[carryOverIndex] - 10; 52 | carryOverIndex -= 1; 53 | } else { 54 | carryOverIndex = -2; 55 | } 56 | } 57 | 58 | return digits; 59 | }; 60 | ``` 61 | -------------------------------------------------------------------------------- /67-add-binary.md: -------------------------------------------------------------------------------- 1 | A video explaining this: https://youtu.be/IWok_vEB3I0 2 | 3 | ```js 4 | /** 5 | * @param {string} a 6 | * @param {string} b 7 | * @return {string} 8 | */ 9 | var addBinary = function (a, b) { 10 | let carryover = 0; 11 | let maxLength = Math.max(a.length, b.length); 12 | let result = Array(maxLength).fill(0); 13 | 14 | let i = 0; 15 | 16 | while (i < maxLength) { 17 | const sum = 18 | (a.length - 1 - i < 0 ? 0 : a[a.length - 1 - i] * 1) + 19 | (b.length - 1 - i < 0 ? 0 : b[b.length - 1 - i] * 1) + 20 | carryover; 21 | result[maxLength - 1 - i] = sum % 2; 22 | carryover = Math.floor(sum / 2); 23 | 24 | i += 1; 25 | } 26 | 27 | if (carryover > 0) { 28 | result.unshift(1); 29 | } 30 | 31 | return result.join(""); 32 | }; 33 | ``` 34 | 35 | Another try in 2024 36 | 37 | ```js 38 | /** 39 | * @param {string} a 40 | * @param {string} b 41 | * @return {string} 42 | */ 43 | var addBinary = function (a, b) { 44 | let carry = 0; 45 | let lenA = a.length; 46 | let lenB = b.length; 47 | let maxLen = Math.max(lenA, lenB); 48 | let result = ""; 49 | for (let i = 0; i < maxLen; i++) { 50 | // backward 51 | let digitA = i < lenA ? a[lenA - 1 - i] : "0"; 52 | let digitB = i < lenB ? b[lenB - 1 - i] : "0"; 53 | 54 | let sum = carry + (digitA !== digitB ? 1 : digitA === "1" ? 2 : 0); 55 | if (sum > 1) { 56 | carry = 1; 57 | sum -= 2; 58 | } else { 59 | carry = 0; 60 | } 61 | result = sum + result; 62 | } 63 | if (carry > 0) { 64 | result = "1" + result; 65 | } 66 | return result; 67 | }; 68 | ``` 69 | -------------------------------------------------------------------------------- /670-maximum-swap.md: -------------------------------------------------------------------------------- 1 | me explaining it on youtube: https://youtu.be/VhqONOjg9go 2 | 3 | ```js 4 | /** 5 | * @param {number} num 6 | * @return {number} 7 | */ 8 | 9 | 10 | // Time: O(n) 11 | // Space: O(n) 12 | var maximumSwap = function(num) { 13 | // 2 7 3 6 14 | 15 | // 9 2 2 7 3 6 16 | 17 | // 9 9 3 7 18 | 19 | // from left to right, find the first turning point (desc => asc), i 20 | // find maximum from i 21 | // find the first number < maximu, j = 0 -> i 22 | 23 | const str = num.toString().split('') 24 | 25 | let turningPoint = null 26 | for (let i = 0; i < str.length - 1; i++) { 27 | if (str[i] < str[i + 1]) { 28 | turningPoint = i 29 | break 30 | } 31 | } 32 | 33 | if (turningPoint === null) return num 34 | 35 | // find the max to the right 36 | let maxIndexToTheRight = turningPoint + 1 37 | for (let i = turningPoint + 2; i < str.length; i++) { 38 | if (str[i] >= str[maxIndexToTheRight]) { 39 | maxIndexToTheRight = i 40 | } 41 | } 42 | 43 | // find the first number < maximu 44 | 45 | for (let i = 0; i <= turningPoint; i++) { 46 | if (str[i] < str[maxIndexToTheRight]) { 47 | // swap 48 | [str[i], str[maxIndexToTheRight]] = [str[maxIndexToTheRight], str[i]] 49 | break 50 | } 51 | } 52 | 53 | return parseInt(str.join(''), 10) 54 | }; 55 | ``` 56 | -------------------------------------------------------------------------------- /674-longest-continuous-increasing-subsequence.md: -------------------------------------------------------------------------------- 1 | My youtube video to explain this: https://youtu.be/SxX8AhuDWic 2 | 3 | 4 | ```js 5 | /** 6 | * @param {number[]} nums 7 | * @return {number} 8 | */ 9 | var findLengthOfLCIS = function(nums) { 10 | if (nums.length === 0) return 0 11 | 12 | let max = 1 13 | 14 | let currentLength = 1 15 | for (let i = 1; i < nums.length; i++) { 16 | if (nums[i] > nums[i - 1]) { 17 | currentLength += 1 18 | max = Math.max(currentLength, max) 19 | } else { 20 | currentLength = 1 21 | } 22 | } 23 | return max 24 | }; 25 | ``` 26 | -------------------------------------------------------------------------------- /69-sqrtx.md: -------------------------------------------------------------------------------- 1 | Here is a video explaining this: https://youtu.be/xoeMhTvg11M 2 | 3 | ```js 4 | /** 5 | * @param {number} x 6 | * @return {number} 7 | */ 8 | // var mySqrt = function(x) { 9 | // // 0 1 2 .... x 10 | // // pick the number, that square of it right equal/smaller than x 11 | // // binary search 12 | 13 | // // O(log(x)) 14 | 15 | // let i = 0 16 | // let j = x 17 | 18 | // // [i, middle, j] 19 | // while (i < j) { 20 | // const middle = Math.ceil((i + j) / 2) // means if i === j - 1, middle would be j 21 | // const square = middle ** 2 22 | // if (square === x) return middle 23 | // if (square < x) { 24 | // i = middle 25 | // } else { 26 | // j = middle - 1 27 | // } 28 | // } 29 | 30 | // return i 31 | // }; 32 | 33 | var mySqrt = function (x) { 34 | if (x < 2) return x; 35 | // 0 1 2 .... x 36 | // pick the number, that square of it right equal/smaller than x 37 | // binary search 38 | 39 | // O(log(x)) 40 | let i = 0; 41 | let j = x; 42 | 43 | // [i, middle, j] 44 | while (i < j) { 45 | const middle = Math.floor((i + j) / 2); // means if i === j - 1, middle would be i 46 | const square = middle ** 2; 47 | if (square === x) return middle; 48 | if (square < x) { 49 | i = middle + 1; 50 | } else { 51 | j = middle; 52 | } 53 | } 54 | 55 | return j - 1; 56 | }; 57 | ``` 58 | -------------------------------------------------------------------------------- /7-reverse-integer.md: -------------------------------------------------------------------------------- 1 | A video explaining this: https://youtu.be/ePMcnHsKGN4 2 | ```js 3 | /** 4 | * @param {number} x 5 | * @return {number} 6 | */ 7 | // TIME: O(n) 8 | // Space: O(1) 9 | var reverse = function(x) { 10 | let result = 0 11 | let sign = 1 12 | 13 | if (x < 0) { 14 | sign = -1 15 | x = -x 16 | } 17 | 18 | while (x > 0) { 19 | const digit = x % 10 20 | x = (x - digit) / 10 21 | result = result * 10 + digit 22 | } 23 | 24 | result *= sign 25 | 26 | if (result > 2**31 - 1) return 0 27 | if (result < -(2**31) - 1) return 0 28 | 29 | return result 30 | }; 31 | ``` 32 | -------------------------------------------------------------------------------- /70-climbing-stairs.md: -------------------------------------------------------------------------------- 1 | You can find my approach here: https://youtu.be/PiCJf_qCxwI 2 | 3 | ```js 4 | /** 5 | * @param {number} n 6 | * @return {number} 7 | */ 8 | var climbStairs = function(n) { 9 | const dp = [1, 1] 10 | 11 | for (let i = 2; i <= n; i++) { 12 | const result = dp[0] + dp[1] 13 | dp[0] = dp[1] 14 | dp[1] = result 15 | } 16 | 17 | return dp[1] 18 | }; 19 | ``` 20 | -------------------------------------------------------------------------------- /702-search-in-a-sorted-array-of-unknown-size.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * // This is the ArrayReader's API interface. 4 | * // You should not implement it, or speculate about its implementation 5 | * function ArrayReader() { 6 | * 7 | * @param {number} index 8 | * @return {number} 9 | * this.get = function(index) { 10 | * ... 11 | * }; 12 | * }; 13 | */ 14 | 15 | /** 16 | * @param {ArrayReader} reader 17 | * @param {number} target 18 | * @return {number} 19 | */ 20 | var search = function (reader, target) { 21 | let i = 0; 22 | let j = 10 ** 4 - 1; 23 | const overflow = 2 ** 31 - 1; 24 | while (i <= j) { 25 | const m = Math.floor((i + j) / 2); 26 | const mval = reader.get(m); 27 | if (mval === overflow) { 28 | j = m - 1; 29 | } else if (mval === target) { 30 | return m; 31 | } else if (mval < target) { 32 | i += 1; 33 | } else { 34 | j -= 1; 35 | } 36 | } 37 | return -1; 38 | }; 39 | ``` 40 | -------------------------------------------------------------------------------- /704-binary-search.md: -------------------------------------------------------------------------------- 1 | ```js 2 | /** 3 | * @param {number[]} nums 4 | * @param {number} target 5 | * @return {number} 6 | */ 7 | var search = function (nums, target) { 8 | let i = 0; 9 | let j = nums.length - 1; 10 | while (i <= j) { 11 | const m = Math.floor((i + j) / 2); 12 | if (nums[m] === target) { 13 | return m; 14 | } 15 | if (nums[m] < target) { 16 | i = m + 1; 17 | } else { 18 | j = m - 1; 19 | } 20 | } 21 | // when it stops i should be where the target should be at 22 | return -1; 23 | }; 24 | ``` 25 | -------------------------------------------------------------------------------- /718-maximum-length-of-repeated-subarray.md: -------------------------------------------------------------------------------- 1 | A video explaining this: https://youtu.be/mJIYFfrdWZ0 2 | 3 | ```js 4 | /** 5 | * @param {number[]} A 6 | * @param {number[]} B 7 | * @return {number} 8 | */ 9 | var findLength = function(A, B) { 10 | let max = 0; 11 | 12 | let matrix = new Array(A.length) 13 | for (let i = 0; i < A.length; i++) { 14 | matrix[i] = new Array(B.length) 15 | for(let j = 0; j < B.length; j++) { 16 | if (i === 0 || j === 0) { 17 | matrix[i][j] = A[i] === B[j] ? 1 : 0 18 | } else { 19 | matrix[i][j] = A[i] === B[j] ? matrix[i - 1][j - 1] + 1 : 0 20 | } 21 | 22 | if (matrix[i][j] > max) { 23 | max = matrix[i][j] 24 | } 25 | } 26 | } 27 | return max 28 | }; 29 | ``` 30 | -------------------------------------------------------------------------------- /724-find-pivot-index.md: -------------------------------------------------------------------------------- 1 | A video explaining this: https://youtu.be/_Df1OgaqPPg 2 | 3 | ```js 4 | /** 5 | * @param {number[]} nums 6 | * @return {number} 7 | */ 8 | var pivotIndex = function (nums) { 9 | if (nums.length === 0) return -1; 10 | if (nums.length === 1) return 1; 11 | 12 | // get the sum array from left to right 13 | let sumAll = 0; 14 | for (let i = 0; i < nums.length; i++) { 15 | sumAll += nums[i]; 16 | } 17 | 18 | let sumLeft = 0; 19 | for (let k = 0; k < nums.length; k++) { 20 | if (k > 0) { 21 | sumLeft += nums[k - 1]; 22 | } 23 | 24 | if (sumLeft * 2 + nums[k] === sumAll) { 25 | return k; 26 | } 27 | } 28 | return -1; 29 | }; 30 | ``` 31 | 32 | Another try in 2024 33 | 34 | ```js 35 | /** 36 | * @param {number[]} nums 37 | * @return {number} 38 | */ 39 | var pivotIndex = function (nums) { 40 | // we need to know the sum until and sum since 41 | // since we need to return the leftmost 42 | // we can first check sum since 43 | 44 | // inclusive. 45 | let sumSince = new Array(nums.length + 1).fill(0); 46 | let sum = 0; 47 | for (let i = nums.length - 1; i >= 0; i--) { 48 | sum += nums[i]; 49 | sumSince[i] = sum; 50 | } 51 | sum = 0; 52 | for (let i = 0; i < nums.length; i++) { 53 | if (sum === sumSince[i + 1]) { 54 | return i; 55 | } 56 | sum += nums[i]; 57 | } 58 | return -1; 59 | }; 60 | ``` 61 | -------------------------------------------------------------------------------- /766-toeplitz-matrix.md: -------------------------------------------------------------------------------- 1 | 2 | Me explaining it on youtube: https://youtu.be/Wy_xnTjTxXI 3 | 4 | 5 | ```js 6 | /** 7 | * @param {number[][]} matrix 8 | * @return {boolean} 9 | */ 10 | 11 | // Time: O(mn) 12 | // Space: O(m + n) 13 | // var isToeplitzMatrix = function(matrix) { 14 | // // 15 | // // m - 1, 0, -1, ... -(n - 1) 16 | // // plus (n - 1) 17 | // // (m - 1) + (n - 1) , ..... 0 18 | 19 | // const rows = matrix.length 20 | // const cols = matrix[0].length 21 | 22 | // const elements = new Array(rows + cols - 1) 23 | 24 | // for (let i = 0; i < rows; i++) { 25 | // for (let j = 0; j < cols; j++) { 26 | // const index = i - j + cols - 1 27 | 28 | // if (elements[index] === undefined) { 29 | // elements[index] = matrix[i][j] 30 | // } else if (elements[index] !== matrix[i][j]) { 31 | // return false 32 | // } 33 | // } 34 | // } 35 | 36 | // return true 37 | // }; 38 | 39 | 40 | // compare each row with 1 index shifted 41 | 42 | // Time: O(mn) 43 | // space: O(1) 44 | var isToeplitzMatrix = function(matrix) { 45 | // 46 | // m - 1, 0, -1, ... -(n - 1) 47 | // plus (n - 1) 48 | // (m - 1) + (n - 1) , ..... 0 49 | 50 | const rows = matrix.length 51 | const cols = matrix[0].length 52 | 53 | for (let i = 1; i < rows; i++) { 54 | for (let j = 1; j < cols; j++) { 55 | if (matrix[i][j] !== matrix[i - 1][j - 1]) { 56 | return false 57 | } 58 | } 59 | } 60 | 61 | return true 62 | }; 63 | ``` 64 | -------------------------------------------------------------------------------- /77-combinations.md: -------------------------------------------------------------------------------- 1 | A video explaining this: https://youtu.be/gx0hB85lgiQ 2 | 3 | ```js 4 | /** 5 | * @param {number} n 6 | * @param {number} k 7 | * @return {number[][]} 8 | */ 9 | var combine = function (n, k) { 10 | const result = []; 11 | 12 | const walk = (temp) => { 13 | if (temp.length === k) { 14 | result.push(temp); 15 | return; 16 | } 17 | 18 | // pick numbers from start to n 19 | const start = temp.length === 0 ? 1 : temp[temp.length - 1] + 1; 20 | for (let i = start; i <= n; i++) { 21 | walk(temp.concat(i)); 22 | } 23 | }; 24 | 25 | walk([]); 26 | 27 | return result; 28 | }; 29 | ``` 30 | -------------------------------------------------------------------------------- /779-k-th-symbol-in-grammar.md: -------------------------------------------------------------------------------- 1 | A video explaining this: https://youtu.be/jL39TzQ7HHo 2 | 3 | 4 | ```js 5 | /** 6 | * @param {number} N 7 | * @param {number} K 8 | * @return {number} 9 | */ 10 | 11 | // Recursion 12 | // Time O(N) 13 | // Space O(N) 14 | // var kthGrammar = function(N, K) { 15 | // if (N === 1) { 16 | // return 0 17 | // } 18 | // const prevK = Math.ceil(K / 2) 19 | // if (K % 2 === 0) { 20 | // return (kthGrammar(N - 1, prevK) + 1) % 2 21 | // } else { 22 | // return kthGrammar(N - 1, prevK) 23 | // } 24 | // }; 25 | 26 | // Tail Recursion 27 | // Time O(N) 28 | // Space O(1) 29 | var kthGrammar = function(N, K) { 30 | 31 | const _kthGrammer = (N, K, carrier) => { 32 | if (N === 1) { 33 | return (0 + carrier) % 2 34 | } 35 | const prevK = Math.ceil(K / 2) 36 | return _kthGrammer(N - 1, prevK, carrier + (K % 2 === 0 ? 1 : 0)) 37 | } 38 | 39 | return _kthGrammer(N, K, 0) 40 | }; 41 | ``` 42 | -------------------------------------------------------------------------------- /78-subsets.md: -------------------------------------------------------------------------------- 1 | A video for this problem: https://youtu.be/XbZQigabJeQ 2 | 3 | ```js 4 | /** 5 | * @param {number[]} nums 6 | * @return {number[][]} 7 | */ 8 | 9 | // Time/Space: O(2^n), 10 | var subsets = function(nums) { 11 | const result = [] 12 | 13 | const walk = (temp, start) => { 14 | result.push(temp) 15 | 16 | // pick on from the rest, then go on 17 | for(let i = start; i < nums.length;i++) { 18 | walk(temp.concat(nums[i]), i + 1) 19 | } 20 | } 21 | 22 | walk([], 0) 23 | return result 24 | }; 25 | ``` 26 | -------------------------------------------------------------------------------- /791-custom-sort-string.md: -------------------------------------------------------------------------------- 1 | 2 | Here is my youtube video to explaing this : https://youtu.be/sgiAbThnatY 3 | 4 | 5 | ```js 6 | /** 7 | * @param {string} S 8 | * @param {string} T 9 | * @return {string} 10 | // */ 11 | // var customSortString = function(S, T) { 12 | // // 1. get the Map 13 | // // 2. create compare 14 | // // 3. use sort() 15 | 16 | // const orderMap = new Map() 17 | // for (let i = 0; i < S.length; i++) { 18 | // orderMap.set(S[i], i) 19 | // } 20 | // // O(nlogn) 21 | // const compare = (a, b) => { 22 | // if (!orderMap.has(a)) return -1 23 | // if (!orderMap.has(b)) return 1 24 | // return orderMap.get(a) - orderMap.get(b) 25 | // } 26 | 27 | // const chars = T.split('') 28 | // chars.sort(compare) 29 | 30 | // return chars.join('') 31 | // }; 32 | 33 | var customSortString = function(S, T) { 34 | const count = new Map() 35 | for (let char of T) { 36 | if (count.has(char)) { 37 | count.set(char, count.get(char) + 1) 38 | } else { 39 | count.set(char, 1) 40 | } 41 | } 42 | 43 | let result = '' 44 | for (let char of S) { 45 | if (count.has(char)) { 46 | let num = count.get(char) 47 | while (num > 0) { 48 | result += char 49 | num -= 1 50 | } 51 | 52 | count.delete(char) 53 | } 54 | } 55 | 56 | for (let [char, num] of count) { 57 | while (num > 0) { 58 | result += char 59 | num -= 1 60 | } 61 | } 62 | 63 | return result 64 | }; 65 | ``` 66 | -------------------------------------------------------------------------------- /8-string-to-integer-atoi.md: -------------------------------------------------------------------------------- 1 | A video explaining this: https://youtu.be/FyKsRjW3Gfc 2 | ```js 3 | /** 4 | * @param {string} str 5 | * @return {number} 6 | */ 7 | var myAtoi = function(str) { 8 | let result = 0 9 | let sign = 1 10 | let isNonWhiteSpaceMet = false 11 | let isNumberPhase = false 12 | 13 | for (let i = 0; i < str.length; i++) { 14 | const char = str[i] 15 | if (char === ' ') { 16 | if (!isNonWhiteSpaceMet) { 17 | continue 18 | } else { 19 | break 20 | } 21 | } 22 | isNonWhiteSpaceMet = true 23 | 24 | if (char >= '0' && char <= '9') { 25 | isNumberPhase = true 26 | result = result * 10 + (char - '0') 27 | continue 28 | } 29 | 30 | if (char === '+' && !isNumberPhase) { 31 | isNumberPhase = true 32 | continue 33 | } 34 | 35 | if (char === '-' && !isNumberPhase) { 36 | isNumberPhase = true 37 | sign *= -1 38 | continue 39 | } 40 | 41 | break 42 | } 43 | result *= sign 44 | return Math.min(Math.max(-(2 ** 31), result), 2**31 - 1) 45 | }; 46 | ``` 47 | -------------------------------------------------------------------------------- /80-remove-duplicates-from-sorted-array-ii.md: -------------------------------------------------------------------------------- 1 | My explanation on youtube: https://youtu.be/-Td6YPyPqzk 2 | 3 | ```js 4 | /** 5 | * @param {number[]} nums 6 | * @return {number} 7 | */ 8 | var removeDuplicates = function (nums) { 9 | let p1 = 0; 10 | let p2 = 0; 11 | let count = 0; 12 | 13 | while (p2 < nums.length) { 14 | if (nums[p2] !== nums[p2 - 1]) { 15 | count = 1; 16 | nums[p1] = nums[p2]; 17 | p1 += 1; 18 | } else { 19 | count += 1; 20 | if (count <= 2) { 21 | nums[p1] = nums[p2]; 22 | p1 += 1; 23 | } 24 | } 25 | 26 | p2 += 1; 27 | } 28 | 29 | return p1; 30 | }; 31 | ``` 32 | -------------------------------------------------------------------------------- /824-goat-latin.md: -------------------------------------------------------------------------------- 1 | Here is my youtube video explaining it: https://youtu.be/T84Nvyubl-c 2 | 3 | ```js 4 | /** 5 | * @param {string} S 6 | * @return {string} 7 | */ 8 | 9 | function* nextWord(str) { 10 | let start = 0 11 | let i = 0 12 | while (i < str.length + 1) { 13 | if (str[i] === ' ' || i === str.length) { 14 | yield str.slice(start, i) 15 | start = i + 1 16 | } 17 | i += 1 18 | } 19 | } 20 | 21 | const VOWELS = new Set(['a', 'e', 'i', 'o', 'u']) 22 | 23 | // Time: O(n) 24 | // space: O(1) 25 | var toGoatLatin = function(S) { 26 | const wordGen = nextWord(S) 27 | 28 | let result = '' 29 | 30 | let appendix = 'a' 31 | 32 | for (let word of wordGen) { 33 | let transformed = null 34 | if (VOWELS.has(word[0].toLowerCase())) { 35 | transformed = word + 'ma' 36 | } else { 37 | transformed = word.slice(1) + word[0] + 'ma' 38 | } 39 | 40 | transformed += appendix 41 | appendix += 'a' 42 | 43 | result += result === '' ? transformed : (' ' + transformed) 44 | } 45 | 46 | return result 47 | }; 48 | ``` 49 | -------------------------------------------------------------------------------- /83-remove-duplicates-from-sorted-list.md: -------------------------------------------------------------------------------- 1 | A video explaining this on youtube: https://youtu.be/lrrM8yXSSiA 2 | 3 | ```js 4 | /** 5 | * Definition for singly-linked list. 6 | * function ListNode(val) { 7 | * this.val = val; 8 | * this.next = null; 9 | * } 10 | */ 11 | /** 12 | * @param {ListNode} head 13 | * @return {ListNode} 14 | */ 15 | // Time: O(n) 16 | // Space: O(1) 17 | var deleteDuplicates = function(head) { 18 | if (head === null) return null 19 | 20 | let p1 = head.next 21 | 22 | const newStart = new ListNode(0) 23 | newStart.next = head 24 | let p2 = head 25 | 26 | while (p1) { 27 | if (p1.val === p2.val) { 28 | // skip 29 | } else { 30 | p2.next = p1 31 | p2 = p2.next 32 | } 33 | p1 = p1.next 34 | } 35 | 36 | p2.next = null 37 | 38 | return newStart.next 39 | }; 40 | ``` 41 | -------------------------------------------------------------------------------- /844-backspace-string-compare.md: -------------------------------------------------------------------------------- 1 | Here is my video explaining : https://youtu.be/ku2EKgqMp0M 2 | 3 | ```js 4 | /** 5 | * @param {string} S 6 | * @param {string} T 7 | * @return {boolean} 8 | */ 9 | 10 | // Time: O(n) n count of the letters 11 | // Space: O(n) 12 | var backspaceCompare = function(S, T) { 13 | // ab#c 14 | 15 | // [a, c] 16 | 17 | 18 | const getTyped = (input) => { 19 | const result = [] 20 | for (let char of input) { 21 | if (char === '#') { 22 | result.pop() 23 | } else { 24 | result.push(char) 25 | } 26 | } 27 | return result.join() 28 | } 29 | 30 | return getTyped(S) === getTyped(T) 31 | }; 32 | ``` 33 | -------------------------------------------------------------------------------- /869-reordered-power-of-2.md: -------------------------------------------------------------------------------- 1 | video explaining https://www.youtube.com/watch?v=GJvX4qqF5u8 2 | 3 | ```js 4 | /** 5 | * @param {number} N 6 | * @return {boolean} 7 | */ 8 | var reorderedPowerOf2 = function(N) { 9 | // get permuation, and check if it is power of 2 10 | 11 | // length of n 12 | // n! * log2 N 13 | 14 | const normalize = (str) => { 15 | return str.split('').sort().join('') 16 | } 17 | 18 | const powers = new Set() 19 | 20 | const max = 10 ** 9 21 | 22 | let base = 1 23 | while (base <= max) { 24 | powers.add(normalize(base.toString())) 25 | base *= 2 26 | } 27 | 28 | return powers.has(normalize(N.toString())) 29 | 30 | }; 31 | ``` 32 | -------------------------------------------------------------------------------- /89-gray-code.md: -------------------------------------------------------------------------------- 1 | Here is my video explaining this: https://youtu.be/xDhEnt0fjc8 2 | 3 | ```js 4 | /** 5 | * @param {number} n 6 | * @return {number[]} 7 | */ 8 | var grayCode = function(n) { 9 | // 0 -> 1 10 | 11 | // {0, 1} + (n - 1) case 12 | 13 | // 00, 01, 10, 11 14 | // list(n - 1). reverse(1 + list(n - 1)) 15 | 16 | 17 | const result = [0] 18 | 19 | for (let i = 1; i <= n; i++) { 20 | result.push(...result.slice(0).reverse().map(item => item + 2 ** (i - 1))) 21 | } 22 | return result 23 | }; 24 | ``` 25 | -------------------------------------------------------------------------------- /896-monotonic-array.md: -------------------------------------------------------------------------------- 1 | Here is my video explaining it: https://youtu.be/qlLqOIDH-Ow 2 | 3 | ```js 4 | /** 5 | * @param {number[]} A 6 | * @return {boolean} 7 | */ 8 | 9 | // Time: O(n) 10 | // Space: O(1) 11 | var isMonotonic = function(A) { 12 | let isAcending = true 13 | let isDecending = true 14 | 15 | for (let i = 1; i < A.length; i++) { 16 | if (A[i] > A[i - 1]) { 17 | isDecending = false 18 | } 19 | 20 | if (A[i] < A[i - 1]) { 21 | isAcending = false 22 | } 23 | } 24 | 25 | return isAcending || isDecending 26 | }; 27 | ``` 28 | -------------------------------------------------------------------------------- /9-palindrome-number.md: -------------------------------------------------------------------------------- 1 | A video explaining this: https://youtu.be/Apz80J3ONVY 2 | 3 | ```js 4 | /** 5 | * @param {number} x 6 | * @return {boolean} 7 | */ 8 | 9 | // TIME: log10(x) 10 | // Space: O(1) 11 | 12 | // by comparing the first and last digit 13 | // var isPalindrome = function(x) { 14 | // if (x < 0) return false 15 | 16 | // // get the length of digits 17 | // let length = 1 18 | // let base = 1 19 | // while (base * 10 <= x) { 20 | // length += 1 21 | // base *= 10 22 | // } 23 | 24 | // while (base > 1) { 25 | // // first digit 26 | // const firstDigit = Math.floor(x / base) 27 | // const lastDigit = x % 10 28 | 29 | // if (firstDigit !== lastDigit) { 30 | // return false 31 | // } 32 | 33 | // x = Math.floor((x - base * firstDigit) / 10) 34 | 35 | // base = base / 100 36 | // } 37 | 38 | // return true 39 | // }; 40 | 41 | // TIME: log10(x) 42 | // Space: O(1) 43 | // calculate the reversed number 44 | var isPalindrome = function(x) { 45 | if (x < 0) return false 46 | 47 | // get the last digits accumulatively 48 | // and sum up 49 | let reversedNumber = 0 50 | const originalX = x 51 | 52 | while (x > 0) { 53 | const digit = x % 10 54 | reversedNumber = reversedNumber * 10 + digit 55 | x = (x - digit) / 10 56 | } 57 | return reversedNumber === originalX 58 | }; 59 | ``` 60 | -------------------------------------------------------------------------------- /90-subsets-ii.md: -------------------------------------------------------------------------------- 1 | Here is a video explaining this: https://youtu.be/cY08iZtNqdI 2 | 3 | ```js 4 | /** 5 | * @param {number[]} nums 6 | * @return {number[][]} 7 | */ 8 | var subsetsWithDup = function(nums) { 9 | // [a, b, c] 10 | // pick a number => pick a number from the rest => until there is no number left 11 | // generate the permupation 12 | 13 | // O(nlgn) 14 | nums.sort() 15 | 16 | const result = [] 17 | 18 | // call stack O(n) 19 | const pick = (current, start) => { 20 | result.push(current) 21 | // [1] 22 | for (let i = start; i < nums.length; i++) { 23 | if (i > start && nums[i] === nums[i - 1]) { 24 | continue 25 | } else { 26 | pick(current.concat(nums[i]), i + 1) 27 | } 28 | } 29 | } 30 | 31 | // Time: O(2^N) 32 | pick([], 0) 33 | 34 | return result 35 | }; 36 | ``` 37 | -------------------------------------------------------------------------------- /905-sort-array-by-parity.md: -------------------------------------------------------------------------------- 1 | A video explaining this: https://youtu.be/9VRcShmQoe0 2 | ```js 3 | /** 4 | * @param {number[]} A 5 | * @return {number[]} 6 | */ 7 | 8 | // Time: O(n) 9 | var sortArrayByParity = function(A) { 10 | // [3,1,2,4] 11 | // [4,1,2,3] 12 | // [4,2,1,3] 13 | 14 | // [3,1,2,5] 15 | // [2,1,3,5] 16 | 17 | let left = 0 18 | let right = A.length - 1 19 | 20 | while (left < right) { 21 | // move left to the first odd number 22 | // move right to the last even number 23 | while(left < A.length && A[left] % 2 === 0) { 24 | left += 1 25 | } 26 | 27 | while (right > -1 && A[right] % 2 === 1) { 28 | right -= 1 29 | } 30 | 31 | if (left < right) { 32 | [A[left], A[right]] = [A[right], A[left]] 33 | left += 1 34 | right -= 1 35 | } 36 | } 37 | 38 | return A 39 | }; 40 | ``` 41 | -------------------------------------------------------------------------------- /921-minimum-add-to-make-parentheses-valid.md: -------------------------------------------------------------------------------- 1 | Here is my video explaining it: https://youtu.be/Bzzu4vAozYA 2 | 3 | ```js 4 | /** 5 | * @param {string} S 6 | * @return {number} 7 | */ 8 | 9 | // Time: O(n) 10 | // Space: O(1) 11 | var minAddToMakeValid = function(S) { 12 | let invalidLeftCount = 0 13 | let invalidRightCount = 0 14 | 15 | for (let char of S) { 16 | if (char === '(') { 17 | invalidLeftCount += 1 18 | } else { 19 | if (invalidLeftCount === 0) { 20 | invalidRightCount += 1 21 | } else { 22 | invalidLeftCount -= 1 23 | } 24 | } 25 | } 26 | 27 | return invalidLeftCount + invalidRightCount 28 | }; 29 | ``` 30 | -------------------------------------------------------------------------------- /93-restore-ip-addresses.md: -------------------------------------------------------------------------------- 1 | Here is a video explaining this: https://youtu.be/XQR0KeyVciM 2 | 3 | ```js 4 | /** 5 | * @param {string} s 6 | * @return {string[]} 7 | */ 8 | var restoreIpAddresses = function(s) { 9 | // 0.0.0.0 ~ 255.255.255.255 10 | const result = [] 11 | 12 | 13 | const walk = (temp, countOfSegments, start) => { 14 | if (countOfSegments === 4 && start === s.length) { 15 | result.push(temp) 16 | } 17 | 18 | if (countOfSegments < 4) { 19 | for (let i = start; i < start + 3 && i < s.length; i++) { 20 | const num = s.slice(start, i + 1) 21 | if (num <= 255) { 22 | walk(temp + (temp === '' ? '' : '.') + num, countOfSegments + 1, i + 1) 23 | if (num == 0) break 24 | } else { 25 | break 26 | } 27 | } 28 | } 29 | } 30 | 31 | walk('', 0, 0) 32 | 33 | return result 34 | }; 35 | ``` 36 | -------------------------------------------------------------------------------- /938-range-sum-of-bst.md: -------------------------------------------------------------------------------- 1 | Me explaining it on youtube: https://youtu.be/YauS1CuAP2Q 2 | 3 | ```js 4 | /** 5 | * Definition for a binary tree node. 6 | * function TreeNode(val, left, right) { 7 | * this.val = (val===undefined ? 0 : val) 8 | * this.left = (left===undefined ? null : left) 9 | * this.right = (right===undefined ? null : right) 10 | * } 11 | */ 12 | /** 13 | * @param {TreeNode} root 14 | * @param {number} L 15 | * @param {number} R 16 | * @return {number} 17 | */ 18 | var rangeSumBST = function(root, L, R) { 19 | if (root === null) return 0 20 | // brute force 21 | // O(N) 22 | 23 | // 10 24 | // 5 15 25 | // 3 7 18 26 | 27 | // return sum to a range [] 28 | const walk = (node, min, max) => { 29 | if (node === null) return 0 30 | 31 | let sumLeft = 0 32 | let sumRight = 0 33 | 34 | if (node.val > min) { 35 | sumLeft = walk(node.left, min, max) 36 | } 37 | 38 | if (node.val < max) { 39 | sumRight = walk(node.right, min, max) 40 | } 41 | 42 | let sum = sumLeft + sumRight 43 | 44 | if (node.val >= min && node.val <= max) { 45 | sum += node.val 46 | } 47 | 48 | return sum 49 | } 50 | 51 | return walk(root, L, R) 52 | }; 53 | ``` 54 | -------------------------------------------------------------------------------- /94-binary-tree-inorder-traversal.md: -------------------------------------------------------------------------------- 1 | Here is me explaining this problem: https://youtu.be/BVY0SKdN0PM 2 | 3 | ```js 4 | /** 5 | * Definition for a binary tree node. 6 | * function TreeNode(val, left, right) { 7 | * this.val = (val===undefined ? 0 : val) 8 | * this.left = (left===undefined ? null : left) 9 | * this.right = (right===undefined ? null : right) 10 | * } 11 | */ 12 | /** 13 | * @param {TreeNode} root 14 | * @return {number[]} 15 | */ 16 | var inorderTraversal = function(root) { 17 | const result = [] 18 | if (root === null) return result 19 | 20 | const stack = [] 21 | 22 | const pushStack = (node) => { 23 | while(node !== null) { 24 | stack.push(node) 25 | node = node.left 26 | } 27 | } 28 | 29 | pushStack(root) 30 | 31 | while(stack.length > 0) { 32 | const top = stack.pop() 33 | result.push(top.val) 34 | if (top.right) { 35 | pushStack(top.right) 36 | } 37 | } 38 | 39 | return result 40 | }; 41 | ``` 42 | -------------------------------------------------------------------------------- /95-unique-binary-search-trees-ii.md: -------------------------------------------------------------------------------- 1 | A video for this problem: https://youtu.be/USeqiWodceU 2 | 3 | 4 | ```js 5 | /** 6 | * Definition for a binary tree node. 7 | * function TreeNode(val) { 8 | * this.val = val; 9 | * this.left = this.right = null; 10 | * } 11 | */ 12 | /** 13 | * @param {number} n 14 | * @return {TreeNode[]} 15 | */ 16 | // Time: Catlan(n) 17 | // Space: Calan(n) 18 | var generateTrees = function(n) { 19 | if (n === 0) return [] 20 | 21 | const generate = (start, end) => { 22 | const result = [] 23 | 24 | if (start > end) return [null] 25 | 26 | for (let i = start; i <= end; i++) { 27 | const leftTrees = generate(start, i - 1) 28 | const rightTrees = generate(i + 1, end) 29 | 30 | for (let leftNode of leftTrees) { 31 | for (let rightNode of rightTrees) { 32 | const root = new TreeNode(i) 33 | root.left = leftNode 34 | root.right = rightNode 35 | 36 | result.push(root) 37 | } 38 | } 39 | } 40 | 41 | return result 42 | } 43 | 44 | return generate(1, n) 45 | }; 46 | ``` 47 | -------------------------------------------------------------------------------- /958-check-completeness-of-a-binary-tree.md: -------------------------------------------------------------------------------- 1 | Here is my video explaining this: https://youtu.be/jB0CeVd312M 2 | 3 | ```js 4 | /** 5 | * Definition for a binary tree node. 6 | * function TreeNode(val, left, right) { 7 | * this.val = (val===undefined ? 0 : val) 8 | * this.left = (left===undefined ? null : left) 9 | * this.right = (right===undefined ? null : right) 10 | * } 11 | */ 12 | /** 13 | * @param {TreeNode} root 14 | * @return {boolean} 15 | */ 16 | 17 | // Time: O(N) 18 | // space: O(N/2) => O(N) 19 | var isCompleteTree = function(root) { 20 | // BFS 21 | 22 | const queue = [root] 23 | 24 | let prevLevelNodeCount = 0 25 | let prevLevelNodeCounteExpected = 0 26 | 27 | while (queue.length > 0) { 28 | if (prevLevelNodeCount !== prevLevelNodeCounteExpected) { 29 | return false 30 | } 31 | 32 | prevLevelNodeCount = queue.length 33 | prevLevelNodeCounteExpected = prevLevelNodeCounteExpected === 0 ? 1 : prevLevelNodeCounteExpected * 2 34 | 35 | queue.push(null) 36 | 37 | let isNullMet = false 38 | let head 39 | while (head = queue.shift()) { 40 | if (head.left) { 41 | if (isNullMet) return false 42 | queue.push(head.left) 43 | } else { 44 | isNullMet = true 45 | } 46 | 47 | if (head.right) { 48 | if (isNullMet) return false 49 | queue.push(head.right) 50 | } else { 51 | isNullMet = true 52 | } 53 | } 54 | } 55 | 56 | return true 57 | 58 | }; 59 | ``` 60 | -------------------------------------------------------------------------------- /96-unique-binary-search-trees.md: -------------------------------------------------------------------------------- 1 | me explaining this on youtube: https://youtu.be/TWPcEDt8Mtg 2 | 3 | ```js 4 | /** 5 | * @param {number} n 6 | * @return {number} 7 | */ 8 | 9 | // recursion solution 10 | // Time: f(n) = Catalan(n) Catalan Number 11 | // Catalan(n) = (2n)C(n) / (n + 1) 12 | 13 | // var numTrees = function(n) { 14 | // if (n <= 1) return 1 15 | 16 | // let count = 0 17 | // for (let i = 1; i <= n; i++) { 18 | // count += numTrees(i - 1) * numTrees(n - i) 19 | // } 20 | 21 | // return count 22 | // }; 23 | 24 | 25 | // memoization 26 | const cache = new Map() 27 | var numTrees = function(n) { 28 | if (cache.has(n)) return cache.get(n) 29 | 30 | if (n <= 1) return 1 31 | 32 | let count = 0 33 | for (let i = 1; i <= n; i++) { 34 | count += numTrees(i - 1) * numTrees(n - i) 35 | } 36 | 37 | cache.set(n, count) 38 | return count 39 | } 40 | ``` 41 | -------------------------------------------------------------------------------- /985-sum-of-even-numbers-after-queries.md: -------------------------------------------------------------------------------- 1 | Here is a youtube video explaining this: https://youtu.be/LwZ0MZ7Z7sk 2 | 3 | ```js 4 | /** 5 | * @param {number[]} A 6 | * @param {number[][]} queries 7 | * @return {number[]} 8 | */ 9 | var sumEvenAfterQueries = function(A, queries) { 10 | const result = [] 11 | let sum = A.reduce((sum, item) => { 12 | return sum + (item % 2 === 0 ? item : 0) 13 | }, 0) 14 | 15 | 16 | for (let query of queries) { 17 | const prev = A[query[1]] 18 | const curr = A[query[1]] + query[0] 19 | 20 | let delta = 0 21 | 22 | if (prev % 2 === 0 && curr % 2 !== 0) { 23 | delta = - prev 24 | } 25 | 26 | if (prev % 2 === 0 && curr % 2 === 0) { 27 | delta = curr - prev 28 | } 29 | 30 | if (prev % 2 !== 0 && curr % 2 === 0) { 31 | delta = curr 32 | } 33 | 34 | sum += delta 35 | A[query[1]] = curr 36 | 37 | result.push(sum) 38 | } 39 | 40 | return result 41 | }; 42 | ``` 43 | -------------------------------------------------------------------------------- /986-interval-list-intersections.md: -------------------------------------------------------------------------------- 1 | me explaining this on youtube: https://youtu.be/zLSGHhE8Tug 2 | 3 | ```js 4 | /** 5 | * @param {number[][]} A 6 | * @param {number[][]} B 7 | * @return {number[][]} 8 | */ 9 | 10 | // Time: O(m + n) 11 | // Space: O(1) 12 | var intervalIntersection = function(A, B) { 13 | 14 | const getIntersection = (a, b) => { 15 | if (a[1] < b[0] || b[1] < a[0]) return null 16 | return [Math.max(a[0], b[0]), Math.min(a[1], b[1])] 17 | } 18 | 19 | const result = [] 20 | // get intersection and shift the heads(smaller) 21 | 22 | 23 | while (A.length > 0 && B.length > 0) { 24 | const a = A[0] 25 | const b = B[0] 26 | 27 | const intersection = getIntersection(a, b) 28 | 29 | if (intersection) { 30 | result.push(intersection) 31 | } 32 | 33 | // shift the interval with smaller right border 34 | if (A[0][1] <= B[0][1]) { 35 | A.shift() 36 | } else { 37 | B.shift() 38 | } 39 | } 40 | 41 | 42 | return result 43 | }; 44 | ``` 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | leetCode solutions in JavaScript 2 | 3 | For detail explanation, you can see the videos on my channel https://www.youtube.com/channel/UC0qiieVBpjA6YODgIFf6Afg 4 | --------------------------------------------------------------------------------