├── .DS_Store ├── Arrays ├── .DS_Store ├── array-of-products.js ├── array-sign.js ├── average-salary.js ├── best-time-to-buy-stock.js ├── best-time-to-buy-stockII.js ├── boats-to-save-people.js ├── can-place-flowers.js ├── circular-game-losers.js ├── concat-array.js ├── container-with-most-water.js ├── contains-dupilcate.js ├── contains-duplicateII.js ├── contains-duplicateIII.js ├── contiguous-array.js ├── count-students.js ├── count-unhappy-friends.js ├── counting-elements.js ├── decreasing-ratings.js ├── delete-columns.js ├── difference-of-arrays.js ├── duplicate-number.js ├── find-pivot-index.js ├── find-zero.js ├── first-missing-positive.js ├── frequency-of-most-frequent.js ├── h-index.js ├── implement-queue.js ├── implement-stack.js ├── insert-interval.js ├── intersect.js ├── intersection-three-arrays.js ├── k-radius-subaverages.js ├── kth-missing-positive.js ├── longest-mountain.js ├── longest-subarray-ones.js ├── max-consecutive-ones.js ├── max-consecutive-onesII.js ├── max-consecutive-onesIII.js ├── max-turbulence-size.js ├── max-value.js ├── maximize-confusion-of-exam.js ├── maximum-product-subarray.js ├── maximum-score-good-subarray.js ├── maximum-subarray.js ├── maximum-sum-circ-subarray.js ├── min-consecutive-cards.js ├── min-cost-array-equal.js ├── min-num-operations-equals.js ├── min-operations-array-incresing.js ├── min-product-sum.js ├── min-size-subarray-sum.js ├── min-swaps-ones.js ├── min-val-positive-step.js ├── minimize-max-of-array.js ├── minimize-string-length.js ├── missing-ranges.js ├── move-zeroes.js ├── non-constructible-change.js ├── non-overlapping-intervals.js ├── plus-one.js ├── product-array-except-self.js ├── queue-reconstruction-by-height.js ├── queue.js ├── rearrange-elements.js ├── remove-duplicates.js ├── remove-duplicatesII.js ├── remove-element.js ├── rotate-array.js ├── shortest-word-distance.js ├── shortest-word-distanceIII.js ├── shuffle-an-array.js ├── single-number.js ├── smallest-difference.js ├── solving-questions.js ├── static-array.js ├── subarray-ranges.js ├── subarray-sum-k.js ├── subarray-sums-divisible.js ├── summary-ranges.js ├── three-sum-closest.js ├── three-sum-zero.js ├── time-needed-tickets.js ├── trapping-rain-water.js ├── two-sum.js ├── two-sumII.js ├── unique-num-occurrences.js ├── valid-sudoku.js ├── zero-filled-subarray.js └── zero-sum-subarray.js ├── Backtracking ├── brace-expansion.js ├── combination-sum.js ├── combination-sumII.js ├── combination-sumIII.js ├── combinations.js ├── letter-case-permutation.js ├── n-queens.js ├── num-tiles.js ├── num-ways-pizza.js ├── palindrome-partioning.js ├── permutations.js ├── permutationsII.js ├── restore-IP-addresses.js ├── subsets.js ├── subsetsII.js ├── unique-pathsIII.js └── word-search.js ├── Binary ├── count-bits.js ├── counting-bits.js ├── num-1-bits.js ├── parity.js └── reverse-bits.js ├── Binary_Trees ├── are-cousins.js ├── average-levels.js ├── binary-tree-completeness.js ├── binary-trees-paths.js ├── bst-greater-sum.js ├── bst-traversal.js ├── closest-bst-value.js ├── construct-tree.js ├── cousins-in-binary-treeII.js ├── deepest-level-sum.js ├── delete-node.js ├── diameter-of-tree.js ├── distance-k.js ├── duplicate-subtree.js ├── evaluate-boolean-binary-tree.js ├── find-bottom-left-val.js ├── find-closest-leaf.js ├── find-corresponding-node.js ├── find-leaves.js ├── find-successor.js ├── flatten-binary-tree.js ├── get-all-elements.js ├── get-lonely-nodes.js ├── good-nodes.js ├── inorder-successor.js ├── insert-into-bst.js ├── invert-tree.js ├── is-balanced.js ├── is-subtree.js ├── kth-largest-sum.js ├── kth-smallest.js ├── largest-value.js ├── level-order-bottom.js ├── level-order-traversal.js ├── longest-univalue-path.js ├── longest-zigzag.js ├── lowest-common-ancestor.js ├── lowest-common-ancestorII.js ├── lowest-common-ancestorIII.js ├── lowest-common-ancestorIV.js ├── lowest-common-manager.js ├── max-binary-tree.js ├── max-depth.js ├── max-level-sum.js ├── max-path-sum.js ├── max-product.js ├── max-width.js ├── maximum-average-subtree.js ├── merge-trees.js ├── min-depth.js ├── min-height-bst.js ├── most-frequent-subtree-sum.js ├── nodes-equal-average-subtree.js ├── nodes-equal-sum-descendants.js ├── path-sum.js ├── path-sumII.js ├── path-sumIII.js ├── populating-right-pointers.js ├── range-sum.js ├── recover-bst.js ├── reverse-odd-levels.js ├── right-side-view.js ├── same-tree.js ├── search-in-bst.js ├── smallest-string-from-leaf.js ├── squares-of-sorted-array.js ├── sum-even-grandparent.js ├── sum-numbers.js ├── sum-of-left-leaves.js ├── symmetric-tree.js ├── time-infected.js ├── two-sum-BSTs.js ├── two-sum.js ├── univalue-subtrees.js ├── upside-down-binary-tree.js ├── validate-bst.js ├── vertical-order-traversal.js └── zigzag-traversal.js ├── Design ├── design-data-store.js ├── design-file-system.js ├── design-hashmap.js ├── design-hashset.js ├── design-hit-counter.js ├── design-leader-board.js ├── design-logger-system.js ├── design-memory-allocator.js ├── design-parking-system.js ├── design-phone-directory.js ├── design-two-sum-class.js ├── design-underground-system.js ├── dot-product-sparse-vectors.js ├── find-pairs-with-sums.js ├── implement-magic-dictionary.js ├── insert-delete-getrandom.js ├── insert-delete-getrandomII.js ├── map-sum-pairs.js ├── my-calendarI.js ├── my-calendarII.js ├── online-stock-span.js ├── range-frequency-queries.js ├── range-sum-query.js ├── range-sum-query2D.js ├── shortest-word-distanceII.js ├── snapshot-array.js ├── subrectangle-queries.js └── time-based-keyvalue-store.js ├── Dynamic_Programming ├── .DS_Store ├── climbing-stairs.js ├── coin-change.js ├── coin-changeII.js ├── combination-sum4.js ├── count-ways-good-strings.js ├── edit-distance.js ├── find-target-sum-ways.js ├── house-robber.js ├── house-robberII.js ├── house-robberIII.js ├── jump-game.js ├── jump-gameII.js ├── last-stone-weightII.js ├── longest-arithmetic-seq-difference.js ├── longest-arithmetic-sequence.js ├── longest-common-subsequence.js ├── longest-increasing-subseq.js ├── longest-palin-subseq.js ├── max-num-events-attended.js ├── max-val-k-coins.js ├── min-cost-cut-stick.js ├── minimize-difference.js ├── minimum-cost-for-tickets.js ├── number-of-LIS.js ├── ones-and-zeros.js ├── partition-subset-sum.js ├── path-equal-ones-zeros.js ├── reducing-dishes.js ├── ship-within-days.js ├── staircase-traversal.js ├── sum-possible.js ├── uncrossed-lines.js ├── unique-paths.js └── unique-pathsII.js ├── Game_Theory ├── king-domino.js └── stone-game.js ├── Graph_Theory ├── critical-connections.js ├── critical-pseudo-critical-edges.js ├── kruskals.js ├── min-cost-supply-water.js └── topological-sort.js ├── Graphs ├── alien-dictionary.js ├── all-paths-from-source.js ├── all-paths.js ├── best-bridge.js ├── bidirectional-bfs.js ├── cheapest-flights-k-stops.js ├── clone-graph.js ├── closest-carrot.js ├── closest-node-to-path.js ├── count-complete-components.js ├── count-paths.js ├── course-schedule.js ├── course-scheduleII.js ├── course-scheduleIV.js ├── detonate-max-bombs.js ├── distance-limited-paths.js ├── distance-to-cycle.js ├── escape-maze.js ├── evaluate-division.js ├── far-from-lands.js ├── find-eventual-safe-states.js ├── find-exit.js ├── find-the-town-judge.js ├── get-food.js ├── graph-schedule.js ├── halfway-course.js ├── has-cycle.js ├── is-bipartite.js ├── island-count.js ├── island-perimeter.js ├── keys-and-rooms.js ├── knight-attack.js ├── largest-path-value.js ├── last-day-to-cross.js ├── longest-cycle.js ├── making-largest-island.js ├── max-area-islands.js ├── max-num-moves.js ├── min-cost-all-points.js ├── min-num-vertices.js ├── min-obstacle-removal.js ├── min-reorder.js ├── min-score.js ├── nearest-exit.js ├── network-delay.js ├── num-closed-islands.js ├── num-similar-groups.js ├── number-of-provinces.js ├── pacific-atlantic-water-flow.js ├── parallel-courses.js ├── path-with-max-probability.js ├── paths-maze-lead-tosamedoor.js ├── reconstruct-itinerary.js ├── rotting-oranges.js ├── set-matrix-zeroes.js ├── shortest-distance-all-buildings.js ├── shortest-path-binary-matrix.js ├── shortest-path-obstacle-elimination.js ├── sliding-puzzle.js ├── surrounded-regions.js ├── swim-in-water.js ├── the-maze.js ├── the-mazeII.js ├── time-needed-inform-employees.js ├── unreachable-pairs.js ├── valid-path.js └── walls-and-gates.js ├── Hacker_Rank ├── array-manipulation.js ├── counting-valleys.js ├── jumping-clouds.js ├── matching-socks.js ├── maximum-toys.js └── min-num-swaps.js ├── Heaps ├── k-closest-points.js ├── k-pairs-smallest-sum.js ├── kth-largest-in-stream.js ├── last-stone-weight.js ├── max-heap.js ├── max-subseq-score.js ├── merge-ksorted-lists.js ├── min-heap.js └── smallest-num-infinite-set.js ├── Intervals ├── employee-free-time.js ├── interval-intersection.js ├── meeting-roomsII.js ├── meeting-roomsIII.js ├── merge-intervals.js └── remove-interval.js ├── Iterators ├── binary-search-iterator.js ├── flatten-2D-vector.js ├── flatten-nested-list-iterator.js └── zigzag-iterator.js ├── Linked_Lists ├── LRU-cache.js ├── add-two-numbersII.js ├── delete-middle.js ├── delete-n-nodes.js ├── delete-node.js ├── doubly-linked-list.js ├── find-cycle.js ├── flatten-multilevel-list.js ├── has-cycle.js ├── interesection-two-ll.js ├── is-palindrome.js ├── linked-list-binary-tree.js ├── linked-list-components.js ├── merge-lists.js ├── merge-nodes.js ├── node-swap.js ├── pair-sum.js ├── partition.js ├── plus-one.js ├── remove-duplicates.js ├── remove-duplicatesII.js ├── remove-nth-node.js ├── reorder.js ├── reverse-k-groups.js ├── reverse-linked-list.js ├── reverse-linked-listII.js ├── rotate-list.js ├── sum-list.js ├── swap-nodes.js └── swap-pairs.js ├── Matrix ├── 01-matrix.js ├── count-negatives.js ├── diagnoal-sort.js ├── diagonal-sum.js ├── equal-row-column.js ├── find-smallest-element.js ├── flipping-an-image.js ├── lonely-pixel.js ├── max-increase-city-skyline.js ├── minimize-max-value.js ├── minimum-path-sum.js ├── rotate-image.js ├── spiral-matrix.js ├── spiral-matrixII.js ├── sum-in-matrix.js ├── valid-word-square.js └── zigzag-conversion.js ├── Miscellaneous ├── add-digits.js ├── bookcase.js ├── count-primes.js ├── filter-rooms.js ├── find_treasure.js ├── fizz-buzz.js ├── is-prime.js ├── matching-titles.js ├── medschool-matching.js ├── number-of-days.js ├── pair-songs.js ├── power-of-three.js ├── roman-to-integer.js ├── shuffle-an-array.js ├── sum-multiples.js ├── train-seats.js └── two-city-scheduling.js ├── OA_Types ├── books-max-number.js ├── class-grouping.js ├── count-binary-substrings.js ├── decode-string-frequency.js ├── find-lowest-price.js ├── grid-connections.js ├── group-imbalance.js ├── linked-list-sum.js ├── max-profit.js ├── maximum-teams.js ├── maximum-units-on-truck.js ├── merge-two-lists.js ├── min-average-stockprice.js ├── min-difficulty-job.js ├── minimum-bribes.js ├── minimum-coin-flips.js ├── number-of-provinces.js ├── optimal-utilization.js ├── reorder-log-files.js ├── repeated-string.js ├── robot-bounded.js ├── sherlock-anagrams.js ├── shipment-imbalances.js ├── shopping-options.js ├── shopping-patterns.js ├── simple-cypher.js ├── slowest-key.js ├── storage-optimazation-2.js ├── storage-optimization.js ├── top-k-elements.js ├── top-k-frequent-words.js ├── tree-heights.js ├── triangle-sums.js ├── two-sum.js ├── unique-primes.js └── valid-discount-coupons.js ├── README.md ├── Recursion ├── generate-div-tags.js ├── generate-parenthesis.js ├── letters-combo.js └── scramble-string.js ├── Searching ├── binary-search.js ├── find-k-closest-elements.js ├── find-min.js ├── find-minII.js ├── first-bad-version.js ├── is-bad-version.js ├── jump-gameIV.js ├── koko-eating-bananas.js ├── kth-largest-element.js ├── longest-valid-obstacle.js ├── max-candies.js ├── max-count-positive-negative.js ├── median-two-sorted-arrays.js ├── num-subseq.js ├── search-for-range.js ├── search-insert-position.js ├── search-matrix.js ├── search-matrixII.js ├── shifted-binary-search.js ├── single-element.js └── spells-potions.js ├── Segement_Trees ├── longest-increasing-subsequenceII.js ├── range-sum-query.js └── segment-tree.js ├── Simulation └── pour-water.js ├── Sorting ├── bucket-sort.js ├── heap-sort.js ├── insertion-sort.js ├── merge-sort.js ├── merge-sorted-array.js ├── quick-sort.js ├── sort-an-array.js ├── sort-colors.js └── wiggle-sort.js ├── Stacks ├── baseball-game.js ├── browser-history.js ├── daily-temperatures.js ├── min-stack.js ├── next-greater-element.js ├── remove-k-digits.js ├── simplify-path.js ├── sort-stack.js ├── stack.js ├── valid-subarrays.js ├── validate-stack-sequences.js └── visible-mountains.js ├── Strings ├── article-formatting.js ├── balanced-brackets.js ├── buddy-strings.js ├── decode-string.js ├── excel-columns.js ├── first-unique-character.js ├── flip-to-monotone-increasing.js ├── group-anagrams.js ├── is-anagram.js ├── is-palindrome.js ├── isValid.js ├── k-length-substr.js ├── kth-distinct-string.js ├── longest-common-prefix.js ├── longest-palin-substring.js ├── longest-repeating-char.js ├── longest-substr-k-chars.js ├── longest-substr-two-chars.js ├── longest-substring-length.js ├── longest-substring.js ├── max-num-vowels.js ├── merge-strings.js ├── min-flips.js ├── naming-a-company.js ├── needle-in-haystack.js ├── optimal-partition-of-string.js ├── palindrome-number.js ├── permutation-in-string.js ├── permutation-palindrome.js ├── pyramid-transition-matrix.js ├── remove-digit.js ├── removing-stars.js ├── repeated-DNA.js ├── reverse-integer.js ├── reverse-string.js ├── reverse-vowels.js ├── reverse-words-in-stringII.js ├── semordnilap.js ├── smallest-letter.js ├── str-str.js ├── string-compression.js ├── string-to-integer.js ├── text-justification.js ├── uncompress.js ├── valid-palindrome.js ├── word-ladder.js └── word-pattern.js ├── Trees ├── bk-tree.js ├── clone-nary-tree.js ├── longest-path.js ├── max-depth-nary.js ├── minimum-absolute-difference.js ├── nary-tree-diameter.js ├── nary-tree-level-order.js ├── nary-tree-postorder.js ├── nary-tree-preorder.js ├── smallest-common-region.js ├── sum-of-distances.js └── tree-diameter.js ├── Tries ├── prefix-trie.js ├── word-dictionary.js └── word-searchII.js └── Union_Find ├── connected-components.js ├── connecting-cities.js ├── is-valid-tree.js ├── max-area-island.js ├── network-connected.js ├── num-islands.js ├── num-islandsII.js ├── redudant-connectionsII.js ├── redundant-connections.js ├── remove-max-num-edges.js └── union-find.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zacharydfreeman/algorithms/11f8a2c55502a51208136afb11d069bdc21d5b00/.DS_Store -------------------------------------------------------------------------------- /Arrays/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zacharydfreeman/algorithms/11f8a2c55502a51208136afb11d069bdc21d5b00/Arrays/.DS_Store -------------------------------------------------------------------------------- /Arrays/array-sign.js: -------------------------------------------------------------------------------- 1 | /* 2 | There is a function signFunc(x) that returns: 3 | 4 | 1 if x is positive. 5 | -1 if x is negative. 6 | 0 if x is equal to 0. 7 | You are given an integer array nums. Let product be the product of all values in the array nums. 8 | 9 | Return signFunc(product). 10 | 11 | Input: nums = [-1,-2,-3,-4,3,2,1] 12 | Output: 1 13 | Explanation: The product of all values in the array is 144, and signFunc(144) = 1 14 | 15 | Input: nums = [1,5,0,2,-3] 16 | Output: 0 17 | Explanation: The product of all values in the array is 0, and signFunc(0) = 0 18 | 19 | Input: nums = [-1,1,-1,1,-1] 20 | Output: -1 21 | Explanation: The product of all values in the array is -1, and signFunc(-1) = -1 22 | 23 | */ 24 | 25 | // O(n) time | O(1) space 26 | const arraySign = (nums) => { 27 | // declare variable to count number of negative numbers 28 | let negativeNums = 0; 29 | for (let num of nums) { 30 | if (num === 0) return 0; 31 | if (num < 0) negativeNums++; 32 | } 33 | return negativeNums % 2 === 0 ? 1 : -1; 34 | }; 35 | 36 | // Approach: Keep track of the sign at each iteration 37 | // O(n) time | O(1) space 38 | const arraySign2 = (nums) => { 39 | let sign = 1; 40 | for (let num of nums) { 41 | if (num === 0) return 0; 42 | if (num < 0) { 43 | sign *= -1; 44 | } 45 | } 46 | return sign; 47 | }; 48 | -------------------------------------------------------------------------------- /Arrays/average-salary.js: -------------------------------------------------------------------------------- 1 | /** 2 | * You are given an array of unique integers salary where salary[i] is the salary of the ith employee. 3 | 4 | Return the average salary of employees excluding the minimum and maximum salary. Answers within 10-5 of the actual answer will be accepted. 5 | 6 | Input: salary = [4000,3000,1000,2000] 7 | Output: 2500.00000 8 | Explanation: Minimum salary and maximum salary are 1000 and 4000 respectively. 9 | Average salary excluding minimum and maximum salary is (2000+3000) / 2 = 2500 10 | 11 | Input: salary = [1000,2000,3000] 12 | Output: 2000.00000 13 | Explanation: Minimum salary and maximum salary are 1000 and 3000 respectively. 14 | Average salary excluding minimum and maximum salary is (2000) / 1 = 2000 15 | */ 16 | 17 | // O(n) time | O(1) space 18 | const average = (salary) => { 19 | // find the min and max 20 | const min = Math.min(...salary); 21 | const max = Math.max(...salary); 22 | let sum = 0; 23 | for (let num of salary) { 24 | if (num === min || num === max) continue; 25 | sum += num; 26 | } 27 | return sum / (salary.length - 2); 28 | }; 29 | -------------------------------------------------------------------------------- /Arrays/best-time-to-buy-stockII.js: -------------------------------------------------------------------------------- 1 | /* 2 | You are given an integer array prices where prices[i] is the 3 | price of a given stock on the ith day. 4 | 5 | On each day, you may decide to buy and/or sell the stock. 6 | You can only hold at most one share of the stock at any time. 7 | However, you can buy it then immediately sell it on the same day. 8 | 9 | Find and return the maximum profit you can achieve. 10 | 11 | Input: prices = [7,1,5,3,6,4] 12 | Output: 7 13 | Explanation: Buy on day 2 (price = 1) and sell on day 3 (price = 5), profit = 5-1 = 4. 14 | Then buy on day 4 (price = 3) and sell on day 5 (price = 6), profit = 6-3 = 3. 15 | Total profit is 4 + 3 = 7. 16 | 17 | Input: prices = [1,2,3,4,5] 18 | Output: 4 19 | Explanation: Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4. 20 | Total profit is 4. 21 | */ 22 | 23 | // Approach: Peak/Valley 24 | // O(n) time | O(1) space 25 | const maxProfit = (prices) => { 26 | // declare profit variable 27 | let profit = 0; 28 | // loop through prices and compare to prior day's price 29 | for (let i = 1; i < prices.length; i++) { 30 | if (prices[i] > prices[i - 1]) { 31 | // if current price is greater than day before, add to profit 32 | profit += prices[i] - prices[i - 1]; 33 | } 34 | } 35 | return profit; 36 | }; 37 | -------------------------------------------------------------------------------- /Arrays/boats-to-save-people.js: -------------------------------------------------------------------------------- 1 | /* 2 | You are given an array people where people[i] is the weight of the ith person, and an infinite number of boats where each boat can carry a maximum weight of limit. Each boat carries at most two people at the same time, provided the sum of the weight of those people is at most limit. 3 | 4 | Return the minimum number of boats to carry every given person. 5 | 6 | Input: people = [1,2], limit = 3 7 | Output: 1 8 | Explanation: 1 boat (1, 2) 9 | 10 | Input: people = [3,2,2,1], limit = 3 11 | Output: 3 12 | Explanation: 3 boats (1, 2), (2) and (3) 13 | 14 | Input: people = [3,5,3,4], limit = 5 15 | Output: 4 16 | Explanation: 4 boats (3), (3), (4), (5) 17 | 18 | */ 19 | 20 | // Approach: Sort and greedy/two pointer approach approach 21 | // O(nlog(n)) time | O(1) space where n is the length of people array 22 | const numRescueBoats = (people, limit) => { 23 | people.sort((a, b) => a - b); 24 | let l = 0; 25 | let r = people.length - 1; 26 | let count = 0; 27 | while (l <= r) { 28 | const remaining = limit - people[r]; 29 | r--; 30 | count++; 31 | if (remaining >= people[l] && l <= r) { 32 | l++; 33 | } 34 | } 35 | return count; 36 | }; 37 | -------------------------------------------------------------------------------- /Arrays/concat-array.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an integer array nums of length n, you want to create an array ans of length 2n where ans[i] == nums[i] and ans[i + n] == nums[i] for 0 <= i < n (0-indexed). 3 | 4 | Specifically, ans is the concatenation of two nums arrays. 5 | 6 | Return the array ans. 7 | 8 | Input: nums = [1,2,1] 9 | Output: [1,2,1,1,2,1] 10 | Explanation: The array ans is formed as follows: 11 | - ans = [nums[0],nums[1],nums[2],nums[0],nums[1],nums[2]] 12 | - ans = [1,2,1,1,2,1] 13 | 14 | Input: nums = [1,3,2,1] 15 | Output: [1,3,2,1,1,3,2,1] 16 | Explanation: The array ans is formed as follows: 17 | - ans = [nums[0],nums[1],nums[2],nums[3],nums[0],nums[1],nums[2],nums[3]] 18 | - ans = [1,3,2,1,1,3,2,1] 19 | */ 20 | 21 | // O(n) time | O(n) space 22 | const getConcatenation = (nums) => { 23 | const output = []; 24 | for (let i = 0; i < 2; i++) { 25 | for (let num of nums) { 26 | output.push(num); 27 | } 28 | } 29 | return output; 30 | }; 31 | 32 | // O(n) time | O(n) space 33 | const getConcatenation2 = (nums) => { 34 | // declare idx and length variable 35 | let i = 0; 36 | const length = nums.length; 37 | while (i < length) { 38 | nums.push(nums[i]); 39 | i++; 40 | } 41 | return nums; 42 | }; 43 | -------------------------------------------------------------------------------- /Arrays/contains-dupilcate.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an integer array nums, return true if any value appears at least twice in the array, 3 | and return false if every element is distinct. 4 | 5 | Input: nums = [1,2,3,1] 6 | Output: true 7 | 8 | Input: nums = [1,2,3,4] 9 | Output: false 10 | */ 11 | 12 | // O(n) time | O(n) space 13 | const containsDupilicate = (nums) => { 14 | const seen = {}; 15 | for (let num of nums) { 16 | if (num in seen) return true; 17 | seen[num] = true; 18 | } 19 | return false; 20 | }; 21 | 22 | // O(nlog(n)) time | O(1) space 23 | const containsDupilcate2 = (nums) => { 24 | nums.sort((a, b) => a - b); 25 | for (let i = 1; i < nums.length; i++) { 26 | if (nums[i] === nums[i - 1]) return true; 27 | } 28 | return false; 29 | }; 30 | 31 | // O(n^2) time | O(1) space 32 | const containsDuplicate3 = (nums) => { 33 | for (let i = 0; i < nums.length - 1; i++) { 34 | for (let j = i + 1; j < nums.length; j++) { 35 | if (nums[i] === nums[j]) return true; 36 | } 37 | } 38 | return false; 39 | }; 40 | -------------------------------------------------------------------------------- /Arrays/contains-duplicateII.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an integer array nums and an integer k, return true if there are two distinct indices i and j in the array such that nums[i] == nums[j] and abs(i - j) <= k. 3 | 4 | Input: nums = [1,2,3,1], k = 3 5 | Output: true 6 | 7 | Input: nums = [1,0,1,1], k = 1 8 | Output: true 9 | 10 | Input: nums = [1,2,3,1,2,3], k = 2 11 | Output: false 12 | 13 | */ 14 | 15 | // O(n) time | O(n) space 16 | const containsNearbyDuplicate = (nums, k) => { 17 | // declare seen hash map that will contain the number and first index seen 18 | const seen = {}; 19 | for (let i = 0; i < nums.length; i++) { 20 | const num = nums[i]; 21 | if (num in seen) { 22 | const firstIdx = seen[num]; 23 | if (i - firstIdx <= k) return true; 24 | } 25 | seen[num] = i; 26 | } 27 | return false; 28 | }; 29 | -------------------------------------------------------------------------------- /Arrays/contiguous-array.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a binary array nums, return the maximum length of a contiguous subarray with an equal number of 0 and 1. 3 | 4 | Input: nums = [0,1] 5 | Output: 2 6 | Explanation: [0, 1] is the longest contiguous subarray with an equal number of 0 and 1. 7 | 8 | Input: nums = [0,1,0] 9 | Output: 2 10 | Explanation: [0, 1] (or [1, 0]) is a longest contiguous subarray with equal number of 0 and 1. 11 | 12 | */ 13 | 14 | // O(n) time | O(n) space 15 | const findMaxLength = (nums) => { 16 | const countMap = {}; 17 | countMap[0] = -1; 18 | let count = 0; 19 | let max = 0; 20 | for (let i = 0; i < nums.length; i++) { 21 | nums[i] ? count++ : count--; 22 | if (count in countMap) { 23 | max = Math.max(max, i - countMap[count]); 24 | } else { 25 | countMap[count] = i; 26 | } 27 | } 28 | return max; 29 | }; 30 | -------------------------------------------------------------------------------- /Arrays/counting-elements.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an integer array arr, count how many elements x there are, such that x + 1 is also in arr. If there are duplicates in arr, count them separately. 3 | 4 | Input: arr = [1,2,3] 5 | Output: 2 6 | Explanation: 1 and 2 are counted cause 2 and 3 are in arr. 7 | 8 | Input: arr = [1,1,3,3,5,5,7,7] 9 | Output: 0 10 | Explanation: No numbers are counted, cause there is no 2, 4, 6, or 8 in arr. 11 | 12 | */ 13 | 14 | // O(n) time | O(n) space 15 | const countElements = (arr) => { 16 | const set = new Set([...arr]); 17 | let count = 0; 18 | for (let num of arr) { 19 | if (set.has(num + 1)) count++; 20 | } 21 | return count; 22 | }; 23 | -------------------------------------------------------------------------------- /Arrays/first-missing-positive.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an unsorted integer array nums, return the smallest missing positive integer. 3 | 4 | You must implement an algorithm that runs in O(n) time and uses constant extra space. 5 | 6 | Input: nums = [1,2,0] 7 | Output: 3 8 | Explanation: The numbers in the range [1,2] are all in the array. 9 | 10 | Input: nums = [3,4,-1,1] 11 | Output: 2 12 | Explanation: 1 is in the array but 2 is missing. 13 | */ 14 | 15 | // O(n) time | O(1) space 16 | const firstMissingPositive = (nums) => { 17 | // convert all negative numbers to zero 18 | for (let i = 0; i < nums.length; i++) { 19 | if (nums[i] < 0) nums[i] = 0; 20 | } 21 | 22 | // traverse array and mark values if it exists in input array 23 | // but only for values in bounds 24 | for (let i = 0; i < nums.length; i++) { 25 | if (Math.abs(nums[i]) > 0) { 26 | const idx = Math.abs(nums[i]) - 1; 27 | if (nums[idx] > 0 && idx < nums.length) { 28 | nums[idx] *= -1; 29 | } else if (nums[idx] === 0) { 30 | nums[idx] = -1 * (nums.length + 1); 31 | } 32 | } 33 | } 34 | // return the index that is not positive 35 | for (let i = 0; i < nums.length; i++) { 36 | if (nums[i] >= 0) return i + 1; 37 | } 38 | // if all numbers are negative in array return length of array + 1 39 | return nums.length + 1; 40 | }; 41 | -------------------------------------------------------------------------------- /Arrays/h-index.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an array of integers citations where citations[i] is the number of citations a researcher received for their ith paper, return the researcher's h-index. 3 | 4 | According to the definition of h-index on Wikipedia: The h-index is defined as the maximum value of h such that the given researcher has published at least h papers that have each been cited at least h times. 5 | 6 | Input: citations = [3,0,6,1,5] 7 | Output: 3 8 | Explanation: [3,0,6,1,5] means the researcher has 5 papers in total and each of them had received 3, 0, 6, 1, 5 citations respectively. 9 | Since the researcher has 3 papers with at least 3 citations each and the remaining two with no more than 3 citations each, their h-index is 3. 10 | 11 | Input: citations = [1,3,1] 12 | Output: 1 13 | */ 14 | 15 | // O(nlog(n)) time | O(1) space 16 | const hIndex = (citations) => { 17 | // length 18 | const n = citations.length; 19 | // sort 20 | citations.sort((a, b) => a - b); 21 | let idx = 0; 22 | while (idx < citations.length && citations[n - 1 - idx] > idx) { 23 | idx++; 24 | } 25 | return idx; 26 | }; 27 | -------------------------------------------------------------------------------- /Arrays/intersection-three-arrays.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given three integer arrays arr1, arr2 and arr3 sorted in strictly increasing order, return a sorted array of only the integers that appeared in all three arrays. 3 | 4 | Input: arr1 = [1,2,3,4,5], arr2 = [1,2,5,7,9], arr3 = [1,3,4,5,8] 5 | Output: [1,5] 6 | Explanation: Only 1 and 5 appeared in the three arrays. 7 | 8 | Input: arr1 = [197,418,523,876,1356], arr2 = [501,880,1593,1710,1870], arr3 = [521,682,1337,1395,1764] 9 | Output: [] 10 | 11 | */ 12 | 13 | // O(n) time | O(1) space 14 | const arraysIntersection = (arr1, arr2, arr3) => { 15 | const output = []; 16 | 17 | let p1 = 0; 18 | let p2 = 0; 19 | let p3 = 0; 20 | 21 | while (p1 < arr1.length && p2 < arr2.length && p3 < arr3.length) { 22 | if (arr1[p1] === arr2[p2] && arr2[p2] === arr3[p3]) { 23 | output.push(arr1[p1]); 24 | p1++; 25 | p2++; 26 | p3++; 27 | } else { 28 | if (arr1[p1] < arr2[p2]) { 29 | p1++; 30 | } else if (arr2[p2] < arr3[p3]) { 31 | p2++; 32 | } else { 33 | p3++; 34 | } 35 | } 36 | } 37 | 38 | return output; 39 | }; 40 | -------------------------------------------------------------------------------- /Arrays/kth-missing-positive.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an array arr of positive integers sorted in a strictly increasing order, and an integer k. 3 | 4 | Return the kth positive integer that is missing from this array. 5 | 6 | Input: arr = [2,3,4,7,11], k = 5 7 | Output: 9 8 | Explanation: The missing positive integers are [1,5,6,8,9,10,12,13,...]. The 5th missing positive integer is 9. 9 | 10 | Input: arr = [1,2,3,4], k = 2 11 | Output: 6 12 | Explanation: The missing positive integers are [5,6,7,...]. The 2nd missing positive integer is 6. 13 | 14 | */ 15 | 16 | const findKthPositive = (arr, k) => {}; 17 | -------------------------------------------------------------------------------- /Arrays/longest-subarray-ones.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a binary array nums, you should delete one element from it. 3 | 4 | Return the size of the longest non-empty subarray containing only 1's in the resulting array. Return 0 if there is no such subarray. 5 | 6 | Input: nums = [1,1,0,1] 7 | Output: 3 8 | Explanation: After deleting the number in position 2, [1,1,1] contains 3 numbers with value of 1's. 9 | 10 | Input: nums = [0,1,1,1,0,1,1,0,1] 11 | Output: 5 12 | Explanation: After deleting the number in position 4, [0,1,1,1,1,1,0,1] longest subarray with value of 1's is [1,1,1,1,1]. 13 | 14 | Input: nums = [1,1,1] 15 | Output: 2 16 | Explanation: You must delete one element. 17 | 18 | */ 19 | 20 | // Approach: Sliding Window 21 | // O(n) time | O(1) space 22 | const longestSubarray = (nums) => { 23 | let max = 0; 24 | let ones = 0; 25 | let l = 0; 26 | for (let r = 0; r < nums.length; r++) { 27 | ones += nums[r]; 28 | 29 | while (r - l + 1 > ones + 1) { 30 | ones -= nums[l++]; 31 | } 32 | 33 | max = Math.max(max, r - l); 34 | } 35 | return max; 36 | }; 37 | -------------------------------------------------------------------------------- /Arrays/max-consecutive-ones.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a binary array nums, return the maximum number of consecutive 1's in the array. 3 | 4 | Input: nums = [1,1,0,1,1,1] 5 | Output: 3 6 | Explanation: The first two digits or the last three digits are consecutive 1s. The maximum number of consecutive 1s is 3. 7 | 8 | Input: nums = [1,0,1,1,0,1] 9 | Output: 2 10 | 11 | */ 12 | 13 | // Approach: Sliding Window 14 | // O(n) time | O(1) space 15 | const findMaxConsecutiveOnes = (nums) => { 16 | let numOnes = 0; 17 | let l = 0; 18 | let maxOnes = 0; 19 | for (let r = 0; r < nums.length; r++) { 20 | numOnes += nums[r]; 21 | 22 | while (r - l + 1 > numOnes) { 23 | numOnes -= nums[l++]; 24 | } 25 | 26 | maxOnes = Math.max(maxOnes, r - l + 1); 27 | } 28 | return maxOnes; 29 | }; 30 | -------------------------------------------------------------------------------- /Arrays/max-consecutive-onesII.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a binary array nums, return the maximum number of consecutive 1's in the array if you can flip at most one 0. 3 | 4 | Input: nums = [1,0,1,1,0] 5 | Output: 4 6 | Explanation: 7 | - If we flip the first zero, nums becomes [1,1,1,1,0] and we have 4 consecutive ones. 8 | - If we flip the second zero, nums becomes [1,0,1,1,1] and we have 3 consecutive ones. 9 | The max number of consecutive ones is 4. 10 | 11 | Input: nums = [1,0,1,1,0,1] 12 | Output: 4 13 | Explanation: 14 | - If we flip the first zero, nums becomes [1,1,1,1,0,1] and we have 4 consecutive ones. 15 | - If we flip the second zero, nums becomes [1,0,1,1,1,1] and we have 4 consecutive ones. 16 | The max number of consecutive ones is 4. 17 | 18 | */ 19 | 20 | // Approach: Sliding Window 21 | // O(n) time | O(1) space 22 | const findMaxConsecutiveOnes = (nums) => { 23 | let numOnes = 0; 24 | let maxOnes = 0; 25 | let l = 0; 26 | for (let r = 0; r < nums.length; r++) { 27 | numOnes += nums[r]; 28 | // condition where window is not valid => length of window is larger than numOnes + k 29 | while (r - l + 1 > numOnes + 1) { 30 | numOnes -= nums[l++]; 31 | } 32 | 33 | maxOnes = Math.max(maxOnes, r - l + 1); 34 | } 35 | return maxOnes; 36 | }; 37 | -------------------------------------------------------------------------------- /Arrays/max-consecutive-onesIII.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a binary array nums and an integer k, return the maximum number of consecutive 1's in the array if you can flip at most k 0's. 3 | 4 | Input: nums = [1,1,1,0,0,0,1,1,1,1,0], k = 2 5 | Output: 6 6 | Explanation: [1,1,1,0,0,1,1,1,1,1,1] 7 | Bolded numbers were flipped from 0 to 1. The longest subarray is underlined. 8 | 9 | Input: nums = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], k = 3 10 | Output: 10 11 | Explanation: [0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1] 12 | Bolded numbers were flipped from 0 to 1. The longest subarray is underlined. 13 | 14 | */ 15 | 16 | // Approach: Sliding Window 17 | // O(n) time | O(1) space 18 | const longestOnes = (nums, k) => { 19 | let numOnes = 0; 20 | let maxOnes = 0; 21 | let l = 0; 22 | for (let r = 0; r < nums.length; r++) { 23 | numOnes += nums[r]; 24 | // condition where window is not valid => length of window is larger than numOnes + k 25 | while (r - l + 1 > numOnes + k) { 26 | numOnes -= nums[l++]; 27 | } 28 | 29 | maxOnes = Math.max(maxOnes, r - l + 1); 30 | } 31 | return maxOnes; 32 | }; 33 | -------------------------------------------------------------------------------- /Arrays/max-value.js: -------------------------------------------------------------------------------- 1 | /* 2 | Write a function, maxValue, that takes in array of numbers as an argument. 3 | The function should return the largest number in the array. 4 | 5 | Solve this without using any built-in array methods. 6 | 7 | You can assume that the array is non-empty. 8 | 9 | maxValue([4, 7, 2, 8, 10, 9]); // -> 10 10 | */ 11 | 12 | // O(n) time | O(1) space 13 | const maxValue = (nums) => { 14 | let max = Infinity; // declare a max variable 15 | 16 | for (let num of nums) { 17 | max = Math.max(num, max); // update max 18 | } 19 | return max; 20 | }; 21 | -------------------------------------------------------------------------------- /Arrays/maximum-product-subarray.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Given an integer array nums, find a 4 | subarray 5 | that has the largest product, and return the product. 6 | 7 | The test cases are generated so that the answer will fit in a 32-bit integer. 8 | 9 | Input: nums = [2,3,-2,4] 10 | Output: 6 11 | Explanation: [2,3] has the largest product 6. 12 | 13 | Input: nums = [-2,0,-1] 14 | Output: 0 15 | Explanation: The result cannot be 2, because [-2,-1] is not a subarray. 16 | 17 | */ 18 | 19 | // O(n) time | O(1) space 20 | const maxProduct = (nums) => { 21 | // delcare current min, current max variables and max variable 22 | let currentMin = 1; 23 | let currentMax = 1; 24 | let max = -Infinity; 25 | // loop through array 26 | for (let num of nums) { 27 | // grab old min to make sure you dont lose reference 28 | const min = currentMin; 29 | // update min and max at each point 30 | currentMin = Math.min(num * currentMin, num * currentMax, num); 31 | currentMax = Math.max(num * min, num * currentMax, num); 32 | max = Math.max(max, currentMin, currentMax); 33 | } 34 | 35 | return max; 36 | }; 37 | -------------------------------------------------------------------------------- /Arrays/maximum-score-good-subarray.js: -------------------------------------------------------------------------------- 1 | /** 2 | * You are given an array of integers nums (0-indexed) and an integer k. 3 | 4 | The score of a subarray (i, j) is defined as min(nums[i], nums[i+1], ..., nums[j]) * (j - i + 1). A good subarray is a subarray where i <= k <= j. 5 | 6 | Return the maximum possible score of a good subarray. 7 | 8 | Input: nums = [1,4,3,7,4,5], k = 3 9 | Output: 15 10 | Explanation: The optimal subarray is (1, 5) with a score of min(4,3,7,4,5) * (5-1+1) = 3 * 5 = 15. 11 | 12 | Input: nums = [5,5,4,5,4,1,1,1], k = 0 13 | Output: 20 14 | Explanation: The optimal subarray is (0, 4) with a score of min(5,5,4,5,4) * (4-0+1) = 4 * 5 = 20. 15 | */ 16 | 17 | // O(n) time | O(1) space 18 | const maximumScore = (nums, k) => { 19 | // declare max, min, left and right variables 20 | let max = nums[k]; 21 | let min = nums[k]; 22 | let l = k; 23 | let r = k; 24 | while (l >= 0 && r < nums.length) { 25 | // calculate max 26 | max = Math.max(max, (r - l + 1) * min); 27 | min = 28 | r === nums.length - 1 || nums[l - 1] > nums[r + 1] 29 | ? Math.min(nums[--l], min) 30 | : Math.min(nums[++r], min); 31 | } 32 | return max; 33 | }; 34 | -------------------------------------------------------------------------------- /Arrays/min-consecutive-cards.js: -------------------------------------------------------------------------------- 1 | /* 2 | You are given an integer array cards where cards[i] represents the value of the ith card. A pair of cards are matching if the cards have the same value. 3 | 4 | Return the minimum number of consecutive cards you have to pick up to have a pair of matching cards among the picked cards. If it is impossible to have matching cards, return -1. 5 | 6 | Input: cards = [3,4,2,3,4,7] 7 | Output: 4 8 | Explanation: We can pick up the cards [3,4,2,3] which contain a matching pair of cards with value 3. Note that picking up the cards [4,2,3,4] is also optimal. 9 | 10 | Input: cards = [1,0,5,3] 11 | Output: -1 12 | Explanation: There is no way to pick up a set of consecutive cards that contain a pair of matching cards. 13 | */ 14 | 15 | // O(n) time | O(n) space 16 | const minimumCardPickup = (cards) => { 17 | // declare map 18 | const map = {}; 19 | // declare min 20 | let min = Infinity; 21 | for (let i = 0; i < cards.length; i++) { 22 | if (cards[i] in map) { 23 | // if card in map then do min logic with currentIdx and idx of last seen 24 | min = Math.min(i - map[cards[i]] + 1, min); 25 | } 26 | // update map 27 | map[cards[i]] = i; 28 | } 29 | return min === Infinity ? -1 : min; 30 | }; 31 | -------------------------------------------------------------------------------- /Arrays/min-num-operations-equals.js: -------------------------------------------------------------------------------- 1 | /* 2 | You are given an array nums consisting of positive integers. 3 | 4 | You are also given an integer array queries of size m. For the ith query, you want to make all of the elements of nums equal to queries[i]. You can perform the following operation on the array any number of times: 5 | 6 | Increase or decrease an element of the array by 1. 7 | Return an array answer of size m where answer[i] is the minimum number of operations to make all elements of nums equal to queries[i]. 8 | 9 | Note that after each query the array is reset to its original state. 10 | 11 | */ 12 | 13 | // O(n * m) time | O(m) space where n is length of nums and m is length of queries 14 | // This times out on leetcode. Use prefix sums and binary search to cut down time complexity 15 | const minOperations2 = (nums, queries) => { 16 | let ans = []; 17 | for (let query of queries) { 18 | let sum = 0; 19 | for (let num of nums) { 20 | sum += Math.abs(query - num); 21 | } 22 | ans.push(sum); 23 | } 24 | return ans; 25 | }; 26 | -------------------------------------------------------------------------------- /Arrays/min-operations-array-incresing.js: -------------------------------------------------------------------------------- 1 | /* 2 | You are given an integer array nums (0-indexed). In one operation, you can choose an element of the array and increment it by 1. 3 | 4 | For example, if nums = [1,2,3], you can choose to increment nums[1] to make nums = [1,3,3]. 5 | Return the minimum number of operations needed to make nums strictly increasing. 6 | 7 | An array nums is strictly increasing if nums[i] < nums[i+1] for all 0 <= i < nums.length - 1. An array of length 1 is trivially strictly increasing. 8 | 9 | Input: nums = [1,1,1] 10 | Output: 3 11 | Explanation: You can do the following operations: 12 | 1) Increment nums[2], so nums becomes [1,1,2]. 13 | 2) Increment nums[1], so nums becomes [1,2,2]. 14 | 3) Increment nums[2], so nums becomes [1,2,3]. 15 | 16 | Input: nums = [1,5,2,4,1] 17 | Output: 14 18 | 19 | Input: nums = [8] 20 | Output: 0 21 | */ 22 | 23 | // Approach: Greedy 24 | // O(n) time | O(1) space 25 | const minOperations = (nums) => { 26 | let count = 0; 27 | for (let i = 1; i < nums.length; i++) { 28 | if (nums[i] <= nums[i - 1]) { 29 | const diff = nums[i - 1] - nums[i] + 1; 30 | count += diff; 31 | nums[i] += diff; 32 | } 33 | } 34 | return count; 35 | }; 36 | -------------------------------------------------------------------------------- /Arrays/min-size-subarray-sum.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an array of positive integers nums and a positive integer target, return the minimal length of a 3 | subarray 4 | whose sum is greater than or equal to target. If there is no such subarray, return 0 instead. 5 | 6 | Input: target = 7, nums = [2,3,1,2,4,3] 7 | Output: 2 8 | Explanation: The subarray [4,3] has the minimal length under the problem constraint. 9 | 10 | Input: target = 4, nums = [1,4,4] 11 | Output: 1 12 | 13 | Input: target = 11, nums = [1,1,1,1,1,1,1,1] 14 | Output: 0 15 | 16 | */ 17 | 18 | // O(n) time | O(1) space 19 | const minSubArrayLen = (target, nums) => { 20 | let minLength = Infinity; 21 | let sum = 0; 22 | let l = 0; 23 | for (let r = 0; r < nums.length; r++) { 24 | sum += nums[r]; 25 | 26 | while (sum >= target) { 27 | minLength = Math.min(minLength, r - l + 1); 28 | sum -= nums[l]; 29 | l++; 30 | } 31 | } 32 | 33 | return minLength === Infinity ? 0 : minLength; 34 | }; 35 | -------------------------------------------------------------------------------- /Arrays/min-swaps-ones.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a binary array data, return the minimum number of swaps required to group all 1’s present in the array together in any place in the array. 3 | 4 | Input: data = [1,0,1,0,1] 5 | Output: 1 6 | Explanation: There are 3 ways to group all 1's together: 7 | [1,1,1,0,0] using 1 swap. 8 | [0,1,1,1,0] using 2 swaps. 9 | [0,0,1,1,1] using 1 swap. 10 | The minimum is 1. 11 | 12 | Input: data = [0,0,0,1,0] 13 | Output: 0 14 | Explanation: Since there is only one 1 in the array, no swaps are needed. 15 | 16 | */ 17 | 18 | // Approach: Sliding Window 19 | // O(n) time | O(1) space 20 | const minSwaps = (data) => { 21 | let numOnes = 0; 22 | for (let num of data) { 23 | if (num === 1) numOnes++; 24 | } 25 | if (numOnes === 1) return 0; 26 | let maxOnesInWindow = 0; 27 | for (let i = 0; i < numOnes; i++) { 28 | if (data[i] === 1) maxOnesInWindow++; 29 | } 30 | 31 | let i = 1; 32 | let j = numOnes; 33 | let currentOnes = maxOnesInWindow; 34 | while (j < data.length) { 35 | if (data[j] === 1) currentOnes++; 36 | if (data[i - 1] === 1) currentOnes--; 37 | maxOnesInWindow = Math.max(maxOnesInWindow, currentOnes); 38 | 39 | j++; 40 | i++; 41 | } 42 | return numOnes - maxOnesInWindow; 43 | }; 44 | -------------------------------------------------------------------------------- /Arrays/min-val-positive-step.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an array of integers nums, you start with an initial positive value startValue. 3 | 4 | In each iteration, you calculate the step by step sum of startValue plus elements in nums (from left to right). 5 | 6 | Return the minimum positive value of startValue such that the step by step sum is never less than 1. 7 | 8 | Input: nums = [-3,2,-3,4,2] 9 | Output: 5 10 | Explanation: If you choose startValue = 4, in the third iteration your step by step sum is less than 1. 11 | step by step sum 12 | startValue = 4 | startValue = 5 | nums 13 | (4 -3 ) = 1 | (5 -3 ) = 2 | -3 14 | (1 +2 ) = 3 | (2 +2 ) = 4 | 2 15 | (3 -3 ) = 0 | (4 -3 ) = 1 | -3 16 | (0 +4 ) = 4 | (1 +4 ) = 5 | 4 17 | (4 +2 ) = 6 | (5 +2 ) = 7 | 2 18 | 19 | Input: nums = [1,2] 20 | Output: 1 21 | Explanation: Minimum start value should be positive. 22 | 23 | Input: nums = [1,-2,-3] 24 | Output: 5 25 | 26 | */ 27 | 28 | // O(n) time | O(1) space 29 | const minStartValue = (nums) => { 30 | let min = 0; 31 | let sum = 0; 32 | 33 | for (let num of nums) { 34 | sum += num; 35 | min = Math.min(min, sum); 36 | } 37 | 38 | return 1 - min; 39 | }; 40 | -------------------------------------------------------------------------------- /Arrays/minimize-max-of-array.js: -------------------------------------------------------------------------------- 1 | /* 2 | You are given a 0-indexed array nums comprising of n non-negative integers. 3 | 4 | In one operation, you must: 5 | 6 | Choose an integer i such that 1 <= i < n and nums[i] > 0. 7 | Decrease nums[i] by 1. 8 | Increase nums[i - 1] by 1. 9 | Return the minimum possible value of the maximum integer of nums after performing any number of operations. 10 | 11 | Input: nums = [3,7,1,6] 12 | Output: 5 13 | Explanation: 14 | One set of optimal operations is as follows: 15 | 1. Choose i = 1, and nums becomes [4,6,1,6]. 16 | 2. Choose i = 3, and nums becomes [4,6,2,5]. 17 | 3. Choose i = 1, and nums becomes [5,5,2,5]. 18 | The maximum integer of nums is 5. It can be shown that the maximum number cannot be less than 5. 19 | Therefore, we return 5. 20 | 21 | Input: nums = [10,1] 22 | Output: 10 23 | Explanation: 24 | It is optimal to leave nums as is, and since 10 is the maximum value, we return 10. 25 | 26 | */ 27 | 28 | // O(n) time | O(1) space 29 | const minimizeArrayValue = (nums) => { 30 | // the min max value cant be lower than the first element 31 | let min = nums[0]; 32 | let sum = nums[0]; 33 | 34 | for (let i = 1; i < nums.length; i++) { 35 | // add num to sum 36 | sum += nums[i]; 37 | min = Math.max(min, Math.ceil(sum / (i + 1))); 38 | } 39 | 40 | return min; 41 | }; 42 | -------------------------------------------------------------------------------- /Arrays/missing-ranges.js: -------------------------------------------------------------------------------- 1 | /* 2 | You are given an inclusive range [lower, upper] and a sorted unique integer array nums, where all elements are within the inclusive range. 3 | 4 | A number x is considered missing if x is in the range [lower, upper] and x is not in nums. 5 | 6 | Return the shortest sorted list of ranges that exactly covers all the missing numbers. That is, no element of nums is included in any of the ranges, and each missing number is covered by one of the ranges. 7 | 8 | 9 | */ 10 | 11 | // O(n) time | O(n) space 12 | const findMissingRanges = (nums, lower, upper) => { 13 | const n = nums.length; 14 | if (n === 0) return [[lower, upper]]; 15 | const output = []; 16 | // check for any missing numbers between the lower bound and nums 17 | if (lower < nums[0]) { 18 | output.push([lower, nums[0] - 1]); 19 | } 20 | 21 | for (let i = 1; i < n; i++) { 22 | if (nums[i] - nums[i - 1] > 1) { 23 | output.push([nums[i - 1] + 1, nums[i] - 1]); 24 | } 25 | } 26 | 27 | // check for missing numbers between last element of nums and upper bound 28 | if (upper > nums[n - 1]) { 29 | output.push([nums[n - 1] + 1, upper]); 30 | } 31 | 32 | return output; 33 | }; 34 | -------------------------------------------------------------------------------- /Arrays/move-zeroes.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an integer array nums, move all 0's to the end of it while maintaining the relative order of the non-zero elements. 3 | 4 | Note that you must do this in-place without making a copy of the array. 5 | 6 | Input: nums = [0,1,0,3,12] 7 | Output: [1,3,12,0,0] 8 | 9 | Input: nums = [0] 10 | Output: [0] 11 | */ 12 | 13 | // Approach: Two pointers and swapping 14 | // O(n) time | O(1) space 15 | const moveZeroes = (nums) => { 16 | let i = 0; 17 | let j = 1; 18 | while (j < nums.length) { 19 | if (i === j) { 20 | j++; 21 | } 22 | if (nums[j] === 0) { 23 | j++; 24 | } else if (nums[i] !== 0) { 25 | i++; 26 | } else { 27 | [nums[i], nums[j]] = [nums[j], nums[i]]; 28 | i++; 29 | j++; 30 | } 31 | } 32 | return nums; 33 | }; 34 | -------------------------------------------------------------------------------- /Arrays/non-constructible-change.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an array of positive integers representing the values of coins in your possession, write a function that returns the minimum amount of change (the minimum sum of money) that you cannot create. The given coins can have any positive ineger value and arent necessarily unique (i.e., you can have multiple coins of the same value) 3 | 4 | For example, if you're given coins = [1, 2, 5], the minimum amount of change that you cant create is 4. If you're given no coins, the minimum amount of change that you cant create is 1 5 | */ 6 | 7 | // O(nlog(n)) time | O(1) space 8 | const nonConstructibleChange = (coins) => { 9 | // sort coins array 10 | coins.sort((a, b) => a - b); 11 | // declare min change variable 12 | let minChange = 1; 13 | for (let coin of coins) { 14 | // if current coin greater than minChange, return minChange 15 | if (coin > minChange) return minChange; 16 | // otherwise add coin value to minChange 17 | minChange += coin; 18 | } 19 | // return minChange 20 | return minChange; 21 | }; 22 | -------------------------------------------------------------------------------- /Arrays/plus-one.js: -------------------------------------------------------------------------------- 1 | /* 2 | You are given a large integer represented as an integer array digits, 3 | where each digits[i] is the ith digit of the integer. 4 | The digits are ordered from most significant to least significant in left-to-right order. 5 | The large integer does not contain any leading 0's. 6 | 7 | Increment the large integer by one and return the resulting array of digits. 8 | 9 | Input: digits = [4,3,2,1] 10 | Output: [4,3,2,2] 11 | Explanation: The array represents the integer 4321. 12 | Incrementing by one gives 4321 + 1 = 4322. 13 | Thus, the result should be [4,3,2,2]. 14 | 15 | Input: digits = [9] 16 | Output: [1,0] 17 | Explanation: The array represents the integer 9. 18 | Incrementing by one gives 9 + 1 = 10. 19 | Thus, the result should be [1,0]. 20 | */ 21 | 22 | // O(n) time | O(n) space 23 | const plusOne = (digits) => { 24 | const output = []; 25 | let carry = 0; 26 | for (let i = digits.length - 1; i >= 0; i--) { 27 | if (i === digits.length - 1) { 28 | const num = 1 + digits[i]; 29 | const digit = num % 10; 30 | carry = num >= 10 ? 1 : 0; 31 | output.push(digit); 32 | } else { 33 | const num = digits[i] + carry; 34 | const digit = num % 10; 35 | carry = num >= 10 ? 1 : 0; 36 | output.push(digit); 37 | } 38 | } 39 | if (carry > 0) { 40 | output.push(carry); 41 | } 42 | return output.reverse(); 43 | }; 44 | -------------------------------------------------------------------------------- /Arrays/product-array-except-self.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an integer array nums, return an array answer such that answer[i] is equal to the product of all the elements of nums except nums[i]. 3 | 4 | The product of any prefix or suffix of nums is guaranteed to fit in a 32-bit integer. 5 | 6 | You must write an algorithm that runs in O(n) time and without using the division operation. 7 | 8 | Input: nums = [1,2,3,4] 9 | Output: [24,12,8,6] 10 | 11 | Input: nums = [-1,1,0,-3,3] 12 | Output: [0,0,9,0,0] 13 | 14 | [1, 1, 2, 6] // left 15 | [24, 12, 4, 1] // right 16 | [24, 12, 8, 6] 17 | */ 18 | 19 | // Prefix Products 20 | // O(n) time | O(n) space 21 | const productExceptSelf = (nums) => { 22 | const output = new Array(nums.length).fill(1); 23 | let runningProduct = 1; 24 | for (let i = 0; i < nums.length; i++) { 25 | output[i] = runningProduct; 26 | runningProduct *= nums[i]; 27 | } 28 | runningProduct = 1; 29 | for (let i = nums.length - 1; i >= 0; i--) { 30 | output[i] *= runningProduct; 31 | runningProduct *= nums[i]; 32 | } 33 | return output; 34 | }; 35 | -------------------------------------------------------------------------------- /Arrays/queue.js: -------------------------------------------------------------------------------- 1 | /* Implement a Queue using a linked list */ 2 | 3 | class ListNode { 4 | constructor(val) { 5 | this.val = val; 6 | this.next = null; 7 | } 8 | } 9 | 10 | class Queue { 11 | // Implementing this with dummy nodes would be easier! 12 | constructor() { 13 | this.left = null; 14 | this.right = null; 15 | } 16 | 17 | enqueue(val) { 18 | const newNode = new ListNode(val); 19 | if (this.right != null) { 20 | // Queue is not empty 21 | this.right.next = newNode; 22 | this.right = this.right.next; 23 | } else { 24 | // Queue is empty 25 | this.left = newNode; 26 | this.right = newNode; 27 | } 28 | } 29 | 30 | dequeue() { 31 | if (this.left == null) { 32 | // Queue is empty 33 | return; 34 | } 35 | // Remove left node and return value 36 | const val = this.left.val; 37 | this.left = this.left.next; 38 | if (!this.left) { 39 | this.right = null; 40 | } 41 | return val; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Arrays/shortest-word-distance.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an array of strings wordsDict and two different strings that already exist in the array word1 and word2, return the shortest distance between these two words in the list. 3 | 4 | Input: wordsDict = ["practice", "makes", "perfect", "coding", "makes"], word1 = "coding", word2 = "practice" 5 | Output: 3 6 | 7 | Input: wordsDict = ["practice", "makes", "perfect", "coding", "makes"], word1 = "makes", word2 = "coding" 8 | Output: 1 9 | 10 | */ 11 | 12 | // O(n) time | O(1) space 13 | const shortestDistance = (wordsDict, word1, word2) => { 14 | let oneIdx = -1; 15 | let twoIdx = -1; 16 | let res = Infinity; 17 | 18 | for (let i = 0; i < wordsDict.length; i++) { 19 | if (wordsDict[i] === word1) oneIdx = i; 20 | if (wordsDict[i] === word2) twoIdx = i; 21 | if (oneIdx !== -1 && twoIdx !== -1) { 22 | res = Math.min(res, Math.abs(oneIdx - twoIdx)); 23 | } 24 | } 25 | return res; 26 | }; 27 | -------------------------------------------------------------------------------- /Arrays/shuffle-an-array.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the array nums consisting of 2n elements in the form [x1,x2,...,xn,y1,y2,...,yn]. 3 | 4 | Return the array in the form [x1,y1,x2,y2,...,xn,yn]. 5 | 6 | Input: nums = [2,5,1,3,4,7], n = 3 7 | Output: [2,3,5,4,1,7] 8 | Explanation: Since x1=2, x2=5, x3=1, y1=3, y2=4, y3=7 then the answer is [2,3,5,4,1,7]. 9 | 10 | Input: nums = [1,2,3,4,4,3,2,1], n = 4 11 | Output: [1,4,2,3,3,2,4,1] 12 | 13 | Input: nums = [1,1,2,2], n = 2 14 | Output: [1,2,1,2] 15 | 16 | 17 | */ 18 | 19 | // O(n) time | O(n) space 20 | const shuffle = (nums, n) => { 21 | const output = []; 22 | let l = 0; 23 | let r = n; 24 | while (r < nums.length) { 25 | output.push(nums[l++], nums[r++]); 26 | } 27 | return output; 28 | }; 29 | -------------------------------------------------------------------------------- /Arrays/single-number.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a non-empty array of integers nums, every element appears twice except for one. 3 | Find that single one. 4 | 5 | You must implement a solution with a linear runtime complexity and use only 6 | constant extra space. 7 | 8 | Input: nums = [2,2,1] 9 | Output: 1 10 | 11 | Input: nums = [4,1,2,1,2] 12 | Output: 4 13 | 14 | */ 15 | 16 | // Approach: Use bitwise manipulation. XOR 17 | // When you use XOR with same number it gives you 0 and when you use XOR with 18 | // zero, it gives you same number. So after XORing all of the numbers whatever 19 | // only appears once it the single number 20 | // O(n) time | O(1) space 21 | const singleNumber = (nums) => { 22 | // declare answer variable and set to 0 23 | let ans = 0; 24 | for (let num of nums) { 25 | ans = num ^ ans; 26 | } 27 | return ans; 28 | }; 29 | 30 | console.log(singleNumber([2, 2, 1, 1, 3, 3, 10])); 31 | 32 | // O(n) time | O(n) space 33 | const singleNumber2 = (nums) => { 34 | const map = {}; 35 | for (let num of nums) { 36 | map[num] = (map[num] || 0) + 1; 37 | } 38 | for (let key in map) { 39 | if (map[key] === 1) return key; 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /Arrays/smallest-difference.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Write a function that takes in two non-empty arrays of integers, finds the pair of numbers (one from each array) whose absolute difference is closest to zero, and returns an array containing these two numbers, with the number from the first array in the first position 4 | 5 | Note that the absolute difference of two integers is the distance between them on the real number line. 6 | 7 | You can assume that there will only be one pair of numbers with the smallest difference 8 | 9 | const arrayOne = [-1, 5, 10, 20, 28, 3] 10 | const arrayTwo = [26, 134, 135, 15, 17] 11 | output = [28, 26] 12 | 13 | */ 14 | 15 | // O(nlog(n) + mlog(m)) time | O(1) space where n is length of arrayOne and m is lenght of arrayTwo 16 | const smallestDifference = (arrayOne, arrayTwo) => { 17 | arrayOne.sort((a, b) => a - b); 18 | arrayTwo.sort((a, b) => a - b); 19 | let min = Infinity; 20 | let output = []; 21 | let i = 0; 22 | let j = 0; 23 | 24 | while (i < arrayOne.length && j < arrayTwo.length) { 25 | const numOne = arrayOne[i]; 26 | const numTwo = arrayTwo[j]; 27 | const difference = Math.abs(numOne - numTwo); 28 | if (difference < min) { 29 | min = difference; 30 | output = [numOne, numTwo]; 31 | } 32 | if (numOne < numTwo) { 33 | i++; 34 | } else { 35 | j++; 36 | } 37 | } 38 | return output; 39 | }; 40 | -------------------------------------------------------------------------------- /Arrays/subarray-sum-k.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Given an array of integers nums and an integer k, return the total number of subarrays whose sum equals to k. 4 | 5 | A subarray is a contiguous non-empty sequence of elements within an array. 6 | 7 | Input: nums = [1,1,1], k = 2 8 | Output: 2 9 | 10 | Input: nums = [1,2,3], k = 3 11 | Output: 2 12 | 13 | */ 14 | 15 | // O(n) time | O(n) space 16 | const subarraySum = (nums, k) => { 17 | // declare a map to store prefix sums, currentSum variable and total count variable 18 | const prefixSums = { 0: 1 }; // we have to initialize 0 to 1 19 | let currentSum = 0; 20 | let total = 0; 21 | 22 | for (let i = 0; i < nums.length; i++) { 23 | // update currentSum 24 | currentSum += nums[i]; 25 | // check to see if currentSum - k is in map. If it is then that means you found a subarray that sums to k 26 | const difference = currentSum - k; 27 | if (difference in prefixSums) { 28 | total += prefixSums[difference]; 29 | } 30 | // update currentSum in prefix Sums 31 | prefixSums[currentSum] = prefixSums[currentSum] + 1 || 1; 32 | } 33 | 34 | return total; 35 | }; 36 | -------------------------------------------------------------------------------- /Arrays/summary-ranges.js: -------------------------------------------------------------------------------- 1 | /* 2 | You are given a sorted unique integer array nums. 3 | 4 | A range [a,b] is the set of all integers from a to b (inclusive). 5 | 6 | Return the smallest sorted list of ranges that cover all the numbers in the array exactly. That is, each element of nums is covered by exactly one of the ranges, and there is no integer x such that x is in one of the ranges but not in nums. 7 | 8 | Each range [a,b] in the list should be output as: 9 | 10 | "a->b" if a != b 11 | "a" if a == b 12 | 13 | Input: nums = [0,1,2,4,5,7] 14 | Output: ["0->2","4->5","7"] 15 | Explanation: The ranges are: 16 | [0,2] --> "0->2" 17 | [4,5] --> "4->5" 18 | [7,7] --> "7" 19 | 20 | Input: nums = [0,2,3,4,6,8,9] 21 | Output: ["0","2->4","6","8->9"] 22 | Explanation: The ranges are: 23 | [0,0] --> "0" 24 | [2,4] --> "2->4" 25 | [6,6] --> "6" 26 | [8,9] --> "8->9" 27 | 28 | */ 29 | 30 | // O(n) time | O(1) space 31 | const summaryRanges = (nums) => { 32 | if (nums.length === 0) return []; 33 | const output = []; 34 | let i = 0; 35 | while (i < nums.length) { 36 | let start = nums[i]; 37 | while (i + 1 < nums.length && nums[i] + 1 === nums[i + 1]) { 38 | i++; 39 | } 40 | if (start !== nums[i]) { 41 | output.push(start + '->' + nums[i]); 42 | } else { 43 | output.push(String(start)); 44 | } 45 | i++; 46 | } 47 | 48 | return output; 49 | }; 50 | -------------------------------------------------------------------------------- /Arrays/trapping-rain-water.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it can trap after raining. 3 | 4 | Input: height = [0,1,0,2,1,0,1,3,2,1,2,1] 5 | Output: 6 6 | Explanation: The above elevation map (black section) is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped. 7 | 8 | Input: height = [4,2,0,3,2,5] 9 | Output: 9 10 | */ 11 | 12 | // O(n) time | O(1) space 13 | const trap = (height) => { 14 | // base case if height length <= 1 return 0 15 | if (height.length <= 1) return 0; 16 | // declare pointers 17 | let l = 0; 18 | let r = height.length - 1; 19 | let leftMax = height[l]; 20 | let rightMax = height[r]; 21 | let ans = 0; 22 | 23 | while (l < r) { 24 | // if leftMax <= rightMax, then we can update our answer 25 | if (leftMax <= rightMax) { 26 | l++; 27 | ans += Math.max(leftMax - height[l], 0); 28 | leftMax = Math.max(leftMax, height[l]); 29 | } else { 30 | r--; 31 | ans += Math.max(rightMax - height[r], 0); 32 | rightMax = Math.max(rightMax, height[r]); 33 | } 34 | } 35 | 36 | return ans; 37 | }; 38 | -------------------------------------------------------------------------------- /Arrays/two-sum.js: -------------------------------------------------------------------------------- 1 | /* 2 | Write a function, pairSum, that takes in an array and a target sum as arguments. 3 | The function should return an array containing a pair of indices whose elements sum to the given target. 4 | The indices returned must be unique. 5 | 6 | Be sure to return the indices, not the elements themselves. 7 | 8 | There is guaranteed to be one such pair that sums to the target. 9 | 10 | pairSum([3, 2, 5, 4, 1], 8); // -> [0, 2] 11 | pairSum([4, 7, 9, 2, 5, 1], 5); // -> [0, 5] 12 | pairSum([1, 6, 7, 2], 13); // -> [1, 2] 13 | */ 14 | 15 | 16 | // O(n) time | O(n) space where n is the length of the array 17 | const pairSum = (array, target) => { 18 | // declare a numbers map that will track what numbers we've visted and at what indexes 19 | const numbers = {}; 20 | // loop through array 21 | for (let i = 0; i < array.length; i++) { 22 | const complement = target - array[i]; 23 | // check if complement exists in map 24 | if (complement in numbers) return [numbers[complement], i]; 25 | numbers[array[i]] = i; 26 | } 27 | }; 28 | 29 | // O(n^2) time | O(1) space where n is the length of the array 30 | const pairSum2 = (array, target) => { 31 | for (let i = 0; i < array.length - 1; i++) { 32 | for (let j = i + 1; j < array.length; j++) { 33 | if (array[i] + array[j] === target) return [i, j]; 34 | } 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /Arrays/unique-num-occurrences.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given an array of integers arr, return true if the number of occurrences of each value in the array is unique or false otherwise. 3 | * 4 | * Input: arr = [1,2,2,1,1,3] 5 | Output: true 6 | Explanation: The value 1 has 3 occurrences, 2 has 2 and 3 has 1. No two values have the same number of occurrences. 7 | 8 | Input: arr = [1,2] 9 | Output: false 10 | 11 | Input: arr = [-3,0,1,-3,1,1,1,-3,10,0] 12 | Output: true 13 | */ 14 | 15 | // Approach: Hash map 16 | // O(n) time | O(n) space 17 | const uniqueOccurrences = (arr) => { 18 | // declare map 19 | const map = {}; 20 | for (let num of arr) { 21 | map[num] = map[num] + 1 || 1; 22 | } 23 | // decalre count map that will keep track of counts 24 | const counts = {}; 25 | for (let key in map) { 26 | const count = map[key]; 27 | // if count is in counts Map, then return false 28 | if (count in counts) return false; 29 | counts[count] = key; 30 | } 31 | 32 | return true; 33 | }; 34 | -------------------------------------------------------------------------------- /Arrays/zero-sum-subarray.js: -------------------------------------------------------------------------------- 1 | /* 2 | You're given a lis of integers num. Write a function that returns a boolean representing whether 3 | there exists a zero-sum subarray of nums; 4 | 5 | A zero-sum subarray is any subarray where all of the values add up to zero. A subarray is any 6 | contiguous section of the array. For the purposes of this problem, a subarray can be as small as 7 | one element and as long as the original array 8 | 9 | nums = [-5, -5, 2, 3, -2]; 10 | true 11 | */ 12 | 13 | // Approach: Prefix sums. If you see the current total sum in seen then that means there is a subarray that adds up to zero 14 | // O(n) time | O(n) space 15 | const zeroSumSubarray = (nums) => { 16 | // declare a seen set initialized with zero and total variable 17 | const seen = new Set([0]); 18 | let total = 0; 19 | // loop through nums 20 | for (let num of nums) { 21 | total += num; 22 | if (seen.has(total)) return true; 23 | seen.add(total); 24 | } 25 | return false; 26 | }; 27 | -------------------------------------------------------------------------------- /Backtracking/combinations.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given two integers n and k, return all possible combinations of k numbers chosen from the range [1, n]. 3 | 4 | You may return the answer in any order. 5 | 6 | Input: n = 4, k = 2 7 | Output: [[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]] 8 | Explanation: There are 4 choose 2 = 6 total combinations. 9 | Note that combinations are unordered, i.e., [1,2] and [2,1] are considered to be the same combination. 10 | 11 | Input: n = 1, k = 1 12 | Output: [[1]] 13 | Explanation: There is 1 choose 1 = 1 total combination. 14 | 15 | */ 16 | 17 | const combine = (n, k) => { 18 | const combinations = []; 19 | const combo = []; 20 | const dfs = (num) => { 21 | if (combo.length === k) { 22 | combinations.push([...combo]); 23 | return; 24 | } 25 | for (let i = num; i <= n; i++) { 26 | combo.push(i); 27 | dfs(i + 1); 28 | combo.pop(); 29 | } 30 | }; 31 | dfs(1); 32 | return combinations; 33 | }; 34 | 35 | const n = 5, 36 | k = 3; 37 | 38 | console.log(combine(n, k)); 39 | -------------------------------------------------------------------------------- /Backtracking/letter-case-permutation.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a string s, you can transform every letter individually to be lowercase or uppercase to create another string. 3 | 4 | Return a list of all possible strings we could create. Return the output in any order. 5 | 6 | Input: s = "a1b2" 7 | Output: ["a1b2","a1B2","A1b2","A1B2"] 8 | */ 9 | 10 | const letterCasePermutation = (s) => { 11 | const output = []; 12 | const numbers = '0123456789'; 13 | const dfs = (idx, string) => { 14 | if (idx === s.length) { 15 | output.push(string); 16 | return; 17 | } 18 | 19 | const char = s[idx]; 20 | if (numbers.includes(char)) { 21 | dfs(idx + 1, string + char); 22 | } else { 23 | dfs(idx + 1, string + char.toUpperCase()); 24 | dfs(idx + 1, string + char.toLowerCase()); 25 | } 26 | }; 27 | dfs(0, ''); 28 | return output; 29 | }; 30 | -------------------------------------------------------------------------------- /Backtracking/num-tiles.js: -------------------------------------------------------------------------------- 1 | /* 2 | You have n tiles, where each tile has one letter tiles[i] printed on it. 3 | 4 | Return the number of possible non-empty sequences of letters you can make using the letters printed on those tiles. 5 | 6 | Input: tiles = "AAB" 7 | Output: 8 8 | Explanation: The possible sequences are "A", "B", "AA", "AB", "BA", "AAB", "ABA", "BAA". 9 | 10 | Input: tiles = "AAABBC" 11 | Output: 188 12 | 13 | Input: tiles = "V" 14 | Output: 1 15 | 16 | */ 17 | 18 | const numTilePossibilities = (tiles) => { 19 | const counts = {}; 20 | for (let char of tiles) { 21 | counts[char] = counts[char] + 1 || 1; 22 | } 23 | let count = 0; 24 | const dfs = () => { 25 | for (let char in counts) { 26 | if (counts[char] <= 0) continue; 27 | counts[char]--; 28 | count++; 29 | dfs(); 30 | counts[char]++; 31 | } 32 | }; 33 | dfs(); 34 | return count; 35 | }; 36 | -------------------------------------------------------------------------------- /Backtracking/num-ways-pizza.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given a rectangular pizza represented as a rows x cols matrix containing the following characters: 'A' (an apple) and '.' (empty cell) and given the integer k. You have to cut the pizza into k pieces using k-1 cuts. 3 | 4 | For each cut you choose the direction: vertical or horizontal, then you choose a cut position at the cell boundary and cut the pizza into two pieces. If you cut the pizza vertically, give the left part of the pizza to a person. If you cut the pizza horizontally, give the upper part of the pizza to a person. Give the last piece of pizza to the last person. 5 | 6 | Return the number of ways of cutting the pizza such that each piece contains at least one apple. Since the answer can be a huge number, return this modulo 10^9 + 7. 7 | 8 | Input: pizza = ["A..","AAA","..."], k = 3 9 | Output: 3 10 | Explanation: The figure above shows the three ways to cut the pizza. Note that pieces must contain at least one apple. 11 | 12 | Input: pizza = ["A..","AA.","..."], k = 3 13 | Output: 1 14 | 15 | Input: pizza = ["A..","A..","..."], k = 1 16 | Output: 1 17 | */ 18 | 19 | const ways = (pizza, k) => { 20 | // you need to do a cut 21 | // and then check if valid? 22 | // only do valid cuts 23 | // vertical cuts 24 | // horizontal cuts 25 | }; 26 | -------------------------------------------------------------------------------- /Backtracking/palindrome-partioning.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a string s, partition s such that every 3 | substring 4 | of the partition is a 5 | palindrome 6 | . Return all possible palindrome partitioning of s. 7 | 8 | Input: s = "aab" 9 | Output: [["a","a","b"],["aa","b"]] 10 | 11 | Input: s = "a" 12 | Output: [["a"]] 13 | 14 | */ 15 | 16 | // Approach: Backtracking 17 | // O(n*2^n) time | O(n) space 18 | const partition = (s) => { 19 | const output = []; 20 | const dfs = (start, strings) => { 21 | if (start >= s.length) { 22 | output.push([...strings]); 23 | } 24 | for (let end = start; end < s.length; end++) { 25 | if (isPalindrome(s, start, end)) { 26 | // push to strings array 27 | strings.push(s.slice(start, end + 1)); 28 | dfs(end + 1, strings); 29 | // backtrack 30 | strings.pop(); 31 | } 32 | } 33 | }; 34 | dfs(0, []); 35 | return output; 36 | }; 37 | 38 | const isPalindrome = (s, start, end) => { 39 | while (start <= end) { 40 | if (s[start++] !== s[end--]) return false; 41 | } 42 | return true; 43 | }; 44 | -------------------------------------------------------------------------------- /Backtracking/subsetsII.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an integer array nums that may contain duplicates, return all possible 3 | subsets 4 | (the power set). 5 | 6 | The solution set must not contain duplicate subsets. Return the solution in any order. 7 | 8 | Input: nums = [1,2,2] 9 | Output: [[],[1],[1,2],[1,2,2],[2],[2,2]] 10 | 11 | Input: nums = [0] 12 | Output: [[],[0]] 13 | 14 | */ 15 | 16 | // O(n * 2^n) time | O(n) space 17 | const subsetsWithDup = (nums) => { 18 | nums.sort((a, b) => a - b); 19 | const output = []; 20 | const subset = []; 21 | const dfs = (idx) => { 22 | if (idx === nums.length) { 23 | output.push([...subset]); 24 | return; 25 | } 26 | // take 27 | subset.push(nums[idx]); 28 | let nextIdx = idx + 1; 29 | while (nextIdx < nums.length && nums[nextIdx] === nums[idx]) { 30 | nextIdx++; 31 | } 32 | dfs(idx + 1); 33 | // backtrack 34 | subset.pop(); 35 | dfs(nextIdx); 36 | }; 37 | dfs(0); 38 | return output; 39 | }; 40 | -------------------------------------------------------------------------------- /Binary/count-bits.js: -------------------------------------------------------------------------------- 1 | /* 2 | Count the number of bits in a nonnegative integer that are set to 1 3 | 4 | Input: 30 => "11110" 5 | Output: 4 6 | */ 7 | 8 | // O(n) time | O(1) space where n is the number of bits in the integer word 9 | const countBits = (num) => { 10 | // declare bits variable 11 | let bits = 0; 12 | // loop until number is 0 13 | while (num) { 14 | // & operation with current number 15 | bits += num & 1; 16 | // bitwise right shift by 1 17 | num >>= 1; 18 | } 19 | return bits; 20 | }; 21 | -------------------------------------------------------------------------------- /Binary/counting-bits.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given an integer n, return an array ans of length n + 1 such that for each i (0 <= i <= n), ans[i] is the number of 1's in the binary representation of i. 3 | * 4 | * Input: n = 2 5 | Output: [0,1,1] 6 | Explanation: 7 | 0 --> 0 8 | 1 --> 1 9 | 2 --> 10 10 | 11 | Input: n = 5 12 | Output: [0,1,1,2,1,2] 13 | Explanation: 14 | 0 --> 0 15 | 1 --> 1 16 | 2 --> 10 17 | 3 --> 11 18 | 4 --> 100 19 | 5 --> 101 20 | */ 21 | 22 | // Approach: 1D DP with Tabulation 23 | // O(n) time | O(n) space 24 | const countBits = (n) => { 25 | const dp = new Array(n + 1).fill(0); 26 | let offset = 1; 27 | for (let i = 1; i <= n; i++) { 28 | if (offset * 2 === i) offset = i; 29 | dp[i] = 1 + dp[i - offset]; 30 | } 31 | return dp; 32 | }; 33 | 34 | // O(nlog(n)) time | O(n) space 35 | const countBits2 = (n) => { 36 | const output = []; 37 | for (let i = 0; i <= n; i++) { 38 | let num = i; 39 | let oneBits = 0; 40 | while (num) { 41 | oneBits += num % 2; 42 | num = Math.floor(num / 2); 43 | } 44 | output.push(oneBits); 45 | } 46 | return output; 47 | }; 48 | -------------------------------------------------------------------------------- /Binary/num-1-bits.js: -------------------------------------------------------------------------------- 1 | /* 2 | Write a function that takes the binary representation of an unsigned integer and returns the number of '1' bits it has (also known as the Hamming weight). 3 | 4 | Input: n = 00000000000000000000000000001011 5 | Output: 3 6 | Explanation: The input binary string 00000000000000000000000000001011 has a total of three '1' bits. 7 | 8 | Input: n = 00000000000000000000000010000000 9 | Output: 1 10 | Explanation: The input binary string 00000000000000000000000010000000 has a total of one '1' bit. 11 | 12 | Input: n = 11111111111111111111111111111101 13 | Output: 31 14 | Explanation: The input binary string 11111111111111111111111111111101 has a total of thirty one '1' bits. 15 | */ 16 | 17 | // O(1) time | O(1) space 18 | const hammingWeight = (n) => { 19 | let count = 0; 20 | while (n) { 21 | count += 1; 22 | n = n & (n - 1); 23 | } 24 | return count; 25 | }; 26 | -------------------------------------------------------------------------------- /Binary/parity.js: -------------------------------------------------------------------------------- 1 | /* 2 | The parity of a word is 1 if the number of 1s in the word is odd; otherwise it is 0. 3 | For example, the parity of 1011 is 1 and the parity of 10001000 is 0 4 | */ 5 | 6 | // Approach: brute-force iteratively tests the value of each bit while tracking the number 7 | // of 1s seen so far. Since we only care if the number of 1s is even or odd, we can store the number mod 2 8 | // O(n) time | O(1) space where n is the length of the word 9 | const parity = (num) => { 10 | // declare count variable 11 | let count = 0; 12 | // while num is not 0 13 | while (num) { 14 | // xOr count with if last bit is 1 15 | count ^= num & 1; 16 | // bitwise shift num by 1 17 | num >>= 1; 18 | } 19 | return count; 20 | }; 21 | -------------------------------------------------------------------------------- /Binary_Trees/are-cousins.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root of a binary tree with unique values and the values of two different nodes of the tree x and y, return true if the nodes corresponding to the values x and y in the tree are cousins, or false otherwise. 3 | 4 | Two nodes of a binary tree are cousins if they have the same depth with different parents. 5 | 6 | Note that in a binary tree, the root node is at the depth 0, and children of each depth k node are at the depth k + 1. 7 | 8 | Input: root = [1,2,3,4], x = 4, y = 3 9 | Output: false 10 | 11 | Input: root = [1,2,3,null,4,null,5], x = 5, y = 4 12 | Output: true 13 | 14 | Input: root = [1,2,3,null,4], x = 2, y = 3 15 | Output: false 16 | 17 | */ 18 | 19 | // O(n) time | O(n) space 20 | const isCousins = (root, x, y) => { 21 | let queue = [[root, 0, null]]; 22 | let xNode; // [parent, depth] 23 | let yNode; 24 | 25 | while (queue.length) { 26 | const nextLevel = []; 27 | for (let [node, depth, parent] of queue) { 28 | if (node.val === x) xNode = [parent, depth]; 29 | if (node.val === y) yNode = [parent, depth]; 30 | if (xNode != null && yNode != null) 31 | return xNode[0] !== yNode[0] && xNode[1] === yNode[1]; 32 | if (node.left) nextLevel.push([node.left, depth + 1, node]); 33 | if (node.right) nextLevel.push([node.right, depth + 1, node]); 34 | } 35 | queue = nextLevel; 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /Binary_Trees/average-levels.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root of a binary tree, return the average value of the nodes on each level in the form of an array. Answers within 10-5 of the actual answer will be accepted. 3 | 4 | Input: root = [3,9,20,null,null,15,7] 5 | Output: [3.00000,14.50000,11.00000] 6 | Explanation: The average value of nodes on level 0 is 3, on level 1 is 14.5, and on level 2 is 11. 7 | Hence return [3, 14.5, 11]. 8 | 9 | Input: root = [3,9,20,15,7] 10 | Output: [3.00000,14.50000,11.00000] 11 | 12 | */ 13 | 14 | // O(n) time | O(n) space 15 | const averageOfLevels = (root) => { 16 | if (!root) return []; 17 | const output = []; 18 | let queue = [root]; 19 | while (queue.length) { 20 | const nextLevel = []; 21 | let sum = 0; 22 | let total = queue.length; 23 | for (let i = 0; i < queue.length; i++) { 24 | const curr = queue[i]; 25 | sum += curr.val; 26 | if (curr.left) nextLevel.push(curr.left); 27 | if (curr.right) nextLevel.push(curr.right); 28 | } 29 | output.push(sum / total); 30 | queue = nextLevel; 31 | } 32 | return output; 33 | }; 34 | -------------------------------------------------------------------------------- /Binary_Trees/binary-trees-paths.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root of a binary tree, return all root-to-leaf paths in any order. 3 | 4 | A leaf is a node with no children. 5 | 6 | Input: root = [1,2,3,null,5] 7 | Output: ["1->2->5","1->3"] 8 | 9 | Input: root = [1] 10 | Output: ["1"] 11 | 12 | */ 13 | 14 | // Approach: Backtracking 15 | // O(n) time | O(n) space 16 | const binaryTreePaths = (root) => { 17 | const paths = []; 18 | const dfs = (root, path) => { 19 | if (!root) return; 20 | if (!root.left && !root.right) { 21 | paths.push([...path, root.val]); 22 | return; 23 | } 24 | path.push(root.val); 25 | dfs(root.left, path); 26 | dfs(root.right, path); 27 | // backtrack 28 | path.pop(); 29 | }; 30 | dfs(root, []); 31 | return paths.map((path) => path.join('->')); 32 | }; 33 | -------------------------------------------------------------------------------- /Binary_Trees/bst-greater-sum.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root of a Binary Search Tree (BST), convert it to a Greater Tree such that every key of the original BST is changed to the original key plus the sum of all keys greater than the original key in BST. 3 | 4 | As a reminder, a binary search tree is a tree that satisfies these constraints: 5 | 6 | The left subtree of a node contains only nodes with keys less than the node's key. 7 | The right subtree of a node contains only nodes with keys greater than the node's key. 8 | Both the left and right subtrees must also be binary search trees. 9 | 10 | Input: root = [4,1,6,0,2,5,7,null,null,null,3,null,null,null,8] 11 | Output: [30,36,21,36,35,26,15,null,null,null,33,null,null,null,8] 12 | 13 | 14 | */ 15 | 16 | // O(n) time | O(h) space 17 | const bstToGst = (root) => { 18 | let sum = 0; 19 | const dfs = (root) => { 20 | if (!root) return; 21 | const { val, left, right } = root; 22 | dfs(right); 23 | root.val += sum; 24 | sum += val; 25 | dfs(left); 26 | }; 27 | dfs(root); 28 | return root; 29 | }; 30 | -------------------------------------------------------------------------------- /Binary_Trees/closest-bst-value.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root of a binary search tree and a target value, return the value in the BST that is closest to the target. If there are multiple answers, print the smallest. 3 | 4 | Input: root = [4,2,5,1,3], target = 3.714286 5 | Output: 4 6 | 7 | Input: root = [1], target = 4.428571 8 | Output: 1 9 | */ 10 | 11 | // O(h) time | O(1) space 12 | const closestValue = (root, target) => { 13 | let current = root; 14 | let ans = Infinity; 15 | let minDiff = Infinity; 16 | 17 | while (current) { 18 | const rootVal = current.val; 19 | const diff = Math.abs(rootVal - target); 20 | if (diff <= minDiff) { 21 | if (diff === minDiff) { 22 | ans = Math.min(ans, rootVal); 23 | } else { 24 | minDiff = diff; 25 | ans = rootVal; 26 | } 27 | } 28 | current = rootVal < target ? current.right : current.left; 29 | } 30 | 31 | return ans; 32 | }; 33 | -------------------------------------------------------------------------------- /Binary_Trees/construct-tree.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given two integer arrays preorder and inorder where preorder is the preorder traversal of a binary tree and inorder is the inorder traversal of the same tree, construct and return the binary tree. 3 | 4 | */ 5 | 6 | // O(n) time | O(n) space 7 | const buildTree = ( 8 | preorder, 9 | inorder, 10 | i = 0, 11 | j = preorder.length - 1, 12 | k = 0, 13 | l = inorder.length - 1 14 | ) => { 15 | if (k > l) return null; 16 | // get the root node 17 | const val = preorder[i]; 18 | const node = new TreeNode(val); 19 | // find the index of val in the inorder array 20 | const idx = inorder.indexOf(val); 21 | // determin left size 22 | const leftSize = idx - k; 23 | // recursively call function to get left subtree left 24 | node.left = buildTree(preorder, inorder, i + 1, i + leftSize, k, idx - 1); 25 | // recusrively call function to get right subtree 26 | node.right = buildTree(preorder, inorder, i + leftSize + 1, j, idx + 1, l); 27 | // return node 28 | return node; 29 | }; 30 | -------------------------------------------------------------------------------- /Binary_Trees/deepest-level-sum.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root of a binary tree, return the sum of values of its deepest leaves. 3 | 4 | Input: root = [1,2,3,4,5,null,6,7,null,null,null,null,8] 5 | Output: 15 6 | 7 | Input: root = [6,7,8,2,7,1,3,9,null,1,4,null,null,null,5] 8 | Output: 19 9 | 10 | */ 11 | 12 | // O(n) time | O(w) space where n is the number of nodes and w is the width of the tree 13 | const deepestLeavesSum = (root) => { 14 | if (!root) return 0; 15 | let queue = [root]; 16 | let levelSum = 0; 17 | while (queue.length) { 18 | const nextLevel = []; 19 | let nextSum = 0; 20 | for (let node of queue) { 21 | nextSum += node.val; 22 | if (node.left) nextLevel.push(node.left); 23 | if (node.right) nextLevel.push(node.right); 24 | } 25 | queue = nextLevel; 26 | levelSum = nextSum; 27 | } 28 | return levelSum; 29 | }; 30 | -------------------------------------------------------------------------------- /Binary_Trees/diameter-of-tree.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given the root of a binary tree, return the length of the diameter of the tree. 3 | 4 | The diameter of a binary tree is the length of the longest path between any two nodes in a tree. This path may or may not pass through the root. 5 | 6 | The length of a path between two nodes is represented by the number of edges between them. 7 | 8 | Input: root = [1,2,3,4,5] 9 | Output: 3 10 | Explanation: 3 is the length of the path [4,2,1,3] or [5,2,1,3]. 11 | 12 | Input: root = [1,2] 13 | Output: 1 14 | */ 15 | 16 | // Approach: DFS 17 | // O(n) time | O(h) space where n is the number of nodes and h is the height of the tree 18 | const diameterOfBinaryTree = (root) => { 19 | // call helper function 20 | return dfs(root).diameter; 21 | }; 22 | 23 | const dfs = (root) => { 24 | if (!root) return { diameter: 0, height: 0 }; 25 | // get left and right subtree info 26 | const leftTree = dfs(root.left); 27 | const rightTree = dfs(root.right); 28 | const maxDiameter = Math.max( 29 | leftTree.diameter, 30 | rightTree.diameter, 31 | leftTree.height + rightTree.height 32 | ); 33 | const newHeight = 1 + Math.max(leftTree.height, rightTree.height); 34 | return { diameter: maxDiameter, height: newHeight }; 35 | }; 36 | -------------------------------------------------------------------------------- /Binary_Trees/find-bottom-left-val.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root of a binary tree, return the leftmost value in the last row of the tree. 3 | 4 | Input: root = [2,1,3] 5 | Output: 1 6 | 7 | Input: root = [1,2,3,4,null,5,6,null,null,7] 8 | Output: 7 9 | 10 | */ 11 | 12 | // O(n) time | O(n) space 13 | const findBottomLeftValue = (root) => { 14 | if (!root) return root; 15 | let queue = [root]; 16 | let leftMost = root; 17 | while (queue.length) { 18 | const nextLevel = []; 19 | for (let curr of queue) { 20 | if (curr.left) nextLevel.push(curr.left); 21 | if (curr.right) nextLevel.push(curr.right); 22 | } 23 | queue = nextLevel; 24 | if (queue.length) leftMost = queue[0]; 25 | } 26 | return leftMost.val; 27 | }; 28 | -------------------------------------------------------------------------------- /Binary_Trees/find-corresponding-node.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given two binary trees original and cloned and given a reference to a node target in the original tree. 3 | 4 | The cloned tree is a copy of the original tree. 5 | 6 | Return a reference to the same node in the cloned tree. 7 | 8 | Note that you are not allowed to change any of the two trees or the target node and the answer must be a reference to a node in the cloned tree. 9 | 10 | Input: tree = [7,4,3,null,null,6,19], target = 3 11 | Output: 3 12 | Explanation: In all examples the original and cloned trees are shown. The target node is a green node from the original tree. The answer is the yellow node from the cloned tree. 13 | 14 | Input: tree = [7], target = 7 15 | Output: 7 16 | 17 | Input: tree = [8,null,6,null,5,null,4,null,3,null,2,null,1], target = 4 18 | Output: 4 19 | 20 | */ 21 | 22 | // O(n) time | O(h) space 23 | const getTargetCopy = (original, cloned, target) => { 24 | if (!cloned) return null; 25 | if (cloned.val === target.val) return cloned; 26 | 27 | const left = getTargetCopy(original, cloned.left, target); 28 | if (left) return left; 29 | const right = getTargetCopy(original, cloned.right, target); 30 | if (right) return right; 31 | }; 32 | -------------------------------------------------------------------------------- /Binary_Trees/find-leaves.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root of a binary tree, collect a tree's nodes as if you were doing this: 3 | 4 | Collect all the leaf nodes. 5 | Remove all the leaf nodes. 6 | Repeat until the tree is empty. 7 | 8 | Input: root = [1,2,3,4,5] 9 | Output: [[4,5,3],[2],[1]] 10 | Explanation: 11 | [[3,5,4],[2],[1]] and [[3,4,5],[2],[1]] are also considered correct answers since per each level it does not matter the order on which elements are returned. 12 | 13 | Input: root = [1] 14 | Output: [[1]] 15 | 16 | */ 17 | 18 | // Approach: DFS 19 | // O(n) time | O(n) space 20 | const findLeaves = (root) => { 21 | const output = []; 22 | const dfs = (root) => { 23 | if (!root) return -1; 24 | const leftHeight = dfs(root.left); 25 | const rightHeight = dfs(root.right); 26 | const newHeight = 1 + Math.max(leftHeight, rightHeight); 27 | // if output size = newHeight add an empty array 28 | if (output.length === newHeight) output.push([]); 29 | // add the root to this level 30 | output[newHeight].push(root.val); 31 | // return newHeight 32 | return newHeight; 33 | }; 34 | dfs(root); 35 | return output; 36 | }; 37 | -------------------------------------------------------------------------------- /Binary_Trees/get-all-elements.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given two binary search trees root1 and root2, return a list containing all the integers from both trees sorted in ascending order. 3 | 4 | Input: root1 = [2,1,4], root2 = [1,0,3] 5 | Output: [0,1,1,2,3,4] 6 | 7 | Input: root1 = [1,null,8], root2 = [8,1] 8 | Output: [1,1,8,8] 9 | */ 10 | 11 | // Approach: Post order, merge sort 12 | // O(n + m) time | O(n + m) space 13 | const getAllElements = (root1, root2) => { 14 | const values1 = []; 15 | const values2 = []; 16 | inOrder(root1, values1); 17 | inOrder(root2, values2); 18 | 19 | const sortedOrder = new Array(values1.length + values2.length); 20 | 21 | let l = 0; 22 | let r = 0; 23 | let idx = 0; 24 | while (l < values1.length || r < values2.length) { 25 | const val1 = l < values1.length ? values1[l] : Infinity; 26 | const val2 = r < values2.length ? values2[r] : Infinity; 27 | 28 | if (val1 < val2) { 29 | sortedOrder[idx++] = val1; 30 | l++; 31 | } else { 32 | sortedOrder[idx++] = val2; 33 | r++; 34 | } 35 | } 36 | 37 | return sortedOrder; 38 | }; 39 | 40 | const inOrder = (root, values) => { 41 | if (!root) return; 42 | inOrder(root.left, values); 43 | values.push(root.val); 44 | inOrder(root.right, values); 45 | }; 46 | -------------------------------------------------------------------------------- /Binary_Trees/good-nodes.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a binary tree root, a node X in the tree is named good if in the path from root to X there are no nodes with a value greater than X. 3 | 4 | Return the number of good nodes in the binary tree. 5 | 6 | Input: root = [3,3,null,4,2] 7 | Output: 3 8 | Explanation: Node 2 -> (3, 3, 2) is not good, because "3" is higher than it. 9 | 10 | Input: root = [1] 11 | Output: 1 12 | Explanation: Root is considered as good. 13 | */ 14 | 15 | // O(n) time | O(h) space 16 | const goodNodes = (root) => { 17 | let count = 0; 18 | const dfs = (root, maxVal) => { 19 | if (!root) return; 20 | if (root.val >= maxVal) count++; 21 | const nextVal = root.val >= maxVal ? root.val : maxVal; 22 | dfs(root.left, nextVal); 23 | dfs(root.right, nextVal); 24 | }; 25 | dfs(root, -Infinity); 26 | return count; 27 | }; 28 | -------------------------------------------------------------------------------- /Binary_Trees/inorder-successor.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root of a binary search tree and a node p in it, return the in-order successor of that node in the BST. If the given node has no in-order successor in the tree, return null. 3 | 4 | The successor of a node p is the node with the smallest key greater than p.val. 5 | 6 | Input: root = [2,1,3], p = 1 7 | Output: 2 8 | Explanation: 1's in-order successor node is 2. Note that both p and the return value is of TreeNode type. 9 | 10 | Input: root = [5,3,6,2,4,null,null,1], p = 6 11 | Output: null 12 | Explanation: There is no in-order successor of the current node, so the answer is null. 13 | 14 | */ 15 | 16 | // O(h) time | O(1) space 17 | const inorderSuccessor = (root, p) => { 18 | let current = root; 19 | let successor = null; 20 | 21 | while (current) { 22 | if (p.val >= current.val) { 23 | current = current.right; 24 | } else { 25 | successor = current; 26 | current = current.left; 27 | } 28 | } 29 | 30 | return successor; 31 | }; 32 | 33 | const inorderSuccessor2 = (root, p) => { 34 | const inorder = []; 35 | dfs(root, inorder); 36 | const idx = inorder.indexOf(p); 37 | return inorder[idx + 1] || null; 38 | }; 39 | 40 | const dfs = (root, inorder) => { 41 | if (!root) return; 42 | dfs(root.left, inorder); 43 | inorder.push(root); 44 | dfs(root.right, inorder); 45 | }; 46 | -------------------------------------------------------------------------------- /Binary_Trees/insert-into-bst.js: -------------------------------------------------------------------------------- 1 | /** 2 | * You are given the root node of a binary search tree (BST) and a value to insert into the tree. Return the root node of the BST after the insertion. It is guaranteed that the new value does not exist in the original BST. 3 | 4 | Notice that there may exist multiple valid ways for the insertion, as long as the tree remains a BST after insertion. You can return any of them. 5 | 6 | Input: root = [40,20,60,10,30,50,70], val = 25 7 | Output: [40,20,60,10,30,50,70,null,null,25] 8 | 9 | Input: root = [4,2,7,1,3,null,null,null,null,null,null], val = 5 10 | Output: [4,2,7,1,3,5] 11 | */ 12 | 13 | // O(log(n)) time | O(1 space) 14 | const insertIntoBST = (root, val) => { 15 | const newNode = new TreeNode(val); 16 | if (!root) return newNode; 17 | let current = root; 18 | while (current) { 19 | if (val > current.val) { 20 | // go right 21 | if (!current.right) { 22 | current.right = newNode; 23 | return root; 24 | } else { 25 | current = current.right; 26 | } 27 | } else { 28 | // go left 29 | if (!current.left) { 30 | current.left = newNode; 31 | return root; 32 | } else { 33 | current = current.left; 34 | } 35 | } 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /Binary_Trees/invert-tree.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root of a binary tree, invert the tree, and return its root. 3 | */ 4 | 5 | // Approach: Recursion 6 | // O(n) time | O(h) space where h is the height of the tree 7 | const invertTree = (tree) => { 8 | // base case is if tree is null return null 9 | if (!tree) return null; 10 | // save left and right tree 11 | const right = tree.right; 12 | const left = tree.left; 13 | // update tree's left and right nodes 14 | tree.left = right; 15 | tree.right = left; 16 | // recursively call function on both subtrees 17 | invertTree(tree.left); 18 | invertTree(tree.right); 19 | // return root 20 | return tree; 21 | }; 22 | -------------------------------------------------------------------------------- /Binary_Trees/is-balanced.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a binary tree, determine if it is height-balanced 3 | 4 | Input: root = [3,9,20,null,null,15,7] 5 | Output: true 6 | 7 | Input: root = [1,2,2,3,3,null,null,4,4] 8 | Output: false 9 | 10 | */ 11 | 12 | // O(n) time | O(h) space where n is the number of nodes and h is the height of the tree 13 | const isBalanced = (root) => { 14 | return getTreeInfo(root).isBalanced; 15 | }; 16 | 17 | class TreeInfo { 18 | constructor(isBalanced, height) { 19 | this.isBalanced = isBalanced; 20 | this.height = height; 21 | } 22 | } 23 | const getTreeInfo = (root) => { 24 | // base case if root is null 25 | if (!root) return new TreeInfo(true, -1); 26 | // recursively call on left and right subtree 27 | const leftTreeInfo = getTreeInfo(root.left); 28 | const rightTreeInfo = getTreeInfo(root.right); 29 | const newHeight = 1 + Math.max(leftTreeInfo.height, rightTreeInfo.height); 30 | const isBalanced = 31 | leftTreeInfo.isBalanced && 32 | rightTreeInfo.isBalanced && 33 | Math.abs(leftTreeInfo.height - rightTreeInfo.height) <= 1; 34 | return new TreeInfo(isBalanced, newHeight); 35 | }; 36 | -------------------------------------------------------------------------------- /Binary_Trees/largest-value.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root of a binary tree, return an array of the largest value in each row of the tree (0-indexed). 3 | 4 | Input: root = [1,3,2,5,3,null,9] 5 | Output: [1,3,9] 6 | 7 | Input: root = [1,2,3] 8 | Output: [1,3] 9 | */ 10 | 11 | // O(n) time | O(n) space 12 | const largestValues = (root) => { 13 | if (!root) return []; 14 | let queue = [root]; 15 | const output = []; 16 | while (queue.length) { 17 | const nextLevel = []; 18 | let max = -Infinity; 19 | for (let current of queue) { 20 | max = Math.max(max, current.val); 21 | if (current.left) nextLevel.push(current.left); 22 | if (current.right) nextLevel.push(current.right); 23 | } 24 | queue = nextLevel; 25 | output.push(max); 26 | } 27 | return output; 28 | }; 29 | -------------------------------------------------------------------------------- /Binary_Trees/level-order-bottom.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given the root of a binary tree, return the bottom-up level order traversal of its nodes' values. (i.e., from left to right, level by level from leaf to root). 3 | * 4 | * Input: root = [3,9,20,null,null,15,7] 5 | Output: [[15,7],[9,20],[3]] 6 | 7 | Input: root = [1] 8 | Output: [[1]] 9 | 10 | Input: root = [] 11 | Output: [] 12 | */ 13 | 14 | // Approach: BFS 15 | // O(n) time | O(n) space where n is the number of nodes 16 | const levelOrderBottom = (root) => { 17 | if (!root) return []; 18 | const output = []; 19 | let currentLevel = [root]; 20 | while (currentLevel.length) { 21 | const newLevel = []; 22 | const outputLevel = []; 23 | for (let i = 0; i < currentLevel.length; i++) { 24 | const current = currentLevel[i]; 25 | outputLevel.push(current.val); 26 | if (current.left) newLevel.push(current.left); 27 | if (current.right) newLevel.push(current.right); 28 | } 29 | output.push(outputLevel); 30 | currentLevel = newLevel; 31 | } 32 | return output.reverse(); 33 | }; 34 | -------------------------------------------------------------------------------- /Binary_Trees/longest-univalue-path.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root of a binary tree, return the length of the longest path, where each node in the path has the same value. This path may or may not pass through the root. 3 | 4 | The length of the path between two nodes is represented by the number of edges between them. 5 | 6 | Input: root = [5,4,5,1,1,null,5] 7 | Output: 2 8 | Explanation: The shown image shows that the longest path of the same value (i.e. 5). 9 | 10 | Input: root = [1,4,5,4,4,null,5] 11 | Output: 2 12 | Explanation: The shown image shows that the longest path of the same value (i.e. 4). 13 | 14 | */ 15 | 16 | // O(n) time | O(h) space 17 | const longestUnivaluePath = (root) => { 18 | return dfs(root)[1]; 19 | }; 20 | 21 | const dfs = (root) => { 22 | if (!root) return [0, 0]; // current height, max path 23 | let leftPath = 0; 24 | let rightPath = 0; 25 | let [leftHeight, leftMax] = dfs(root.left, root.val); 26 | let [rightHeight, rightMax] = dfs(root.right, root.val); 27 | 28 | if (root.left && root.val === root.left.val) { 29 | leftPath += leftHeight + 1; 30 | } 31 | if (root.right && root.val === root.right.val) { 32 | rightPath += rightHeight + 1; 33 | } 34 | // new max height 35 | const newHeight = Math.max(leftPath, rightPath); 36 | const newMax = Math.max(leftMax, rightMax, leftPath + rightPath); 37 | return [newHeight, newMax]; 38 | }; 39 | -------------------------------------------------------------------------------- /Binary_Trees/longest-zigzag.js: -------------------------------------------------------------------------------- 1 | /** 2 | * You are given the root of a binary tree. 3 | 4 | A ZigZag path for a binary tree is defined as follow: 5 | 6 | Choose any node in the binary tree and a direction (right or left). 7 | If the current direction is right, move to the right child of the current node; otherwise, move to the left child. 8 | Change the direction from right to left or from left to right. 9 | Repeat the second and third steps until you can't move in the tree. 10 | Zigzag length is defined as the number of nodes visited - 1. (A single node has a length of 0). 11 | 12 | Return the longest ZigZag path contained in that tree. 13 | */ 14 | 15 | // O(n) time | O(h) space 16 | const longestZigZag = (root) => { 17 | // left direction = 0, right direction = 1, start direction -1 18 | return dfs(root, -1, -1); 19 | }; 20 | 21 | const dfs = (root, parentDirection, length) => { 22 | // base case if root is null return length 23 | if (!root) return length; 24 | // recursively call dfs on both sides, passing in correct direction and updated length 25 | const left = dfs(root.left, 0, parentDirection === 0 ? 0 : length + 1); 26 | const right = dfs(root.right, 1, parentDirection === 1 ? 0 : length + 1); 27 | // return max 28 | return Math.max(left, right); 29 | }; 30 | -------------------------------------------------------------------------------- /Binary_Trees/max-depth.js: -------------------------------------------------------------------------------- 1 | // Return max depth of a binary tree 2 | 3 | class Node { 4 | constructor(value) { 5 | this.value = value; 6 | this.left = null; 7 | this.right = null; 8 | } 9 | } 10 | // Approach: Recursive 11 | // O(n) time | O(h) space where n is number of nodes and h is height of tree 12 | const maxDepth = (root) => { 13 | // base case if root is null return 0 14 | if (root === null) return 0; 15 | // return 1 + max of left and right height 16 | return 1 + Math.max(maxDepth(root.left), maxDepth(root.right)); 17 | }; 18 | -------------------------------------------------------------------------------- /Binary_Trees/max-level-sum.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root of a binary tree, the level of its root is 1, the level of its children is 2, and so on. 3 | 4 | Return the smallest level x such that the sum of all the values of nodes at level x is maximal. 5 | 6 | Input: root = [1,7,0,7,-8,null,null] 7 | Output: 2 8 | Explanation: 9 | Level 1 sum = 1. 10 | Level 2 sum = 7 + 0 = 7. 11 | Level 3 sum = 7 + -8 = -1. 12 | So we return the level with the maximum sum which is level 2. 13 | 14 | Input: root = [989,null,10250,98693,-89388,null,null,null,-32127] 15 | Output: 2 16 | */ 17 | 18 | // O(n) time | O(n) space 19 | const maxLevelSum = (root) => { 20 | let level = 1; 21 | let maxVal = -Infinity; 22 | let maxLevel = 1; 23 | 24 | let queue = [root]; 25 | while (queue.length) { 26 | let total = 0; 27 | const nextLevel = []; 28 | for (let node of queue) { 29 | total += node.val; 30 | if (node.left) nextLevel.push(node.left); 31 | if (node.right) nextLevel.push(node.right); 32 | } 33 | if (total > maxVal) { 34 | maxVal = total; 35 | maxLevel = level; 36 | } 37 | queue = nextLevel; 38 | level++; 39 | } 40 | return maxLevel; 41 | }; 42 | -------------------------------------------------------------------------------- /Binary_Trees/max-product.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given the root of a binary tree, split the binary tree into two subtrees by removing one edge such that the product of the sums of the subtrees is maximized. 3 | 4 | Return the maximum product of the sums of the two subtrees. Since the answer may be too large, return it modulo 109 + 7. 5 | 6 | Note that you need to maximize the answer before taking the mod and not after taking it. 7 | */ 8 | 9 | // O(n) time | O(n) space 10 | const maxProduct = (root) => { 11 | const sums = []; 12 | const totalSum = dfs(root, sums); 13 | let max = 0; 14 | for (let sum of sums) { 15 | max = Math.max(max, sum * (totalSum - sum)); 16 | } 17 | return max % (Math.pow(10, 9) + 7); 18 | }; 19 | 20 | const dfs = (root, sums) => { 21 | if (!root) return 0; 22 | const leftSum = dfs(root.left, sums); 23 | const rightSum = dfs(root.right, sums); 24 | const totalSum = leftSum + rightSum + root.val; 25 | sums.push(totalSum); 26 | return totalSum; 27 | }; 28 | -------------------------------------------------------------------------------- /Binary_Trees/maximum-average-subtree.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root of a binary tree, return the maximum average value of a subtree of that tree. Answers within 10-5 of the actual answer will be accepted. 3 | 4 | A subtree of a tree is any node of that tree plus all its descendants. 5 | 6 | The average value of a tree is the sum of its values, divided by the number of nodes. 7 | 8 | Input: root = [5,6,1] 9 | Output: 6.00000 10 | Explanation: 11 | For the node with value = 5 we have an average of (5 + 6 + 1) / 3 = 4. 12 | For the node with value = 6 we have an average of 6 / 1 = 6. 13 | For the node with value = 1 we have an average of 1 / 1 = 1. 14 | So the answer is 6 which is the maximum. 15 | 16 | Input: root = [0,null,1] 17 | Output: 1.00000 18 | 19 | */ 20 | 21 | // O(n) time | O(h) space 22 | const maximumAverageSubtree = (root) => { 23 | let max = -Infinity; 24 | 25 | const dfs = (root) => { 26 | if (!root) return [0, 0]; // [sum, count] 27 | 28 | const [leftSum, leftCount] = dfs(root.left); 29 | const [rightSum, rightCount] = dfs(root.right); 30 | 31 | const totalSum = root.val + leftSum + rightSum; 32 | const totalCount = leftCount + rightCount + 1; 33 | max = Math.max(totalSum / totalCount, max); 34 | return [totalSum, totalCount]; 35 | }; 36 | 37 | dfs(root); 38 | return max; 39 | }; 40 | -------------------------------------------------------------------------------- /Binary_Trees/merge-trees.js: -------------------------------------------------------------------------------- 1 | /* 2 | You are given two binary trees root1 and root2. 3 | 4 | Imagine that when you put one of them to cover the other, some nodes of the two trees are overlapped while the others are not. You need to merge the two trees into a new binary tree. The merge rule is that if two nodes overlap, then sum node values up as the new value of the merged node. Otherwise, the NOT null node will be used as the node of the new tree. 5 | 6 | Return the merged tree. 7 | 8 | Note: The merging process must start from the root nodes of both trees. 9 | 10 | */ 11 | 12 | // O(n) time | O(h) where n is the number of nodes and h is max height of both trees 13 | const mergeTrees = (root1, root2) => { 14 | if (!root2) return root1; 15 | if (!root1) return root2; 16 | root1.val += root2.val; 17 | root1.left = mergeTrees(root1.left, root2.left); 18 | root1.right = mergeTrees(root1.right, root2.right); 19 | return root1; 20 | }; 21 | -------------------------------------------------------------------------------- /Binary_Trees/min-depth.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given a binary tree, find its minimum depth. 3 | 4 | The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node. 5 | 6 | Note: A leaf is a node with no children. 7 | */ 8 | 9 | // O(n) time | O(h) space where n is the number of nodes and h is the height of the tree 10 | const minDepth = (root) => { 11 | const res = _minDepth(root); 12 | return res === Infinity ? 0 : res; 13 | }; 14 | const _minDepth = (root) => { 15 | // base case if null return 0, if leaf return 1 16 | if (!root) return Infinity; 17 | if (!root.left && !root.right) return 1; 18 | return 1 + Math.min(_minDepth(root.left), _minDepth(root.right)); 19 | }; 20 | -------------------------------------------------------------------------------- /Binary_Trees/min-height-bst.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an integer array nums where the elements are sorted in ascending order, convert it to a height-balanced binary search tree. 3 | */ 4 | 5 | // O(n) time | O(n) space 6 | const sortedArrayToBST = (nums, i = 0, j = nums.length - 1) => { 7 | // base case: if i > j return null 8 | if (i > j) return null; 9 | // get middle index and middle value that will become root node 10 | const midIdx = Math.floor((i + j) / 2); 11 | const rootNode = new TreeNode(nums[midIdx]); 12 | // set rootNode.left to be result of recursively calling function with updated indexes 13 | rootNode.left = sortedArrayToBST(nums, i, midIdx - 1); 14 | // set rootNode.right to be result of recursively calling function with updated indexes 15 | rootNode.right = sortedArrayToBST(nums, midIdx + 1, j); 16 | // return rootNode 17 | return rootNode; 18 | }; 19 | -------------------------------------------------------------------------------- /Binary_Trees/most-frequent-subtree-sum.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root of a binary tree, return the most frequent subtree sum. If there is a tie, return all the values with the highest frequency in any order. 3 | 4 | The subtree sum of a node is defined as the sum of all the node values formed by the subtree rooted at that node (including the node itself). 5 | 6 | Input: root = [5,2,-3] 7 | Output: [2,-3,4] 8 | 9 | Input: root = [5,2,-5] 10 | Output: [2] 11 | */ 12 | 13 | // O(n) time | O(n) space 14 | const findFrequentTreeSum = (root) => { 15 | const sums = {}; 16 | const dfs = (root) => { 17 | if (!root) return 0; 18 | const leftSum = dfs(root.left); 19 | const rightSum = dfs(root.right); 20 | const total = root.val + leftSum + rightSum; 21 | sums[total] = sums[total] + 1 || 1; 22 | return total; 23 | }; 24 | dfs(root); 25 | let maxCount = 0; 26 | let output = []; 27 | for (let sum in sums) { 28 | if (sums[sum] === maxCount) output.push(Number(sum)); 29 | if (sums[sum] > maxCount) { 30 | maxCount = sums[sum]; 31 | output = [Number(sum)]; 32 | } 33 | } 34 | return output; 35 | }; 36 | -------------------------------------------------------------------------------- /Binary_Trees/nodes-equal-sum-descendants.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root of a binary tree, return the number of nodes where the value of the node is equal to the sum of the values of its descendants. 3 | 4 | A descendant of a node x is any node that is on the path from node x to some leaf node. The sum is considered to be 0 if the node has no descendants. 5 | 6 | Input: root = [10,3,4,2,1] 7 | Output: 2 8 | Explanation: 9 | For the node with value 10: The sum of its descendants is 3+4+2+1 = 10. 10 | For the node with value 3: The sum of its descendants is 2+1 = 3. 11 | 12 | Input: root = [2,3,null,2,null] 13 | Output: 0 14 | Explanation: 15 | No node has a value that is equal to the sum of its descendants. 16 | 17 | Input: root = [0] 18 | Output: 1 19 | For the node with value 0: The sum of its descendants is 0 since it has no descendants. 20 | 21 | */ 22 | 23 | // O(n) time | O(h) space 24 | const equalToDescendants = (root) => { 25 | let count = 0; 26 | 27 | const dfs = (root) => { 28 | if (!root) return 0; 29 | 30 | const leftSum = dfs(root.left); 31 | const rightSum = dfs(root.right); 32 | 33 | if (root.val === leftSum + rightSum) count++; 34 | 35 | return root.val + leftSum + rightSum; 36 | }; 37 | 38 | dfs(root); 39 | return count; 40 | }; 41 | -------------------------------------------------------------------------------- /Binary_Trees/path-sum.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given the root of a binary tree and an integer targetSum, return true if the tree has a root-to-leaf path such that adding up all the values along the path equals targetSum. 3 | 4 | A leaf is a node with no children. 5 | 6 | Input: root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22 7 | Output: true 8 | Explanation: The root-to-leaf path with the target sum is shown. 9 | 10 | Input: root = [1,2,3], targetSum = 5 11 | Output: false 12 | Explanation: There two root-to-leaf paths in the tree: 13 | (1 --> 2): The sum is 3. 14 | (1 --> 3): The sum is 4. 15 | There is no root-to-leaf path with sum = 5. 16 | 17 | Input: root = [], targetSum = 0 18 | Output: false 19 | Explanation: Since the tree is empty, there are no root-to-leaf paths. 20 | */ 21 | 22 | // Approach: DFS 23 | // O(n) time | O(h) space where n is number of nodes and h is height of the tree 24 | const hasPathSum = (root, targetSum) => { 25 | if (!root) return false; 26 | if (!root.left && !root.right) return targetSum - root.val === 0; 27 | return ( 28 | hasPathSum(root.left, targetSum - root.val) || 29 | hasPathSum(root.right, targetSum - root.val) 30 | ); 31 | }; 32 | -------------------------------------------------------------------------------- /Binary_Trees/path-sumII.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root of a binary tree and an integer targetSum, return all root-to-leaf paths where the sum of the node values in the path equals targetSum. Each path should be returned as a list of the node values, not node references. 3 | 4 | A root-to-leaf path is a path starting from the root and ending at any leaf node. A leaf is a node with no children. 5 | 6 | Input: root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22 7 | Output: [[5,4,11,2],[5,8,4,5]] 8 | Explanation: There are two paths whose sum equals targetSum: 9 | 5 + 4 + 11 + 2 = 22 10 | 5 + 8 + 4 + 5 = 22 11 | 12 | 13 | */ 14 | 15 | // O(n^2) time | O(n) space where n is the number of nodes 16 | const pathSum = (root, targetSum) => { 17 | const paths = []; 18 | const dfs = (root, targetSum, path) => { 19 | if (!root) return; 20 | if (!root.left && !root.right && targetSum - root.val === 0) { 21 | paths.push([...path, root.val]); 22 | return; 23 | } 24 | const leftPath = [...path, root.val]; 25 | const rightPath = [...path, root.val]; 26 | dfs(root.left, targetSum - root.val, leftPath); 27 | dfs(root.right, targetSum - root.val, rightPath); 28 | return; 29 | }; 30 | dfs(root, targetSum, []); 31 | return paths; 32 | }; 33 | -------------------------------------------------------------------------------- /Binary_Trees/path-sumIII.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Given the root of a binary tree and an integer targetSum, return the number of paths where the sum of the values along the path equals targetSum. 4 | 5 | The path does not need to start or end at the root or a leaf, but it must go downwards (i.e., traveling only from parent nodes to child nodes). 6 | 7 | Input: root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8 8 | Output: 3 9 | Explanation: The paths that sum to 8 are shown. 10 | 11 | Input: root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22 12 | Output: 3 13 | 14 | */ 15 | 16 | // O(n) time | O(n) space 17 | const pathSum = (root, targetSum) => { 18 | let count = 0; 19 | const sums = {}; 20 | const dfs = (root, currentSum) => { 21 | if (!root) return null; 22 | currentSum += root.val; 23 | if (currentSum === targetSum) count++; 24 | if (currentSum - targetSum in sums) count += sums[currentSum - targetSum]; 25 | sums[currentSum] = sums[currentSum] + 1 || 1; 26 | dfs(root.left, currentSum); 27 | dfs(root.right, currentSum); 28 | sums[currentSum]--; 29 | }; 30 | dfs(root, 0); 31 | return count; 32 | }; 33 | -------------------------------------------------------------------------------- /Binary_Trees/range-sum.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given the root node of a binary search tree and two integers low and high, return the sum of values of all nodes with a value in the inclusive range [low, high]. 3 | * 4 | * Input: root = [10,5,15,3,7,null,18], low = 7, high = 15 5 | Output: 32 6 | Explanation: Nodes 7, 10, and 15 are in the range [7, 15]. 7 + 10 + 15 = 32. 7 | 8 | Input: root = [10,5,15,3,7,13,18,1,null,6], low = 6, high = 10 9 | Output: 23 10 | Explanation: Nodes 6, 7, and 10 are in the range [6, 10]. 6 + 7 + 10 = 23. 11 | */ 12 | 13 | // O(n) time | O(h) space 14 | const rangeSumBST = (root, low, high) => { 15 | // base case if root is null, return 0 16 | if (!root) return 0; 17 | // recursively call function on left and right subtree to get sums 18 | const leftSum = rangeSumBST(root.left, low, high); 19 | const rightSum = rangeSumBST(root.right, low, high); 20 | // if root.val is within range add to left and right sum, then return 21 | if (root.val >= low && root.val <= high) return root.val + leftSum + rightSum; 22 | // return left + right sum if root.val is not within range 23 | return leftSum + rightSum; 24 | }; 25 | -------------------------------------------------------------------------------- /Binary_Trees/right-side-view.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Given the root of a binary tree, imagine yourself standing on the right side of it, return the values of the nodes you can see ordered from top to bottom. 4 | 5 | */ 6 | 7 | // O(n) time | O(d) space where n is the number of nodes and d is the diameter of the tree 8 | const rightSideView = (root) => { 9 | // if root is null return empty array 10 | if (!root) return []; 11 | // declare output array and current level 12 | const output = []; 13 | let currentLevel = [root]; 14 | // while there are nodes in the currentLevel 15 | while (currentLevel.length) { 16 | const length = currentLevel.length; 17 | const newLevel = []; 18 | for (let i = 0; i < length; i++) { 19 | const current = currentLevel[i]; 20 | // push val at first index into output 21 | if (i === 0) output.push(current.val); 22 | // push right first if it exists 23 | if (current.right) newLevel.push(current.right); 24 | if (current.left) newLevel.push(current.left); 25 | } 26 | // update current level 27 | currentLevel = newLevel; 28 | } 29 | // return output 30 | return output; 31 | }; 32 | -------------------------------------------------------------------------------- /Binary_Trees/same-tree.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the roots of two binary trees p and q, write a function to check if they are the same or not. 3 | 4 | Two binary trees are considered the same if they are structurally identical, 5 | and the nodes have the same value. 6 | 7 | */ 8 | 9 | class Node { 10 | constructor(val) { 11 | this.val = val; 12 | this.left = null; 13 | this.right = null; 14 | } 15 | } 16 | 17 | // O(n) time | O(h) space where n is min number of nodes between both trees and h is min height b/w trees 18 | const isSameTree = (p, q) => { 19 | // if both trees are null then return true 20 | if (p === null && q === null) return true; 21 | // if one of them is false return false 22 | if (p === null || q === null) return false; 23 | // if values do not equal return false 24 | if (p.val !== q.val) return false; 25 | 26 | // check if left is same 27 | const isLeftSame = isSameTree(p.left, q.left); 28 | // check if right is same 29 | const isRightSame = isSameTree(p.right, q.right); 30 | 31 | return isLeftSame && isRightSame; 32 | }; 33 | -------------------------------------------------------------------------------- /Binary_Trees/search-in-bst.js: -------------------------------------------------------------------------------- 1 | /* 2 | You are given the root of a binary search tree (BST) and an integer val. 3 | 4 | Find the node in the BST that the node's value equals val and return the subtree rooted with that node. If such a node does not exist, return null. 5 | 6 | Input: root = [4,2,7,1,3], val = 2 7 | Output: [2,1,3] 8 | 9 | Input: root = [4,2,7,1,3], val = 5 10 | Output: [] 11 | */ 12 | 13 | // O(log(n)) time | O(1) space 14 | const searchBST = (root, val) => { 15 | // declare current pointer 16 | let current = root; 17 | while (current) { 18 | // if current.val = val return current 19 | if (current.val === val) return current; 20 | if (current.val < val) { 21 | // go right 22 | current = current.right; 23 | } else { 24 | current = current.left; 25 | } 26 | } 27 | return null; 28 | }; 29 | -------------------------------------------------------------------------------- /Binary_Trees/squares-of-sorted-array.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an integer array nums sorted in non-decreasing order, return an array of the squares of each number sorted in non-decreasing order. 3 | 4 | Input: nums = [-4,-1,0,3,10] 5 | Output: [0,1,9,16,100] 6 | Explanation: After squaring, the array becomes [16,1,0,9,100]. 7 | After sorting, it becomes [0,1,9,16,100]. 8 | 9 | Input: nums = [-7,-3,2,3,11] 10 | Output: [4,9,9,49,121] 11 | 12 | */ 13 | 14 | // Approach: Two Pointer 15 | // O(n) time | O(n) space 16 | const sortedSquares = (nums) => { 17 | const output = new Array(nums.length); 18 | let l = 0; 19 | let r = nums.length - 1; 20 | for (let i = output.length - 1; i >= 0; i--) { 21 | if (Math.abs(nums[l]) > Math.abs(nums[r])) { 22 | output[i] = nums[l] * nums[l]; 23 | l++; 24 | } else { 25 | output[i] = nums[r] * nums[r]; 26 | r--; 27 | } 28 | } 29 | return output; 30 | }; 31 | -------------------------------------------------------------------------------- /Binary_Trees/sum-of-left-leaves.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root of a binary tree, return the sum of all left leaves. 3 | 4 | A leaf is a node with no children. A left leaf is a leaf that is the left child of another node. 5 | 6 | Input: root = [3,9,20,null,null,15,7] 7 | Output: 24 8 | Explanation: There are two left leaves in the binary tree, with values 9 and 15 respectively. 9 | 10 | Input: root = [1] 11 | Output: 0 12 | 13 | */ 14 | // O(n) time | O(h) space 15 | const sumOfLeftLeaves = (root) => { 16 | let count = 0; 17 | const dfs = (root) => { 18 | if (!root) return; 19 | if (root.left && !root.left.left && !root.left.right) 20 | count += root.left.val; 21 | dfs(root.left); 22 | dfs(root.right); 23 | }; 24 | dfs(root); 25 | return count; 26 | }; 27 | -------------------------------------------------------------------------------- /Binary_Trees/symmetric-tree.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root of a binary tree, check whether it is a mirror of itself (i.e., symmetric around its center). 3 | */ 4 | 5 | // Approach: Recursive helper functions that checks for equality at each node 6 | // O(n) time | O(h) space where n is number of nodes and h is height of tree 7 | const isSymmetric = (root) => { 8 | return _isSymmetric(root.left, root.right); 9 | }; 10 | 11 | const _isSymmetric = (left, right) => { 12 | // base cases: if both are null return true, if not, if either are null or vals dont equal return false 13 | if (!left && !right) return true; 14 | if (!left || !right || left.val !== right.val) return false; 15 | // recursively call and return function comparing left.left & right.right && left.right & right.left 16 | return ( 17 | _isSymmetric(left.left, right.right) && _isSymmetric(left.right, right.left) 18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /Binary_Trees/two-sum-BSTs.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the roots of two binary search trees, root1 and root2, return true if and only if there is a node in the first tree and a node in the second tree whose values sum up to a given integer target. 3 | 4 | Input: root1 = [2,1,4], root2 = [1,0,3], target = 5 5 | Output: true 6 | Explanation: 2 and 3 sum up to 5. 7 | 8 | Input: root1 = [0,-10,10], root2 = [5,1,7,0,2], target = 18 9 | Output: false 10 | 11 | */ 12 | 13 | // O(m + n) time | O(m + n) space 14 | const twoSumBSTs = (root1, root2, target) => { 15 | const set1 = new Set(); 16 | const set2 = new Set(); 17 | dfs(root1, set1); 18 | dfs(root2, set2); 19 | 20 | for (let value of set1) { 21 | if (set2.has(target - value)) return true; 22 | } 23 | return false; 24 | }; 25 | 26 | const dfs = (root, set) => { 27 | if (!root) return; 28 | dfs(root.left, set); 29 | set.add(root.val); 30 | dfs(root.right, set); 31 | }; 32 | -------------------------------------------------------------------------------- /Binary_Trees/two-sum.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root of a binary search tree and an integer k, return true if there exist two elements in the BST such that their sum is equal to k, or false otherwise. 3 | 4 | */ 5 | 6 | // Approach BST 7 | // O(n) time | O(n) space 8 | const findTarget = (root, k) => { 9 | // declare sums map 10 | const sums = {}; 11 | // declare stack 12 | const stack = [root]; 13 | // dfs 14 | while (stack.length) { 15 | const current = stack.pop(); 16 | const complement = k - current.val; 17 | if (complement in sums) return true; 18 | sums[current.val] = true; 19 | if (current.left) stack.push(current.left); 20 | if (current.right) stack.push(current.right); 21 | } 22 | return false; 23 | }; 24 | 25 | // Approach: Inorder traversal and then two pointers 26 | // O(n) time | O(n) space 27 | const findTarget2 = (root, k) => { 28 | const vals = []; 29 | inorder(root, vals); 30 | let i = 0; 31 | let j = vals.length - 1; 32 | while (i < j) { 33 | const sum = vals[i] + vals[j]; 34 | if (sum === k) return true; 35 | if (sum > k) { 36 | j--; 37 | } else { 38 | i++; 39 | } 40 | } 41 | return false; 42 | }; 43 | 44 | const inorder = (root, values) => { 45 | if (!root) return; 46 | inorder(root.left, values); 47 | values.push(root.val); 48 | inorder(root.right, values); 49 | return; 50 | }; 51 | -------------------------------------------------------------------------------- /Binary_Trees/univalue-subtrees.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root of a binary tree, return the number of uni-value 3 | subtrees 4 | . 5 | 6 | A uni-value subtree means all nodes of the subtree have the same value. 7 | 8 | Input: root = [5,1,5,5,5,null,5] 9 | Output: 4 10 | 11 | Input: root = [] 12 | Output: 0 13 | 14 | Input: root = [5,5,5,5,5,null,5] 15 | Output: 6 16 | 17 | */ 18 | 19 | // O(n) time | O(h) space 20 | const countUnivalSubtrees = (root) => { 21 | let count = 0; 22 | const dfs = (root) => { 23 | if (!root) return [true, null]; 24 | const [leftBool, leftVal] = dfs(root.left); 25 | const [rightBool, rightVal] = dfs(root.right); 26 | 27 | if (!leftBool || !rightBool) return [false, Infinity]; 28 | 29 | if ( 30 | (leftVal === null || root.val === leftVal) && 31 | (rightVal === null || root.val === rightVal) 32 | ) { 33 | count++; 34 | return [true, root.val]; 35 | } 36 | 37 | return [false, Infinity]; 38 | }; 39 | dfs(root); 40 | return count; 41 | }; 42 | -------------------------------------------------------------------------------- /Binary_Trees/upside-down-binary-tree.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root of a binary tree, turn the tree upside down and return the new root. 3 | 4 | You can turn a binary tree upside down with the following steps: 5 | 6 | The original left child becomes the new root. 7 | The original root becomes the new right child. 8 | The original right child becomes the new left child. 9 | 10 | Input: root = [1,2,3,4,5] 11 | Output: [4,5,2,null,null,3,1] 12 | 13 | Input: root = [] 14 | Output: [] 15 | 16 | */ 17 | 18 | // O(n) time | O(h) space 19 | const upsideDownBinaryTree = (root) => { 20 | if (!root) return root; 21 | 22 | return rotate(root); 23 | }; 24 | 25 | const rotate = (root) => { 26 | // if no left node return 27 | if (!root.left) return root; 28 | // get root. Root will be recursive call to left subtree 29 | const newRoot = rotate(root.left); 30 | root.left.right = root; 31 | root.left.left = root.right; 32 | root.left = null; 33 | root.right = null; 34 | return newRoot; 35 | }; 36 | -------------------------------------------------------------------------------- /Binary_Trees/validate-bst.js: -------------------------------------------------------------------------------- 1 | /* 2 | Write a function that takes in a potentially invalvid Binary Search Tree and returns a boolean if the BST is valid 3 | 4 | */ 5 | 6 | class BST { 7 | construcot(value) { 8 | this.value = value; 9 | this.right = null; 10 | this.left = null; 11 | } 12 | } 13 | 14 | // O(n) height | O(d) space where d is height of the tree 15 | const validateBst = (tree, minVal = -Infinity, maxVal = Infinity) => { 16 | if (tree === null) return true; 17 | if (tree.value < minVal || tree.value >= maxVal) return false; 18 | // check if left is valid making a recursive call with tree.left, min val and the tree.value for max Val 19 | const isLeftValid = validateBst(tree.left, minVal, tree.value); 20 | // check if right is valid by making recursive call with right subtree, the tree.value for the min value and max value 21 | const isRightValid = validateBst(tree.right, tree.value, maxVal); 22 | return isLeftValid && isRightValid; 23 | }; 24 | -------------------------------------------------------------------------------- /Binary_Trees/vertical-order-traversal.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root of a binary tree, return the vertical order traversal of its nodes' values. (i.e., from top to bottom, column by column). 3 | 4 | If two nodes are in the same row and column, the order should be from left to right. 5 | 6 | Input: root = [3,9,20,null,null,15,7] 7 | Output: [[9],[3,15],[20],[7]] 8 | 9 | Input: root = [3,9,8,4,0,1,7] 10 | Output: [[4],[9],[3,0,1],[8],[7]] 11 | 12 | Input: root = [3,9,8,4,0,1,7,null,null,null,2,5] 13 | Output: [[4],[9,5],[3,0,1],[8,2],[7]] 14 | */ 15 | 16 | // O(n) time | O(n) space 17 | const verticalOrder = (root) => { 18 | if (!root) return []; 19 | const levels = {}; 20 | let queue = [[root, 0]]; 21 | let minCol = Infinity; 22 | let maxCol = -Infinity; 23 | while (queue.length) { 24 | const nextLevel = []; 25 | for (let [current, col] of queue) { 26 | minCol = Math.min(minCol, col); 27 | maxCol = Math.max(maxCol, col); 28 | if (!(col in levels)) levels[col] = []; 29 | levels[col].push(current.val); 30 | if (current.left) nextLevel.push([current.left, col - 1]); 31 | if (current.right) nextLevel.push([current.right, col + 1]); 32 | } 33 | queue = nextLevel; 34 | } 35 | const output = []; 36 | for (let i = minCol; i <= maxCol; i++) { 37 | output.push(levels[i]); 38 | } 39 | return output; 40 | }; 41 | -------------------------------------------------------------------------------- /Design/range-sum-query.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an integer array nums, handle multiple queries of the following type: 3 | 4 | Calculate the sum of the elements of nums between indices left and right inclusive where left <= right. 5 | Implement the NumArray class: 6 | 7 | NumArray(int[] nums) Initializes the object with the integer array nums. 8 | int sumRange(int left, int right) Returns the sum of the elements of nums between indices left and right inclusive (i.e. nums[left] + nums[left + 1] + ... + nums[right]). 9 | 10 | Input 11 | ["NumArray", "sumRange", "sumRange", "sumRange"] 12 | [[[-2, 0, 3, -5, 2, -1]], [0, 2], [2, 5], [0, 5]] 13 | Output 14 | [null, 1, -1, -3] 15 | 16 | Explanation 17 | NumArray numArray = new NumArray([-2, 0, 3, -5, 2, -1]); 18 | numArray.sumRange(0, 2); // return (-2) + 0 + 3 = 1 19 | numArray.sumRange(2, 5); // return 3 + (-5) + 2 + (-1) = -1 20 | numArray.sumRange(0, 5); // return (-2) + 0 + 3 + (-5) + 2 + (-1) = -3 21 | */ 22 | 23 | class NumArray { 24 | constructor(nums) { 25 | this.prefixSums = []; 26 | let total = 0; 27 | for (let num of nums) { 28 | total += num; 29 | this.prefixSums.push(total); 30 | } 31 | } 32 | 33 | sumRange(left, right) { 34 | let leftPrefix = left > 0 ? this.prefixSums[left - 1] : 0; 35 | let rightPrefix = this.prefixSums[right]; 36 | return rightPrefix - leftPrefix; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Dynamic_Programming/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zacharydfreeman/algorithms/11f8a2c55502a51208136afb11d069bdc21d5b00/Dynamic_Programming/.DS_Store -------------------------------------------------------------------------------- /Dynamic_Programming/climbing-stairs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * You are climbing a staircase. It takes n steps to reach the top. 3 | 4 | Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top? 5 | 6 | Input: n = 2 7 | Output: 2 8 | Explanation: There are two ways to climb to the top. 9 | 1. 1 step + 1 step 10 | 2. 2 steps 11 | 12 | Input: n = 3 13 | Output: 3 14 | Explanation: There are three ways to climb to the top. 15 | 1. 1 step + 1 step + 1 step 16 | 2. 1 step + 2 steps 17 | 3. 2 steps + 1 step 18 | */ 19 | 20 | // Approach: Tabulaion Optimized 21 | // O(n) time | O(1) space 22 | const climbStairs = (n) => { 23 | let ways = [1, 1]; 24 | for (let stairs = 2; stairs <= n; stairs++) { 25 | const temp = ways[1]; 26 | ways[1] = ways[0] + ways[1]; 27 | ways[0] = temp; 28 | } 29 | return ways[1]; 30 | }; 31 | 32 | // Approach: 1D DP/Tabulation 33 | // O(n) time | O(n) space 34 | const climbStairs2 = (n) => { 35 | const dp = new Array(n + 1).fill(1); 36 | for (let stairs = 2; stairs <= n; stairs++) { 37 | dp[stairs] = dp[stairs - 1] + dp[stairs - 2]; 38 | } 39 | return dp[n]; 40 | }; 41 | -------------------------------------------------------------------------------- /Dynamic_Programming/coin-changeII.js: -------------------------------------------------------------------------------- 1 | /* 2 | You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount of money. 3 | 4 | Return the number of combinations that make up that amount. If that amount of money cannot be made up by any combination of the coins, return 0. 5 | 6 | You may assume that you have an infinite number of each kind of coin. 7 | 8 | The answer is guaranteed to fit into a signed 32-bit integer. 9 | 10 | Input: amount = 5, coins = [1,2,5] 11 | Output: 4 12 | Explanation: there are four ways to make up the amount: 13 | 5=5 14 | 5=2+2+1 15 | 5=2+1+1+1 16 | 5=1+1+1+1+1 17 | 18 | Input: amount = 3, coins = [2] 19 | Output: 0 20 | Explanation: the amount of 3 cannot be made up just with coins of 2. 21 | 22 | */ 23 | 24 | // Approach: 1D DP with Tabulation 25 | // O(n*k) time | O(n) space where n is the amount and k is the number of coins 26 | const change = (amount, coins) => { 27 | const dp = new Array(amount + 1).fill(0); 28 | dp[0] = 1; 29 | for (let coin of coins) { 30 | for (let i = 1; i < amount + 1; i++) { 31 | if (coin <= i) { 32 | dp[i] += dp[i - coin]; 33 | } 34 | } 35 | } 36 | return dp[amount]; 37 | }; 38 | -------------------------------------------------------------------------------- /Dynamic_Programming/edit-distance.js: -------------------------------------------------------------------------------- 1 | /* 2 | Implement Levenshtein Distance 3 | 4 | Given two strings word1 and word2, return the minimum number of operations required to convert word1 to word2. 5 | 6 | You have the following three operations permitted on a word: 7 | 8 | Insert a character 9 | Delete a character 10 | Replace a character 11 | 12 | */ 13 | 14 | // 2D Dynamic Programming 15 | // O(n*m) time | O(n*m) space 16 | const minDistance = (word1, word2) => { 17 | const edits = []; 18 | for (let row = 0; row <= word1.length; row++) { 19 | const newRow = []; 20 | for (let col = 0; col <= word2.length; col++) { 21 | newRow.push(col); 22 | } 23 | newRow[0] = row; 24 | edits.push(newRow); 25 | } 26 | 27 | for (let row = 1; row <= word1.length; row++) { 28 | for (let col = 1; col <= word2.length; col++) { 29 | if (word1[row - 1] === word2[col - 1]) { 30 | edits[row][col] = edits[row - 1][col - 1]; 31 | } else { 32 | edits[row][col] = 33 | 1 + 34 | Math.min( 35 | edits[row - 1][col - 1], 36 | edits[row - 1][col], 37 | edits[row][col - 1] 38 | ); 39 | } 40 | } 41 | } 42 | 43 | return edits[word1.length][word2.length]; 44 | }; 45 | -------------------------------------------------------------------------------- /Dynamic_Programming/house-robber.js: -------------------------------------------------------------------------------- 1 | /*** 2 | * You are a professional robber planning to rob houses along a street. 3 | * Each house has a certain amount of money stashed, the only constraint stopping you 4 | * from robbing each of them is that adjacent houses have security systems connected and 5 | * it will automatically contact the police if two adjacent houses were broken into on the same night. 6 | * Given an integer array nums representing the amount of money of each house, return the maximum amount of 7 | * money you can rob tonight without alerting the police. 8 | * 9 | * Input: nums = [1,2,3,1] 10 | Output: 4 11 | Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3). 12 | Total amount you can rob = 1 + 3 = 4. 13 | 14 | Input: nums = [2,7,9,3,1] 15 | Output: 12 16 | Explanation: Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 (money = 1). 17 | Total amount you can rob = 2 + 9 + 1 = 12. 18 | * 19 | */ 20 | 21 | const rob = (array) => { 22 | if (array.length === 1) return array[0]; 23 | 24 | let first = Math.max(array[0], array[1]); 25 | let second = array[0]; 26 | 27 | for (let i = 2; i < array.length; i++) { 28 | const oldFirst = first; 29 | first = Math.max(first, second + array[i]); 30 | second = oldFirst; 31 | } 32 | 33 | return first; 34 | } -------------------------------------------------------------------------------- /Dynamic_Programming/longest-arithmetic-sequence.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an array nums of integers, return the length of the longest arithmetic subsequence in nums. 3 | 4 | Note that: 5 | 6 | A subsequence is an array that can be derived from another array by deleting some or no elements without changing the order of the remaining elements. 7 | A sequence seq is arithmetic if seq[i + 1] - seq[i] are all the same value (for 0 <= i < seq.length - 1). 8 | 9 | Input: nums = [3,6,9,12] 10 | Output: 4 11 | Explanation: The whole array is an arithmetic sequence with steps of length = 3. 12 | 13 | Input: nums = [9,4,7,2,10] 14 | Output: 3 15 | Explanation: The longest arithmetic subsequence is [4,7,10]. 16 | 17 | Input: nums = [20,1,15,3,10,5,8] 18 | Output: 4 19 | Explanation: The longest arithmetic subsequence is [20,15,10,5]. 20 | */ 21 | 22 | // O(n^2) time | O(n^2) space 23 | const longestArithSeqLength = (nums) => { 24 | const lengths = {}; 25 | let max = 0; 26 | for (let i = 0; i < nums.length; i++) { 27 | const currObj = {}; 28 | for (let j = 0; j < i; j++) { 29 | const obj = lengths[j]; 30 | const diff = nums[i] - nums[j]; 31 | if (diff in obj) { 32 | currObj[diff] = obj[diff] + 1; 33 | } else { 34 | currObj[diff] = 1; 35 | } 36 | max = Math.max(max, currObj[diff] + 1); 37 | } 38 | lengths[i] = currObj; 39 | } 40 | return max; 41 | }; 42 | -------------------------------------------------------------------------------- /Dynamic_Programming/longest-palin-subseq.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a string s, find the longest palindromic subsequence's length in s. 3 | 4 | A subsequence is a sequence that can be derived from another sequence by deleting some or no elements without changing the order of the remaining elements. 5 | 6 | Input: s = "bbbab" 7 | Output: 4 8 | Explanation: One possible longest palindromic subsequence is "bbbb". 9 | 10 | Input: s = "cbbd" 11 | Output: 2 12 | Explanation: One possible longest palindromic subsequence is "bb". 13 | 14 | */ 15 | 16 | // O(n^2) time | O(n^2) space where n is the length of the string 17 | const longestPalindromeSubseq = (s, i = 0, j = s.length - 1, memo = {}) => { 18 | const key = i + ',' + j; 19 | if (key in memo) return memo[key]; 20 | if (i === j) return 1; 21 | if (i > j) return 0; 22 | 23 | if (s[i] === s[j]) { 24 | memo[key] = 2 + longestPalindromeSubseq(s, i + 1, j - 1, memo); 25 | } else { 26 | const result = longestPalindromeSubseq(s, i + 1, j, memo); 27 | const result2 = longestPalindromeSubseq(s, i, j - 1, memo); 28 | memo[key] = Math.max(result, result2); 29 | } 30 | 31 | return memo[key]; 32 | }; 33 | -------------------------------------------------------------------------------- /Dynamic_Programming/number-of-LIS.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an integer array nums, return the number of longest increasing subsequences. 3 | 4 | Notice that the sequence has to be strictly increasing. 5 | 6 | Input: nums = [1,3,5,4,7] 7 | Output: 2 8 | Explanation: The two longest increasing subsequences are [1, 3, 4, 7] and [1, 3, 5, 7]. 9 | 10 | Input: nums = [2,2,2,2,2] 11 | Output: 5 12 | Explanation: The length of the longest increasing subsequence is 1, and there are 5 increasing subsequences of length 1, so output 5. 13 | */ 14 | 15 | // O(n^2) time | O(n) space 16 | const findNumberOfLIS = (nums) => { 17 | const dp = new Array(nums.length).fill(1); 18 | const count = new Array(nums.length).fill(1); 19 | let max = 1; 20 | for (let i = 0; i < nums.length; i++) { 21 | for (let j = 0; j < i; j++) { 22 | if (nums[i] > nums[j]) { 23 | if (dp[j] + 1 > dp[i]) { 24 | dp[i] = dp[j] + 1; 25 | count[i] = count[j]; 26 | } else if (dp[i] === dp[j] + 1) { 27 | count[i] += count[j]; 28 | } 29 | } 30 | } 31 | max = Math.max(max, dp[i]); 32 | } 33 | 34 | let ans = 0; 35 | for (let i = 0; i < dp.length; i++) { 36 | if (dp[i] === max) ans += count[i]; 37 | } 38 | return ans; 39 | }; 40 | -------------------------------------------------------------------------------- /Graphs/all-paths.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a directed acyclic graph (DAG) of n nodes labeled from 0 to n - 1, find all possible paths from node 0 to node n - 1 and return them in any order. 3 | 4 | The graph is given as follows: graph[i] is a list of all nodes you can visit from node i (i.e., there is a directed edge from node i to node graph[i][j]). 5 | 6 | Input: graph = [[1,2],[3],[3],[]] 7 | Output: [[0,1,3],[0,2,3]] 8 | Explanation: There are two paths: 0 -> 1 -> 3 and 0 -> 2 -> 3. 9 | 10 | Input: graph = [[4,3,1],[3,2,4],[3],[4],[]] 11 | Output: [[0,4],[0,3,4],[0,1,3,4],[0,1,2,3,4],[0,1,4]] 12 | 13 | 14 | */ 15 | 16 | // Approach: Backtracking 17 | // O(2^n * n) time | O(2^n * n) space 18 | const allPathsSourceTarget = (graph) => { 19 | const paths = []; 20 | const dfs = (graph, src, dst, path) => { 21 | if (src === dst) { 22 | paths.push([...path, src]); 23 | return; 24 | } 25 | path.push(src); 26 | for (let nei of graph[src]) { 27 | dfs(graph, nei, dst, path); 28 | } 29 | path.pop(); 30 | return; 31 | }; 32 | dfs(graph, 0, graph.length - 1, []); 33 | return paths; 34 | }; 35 | -------------------------------------------------------------------------------- /Graphs/has-cycle.js: -------------------------------------------------------------------------------- 1 | /* 2 | Write a function, hasCycle, that takes in an object representing the adjacency list of a directed graph. The function should return a boolean indicating whether or not the graph contains a cycle. 3 | 4 | hasCycle({ 5 | a: ["b"], 6 | b: ["c"], 7 | c: ["a"], 8 | }); // -> true 9 | 10 | hasCycle({ 11 | a: ["b", "c"], 12 | b: ["c"], 13 | c: ["d"], 14 | d: [], 15 | }); // -> false 16 | 17 | */ 18 | 19 | // Approach: White-grey-black algorithm 20 | // O(v + e) time | O(v) space where v is the number of vertices and e is the number of edges 21 | const hasCycle = (graph) => { 22 | const visited = new Set(); 23 | for (let startNode in graph) { 24 | if (cycleDetect(graph, startNode, new Set(), visited)) return true; 25 | } 26 | return false; 27 | }; 28 | 29 | const cycleDetect = (graph, node, visiting, visited) => { 30 | if (visited.has(node)) return false; 31 | 32 | if (visiting.has(node)) return true; 33 | 34 | visiting.add(node); 35 | 36 | for (let neighbor of graph[node]) { 37 | if (cycleDetect(graph, neighbor, visiting, visited)) return true; 38 | } 39 | 40 | visiting.delete(node); 41 | visited.add(node); 42 | return false; 43 | }; 44 | -------------------------------------------------------------------------------- /Graphs/min-num-vertices.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a directed acyclic graph, with n vertices numbered from 0 to n-1, and an array edges where edges[i] = [fromi, toi] represents a directed edge from node fromi to node toi. 3 | 4 | Find the smallest set of vertices from which all nodes in the graph are reachable. It's guaranteed that a unique solution exists. 5 | 6 | Notice that you can return the vertices in any order. 7 | 8 | Input: n = 6, edges = [[0,1],[0,2],[2,5],[3,4],[4,2]] 9 | Output: [0,3] 10 | Explanation: It's not possible to reach all the nodes from a single vertex. From 0 we can reach [0,1,2,5]. From 3 we can reach [3,4,2,5]. So we output [0,3]. 11 | 12 | Input: n = 5, edges = [[0,1],[2,1],[3,1],[1,4],[2,4]] 13 | Output: [0,2,3] 14 | Explanation: Notice that vertices 0, 3 and 2 are not reachable from any other node, so we must include them. Also any of these vertices can reach nodes 1 and 4. 15 | 16 | */ 17 | 18 | // O(v + e) time | O(v) space where v is the number of vertices and e is the number of edges 19 | const findSmallestSetOfVertices = (n, edges) => { 20 | const inbound = new Array(n).fill(0); 21 | for (let [a, b] of edges) { 22 | inbound[b]++; 23 | } 24 | const output = []; 25 | for (let i = 0; i < inbound.length; i++) { 26 | if (inbound[i] === 0) output.push(i); 27 | } 28 | return output; 29 | }; 30 | -------------------------------------------------------------------------------- /Iterators/zigzag-iterator.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given two vectors of integers v1 and v2, implement an iterator to return their elements alternately. 3 | 4 | Implement the ZigzagIterator class: 5 | 6 | ZigzagIterator(List v1, List v2) initializes the object with the two vectors v1 and v2. 7 | boolean hasNext() returns true if the iterator still has elements, and false otherwise. 8 | int next() returns the current element of the iterator and moves the iterator to the next element. 9 | 10 | */ 11 | 12 | class ZigzagIterator { 13 | constructor(v1, v2) { 14 | this.v1 = v1; 15 | this.v2 = v2; 16 | this.idxOne = 0; 17 | this.idxTwo = 0; 18 | this.called = 0; 19 | } 20 | 21 | hasNext() { 22 | return this.idxOne < this.v1.length || this.idxTwo < this.v2.length; 23 | } 24 | 25 | next() { 26 | if (this.called % 2 === 0) { 27 | this.called++; 28 | if (this.idxOne < this.v1.length) { 29 | return this.v1[this.idxOne++]; 30 | } else { 31 | return this.v2[this.idxTwo++]; 32 | } 33 | } else { 34 | this.called++; 35 | if (this.idxTwo < this.v2.length) { 36 | return this.v2[this.idxTwo++]; 37 | } else { 38 | return this.v1[this.idxOne++]; 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Linked_Lists/delete-node.js: -------------------------------------------------------------------------------- 1 | /* 2 | There is a singly-linked list head and we want to delete a node node in it. 3 | 4 | You are given the node to be deleted node. You will not be given access to the first node of head. 5 | 6 | All the values of the linked list are unique, and it is guaranteed that the given node node is not the last node in the linked list. 7 | 8 | Delete the given node. Note that by deleting the node, we do not mean removing it from memory. We mean: 9 | 10 | The value of the given node should not exist in the linked list. 11 | The number of nodes in the linked list should decrease by one. 12 | All the values before node should be in the same order. 13 | All the values after node should be in the same order. 14 | Custom testing: 15 | 16 | For the input, you should provide the entire linked list head and the node to be given node. node should not be the last node of the list and should be an actual node in the list. 17 | We will build the linked list and pass the node to your function. 18 | The output will be the entire list after calling your function. 19 | 20 | */ 21 | 22 | // O(1) time | O(1) space 23 | const deleteNode = (node) => { 24 | // change current node's val to be the val of the next node 25 | node.val = node.next.val; 26 | // set current node next to be next node's next 27 | node.next = node.next.next; 28 | }; 29 | -------------------------------------------------------------------------------- /Linked_Lists/has-cycle.js: -------------------------------------------------------------------------------- 1 | /* 2 | Determine if linked list has cycle 3 | 4 | 3 -> 4 -> 5 -> 1 -> 7 -> 4 5 | */ 6 | 7 | class Node { 8 | constructor(value) { 9 | this.value = value; 10 | this.next = null; 11 | } 12 | } 13 | 14 | // O(n) time | O(1) space 15 | const hasCycle = (head) => { 16 | // declare fast and slow pointers 17 | let slow = head; 18 | let fast = head; 19 | while (fast !== null && fast.next !== null) { 20 | slow = slow.next; 21 | fast = fast.next.next; 22 | // if slow === fast there is a cycle, return true 23 | if (slow === fast) return true; 24 | } 25 | return false; 26 | }; 27 | -------------------------------------------------------------------------------- /Linked_Lists/is-palindrome.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given head of linked list, determine if it is a palindrome. 3 | 4 | Do in O(n) time | O(1) space 5 | 6 | a -> b -> c -> c -> b -> a 7 | a <- b <- c c -> b -> a 8 | 1 -> 0 -> 1 9 | s f 10 | */ 11 | 12 | // Approach: Reverse the second half of the linked list and determine if palindrome 13 | // O(n) time | O(1) space 14 | const isPalindrome = (head) => { 15 | // get the middle of the linked list 16 | let slow = head; 17 | let fast = head; 18 | while (fast && fast.next) { 19 | slow = slow.next; 20 | fast = fast.next.next; 21 | } 22 | 23 | // reverse second half 24 | let prev = null; 25 | let current = slow; 26 | while (current) { 27 | const next = current.next; 28 | current.next = prev; 29 | prev = current; 30 | current = next; 31 | } 32 | 33 | // traverse at each pointer and check if values are equal 34 | let current1 = head; 35 | let current2 = prev; 36 | while (current2) { 37 | if (current1.val !== current2.val) return false; 38 | current1 = current1.next; 39 | current2 = current2.next; 40 | } 41 | 42 | return true; 43 | }; 44 | 45 | class Node { 46 | constructor(val) { 47 | this.val = val; 48 | this.next = null; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Linked_Lists/merge-lists.js: -------------------------------------------------------------------------------- 1 | /* 2 | Merge two sorted linked lists. 3 | */ 4 | 5 | // O(n) time | O(1) space 6 | const mergeLists = (head1, head2) => { 7 | const dummyNode = new Node(null); 8 | let tail = dummyNode; 9 | let current1 = head1; 10 | let current2 = head2; 11 | 12 | while (current1 || current2) { 13 | const val1 = current1 ? current1.val : Infinity; 14 | const val2 = current2 ? current2.val : Infinity; 15 | if (val1 < val2) { 16 | tail.next = current1; 17 | current1 = current1.next; 18 | } else { 19 | tail.next = current2; 20 | current2 = current2.next; 21 | } 22 | tail = tail.next; 23 | } 24 | 25 | return dummyNode.next; 26 | }; 27 | -------------------------------------------------------------------------------- /Linked_Lists/partition.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the head of a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x. 3 | 4 | You should preserve the original relative order of the nodes in each of the two partitions. 5 | 6 | Input: head = [1,4,3,2,5,2], x = 3 7 | Output: [1,2,2,4,3,5] 8 | 9 | Input: head = [2,1], x = 2 10 | Output: [1,2] 11 | 12 | */ 13 | 14 | // O(n) time | O(1) space 15 | const partition = (head, x) => { 16 | if (!head) return null; 17 | const lower = new ListNode(0); 18 | const upper = new ListNode(0); 19 | let p1 = lower; 20 | let p2 = upper; 21 | let current = head; 22 | while (current) { 23 | if (current.val < x) { 24 | p1.next = current; 25 | p1 = current; 26 | } else { 27 | p2.next = current; 28 | p2 = current; 29 | } 30 | current = current.next; 31 | } 32 | p2.next = null; 33 | p1.next = upper.next; 34 | return lower.next; 35 | }; 36 | -------------------------------------------------------------------------------- /Linked_Lists/plus-one.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a non-negative integer represented as a linked list of digits, plus one to the integer. 3 | 4 | The digits are stored such that the most significant digit is at the head of the list. 5 | 6 | 7 | 8 | 9 | */ 10 | 11 | // O(n) time | O(1) space 12 | const plusOne = (head) => { 13 | // reverse list 14 | const newHead = reverse(head); 15 | let current = newHead; 16 | let prev = null; 17 | let carry = 1; 18 | while (current) { 19 | const val = current.val + carry; 20 | if (val >= 10) { 21 | current.val = 0; 22 | } else { 23 | current.val = val; 24 | } 25 | carry = val >= 10 ? 1 : 0; 26 | prev = current; 27 | current = current.next; 28 | } 29 | if (carry) { 30 | const node = new ListNode(1); 31 | prev.next = node; 32 | } 33 | return reverse(newHead); 34 | }; 35 | 36 | const reverse = (head) => { 37 | let prev = null; 38 | let current = head; 39 | while (current) { 40 | const next = current.next; 41 | current.next = prev; 42 | prev = current; 43 | current = next; 44 | } 45 | return prev; 46 | }; 47 | -------------------------------------------------------------------------------- /Linked_Lists/remove-duplicates.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given the head of a sorted linked list, delete all duplicates such that each element appears only once. Return the linked list sorted as well. 3 | * 4 | * Input: head = [1,1,2] 5 | Output: [1,2] 6 | 7 | Input: head = [1,1,2,3,3] 8 | Output: [1,2,3] 9 | */ 10 | 11 | // O(n) time | O(1) space where n is the number of nodes 12 | const deleteDuplicates = (head) => { 13 | let prev = null; 14 | let current = head; 15 | while (current) { 16 | if (prev) { 17 | while (current && current.val === prev.val) { 18 | current = current.next; 19 | } 20 | prev.next = current; 21 | } 22 | prev = current; 23 | current = !current ? null : current.next; 24 | } 25 | return head; 26 | }; 27 | -------------------------------------------------------------------------------- /Linked_Lists/remove-duplicatesII.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given the head of a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list. Return the linked list sorted as well. 3 | * 4 | * Input: head = [1,2,3,3,4,4,5] 5 | Output: [1,2,5] 6 | 7 | Input: head = [1,1,1,2,3] 8 | Output: [2,3] 9 | */ 10 | 11 | // O(n) time | O(1) space 12 | const deleteDuplicates = (head) => { 13 | const dummyNode = new ListNode(0, head); 14 | // last node before dupilicates 15 | let prev = dummyNode; 16 | let current = head; 17 | while (current) { 18 | // if beg, skip all duplicates 19 | if (current.next && current.next.val === current.val) { 20 | // move till the end of the duplicates 21 | while (current.next && current.next.val === current.val) { 22 | current = current.next; 23 | } 24 | // skip all duplicates 25 | prev.next = current.next; 26 | // you dont update the prev pointer becuase you could still have duplicates at where the prev pointer is currently pointing at 27 | } else { 28 | prev = prev.next; 29 | } 30 | // move current 31 | current = current.next; 32 | } 33 | return dummyNode.next; 34 | }; 35 | -------------------------------------------------------------------------------- /Linked_Lists/reorder.js: -------------------------------------------------------------------------------- 1 | /** 2 | *You are given the head of a singly linked-list. The list can be represented as: 3 | 4 | L0 → L1 → … → Ln - 1 → Ln 5 | Reorder the list to be on the following form: 6 | 7 | L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → … 8 | You may not modify the values in the list's nodes. Only nodes themselves may be changed. 9 | */ 10 | 11 | // O(n) time | O(1) space 12 | const reorderList = (head) => { 13 | // find the middle 14 | let slow = head; 15 | let fast = head; 16 | while (fast && fast.next) { 17 | slow = slow.next; 18 | fast = fast.next.next; 19 | } 20 | // reverse second half 21 | let prev = null; 22 | let current = slow; 23 | while (current) { 24 | const next = current.next; 25 | current.next = prev; 26 | prev = current; 27 | current = next; 28 | } 29 | 30 | // do merge 31 | let left = head; 32 | let right = prev; 33 | 34 | while (right.next) { 35 | const nextLeft = left.next; 36 | const nextRight = right.next; 37 | // merge logic 38 | left.next = right; 39 | right.next = nextLeft; 40 | // update pointers 41 | left = nextLeft; 42 | right = nextRight; 43 | } 44 | 45 | return head; 46 | }; 47 | -------------------------------------------------------------------------------- /Linked_Lists/reverse-linked-listII.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the head of a singly linked list and two integers left and right where left <= right, reverse the nodes of the list from position left to position right, and return the reversed list. 3 | */ 4 | 5 | // Approach: Dummy node 6 | // O(n) time | O(1) space 7 | const reverseBetween = (head, left, right) => { 8 | const dummyNode = new ListNode(0); 9 | dummyNode.next = head; 10 | let leftPrev = dummyNode; 11 | let current = head; 12 | // find the left pointer node 13 | let count = 1; 14 | while (count !== left) { 15 | leftPrev = current; 16 | current = current.next; 17 | count++; 18 | } 19 | // now our current pointer is at the left node, so we want to iterate 20 | let prev = null; 21 | while (count !== right + 1) { 22 | const next = current.next; 23 | current.next = prev; 24 | prev = current; 25 | current = next; 26 | count++; 27 | } 28 | // update pointers 29 | leftPrev.next.next = current; 30 | leftPrev.next = prev; 31 | 32 | return dummyNode.next; 33 | }; 34 | -------------------------------------------------------------------------------- /Linked_Lists/rotate-list.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the head of a linked list, rotate the list to the right by k places. 3 | 4 | 5 | 6 | */ 7 | 8 | // O(n) time | O(1) space 9 | const rotateRight = (head, k) => { 10 | if (!head) return null; 11 | // get length; 12 | const length = getLength(head); 13 | // mod k by length 14 | k = k % length; 15 | if (k === 0) return head; 16 | // get new head and track prev 17 | let prev = null; 18 | let current = head; 19 | let count = 0; 20 | let prevHead; 21 | let newHead; 22 | while (current) { 23 | if (count === length - k) { 24 | newHead = current; 25 | prevHead = prev; 26 | } 27 | count++; 28 | prev = current; 29 | current = current.next; 30 | } 31 | // update pointers 32 | // prev points to null 33 | prevHead.next = null; 34 | prev.next = head; 35 | 36 | return newHead; 37 | }; 38 | 39 | const getLength = (head) => { 40 | let length = 0; 41 | let current = head; 42 | while (current) { 43 | length++; 44 | current = current.next; 45 | } 46 | return length; 47 | }; 48 | -------------------------------------------------------------------------------- /Linked_Lists/sum-list.js: -------------------------------------------------------------------------------- 1 | /* 2 | Write a function, sumList, that takes in the head of a linked list containing numbers as an argument. 3 | The function should return the total sum of all values in the linked list. 4 | 5 | */ 6 | 7 | class Node { 8 | constructor(value) { 9 | this.value = value; 10 | this.next = null; 11 | } 12 | } 13 | 14 | // O(n) time | O(1) space 15 | const sumList = (head) => { 16 | // declare total and current node variable 17 | let current = head; 18 | let total = 0; 19 | // traverse list 20 | while (current) { 21 | total += current.value; 22 | current = current.next; 23 | } 24 | return total; 25 | }; 26 | -------------------------------------------------------------------------------- /Linked_Lists/swap-nodes.js: -------------------------------------------------------------------------------- 1 | /* 2 | You are given the head of a linked list, and an integer k. 3 | 4 | Return the head of the linked list after swapping the values of the kth node from the beginning and the kth node from the end (the list is 1-indexed). 5 | 6 | Input: head = [1,2,3,4,5], k = 2 7 | Output: [1,4,3,2,5] 8 | 9 | Input: head = [7,9,6,6,7,8,3,0,9,5], k = 5 10 | Output: [7,9,6,6,8,7,3,0,9,5] 11 | 12 | */ 13 | 14 | // O(n) time | O(1) space 15 | const swapNodes = (head, k) => { 16 | if (!head) return null; 17 | let length = 0; 18 | let firstNode; 19 | let current = head; 20 | while (current) { 21 | length++; 22 | if (length === k) firstNode = current; 23 | current = current.next; 24 | } 25 | let secondNode = head; 26 | let count = 0; 27 | while (count !== length - k) { 28 | count++; 29 | secondNode = secondNode.next; 30 | } 31 | const firstVal = firstNode.val; 32 | firstNode.val = secondNode.val; 33 | secondNode.val = firstVal; 34 | return head; 35 | }; 36 | -------------------------------------------------------------------------------- /Linked_Lists/swap-pairs.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a linked list, swap every two adjacent nodes and return its head. You must solve the problem without modifying the values in the list's nodes (i.e., only nodes themselves may be changed.) 3 | 4 | Input: head = [1,2,3,4] 5 | Output: [2,1,4,3] 6 | 7 | Input: head = [] 8 | Output: [] 9 | 10 | Input: head = [1] 11 | Output: [1] 12 | 13 | */ 14 | 15 | // O(n) time | O(1) space 16 | const swapPairs = (head) => { 17 | if (!head || !head.next) return head; 18 | const dummyNode = new ListNode(0); 19 | let tail = dummyNode; 20 | let p1 = head; 21 | let p2 = head.next; 22 | while (p1 && p2) { 23 | // grab next of p2 to not lose reference 24 | const next = p2.next; 25 | // update node pointers 26 | p2.next = p1; 27 | p1.next = next; 28 | tail.next = p2; 29 | // update tail, p1, p2 pointers 30 | tail = p1; 31 | p1 = next; 32 | p2 = p1 === null ? null : p1.next; 33 | } 34 | return dummyNode.next; 35 | }; 36 | -------------------------------------------------------------------------------- /Matrix/diagonal-sum.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given a square matrix mat, return the sum of the matrix diagonals. 3 | 4 | Only include the sum of all the elements on the primary diagonal and all the elements on the secondary diagonal that are not part of the primary diagonal. 5 | 6 | 7 | */ 8 | 9 | // O(n^2) time | O(1) space 10 | const diagonalSum = (mat) => { 11 | let sum = 0; 12 | for (let row = 0; row < mat.length; row++) { 13 | for (let col = 0; col < mat[0].length; col++) { 14 | if (row === col) { 15 | sum += mat[row][col]; 16 | continue; 17 | } 18 | if (row + col === mat.length - 1) sum += mat[row][col]; 19 | } 20 | } 21 | return sum; 22 | }; 23 | -------------------------------------------------------------------------------- /Matrix/equal-row-column.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a 0-indexed n x n integer matrix grid, return the number of pairs (ri, cj) such that row ri and column cj are equal. 3 | 4 | A row and column pair is considered equal if they contain the same elements in the same order (i.e., an equal array). 5 | 6 | Input: grid = [[3,2,1],[1,7,6],[2,7,7]] 7 | Output: 1 8 | Explanation: There is 1 equal row and column pair: 9 | - (Row 2, Column 1): [2,7,7] 10 | 11 | Input: grid = [[3,1,2,2],[1,4,4,5],[2,4,2,2],[2,4,2,2]] 12 | Output: 3 13 | Explanation: There are 3 equal row and column pairs: 14 | - (Row 0, Column 0): [3,1,2,2] 15 | - (Row 2, Column 2): [2,4,2,2] 16 | - (Row 3, Column 2): [2,4,2,2] 17 | 18 | */ 19 | 20 | // O(m * n) time | O(m * n) space 21 | const equalPairs = (grid) => { 22 | // put rows in a set 23 | const rows = {}; 24 | for (let row = 0; row < grid.length; row++) { 25 | let key = ''; 26 | for (let col = 0; col < grid[0].length; col++) { 27 | key += grid[row][col] + ','; 28 | } 29 | rows[key] = rows[key] + 1 || 1; 30 | } 31 | 32 | let count = 0; 33 | for (let col = 0; col < grid[0].length; col++) { 34 | let key = ''; 35 | for (let row = 0; row < grid.length; row++) { 36 | key += grid[row][col] + ','; 37 | } 38 | 39 | if (key in rows) count += rows[key]; 40 | } 41 | return count; 42 | }; 43 | -------------------------------------------------------------------------------- /Matrix/find-smallest-element.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an m x n matrix mat where every row is sorted in strictly increasing order, return the smallest common element in all rows. 3 | 4 | If there is no common element, return -1. 5 | 6 | Input: mat = [[1,2,3,4,5],[2,4,5,8,10],[3,5,7,9,11],[1,3,5,7,9]] 7 | Output: 5 8 | 9 | Input: mat = [[1,2,3],[2,3,4],[2,3,5]] 10 | Output: 2 11 | */ 12 | 13 | // O(m * n) time | O(n) space where m is the number of rows and n is the number of columns 14 | const smallestCommonElement = (mat) => { 15 | let currentSet = new Set([...mat[0]]); 16 | for (let row = 1; row < mat.length; row++) { 17 | const newSet = new Set(); 18 | for (let col = 0; col < mat[0].length; col++) { 19 | if (currentSet.has(mat[row][col])) { 20 | newSet.add(mat[row][col]); 21 | if (row === mat.length - 1) return mat[row][col]; 22 | } 23 | } 24 | currentSet = newSet; 25 | } 26 | return -1; 27 | }; 28 | -------------------------------------------------------------------------------- /Matrix/minimum-path-sum.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right, which minimizes the sum of all numbers along its path. 3 | 4 | Note: You can only move either down or right at any point in time. 5 | 6 | Input: grid = [[1,3,1],[1,5,1],[4,2,1]] 7 | Output: 7 8 | Explanation: Because the path 1 → 3 → 1 → 1 → 1 minimizes the sum. 9 | 10 | Input: grid = [[1,2,3],[4,5,6]] 11 | Output: 12 12 | 13 | */ 14 | 15 | // Approach: Dynamic Programming with constant space 16 | // O(n * m) time | O(1) space 17 | const minPathSum = (grid) => {}; 18 | 19 | // Approach: 2-D Dynamic Programming 20 | // O(n * m) time | O(n * m) space 21 | const minPathSum2 = (grid, row = 0, col = 0, memo = {}) => { 22 | const pos = row + ',' + col; 23 | if (pos in memo) return memo[pos]; 24 | if (row < 0 || row >= grid.length || col < 0 || col >= grid[0].length) 25 | return Infinity; 26 | if (row === grid.length - 1 && col === grid[0].length - 1) 27 | return grid[row][col]; 28 | 29 | const down = minPathSum(grid, row + 1, col, memo); 30 | const right = minPathSum(grid, row, col + 1, memo); 31 | 32 | memo[pos] = grid[row][col] + Math.min(down, right); 33 | return memo[pos]; 34 | }; 35 | -------------------------------------------------------------------------------- /Matrix/rotate-image.js: -------------------------------------------------------------------------------- 1 | /* 2 | You are given an n x n 2D matrix representing an image, rotate the image by 90 degrees (clockwise). 3 | 4 | You have to rotate the image in-place, which means you have to modify the input 2D matrix directly. 5 | DO NOT allocate another 2D matrix and do the rotation. 6 | 7 | Input: matrix = [[1,2,3],[4,5,6],[7,8,9]] 8 | Output: [[7,4,1],[8,5,2],[9,6,3]] 9 | 10 | */ 11 | // O(n^2) time | O(1) space 12 | const rotate = (matrix) => { 13 | // declare left and right pointers 14 | let l = 0; 15 | let r = matrix[0].length - 1; 16 | // while l < r 17 | while (l < r) { 18 | // declare top and bottom pointers 19 | let t = l; 20 | let b = r; 21 | // loop through columns 22 | for (let i = 0; i < r - l; i++) { 23 | // grab top left value 24 | const topLeftVal = matrix[t][l + i]; 25 | // move bottom left into top right 26 | matrix[t][l + i] = matrix[b - i][l]; 27 | //move bottom right into bottom left 28 | matrix[b - i][l] = matrix[b][r - i]; 29 | //move top right into bottom right 30 | matrix[b][r - i] = matrix[t + i][r]; 31 | //move top left value into top right 32 | matrix[t + i][r] = topLeftVal; 33 | } 34 | // update pointers 35 | l++; 36 | r--; 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /Matrix/spiral-matrixII.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given a positive integer n, generate an n x n matrix filled with elements from 1 to n2 in spiral order. 3 | */ 4 | 5 | // O(n^2) time | O(n^2) space 6 | const generateMatrix = (n) => { 7 | const output = Array.from(Array(n), () => Array(n)); 8 | 9 | let startRow = 0; 10 | let endRow = n - 1; 11 | let startCol = 0; 12 | let endCol = n - 1; 13 | let currentNum = 1; 14 | while (startRow <= endRow && startCol <= endCol) { 15 | for (let col = startCol; col <= endCol; col++) { 16 | output[startRow][col] = currentNum; 17 | currentNum++; 18 | } 19 | startRow++; 20 | 21 | for (let row = startRow; row <= endRow; row++) { 22 | output[row][endCol] = currentNum; 23 | currentNum++; 24 | } 25 | 26 | endCol--; 27 | 28 | for (let col = endCol; col >= startCol; col--) { 29 | output[endRow][col] = currentNum; 30 | currentNum++; 31 | } 32 | 33 | endRow--; 34 | for (let row = endRow; row >= startRow; row--) { 35 | output[row][startCol] = currentNum; 36 | currentNum++; 37 | } 38 | 39 | startCol++; 40 | } 41 | 42 | return output; 43 | }; 44 | -------------------------------------------------------------------------------- /Miscellaneous/add-digits.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given an integer num, repeatedly add all its digits until the result has only one digit, and return it. 3 | * 4 | * Input: num = 38 5 | Output: 2 6 | Explanation: The process is 7 | 38 --> 3 + 8 --> 11 8 | 11 --> 1 + 1 --> 2 9 | Since 2 has only one digit, return it. 10 | 11 | Input: num = 0 12 | Output: 0 13 | */ 14 | 15 | // O(1) time | O(1) space 16 | const addDigits = (num) => { 17 | if (num === 0) return 0; 18 | if (num % 9 === 0) return 9; 19 | return num % 9; 20 | }; 21 | -------------------------------------------------------------------------------- /Miscellaneous/count-primes.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an integer n, return the number of prime numbers that are strictly less than n. 3 | 4 | Input: n = 10 5 | Output: 4 6 | Explanation: There are 4 prime numbers less than 10, they are 2, 3, 5, 7. 7 | 8 | Input: n = 0 9 | Output: 0 10 | 11 | Input: n = 1 12 | Output: 0 13 | */ 14 | 15 | const countPrimes = (n) => { 16 | // create a primes array that sets all values to true 17 | const primes = new Array(n).fill(true); 18 | // set non primes to false 19 | for (let i = 2; i <= Math.sqrt(n); i++) { 20 | if (primes[i]) { 21 | for (let j = i; j * i < n; j++) { 22 | primes[i * j] = false; 23 | } 24 | } 25 | } 26 | // count number of primes in primes array 27 | let primesCount = 0; 28 | for (let i = 2; i < primes.length; i++) { 29 | if (primes[i]) primesCount++; 30 | } 31 | // return primesCount 32 | return primesCount; 33 | }; 34 | 35 | const countPrimes2 = (n) => { 36 | let count = 0; 37 | for (let i = 2; i < n; i++) { 38 | if (isPrime(i)) count++; 39 | } 40 | return count; 41 | }; 42 | 43 | const isPrime = (n) => { 44 | for (let i = 2; i <= Math.sqrt(n); i++) { 45 | if (n % i === 0) return false; 46 | } 47 | return true; 48 | }; 49 | -------------------------------------------------------------------------------- /Miscellaneous/fizz-buzz.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an integer n, return a string array answer (1-indexed) where: 3 | 4 | answer[i] == "FizzBuzz" if i is divisible by 3 and 5. 5 | answer[i] == "Fizz" if i is divisible by 3. 6 | answer[i] == "Buzz" if i is divisible by 5. 7 | answer[i] == i (as a string) if none of the above conditions are true. 8 | 9 | Input: n = 3 10 | Output: ["1","2","Fizz"] 11 | 12 | Input: n = 5 13 | Output: ["1","2","Fizz","4","Buzz"] 14 | 15 | Input: n = 15 16 | Output: ["1","2","Fizz","4","Buzz","Fizz","7","8","Fizz","Buzz","11","Fizz","13","14","FizzBuzz"] 17 | */ 18 | 19 | // O(n) time | O(1) space 20 | const fizzBuzz = (n) => { 21 | const output = []; 22 | 23 | for (let i = 1; i <= n; i++) { 24 | if (i % 15 === 0) { 25 | output.push('FizzBuzz'); 26 | } else if (i % 5 === 0) { 27 | output.push('Buzz'); 28 | } else if (i % 3 === 0) { 29 | output.push('Fizz'); 30 | } else { 31 | output.push(String(i)); 32 | } 33 | } 34 | 35 | return output; 36 | }; 37 | -------------------------------------------------------------------------------- /Miscellaneous/is-prime.js: -------------------------------------------------------------------------------- 1 | /* 2 | Write a function, isPrime, that takes in a number as an argument. 3 | The function should return a boolean indicating whether or not the given number is prime. 4 | 5 | A prime number is a number that is only divisible by two distinct numbers: 1 and itself. 6 | 7 | For example, 7 is a prime because it is only divisible by 1 and 7. 8 | For example, 6 is not a prime because it is divisible by 1, 2, 3, and 6. 9 | 10 | You can assume that the input number is a positive integer. 11 | */ 12 | 13 | const isPrime = (num) => { 14 | if (num < 2) return false; 15 | for (let i = 2; i <= Math.sqrt(num); i++) { 16 | if (num % i === 0) return false; 17 | } 18 | return true; 19 | }; 20 | -------------------------------------------------------------------------------- /Miscellaneous/matching-titles.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | 4 | */ 5 | // O(L^2 * T) time | O(T*L) space 6 | const matching = (titles, query) => { 7 | const matchingTitles = []; 8 | for (let title of titles) { 9 | if (check(title, query)) matchingTitles.push(title); 10 | } 11 | return matchingTitles; 12 | }; 13 | 14 | const check = (title, query) => { 15 | for (let i = 0; i < title.length; i++) { 16 | if (_matching(title.slice(i), query)) return true; 17 | } 18 | return false; 19 | }; 20 | 21 | const _matching = (title, query) => { 22 | let incorrect = 0; 23 | let i = 0; // title 24 | let j = 0; // query 25 | while (j < query.length) { 26 | if (title[i] === query[j]) { 27 | i++; 28 | j++; 29 | } else { 30 | incorrect++; 31 | i++; 32 | j++; 33 | } 34 | if (incorrect === 3) return false; 35 | } 36 | return j === query.length; 37 | }; 38 | -------------------------------------------------------------------------------- /Miscellaneous/medschool-matching.js: -------------------------------------------------------------------------------- 1 | /* 2 | Create algorithm that matches med school students for residency 3 | */ 4 | 5 | class Student { 6 | constructor(name) { 7 | this.name = name; 8 | this.ranks = {}; 9 | } 10 | } 11 | 12 | class School { 13 | constructor(name, capacity) { 14 | this.name = name; 15 | this.capacity = capacity; 16 | this.spots = []; 17 | 1; 18 | this.ranks = {}; 19 | } 20 | } 21 | 22 | const match = (students, schools) => {}; 23 | 24 | const toliver = new Student('toliver'); 25 | const duke = new School('Duke', 5); 26 | -------------------------------------------------------------------------------- /Miscellaneous/number-of-days.js: -------------------------------------------------------------------------------- 1 | /* 2 | Write a program to count the number of days between two dates. 3 | 4 | The two dates are given as strings, their format is YYYY-MM-DD as shown in the examples. 5 | 6 | Input: date1 = "2019-06-29", date2 = "2019-06-30" 7 | Output: 1 8 | 9 | Input: date1 = "2020-01-15", date2 = "2019-12-31" 10 | Output: 15 11 | */ 12 | 13 | // O(1) time | O(1) space 14 | const daysBetweenDates = (date1, date2) => { 15 | const millisecondsInADay = 1000 * 60 * 60 * 24; 16 | return Math.abs( 17 | (new Date(date1).getTime() - new Date(date2).getTime()) / millisecondsInADay 18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /Miscellaneous/power-of-three.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given an integer n, return true if it is a power of three. Otherwise, return false. 3 | 4 | An integer n is a power of three, if there exists an integer x such that n == 3x. 5 | */ 6 | 7 | // O(log3(n)) time | O(1) space 8 | const isPowerOfThree = (n) => { 9 | if (n <= 0) return false; 10 | let exp = 0; 11 | while (true) { 12 | const res = Math.pow(3, exp); 13 | if (res === n) return true; 14 | if (res > n) return false; 15 | exp++; 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /Miscellaneous/sum-multiples.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given a positive integer n, find the sum of all integers in the range [1, n] inclusive that are divisible by 3, 5, or 7. 3 | 4 | Return an integer denoting the sum of all numbers in the given range satisfying the constraint. 5 | 6 | Input: n = 7 7 | Output: 21 8 | Explanation: Numbers in the range [1, 7] that are divisible by 3, 5, or 7 are 3, 5, 6, 7. The sum of these numbers is 21. 9 | 10 | Input: n = 10 11 | Output: 40 12 | Explanation: Numbers in the range [1, 10] that are divisible by 3, 5, or 7 are 3, 5, 6, 7, 9, 10. The sum of these numbers is 40. 13 | 14 | Input: n = 9 15 | Output: 30 16 | Explanation: Numbers in the range [1, 9] that are divisible by 3, 5, or 7 are 3, 5, 6, 7, 9. The sum of these numbers is 30. 17 | */ 18 | 19 | const sumOfMultiples = (n) => { 20 | let sum = 0; 21 | for (let i = 1; i <= n; i++) { 22 | const threeDivisible = i % 3 === 0; 23 | const fiveDivisible = i % 5 === 0; 24 | const sevenDivisible = i % 7 === 0; 25 | if (threeDivisible || fiveDivisible || sevenDivisible) sum += i; 26 | } 27 | return sum; 28 | }; 29 | -------------------------------------------------------------------------------- /OA_Types/class-grouping.js: -------------------------------------------------------------------------------- 1 | /* 2 | Amazon Technical Academy provides in-demand, tehcnical training to current 3 | Amazon employees looking to broaden their skill sets. ATA has admited a group of 4 | n prospectives trainees with varying skills. To better accomodate the trainees, 5 | ATA has decided to create classes tailored to the skills levels. 6 | A placement examination will return a skill level that will be used to group the 7 | trainees into calsses, where levels[i] represent the skill level of trainee i. 8 | All traines within a clas smust have a skill level within the maxSpread. 9 | 10 | Determine the min number of classes that must be formed 11 | 12 | Sample input: 13 | const levels = [1, 4, 7, 3, 4] 14 | const spread = 2 15 | 16 | Sample output: 17 | 3 18 | */ 19 | 20 | //[1, 3, 4, 4, 7] 21 | const minClasses = (levels, spread) => { 22 | levels.sort((a, b) => a - b); 23 | let groups = 1; 24 | let minLevel = levels[0]; 25 | for (let i = 1; i < levels.length; i++) { 26 | if (levels[i] - minLevel >= spread) { 27 | groups++; 28 | minLevel = levels[i]; 29 | } 30 | } 31 | return groups; 32 | }; 33 | 34 | -------------------------------------------------------------------------------- /OA_Types/group-imbalance.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | There are 4 students whose class ranks are [ 2,3,1,4] . 4 | A group imbalance is when there exists a sequence where arr[i+1] - arr[i] > 1 . Find all imbalances in all possible sequences 5 | 6 | <> We need to consider contiguous sequence and NOT sub squence . 7 | Also we only need the total imbalance (integer) as the answer and not all the possible sequences 8 | 9 | 100 < n < 10^5 10 | 11 | Eg n = 4 12 | arr = [2,3,1,4] 13 | Possible subarray groups | rearranging | Imbalance 14 | [2] [2] 0 15 | [3] [3] 0 16 | [1] [1] 0 17 | [4] [4] 0 18 | [2,3] [2,3] 0 19 | [3,1] [1,3] 1 ( 3-1 = 2 >1) 20 | [1,4] [1,4] 1 (4-1 = 3 > 1) 21 | [2,3,1] [1,2,3] 0 22 | [3,1,4] [1,3,4] 1(3-1 =2 >1) 23 | [2,3,1,4] [1,2,3,4] 0 24 | 25 | Answe total imbalance = 3 26 | */ 27 | 28 | // O(n^3log(n)) time | O(n) space 29 | const imbalance = (ranks) => { 30 | let count = 0; 31 | for (let i = 0; i < ranks.length - 1; i++) { 32 | for (let j = i + 1; j < ranks.length; j++) { 33 | const arr = ranks.slice(i, j + 1); 34 | arr.sort((a, b) => a - b); 35 | for (let k = 1; k < arr.length; k++) { 36 | if (arr[k] - arr[k - 1] > 1) count++; 37 | } 38 | } 39 | } 40 | return count; 41 | }; 42 | -------------------------------------------------------------------------------- /OA_Types/maximum-teams.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Amazon is hosting a team hackathon. 4 | 5 | Each team will have exactly teamSize developers. 6 | A developer's skill level is denoted by skill[i]. 7 | The difference between the maximum and minimum skill levels within a team cannot exceed a threshold, maxDiff. 8 | Determine the maximum number of teams that can be formed from the contestants. 9 | 10 | Example 11 | skill = [3, 4, 3, 1, 6, 5], teamSize = 3, maxDiff = 2: 12 | At most, 2 teams can be formed: [3, 3, 1] and [4, 6, 5]. 13 | The difference between the maximum and minimum skill levels is 2 in each case, 14 | which does not exceed the threshold value of 2. 15 | 16 | 17 | */ 18 | 19 | // Approach: Sort to be able to easily determine if group is valid. [1, 3, 3, 4, 5, 6] 20 | // O(nlog(n)) time | O(1) space 21 | const teams = (skill, size, diff) => { 22 | // sort 23 | skill.sort((a, b) => a - b); 24 | let groups = 0; 25 | let i = size - 1; 26 | 27 | while (i < skill.length) { 28 | // check if current team is possible 29 | if (skill[i] - skill[i - size + 1] <= diff) { 30 | groups++; 31 | i += size - 1; 32 | } else { 33 | i++; 34 | } 35 | } 36 | 37 | return groups; 38 | }; 39 | -------------------------------------------------------------------------------- /OA_Types/merge-two-lists.js: -------------------------------------------------------------------------------- 1 | /* 2 | Merge two sorted linked list 3 | */ 4 | 5 | const mergeTwoLists = (list1, list2) => { 6 | // declare dummy node 7 | const dummyNode = new Node(null); 8 | let tail = dummyNode; 9 | let current1 = list1; 10 | let current2 = list2; 11 | 12 | while (current1 || current2) { 13 | const val1 = current1 ? current1.val : Infinity; 14 | const val2 = current2 ? current2.val : Infinity; 15 | if (val1 < val2) { 16 | tail.next = current1; 17 | current1 = current1.next; 18 | tail = tail.next; 19 | } else { 20 | tail.next = current2; 21 | current2 = current2.next; 22 | tail = tail.next; 23 | } 24 | } 25 | 26 | return dummyNode.next; 27 | }; 28 | -------------------------------------------------------------------------------- /OA_Types/minimum-bribes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | 5 | // O(n) time | O(1) space 6 | const minimumBribes = (q) => { 7 | // declare bribes variable 8 | let bribes = 0; 9 | // loop starting from back 10 | for (let i = q.length - 1; i >= 0; i--) { 11 | // check to see if current num is equal to index + 1 12 | if (q[i] !== i + 1) { 13 | // check to the element one place to left to see if is the correct number 14 | if (q[i - 1] === i + 1) { 15 | // add one to bribe and swap elements 16 | bribes++; 17 | [q[i - 1], q[i]] = [q[i], q[i + 1]]; 18 | } else if (q[i - 2] === i + 1) { 19 | // add two to bribe and swap the three elements 20 | bribes += 2; 21 | [q[i - 2], q[i - 1], q[i]] = [q[i - 1], q[i], q[i + 2]]; 22 | } else { 23 | console.log('Too chaotic'); 24 | return; 25 | } 26 | } 27 | } 28 | console.log(bribes); 29 | }; 30 | -------------------------------------------------------------------------------- /OA_Types/minimum-coin-flips.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the initial sequence of coins, find the minimum number of coins that can be flipped to obtain 3 | a beautiful sequence. All head facing coins or tails facing coins sequence is also valid 4 | */ 5 | 6 | const minCoinFlips = (string) => { 7 | // initialize flips and number of tails variable 8 | let flips = 0; 9 | let tails = 0; 10 | for (let char of string) { 11 | if (char === "T") { 12 | tails++; 13 | } else if (tails > 0) { 14 | flips++; 15 | tails--; 16 | } 17 | } 18 | return flips; 19 | }; 20 | -------------------------------------------------------------------------------- /OA_Types/shipment-imbalances.js: -------------------------------------------------------------------------------- 1 | /* 2 | Amazon logistics has multiple delivery centers from which products are sent. 3 | 4 | In one such delivery center, parcels are placed in a sequence where the i-th parcel has a weight of weight[i]. 5 | A shipment is constituted of a contiguous segment of parcels. The shipment imbalance of a shipment is defined 6 | as the difference between the max and min weights within a shipment. 7 | 8 | Given the arrangement of parcels, find the sum of shipment imbalance of all the shipments that can be formed 9 | from the given sequence of parcels. 10 | 11 | Input: 12 | List arrangement = {1, 2, 3} 13 | 14 | output: 4 15 | diff1 = 1 - 1 = 0; 16 | diff2 = 2 - 2 = 0; 17 | diff3 = 3 - 3 = 0; 18 | diff4 = 2 - 1 = 1; 19 | diff5 = 3 - 2 = 1; 20 | diff6 = 3 - 1 = 2; 21 | */ 22 | 23 | // Approach: This is subarray ranges problem 24 | const shipmentImbalance = (parcels) => { 25 | // declare count variable 26 | let count = 0; 27 | for (let i = 0; i < parcels.length; i++) { 28 | // declare max and min variabes 29 | let max = parcels[i]; 30 | let min = parcels[i]; 31 | for (let j = i + 1; j < parcels.length; j++) { 32 | max = Math.max(max, parcels[j]); 33 | min = Math.min(min, parcels[j]); 34 | count += max - min; 35 | } 36 | } 37 | return count; 38 | }; 39 | 40 | console.log(shipmentImbalance([1, 2, 3, 4])); 41 | -------------------------------------------------------------------------------- /OA_Types/top-k-elements.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an integer array nums and an integer k, return the k most frequent elements. 3 | You may return the answer in any order. 4 | 5 | Input: nums = [1,1,1,2,2,3], k = 2 6 | Output: [1,2] 7 | 8 | Input: nums = [1], k = 1 9 | Output: [1] 10 | */ 11 | 12 | const topKFrequent = (nums, k) => { 13 | // declare nums and bucket array 14 | const numsMap = {}; 15 | const buckets = []; 16 | // create nums map 17 | for (let num of nums) { 18 | numsMap[num] = numsMap[num] + 1 || 1; 19 | } 20 | // create buckets. Index will be frequence and value will be a set of the numbers that have that frequency 21 | for (let [key, value] of Object.entries(numsMap)) { 22 | if (!buckets[value]) { 23 | buckets[value] = new Set().add(key); 24 | } else { 25 | buckets[value].add(key); 26 | } 27 | } 28 | // declare output variable 29 | const output = []; 30 | // loop through buckets going backwards 31 | for (let i = buckets.length - 1; i >= 0; i--) { 32 | if (buckets[i]) { 33 | output.push(...buckets[i]); 34 | if (output.length === k) break; 35 | } 36 | } 37 | 38 | return output; 39 | }; 40 | 41 | const nums = [1, 1, 1, 2, 2, 3]; 42 | const k = 2; 43 | console.log(topKFrequent(nums, k)); 44 | -------------------------------------------------------------------------------- /OA_Types/two-sum.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Write a function that takes a list of numbers and a target number, 4 | and then returns the number of unique pairs that add up to the target number. 5 | 6 | [X, Y] and [Y, X] are considered the same pair, and not unique. 7 | 8 | Examples 9 | Example 1: 10 | Input: [1, 1, 2, 45, 46, 46], target = 47 11 | Output: 2 12 | Explanation: 13 | 1 + 46 = 47 14 | 15 | 2 + 45 = 47 16 | 17 | Example 2: 18 | Input: [1, 1], target = 2 19 | Output: 1 20 | Explanation: 21 | 1 + 1 = 2 22 | 23 | Example 3: 24 | Input: [1, 5, 1, 5], target = 6 25 | Output: 1 26 | Explanation: 27 | [1, 5] and [5, 1] are considered the same, therefore there is only one unique pair that adds up to 6. 28 | */ 29 | 30 | const twoSumUniquePairs = (nums, target) => { 31 | const diff = {}; 32 | const added = new Set(); 33 | let count = 0; 34 | for (let num of nums) { 35 | const difference = target - num; 36 | if (difference in diff && !added.has(difference) && !added.has(num)) { 37 | count++; 38 | added.add(num); 39 | } else { 40 | diff[num] = true; 41 | } 42 | } 43 | return count; 44 | }; 45 | 46 | console.log(twoSumUniquePairs([1, 2, 45, 1, 46, 46], 47)); 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # algorithms 2 | 3 | I love DS & algos so here is a repository of all the data structure & algorithm type questions I've answered with detailed explanations to solutions that I hope can help someone else out. 4 | 5 | Questions come from Leetcode, AlgoExpert, Structy, Elements of Programming Interviews and other miscellaneous sources. 6 | -------------------------------------------------------------------------------- /Recursion/generate-div-tags.js: -------------------------------------------------------------------------------- 1 | /* 2 | Write a function tht takes in a positive integer numberOfTags 3 | and returns a list of all the valid strings that you can generate 4 | with that number of matched
5 | 6 | */ 7 | 8 | // Approach: Brute-froce approach would be to generate all possible combinations and then check if valid 9 | // More optimal solution would be to only generate valid combos of div tags 10 | // O((2n)!/((n!((n + 1)!)))) time | O((2n)!/((n!((n + 1)!)))) space 11 | const generateDivTags = (numberOfTags) => { 12 | // delcare combos array 13 | const combos = []; 14 | // recursive helper function that will take empty string, 15 | // how many opening and closing parens you have and combos 16 | getCombos("", numberOfTags, numberOfTags, combos); 17 | return combos; 18 | }; 19 | 20 | const getCombos = (prefix, openingTags, closingTags, combos) => { 21 | // base case => if there are no more opening and closing parenthesis 22 | if (openingTags === 0 && closingTags === 0) combos.push(prefix); 23 | // if there are any opening parenthesis, place it 24 | if (openingTags > 0) { 25 | getCombos(prefix + "
", openingTags - 1, closingTags, combos); 26 | } 27 | // if opening < closing, then a closing parenthesis can be placed 28 | if (openingTags < closingTags) { 29 | getCombos(prefix + "
", openingTags, closingTags - 1, combos); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /Recursion/letters-combo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could represent. Return the answer in any order. 3 | 4 | A mapping of digits to letters (just like on the telephone buttons) is given below. Note that 1 does not map to any letters. 5 | 6 | Input: digits = "23" 7 | Output: ["ad","ae","af","bd","be","bf","cd","ce","cf"] 8 | 9 | Input: digits = "" 10 | Output: [] 11 | 12 | Input: digits = "2" 13 | Output: ["a","b","c"] 14 | */ 15 | 16 | // O(4^n * n) time | O(4^n * n) space where n is the length of digits 17 | const letterCombinations = (digits) => { 18 | if (!digits.length) return []; 19 | const map = { 20 | 2: ['a', 'b', 'c'], 21 | 3: ['d', 'e', 'f'], 22 | 4: ['g', 'h', 'i'], 23 | 5: ['j', 'k', 'l'], 24 | 6: ['m', 'n', 'o'], 25 | 7: ['p', 'q', 'r', 's'], 26 | 8: ['t', 'u', 'v'], 27 | 9: ['w', 'x', 'y', 'z'], 28 | }; 29 | const combos = []; 30 | getCombos(digits, '', map, combos); 31 | return combos; 32 | }; 33 | 34 | const getCombos = (digits, prefix, map, combos, i = 0) => { 35 | if (i === digits.length) { 36 | combos.push(prefix); 37 | return; 38 | } 39 | 40 | const digit = digits[i]; 41 | const letters = map[digit]; 42 | 43 | for (let letter of letters) { 44 | getCombos(digits, prefix + letter, map, combos, i + 1); 45 | } 46 | }; 47 | -------------------------------------------------------------------------------- /Searching/binary-search.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an array of integers nums which is sorted in ascending order, and an integer target, write a function to search target in nums. If target exists, then return its index. Otherwise, return -1. 3 | 4 | You must write an algorithm with O(log n) runtime complexity. 5 | 6 | Input: nums = [-1,0,3,5,9,12], target = 9 7 | Output: 4 8 | Explanation: 9 exists in nums and its index is 4 9 | Input: nums = [-1,0,3,5,9,12], target = 2 10 | Output: -1 11 | Explanation: 2 does not exist in nums so return -1 12 | 13 | */ 14 | 15 | // O(log(n)) time | O(1) space 16 | const search = (nums, target) => { 17 | let l = 0; 18 | let r = nums.length - 1; 19 | while (l <= r) { 20 | const mid = Math.floor((l + r) / 2); 21 | if (nums[mid] === target) return mid; 22 | if (nums[mid] < target) { 23 | l = mid + 1; 24 | } else { 25 | r = mid - 1; 26 | } 27 | } 28 | return -1; 29 | }; 30 | -------------------------------------------------------------------------------- /Searching/find-k-closest-elements.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a sorted integer array arr, two integers k and x, return the k closest integers to x in the array. The result should also be sorted in ascending order. 3 | 4 | An integer a is closer to x than an integer b if: 5 | 6 | |a - x| < |b - x|, or 7 | |a - x| == |b - x| and a < b 8 | 9 | Input: arr = [1,2,3,4,5], k = 4, x = 3 10 | Output: [1,2,3,4] 11 | 12 | Input: arr = [1,2,3,4,5], k = 4, x = -1 13 | Output: [1,2,3,4] 14 | 15 | 16 | */ 17 | 18 | // Approach: Modified Binary Search 19 | // O(log(n - k) + k) time | O(k) space 20 | const findClosestElements = (arr, k, x) => { 21 | // find the left most idx 22 | let leftIdx = -1; 23 | let l = 0; 24 | let r = arr.length - k; 25 | 26 | while (l < r) { 27 | const mid = Math.floor((l + r) / 2); 28 | if (x - arr[mid] > arr[mid + k] - x) { 29 | l = mid + 1; 30 | } else { 31 | r = mid; 32 | } 33 | } 34 | const output = []; 35 | for (let i = l; i < l + k; i++) { 36 | output.push(arr[i]); 37 | } 38 | return output; 39 | }; 40 | -------------------------------------------------------------------------------- /Searching/find-minII.js: -------------------------------------------------------------------------------- 1 | /* 2 | Suppose an array of length n sorted in ascending order is rotated between 1 and n times. For example, the array nums = [0,1,4,4,5,6,7] might become: 3 | 4 | [4,5,6,7,0,1,4] if it was rotated 4 times. 5 | [0,1,4,4,5,6,7] if it was rotated 7 times. 6 | Notice that rotating an array [a[0], a[1], a[2], ..., a[n-1]] 1 time results in the array [a[n-1], a[0], a[1], a[2], ..., a[n-2]]. 7 | 8 | Given the sorted rotated array nums that may contain duplicates, return the minimum element of this array. 9 | 10 | You must decrease the overall operation steps as much as possible. 11 | 12 | */ 13 | 14 | // O(log(n)) time | O(1) space 15 | const findMin = (nums) => { 16 | let low = 0; 17 | let high = nums.length - 1; 18 | while (low < high) { 19 | const mid = Math.floor((low + high) / 2); 20 | if (nums[mid] > nums[high]) { 21 | low = mid + 1; 22 | } else if (nums[mid] < nums[high]) { 23 | high = mid; 24 | } else { 25 | high--; 26 | } 27 | } 28 | return nums[low]; 29 | }; 30 | -------------------------------------------------------------------------------- /Searching/first-bad-version.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | You are a product manager and currently leading a team to develop a new product. Unfortunately, the latest version of your product fails the quality check. Since each version is developed based on the previous version, all the versions after a bad version are also bad. 4 | 5 | Suppose you have n versions [1, 2, ..., n] and you want to find out the first bad one, which causes all the following ones to be bad. 6 | 7 | You are given an API bool isBadVersion(version) which returns whether version is bad. Implement a function to find the first bad version. You should minimize the number of calls to the API. 8 | 9 | Input: n = 5, bad = 4 10 | Output: 4 11 | Explanation: 12 | call isBadVersion(3) -> false 13 | call isBadVersion(5) -> true 14 | call isBadVersion(4) -> true 15 | Then 4 is the first bad version. 16 | */ 17 | 18 | const solution = function (isBadVersion) { 19 | /** 20 | * @param {integer} n Total versions 21 | * @return {integer} The first bad version 22 | */ 23 | return function (n) { 24 | let l = 1; 25 | let r = n; 26 | while (l <= r) { 27 | const mid = Math.floor((l + r) / 2); 28 | if (isBadVersion(mid)) { 29 | r = mid - 1; 30 | } else { 31 | l = mid + 1; 32 | } 33 | } 34 | 35 | return l; 36 | }; 37 | }; 38 | -------------------------------------------------------------------------------- /Searching/search-insert-position.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given a sorted array of distinct integers and a target value, return the index if the target is found. 3 | * If not, return the index where it would be if it were inserted in order. 4 | * You must write an algorithm with O(log n) runtime complexity. 5 | * 6 | * Input: nums = [1,3,5,6], target = 5 Output: 2 7 | * Input: nums = [1,3,5,6], target = 2 Output: 1 8 | */ 9 | 10 | // Approach: Binary search and return l if target is not found 11 | // O(log(n)) time | O(1) space 12 | const searchInsert = (nums, target) => { 13 | let l = 0; 14 | let r = nums.length - 1; 15 | while (l <= r) { 16 | const mid = Math.floor((l + r) / 2); 17 | if (nums[mid] === target) return mid; 18 | if (nums[mid] > target) { 19 | r = mid - 1; 20 | } else { 21 | l = mid + 1; 22 | } 23 | } 24 | return l; 25 | }; 26 | 27 | console.log(searchInsert([1, 3, 5, 6], 100)); 28 | -------------------------------------------------------------------------------- /Searching/search-matrixII.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Write an efficient algorithm that searches for a value target in an m x n integer matrix matrix. This matrix has the following properties: 3 | 4 | Integers in each row are sorted in ascending from left to right. 5 | Integers in each column are sorted in ascending from top to bottom. 6 | 7 | Input: matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5 8 | Output: true 9 | 10 | Input: matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 20 11 | Output: false 12 | */ 13 | 14 | // O(n + m) time | O(1) space 15 | const searchMatrix = (matrix, target) => { 16 | let row = 0; 17 | let col = matrix[0].length - 1; 18 | while (row < matrix.length && col >= 0) { 19 | const num = matrix[row][col]; 20 | if (num === target) return true; 21 | if (num < target) { 22 | row++; 23 | } else { 24 | col--; 25 | } 26 | } 27 | return false; 28 | }; 29 | -------------------------------------------------------------------------------- /Searching/single-element.js: -------------------------------------------------------------------------------- 1 | /* 2 | You are given a sorted array consisting of only integers where every 3 | element appears exactly twice, 4 | except for one element which appears exactly once. 5 | 6 | Return the single element that appears only once. 7 | 8 | Your solution must run in O(log n) time and O(1) space. 9 | 10 | Input: nums = [1,1,2,3,3,4,4,8,8] 11 | Output: 2 12 | 13 | Input: nums = [3,3,7,7,10,11,11] 14 | Output: 10 15 | */ 16 | 17 | // Approach: Modified binary search 18 | // O(log(n)) time | O(1) space 19 | const singleNonDuplicate = (nums) => { 20 | // declare two pointers 21 | let l = 0; 22 | let r = nums.length - 1; 23 | while (l <= r) { 24 | // get midpoint 25 | const mid = Math.floor((l + r) / 2); 26 | // check to see if right side if even because this will determine where o go 27 | const rightEven = (r - mid) % 2 === 0; 28 | if (nums[mid - 1] === nums[mid]) { 29 | if (rightEven) { 30 | r = mid - 2; 31 | } else { 32 | l = mid + 1; 33 | } 34 | } else if (nums[mid + 1] === nums[mid]) { 35 | if (rightEven) { 36 | l = mid + 2; 37 | } else { 38 | r = mid - 1; 39 | } 40 | } else { 41 | return nums[mid]; 42 | } 43 | } 44 | 45 | return nums[l]; 46 | }; 47 | -------------------------------------------------------------------------------- /Sorting/bucket-sort.js: -------------------------------------------------------------------------------- 1 | /* Implement bucket sort */ 2 | 3 | // O(n) time | O(1) space where n is the length of the array 4 | const bucketSort = (arr) => { 5 | // Assuming arr only contains 0, 1 or 2 6 | const counts = [0, 0, 0]; 7 | 8 | // Count the quantity of each val in arr 9 | for (let i = 0; i < arr.length; i++) { 10 | counts[arr[i]] += 1; 11 | } 12 | 13 | // Fill each bucket in the original array 14 | let i = 0; 15 | for (let n = 0; n < counts.length; n++) { 16 | for (let j = 0; j < counts[n]; j++) { 17 | arr[i] = n; 18 | i++; 19 | } 20 | } 21 | return arr; 22 | }; 23 | -------------------------------------------------------------------------------- /Sorting/insertion-sort.js: -------------------------------------------------------------------------------- 1 | /* Insertion sort*/ 2 | 3 | // O(n^2) time | O(1) space where n is the length of the array 4 | const insertionSort = (array) => { 5 | for (let i = 1; i < array.length; i++) { 6 | let j = i - 1; 7 | while (j >= 0 && array[j] > array[j + 1]) { 8 | // swap 9 | [array[j], array[j + 1]] = [array[j + 1], array[j]]; 10 | j--; 11 | } 12 | } 13 | return array; 14 | }; 15 | -------------------------------------------------------------------------------- /Sorting/sort-colors.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given an array nums with n objects colored red, white, or blue, sort them in-place so that objects of the same color are adjacent, with the colors in the order red, white, and blue. 3 | 4 | We will use the integers 0, 1, and 2 to represent the color red, white, and blue, respectively. 5 | 6 | You must solve this problem without using the library's sort function. 7 | 8 | Input: nums = [2,0,2,1,1,0] 9 | Output: [0,0,1,1,2,2] 10 | 11 | Input: nums = [2,0,1] 12 | Output: [0,1,2] 13 | */ 14 | 15 | // O(n) time | O(1) space 16 | const sortColors = (nums) => { 17 | // decalre counts array that will store the number of time a number appears in the array 18 | const counts = [0, 0, 0]; 19 | for (let num of nums) { 20 | counts[num] += 1; 21 | } 22 | // sort array in place 23 | let idx = 0; 24 | for (let i = 0; i < counts.length; i++) { 25 | for (let j = 0; j < counts[i]; j++) { 26 | nums[idx] = i; 27 | idx++; 28 | } 29 | } 30 | // return nums 31 | return nums; 32 | }; 33 | -------------------------------------------------------------------------------- /Sorting/wiggle-sort.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an integer array nums, reorder it such that nums[0] <= nums[1] >= nums[2] <= nums[3].... 3 | 4 | You may assume the input array always has a valid answer. 5 | 6 | Input: nums = [3,5,2,1,6,4] 7 | Output: [3,5,1,6,2,4] 8 | Explanation: [1,6,2,5,3,4] is also accepted. 9 | 10 | Input: nums = [6,6,5,6,3,8] 11 | Output: [6,6,5,6,3,8] 12 | */ 13 | 14 | // O(n) time | O(1) space 15 | const wiggleSort = (nums) => { 16 | for (let i = 0; i < nums.length - 1; i++) { 17 | if ( 18 | (i % 2 === 0 && nums[i] > nums[i + 1]) || 19 | (i % 2 === 1 && nums[i] < nums[i + 1]) 20 | ) { 21 | // swap 22 | [nums[i], nums[i + 1]] = [nums[i + 1], nums[i]]; 23 | } 24 | } 25 | return nums; 26 | }; 27 | -------------------------------------------------------------------------------- /Stacks/daily-temperatures.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given an array of integers temperatures represents the daily temperatures, return an array answer such that answer[i] is the number of days you have to wait after the ith day to get a warmer temperature. If there is no future day for which this is possible, keep answer[i] == 0 instead. 3 | * 4 | * Input: temperatures = [73,74,75,71,69,72,76,73] 5 | Output: [1,1,4,2,1,1,0,0] 6 | 7 | Input: temperatures = [30,40,50,60] 8 | Output: [1,1,1,0] 9 | 10 | Input: temperatures = [30,60,90] 11 | Output: [1,1,0] 12 | */ 13 | 14 | // O(n) time | O(n) space 15 | const dailyTemperatures = (temperatures) => { 16 | const output = new Array(temperatures.length).fill(0); 17 | const stack = [0]; 18 | for (let i = 1; i < temperatures.length * 2; i++) { 19 | const circIdx = i % temperatures.length; 20 | while ( 21 | stack.length > 0 && 22 | temperatures[circIdx] > temperatures[stack[stack.length - 1]] 23 | ) { 24 | const idxRemoved = stack.pop(); 25 | if (circIdx > idxRemoved) output[idxRemoved] = circIdx - idxRemoved; 26 | } 27 | stack.push(circIdx); 28 | } 29 | return output; 30 | }; 31 | -------------------------------------------------------------------------------- /Stacks/remove-k-digits.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given string num representing a non-negative integer num, and an integer k, return the smallest possible integer after removing k digits from num. 3 | 4 | Input: num = "1432219", k = 3 5 | Output: "1219" 6 | Explanation: Remove the three digits 4, 3, and 2 to form the new number 1219 which is the smallest. 7 | 8 | Input: num = "10200", k = 1 9 | Output: "200" 10 | Explanation: Remove the leading 1 and the number is 200. Note that the output must not contain leading zeroes. 11 | 12 | Input: num = "10", k = 2 13 | Output: "0" 14 | Explanation: Remove all the digits from the number and it is left with nothing which is 0. 15 | 16 | */ 17 | 18 | // O(n) time | O(n) space 19 | const removeKDigits = (num, k) => { 20 | if (k >= num.length) return '0'; 21 | const stack = []; 22 | let removed = 0; 23 | for (let digit of num) { 24 | while ( 25 | stack.length && 26 | Number(stack[stack.length - 1]) > Number(digit) && 27 | removed < k 28 | ) { 29 | stack.pop(); 30 | removed++; 31 | } 32 | stack.push(digit); 33 | } 34 | while (removed < k) { 35 | stack.pop(); 36 | removed++; 37 | } 38 | 39 | const temp = stack.join(''); 40 | // find where leading 0s stop 41 | let i = 0; 42 | while (temp[i] === '0') { 43 | i++; 44 | } 45 | const res = temp.slice(i); 46 | return res === '' ? '0' : res; 47 | }; 48 | -------------------------------------------------------------------------------- /Stacks/stack.js: -------------------------------------------------------------------------------- 1 | /* Implement a stack class*/ 2 | 3 | class Stack { 4 | constructor() { 5 | this.stack = new Array(); 6 | } 7 | 8 | push(n) { 9 | this.stack.push(n); 10 | } 11 | 12 | pop() { 13 | return this.stack.pop(); 14 | } 15 | 16 | size() { 17 | return this.stack.length; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Stacks/valid-subarrays.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an integer array nums, return the number of non-empty subarrays with the leftmost element of the subarray not larger than other elements in the subarray. 3 | 4 | A subarray is a contiguous part of an array. 5 | 6 | Input: nums = [1,4,2,5,3] 7 | Output: 11 8 | Explanation: There are 11 valid subarrays: [1],[4],[2],[5],[3],[1,4],[2,5],[1,4,2],[2,5,3],[1,4,2,5],[1,4,2,5,3]. 9 | 10 | Input: nums = [3,2,1] 11 | Output: 3 12 | Explanation: The 3 valid subarrays are: [3],[2],[1]. 13 | 14 | 15 | */ 16 | 17 | // O(n) time | O(1) space 18 | const validSubarrays = (nums) => { 19 | let count = 0; 20 | for (let i = 0; i < nums.length; i++) { 21 | count++; 22 | let nextIdx = i + 1; 23 | while (nums[nextIdx] >= nums[i]) { 24 | count++; 25 | nextIdx++; 26 | } 27 | } 28 | return count; 29 | }; 30 | -------------------------------------------------------------------------------- /Stacks/validate-stack-sequences.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given two integer arrays pushed and popped each with distinct values, return true if this could have been the result of a sequence of push and pop operations on an initially empty stack, or false otherwise. 3 | 4 | Input: pushed = [1,2,3,4,5], popped = [4,5,3,2,1] 5 | Output: true 6 | Explanation: We might do the following sequence: 7 | push(1), push(2), push(3), push(4), 8 | pop() -> 4, 9 | push(5), 10 | pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1 11 | 12 | Input: pushed = [1,2,3,4,5], popped = [4,3,5,1,2] 13 | Output: false 14 | Explanation: 1 cannot be popped before 2. 15 | */ 16 | 17 | // O(n) time | O(n) space 18 | const validateStackSequences = (pushed, popped) => { 19 | const stack = []; 20 | let i = 0; 21 | let j = 0; 22 | while (i <= pushed.length && j < popped.length) { 23 | // check if stack is not empty and top of stack does not euqal current index of pop, then push 24 | if (!stack.length || stack[stack.length - 1] !== popped[j]) { 25 | // push a number into stack and move i pointer 26 | stack.push(pushed[i]); 27 | i++; 28 | } else { 29 | // pop and move j pointer 30 | stack.pop(); 31 | j++; 32 | } 33 | } 34 | 35 | return stack.length === 0; 36 | }; 37 | -------------------------------------------------------------------------------- /Strings/balanced-brackets.js: -------------------------------------------------------------------------------- 1 | /* 2 | Write a function, befittingBrackets, that takes in a string as an argument. 3 | The function should return a boolean indicating whether or not the string 4 | contains correctly matched brackets. 5 | 6 | You may assume the string contains only characters: ( ) [ ] { } 7 | */ 8 | 9 | // O(n) time | O(n) space where n is the length of string 10 | const balancedBrackets = (string) => { 11 | // declare a dictionary to hold valid characters 12 | const dict = { '(': ')', '[': ']', '{': '}' }; 13 | // declare stack to track which chars we have seen 14 | const stack = []; 15 | for (let char of string) { 16 | // if the char is in the dictionary, then we want to add its value pair to stack 17 | if (char in dict) { 18 | stack.push(dict[char]); 19 | } else if (char === ')' || char === '}' || char === ']') { 20 | // if stack is empty, return false 21 | if (!stack.length) return false; 22 | // we need to check if the last char that was popped off from stack equals current char 23 | const lastChar = stack.pop(); 24 | if (char !== lastChar) return false; 25 | } 26 | } 27 | // return true if the stack length is 0 28 | return stack.length === 0; 29 | }; 30 | -------------------------------------------------------------------------------- /Strings/excel-columns.js: -------------------------------------------------------------------------------- 1 | /* excel columns */ 2 | 3 | // O(n) time | O(1) space 4 | const excel = (str) => { 5 | let number = 0; 6 | // iterate through the string 7 | for (let i = 0; i < str.length; i++) { 8 | // grab current char 9 | const char = str[i]; 10 | // multiply current num by 26 11 | number *= 26; 12 | // get the "distance" 13 | number += char.charCodeAt() - 'A'.charCodeAt() + 1; 14 | } 15 | return number; 16 | }; 17 | -------------------------------------------------------------------------------- /Strings/first-unique-character.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a string s, find the first non-repeating character in it and return its index. 3 | If it does not exist, return -1. 4 | 5 | Input: s = "leetcode" 6 | Output: 0 7 | 8 | Input: s = "loveleetcode" 9 | Output: 2 10 | 11 | Input: s = "aabb" 12 | Output: -1 13 | */ 14 | 15 | // O(n) time | O(1) space becuase there are 26 letters in alphabet 16 | const firstUniqChar = (s) => { 17 | // create map of counts 18 | const counts = {}; 19 | for (let char of s) { 20 | counts[char] = (counts[char] || 0) + 1; 21 | } 22 | // loop through s and check count in counts map and if 1 return 23 | for (let i = 0; i < s.length; i++) { 24 | if (counts[s[i]] === 1) return i; 25 | } 26 | return -1; 27 | }; 28 | -------------------------------------------------------------------------------- /Strings/flip-to-monotone-increasing.js: -------------------------------------------------------------------------------- 1 | /* 2 | A binary string is monotone increasing if it consists of some number of 0's (possibly none), 3 | followed by some number of 1's (also possibly none). 4 | 5 | You are given a binary string s. You can flip s[i] changing it from 0 to 1 or from 1 to 0. 6 | 7 | Return the minimum number of flips to make s monotone increasing. 8 | 9 | Input: s = "00110" 10 | Output: 1 11 | Explanation: We flip the last digit to get 00111. 12 | 13 | Input: s = "010110" 14 | Output: 2 15 | Explanation: We flip to get 011111, or alternatively 000111. 16 | 17 | Input: s = "00011000" 18 | Output: 2 19 | Explanation: We flip to get 00000000. 20 | */ 21 | 22 | // O(n) time | O(1) space 23 | const minFlipsMonoIncr = (s) => { 24 | // declare number of flips and number of ones variables 25 | let flips = 0; 26 | let ones = 0; // these are our potential flips 27 | // loop through string 28 | for (let char of s) { 29 | // if char is 1, just increment one count 30 | if (char === "1") { 31 | // increment ones 32 | ones++; 33 | } else if (ones > 0) { 34 | // if char is 0 and one is greater than zero then we have to make a flip 35 | flips++; 36 | ones--; 37 | } 38 | } 39 | return flips; 40 | }; 41 | -------------------------------------------------------------------------------- /Strings/is-anagram.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given two strings s and t, return true if t is an anagram of s, and false otherwise. 3 | 4 | An Anagram is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all the original letters exactly once. 5 | 6 | Input: s = "anagram", t = "nagaram" 7 | Output: true 8 | 9 | Input: s = "rat", t = "car" 10 | Output: false 11 | */ 12 | 13 | // O(n + m) time | O(n) space where n is length of s and m is length of t 14 | const isAnagram = (s, t) => { 15 | // put char count of s in map 16 | const sMap = {}; 17 | for (let char of s) { 18 | sMap[char] = (sMap[char] || 0) + 1; 19 | } 20 | 21 | // reduce sMap with char of t 22 | for (let char of t) { 23 | if (!(char in sMap)) return false; 24 | sMap[char]--; 25 | } 26 | 27 | // check to see that all characters are 0 28 | for (let char in sMap) { 29 | if (sMap[char] !== 0) return false; 30 | } 31 | 32 | return true; 33 | }; 34 | -------------------------------------------------------------------------------- /Strings/is-palindrome.js: -------------------------------------------------------------------------------- 1 | /* 2 | Write a function that takes in a strings and returns whether or not it is a palindrome 3 | */ 4 | 5 | // Approach: Iterative 6 | // O(n) time | O(1) space 7 | const isPalindrome = (string) => { 8 | let l = 0; 9 | let r = string.length - 1; 10 | while (l <= r) { 11 | if (string[l] !== string[r]) return false; 12 | l++; 13 | r--; 14 | } 15 | return true; 16 | }; 17 | 18 | // Approach: Recursive 19 | // O(n) time | O(n) space where n is length of string 20 | const isPalindrome2 = (string, i = 0, j = string.length - 1) => { 21 | if (i > j) return true; 22 | if (string[i] !== string[j]) return false; 23 | return isPalindrome2(string, i + 1, j - 1); 24 | }; 25 | -------------------------------------------------------------------------------- /Strings/longest-common-prefix.js: -------------------------------------------------------------------------------- 1 | /* 2 | Write a function to find the longest common prefix string amongst an array of strings. 3 | 4 | If there is no common prefix, return an empty string "". 5 | 6 | Input: strs = ["flower","flow","flight"] 7 | Output: "fl" 8 | 9 | Input: strs = ["dog","racecar","car"] 10 | Output: "" 11 | Explanation: There is no common prefix among the input strings. 12 | */ 13 | 14 | // O(n*m) time | O(m) space where n is length of strs array and m is length of longest string 15 | const longestCommonPrefix = (strs) => { 16 | let output = ""; 17 | let idx = 0; 18 | // you can go until an arbitray string length 19 | while (idx < strs[0].length) { 20 | for (let i = 1; i < strs.length; i++) { 21 | if (strs[i][idx] !== strs[i - 1][idx]) return output; 22 | } 23 | output += strs[0][idx]; 24 | idx++; 25 | } 26 | 27 | return output; 28 | }; 29 | 30 | -------------------------------------------------------------------------------- /Strings/longest-substr-k-chars.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a string s and an integer k, return the length of the longest 3 | substring 4 | of s that contains at most k distinct characters. 5 | 6 | Input: s = "eceba", k = 2 7 | Output: 3 8 | Explanation: The substring is "ece" with length 3. 9 | 10 | Input: s = "aa", k = 1 11 | Output: 2 12 | Explanation: The substring is "aa" with length 2. 13 | 14 | 15 | */ 16 | 17 | // Approach: Sliding Window 18 | // O(n) time | O(1) space 19 | const lengthOfLongestSubstringKDistinct = (s, k) => { 20 | const freqMap = {}; 21 | let distinct = 0; // distinct character count 22 | let longest = 0; // length of longest valid string 23 | let l = 0; // left pointer 24 | for (let r = 0; r < s.length; r++) { 25 | const char = s[r]; // grab current char 26 | // if char is not in freqMap or count euqal to 0, increment distinct count 27 | if (!(char in freqMap) || freqMap[char] === 0) distinct++; 28 | freqMap[char] = freqMap[char] + 1 || 1; 29 | // while distinct count > k, shrink window 30 | while (distinct > k) { 31 | freqMap[s[l]]--; 32 | if (freqMap[s[l]] === 0) distinct--; 33 | l++; 34 | } 35 | // max logic 36 | longest = Math.max(longest, r - l + 1); 37 | } 38 | // return longest 39 | return longest; 40 | }; 41 | -------------------------------------------------------------------------------- /Strings/longest-substring-length.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a string s, find the length of the longest 3 | substring without repeating characters. 4 | 5 | Input: s = "abcabcbb" 6 | Output: 3 7 | Explanation: The answer is "abc", with the length of 3. 8 | 9 | Input: s = "bbbbb" 10 | Output: 1 11 | Explanation: The answer is "b", with the length of 1. 12 | */ 13 | 14 | // O(n) time | O(min(n, a)) space where n is length of string and 15 | // a is number of characters in alphabet 16 | const lengthOfLongestSubstring = (s) => { 17 | // declare last seen map 18 | const lastSeen = {}; 19 | // declare longest and start index variables 20 | let longest = 0; 21 | let startIdx = 0; 22 | for (let i = 0; i < s.length; i++) { 23 | const char = s[i]; 24 | // check to see if in seen and update start accordingly 25 | if (char in lastSeen) { 26 | startIdx = Math.max(startIdx, lastSeen[char] + 1); 27 | } 28 | // update longest 29 | longest = Math.max(longest, i - startIdx + 1); 30 | // update char in seen map 31 | lastSeen[char] = i; 32 | } 33 | return longest; 34 | }; 35 | -------------------------------------------------------------------------------- /Strings/longest-substring.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a string s, find the longest 3 | substring without repeating characters. 4 | 5 | Input: s = "abcabcbb" 6 | Output: "abc" 7 | 8 | Input: s = "bbbbb" 9 | Output: "b" 10 | 11 | Input: s = "clementisacap" 12 | Output: "mentisac" 13 | */ 14 | 15 | // O(n) time | O(min(n, a)) space where n is length of string and a is number of characters in alphabet 16 | const longestSubstring = (s) => { 17 | const lastSeen = {}; 18 | let index = [0, 1]; 19 | let startIdx = 0; 20 | for (let i = 0; i < s.length; i++) { 21 | const char = s[i]; 22 | // check to see if in seen and update start accordingly 23 | if (char in lastSeen) { 24 | startIdx = Math.max(startIdx, lastSeen[char] + 1); 25 | } 26 | // update index 27 | if (index[1] - index[0] < i + 1 - startIdx) { 28 | index[0] = startIdx; 29 | index[1] = i + 1; 30 | } 31 | // update char in seen map 32 | lastSeen[char] = i; 33 | } 34 | return s.slice(index[0], index[1]); 35 | }; 36 | -------------------------------------------------------------------------------- /Strings/max-num-vowels.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a string s and an integer k, return the maximum number of vowel letters in any substring of s with length k. 3 | 4 | Vowel letters in English are 'a', 'e', 'i', 'o', and 'u'. 5 | 6 | Input: s = "abciiidef", k = 3 7 | Output: 3 8 | Explanation: The substring "iii" contains 3 vowel letters. 9 | 10 | Input: s = "aeiou", k = 2 11 | Output: 2 12 | Explanation: Any substring of length 2 contains 2 vowels. 13 | 14 | Input: s = "leetcode", k = 3 15 | Output: 2 16 | Explanation: "lee", "eet" and "ode" contain 2 vowels. 17 | 18 | */ 19 | 20 | // Approach: Sliding window 21 | // O(n) time | O(1) space 22 | const maxVowels = (s, k) => { 23 | const vowels = new Set(['a', 'e', 'i', 'o', 'u']); 24 | let count = 0; 25 | // initialize window 26 | for (let i = 0; i < k; i++) { 27 | if (vowels.has(s[i])) count++; 28 | } 29 | // declare pointers and max variable 30 | let max = count; 31 | let i = 1; 32 | let j = k; 33 | while (j < s.length) { 34 | // check if char at i - 1 was a vowel. If so, remove 35 | if (vowels.has(s[i - 1])) count--; 36 | // check if chat at j is a vowel. If so, add to count 37 | if (vowels.has(s[j])) count++; 38 | // update max 39 | max = Math.max(max, count); 40 | // update pointers 41 | i++; 42 | j++; 43 | } 44 | return max; 45 | }; 46 | -------------------------------------------------------------------------------- /Strings/optimal-partition-of-string.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a string s, partition the string into one or more substrings such that the characters in each substring are unique. That is, no letter appears in a single substring more than once. 3 | 4 | Return the minimum number of substrings in such a partition. 5 | 6 | Note that each character should belong to exactly one substring in a partition. 7 | 8 | Input: s = "abacaba" 9 | Output: 4 10 | Explanation: 11 | Two possible partitions are ("a","ba","cab","a") and ("ab","a","ca","ba"). 12 | It can be shown that 4 is the minimum number of substrings needed. 13 | 14 | Input: s = "ssssss" 15 | Output: 6 16 | Explanation: 17 | The only valid partition is ("s","s","s","s","s","s"). 18 | */ 19 | 20 | // Approach: Greedy 21 | // O(n) time | O(1) space 22 | const partitionString = (s) => { 23 | let seen = new Set(); 24 | let count = 1; 25 | for (let char of s) { 26 | if (!seen.has(char)) { 27 | seen.add(char); 28 | } else { 29 | count++; 30 | // clear object 31 | seen = new Set(); 32 | seen.add(char); 33 | } 34 | } 35 | return count; 36 | }; 37 | -------------------------------------------------------------------------------- /Strings/palindrome-number.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Given an integer x, return true if x is a 4 | palindrome 5 | , and false otherwise. 6 | 7 | Input: x = 121 8 | Output: true 9 | Explanation: 121 reads as 121 from left to right and from right to left. 10 | 11 | Input: x = -121 12 | Output: false 13 | Explanation: From left to right, it reads -121. From right to left, it becomes 121-. Therefore it is not a palindrome. 14 | */ 15 | 16 | const isPalindrome = (x) => { 17 | // if x is negative, it cant be a palindrome 18 | if (x === 0) return true; 19 | if (x < 0 || x % 10 === 0) return false; 20 | // declare a new number and current number 21 | let current = x; 22 | let num = 0; // we are going to reverse half of the number 23 | // keep going until num > x becuase that means you converted half the number 24 | while (num < current) { 25 | // get last digit 26 | let digit = current % 10; 27 | // update num 28 | num = num * 10 + digit; 29 | // update current 30 | current = Math.floor(current / 10); 31 | } 32 | return num === current || Math.floor(num / 10) === current; 33 | }; 34 | -------------------------------------------------------------------------------- /Strings/permutation-in-string.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given two strings s1 and s2, return true if s2 contains a permutation of s1, or false otherwise. 3 | 4 | In other words, return true if one of s1's permutations is the substring of s2. 5 | 6 | Input: s1 = "ab", s2 = "eidbaooo" 7 | Output: true 8 | Explanation: s2 contains one permutation of s1 ("ba"). 9 | 10 | Input: s1 = "ab", s2 = "eidboaoo" 11 | Output: false 12 | 13 | */ 14 | 15 | // Approach: Sliding Window 16 | // O(n) time | O(n) space 17 | const checkInclusion = (s1, s2) => { 18 | // Loop through s1 to get frequency of each char 19 | const freq1 = new Array(26).fill(0); 20 | for (const i in s1) { 21 | const char = s1.charCodeAt(i) - 97; 22 | freq1[char]++; 23 | } 24 | 25 | let freq2 = new Array(26).fill(0); 26 | // Initialize left pointer for sliding window 27 | let l = 0; 28 | // Loop through s2 to find permutation 29 | for (const r in s2) { 30 | // Update freq2 31 | const char = s2.charCodeAt(r) - 97; 32 | freq2[char]++; 33 | // If freq2 > freq1, move l pointers till freq2 <= freq1 34 | while (freq2[char] > freq1[char]) { 35 | freq2[s2.charCodeAt(l) - 97]--; 36 | l++; 37 | } 38 | // Check if found permutation, return true 39 | if (r - l + 1 === s1.length) return true; 40 | } 41 | // If you havent found permuation, return false 42 | return false; 43 | }; 44 | -------------------------------------------------------------------------------- /Strings/permutation-palindrome.js: -------------------------------------------------------------------------------- 1 | /** 2 | Given a string s, return true if a permutation of the string could form a 3 | palindrome and false otherwise. 4 | 5 | Input: s = "code" 6 | Output: false 7 | 8 | Input: s = "aab" 9 | Output: true 10 | 11 | Input: s = "carerac" 12 | Output: true 13 | */ 14 | 15 | // Approach: Make a char count hash map and determine how many odd counts there are 16 | // O(n) time | O(1) space 17 | const canPermutePalindrome = (str) => { 18 | if (typeof str !== 'string') return false; 19 | const counts = {}; 20 | for (let char of str) { 21 | counts[char] = counts[char] + 1 || 1; 22 | } 23 | let odd = 0; 24 | for (let char in counts) { 25 | if (counts[char] % 2) odd++; 26 | } 27 | 28 | return odd <= 1; 29 | }; 30 | -------------------------------------------------------------------------------- /Strings/remove-digit.js: -------------------------------------------------------------------------------- 1 | /* 2 | You are given a string number representing a positive integer and a character digit. 3 | 4 | Return the resulting string after removing exactly one occurrence of digit from number such that the value of the resulting string in decimal form is maximized. The test cases are generated such that digit occurs at least once in number. 5 | 6 | Input: number = "123", digit = "3" 7 | Output: "12" 8 | Explanation: There is only one '3' in "123". After removing '3', the result is "12". 9 | 10 | Input: number = "1231", digit = "1" 11 | Output: "231" 12 | Explanation: We can remove the first '1' to get "231" or remove the second '1' to get "123". 13 | Since 231 > 123, we return "231". 14 | 15 | Input: number = "551", digit = "5" 16 | Output: "51" 17 | Explanation: We can remove either the first or second '5' from "551". 18 | Both result in the string "51". 19 | 20 | 21 | */ 22 | 23 | // O(n) time | O(n) space 24 | const removeDigit = (number, digit) => { 25 | // find the index where 3 is less than 26 | let idx = number.length - 1; 27 | for (let i = 0; i < number.length; i++) { 28 | if (number[i] === digit) { 29 | idx = i; 30 | if (number[i + 1] > digit) break; 31 | } 32 | } 33 | let res = ''; 34 | for (let i = 0; i < number.length; i++) { 35 | if (i === idx) continue; 36 | res += number[i]; 37 | } 38 | 39 | return res; 40 | }; 41 | -------------------------------------------------------------------------------- /Strings/repeated-DNA.js: -------------------------------------------------------------------------------- 1 | /* 2 | The DNA sequence is composed of a series of nucleotides abbreviated as 'A', 'C', 'G', and 'T'. 3 | 4 | For example, "ACGAATTCCG" is a DNA sequence. 5 | When studying DNA, it is useful to identify repeated sequences within the DNA. 6 | 7 | Given a string s that represents a DNA sequence, return all the 10-letter-long sequences (substrings) that occur more than once in a DNA molecule. You may return the answer in any order. 8 | 9 | Input: s = "AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT" 10 | Output: ["AAAAACCCCC","CCCCCAAAAA"] 11 | 12 | Input: s = "AAAAAAAAAAAAA" 13 | Output: ["AAAAAAAAAA"] 14 | 15 | 16 | */ 17 | 18 | // O(n) time | O(n) space where n is the length of s 19 | const findRepeatedDnaSequences = (s) => { 20 | const seen = new Set(); 21 | const output = new Set(); 22 | let length = 10; 23 | for (let i = 0; i < s.length - length + 1; i++) { 24 | const subStr = s.slice(i, i + length); 25 | if (seen.has(subStr)) output.add(subStr); 26 | seen.add(subStr); 27 | } 28 | 29 | return Array.from(output); 30 | }; 31 | -------------------------------------------------------------------------------- /Strings/reverse-integer.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a signed 32-bit integer x, return x with its digits reversed. If reversing x causes 3 | the value to go outside the signed 32-bit integer range [-2^31, 2^31 - 1], then return 0. 4 | */ 5 | 6 | const reverse = (x) => { 7 | // check if x is negative 8 | const xIsNegative = x < 0; 9 | // declare num variable and make x positive 10 | let num = 0; 11 | x = Math.abs(x); 12 | while (x) { 13 | // grab remainder, update num and x 14 | const remainder = x % 10; 15 | num = num * 10 + remainder; 16 | x = Math.floor(x / 10); 17 | } 18 | // num is greater than 2 ^ 31 - 1 return 0 19 | if (num > 2 ** 31 - 1) return 0; 20 | return xIsNegative ? -1 * num : num; 21 | }; 22 | -------------------------------------------------------------------------------- /Strings/reverse-string.js: -------------------------------------------------------------------------------- 1 | /* 2 | Write a function that reverses a string. The input string is given as an array of characters s. 3 | 4 | You must do this by modifying the input array in-place with O(1) extra memory. 5 | 6 | Input: s = ["h","e","l","l","o"] 7 | Output: ["o","l","l","e","h"] 8 | */ 9 | 10 | // O(n) time | O(1) space 11 | const reverseString = (s) => { 12 | for (let i = 0; i < Math.floor(s.length / 2); i++) { 13 | [s[i], s[s.length - 1 - i]] = [s[s.length - 1 - i], s[i]]; 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /Strings/reverse-vowels.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a string s, reverse only all the vowels in the string and return it. 3 | 4 | The vowels are 'a', 'e', 'i', 'o', and 'u', and they can appear in both lower and upper cases, more than once. 5 | 6 | Input: s = "hello" 7 | Output: "holle" 8 | 9 | Input: s = "leetcode" 10 | Output: "leotcede" 11 | 12 | */ 13 | 14 | // Approach: Two pointers 15 | // O(n) time | O(n) space 16 | const reverseVowels = (s) => { 17 | const vowels = new Set(['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U']); 18 | let l = 0; 19 | let r = s.length - 1; 20 | let leftHalf = ''; 21 | let rightHalf = ''; 22 | while (l <= r) { 23 | if (!vowels.has(s[l])) { 24 | leftHalf += s[l]; 25 | l++; 26 | } else if (!vowels.has(s[r])) { 27 | rightHalf = s[r] + rightHalf; 28 | r--; 29 | } else { 30 | leftHalf += s[r]; 31 | if (r !== l) rightHalf = s[l] + rightHalf; 32 | r--; 33 | l++; 34 | } 35 | } 36 | return leftHalf + rightHalf; 37 | }; 38 | -------------------------------------------------------------------------------- /Strings/reverse-words-in-stringII.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a character array s, reverse the order of the words. 3 | 4 | A word is defined as a sequence of non-space characters. The words in s will be separated by a single space. 5 | 6 | Your code must solve the problem in-place, i.e. without allocating extra space. 7 | 8 | Input: s = ["t","h","e"," ","s","k","y"," ","i","s"," ","b","l","u","e"] 9 | Output: ["b","l","u","e"," ","i","s"," ","s","k","y"," ","t","h","e"] 10 | 11 | Input: s = ["a"] 12 | Output: ["a"] 13 | 14 | */ 15 | 16 | // O(n) time | O(1) space 17 | const reverseWords = (s) => { 18 | s.reverse(); 19 | let l = 0; 20 | for (let i = 0; i < s.length; i++) { 21 | if (s[i] === ' ' || i === s.length - 1) { 22 | // reverse 23 | let r = i === s.length - 1 ? i : i - 1; 24 | while (l <= r) { 25 | [s[l], s[r]] = [s[r], s[l]]; 26 | l++; 27 | r--; 28 | } 29 | l = i + 1; 30 | } 31 | } 32 | return s; 33 | }; 34 | -------------------------------------------------------------------------------- /Strings/semordnilap.js: -------------------------------------------------------------------------------- 1 | /* 2 | Write a function that takes in a list of unique string and returns a list of semordinlap 3 | pairs. 4 | 5 | word = ['diaper', 'abc', 'test', 'cba', 'repaid'] 6 | output = [[diaper, repaid], [abc, cba]] 7 | */ 8 | 9 | // O(n * m) time | O(n * m) space where n is length of array and m is length of longest word 10 | const semordnilap = (words) => { 11 | // declare words and output array 12 | const map = {}; 13 | const output = []; 14 | for (let word of words) { 15 | // get palindrome and check if in map 16 | const palindrome = word.split("").reverse().join(""); 17 | if (palindrome in map) { 18 | output.push([palindrome, word]); 19 | } else { 20 | map[word] = true; 21 | } 22 | } 23 | return output; 24 | }; 25 | -------------------------------------------------------------------------------- /Strings/word-pattern.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a pattern and a string s, find if s follows the same pattern. 3 | 4 | Here follow means a full match, such that there is a bijection between a letter in pattern and a non-empty word in s. 5 | 6 | Input: pattern = "abba", s = "dog cat cat dog" 7 | Output: true 8 | 9 | Input: pattern = "abba", s = "dog cat cat fish" 10 | Output: false 11 | 12 | Input: pattern = "aaaa", s = "dog cat cat dog" 13 | Output: false 14 | 15 | */ 16 | 17 | // O(p + s) time | O(w) space where w is the number of unique words 18 | const wordPattern = (pattern, s) => { 19 | // {char: word}, {word: char} 20 | const charMap = {}; 21 | const wordMap = {}; 22 | const words = s.split(' '); 23 | 24 | if (words.length !== pattern.length) return false; 25 | for (let i = 0; i < words.length; i++) { 26 | const char = pattern[i]; 27 | const word = words[i]; 28 | if (!(char in charMap)) { 29 | if (wordMap.hasOwnProperty(word)) { 30 | return false; 31 | } else { 32 | charMap[char] = word; 33 | wordMap[word] = char; 34 | } 35 | } else { 36 | if (charMap[char] !== word) return false; 37 | } 38 | } 39 | return true; 40 | }; 41 | 42 | const pattern = 'abba'; 43 | const s = 'dog constructor constructor dog'; 44 | 45 | console.log(wordPattern(pattern, s)); 46 | -------------------------------------------------------------------------------- /Trees/clone-nary-tree.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given a root of an N-ary tree, return a deep copy (clone) of the tree. 3 | 4 | Each node in the n-ary tree contains a val (int) and a list (List[Node]) of its children. 5 | 6 | class Node { 7 | public int val; 8 | public List children; 9 | } 10 | Nary-Tree input serialization is represented in their level order traversal, each group of children is separated by the null value (See examples). 11 | 12 | 13 | 14 | Example 1: 15 | 16 | 17 | 18 | Input: root = [1,null,3,2,4,null,5,6] 19 | Output: [1,null,3,2,4,null,5,6] 20 | Example 2: 21 | 22 | 23 | 24 | Input: root = [1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14] 25 | Output: [1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14] 26 | */ 27 | 28 | // O(n) time | O(n) space where n is the number of nodes in the tree 29 | const cloneTree = (root) => { 30 | // base case if root is null 31 | if (!root) return null; 32 | // create a deep copy of node 33 | const clone = new Node(root.val, []); 34 | for (let child of root.children) { 35 | // recursively call function on child and push into children array of clone 36 | clone.children.push(cloneTree(child)); 37 | } 38 | // return clone 39 | return clone; 40 | }; 41 | -------------------------------------------------------------------------------- /Trees/max-depth-nary.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given a n-ary tree, find its maximum depth. 3 | 4 | The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node. 5 | 6 | Nary-Tree input serialization is represented in their level order traversal, each group of children is separated by the null value (See examples). 7 | 8 | Input: root = [1,null,3,2,4,null,5,6] 9 | Output: 3 10 | 11 | Input: root = [1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14] 12 | Output: 5 13 | 14 | 15 | */ 16 | 17 | // O(n) time | O(h) space 18 | const maxDepth = (root) => { 19 | if (!root) return 0; 20 | if (root.children.length === 0) return 1; 21 | let maxDepthOfChildren = 0; 22 | for (let child of root.children) { 23 | maxDepthOfChildren = Math.max(maxDepthOfChildren, maxDepth(child)); 24 | } 25 | return maxDepthOfChildren + 1; 26 | }; 27 | -------------------------------------------------------------------------------- /Trees/minimum-absolute-difference.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root of a Binary Search Tree (BST), return the minimum absolute difference between the values of any two different nodes in the tree. 3 | 4 | Input: root = [4,2,6,1,3] 5 | Output: 1 6 | 7 | Input: root = [1,0,48,null,null,12,49] 8 | Output: 1 9 | 10 | */ 11 | 12 | // O(n) time | O(h) space 13 | const getMinimumDifference = (root) => { 14 | let min = Infinity; 15 | let prevNode = null; 16 | 17 | const dfs = (root) => { 18 | if (!root) return; 19 | dfs(root.left); 20 | if (prevNode) { 21 | min = Math.min(min, root.val - prevNode.val); 22 | } 23 | prevNode = root; 24 | dfs(root.right); 25 | }; 26 | dfs(root); 27 | return min; 28 | }; 29 | 30 | // Approach: Inorder traversal 31 | // O(n) time | O(n) space 32 | const getMinimumDifference2 = (root) => { 33 | const values = []; 34 | inOrder(root, values); 35 | let min = Infinity; 36 | for (let i = 1; i < values.length; i++) { 37 | min = Math.min(min, values[i] - values[i - 1]); 38 | } 39 | return min; 40 | }; 41 | 42 | const inOrder = (root, values) => { 43 | if (!root) return; 44 | inOrder(root.left, values); 45 | values.push(root.val); 46 | inOrder(root.right, values); 47 | }; 48 | -------------------------------------------------------------------------------- /Trees/nary-tree-level-order.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an n-ary tree, return the level order traversal of its nodes' values. 3 | 4 | Nary-Tree input serialization is represented in their level order traversal, each group of children is separated by the null value (See examples). 5 | 6 | Input: root = [1,null,3,2,4,null,5,6] 7 | Output: [[1],[3,2,4],[5,6]] 8 | 9 | Input: root = [1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14] 10 | Output: [[1],[2,3,4,5],[6,7,8,9,10],[11,12,13],[14]] 11 | 12 | */ 13 | 14 | const levelOrder = (root) => { 15 | if (!root) return []; 16 | const output = [[root.val]]; 17 | let queue = [root]; 18 | while (queue.length) { 19 | let nextValues = []; 20 | let nextLevel = []; 21 | for (let i = 0; i < queue.length; i++) { 22 | const current = queue[i]; 23 | for (let child of current.children) { 24 | nextLevel.push(child); 25 | nextValues.push(child.val); 26 | } 27 | } 28 | if (nextValues.length !== 0) output.push(nextValues); 29 | queue = nextLevel; 30 | } 31 | return output; 32 | }; 33 | 34 | -------------------------------------------------------------------------------- /Trees/nary-tree-postorder.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root of an n-ary tree, return the postorder traversal of its nodes' values. 3 | 4 | Nary-Tree input serialization is represented in their level order traversal. Each group of children is separated by the null value (See examples) 5 | 6 | Input: root = [1,null,3,2,4,null,5,6] 7 | Output: [5,6,3,2,4,1] 8 | 9 | Input: root = [1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14] 10 | Output: [2,6,14,11,7,3,12,8,4,13,9,10,5,1] 11 | 12 | */ 13 | 14 | // O(n) time | O(h) space where n is the number of nodes and h is the height of the tree 15 | const postorder = (root) => { 16 | const values = []; 17 | dfs(root, values); 18 | return values; 19 | }; 20 | 21 | const dfs = (root, values) => { 22 | if (!root) return; 23 | for (let child of root.children) { 24 | dfs(child, values); 25 | } 26 | values.push(root.val); 27 | }; 28 | -------------------------------------------------------------------------------- /Trees/nary-tree-preorder.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given the root of an n-ary tree, return the preorder traversal of its nodes' values. 3 | 4 | Nary-Tree input serialization is represented in their level order traversal. Each group of children is separated by the null value (See examples) 5 | 6 | Input: root = [1,null,3,2,4,null,5,6] 7 | Output: [1,3,5,6,2,4] 8 | 9 | Input: root = [1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14] 10 | Output: [1,2,3,6,7,11,14,4,8,12,5,9,13,10] 11 | 12 | */ 13 | 14 | // O(n) time | O(h) space where n is the number of nodes and h is the height of the tree 15 | const preorder = (root) => { 16 | const values = []; 17 | dfs(root, values); 18 | return values; 19 | }; 20 | 21 | const dfs = (root, values) => { 22 | if (!root) return; 23 | values.push(root.val); 24 | for (let child of root.children) { 25 | dfs(child, values); 26 | } 27 | return; 28 | }; 29 | -------------------------------------------------------------------------------- /Tries/prefix-trie.js: -------------------------------------------------------------------------------- 1 | /* 2 | Implement a trie class 3 | */ 4 | 5 | class Trie { 6 | constructor() { 7 | this.root = {}; 8 | this.endSymbol = '*'; 9 | } 10 | 11 | insert(word) { 12 | let current = this.root; 13 | for (let char of word) { 14 | if (char in current) { 15 | current = current[char]; 16 | } else { 17 | current[char] = {}; 18 | current = current[char]; 19 | } 20 | } 21 | current[this.endSymbol] = true; 22 | } 23 | 24 | search(word) { 25 | let current = this.root; 26 | for (let char of word) { 27 | if (!(char in current)) return false; 28 | current = current[char]; 29 | } 30 | 31 | return this.endSymbol in current; 32 | } 33 | 34 | startsWith(prefix) { 35 | let current = this.root; 36 | for (let char of prefix) { 37 | if (!(char in current)) return false; 38 | current = current[char]; 39 | } 40 | 41 | return true; 42 | } 43 | } 44 | --------------------------------------------------------------------------------