├── .gitignore ├── Gemfile ├── Gemfile.lock ├── README.md ├── ch2 ├── binary_search.rb ├── bubble_sort.rb ├── ch2Problems.rb ├── chapter2cormen.txt ├── merge_sort.rb ├── problems.txt └── recurs_ins_sort.rb ├── ch3 └── ch3notes.txt ├── ch3notes.txt ├── ch4 ├── notes.txt └── subarray.rb ├── ch6 ├── heap_sort.rb └── notes.txt ├── ch7 └── quicksort.rb ├── ch8 ├── counting_sort.rb └── linear_sort.txt ├── data_structures ├── avl.rb ├── bst.rb ├── dynamic_array.rb ├── graph.rb ├── graph_bfs.rb ├── linked_list.rb ├── memory.rb ├── red_black.rb ├── red_black_tree.rb ├── ring_buffer.rb ├── set.rb ├── static_array.rb └── topological_sort.rb └── problems ├── javascript ├── Algorthim.js ├── Atoi.js ├── AuthorizeEvents.js ├── AutoComplete.js ├── BalancePoint.js ├── BalancedParenthesis.js ├── BinaryReversal.js ├── BoundaryPerimeter.js ├── CommonSubstring.js ├── CountPairs.js ├── CountSubstringsWithDistincLetter.js ├── CurrencyRates.js ├── CustomBind.js ├── Debounce.js ├── DefangingIp.js ├── DiameterOfTree.js ├── DivideWinterSummer.js ├── EventEmitter.js ├── FindFirstLast.js ├── FindMaxBoat.js ├── FindMaxSliceArray.js ├── FindSubstringWithMostVowels.js ├── FirstUniqueCharString.js ├── FlattenNestedArray.js ├── GenerateAreas.js ├── GenerateArrayRandomNumbers.js ├── HighestPeak.js ├── ImplementRegex.js ├── KnightGame.js ├── LinkedListPalindrome.js ├── ListNode.js ├── LongestCall.js ├── MakeChange.js ├── MedianSortedArrays.js ├── MemoizeFib.js ├── MinCostClimbingStairs.js ├── MinStack.js ├── MinSteps.js ├── MostCommonGiftDFS.js ├── MovingMiners.js ├── NeedleHaystack.js ├── NthFib.js ├── NumberOfStepsToReduceToZero.js ├── OnlyTwoSwapped.js ├── Pagination.js ├── PermutationsOfWords.js ├── PermutationsString.js ├── PhoneBookOrderForm.js ├── PrintList.js ├── PrintReverseList.js ├── ProductToN.js ├── PromiseRace.js ├── Promisify.js ├── QuestionMarks.js ├── RemoveVowelsString.js ├── ReverseWordsString.js ├── RnadomSet.js ├── Set.js ├── ShoppingCartMinLimit.js ├── ThreeSum.js ├── Throttle.js ├── TwoSum.js ├── UniqueChars.js ├── boats.js ├── contFrequencyArray.js ├── duolingoScript.js ├── flattenArray.js ├── formatObjects.js ├── generateBalancedSubstring.js ├── maxLength.js ├── routesFromCities.js ├── subtasksList.js ├── toWindows.js └── treesandgraphs.js ├── python └── prime_factors.py ├── ruby ├── binary.rb ├── binary_search.rb ├── braces.rb ├── bst.rb ├── caesar_cipher.rb ├── circular_array.rb ├── common_substr.rb ├── counting_sort.rb ├── delete_node.rb ├── digital_root.rb ├── digits.rb ├── dutch_flag.rb ├── egg_drop.rb ├── fibs.rb ├── file_dictionary.rb ├── file_read.rb ├── find_double.rb ├── flight_lengths.rb ├── has_cycle.rb ├── in_place_shuffle.rb ├── k_consensus.rb ├── kth_largest.rb ├── largest_stack.rb ├── link_reverse.rb ├── make_change.rb ├── make_cloud.rb ├── matrix_search.rb ├── max_pair.rb ├── max_profit.rb ├── maximum_windows.rb ├── move_zeros.rb ├── multiple_merge.rb ├── n_choose_k.rb ├── nested_parentheticals.rb ├── next_largest_bst.rb ├── parenthesis_matcher.rb ├── partition_palindrome.rb ├── points.rb ├── powers_of_two.rb ├── print_tree.rb ├── products.rb ├── queue_two_stacks.rb ├── rectangle-intersection.rb ├── recursion.rb ├── recursive_permutations.rb ├── replace_array.rb ├── reverse_strings.rb ├── riffle.rb ├── rotation.rb ├── string_permutation.rb ├── substringsv2.rb ├── sum_rec.rb ├── super_balanced.rb ├── temp_tracker.rb ├── threesum.rb ├── tuples.rb ├── two_stacks.rb ├── two_sum.rb └── unique.rb └── typescript ├── orderedWeight.ts └── rotatingStrings.ts /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem 'algorithms' 4 | gem 'pry-byebug' 5 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | algorithms (0.6.1) 5 | byebug (3.5.1) 6 | columnize (~> 0.8) 7 | debugger-linecache (~> 1.2) 8 | slop (~> 3.6) 9 | coderay (1.1.0) 10 | columnize (0.9.0) 11 | debugger-linecache (1.2.0) 12 | method_source (0.8.2) 13 | pry (0.10.1) 14 | coderay (~> 1.1.0) 15 | method_source (~> 0.8.1) 16 | slop (~> 3.4) 17 | pry-byebug (3.0.1) 18 | byebug (~> 3.4) 19 | pry (~> 0.10) 20 | slop (3.6.0) 21 | 22 | PLATFORMS 23 | ruby 24 | 25 | DEPENDENCIES 26 | algorithms 27 | pry-byebug 28 | -------------------------------------------------------------------------------- /ch2/binary_search.rb: -------------------------------------------------------------------------------- 1 | def binary_search(array, value) 2 | return true if array[0] == value 3 | return false if array.size == 1 && array[0] != value 4 | length = array.length 5 | mid = length/2 6 | left = array[(0...mid)] 7 | right = array[(mid...length)] 8 | 9 | if left[-1] < value 10 | value = binary_search(right, value) 11 | else right[-1] < value 12 | value = binary_search(left, value) 13 | end 14 | return value 15 | end 16 | 17 | p binary_search([1,2,3,4,5,6],1) 18 | p binary_search([2,3,4,6],5) 19 | -------------------------------------------------------------------------------- /ch2/bubble_sort.rb: -------------------------------------------------------------------------------- 1 | def bubble_sort(arr) 2 | sorted = false 3 | until sorted 4 | sorted = true 5 | (0...arr.length - 1).each do |index| 6 | if arr[index] > arr[index + 1] 7 | sorted = false 8 | arr[index], arr[index + 1] = arr[index + 1], arr[index] 9 | end 10 | end 11 | end 12 | arr 13 | end 14 | -------------------------------------------------------------------------------- /ch2/ch2Problems.rb: -------------------------------------------------------------------------------- 1 | #cormen algorithms book ch. 2 2 | 3 | 4 | #[5,2,4,6,1,3] 5 | def insertion_sort(array) 6 | #go through the array 7 | array.each_with_index do |el, i| #n steps 8 | #the elements starting from [0..i - 1] will always be sorted (initialization loop invariant) 9 | # run 1: el: 5, i: 1 10 | j = i - 1 #1 step 11 | while j >= 0 && array[j] > el #at worst, we will do n-1 comparisons 12 | # run 1: true 13 | array[j+1] = array[j] 14 | # run1: [5,5,4,6,1,3] 15 | j -= 1 16 | # run1: 0 17 | end 18 | array[j+1] = el #1 step 19 | # run1: [2,5,4,6,1,3] 20 | #the array is still sorted (propagation) 21 | end 22 | array #1 step (then here at termination, the algorithm is correct) 23 | end 24 | #fn => comparisons are in the worst form as form (n)(n - 1) + c, so O(n^2) at worst, 25 | #but actually not that bad because it skips a bunch of iterations if the array is semi-sorted 26 | 27 | def inc_insertion_sort(array) 28 | array.each_with_index do |el, i| 29 | j = i - 1 30 | while j >= 0 && array[j] < el 31 | array[j+1] = array[j] 32 | j -= 1 33 | end 34 | array[j+1] = el 35 | end 36 | array 37 | end 38 | 39 | 40 | def linear_search(array, v) 41 | array.each do |item| 42 | #loop invariant: initialization (item v does exist) 43 | #propagation (if item != v) propagates the loop, yielding more items (propagation) 44 | return v if item == v 45 | #termination, two conditions (if item == v), we return from the method, avoiding the nil 46 | end 47 | #if the item does not exist in the array, then we return nil 48 | nil 49 | end 50 | #O(n) runtime, possibly O(1) memory. 51 | 52 | def add_bits(array1, array2) 53 | #return if the array is not the same size 54 | return nil if array1.size != array2.size 55 | destination_array = [] 56 | array1.each_with_index do |item, index| 57 | destination_array[index] ||= 0 58 | if (item + array2[index] + destination_array[index] > 1) 59 | destination_array[index + 1] = 1 60 | elsif (item + array2[index] + destination_array[index] == 1) 61 | destination_array[index] = 1 62 | end 63 | end 64 | destination_array.push(0).reverse 65 | end 66 | #worst case scenario is O(n) 67 | 68 | 69 | def selection_sort(array) 70 | #assume array has a size and has numbers in it and is an array 71 | 72 | (0..array.length - 1).each do |i| 73 | small = array[i] 74 | smallidx = i 75 | #for propagation, we know that the n-1 smallest element will be found 76 | #because the element is not within the scope of the next outer loop 77 | (i..array.length - 1).each do |j| 78 | small, smallidx = array[j], j if array[j] < small 79 | end 80 | # we find the n-th smallest element of the array initialization 81 | # replace the n-th smallest elements index with the not nth-smallest element 82 | array[smallidx] = array[i] 83 | array[i] = small 84 | end 85 | #the loop terminates when all portions of the loop have been reached 86 | array 87 | end 88 | 89 | #this algorithm is really, really bad. it has O(n^2) running time always. 90 | -------------------------------------------------------------------------------- /ch2/chapter2cormen.txt: -------------------------------------------------------------------------------- 1 | Algorithms solution 2 | 3 | 4 | 2.1 5 | 1. Insertion sort diagram 6 | 7 | A = [31,41,59,26,41,58] 8 | no sorts until the third element 9 | A = [26,31,41,59,41,58] 10 | A = [26,31,41,41,59,58] 11 | A = [26,31,41,41,58,59] 12 | 13 | This is a mixed-case scenario where insertion sort is actually not ~that~ bad. Between O(n) and O(n^2) 14 | 15 | 2. Just flip the comparator. 16 | 17 | def inc_insertion_sort(array) 18 | #go through the array 19 | array.each_with_index do |el, i| #n steps 20 | #the elements starting from [0..i - 1] will always be sorted (initialization loop invariant) 21 | # run 1: el: 5, i: 1 22 | j = i - 1 #1 step 23 | while j >= 0 && array[j] < el #at worst, we will do n-1 comparisons 24 | # run 1: true 25 | array[j+1] = array[j] 26 | # run1: [5,5,4,6,1,3] 27 | j -= 1 28 | # run1: 0 29 | end 30 | array[j+1] = el #1 step 31 | # run1: [2,5,4,6,1,3] 32 | #the array is still sorted (propagation) 33 | end 34 | array #1 step (then here at termination, the algorithm is correct) 35 | end 36 | 37 | 3. Write linear search, and use the loop invariant induction to prove your algorithm is correct. 38 | def linear_search(array, v) 39 | array.each do |item| 40 | #loop invariant: initialization (item v does exist) 41 | #propagation (if item != v) propagates the loop, yielding more items (propagation) 42 | return v if item == v 43 | #termination, two conditions (if item == v), we return from the method, avoiding the nil 44 | end 45 | #if the item does not exist in the array, then we return nil 46 | nil 47 | end 48 | #O(n) runtime, possibly O(1) memory, at most O(2) not sure if storing V counts as an object? 49 | 50 | 4. Input: two (n-bit arrays are inserted, with length of n elements) are to be added. 51 | 52 | Output: a new n+1 array with the output of the addition: 53 | 54 | 2.2 55 | Algorithm analysis usually happens in terms of the input size, which for numbers is n or for graphs (vertex, edge) notation is usually sufficient. 56 | 57 | 2.2 Exercises 58 | 1. 59 | Express n^3/1000 - 100n^2 - 100n + 3 in O(n) - > O(n^3) 60 | 61 | 62 | 2. 63 | Selection Sort. 64 | def selection_sort(array) 65 | #assume array has a size and has numbers in it and is an array 66 | 67 | (0..array.length - 1).each do |i| 68 | small = array[i] 69 | smallidx = i 70 | #for propagation, we know that the n-1 smallest element will be found 71 | #because the element is not within the scope of the next outer loop 72 | (i..array.length - 1).each do |j| 73 | small, smallidx = array[j], j if array[j] < small 74 | end 75 | # we find the n-th smallest element of the array initialization 76 | # replace the n-th smallest elements index with the not nth-smallest element 77 | array[smallidx] = array[i] 78 | array[i] = small 79 | end 80 | #the loop terminates when all portions of the loop have been reached 81 | array 82 | end 83 | 84 | 85 | 3. 86 | Linear search on average could take n/2 searches to find the element and n at most... 87 | 88 | 4. 89 | You can modify any algorithm to have a good-case running time by making sure the input is small as possible, by making sure the input is kinda-sorta-right. 90 | 91 | 92 | 2.3 Divide and conquer 93 | 1. smaller subproblems 94 | 2. subdivide subproblems or solve if small 95 | 3. combine the subproblems into the solution for the original problem 96 | 97 | 1. Write [3,4,52,26,38,57,9,49] a series of merge-sort operations 98 | 99 | L1 = [3,4,52,26] 100 | 101 | === [3,4,26,52] (+2) 102 | L1-L2 = [3,4] 103 | ==[3,4] (+1) 104 | L1-R2-L3 = [3] 105 | L1-R2-R3 = [4] 106 | L1-R2 = [52,26] 107 | ==[26,52] (+1) 108 | L1-R2-L3 = [52] 109 | L1-R2-R3 = [26] 110 | 111 | === [9,38, 49, 57] (+2) 112 | R1 = [38,57,9,49] 113 | 114 | R1-L2 = [38, 57] 115 | ==[38,57] (+1) 116 | R1-L2-L3 = [38] 117 | R1-L2-R3 = [57] 118 | 119 | R1-R2 = [9, 49] 120 | ==[9,49] (+1) 121 | R1-R2-L3 = [9] 122 | R1-R2-R3 = [49] 123 | 124 | ===== [3,4,9,26,38,49,52,57] (+4) 125 | total steps: (4+2+1) * 2 = 14 steps for the merge; 2 for the splits, which results in 18 steps total?. 126 | I can't calculate the 24 steps by n log n 127 | 128 | 2. Mathematical induction 129 | T(2n) = 2T(n) + 2n 130 | = 2nlg(n) + 2n 131 | = 2n(lg(2n) - 1) + 2n 132 | = 2n lg(2n) 133 | 2. Recurrence relation for recurs_ins_sort 134 | t(n) = t(n-1) + n 135 | 3. Binary Search 136 | assume 2^n array 137 | then ith iteration elements is given by 138 | i = n / 2^i 139 | when i = 1, there is only 1 element in view, this is the worst case 140 | 1 = n / 2^i 141 | lg n = i iterations to get to 1, 142 | so as a function of n it can take lg n iterations to find the answer at most. 143 | 4. 144 | -------------------------------------------------------------------------------- /ch2/merge_sort.rb: -------------------------------------------------------------------------------- 1 | def merge_sort(array) 2 | return array if array.size <= 1 3 | 4 | length = array.length 5 | #ruby automatically floors stuff 6 | mid = length / 2 7 | left = merge_sort(array[(0...mid)]) 8 | right = merge_sort(array[(mid...length)]) 9 | 10 | merge_helper(left, right) 11 | 12 | end 13 | 14 | def merge_helper(array1, array2) 15 | arr = [] 16 | until array1.empty? || array2.empty? 17 | if array1[0] <= array2[0] 18 | arr << array1.shift 19 | else 20 | arr << array2.shift 21 | end 22 | end 23 | arr + array1 + array2 24 | end 25 | -------------------------------------------------------------------------------- /ch2/problems.txt: -------------------------------------------------------------------------------- 1 | 2.1 Insertion Sort on Small Arrays in Merge Sort 2 | 3 | 2.2 BubbleSort correctness 4 | 5 | Initiation 6 | We have a flag which indicates sorted behavior quality, we assume it's false 7 | Once we enter, the flag is true. 8 | 9 | Propagation 10 | The propagative step is to set the sorted flag back to false if any swaps occur. This means the array has changed and we need to go through it again 11 | 12 | Termination 13 | Once sorted, the flag will fly true, that final iteration of the loop will not generate another loop, and the 14 | 15 | Since we've proved loop invariance, bubble sort must sort. 16 | 17 | def bubble_sort(arr) 18 | sorted = false 19 | until sorted 20 | sorted = true 21 | (0...arr.length - 1).each do |index| 22 | if arr[index] > arr[index + 1] 23 | sorted = false 24 | arr[index], arr[index + 1] = arr[index + 1], arr[index] 25 | end 26 | end 27 | end 28 | arr 29 | end 30 | 31 | 32 | 2.3 Correctness of Hortner's Rule 33 | 34 | 2.4 Inversions 35 | -------------------------------------------------------------------------------- /ch2/recurs_ins_sort.rb: -------------------------------------------------------------------------------- 1 | def recurs_ins_sort(array) 2 | return array if array.size == 1 3 | value = array.shift 4 | smallest_arr = recurs_ins_sort(array) 5 | smallest_arr << value if smallest_arr[0] < value 6 | smallest_arr.unshift(value) if smallest_arr[0] >= value 7 | smallest_arr 8 | end 9 | 10 | # (tn) = t(n-1) + n 11 | 12 | p recurs_ins_sort([5,4,3,2,1]) 13 | 14 | #[3,2,1] 15 | #[2,1] 16 | #[1] 17 | 18 | ##[1,2] 19 | -------------------------------------------------------------------------------- /ch3/ch3notes.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesusmaldonado/algorithms/73f0b633b55ae0e307bbba4932ad9a777e59e730/ch3/ch3notes.txt -------------------------------------------------------------------------------- /ch3notes.txt: -------------------------------------------------------------------------------- 1 | Chapter 3 - Growth of Functions 2 | asymptotic notation doesn't just apply to time, also can apply to space etc 3 | math etc, conventions. 4 | -------------------------------------------------------------------------------- /ch4/notes.txt: -------------------------------------------------------------------------------- 1 | Given a stock-bearing problem a brute force solution is to consider every possible buy and sell date and simply select the highest. 2 | This solution has (n 2) binomial pairs of dates, which is n!/(2! * (n - 2)!), which is at least o(n^2) 3 | 4 | So, a better solution would be to: 5 | 1. Find a sequence of days over which the net change from the first day to the last is maximum 6 | 2. Then, the problem is to find a nonempty, contiguous subarray of A whose values have the largest sum 7 | 3. We need to find the maximum subarray 8 | What's special about this is that we can utilize the value of previously computed subarray sums. 9 | 10 | We can apply divide and conquer to this 11 | 1. Divide the subarray into two subarrays of equal size. 12 | Any contiguous subarray is either in the left, in the right, or somewhere in between those two arrays. 13 | For the middle array, it has form A[i..mid] and A[mid +1..j], and then this is combined 14 | 15 | max-crossing subarray(A, low, mid, high) 16 | left-sum = -inf 17 | sum = 0 18 | for i = mid downto low 19 | sum = sum + A[i] 20 | if sum > left-sum 21 | left-sum = sum 22 | max-left = i 23 | right-sum = -inf 24 | sum = 0 25 | for j = mid + 1 to high 26 | sum = sum + A[j] 27 | if sum > right-sum 28 | right-sum = sum 29 | max-irhgt = j 30 | return (max-left, max-right, left-sum + right-sumewsasz) 31 | -------------------------------------------------------------------------------- /ch4/subarray.rb: -------------------------------------------------------------------------------- 1 | #the maximum-subarray problem applies to problems that consist of finding a maximum sum for a given subarray 2 | #let's say we have an array of stock prices with sell and buy dates, and we find to maximum profit 3 | #to find the maximum profit, we can do the brute force solution 4 | #or we can find the maximum of the array being split on the right-side and left-side 5 | #the maximum must exist within either the right-side, the left-side, or somewhere in between these two arrays 6 | #this is called the divide and conquer approach 7 | 8 | def find_max_subarray(array, low, high) 9 | return [low, high, array[low]] if high == low 10 | mid = (low + high)/2 11 | left_low, left_high, left_sum = find_max_subarray(array, low, mid) 12 | right_low, right_high, right_sum = find_max_subarray(array, mid + 1, high) 13 | cross_low, cross_high, cross_sum = find_max_crossing(array, low, mid, high) 14 | return [left_low, left_high, left_sum] if left_sum >= right_sum && left_sum >= cross_sum 15 | return [right_low, right_high, right_sum] if right_sum >= left_sum && right_sum >= cross_sum 16 | return [cross_low, cross_high, cross_sum] 17 | end 18 | 19 | def find_max_crossing(array, low, mid, high) 20 | left_sum = -1 21 | sum = 0 22 | max_left = low 23 | (low...mid).each do |left_index| 24 | sum = sum + array[left_index] 25 | if sum > left_sum 26 | left_sum = sum 27 | max_left = left_index 28 | end 29 | end 30 | sum = 0 31 | right_sum = -1 32 | max_right = high 33 | (mid..high).each do |right_index| 34 | sum = sum + array[right_index] 35 | if sum > right_sum 36 | right_sum = sum 37 | max_right = right_index 38 | end 39 | end 40 | [max_left, max_right, left_sum + right_sum] 41 | end 42 | 43 | p find_max_subarray([4,3,-1,27], 0, 3) 44 | 45 | ##ruby implementation 46 | def max_subarr(arr) 47 | return arr[0] if arr.size == 1 48 | mid = arr.length/2 49 | left = max_subarr(arr[(0...mid)]) 50 | right = max_subarr(arr[(mid...arr.length)]) 51 | cross = max_cross(arr) 52 | return left if left >= right && left >= cross 53 | return right if right >= left && right >= cross 54 | return cross 55 | end 56 | 57 | def max_cross(arr) 58 | mid = arr.length/2 59 | left_arr = arr[(0...mid)] 60 | left_sum = -1 61 | sum = 0 62 | left_arr.each do |element| 63 | sum += element 64 | left_sum = sum if sum >= left_sum 65 | end 66 | right_sum = 0 67 | sum = 0 68 | right_arr = arr[(mid...arr.length)] 69 | right_arr.each do |element| 70 | sum += element 71 | right_sum = sum if sum >= right_sum 72 | end 73 | right_sum + left_sum 74 | end 75 | 76 | p max_subarr([-1,-3,-1,-4]) 77 | -------------------------------------------------------------------------------- /ch6/heap_sort.rb: -------------------------------------------------------------------------------- 1 | require 'pry' 2 | ##In Ruby, the key methods of a heap are 3 | ##left --> 2n+1 4 | ##right --> 2n + 2 5 | ##parent --> (n - 1)/2 6 | ##where n is the number of nodes 7 | ##unless we initialize a heap w nil, then we can just do 2n, 2n + 1, and n/2 respectively for these operations 8 | ##implementation taken from rubyquiz.com/quiz40.html 9 | ##HEAPSORT IS COOL BECAUSE WE HAVE O(n lgn) sort time and O(1) memory! 10 | 11 | class Heap 12 | 13 | 14 | 15 | def initialize( *elements, &comp) 16 | @heap = [nil] 17 | @comp ||= lambda {|p, c| p <=> c} 18 | insert(*elements) 19 | end 20 | 21 | def insert(*elements) 22 | elements.each do |element| 23 | @heap << element 24 | sift_down 25 | end 26 | end 27 | 28 | def extract_max 29 | return "no elements in heap" if self.heapsize < 1 30 | extract = @heap[1] 31 | if self.heapsize > 1 32 | @heap[1] = @heap.pop 33 | sift_up 34 | else 35 | @heap.pop 36 | end 37 | extract 38 | end 39 | 40 | def inspect 41 | @heap[1..-1] 42 | end 43 | 44 | def heapsize 45 | @heap.size - 1 46 | end 47 | 48 | def [](num) 49 | @heap[num] 50 | end 51 | 52 | def root 53 | @heap[1] 54 | end 55 | 56 | def to_s 57 | # (1..self.heapsize).each do |num| 58 | # todo, write a heap representation of an array 59 | end 60 | 61 | def height 62 | Math.log2(@heapsize) 63 | end 64 | 65 | def heapsort 66 | arr = [] 67 | arr << self.extract_max until self.heapsize == 0 68 | arr 69 | end 70 | 71 | def left(num) 72 | return "this is the root of the tree" if num == 1 73 | @heap[2 * num] 74 | end 75 | 76 | def right(num) 77 | return "this is the root of the tree" if num == 1 78 | @heap[2 * num + 1] 79 | end 80 | 81 | def parent(num) 82 | return "this is the root of the tree" if num == 1 83 | @heap[num /2] 84 | end 85 | 86 | private 87 | def sift_down 88 | i = @heap.size - 1 89 | until i == 1 90 | j = i /2 91 | break if @comp[@heap[i], @heap[j]] <= 0 92 | 93 | @heap[j], @heap[i] = @heap[i], @heap[j] 94 | i = j 95 | end 96 | end 97 | 98 | def sift_up 99 | i = 1 100 | loop do 101 | c = 2 * i 102 | break if c >= @heap.size 103 | c += 1 if c + 1 < @heap.size && @heap[c + 1] > @heap[c] 104 | break if @comp[@heap[c], @heap[i]] <= 0 105 | @heap[c], @heap[i] = @heap[i], @heap[c] 106 | i = c 107 | end 108 | end 109 | 110 | 111 | 112 | 113 | end 114 | -------------------------------------------------------------------------------- /ch6/notes.txt: -------------------------------------------------------------------------------- 1 | Heapsort 2 | 3 | -- 4 | 5 | Creates a node representation of an array called a heap structure. A heap representation begins by representing the first eelement of the array with the next two as childen, up to a maximum of two. 6 | The pattern is continued onto the next array. 7 | 8 | We have methods we can call on a node 9 | 10 | Parent(i) 11 | return i/2 12 | 13 | Left(i) 14 | return 2i 15 | 16 | Right(i) 17 | return 2i + 1 18 | 19 | These procedures can compute 2i in one instruction by shifting the binary representation of i by one position, 2i+ 1 by one shifting the binary representation of a number to the left and then adding 1 as the lowest bit 20 | 21 | And a parent element can be found by shifting a bit 1 to the right. 22 | 23 | There are two kinds of binary heaps 24 | 25 | max-heaps 26 | the value of a node is never greater than its parent 27 | --so the root is the greatest value of the heap 28 | 29 | min-heaps 30 | a parent of a node is always smaller than both of its children 31 | --so the root is the smallest value of the heap 32 | 33 | 34 | height of a heap is the height of its root 35 | the height of any node is the traversal to the shortest element 36 | -------------------------------------------------------------------------------- /ch7/quicksort.rb: -------------------------------------------------------------------------------- 1 | ##quicksort is a divide and conquer algorithm, with o(1) memory 2 | ##we divide the array by a "pivot" and sort relative to the pivot.. 3 | ##the left and right hand side is in an order, and those are called into subsequent calls of quicksort (divide) 4 | ##these two new calls to quicksort subsequently sort about a new pivot. (conquer) 5 | ##as the array gets smaller(from n < 3) then we know definitively that the array is sorted and we return the sorted array to preceding calls (combine) 6 | ##the left and right sides are combined as sorted arrays into one array, and these combined arrays are returned to previous calls of quicksort 7 | ##quicksort sorts in place 8 | 9 | ##cormen implementation 10 | 11 | ##this implementation is subject to the choice of the initial pivot. if the last element is the largest or smallest, then we have an unbalanced partition. 12 | 13 | def quicksort(arr, p, r) 14 | if p < r 15 | q = partition(arr, p, r) 16 | quicksort(arr,p, q - 1) 17 | quicksort(arr, q + 1, r) 18 | end 19 | arr 20 | end 21 | 22 | 23 | def partition(arr, p, r) 24 | x = arr[r] ##we will watch the end of the array this is hte pivot 25 | i = p - 1 ##start at zero 26 | (p..r - 1).each do |j| 27 | if arr[j] <= x #if the value we are looking at, is less than x 28 | i += 1 29 | arr[i], arr[j] = arr[j], arr[i] ##swap it with the ith place, move it down the line 30 | end 31 | end 32 | arr[i + 1], arr[r] = arr[r], arr[i + 1] 33 | ##so i represents a dividing point, after i all values are greater than x, before i all values are less than or equal 34 | ##i + 1 represents the first element (not the next greatest, necessarily...) that is greater than a[r], so we will return this element 35 | i + 1 ##so what we are reutnring is the index of the PIVOT 36 | end 37 | 38 | ##ruby has a partition function 39 | def quicksort_r(arr) 40 | return arr if arr.size <= 1 41 | pivot = arr.shift 42 | left, right = arr.partition{|el| el <= pivot} 43 | quicksort_r(left) + [pivot] + quicksort_r(right) 44 | end 45 | 46 | p quicksort_r([5,4,5,5,523,537]) 47 | -------------------------------------------------------------------------------- /ch8/counting_sort.rb: -------------------------------------------------------------------------------- 1 | ##counting sort is dependent on the number of elements, plus how big your guess is of what's inside / the maximum 2 | 3 | def counting_sort(arr, k) 4 | sorted_array = [] 5 | ##first we construct a temporary array, this will hold our comparisons 6 | tmp = Array.new(k) {0} #code runs k times 7 | arr.each do |element| #code runs n times 8 | tmp[element - 1] += 1 9 | end 10 | p tmp 11 | ##array 0 represents the first, so we go through element and count how many of each element there is 12 | tmp.each_with_index do |element, index| #n times 13 | prior_index = (index - 1 < 0) ? nil : (index - 1) 14 | tmp[index] = tmp[index] + tmp[prior_index] if prior_index 15 | end 16 | #now since we know how many elements there of an element, you can count how many are less than by successively adding the previous sum (if there is one) 17 | ##this part right here is super important, it makes counting_sort STABLE. that means the order that the elements appear int he final sorted_array is the same as they appear in the input array 18 | (0...arr.length).each do |j| #n times 19 | sorted_array[tmp[arr[j] - 1] - 1] = arr[j] 20 | tmp[arr[j] - 1] -= 1 21 | sorted_array 22 | end 23 | sorted_array 24 | end 25 | 26 | ##radix sort taken from 27 | ##https://medium.com/@tyguyo/all-sorts-of-sorts-5da9873aa046 28 | def radix_sort(list) 29 | passes = (list.max == 0) ? 1 : Math.log10(list.max).to_i + 1 30 | new_list = [] 31 | passes.times do |i| 32 | buckets = make_buckets 33 | list.each do |n| 34 | p get_digit(n, i) 35 | digit = get_digit(n, i) 36 | buckets[digit] += [n] 37 | end 38 | p buckets 39 | new_list, buckets = buckets.flatten, make_buckets 40 | end 41 | new_list 42 | end 43 | def make_buckets 44 | Array.new(10,[]) 45 | end 46 | def get_digit(n, i) 47 | n % (10 ** (i + 1)) / (10 ** i) 48 | end 49 | 50 | p radix_sort([5,6,4,3,27,0,9]) 51 | -------------------------------------------------------------------------------- /ch8/linear_sort.txt: -------------------------------------------------------------------------------- 1 | Algorithms with comparison sorts can be viewed as decision trees where comparisons are held between elements. 2 | 3 | In the decision tree, the height of the decision tree represents the worst possible number of comparisons it takes to get to the solution. 4 | 5 | Every comparison sort has at worst an O(n log n) solution, so in order to optimize we need to sort an array differently. 6 | 7 | These are the non-comparison sorts 8 | -------------------------------------------------------------------------------- /data_structures/bst.rb: -------------------------------------------------------------------------------- 1 | require 'pry-byebug' 2 | 3 | class BSTNode 4 | attr_accessor :left, :right, :parent 5 | attr_reader :value 6 | def initialize(value) 7 | self.value = value 8 | end 9 | 10 | def inspect 11 | self.value 12 | end 13 | protected 14 | attr_writer :value 15 | end 16 | 17 | 18 | class BST 19 | attr_reader :root 20 | def initialize 21 | self.root = nil 22 | end 23 | 24 | def empty? 25 | @root.nil? 26 | end 27 | 28 | def include?(value) 29 | node, parent = find(value) 30 | !!node 31 | end 32 | 33 | def insert(value) 34 | if empty? 35 | @root = BSTNode.new(value) 36 | return true 37 | end 38 | node, parent = find(value) 39 | return false if include?(value) 40 | 41 | new_node = BSTNode.new(value) 42 | new_node.parent = parent 43 | if new_node.value < parent.value 44 | parent.left = new_node 45 | else 46 | parent.right = new_node 47 | end 48 | end 49 | 50 | def print_flipped 51 | @curved = [] 52 | recurse 53 | end 54 | 55 | def recurse(node=@root, level=0) 56 | result = '' 57 | return result unless node 58 | (0...level).each do |j| 59 | if @curved[j] 60 | result += ( j == level - 1 ? '`---' : ' ' * 4 ) 61 | else 62 | result += ( j == level - 1 ? '+---' : '| ' ) 63 | end 64 | end 65 | result += "#{node.value}\n" 66 | if node.right 67 | @curved[level] = false 68 | result += recurse(node.right, level + 1) 69 | end 70 | if node.left 71 | @curved[level] = true 72 | result += recurse(node.left, level + 1) 73 | end 74 | 75 | 76 | result 77 | end 78 | 79 | def inspect 80 | self.to_s 81 | end 82 | 83 | def to_s 84 | if empty? 85 | "[empty bst]" 86 | else 87 | res , = to_s_rec(root) 88 | res.shift 89 | res.join "\n" 90 | end 91 | puts res 92 | end 93 | 94 | def to_s_rec(node) 95 | str = "#{node.value}" 96 | str = " " if str.empty? 97 | 98 | if node.left 99 | l, wl = to_s_rec(node.left) 100 | else 101 | return [["|".center(str.size) , str], str.size] 102 | end 103 | if node.right 104 | r, wr = to_s_rec(node.right) 105 | else 106 | r, wr = [], -1 107 | end 108 | 109 | sumw = wl + wr + 1 110 | w = [sumw, str.size].max 111 | indent = (w - sumw) / 2 112 | res = merge_rows(l, r, indent, indent + wl + 1) 113 | 114 | vert = "|".center(w) 115 | 116 | con = res[0].gsub("|", "+") 117 | con[vert.index("|")] = ?+ 118 | con.sub!(/\+(.+)\+/) {|s| s.gsub(" ", "-")} 119 | [[vert, str.center(w), vert, con] + res, w] 120 | end 121 | 122 | def merge_rows(rows1, rows2, p1, p2) 123 | i = 0 124 | res = [] 125 | while i < rows1.size || i < rows2.size 126 | res << " " * p1 127 | res.last << rows1[i] if i < rows1.size 128 | if i < rows2.size 129 | res.last << " " * [0, p2 - res.last.size].max 130 | res.last << rows2[i] 131 | end 132 | i += 1 133 | end 134 | res 135 | end 136 | 137 | def kth_smallest(k) 138 | node = self.root 139 | stack = [node] 140 | until stack.empty? 141 | if node 142 | stack << node.left 143 | node = node.left 144 | else 145 | node = stack.pop 146 | k -= 1 147 | return node if k == 0 148 | node = node.right 149 | end 150 | end 151 | raise "element not in tree" 152 | end 153 | 154 | def in_order_traversal(node = @root) 155 | return if node.nil? 156 | in_order_traversal(node.left) 157 | puts node.value.to_s 158 | in_order_traversal(node.right) 159 | end 160 | 161 | def post_order_traversal(node = @root) 162 | return if node.nil? 163 | puts node.value.to_s 164 | post_order_traversal(node.left) 165 | post_order_traversal(node.right) 166 | end 167 | 168 | def pre_order_traversal(node = @root) 169 | return if node.nil? 170 | pre_order_traversal(node.left) 171 | pre_order_traversal(node.right) 172 | puts node.value.to_s 173 | end 174 | 175 | protected 176 | attr_writer :root 177 | def find(value) 178 | parent = nil 179 | node = self.root 180 | until node.nil? 181 | break if node.value == value 182 | parent = node 183 | if value < node.value 184 | node = node.left 185 | else 186 | node = node.right 187 | end 188 | end 189 | [node, parent] 190 | end 191 | 192 | end 193 | 194 | 195 | a = BST.new 196 | 197 | b = (0..100).to_a.sample(80).shuffle! 198 | b.each{|val| a.insert(val)} 199 | 200 | a.to_s 201 | -------------------------------------------------------------------------------- /data_structures/dynamic_array.rb: -------------------------------------------------------------------------------- 1 | require_relative "static_array" 2 | ##implementation taken from aA 3 | 4 | class DynamicArray 5 | 6 | def initialize 7 | @store, @capacity, @length = StaticArray.new(8), 8, 8 8 | end 9 | 10 | 11 | def [](index) 12 | check_index(index) 13 | self.store[index] 14 | end 15 | 16 | def []=(index) 17 | check_index(index) 18 | self.store[index] = value 19 | end 20 | #0(1) 21 | def pop 22 | raise "index out of bounds" unless (length > 0) 23 | val, self[length - 1] = self[length - 1], nil 24 | self.length -= 1 25 | val 26 | end 27 | 28 | #O(n) at worst 29 | def push(val) 30 | resize! if length == capacity 31 | 32 | self.length += 1 33 | self[length - 1] = val 34 | 35 | nil 36 | end 37 | 38 | #O(n) 39 | def shift 40 | raise "index out of bounds" if (length == 0) 41 | val = self[0] 42 | self.length -= 1 43 | (1..self.length).each { |i| self [i - 1] = self[i] } 44 | self.length -= 1 45 | val 46 | end 47 | 48 | #O(n) 49 | def unshift(val) 50 | resize! if length == capacity 51 | self.length += 1 52 | (length - 2).downto(0).each { |i| self[i + 1] = self[i] } 53 | self[0] = val 54 | nil 55 | end 56 | 57 | 58 | protected 59 | attr_accessor :capacity, :store 60 | attr_writer :length 61 | 62 | def check_index(index) 63 | raise "index out of bounds" unless index >= 0 && index < length 64 | end 65 | 66 | def resize! 67 | ##grow the array by two 68 | new_capacity = capacity * 2 69 | new_store = StaticArray.new(new_capacity) 70 | length.times { |i| new_store[i] = self[i] } 71 | self.capacity = new_capacity 72 | self.store = new_store 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /data_structures/graph.rb: -------------------------------------------------------------------------------- 1 | class Vertex 2 | attr_reader :value, :in_edges, :out_edges 3 | 4 | def initialize(value) 5 | @value, @in_edges, @out_edges = value, [], [] 6 | end 7 | 8 | def inspect 9 | self.value 10 | end 11 | end 12 | 13 | class Edge 14 | attr_reader :to_vertex, :from_vertex, :cost 15 | 16 | def initialize(from_vertex, to_vertex, cost = 1) 17 | self.from_vertex = from_vertex 18 | self.to_vertex = to_vertex 19 | self.cost = cost 20 | 21 | to_vertex.in_edges << self 22 | from_vertex.out_edges << self 23 | end 24 | 25 | def destroy! 26 | self.to_vertex.out_edges.delete(self) 27 | self.to_vertex = nil 28 | self.from_vertex.in_edges.delete(self) 29 | self.from_vertex = nil 30 | end 31 | 32 | def inspect 33 | [self.from_vertex.value, self.to_vertex.value] 34 | end 35 | protected 36 | attr_writer :from_vertex, :to_vertex, :cost 37 | end 38 | -------------------------------------------------------------------------------- /data_structures/graph_bfs.rb: -------------------------------------------------------------------------------- 1 | require_relative "graph" 2 | def graph_bfs(source) 3 | last_edges = { 4 | source => nil 5 | } 6 | queue = [source] 7 | until queue.empty? 8 | vertex = queue.shift 9 | 10 | vertex.out_edges.each do |outer_edge| 11 | to_vertex = outer_edge.to_vertex 12 | next if last_edges.has_key?(to_vertex) 13 | 14 | last_edges[to_vertex] = outer_edge 15 | queue << to_vertex 16 | end 17 | end 18 | p last_edges 19 | end 20 | 21 | 22 | vertices = [] 23 | vertices << (v1 = Vertex.new("A")) 24 | vertices << (v2 = Vertex.new("B")) 25 | vertices << (v3 = Vertex.new("C")) 26 | vertices << (v4 = Vertex.new("D")) 27 | vertices << (v5 = Vertex.new("E")) 28 | 29 | e1 = Edge.new(v1, v2) 30 | e2 = Edge.new(v2, v3) 31 | e3 = Edge.new(v2, v4) 32 | e4 = Edge.new(v4, v5) 33 | # 34 | # fail unless graph_bfs(v1) == { 35 | # v1 => nil, 36 | # v2 => e1, 37 | # v3 => e2, 38 | # v4 => e3, 39 | # v5 => e4 40 | # } 41 | graph_bfs(v2) 42 | -------------------------------------------------------------------------------- /data_structures/linked_list.rb: -------------------------------------------------------------------------------- 1 | class Link 2 | 3 | def initialize(value) 4 | self.value = value 5 | end 6 | 7 | 8 | 9 | end 10 | 11 | 12 | class LinkedList 13 | 14 | end 15 | -------------------------------------------------------------------------------- /data_structures/memory.rb: -------------------------------------------------------------------------------- 1 | ##just a couple of expriments in ruby to determine the relative sizes of things 2 | 3 | require 'objspace' 4 | 5 | puts RUBY_VERSION 6 | 7 | require 'objspace' 8 | 9 | p ObjectSpace.memsize_of("a"*23) 10 | p "string after me" 11 | p ObjectSpace.memsize_of("abc"*20) 12 | p "number after me " 13 | p ObjectSpace.memsize_of(1111111**24) 14 | p ObjectSpace.memsize_of("a".*1000) 15 | p ObjectSpace.memsize_of("BLACK").to_f == ObjectSpace.memsize_of(false).to_f 16 | -------------------------------------------------------------------------------- /data_structures/red_black.rb: -------------------------------------------------------------------------------- 1 | class RedBlackNode 2 | attr_accessor :color, :left, :right, :parent 3 | def initialize(value, color="RED") 4 | self.value 5 | self.color = "RED" 6 | end 7 | end 8 | 9 | class RedBlackTree 10 | def initialize 11 | @root = nil 12 | end 13 | 14 | def insert(value) 15 | 16 | end 17 | -------------------------------------------------------------------------------- /data_structures/red_black_tree.rb: -------------------------------------------------------------------------------- 1 | require_relative "bst" 2 | class RBNode < BSTNode 3 | 4 | def initialize(value) 5 | self.color = "RED" 6 | super 7 | end 8 | 9 | end 10 | 11 | class RBT < BST 12 | 13 | def initialize 14 | self.root = nil 15 | end 16 | 17 | 18 | 19 | end 20 | -------------------------------------------------------------------------------- /data_structures/ring_buffer.rb: -------------------------------------------------------------------------------- 1 | require_relative "static_array" 2 | 3 | 4 | class CircularBuffer 5 | 6 | attr_reader :length 7 | 8 | def initialize 9 | self.store, self.capacity = StaticArray.new(8),8 10 | self.start_idx, self.length = 0, 0 11 | end 12 | 13 | 14 | def [](index) 15 | check_index(index) 16 | store[(start_idx + index) % capacity] 17 | end 18 | 19 | def []=(index, val) 20 | check_index(index) 21 | store[(start_idx + index) % capacity] = val 22 | end 23 | 24 | def pop 25 | raise "index out of bounds" if (length == 0) 26 | 27 | val, self[length - 1] = self[length - 1], nil 28 | self.length -= 1 29 | 30 | val 31 | end 32 | 33 | 34 | 35 | end 36 | -------------------------------------------------------------------------------- /data_structures/set.rb: -------------------------------------------------------------------------------- 1 | ##A set has certain methods 2 | ##insert, include, and delete 3 | ##unordered collection of values with no duplicates 4 | ##you get the benefits of hash's fast-lookup 5 | ##with the iteration of an array 6 | ##you must require the set it is not standard 7 | require 'set' 8 | 9 | class ArraySet 10 | def initialize 11 | @store = [] 12 | end 13 | 14 | def include?(el) 15 | @store.any?{ |el2| el == el2 } 16 | end 17 | 18 | def add(el) 19 | return false if self.include?(el) 20 | @store << el 21 | true 22 | end 23 | 24 | def delete(el) 25 | return false if !self.include?(el) 26 | @store.delete(el) 27 | true 28 | end 29 | 30 | end 31 | 32 | 33 | ##with this kind of set we have two problems 34 | ##the first is we are restricted to only numbers. Integers, in fact. 35 | ##the second is that if the range of numbers is big, it makes more effort in memory to store them 36 | ##therefore, this approach takes up considerable memory dependent upon the range of values 37 | class MaxIntSet 38 | 39 | def initialize(max) 40 | @store = Array.new(max, false) 41 | end 42 | 43 | def include?(el) 44 | @store[el] 45 | end 46 | 47 | def add(el) 48 | @store[el] = true 49 | end 50 | 51 | def delete(el) 52 | @store[el] = false 53 | end 54 | 55 | end 56 | 57 | 58 | #for n elements, every bucket has essentially n/8. 59 | ## what does htis mean? it means that we are approaching linear time. (Why?) 60 | 61 | class IntSet 62 | 63 | def initialize 64 | @buckets = Array.new(8), {[]} 65 | end 66 | 67 | def include?(el) 68 | bucket = @buckets[num % buckets.length] 69 | bucket.include?(num) 70 | end 71 | 72 | def delete(el) 73 | return false unless self.include?(el) 74 | bucket = @buckets[num % buckets.length] 75 | bucket.delete(el) 76 | true 77 | end 78 | 79 | def add(el) 80 | return false if self.include?(el) 81 | bucket = @buckets[num % buckets.length] 82 | bucket << el 83 | true 84 | end 85 | 86 | 87 | 88 | end 89 | 90 | 91 | 92 | 93 | ##we need to use a hash-set to have O(1) operations 94 | ##sets are convenient in finding duplicates. you can create a set and then yield values from it. 95 | -------------------------------------------------------------------------------- /data_structures/static_array.rb: -------------------------------------------------------------------------------- 1 | ##a static array has a fixed length and can only hold up to n items 2 | 3 | class StaticArray 4 | def initialize(length) 5 | @store = Array.new(length, nil) 6 | end 7 | 8 | #getting is O(1) because there is only one memory address being accessed 9 | def [](index) 10 | @store[index] 11 | end 12 | 13 | #setting is O(1) because there is only one memory address being accessed 14 | def []=(index, value) 15 | @store[index] = value 16 | end 17 | 18 | 19 | protected 20 | attr_accessor :store 21 | end 22 | -------------------------------------------------------------------------------- /data_structures/topological_sort.rb: -------------------------------------------------------------------------------- 1 | require_relative "./graph" 2 | require 'pp' 3 | def topological_sort(vertices) 4 | in_edge_counts = {} 5 | queue, sorted_vertices = [], [] 6 | 7 | vertices.each do |vertex| 8 | vertex 9 | in_edge_counts[vertex] = vertex.in_edges.count 10 | queue << vertex if vertex.in_edges.empty? 11 | end 12 | 13 | until queue.empty? 14 | vertex = queue.shift 15 | sorted_vertices << vertex 16 | 17 | vertex.out_edges.each do |outer_edge| 18 | ##change the in count of any outgoing vertices 19 | to_vertex = outer_edge.to_vertex 20 | in_edge_counts[to_vertex] -= 1 21 | queue << to_vertex if in_edge_counts[to_vertex] == 0 22 | end 23 | end 24 | sorted_vertices 25 | end 26 | 27 | vertices = [v1 = Vertex.new("hey"), v2 = Vertex.new("hi"), v3 = Vertex.new("sup"), v4 = Vertex.new("hello there bby")] 28 | Edge.new(v1, v3) 29 | Edge.new(v1, v2) 30 | Edge.new(v2, v4) 31 | Edge.new(v3, v4) 32 | 33 | topological_sort(vertices.shuffle).each do |vertex| 34 | p vertex.value 35 | end 36 | -------------------------------------------------------------------------------- /problems/javascript/Algorthim.js: -------------------------------------------------------------------------------- 1 | // Given a string of text (let's call it haystack), and an array of other strings (let's call them needles), find all the needles in the haystack. 2 | //The output of the function should be the input string but have each occurrence wrapped in HTML tags (eg: ). The output must be valid HTML. 3 | 4 | // function highlight(haystack: string, needles: Array): string {} 5 | 6 | // highlight("abcdefgabxyz", ["ab", "ga", "bcd"]); // "abcdefgabxyz" 7 | //abcd ==> ab + bcd 8 | // e not matched 9 | // gab ==> ga + bcd 10 | // function replaceAt(str, index, value) { 11 | // let strArr = str.split(""); 12 | // strArr.splice(index, 0, value); 13 | // return strArr.join(""); 14 | // // return str.substring(0, index) + value, +str.substring(index + 1); 15 | // } 16 | 17 | // function isBetween(num, first, last){ 18 | // return num >= first && num <= last 19 | // } 20 | 21 | // function highlight(haystack, needles) { 22 | // let highlighted = []; 23 | // for (let i = 0; i < needles.length; i++) { 24 | // let needle = needles[i]; 25 | // let re = new RegExp(needle, "g"); 26 | // let match = re.exec(haystack); 27 | // while (match) { 28 | // const firstIndex = match.index; 29 | // const length = match[0].length; 30 | // const lastIndex = length - 1 + firstIndex; 31 | // highlighted.push([firstIndex, lastIndex]); 32 | // match = re.exec(haystack); 33 | // } 34 | // } 35 | // highlighted.sort((a, b) => a[0] - b[0]); 36 | // const mergedHighlighted = highlighted.reduce((accum, b) => { 37 | // if (accum.length === 0){ 38 | // accum.push(b); 39 | // return accum; 40 | // } 41 | // const a = accum[accum.length - 1]; 42 | // const [sA, fA] = a; 43 | // const [sB, fB] = b; 44 | // if (isBetween(fA, sB, fB) || isBetween(sB, sA, fA)){ 45 | // accum.pop() 46 | // accum.push([Math.min(sA, sB), Math.max(fA, fB)]); 47 | // } else { 48 | // accum.push(b); 49 | // } 50 | // return accum; 51 | // }, []); 52 | // let haystackSplit = haystack.split(""); 53 | // let numAdded = 0; 54 | // for (let i = 0; i < mergedHighlighted.length; i++) { 55 | // let [firstIndex, lastIndex] = mergedHighlighted[i]; 56 | // haystackSplit.splice(firstIndex + numAdded, 0, ""); 57 | // numAdded++; 58 | // haystackSplit.splice(lastIndex + numAdded + 1, 0, ""); 59 | // numAdded++; 60 | // } 61 | // return haystackSplit.join(""); 62 | // } 63 | // Given a string of text (let's call it haystack), and an array of other strings (let's call them needles), find all the needles in the haystack. 64 | //The output of the function should be the input string but have each occurrence wrapped in HTML tags (eg: ). The output must be valid HTML. 65 | 66 | // function highlight(haystack: string, needles: Array): string {} 67 | 68 | // highlight("abcdefgabxyz", ["ab", "ga", "bcd"]); // "abcdefgabxyz" 69 | //abcd ==> ab + bcd 70 | // e not matched 71 | // gab ==> ga + bcd 72 | 73 | 74 | -------------------------------------------------------------------------------- /problems/javascript/Atoi.js: -------------------------------------------------------------------------------- 1 | //only first valid characters are whitespace, -, + or numbers" 2 | ` 3 | Implement atoi which converts a string to an integer. 4 | 5 | The function first discards as many whitespace characters as necessary until the first non-whitespace character is found. Then, starting from this character, takes an optional initial plus or minus sign followed by as many numerical digits as possible, and interprets them as a numerical value. 6 | 7 | The string can contain additional characters after those that form the integral number, which are ignored and have no effect on the behavior of this function. 8 | 9 | If the first sequence of non-whitespace characters in str is not a valid integral number, or if no such sequence exists because either str is empty or it contains only whitespace characters, no conversion is performed. 10 | 11 | If no valid conversion could be performed, a zero value is returned. 12 | 13 | Note: 14 | 15 | Only the space character ' ' is considered as whitespace character. 16 | Assume we are dealing with an environment which could only store integers within the 32-bit signed integer range: [−231, 231 − 1]. If the numerical value is out of the range of representable values, INT_MAX (231 − 1) or INT_MIN (−231) is returned. 17 | Example 1: 18 | 19 | Input: "42" 20 | Output: 42 21 | Example 2: 22 | 23 | Input: " -42" 24 | Output: -42 25 | Explanation: The first non-whitespace character is '-', which is the minus sign. 26 | Then take as many numerical digits as possible, which gets 42. 27 | Example 3: 28 | 29 | Input: "4193 with words" 30 | Output: 4193 31 | Explanation: Conversion stops at digit '3' as the next character is not a numerical digit. 32 | Example 4: 33 | 34 | Input: "words and 987" 35 | Output: 0 36 | Explanation: The first non-whitespace character is 'w', which is not a numerical 37 | digit or a +/- sign. Therefore no valid conversion could be performed. 38 | Example 5: 39 | 40 | Input: "-91283472332" 41 | Output: -2147483648 42 | Explanation: The number "-91283472332" is out of the range of a 32-bit signed integer. 43 | Thefore INT_MIN (−231) is returned. 44 | ` 45 | 46 | /** 47 | * @param {string} str 48 | * @return {number} 49 | */ 50 | var myAtoi = function(str) { 51 | let INT_MIN = -Math.pow(2, 31); 52 | let INT_MAX = Math.pow(2, 31) - 1; 53 | const re = /^(\s+)?((-?|\+?)[0-9]+)/; 54 | const match = str.match(re); 55 | if (Boolean(match)){ 56 | const num = parseInt(match[0]); 57 | if (num > INT_MAX){ 58 | return INT_MAX; 59 | } else if (num < INT_MIN){ 60 | return INT_MIN; 61 | } else { 62 | return num; 63 | } 64 | } else { 65 | return 0; 66 | } 67 | }; 68 | 69 | 70 | console.log(myAtoi("42"), 42); 71 | console.log(myAtoi("+42"), 42); 72 | console.log(myAtoi(" -42"), -42); 73 | console.log(myAtoi("4193 with words"), 4193); 74 | console.log(myAtoi("words and 987"), 0); 75 | console.log(myAtoi("-91283472332"), -2147483648); 76 | console.log(myAtoi("2147483648"), 2147483647); 77 | -------------------------------------------------------------------------------- /problems/javascript/AutoComplete.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string[]} sentences 3 | * @param {number[]} times 4 | */ 5 | 6 | function constructSentences(sentences, times){ 7 | let s = new Map(); 8 | for (const [idx, val] of sentences.entries()){ 9 | s.set(val, times[idx]); 10 | } 11 | return s; 12 | } 13 | class AutocompleteSystem { 14 | constructor(sentences, times){ 15 | this.sentences = constructSentences(sentences, times); 16 | this.userInput = ''; 17 | this.cache = []; 18 | } 19 | filterResults(){ 20 | const re = new RegExp(`^${this.userInput}`); 21 | if (this.userInput === '') { 22 | //we have finished a sentence 23 | return [] 24 | } else { 25 | if (this.userInput.length === 1){ 26 | this.cache = filterCache(constructCache(this.sentences),re) 27 | } else { 28 | this.cache = filterCache(this.cache, re); 29 | } 30 | return searchResults(this.cache); 31 | } 32 | } 33 | input(c){ 34 | if (c === '#'){ 35 | const val = this.sentences.get(this.userInput); 36 | if (val === undefined){ 37 | this.sentences.set(this.userInput, 1); 38 | } else { 39 | this.sentences.set(this.userInput, val + 1); 40 | } 41 | this.userInput = ''; 42 | this.cache = []; 43 | } else { 44 | this.userInput = this.userInput + c; 45 | } 46 | return this.filterResults(); 47 | } 48 | } 49 | function findFirstDifference(k1, k2){ 50 | if (k1.length > k2.length){ 51 | return findFirstDifference(k2, k1); 52 | } 53 | 54 | for (let i = 0; i < k1.length; i++){ 55 | const k1Code = k1.charCodeAt(i); 56 | const k2Code = k2.charCodeAt(i); 57 | if (k1Code > k2Code){ 58 | return k2; 59 | } else if (k1Code < k2Code){ 60 | return k1; 61 | } 62 | } 63 | return k1; 64 | } 65 | function filterCache(cache, re){ 66 | return cache.filter((a) => { 67 | const [k, v] = a; 68 | return k.match(re); 69 | }); 70 | } 71 | function constructCache(sentences){ 72 | return [...sentences.entries()]; 73 | } 74 | function searchResults(cache){ 75 | return cache.sort(([k1, v1], [k2, v2]) => { 76 | const diff = v2 - v1; 77 | if (diff === 0){ 78 | const val = findFirstDifference(k1, k2); 79 | return val === k1 ? -1 : 1; 80 | } else { 81 | return diff; 82 | } 83 | }) 84 | .slice(0, 3) 85 | .map(([k,v]) => k); 86 | } 87 | -------------------------------------------------------------------------------- /problems/javascript/BalancePoint.js: -------------------------------------------------------------------------------- 1 | /* 2 | 1. Array balance point. 3 | Find, if possible, an index in the array such that 4 | the sum of all integers to left of the index 5 | is equal to the sum of all the integers right of the index. e.g. 6 | 7 | balancePoint([5, 4, 0, 19, 3, 4, 2, 0]) = 3 8 | balancePoint([5, 4, 2, 1]) = -1 9 | */ 10 | 11 | // const sum = () => { 12 | // return arr.reduce((prev, next) => { 13 | // prev + next 14 | // }, 0); 15 | // }; 16 | // 17 | // const findDuplicates = (arr) => { 18 | // let dupMap = {}; 19 | // return arr.find((elem) => { 20 | // if (dupMap[elem]){ 21 | // return true; 22 | // } 23 | // dupMap[elem] = true; 24 | // return false; 25 | // }); 26 | // }; 27 | // function balancePoint(arr){ 28 | // let sumMap = {}; 29 | // let forwardSum = null; 30 | // let backwardSum = null; 31 | // for (let fInt=1; fInt < arr.length; fInt++) { 32 | // let bInt = arr.length - 1 - fInt; 33 | // let forwardVal = arr[fInt - 1]; 34 | // let rightVal = arr[bInt + 1]; 35 | // if (Number.isInteger(forwardVal)) { 36 | // forwardSum = forwardSum ? (forwardVal + forwardSum) : forwardVal; 37 | // sumMap[forwardSum] = sumMap[forwardSum] ? sumMap[forwardSum].concat(fInt) : [fInt]; 38 | // } 39 | // if (Number.isInteger(rightVal)) { 40 | // backwardSum = backwardSum ? (rightVal + backwardSum) : rightVal; 41 | // sumMap[backwardSum] = sumMap[backwardSum] ? sumMap[backwardSum].concat(bInt) : [bInt]; 42 | // } 43 | // } 44 | // 45 | // for (let [sum, indices] of Object.entries(sumMap)) { 46 | // const firstDupe = findDuplicates(indices); 47 | // if (firstDupe){ 48 | // return firstDupe 49 | // } 50 | // } 51 | // return -1; 52 | // } 53 | 54 | function balancePoint(arr) { 55 | const store1 = {}; 56 | const store2 = {}; 57 | let sum1 = 0; 58 | let sum2 = 0; 59 | for (let i = 0; i < arr.length - 1; i++) { 60 | const elem = arr[i]; 61 | const rightIdx = arr.length - 1 - i; 62 | const secondElem = arr[rightIdx]; 63 | sum1 += elem; 64 | sum2 += secondElem 65 | store1[i] = sum1; 66 | store2[rightIdx] = sum2; 67 | } 68 | 69 | return findCommonSums(store1, store2); 70 | } 71 | 72 | function findCommonSums(store1, store2) { 73 | for (let [rightIdx, rightVal] of Object.entries(store2)) { 74 | // O(N) 75 | const leftVal = store1[rightIdx - 2]; 76 | if (leftVal === rightVal) { 77 | //we have a balance point 78 | return rightIdx - 1; 79 | } 80 | } 81 | return -1; 82 | } 83 | 84 | console.log(balancePoint([5, 4, 0, 19, 3, 4, 2, 0]), 3); 85 | console.log(balancePoint([5, 4, 2, 1]), -1); 86 | console.log(balancePoint([7, 10, 1, 1, 1, 1, 1, 1, 1]), 1) 87 | console.log(balancePoint([2, 3, 4, 7, 4, 3, 2]), 3) 88 | -------------------------------------------------------------------------------- /problems/javascript/BalancedParenthesis.js: -------------------------------------------------------------------------------- 1 | // Given a string that contains only the characters "(" and ")", determine if the sequence of parentheses is balanced. 2 | // Having balanced parentheses means that for each opening symbol there is a corresponding closing symbol. 3 | // Pairs need to be properly nested, containing only matching pairs, to be valid. 4 | 5 | // ) // invalid (no opening parentheses) 6 | // () // valid (every opening symbol has a valid and matching closing symbol) 7 | // ()() // valid (parentheses both match) 8 | // ()(()) // valid (parentheses are properly nested) 9 | // )()( // invalid (there are unmatched parentheses) 10 | // ((()) // invalid (too many opening parentheses) 11 | 12 | function isBalanced(str) { 13 | const opener = '('; 14 | let openerStack = []; 15 | for (let character of str){ 16 | if (character === opener) { 17 | openerStack.push(character); 18 | } else { 19 | const lastOpener = openerStack.pop(); 20 | if (Boolean(lastOpener) === false){ 21 | return false; 22 | } 23 | } 24 | } 25 | return !Boolean(openerStack.length); 26 | } 27 | console.assert(isBalanced(')') === false, '1') 28 | console.assert(isBalanced('()')=== true, '2') 29 | console.assert(isBalanced('()()')=== true, '3') 30 | console.assert(isBalanced('()(())')=== true, '4') 31 | console.assert(isBalanced(')()(')=== false, '5') 32 | console.assert(isBalanced('((())')=== false, '6'); 33 | 34 | 35 | function isSuperBalanced(str) { 36 | const openerSet = ['(', '[', '{']; 37 | const openerCloserMap = { 38 | '{': '}', 39 | '(': ')', 40 | '[': ']' 41 | }; 42 | let openerStack = []; 43 | for (let character of str){ 44 | if (openerSet.includes(character)) { 45 | openerStack.push(character); 46 | } else { 47 | const lastOpener = openerStack.pop(); 48 | const correspondingCloser = openerCloserMap[lastOpener]; 49 | if (correspondingCloser !== character){ 50 | return false; 51 | } 52 | } 53 | } 54 | return !Boolean(openerStack.length); 55 | } 56 | 57 | 58 | console.log(isSuperBalanced('[()]{}'), true, '1'); 59 | console.log(isSuperBalanced('{([])}'), true, '2'); 60 | console.log(isSuperBalanced('(][)'), false, '3'); 61 | console.log(isSuperBalanced('[(])'), false, '4'); 62 | -------------------------------------------------------------------------------- /problems/javascript/BinaryReversal.js: -------------------------------------------------------------------------------- 1 | let binary = Array(20).fill(1); 2 | binary = binary.map((e, i) => e * Math.pow(8, i+1)); 3 | 4 | function BinaryReversal(str) { 5 | const int = parseInt(str); 6 | let binary = int.toString(2); 7 | while (binary.length < ){ 8 | binary = "0" + binary; 9 | } 10 | 11 | let reversed = binary.split("").reverse().join(); 12 | 13 | const revInt = parseInt(reversed, 2);; 14 | console.log('revint', revInt); 15 | // code goes here 16 | return revInt; 17 | } 18 | console.log(BinaryReversal("213"), 171); 19 | console.log(BinaryReversal("4567"), 60296); -------------------------------------------------------------------------------- /problems/javascript/BoundaryPerimeter.js: -------------------------------------------------------------------------------- 1 | // An algorithm to calculate the permimeter of a shape 2 | 3 | function cross(a, b, o) { 4 | return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]) 5 | } 6 | function convexHull(points) { 7 | points.sort(function(a, b) { 8 | return a[0] == b[0] ? a[1] - b[1] : a[0] - b[0]; 9 | }); 10 | 11 | var lower = []; 12 | for (var i = 0; i < points.length; i++) { 13 | while (lower.length >= 2 && cross(lower[lower.length - 2], lower[lower.length - 1], points[i]) <= 0) { 14 | lower.pop(); 15 | } 16 | lower.push(points[i]); 17 | } 18 | 19 | var upper = []; 20 | for (var i = points.length - 1; i >= 0; i--) { 21 | while (upper.length >= 2 && cross(upper[upper.length - 2], upper[upper.length - 1], points[i]) <= 0) { 22 | upper.pop(); 23 | } 24 | upper.push(points[i]); 25 | } 26 | 27 | upper.pop(); 28 | lower.pop(); 29 | return lower.concat(upper); 30 | } 31 | 32 | function calculateArea(p) { 33 | const l = p.length 34 | let det = 0 35 | 36 | if (p[0] !== p[p.length -1]) 37 | p = p.concat([p[0]]) 38 | 39 | for (var i = 0; i < l; i++) { 40 | det = det + (p[i][0] * p[i + 1][1]) - (p[i][1] * p[i + 1][0]); 41 | debugger; 42 | } 43 | return Math.abs(det/2); 44 | } 45 | 46 | var line; 47 | let points = []; 48 | while (line = readline()) { 49 | var nums = line.split(' '); 50 | if (nums.length === 1 && points.length === 0) { 51 | continue 52 | } 53 | if (nums.length === 2) { 54 | const point = nums.map(i => Number(i)); 55 | points.push(point); 56 | } 57 | if (nums.length === 1 && points.length !== 0) { 58 | // calculate stuff here 59 | const boundaryPoints = convexHull(points); 60 | const area = calculateArea(boundaryPoints); 61 | // print 62 | print(area); 63 | // then clear 64 | points = []; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /problems/javascript/CommonSubstring.js: -------------------------------------------------------------------------------- 1 | const a = ['a', 'b', 'c']; 2 | const b = ['b', 'c', 'd']; 3 | 4 | function interSection(arrA, arrB){ 5 | return arrA.filter((a) => arrB.includes(a)); 6 | } 7 | function interSectionMap(arrA, arrB){ 8 | const map = new WeakMap(); 9 | let common = []; 10 | for (let elem of arrA){ 11 | map.set(elem, true); 12 | } 13 | for (let elemB of arrB){ 14 | if (map.has(elemB)){ 15 | common.push(elemB); 16 | } 17 | } 18 | 19 | return common 20 | } 21 | 22 | function commonSubstring(haystack, needle){ 23 | let lastIndexFound = null; 24 | for (let char of needle){ 25 | if (haystack.indexOf(char) !== -1) { 26 | lastIndexFound = haystack.indexOf(char) 27 | haystack = haystack.slice(lastIndexFound); 28 | } else { 29 | return false; 30 | } 31 | } 32 | return true; 33 | } 34 | // console.log(interSectionMap(a, b), 'b, c') 35 | console.log(commonSubstring('aepple', 'ale'), true) 36 | console.log(commonSubstring('apple', 'ale'), true) 37 | console.log(commonSubstring('apple', 'ael'), false) 38 | -------------------------------------------------------------------------------- /problems/javascript/CountPairs.js: -------------------------------------------------------------------------------- 1 | // returns the number of pairs seen in order from least to greatest. 2 | // linear runtime, linear space. 3 | 4 | function countPairs(arr){ 5 | const dict = new Map(); 6 | for (let i = 0; i < arr.length; i++){ 7 | let value = arr[i]; 8 | let mVal = dict.get(value); 9 | if (mVal === undefined){ 10 | dict.set(value, [i]) 11 | } else { 12 | dict.set(value, mVal.concat([i])); 13 | } 14 | } 15 | let ans = 0; 16 | for (let [k, v] of dict.entries()){ 17 | if (v.length > 1){ 18 | let count = v.length; 19 | // this is a combinatorial trick, the number of ways to grab 2 distinct things from n 20 | // where n is the number of entries. 21 | let seen = Math.floor((count * count - 1)/2); 22 | ans += seen; 23 | } 24 | } 25 | return ans; 26 | } 27 | 28 | console.log(countPairs([3,3,4,5,2,5,3])); 29 | -------------------------------------------------------------------------------- /problems/javascript/CountSubstringsWithDistincLetter.js: -------------------------------------------------------------------------------- 1 | ``` 2 | Given a string S, return the number of substrings that have only one distinct letter. 3 | 4 | 5 | 6 | Example 1: 7 | 8 | Input: S = "aaaba" 9 | Output: 8 10 | Explanation: The substrings with one distinct letter are "aaa", "aa", "a", "b". 11 | "aaa" occurs 1 time. 12 | "aa" occurs 2 times. 13 | "a" occurs 4 times. 14 | "b" occurs 1 time. 15 | So the answer is 1 + 2 + 4 + 1 = 8. 16 | Example 2: 17 | 18 | Input: S = "aaaaaaaaaa" 19 | Output: 55 20 | 21 | 22 | Constraints: 23 | 24 | 1 <= S.length <= 1000 25 | S[i] consists of only lowercase English letters. 26 | ``` 27 | /** 28 | * @param {string} S 29 | * @return {number} 30 | */ 31 | const countLetters = function(S) { 32 | let currentNumberOfRepeatedElements, 33 | count = 0; 34 | for(let i = S.length - 1; i >= 0 ; i -= 1) { 35 | if(S[i + 1] !== S[i]) { 36 | currentNumberOfRepeatedElements = 0; 37 | } 38 | currentNumberOfRepeatedElements += 1; 39 | count += currentNumberOfRepeatedElements; 40 | } 41 | return count; 42 | }; 43 | -------------------------------------------------------------------------------- /problems/javascript/CurrencyRates.js: -------------------------------------------------------------------------------- 1 | let rates = { 2 | 'USD': { 3 | 'JPY': 108.43, 4 | }, 5 | 'AUD': { 6 | 'USD': 0.60 7 | }, 8 | 'JPY': { 9 | 'GBP': .0075 10 | }, 11 | 'KRU': { 12 | 'GBP': 0.00066 13 | } 14 | } 15 | 16 | function findPath(iCurr, tCurr, rates){ 17 | let paths = [[iCurr]]; 18 | let path = paths.pop(); 19 | while (!path.includes(tCurr)){ 20 | let lastCurrency = path[path.length - 1]; 21 | if (rates[lastCurrency] && rates[lastCurrency][tCurr]){ 22 | return path.concat(tCurr); 23 | } 24 | let entries = Object.entries(rates).filter(([k, v]) => { 25 | return !path.includes(k) 26 | }); 27 | for (let [key, value] of entries){ 28 | paths.unshift(path.concat(key)); 29 | } 30 | paths.pop(); 31 | } 32 | } 33 | 34 | function findRate(path, rates){ 35 | let r = [] 36 | let curr = path.shift(); 37 | while (path.length !== 0){ 38 | let pair = path.shift() 39 | let el; 40 | if (rates[curr] && rates[curr][pair]){ 41 | el = rates[curr][pair] 42 | } else { 43 | el = 1/rates[pair][curr]; 44 | } 45 | curr = pair; 46 | r.push(el); 47 | } 48 | return r; 49 | } 50 | 51 | function findCurrency(amount = 5, iCurr, tCurr, obj){ 52 | let rate; 53 | if (obj[iCurr] && obj[iCurr][tCurr]){ 54 | rate = obj[iCurr][tCurr]; 55 | return amount * rate; 56 | } 57 | if (obj[tCurr] && obj[tCurr][iCurr]){ 58 | rate = 1/obj[tCurr][iCurr]; 59 | return amount * rate; 60 | } 61 | const path = findPath(iCurr, tCurr, obj); 62 | const consecutiveRates = findRate(path, rates); 63 | return consecutiveRates.reduce((a, b) =>(a * b), amount); 64 | } 65 | console.log(findCurrency(undefined, 'JPY', 'KRU', rates)) 66 | -------------------------------------------------------------------------------- /problems/javascript/CustomBind.js: -------------------------------------------------------------------------------- 1 | Function.prototype.bind = null; 2 | function Cow(){ 3 | }; 4 | 5 | Cow.prototype.foo = function(arg1, arg2){ 6 | console.log(this.bar, arg1, arg2); 7 | } 8 | 9 | Function.prototype.bind = function (...rest){ 10 | return (...restArgs) => { 11 | const args = rest.concat(restArgs); 12 | return this.call(...args); 13 | }; 14 | } 15 | 16 | let r = new Cow() 17 | let foo = r.foo; 18 | let z = foo.bind({bar: 'hello'}, 'sally'); 19 | z('you suck') 20 | -------------------------------------------------------------------------------- /problems/javascript/Debounce.js: -------------------------------------------------------------------------------- 1 | //implements debounce, which cancels all previous calls 2 | 3 | function debounce(fn, t){ 4 | let id; 5 | return (...rest) => { 6 | if (id){ 7 | clearTimeout(id); 8 | } 9 | id = setTimeout(() => { 10 | fn.call(this, ...rest); 11 | id = null; 12 | }, t); 13 | } 14 | } 15 | 16 | function test(arg1, arg2){ 17 | console.log(arg1, arg2); 18 | } 19 | class Cow { 20 | constructor(){ 21 | this.moocall = 'lol'; 22 | } 23 | moo(arg1, arg2){ 24 | console.log(this.moocall, arg1, arg2); 25 | } 26 | } 27 | const r = new Cow(); 28 | debouncedTest = debounce(r.moo.bind(r), 10); 29 | debouncedTest('de','bounce1'); 30 | debouncedTest('de','bounce2'); 31 | debouncedTest('de','bounce3'); 32 | debouncedTest('de','bounce4'); 33 | debouncedTest('de','bounce5'); 34 | debouncedTest('de','bounce6'); 35 | let i = 1; 36 | setInterval(() => { 37 | i++; 38 | debouncedTest(i, 'interval'); 39 | }, 1000) 40 | -------------------------------------------------------------------------------- /problems/javascript/DefangingIp.js: -------------------------------------------------------------------------------- 1 | ``` 2 | Given a valid (IPv4) IP address, return a defanged version of that IP address. 3 | 4 | A defanged IP address replaces every period "." with "[.]". 5 | 6 | 7 | 8 | Example 1: 9 | 10 | Input: address = "1.1.1.1" 11 | Output: "1[.]1[.]1[.]1" 12 | Example 2: 13 | 14 | Input: address = "255.100.50.0" 15 | Output: "255[.]100[.]50[.]0" 16 | 17 | 18 | Constraints: 19 | 20 | The given address is a valid IPv4 address. 21 | ``` 22 | /** 23 | * @param {string} address 24 | * @return {string} 25 | */ 26 | var defangIPaddr = function(address) { 27 | return address.replace(/\./g, "[.]"); 28 | }; 29 | -------------------------------------------------------------------------------- /problems/javascript/DiameterOfTree.js: -------------------------------------------------------------------------------- 1 | ``` 2 | 543. Diameter of Binary Tree 3 | Easy 4 | 5 | 2280 6 | 7 | 145 8 | 9 | Add to List 10 | 11 | Share 12 | Given a binary tree, you need to compute the length of the diameter of the tree. 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. 13 | 14 | Example: 15 | Given a binary tree 16 | 1 17 | / \ 18 | 2 3 19 | / \ 20 | 4 5 21 | Return 3, which is the length of the path [4,2,1,3] or [5,2,1,3]. 22 | 23 | Note: The length of path between two nodes is represented by the number of edges between them. 24 | 25 | Accepted 26 | 219,579 27 | Submissions 28 | 454,372 29 | ``` 30 | //using postorder 31 | var diameterOfBinaryTree = function(root) { 32 | let max = root ? 1 : 0; 33 | function postOrder(node){ 34 | if (node === null) { 35 | return 0; 36 | } 37 | const left = postOrder(node.left); 38 | const right = postOrder(node.right); 39 | const path = left + right; 40 | if (path > max){ 41 | max = path; 42 | } 43 | return Math.max(left, right); 44 | } 45 | postOrder(root); 46 | return max; 47 | }; 48 | -------------------------------------------------------------------------------- /problems/javascript/DivideWinterSummer.js: -------------------------------------------------------------------------------- 1 | // let tempArr = [5, -2, 3, 8, 6] 2 | tempArr = [-5, -5, -5, -42, 6, 12] 3 | //return the shortest length of winter, where winter is defined as the point at which temperatures all are decreasing. 4 | function solution(tempArr){ 5 | let winterHigh = tempArr[0]; 6 | let overHigh = tempArr[0]; 7 | let winterLength = 0; 8 | for (let temp of tempArr) { 9 | //this means we have a new low, so we are still in winter. we record the current highest temperature. 10 | if (temp <= winterHigh) { 11 | // when this is true, we have a new low, therefore 12 | // we know we are still in winter 13 | // and must record the new winter high 14 | winterHigh = overHigh; 15 | } else if (temp > overHigh) { 16 | //this will always keep of the current highest temperature. this means 17 | overHigh=temp; 18 | } 19 | } 20 | 21 | for (let temp of tempArr){ 22 | //now since we have the winterhigh we can just count the number of temperatures 23 | //less than or equal to the winter high. 24 | if (temp <= winterHigh){ 25 | winterLength++; 26 | } 27 | } 28 | return winterLength; 29 | } 30 | -------------------------------------------------------------------------------- /problems/javascript/EventEmitter.js: -------------------------------------------------------------------------------- 1 | class EventEmitter { 2 | constructor(){ 3 | this.events = new Map(); 4 | } 5 | 6 | add(evtName, fn){ 7 | this.events.set(evtName, fn); 8 | } 9 | 10 | remove(evtName){ 11 | this.events.delete(evtName); 12 | } 13 | 14 | dispatch(evtName, ...rest){ 15 | try { 16 | const fn = this.events.get(evtName); 17 | fn.apply(null, rest); 18 | } catch (err) { 19 | console.error(err); 20 | } 21 | } 22 | } 23 | 24 | const fn = ({cool, bob}) => { 25 | console.log(`${cool}${bob}`) 26 | } 27 | 28 | const eventEmitter = new EventEmitter(); 29 | eventEmitter.add('click', fn); 30 | eventEmitter.dispatch('click', {cool: 'cool', bob: 'bob'}); 31 | eventEmitter.remove('click'); 32 | eventEmitter.dispatch('click'); 33 | -------------------------------------------------------------------------------- /problems/javascript/FindFirstLast.js: -------------------------------------------------------------------------------- 1 | var searchRange = function(nums, target){ 2 | return [find(nums, target), find(nums, target, false)]; 3 | } 4 | function find(nums, target, left = true){ 5 | let [l, r] = [0, nums.length - 1]; 6 | while (l <= r){ 7 | let mi = Math.floor((l + r)/2); 8 | if (nums[mi] < target){ 9 | l = mi + 1; 10 | } else if (nums[mi] > target){ 11 | r = mi - 1; 12 | } else { 13 | //we have an equality 14 | // we would normally return mi here, but we have additional work to do 15 | if (left){ 16 | if (nums[mi - 1] === target){ 17 | // this means that there are further candidates on the left and we 18 | // shift the right bound to search the left side 19 | r = mi -1; 20 | } else { 21 | // if we have hit this point, we have hit the left-most. 22 | return mi; 23 | } 24 | } else { 25 | if (nums[mi + 1] === target){ 26 | //if this condition is true, we shift the left bounds 27 | // to search the right-side right of the array 28 | l = mi + 1; 29 | } else { 30 | //this means that we have reached the right-most; 31 | return mi; 32 | } 33 | } 34 | } 35 | } 36 | // if we reach this point, we have no alternatives. 37 | return -1; 38 | } 39 | 40 | 41 | 42 | 43 | console.log(searchRange(nums, target)); 44 | -------------------------------------------------------------------------------- /problems/javascript/FindMaxBoat.js: -------------------------------------------------------------------------------- 1 | const {people, limit } = require('./boats.js'); 2 | 3 | const numRescueBoats = function(people, limit) { 4 | let numBoats = 0; 5 | people.sort(); 6 | while (people.length){ 7 | let firstPerson = people.pop(); 8 | let isAnyoneLeft = people.length; 9 | if (isAnyoneLeft){ 10 | let limitLeft = limit - firstPerson; 11 | let peopleLeft = people.filter(p => p <= limitLeft); 12 | if (peopleLeft.length) { 13 | const removeIdx = people.indexOf(peopleLeft.slice(-1)[0]); 14 | people.splice(removeIdx, 1); 15 | } 16 | } 17 | numBoats++ 18 | } 19 | return numBoats; 20 | }; 21 | 22 | numRescueBoats(people, limit); 23 | -------------------------------------------------------------------------------- /problems/javascript/FindMaxSliceArray.js: -------------------------------------------------------------------------------- 1 | //O(n) space, O(n) runtime; 2 | // function findMaxSlice(array){ 3 | // if (array.length === 0){ 4 | // return []; 5 | // } 6 | // let slices = {}; 7 | // let currentElem = array[0]; 8 | // let idx = 0; 9 | // for (let i = 0; i < array.length; i++){ 10 | // let elem = array[i]; 11 | // if (elem < currentElem){ 12 | // slices[idx] = i - idx; 13 | // idx = i; 14 | // } else { 15 | // //we are at the end and we need the last guy potentially 16 | // } 17 | // currentElem = elem; 18 | // } 19 | // if (!Boolean(slices[idx])){ 20 | // slices[idx] = array.length - idx; 21 | // } 22 | // const maxSlice = Math.max(...Object.values(slices)); 23 | // return Object.keys(slices).filter(k => slices[k] === maxSlice).map(k => Number(k)); 24 | // } 25 | 26 | //proprotional to number of slices, n but can also be made constant easily; 27 | function findMaxSlice(array){ 28 | if (array.length === 0){ 29 | return -1; 30 | } 31 | let maxSize = 0; 32 | let maxIdx = 0 33 | let priorElem = array[0]; 34 | let idx = 0; 35 | let results = []; 36 | for (let i = 0; i < array.length; i++){ 37 | let elem = array[i]; 38 | let diff = (i - idx); 39 | if (elem < priorElem){ 40 | // we are not ascending 41 | if (diff > maxSize){ 42 | maxSize = diff; 43 | results = []; 44 | results.push(idx); 45 | } else if (diff === maxSize){ 46 | results.push(idx); 47 | if (i === array.length -1){ 48 | results.push(i); 49 | } 50 | } 51 | // we could be ascending 52 | idx = i; 53 | } else { 54 | if (i === (array.length - 1) && diff > maxSize){ 55 | results = []; 56 | results.push(idx); 57 | } else if (i === array.length - 1 && diff === maxSize){ 58 | results.push(idx); 59 | } 60 | } 61 | priorElem = elem; 62 | } 63 | return results; 64 | } 65 | 66 | 67 | 68 | console.log(findMaxSlice([10,20,30]), [0]); 69 | console.log(findMaxSlice([30,20,10]), [0,1,2]); 70 | console.log(findMaxSlice([-10, 20, 10,20,30,60,70,100,9000,80000,10,20,30,40,60]), [2]); 71 | console.log(findMaxSlice([10,20,30,10,20,30,40,60]), [3]); 72 | console.log(findMaxSlice([1]), [0]) 73 | -------------------------------------------------------------------------------- /problems/javascript/FindSubstringWithMostVowels.js: -------------------------------------------------------------------------------- 1 | function isVowel(char){ 2 | const hash = { 3 | 'a': true, 4 | 'e': true, 5 | 'i': true, 6 | 'o': true, 7 | 'u': true 8 | } 9 | return Boolean(hash[char]); 10 | } 11 | 12 | 13 | function findSubstring(str, k) { 14 | // Write your code here 15 | if (str.length < k){ 16 | return 'Not Found!' 17 | } 18 | let start = 0; 19 | let maxVowels = 0; 20 | let count = 0; 21 | for (let i = 0; i <= str.length - k; i++){ 22 | let char = str[i]; 23 | if (count < k) { 24 | count++; 25 | maxVowels = isVowel(char) ? maxVowels + 1 : maxVowels; 26 | } else { 27 | let lastChar = str[k - 1 + i]; 28 | if (isVowel(lastChar) && !isVowel(char)){ 29 | //then I know i increased the count; 30 | maxVowels++; 31 | start = i; 32 | } 33 | } 34 | 35 | if (maxVowels === k){ 36 | return str.substring(start, start+k); 37 | } 38 | } 39 | if (maxVowels !== 0){ 40 | return str.substring(start, start+k); 41 | } 42 | return 'Not found!' 43 | } 44 | 45 | console.log(findSubstring('aeeddwdoooiou', 5)) 46 | console.log(findSubstring('zzzzzzzz', 5)) 47 | -------------------------------------------------------------------------------- /problems/javascript/FirstUniqueCharString.js: -------------------------------------------------------------------------------- 1 | /** 2 | Given a string, find the first non-repeating character in it and return it's index. If it doesn't exist, return -1. 3 | 4 | Examples: 5 | 6 | s = "leetcode" 7 | return 0. 8 | 9 | s = "loveleetcode", 10 | return 2. 11 | Note: You may assume the string contain only lowercase letters. 12 | */ 13 | 14 | /** 15 | * @param {string} s 16 | * @return {number} 17 | */ 18 | //O(n), O(characters) memory 19 | const firstUniqChar = function(s) { 20 | const seen = new Map(); 21 | for (let char of s){ 22 | let occurence = seen.get(char); 23 | if (occurence){ 24 | occurence === 2 ? null : seen.set(char, 2); 25 | } else { 26 | seen.set(char, 1); 27 | } 28 | } 29 | for (let [key, value] of seen) { 30 | if (value === 1) { 31 | return s.indexOf(key); 32 | } 33 | } 34 | return -1; 35 | }; 36 | 37 | //O(n) runtime, but constant space. 38 | const firstUniqChar = function(s){ 39 | const states = Array(26).fill(-1); 40 | const order = []; 41 | for (let i = 0; i < s.length; i++){ 42 | const charCode = s.charCodeAt(i) - 97; 43 | //a is 97, so 'a' would be 0; 44 | if (states[charCode] === -1){ 45 | // we have not seen this; 46 | order.push(charCode); 47 | states[charCode] = i; 48 | } else { 49 | states[charCode] = -2 50 | } 51 | } 52 | 53 | for (let i = 0; i < order.length; i++) { 54 | let charCode = order[i]; 55 | let index = states[charCode]; 56 | if (index > -1){ 57 | return index; 58 | } 59 | } 60 | return -1; 61 | } 62 | -------------------------------------------------------------------------------- /problems/javascript/FlattenNestedArray.js: -------------------------------------------------------------------------------- 1 | function flatten(arr){ 2 | return arr.reduce((a, b) => { 3 | if (Array.isArray(b)) { 4 | a.push(...flatten(b)); 5 | } else { 6 | a.push(b); 7 | } 8 | return a; 9 | }, []); 10 | } 11 | 12 | function simpleFlat(arr){ 13 | return arr.reduce((a, b) => a.concat(b), []); 14 | } 15 | 16 | function flattenStack(arr){ 17 | let newArr = []; 18 | for (let i = 0; i < arr.length; i++) { 19 | let val = arr[i]; 20 | const isArray = Array.isArray(val); 21 | while ( isArray && val.some(v => Array.isArray(v)) ) { 22 | val = simpleFlat(val); 23 | } 24 | isArray ? newArr.push(...val) : newArr.push(val); 25 | } 26 | return newArr; 27 | } 28 | let arr1 = [[1,1,[1,2]],1,[3,[4]]]; 29 | // bind(1,2,3) 30 | // console.log('rec', flatten(arr1)); 31 | // console.log('flattenStack', flattenStack(arr1)); 32 | -------------------------------------------------------------------------------- /problems/javascript/GenerateAreas.js: -------------------------------------------------------------------------------- 1 | // Complete the calculateArea function below. 2 | // It returns a Promise which on success, returns area of the shape, and on failure returns [-1]. 3 | let calculateArea = (shape, values) => { 4 | const allowedShapes = ['circle', 'rectangle', 'triangle', 'square']; 5 | const pi = 3.14; 6 | return new Promise((res, rej) => { 7 | if (!allowedShapes.includes(shape)) { 8 | reject(-1); 9 | } 10 | let result; 11 | if (shape === 'circle') { 12 | result = pi * Math.pow(values[0], 2) 13 | } 14 | if (shape === 'rectangle') { 15 | result = values[0] * values[1]; 16 | } 17 | if (shape === 'triangle') { 18 | result = 0.5 * values[0] * values[1]; 19 | } 20 | if (shape === 'square'){ 21 | result = Math.pow(values[0], 2); 22 | } 23 | result = Number(result.toFixed(2)); 24 | res(result); 25 | }); 26 | } 27 | 28 | // Complete the generateArea function below. 29 | // It returns a Promise which on success, returns an array of areas of all the shapes and on failure, returns [-1]. 30 | let getAreas = (shapes, values_arr) => { 31 | let promises = shapes.map((shape, i)) => calculateArea(shape, values_arr[i]) 32 | return Promise.all(promises); 33 | } 34 | 35 | let callCalculateArea = async (shapes, values) => await calculateArea(shapes[0], values[0]).catch(error => error) instanceof Promise; 36 | -------------------------------------------------------------------------------- /problems/javascript/GenerateArrayRandomNumbers.js: -------------------------------------------------------------------------------- 1 | //Generate an Array of Random numbers between 1-2 2 | 3 | function solution(N) { 4 | // write your code in JavaScript (Node.js 8.9.4) 5 | return [...new Array(N)] 6 | .map(() => Math.round(Math.random() * 1 + 1)); 7 | } 8 | 9 | console.log(solution(500)); 10 | -------------------------------------------------------------------------------- /problems/javascript/HighestPeak.js: -------------------------------------------------------------------------------- 1 | /** 2 | 852. Peak Index in a Mountain Array 3 | Easy 4 | 5 | 480 6 | 7 | 996 8 | 9 | Add to List 10 | 11 | Share 12 | Let's call an array A a mountain if the following properties hold: 13 | 14 | A.length >= 3 15 | There exists some 0 < i < A.length - 1 such that A[0] < A[1] < ... A[i-1] < A[i] > A[i+1] > ... > A[A.length - 1] 16 | Given an array that is definitely a mountain, return any i such that A[0] < A[1] < ... A[i-1] < A[i] > A[i+1] > ... > A[A.length - 1]. 17 | */ 18 | 19 | /** 20 | * @param {number[]} A 21 | * @return {number} 22 | */ 23 | 24 | //O(n), works for multi-peaks [0,1,2,3,4,2,6000,0] 25 | 26 | var peakIndexInMountainArray = function(arr) { 27 | let maxI = null; 28 | let rightMost = null; 29 | for (let i=0; i < arr.length; i++){ 30 | const left = arr[i-1]; 31 | const currentElement = arr[i]; 32 | const right = arr[i+1]; 33 | if (left === undefined || right === undefined){ 34 | continue; 35 | } 36 | if (currentElement > right && currentElement > left){ 37 | maxI = i; 38 | rightMost = right; 39 | continue; 40 | } 41 | if (maxI){ 42 | //we know for sure there is a right element 43 | if (rightMost > right){ 44 | rightMost = right; 45 | } else { 46 | // this is not a local top 47 | maxI = null; 48 | rightMost = null; 49 | } 50 | } 51 | } 52 | return maxI; 53 | }; 54 | //O(n) time, but only works for one peak mountains i.e. not 55 | // [0,1,2,3,4,2,6000,0] 56 | const peakIndexInMountainArray = function(arr){ 57 | let maxI = null; 58 | for (let i = 0; i < arr.length; i++){ 59 | if (arr[i] > arr[i+1]){ 60 | maxI = i; 61 | } 62 | 63 | if(maxI){ 64 | 65 | } 66 | } 67 | return maxI; 68 | } 69 | //O(log(n)) (binarysearch) (must be a unipeak mountain) 70 | const peakIndexInMountainArray = function(arr){ 71 | const [lo, hi] = [0, arr.length - 1]; 72 | while (lo < hi){ 73 | mi = Math.floor((lo+hi)/2); 74 | if (arr[mi] < a[mi + 1]) { 75 | lo = mi + 1; 76 | } else { 77 | hi = mi; 78 | } 79 | } 80 | return lo; 81 | } 82 | peakIndexInMountainArray([0,2,1,0]) 83 | -------------------------------------------------------------------------------- /problems/javascript/ImplementRegex.js: -------------------------------------------------------------------------------- 1 | // Implement regular expression matching with support for '.' and '*'. 2 | // 3 | // '.' Matches any single character. 4 | // '*' Matches zero or more of the preceding element. 5 | // 6 | // The matching should cover the entire input string (not partial). 7 | // 8 | // The function prototype should be: 9 | // bool isMatch(const char *s, const char *p) 10 | // 11 | // Some examples: 12 | // isMatch("aa","a") → false 13 | // isMatch("aa","aa") → true 14 | // isMatch("aaa","aa") → false 15 | // isMatch("aa", "a*") → true 16 | // isMatch("aa", ".*") → true 17 | // isMatch("ab", ".*") → true 18 | // isMatch("aab", "c*a*b") → true 19 | 20 | /** 21 | * @param {string} s 22 | * @param {string} p 23 | * @return {boolean} 24 | */ 25 | var isMatch = function(s, p) { 26 | let index = 0; 27 | let hasEncounteredPrecedingCheck = false; 28 | for (let [idx, check] of p.split("").entries()){ 29 | const isPrecedingCheck = p[idx+1] === '*'; 30 | if (isPrecedingCheck){ 31 | hasEncounteredPrecedingCheck = true; 32 | continue; 33 | } 34 | if (hasEncounteredPrecedingCheck){ 35 | hasEncounteredPrecedingCheck = false; 36 | index = (index + 1 < s.length - 1) ? (index + 1) : index; 37 | continue; 38 | } 39 | const currentChar = s[index]; 40 | 41 | if (!hasEncounteredPrecedingCheck) { 42 | //here we know we have a specific value either . 43 | //there are two conditions here, we have . which is the existence of any character 44 | //or we have a specific value 45 | const proceed = (check === '.' && currentChar) || (check !== '.' && check !== '*' && currentChar == check); 46 | if (proceed) { 47 | index += 1; 48 | } else { 49 | return false; 50 | } 51 | } 52 | } 53 | return index === s.length - 1; 54 | }; 55 | // console.log(isMatch("aa","a") === false) 56 | // console.log(isMatch("aa","aa") === true) 57 | // console.log(isMatch("aaa","aa") === false) 58 | // console.log(isMatch("aa", "a*") === true) 59 | // console.log(isMatch("aa", ".*") === true) 60 | // console.log(isMatch("ab", ".*") === true) 61 | console.log(isMatch("aab", "c*a*b") === true) 62 | -------------------------------------------------------------------------------- /problems/javascript/KnightGame.js: -------------------------------------------------------------------------------- 1 | class Knight { 2 | constructor(props){ 3 | const { place } = props; 4 | this.hp = 100; 5 | this.place = place; 6 | } 7 | 8 | receiveDamage(dmg){ 9 | this.hp = this.hp - dmg; 10 | } 11 | } 12 | 13 | class Game { 14 | constructor(){ 15 | this.game = [0,1,2,3,4,5].map(a => new Knight({ place: a + 1})); 16 | this.knightHittingIdx = 0; 17 | console.log(this.game); 18 | } 19 | checkWin(){ 20 | if (this.game.length === 1){ 21 | const winningPlace = this.game[0].place; 22 | console.log(`Knight ${winningPlace} Wins`); 23 | } 24 | } 25 | 26 | round(){ 27 | const places = [-1, 1]; 28 | const tupleIdx = Math.round(Math.random()); 29 | let kHIdx = places[tupleIdx]; 30 | let knightHit = this.knightHittingIdx + kHIdx; 31 | 32 | if (!this.game[knightHit] && this.knightHittingIdx === 0){ 33 | kHIdx = this.game.length - 1; 34 | knightHit = this.knightHittingIdx + kHIdx; 35 | } 36 | 37 | if (!this.game[knightHit] && this.knightHittingIdx === this.game.length -1){ 38 | kHIdx = 0; 39 | knightHit = this.knightHittingIdx + kHIdx; 40 | } 41 | 42 | const injuredKnight = this.game[knightHit]; 43 | const damage = Math.round(Math.random() * 6); 44 | injuredKnight.receiveDamage(damage); 45 | this.outputStatement(this.game[this.knightHittingIdx].place, injuredKnight.place, damage); 46 | if (injuredKnight.hp <= 0){ 47 | this.handleDeath(injuredKnight); 48 | } 49 | 50 | if (this.knightHittingIdx < this.game.length - 1) { 51 | this.knightHittingIdx += 1; 52 | } else { 53 | this.knightHittingIdx = 0; 54 | } 55 | this.checkWin(); 56 | } 57 | handleDeath(knight){ 58 | if(knight.hp <= 0) { 59 | this.game = this.game.filter(k => k.place !== knight.place); 60 | console.log(`Knight ${knight.place} dies`); 61 | } 62 | } 63 | outputStatement(hitter, hit, dmg){ 64 | console.log(`Knight${hitter} hits Knight${hit} for ${dmg} Points`); 65 | } 66 | } 67 | 68 | const game = new Game(); 69 | 70 | while (game.game.length !== 1){ 71 | game.round(); 72 | } 73 | -------------------------------------------------------------------------------- /problems/javascript/LinkedListPalindrome.js: -------------------------------------------------------------------------------- 1 | //Implement a function to check if a linked list is a palindrome. 2 | 3 | //reverse approach 4 | //O(1) memory ; O(n) 5 | function isPalindromeList(node) { 6 | let runner = node; 7 | let prevNode = null; 8 | while (runner) { 9 | const nextReference = runner.next; 10 | runner.next = prevNode; 11 | prevNode = runner; 12 | runner = nextReference; 13 | } 14 | let runner1 = node; 15 | let runner2 = prevNode; 16 | while (runner1) { 17 | if (runner1.value !== runner2.value) { 18 | return false; 19 | } 20 | runner1 = runner1.next; 21 | runner2 = runner2.next; 22 | } 23 | 24 | return true; 25 | } 26 | 27 | //fast stack approach 28 | //O(n) memory ; O(n) time 29 | function isPalindromeListStack(node){ 30 | let fast = node; 31 | let slow = node; 32 | let stack = []; 33 | while (fast && fast.next) { 34 | stack.push(slow.value); 35 | slow = slow.next; 36 | fast = fast.next.next; 37 | } 38 | 39 | if (fast) { 40 | // this means we have an odd number of letters 41 | slow = slow.next; 42 | } 43 | 44 | while (slow){ 45 | const letter = stack.pop(); 46 | if (letter !== slow.value) { 47 | return false; 48 | } 49 | slow = slow.next; 50 | } 51 | 52 | return true; 53 | } 54 | -------------------------------------------------------------------------------- /problems/javascript/ListNode.js: -------------------------------------------------------------------------------- 1 | // 2. Add Two Numbers 2 | // You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list. 3 | // 4 | // You may assume the two numbers do not contain any leading zero, except the number 0 itself. 5 | // 6 | // Input: (2 -> 4 -> 3) + (5 -> 6 -> 4) 7 | // Output: 7 -> 0 -> 8 8 | 9 | // * 10 | // * Definition for singly-linked list. 11 | // * function ListNode(val) { 12 | // * this.val = val; 13 | // * this.next = null; 14 | // * } 15 | // https://leetcode.com/submissions/detail/125150425/ 16 | /** 17 | * @param {ListNode} l1 18 | * @param {ListNode} l2 19 | * @return {ListNode} 20 | */ 21 | 22 | // 1h:30m, 97.22% runtime, 199ms. 23 | var addTwoNumbers = function(l1, l2) { 24 | var firstListItem = l1; 25 | var otherListItem = l2; 26 | var remainder = 0; 27 | var itemToReturn = []; 28 | var sum = 0; 29 | while (firstListItem){ 30 | if (otherListItem) { 31 | var sum = firstListItem.val + otherListItem.val + remainder; 32 | digit = (sum) % 10; 33 | remainder = sum > 9 ? Math.floor(sum / 10) : 0; 34 | var tempNext = firstListItem.next; 35 | var tempOther = otherListItem.next; 36 | itemToReturn.push(digit); 37 | firstListItem = tempNext; 38 | otherListItem = tempOther; 39 | } else { 40 | // this is much simpler here, we just keep adding to either firstNode or newNode 41 | var sum = firstListItem.val + remainder; 42 | digit = (sum) % 10; 43 | remainder = sum > 9 ? Math.floor(sum / 10) : 0; 44 | var tempNext = firstListItem.next; 45 | itemToReturn.push(digit); 46 | firstListItem = tempNext; 47 | } 48 | } 49 | while (otherListItem){ 50 | var sum = otherListItem.val + remainder; 51 | digit = (sum) % 10; 52 | remainder = sum > 9 ? Math.floor(sum / 10) : 0; 53 | //this means we have more to add 54 | var tempNext = otherListItem.next; 55 | itemToReturn.push(digit); 56 | otherListItem = tempNext; 57 | } 58 | 59 | if (remainder !== 0) { 60 | itemToReturn.push(remainder); 61 | } 62 | 63 | return itemToReturn; 64 | }; 65 | 66 | // linearruntime, constant memory; 67 | -------------------------------------------------------------------------------- /problems/javascript/LongestCall.js: -------------------------------------------------------------------------------- 1 | // n % 2 === 0 n/ 2 2 | // odd 3n + 1 3 | 4 | function longestCall(limit){ 5 | let numberCalls = 0; 6 | let greatestNumber; 7 | for(let i = 1; i < limit; i++) { 8 | let numberChecking = i; 9 | let localCalls = 0; 10 | while (numberChecking !== 1) { 11 | if (numberChecking % 2 === 0) { 12 | const numberOfDivisions = numberChecking / 2; 13 | localCalls += 1 + numberOfDivisions 14 | if (numberCalls > localCalls) { 15 | numberCalls = localCalls; 16 | } 17 | break; 18 | } else { 19 | numberChecking = 3 * numberChecking + 1; 20 | localCalls += 1 21 | } 22 | } 23 | if (localCalls > numberCalls) { 24 | numberCalls = localCalls; 25 | greatestNumber = i; 26 | } 27 | } 28 | return [ 29 | greatestNumber, 30 | numberCalls 31 | ]; 32 | } 33 | -------------------------------------------------------------------------------- /problems/javascript/MakeChange.js: -------------------------------------------------------------------------------- 1 | function possibleChange(amount, denominations){ 2 | return denominations.filter((denom) => (amount - denom) >= 0) 3 | } 4 | 5 | function makeChange(amount, denominations){ 6 | if (amount < 0){ 7 | return false; 8 | } 9 | if (amount === 0){ 10 | return []; 11 | } 12 | let combinations = []; 13 | for (let i = 0; i < denominations.length; i++){ 14 | let denom = denominations[i]; 15 | const newAmount = amount - denom; 16 | let filtered = possibleChange(newAmount, denominations); 17 | let coins = makeChange(newAmount, filtered); 18 | if (coins){ 19 | coins.push(denom); 20 | } 21 | if (newAmount === 0) 22 | combinations.push(coins); 23 | } 24 | } 25 | console.log(makeChange(4,[1,2,3])) -------------------------------------------------------------------------------- /problems/javascript/MedianSortedArrays.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | var findMedianSortedArrays = function(x, y) { 6 | if (x.length > y.length) { 7 | return findMedianSortedArrays(y,x); 8 | } 9 | const xLength = x.length; 10 | const yLength = y.length; 11 | let [lo, hi] = [0, xLength]; 12 | let i = 1; 13 | const top = xLength + yLength + 1; 14 | while (lo <= hi){ 15 | const partitionX = Math.floor((lo+hi)/2); 16 | const partitionY = Math.floor((top)/2 - partitionX); 17 | 18 | const maxLeftX = partitionX === 0 ? -Infinity : x[partitionX - 1]; 19 | const minRightX = partitionX === xLength ? Infinity : x[partitionX]; 20 | 21 | const maxLeftY = partitionY === 0 ? -Infinity : y[partitionY - 1]; 22 | const minRightY = partitionY === yLength ? Infinity : y[partitionY]; 23 | if (maxLeftX <= minRightY && maxLeftY <= minRightX){ 24 | // we have our answer 25 | const isEven = (xLength + yLength) % 2 === 0; 26 | const valuesMax = [maxLeftX, maxLeftY].filter(v => v !== -Infinity); 27 | if (!isEven){ 28 | return Math.max(...valuesMax); 29 | } else { 30 | const valuesMin = [minRightX, minRightY].filter(v => v!== Infinity); 31 | const sum = (Math.max(...valuesMax) + Math.min(...valuesMin)); 32 | return (sum/2); 33 | } 34 | } else if (maxLeftX > minRightY){ 35 | //we need to move left 36 | hi = partitionX - 1; 37 | } else { 38 | //we need to move right 39 | lo = partitionX + 1; 40 | } 41 | i++ 42 | 43 | } 44 | }; 45 | const x = [1,2]; 46 | const y = [3]; 47 | 48 | console.log(findMedianSortedArrays(y,x), 2) 49 | console.log(findMedianSortedArrays([23,26,31,35],[3,5,7,9,11,16]), 13.5) 50 | console.log(findMedianSortedArrays([2,2],[2,2]), 2) 51 | console.log(findMedianSortedArrays([2,2],[3,3]), 2.5) 52 | console.log(findMedianSortedArrays([1,3,8,9,15],[7,11,18,19,21,25]), 11) 53 | -------------------------------------------------------------------------------- /problems/javascript/MemoizeFib.js: -------------------------------------------------------------------------------- 1 | let hash = {}; 2 | // memoization is key to dynamic programming, we just save prior results 3 | function fib(n){ 4 | if (hash[n]){ 5 | console.log('grabbing from memo', n) 6 | return hash[n]; 7 | } 8 | if (n == 0 || n === 1){ 9 | hash[n] = n; 10 | return hash[n]; 11 | } 12 | console.log('computing fib', n) 13 | const result = fib(n-1) + fib(n-2); 14 | hash[n] = result; 15 | return hash[n]; 16 | } 17 | 18 | console.log('result fib', fib(5)) -------------------------------------------------------------------------------- /problems/javascript/MinCostClimbingStairs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} cost 3 | * @return {number} 4 | */ 5 | var minCostClimbingStairs = function(cost) { 6 | let dp = []; 7 | dp[0] = cost[0]; 8 | dp[1] = cost[1]; 9 | for (let i = 2; i < cost.length; i++){ 10 | dp[i] = Math.min(dp[i-1], dp[i-2]) + cost[i]; 11 | } 12 | return Math.min(dp[cost.length -1], dp[cost.length - 2]); 13 | }; 14 | 15 | const cost = [10, 15, 20]; 16 | 17 | console.log(minCostClimbingStairs(cost)); 18 | -------------------------------------------------------------------------------- /problems/javascript/MinStack.js: -------------------------------------------------------------------------------- 1 | ` 2 | Design a stack that supports push, pop, top, and retrieving the minimum element in constant time. 3 | 4 | push(x) -- Push element x onto stack. 5 | pop() -- Removes the element on top of the stack. 6 | top() -- Get the top element. 7 | getMin() -- Retrieve the minimum element in the stack. 8 | 9 | 10 | Example: 11 | 12 | MinStack minStack = new MinStack(); 13 | minStack.push(-2); 14 | minStack.push(0); 15 | minStack.push(-3); 16 | minStack.getMin(); --> Returns -3. 17 | minStack.pop(); 18 | minStack.top(); --> Returns 0. 19 | minStack.getMin(); --> Returns -2. 20 | ` 21 | // WAY 1, requires O(n reshifting); 22 | class MinStack { 23 | constructor(){ 24 | this.stack = []; 25 | this.minIndex = -1; 26 | } 27 | push(x){ 28 | this.stack.push(x); 29 | if (this.minIndex !== -1){ 30 | //we have items 31 | let currentMin = this.stack[this.minIndex]; 32 | if (x < currentMin){ 33 | this.minIndex = this.stack.length - 1; 34 | } 35 | } else { 36 | //this is a new min 37 | this.minIndex = 0; 38 | } 39 | 40 | } 41 | pop(){ 42 | this.stack.pop(); 43 | if (this.stack.length === 0){ 44 | this.minIndex = -1; 45 | } 46 | if (this.stack[this.minIndex] === undefined && this.stack.length){ 47 | this.minIndex = 0; 48 | const currentMin = this.stack[this.minIndex]; 49 | for (let i = 0; i < this.stack.length; i++){ 50 | const val = this.stack[i]; 51 | if (val < currentMin){ 52 | currentMin = val; 53 | this.minIndex = i; 54 | } 55 | } 56 | } 57 | } 58 | top(){ 59 | return this.stack[this.stack.length - 1]; 60 | } 61 | getMin(){ 62 | return this.stack[this.minIndex]; 63 | } 64 | } 65 | const minStack = new MinStack; 66 | minStack.push(-2); 67 | // console.log(minStack.stack) //-3 68 | 69 | minStack.push(0); 70 | // console.log(minStack.stack) //-3 71 | 72 | minStack.push(-3); 73 | console.log(minStack.getMin(), -3) //-3 74 | console.log(minStack.pop()) 75 | console.log(minStack.top(), 0); //0 76 | console.log(minStack.getMin(), -2) //-3 77 | // console.log(minStack.pop()); 78 | // console.log(minStack.getMin()); //-2 79 | 80 | class MinStack { 81 | constructor(){ 82 | this.stack = []; 83 | } 84 | push(x){ 85 | let min = this.stack.length ? this.stack[this.stack.length - 1].min : x; 86 | // this is smart, so we always know the size of the last minimum; 87 | stack.push({val: x, min: Math.min(min, x)}); 88 | } 89 | pop(){ 90 | this.stack.pop(); 91 | } 92 | top(){ 93 | if (this.stack.length){ 94 | return this.stack[this.stack.length -1].val; 95 | } 96 | } 97 | getMin(){ 98 | if (this.stack.length){ 99 | return this.stack[this.stack.length - 1].min; 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /problems/javascript/MinSteps.js: -------------------------------------------------------------------------------- 1 | 2 | // function minSteps(arr){ 3 | // const n = arr.length; 4 | // let dp = []; 5 | // dp[0] = 0; 6 | // for (let i=1; i < n; i++){ 7 | // dp[i] = Infinity; 8 | // for (let j=1; j <= i; j++){ 9 | // if (arr[i-j] >= j){ 10 | // dp[i] = Math.min(dp[i], dp[i-j] + 1); 11 | // } 12 | // } 13 | // } 14 | // return dp[n -1] 15 | // } 16 | function minSteps(arr){ 17 | const goal = arr.length; 18 | let steps = 0; 19 | if (arr.length === 0 || arr.length === 1){ 20 | return steps; 21 | } 22 | let jumps = 1; 23 | let ladder = arr[0]; 24 | let stairs = arr[0]; 25 | for (let i = 0; i < goal; i++){ 26 | if (i + arr[i] > ladder) { 27 | ladder = i + arr[i]; 28 | stairs--; 29 | } 30 | if (stairs + i >= goal - 1){ 31 | return jumps; 32 | } 33 | if (stairs == 0){ 34 | jumps++; 35 | stairs = ladder - i; 36 | } 37 | } 38 | return jumps; 39 | } 40 | { 41 | "USD": { 42 | "EUR": 0.9272137228, 43 | "GBP": 0.8145572554 44 | }, 45 | "EUR": { 46 | "GBP": 0.8785, 47 | "USD": 1.0785 48 | }, 49 | "GBP": { 50 | "EUR": 1.1383039271, 51 | "USD": 1.2276607854 52 | } 53 | } 54 | 55 | // do currency exchange problem 56 | console.log(minSteps([1, 3, 5])); 57 | console.log(minSteps([2, 3, 1, 1, 4])); 58 | -------------------------------------------------------------------------------- /problems/javascript/MostCommonGiftDFS.js: -------------------------------------------------------------------------------- 1 | // customer = { 2 | // friends: [ 3 | // { 4 | // friends: [{ wishlist: [], friends: []}] 5 | // wishlist: ['dhasjkd', 'dasdjk'] 6 | 7 | // },...etc 8 | // ], 9 | // wishlist: ["xBox", "PSP", "Stadia"] 10 | // }; 11 | // find the most mostCommonGift ina list of friends; 12 | 13 | function findMax(entries){ 14 | let maxItem = null; 15 | let maxItemCount = null; 16 | for (let [k, v] of entries){ 17 | if (maxItem === null && maxItemCount === null) { 18 | maxItem = k; 19 | maxItemCount = v; 20 | continue; 21 | } 22 | if (v > maxItemCount){ 23 | maxItem = k; 24 | maxItemCount = v; 25 | } 26 | } 27 | return maxItem; 28 | } 29 | 30 | function findMostCommonGift(customer){ 31 | const giftStore = {}; 32 | let friends = [customer]; 33 | let friendIds = {}; 34 | while (friends.length !== 0){ 35 | const givenFriend = friends.pop(); 36 | friendIds[givenFriend.id] = true; 37 | const { wishlist } = givenFriend; 38 | // add gifts to store 39 | for (let item of wishlist){ 40 | giftStore[item] = giftStore[item] ? giftStore[item] + 1 : 1; 41 | } 42 | // add anymore friends to the queue 43 | // |firends| = 1 000 000 44 | // |givenFriend.friends| = 1 45 | // friends = friends.concat(givenFriends.friends); O(branches * connections) 46 | for (let friend of givenFriend.friends){ 47 | if (!friendIds[friend.id]){ 48 | friends.push(friend); 49 | } 50 | } 51 | } 52 | // select the giftStore item with the most count 53 | // O(N) where N is number of unique gifts 54 | // this can be optimized as well 55 | let mostCommonGift = findMax(Object.entries(giftStore)); 56 | return mostCommonGift; 57 | } 58 | 59 | 60 | let customerA = { 61 | id: 1, 62 | friends: [], 63 | wishlist: ['abc', 'a'] 64 | } 65 | let customerB = { 66 | id: 2, 67 | friends: [customerA], 68 | wishlist: ['abc', 'a'] 69 | } 70 | let customerC= { 71 | id: 3, 72 | friends: [customerB, customerA], 73 | wishlist: ['b', 'a', 'c'] 74 | }; 75 | customerA.friends = [customerB]; 76 | customerB.friends = [customerC, customerA]; 77 | 78 | console.log(findMostCommonGift(customerA)); 79 | 80 | // a <-> b 81 | -------------------------------------------------------------------------------- /problems/javascript/MovingMiners.js: -------------------------------------------------------------------------------- 1 | //we aim to have a list of move vectors 2 | const moves = { 3 | 'right': {x: 0, y: 1}, 4 | 'down': {x: 1, y: 0}, 5 | 'left': {x: 0, y: -1}, 6 | 'up': {x: -1, y: 0}, 7 | }; 8 | function isAtEndGoal(minerX, minerY, endX, endY) { 9 | return ((minerX === endX) && (minerY === endY)); 10 | } 11 | 12 | function findUpdated(direction, minerCoordinate, key){ 13 | const {[direction]: { [key]: updatedValue } } = moves; 14 | return minerCoordinate + updatedValue; 15 | } 16 | 17 | function findPossibleDirections(map, minerX, minerY, visitedCoordinates) { 18 | const possibleDirections = []; 19 | for (const direction of Object.keys(moves)) { 20 | const updatedX = findUpdated(direction, minerX, 'x'); 21 | const updatedY = findUpdated(direction, minerY, 'y'); 22 | 23 | let noNegatives = (updatedX >= 0) && (updatedY >= 0) 24 | let isPossiblePlace = map[updatedX] && map[updatedX][updatedY] === true; 25 | let isNewCoordindate = !visitedCoordinates.find(({x: priorX, y: priorY}) => { 26 | return (priorX === updatedX) && (priorY === updatedY); 27 | }); 28 | 29 | if (isPossiblePlace && isNewCoordindate && noNegatives){ 30 | possibleDirections.push(direction); 31 | } 32 | } 33 | return possibleDirections; 34 | } 35 | 36 | function solve(mazeMap, miner, exit, invert = true, visitedCoordinates = [], ) { 37 | let path = []; 38 | 39 | // this doesn't make sense as an array of columns, i am going to transpose it. 40 | // this is just the inversion logic that follows 41 | let transposedArray, invertedExit, invertedMiner; 42 | if (invert) { 43 | transposedArray = mazeMap[0].map((col, i) => mazeMap.map(row => row[i])); 44 | let {x: eX, y: eY } = exit; 45 | invertedExit = {x: eY, y: eX }; 46 | let {x: mX, y: mY } = miner; 47 | invertedMiner = {x: mY, y: mX }; 48 | } 49 | if (!invert) { 50 | transposedArray = mazeMap; 51 | invertedExit = exit; 52 | invertedMiner = miner; 53 | } 54 | 55 | const { x: minerX, y: minerY } = invertedMiner; 56 | const { x: endX, y: endY } = invertedExit; 57 | 58 | // 59 | visitedCoordinates.push(invertedMiner); 60 | if (isAtEndGoal(minerX, minerY, endX, endY)) { 61 | return path; 62 | } 63 | const possibleDirections = findPossibleDirections(transposedArray, minerX, minerY, visitedCoordinates); 64 | if (possibleDirections.length === 0) { 65 | path.push('not possible'); 66 | return path; 67 | } 68 | 69 | if (possibleDirections.length >= 1) { 70 | for (let possibleDirection of possibleDirections) { 71 | const [ 72 | updatedX, 73 | updatedY 74 | ] = [ 75 | findUpdated(possibleDirection, minerX, 'x'), 76 | findUpdated(possibleDirection, minerY, 'y') 77 | ]; 78 | path.push(possibleDirection); 79 | // we recurse, if there is no possible path, we continue. 80 | let possiblePath = solve(transposedArray, {x: updatedX, y: updatedY}, invertedExit, false, visitedCoordinates, tries); 81 | if (!possiblePath) { 82 | continue; 83 | } 84 | if (!possiblePath.includes('not possible')) { 85 | path = path.concat(possiblePath); 86 | return path; 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /problems/javascript/NeedleHaystack.js: -------------------------------------------------------------------------------- 1 | console.log('hello world'); 2 | 3 | const a = ['a', 'b', 'c']; 4 | const b = ['b', 'c', 'd']; 5 | 6 | function interSection(arrA, arrB){ 7 | return arrA.filter((a) => arrB.includes(a)); 8 | } 9 | function interSectionMap(arrA, arrB){ 10 | const map = new WeakMap(); 11 | let common = []; 12 | for (let elem of arrA){ 13 | map.set(elem, true); 14 | } 15 | for (let elemB of arrB){ 16 | if (map.has(elemB)){ 17 | common.push(elemB); 18 | } 19 | } 20 | 21 | return common 22 | } 23 | 24 | function commonSubstring(haystack, needle){ 25 | let lastIndexFound = null; 26 | for (let char of needle){ 27 | if (haystack.indexOf(char) !== -1) { 28 | lastIndexFound = haystack.indexOf(char); 29 | haystack = haystack.slice(lastIndexFound); 30 | } else { 31 | return false; 32 | } 33 | } 34 | return true; 35 | } 36 | 37 | function commonSubstringEfficient(haystack, needle){ 38 | let lastIndexFound = null; 39 | for (let char of needle){ 40 | if (haystack.indexOf(char) !== -1) { 41 | lastIndexFound = haystack.indexOf(char); 42 | haystack = haystack.slice(lastIndexFound); 43 | } else { 44 | return false; 45 | } 46 | } 47 | return true; 48 | } 49 | 50 | 51 | 52 | 53 | // console.log(interSectionMap(a, b), 'b, c') 54 | console.log(commonSubstring('aepple', 'ale'), true) 55 | console.log(commonSubstring('apple', 'ale'), true) 56 | console.log(commonSubstring('apple', 'ael'), false) 57 | -------------------------------------------------------------------------------- /problems/javascript/NthFib.js: -------------------------------------------------------------------------------- 1 | let memo = { 2 | '0': 0, 3 | '1': 1 4 | }; 5 | 6 | function fib(n){ 7 | if (n === 0 || n === 1){ 8 | return memo[n]; 9 | } 10 | if (memo[n]) { 11 | return memo[n]; 12 | } 13 | 14 | return fib(n -1) + fib(n - 2); 15 | } 16 | //recursive space O(N) runtime O(2^n) 17 | function findNthFib(n){ 18 | let val = fib(n-1); 19 | return val 20 | } 21 | //constant space, //linear runtime; 22 | function constantSpaceFindFib(n){ 23 | let first = 0; 24 | let second = 1; 25 | let current; 26 | if (n === 1){ 27 | return first; 28 | } 29 | if (n === 2){ 30 | return second; 31 | } 32 | for (let i = 2; i < n; i++){ 33 | current = second + first; 34 | first = second; 35 | second = current; 36 | } 37 | return current; 38 | } 39 | 40 | 41 | console.log(constantSpaceFindFib(8)); -------------------------------------------------------------------------------- /problems/javascript/NumberOfStepsToReduceToZero.js: -------------------------------------------------------------------------------- 1 | ``` 2 | Given a non-negative integer num, return the number of steps to reduce it to zero. If the current number is even, you have to divide it by 2, otherwise, you have to subtract 1 from it. 3 | 4 | 5 | 6 | Example 1: 7 | 8 | Input: num = 14 9 | Output: 6 10 | Explanation: 11 | Step 1) 14 is even; divide by 2 and obtain 7. 12 | Step 2) 7 is odd; subtract 1 and obtain 6. 13 | Step 3) 6 is even; divide by 2 and obtain 3. 14 | Step 4) 3 is odd; subtract 1 and obtain 2. 15 | Step 5) 2 is even; divide by 2 and obtain 1. 16 | Step 6) 1 is odd; subtract 1 and obtain 0. 17 | Example 2: 18 | 19 | Input: num = 8 20 | Output: 4 21 | Explanation: 22 | Step 1) 8 is even; divide by 2 and obtain 4. 23 | Step 2) 4 is even; divide by 2 and obtain 2. 24 | Step 3) 2 is even; divide by 2 and obtain 1. 25 | Step 4) 1 is odd; subtract 1 and obtain 0. 26 | Example 3: 27 | 28 | Input: num = 123 29 | Output: 12 30 | 31 | 32 | Constraints: 33 | 34 | 0 <= num <= 10^6 35 | ``` 36 | /** 37 | * @param {number} num 38 | * @return {number} 39 | */ 40 | var numberOfSteps = function(num) { 41 | let steps = 0; 42 | let currentNum = num; 43 | while (currentNum !== 0) { 44 | if (currentNum % 2 == 0) { 45 | currentNum = currentNum / 2; 46 | } else { 47 | currentNum = currentNum - 1; 48 | } 49 | steps++; 50 | } 51 | return steps; 52 | }; 53 | -------------------------------------------------------------------------------- /problems/javascript/OnlyTwoSwapped.js: -------------------------------------------------------------------------------- 1 | //Similar strings ("face", "eacf") returns true if only 2 positions in the strings are swapped. Here 'f' and 'e' are swapped in the example. 2 | 3 | //O(n), constant memory 4 | // function onlyTwoSwapped(strA, strB){ 5 | // if (strA.length !== strB.length){ 6 | // return false; 7 | // } 8 | // let diffCount = 0; 9 | // // assuming they all contain the same characters... 10 | // for (let i = 0; i < strA.length; i++) { 11 | // if (strA[i] !== strB[i]){ 12 | // diffCount++; 13 | // } 14 | // } 15 | // return diffCount === 2 ? true : false; 16 | // } 17 | 18 | //O(n), constantmemory, checks for different characters 19 | function onlyTwoSwapped(strA, strB){ 20 | if (strA.length !== strB.length){ 21 | return false; 22 | } 23 | let diffCount = 0; 24 | let strASum = 0; 25 | let strBSum = 0; 26 | for (let i = 0; i < strA.length; i++) { 27 | if (strA[i] !== strB[i]){ 28 | diffCount++; 29 | strASum += strA[i].charCodeAt(0); 30 | strBSum += strB[i].charCodeAt(0); 31 | } 32 | } 33 | //if they have different characters, they will have different sums 34 | return diffCount === 2 && strBSum === strASum ? true : false; 35 | } 36 | 37 | console.log(onlyTwoSwapped('face', 'eacf')); 38 | //we expectfalse 39 | console.log(onlyTwoSwapped('face', 'eacF')); 40 | console.log(onlyTwoSwapped('face', '8acf')); 41 | console.log(onlyTwoSwapped('duh', 'hue')); 42 | -------------------------------------------------------------------------------- /problems/javascript/Pagination.js: -------------------------------------------------------------------------------- 1 | 2 | function sortItems(items, sortParameter, sortOrder){ 3 | const sortedItems = items.sort((itemA, itemB) => { 4 | const sortA = itemA[sortParameter]; 5 | const sortB = itemB[sortParameter]; 6 | if (sortParameter === 0){ 7 | return sortA.localeCompare(sortB); 8 | } 9 | return Number(sortA) - Number(sortB); 10 | }); 11 | if (sortOrder === 1){ 12 | return sortedItems.reverse(); 13 | } 14 | return sortedItems; 15 | } 16 | 17 | function fetchItemsToDisplay(items, sortParameter, sortOrder, itemsPerPage, pageNumber) { 18 | // Write your code here 19 | const sortedItems = sortItems(items, sortParameter, sortOrder); 20 | const start = itemsPerPage * pageNumber; 21 | const stop = pageNumber * itemsPerPage + itemsPerPage; 22 | return sortedItems.slice(start, stop).map((item) => item[0]); 23 | } 24 | 25 | fetchItemsToDisplay(items, sortParameter, sortOrder, itemsPerPage, pageNumber); 26 | -------------------------------------------------------------------------------- /problems/javascript/PermutationsOfWords.js: -------------------------------------------------------------------------------- 1 | // Given a function isWord() which returns whether a string is a isWord 2 | // Write a function that returns the list of words a 1-9 T9 String can return 3 | // isWord() => True/False 4 | 5 | // 323 => DAD, FAD 6 | const map = { 7 | // ''' 8 | // 2 -> ["a", "b", "c"] 9 | // 3 -> d, e, f 10 | // 4 -> g, h, i 11 | // 5 -> j, k, l 12 | // 6 -> m, n, o 13 | // 7 -> p, q, r, s 14 | // 8 -> t, u, v 15 | // 9 -> w, x, y, z 16 | // ''' 17 | } 18 | 19 | function isWord(word) { 20 | return true; 21 | } 22 | 23 | // 3234 24 | // [a b c] n=4 25 | // 3 26 | // 3 * 3 27 | // 3 * 3 * 3 28 | // 3 * 3 * 3 * 3 === 3 ^ 4 29 | // 3 or 4 30 | // 3^n 31 | 32 | function makeWords(num){ 33 | const stringChars = String(num).split(""); 34 | let permArray = []; 35 | // (P) = possible chars 36 | // n = length of input 37 | // P ^ N 38 | for (let [idx, char] of stringChars.entries()){ 39 | const possibleChars = map[char]; 40 | if (permArray.length === 0){ 41 | permArray = possibleChars; 42 | continue; 43 | } 44 | 45 | // map 46 | // [['ad', 'ae', 'af'], ['bd', 'be', 'bf']] 47 | // result 48 | // ['ad', 'ae', 'af', 'bd', 'be', 'bf'] 49 | permArray = permArray.map((item) => { 50 | // ['ad', 'ae', 'af'] 51 | return possibleChars.map((char) => { 52 | //this would return an error 53 | if (idx === stringChars.length - 1) { 54 | const returnVal = isWord(item + char) ? (item + char) : ''; 55 | return returnVal; 56 | } 57 | return item + char; 58 | 59 | }); 60 | }).reduce((sum, iterator) => { 61 | return sum.filter(i => i.length).concat(iterator); 62 | }, []); 63 | 64 | } 65 | return permArray; 66 | } 67 | -------------------------------------------------------------------------------- /problems/javascript/PermutationsString.js: -------------------------------------------------------------------------------- 1 | function permutations(str){ 2 | if (str.length === 1){ 3 | return [str]; 4 | } 5 | let perms = []; 6 | let hash = {} 7 | for (let i = 0; i < str.length; i++){ 8 | let char = str[i]; 9 | let initial = str.slice(0, i); 10 | let remaining = str.slice(i+1); 11 | let strArr = permutations(initial + remaining); 12 | const uniqueChars = strArr.reduce((arr, el) => { 13 | let perm = char.concat(el); 14 | if (!hash[perm]){ 15 | hash[perm] = true; 16 | arr.push(perm) 17 | } 18 | return arr; 19 | }, []); 20 | perms = perms.concat(uniqueChars); 21 | } 22 | return perms; 23 | } 24 | 25 | console.log(permutations('abb')) 26 | console.log(permutations('5432')) -------------------------------------------------------------------------------- /problems/javascript/PhoneBookOrderForm.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useReducer } from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | const style = { 5 | table: { 6 | borderCollapse: 'collapse' 7 | }, 8 | tableCell: { 9 | border: '1px solid gray', 10 | margin: 0, 11 | padding: '5px 10px', 12 | width: 'max-content', 13 | minWidth: '150px' 14 | }, 15 | form: { 16 | container: { 17 | padding: '20px', 18 | border: '1px solid #F0F8FF', 19 | borderRadius: '15px', 20 | width: 'max-content', 21 | marginBottom: '40px' 22 | }, 23 | inputs: { 24 | marginBottom: '5px' 25 | }, 26 | submitBtn: { 27 | marginTop: '10px', 28 | padding: '10px 15px', 29 | border:'none', 30 | backgroundColor: 'lightseagreen', 31 | fontSize: '14px', 32 | borderRadius: '5px' 33 | } 34 | } 35 | } 36 | 37 | const initialState = { 38 | userFirstname: null, 39 | userLastname: null, 40 | userPhone: null 41 | }; 42 | function reducer(state, action) { 43 | switch (action.type) { 44 | case 'SET_USER_FIRST_NAME': 45 | return {...state, userFirstname: action.payload}; 46 | case 'SET_USER_LAST_NAME': 47 | return {...state, userLastname: action.payload}; 48 | case 'SET_USER_PHONE': 49 | return {...state, userPhone: action.payload}; 50 | default: 51 | throw new Error(); 52 | } 53 | } 54 | 55 | function PhoneBookForm({ addEntryToPhoneBook }) { 56 | const [state, dispatch] = useReducer(reducer, initialState); 57 | const handleChange = (evt) => { 58 | const name = evt.target.name; 59 | const value = evt.target.value; 60 | switch (name) { 61 | case 'userFirstname': 62 | dispatch({type: 'SET_USER_FIRST_NAME', payload: value}); 63 | break; 64 | case 'userLastname': 65 | dispatch({type: 'SET_USER_LAST_NAME', payload: value}); 66 | break; 67 | case 'userPhone': 68 | dispatch({type: 'SET_USER_PHONE', payload: value}); 69 | break; 70 | default: 71 | throw new Error(); 72 | } 73 | } 74 | 75 | const handleSubmit = (evt) => { 76 | evt.preventDefault(); 77 | const { userFirstname, userLastname, userPhone } = state; 78 | if (userFirstname && userLastname && userPhone) { 79 | addEntryToPhoneBook(state); 80 | } 81 | } 82 | return ( 83 |
84 | 85 |
86 | 93 |
94 | 95 |
96 | 103 |
104 | 105 |
106 | 113 |
114 | 120 |
121 | ) 122 | } 123 | 124 | function InformationTable(props) { 125 | const { userFirstname, userLastname, userPhone } = props; 126 | return ( 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 |
First nameLast namePhone
{userFirstname && userFirstname}{userLastname && userLastname}{userPhone && userPhone}
143 | ); 144 | } 145 | 146 | function Application() { 147 | const [user, setUser] = useState({}) 148 | return ( 149 |
150 | 151 | 152 |
153 | ); 154 | } 155 | 156 | ReactDOM.render( 157 | , 158 | document.getElementById('root') 159 | ); 160 | -------------------------------------------------------------------------------- /problems/javascript/PrintList.js: -------------------------------------------------------------------------------- 1 | // Q1... 2 | // time complexity: => N*A 3 | // space complexity: counts * 5-6 counts 4 | 5 | 6 | function buildStore(store, numCount, query){ 7 | while (query.length) { 8 | store[query] = store[query] ? 9 | (store[query] + numCount) : 10 | numCount; 11 | //1 member debug? 12 | let unjoinedArray = query.split("."); 13 | unjoinedArray.shift(); 14 | query = unjoinedArray.join("."); 15 | } 16 | return store; 17 | } 18 | 19 | 20 | function createCounts(counts) { 21 | let store = {} 22 | for (let count of counts) { 23 | let [numCount, query] = count.split(","); 24 | store = buildStore(store, Number(numCount), query); 25 | } 26 | 27 | printStore(store); 28 | } 29 | 30 | function printStore(store) { 31 | console.log(store); 32 | } 33 | 34 | createCounts(counts); 35 | -------------------------------------------------------------------------------- /problems/javascript/PrintReverseList.js: -------------------------------------------------------------------------------- 1 | ``` 2 | You are given an immutable linked list, print out all values of each node in reverse with the help of the following interface: 3 | 4 | ImmutableListNode: An interface of immutable linked list, you are given the head of the list. 5 | You need to use the following functions to access the linked list (you can't access the ImmutableListNode directly): 6 | 7 | ImmutableListNode.printValue(): Print value of the current node. 8 | ImmutableListNode.getNext(): Return the next node. 9 | The input is only given to initialize the linked list internally. You must solve this problem without modifying the linked list. In other words, you must operate the linked list using only the mentioned APIs. 10 | 11 | 12 | 13 | Follow up: 14 | 15 | Could you solve this problem in: 16 | 17 | Constant space complexity? 18 | Linear time complexity and less than linear space complexity? 19 | 20 | 21 | Example 1: 22 | 23 | Input: head = [1,2,3,4] 24 | Output: [4,3,2,1] 25 | Example 2: 26 | 27 | Input: head = [0,-4,-1,3,-5] 28 | Output: [-5,3,-1,-4,0] 29 | Example 3: 30 | 31 | Input: head = [-2,0,6,4,4,-6] 32 | Output: [-6,4,4,6,0,-2] 33 | 34 | 35 | Constraints: 36 | 37 | The length of the linked list is between [1, 1000]. 38 | The value of each node in the linked list is between [-1000, 1000]. 39 | ``` 40 | 41 | /** 42 | * // This is the ImmutableListNode's API interface. 43 | * // You should not implement it, or speculate about its implementation. 44 | * function ImmutableListNode() { 45 | * @ return {void} 46 | * this.printValue = function() { // print the value of this node. 47 | * ... 48 | * }; 49 | * 50 | * @return {ImmutableListNode} 51 | * this.getNext = function() { // return the next node. 52 | * ... 53 | * }; 54 | * }; 55 | */ 56 | 57 | /** 58 | * @param {ImmutableListNode} head 59 | * @return {void} 60 | */ 61 | var printLinkedListInReverse = function(head) { 62 | let listValues = []; 63 | let node = head; 64 | while (node.getNext()){ 65 | listValues.unshift(node); 66 | node = node.getNext(); 67 | } 68 | listValues.unshift(node); 69 | for (let node of listValues){ 70 | node.printValue(); 71 | } 72 | }; 73 | -------------------------------------------------------------------------------- /problems/javascript/ProductToN.js: -------------------------------------------------------------------------------- 1 | //recursion approach 2 | //javasript does allow tail optimized recursion tho; 3 | 4 | // function product1ToN(n) { 5 | // // We assume n >= 1 6 | // return (n > 1) ? (n * product1ToN(n-1)) : 1; 7 | // } 8 | 9 | //bottom-up approach 10 | 11 | function product1ToN(n){ 12 | let res = 1; 13 | for (let i = 1; i <= n; i++){ 14 | res *= i; 15 | } 16 | 17 | return res; 18 | } 19 | 20 | console.log(product1ToN(30)) -------------------------------------------------------------------------------- /problems/javascript/PromiseRace.js: -------------------------------------------------------------------------------- 1 | function race(promises) { 2 | return new Promise((res, rej) => { 3 | promises.map((p) => p.then((resp) => { 4 | res(resp); 5 | })); 6 | }) 7 | } 8 | 9 | const timeout = (ms) => { 10 | return new Promise((resolve, reject) => { 11 | setTimeout(() => { 12 | resolve(ms); 13 | }, ms); 14 | }); 15 | }; 16 | 17 | const promises = [ 18 | timeout(0), 19 | timeout(500), 20 | timeout(1000) 21 | ]; 22 | 23 | race(promises).then((response) => { 24 | // 0 25 | console.log(response); 26 | }); -------------------------------------------------------------------------------- /problems/javascript/Promisify.js: -------------------------------------------------------------------------------- 1 | function promisify(fn){ 2 | return (...rest) => { 3 | const executor = (res, rej) => { 4 | const customCallback = (result) => { 5 | res(Promise.resolve(result)); 6 | }; 7 | fn.call(this, ...rest.concat(customCallback)); 8 | } 9 | 10 | return new Promise(executor); 11 | } 12 | } 13 | 14 | 15 | function multiply(num){ 16 | return num * 4; 17 | } 18 | function add(x, y, cb){ 19 | const res = x+y; 20 | cb(res); 21 | } 22 | 23 | addPromise = promisify(add); 24 | 25 | addPromise(1,2).then(res => { console.log(res) }).then().then(); 26 | -------------------------------------------------------------------------------- /problems/javascript/QuestionMarks.js: -------------------------------------------------------------------------------- 1 | // Questions Marks 2 | // Have the function QuestionsMarks(str) take the str string parameter, which will contain single digit numbers, letters, and question marks, and check if there are exactly 3 question marks between every pair of two numbers that add up to 10. If so, then your program should return the string true, otherwise it should return the string false. If there aren't any two numbers that add up to 10 in the string, then your program should return false as well. 3 | 4 | // For example: if str is "arrb6???4xxbl5???eee5" then your program should return true because there are exactly 3 question marks between 6 and 4, and 3 question marks between 5 and 5 at the end of the string. 5 | // Examples 6 | // Input: "aa6?9" 7 | // Output: false 8 | // Input: "acc?7??sss?3rr1??????5" 9 | // Output: true 10 | // let str = "acc?7??sss?3rr1??????5" 11 | // function QuestionsMarks(str){ 12 | // let qNumArr = str.replace(/[a-z]/ig, '').split("").map((c) => { 13 | // if (c.match(/\d/)) { 14 | // return parseInt(c); 15 | // } 16 | // return c; 17 | // }); 18 | // let hash = {}; 19 | // let questionMarks = 0; 20 | // let output = false; 21 | // for (let i = 0; i < qNumArr.length; i++){ 22 | // const val = qNumArr[i]; 23 | // if (Number.isInteger(val)){ 24 | // // check if ten pair exists 25 | // const pair = 10 - val; 26 | // const isTen = hash[pair]; 27 | // if (isTen && questionMarks !== 3){ 28 | // return false; 29 | // } else if (isTen && questionMarks === 3){ 30 | // output = true; 31 | // hash = {}; 32 | // } 33 | // hash[val] = i; 34 | // questionMarks = 0; 35 | // } else { 36 | // questionMarks++ 37 | // } 38 | // } 39 | // return output; 40 | // } 41 | 42 | // function QuestionsMarks(str){ 43 | // let numPlace = []; 44 | // let output = false; 45 | // for (let i = 0; i < str.length; i++){ 46 | // const v = str[i]; 47 | // if (v.match(/\d/)){ 48 | // numPlace.push(i); 49 | // } 50 | // } 51 | 52 | // for (let i = 0; i < numPlace.length - 1; i++){ 53 | // const firstIdx = numArr[i]; 54 | // const firstNum = parseInt(str[firstIdx]); 55 | // const secondIdx = numArr[i+1]; 56 | // const secondNum = parseInt(str[secondIdx]); 57 | // const isTen = firstNum + secondNum === 10; 58 | // if (isTen) { 59 | // const isMatch = str.substring(firstIdx, secondIdx).match(/^???$/); 60 | // if (isMatch){ 61 | // output = true; 62 | // } else { 63 | // return false; 64 | // } 65 | // } 66 | // } 67 | // return output; 68 | // } 69 | 70 | 71 | function QuestionsMarks(str){ 72 | let output = false; 73 | let numArr = []; 74 | for (let i = 0; i < str.length; i++){ 75 | const char = str[i]; 76 | if (char.match(/\d/)){ 77 | numArr.push(i); 78 | } 79 | } 80 | 81 | for (let i = 0; i < (numArr.length - 1); i++){ 82 | const fIdx = numArr[i]; 83 | const sIdx = numArr[i+1]; 84 | const firstNum = parseInt(str[fIdx]); 85 | const secondNum = parseInt(str[sIdx]); 86 | const isTen = firstNum + secondNum === 10; 87 | if (isTen){ 88 | // check what is in between 89 | const qMark = str.slice(fIdx,sIdx).replace(/[^\?]/g, '') 90 | if (qMark === '???'){ 91 | output = true; 92 | } else { 93 | return false; 94 | } 95 | } 96 | } 97 | return output; 98 | } 99 | 100 | 101 | 102 | const expectations = [["arrb6???4xxbl5???eee5" , true], 103 | ["acc?7??sss?3rr1??????5",true], 104 | ["9???1???9???1???9",true], 105 | ["5??aaaaaaaaaaaaaaaaaaa?5?a??5",true] 106 | ] 107 | for (let [str, exp] of expectations){ 108 | console.log(QuestionsMarks(str), 'expe', exp); 109 | } 110 | 111 | // let numberQuestionMarks = 0; 112 | // let firstNum = null; 113 | // let output = []; 114 | // qNumArr.forEach((v, i) => { 115 | // if (Number.isInteger(v)){ 116 | // const isTen = v + firstNum === 10; 117 | // if (firstNum === 5){ 118 | // console.log(v); 119 | // console.log(numberQuestionMarks); 120 | // } 121 | // if (isTen && numberQuestionMarks !== 3){ 122 | // return false; 123 | // } else if (isTen && numberQuestionMarks === 3){ 124 | // firstNum = v; 125 | // numberQuestionMarks = 0; 126 | // output.push(true); 127 | // } 128 | // } else { 129 | // numberQuestionMarks++; 130 | // } 131 | // }); 132 | // return output.length === 0 ? false : true; 133 | // } -------------------------------------------------------------------------------- /problems/javascript/RemoveVowelsString.js: -------------------------------------------------------------------------------- 1 | ``` 2 | Given a string S, remove the vowels 'a', 'e', 'i', 'o', and 'u' from it, and return the new string. 3 | 4 | 5 | 6 | Example 1: 7 | 8 | Input: "leetcodeisacommunityforcoders" 9 | Output: "ltcdscmmntyfrcdrs" 10 | Example 2: 11 | 12 | Input: "aeiou" 13 | Output: "" 14 | 15 | 16 | Note: 17 | 18 | S consists of lowercase English letters only. 19 | 1 <= S.length <= 1000 20 | ``` 21 | 22 | /** 23 | * @param {string} S 24 | * @return {string} 25 | */ 26 | var removeVowels = function(str) { 27 | let stringWithoutVowels = str.split("").filter(s => !s.match(/[a,e,i,o,u]/)); 28 | return stringWithoutVowels.join(""); 29 | }; 30 | -------------------------------------------------------------------------------- /problems/javascript/ReverseWordsString.js: -------------------------------------------------------------------------------- 1 | /** 2 | Given a string, you need to reverse the order of characters in each word within a sentence while still preserving whitespace and initial word order. 3 | 4 | Example 1: 5 | Input: "Let's take LeetCode contest" 6 | Output: "s'teL ekat edoCteeL tsetnoc" 7 | Note: In the string, each word is separated by single space and there will not be any extra space in the string. 8 | */ 9 | 10 | /** 11 | * @param {string} s 12 | * @return {string} 13 | */ 14 | var reverseWords = function(s) { 15 | let stringArr = s.split(" "); 16 | stringArr = stringArr.map((string) => [string]); 17 | stringArr = stringArr.map((sArray) => { 18 | const strA = sArray[0].split(""); 19 | const revStrA = strA.reverse(); 20 | return revStrA.join(""); 21 | }); 22 | return stringArr.join(" "); 23 | }; 24 | 25 | console.log(reverseWords("Let's take LeetCode contest")) 26 | -------------------------------------------------------------------------------- /problems/javascript/RnadomSet.js: -------------------------------------------------------------------------------- 1 | 2 | class RandomizedSet { 3 | constructor(){ 4 | this.map = new Map(); 5 | this.items = []; 6 | } 7 | /** 8 | * Inserts a value to the set. Returns true if the set did not already contain the specified element. 9 | * @param {number} val 10 | * @return {boolean} 11 | */ 12 | insert(val){ 13 | if (this.map.has(val)){ 14 | return false 15 | } 16 | this.items.push(val); 17 | this.map.set(val, this.items.length - 1); 18 | return true; 19 | } 20 | /** 21 | * Removes a value from the set. Returns true if the set contained the specified element. 22 | * @param {number} val 23 | * @return {boolean} 24 | */ 25 | remove(val){ 26 | if (this.map.has(val)) { 27 | const [last, index] = [this.items[this.items.length - 1], this.map.get(val)]; 28 | this.items[index] = last; 29 | this.map.set(last, index); 30 | this.items.pop(); 31 | this.map.delete(val); 32 | return true; 33 | } 34 | return false; 35 | } 36 | 37 | getRandom(){ 38 | let idx = Math.floor(Math.random()*this.map.size); 39 | return this.items[idx]; 40 | } 41 | } 42 | let a = ["RandomizedSet","insert","insert","remove","insert","remove","getRandom"] 43 | let b = [[],[0],[1],[0],[2],[1],[]] 44 | let r = new RandomizedSet(); 45 | for (let i = 1; i< b.length; i++){ 46 | let arg = b[i][0]; 47 | let action = a[i]; 48 | console.log('result', r[action](arg)); 49 | console.log('args', arg) 50 | console.log('action', action) 51 | console.log('this.items', r.items) 52 | console.log('map', r.map) 53 | console.log('count', r.count) 54 | console.log('result', r[action](arg)); 55 | console.log('\n'); 56 | } -------------------------------------------------------------------------------- /problems/javascript/Set.js: -------------------------------------------------------------------------------- 1 | // [2] Implement a set-like data structure that supports Insert, Remove, and GetRandomElement efficiently. Example: If you insert the elements 1, 3, 6, 8 and remove 6, the structure should contain [1, 3, 8]. Now, GetRandom should return one of 1, 3 or 8 with equal probability. 2 | 3 | class Set { 4 | container = { }; 5 | 6 | insert(element) { 7 | this.container[element] = true; 8 | } 9 | 10 | remove(element) { 11 | // no returns; 12 | delete this.container[element]; 13 | } 14 | 15 | getRandomElement(){ 16 | const randomElementIndex = Math.floor( 17 | Math.random() * (Object.keys(this.container).length - 1) 18 | ); 19 | return Object.keys(this.container)[randomElementIndex]; 20 | } 21 | } 22 | // { 1: true, 3: true, 8:true } 23 | // getRandomElement -> Math.random() * (2) 0 1 or 2 24 | 25 | class Set{ 26 | container = new Map(); 27 | 28 | insert(el) { 29 | return this.container.set(el, true); 30 | } 31 | 32 | remove(el) { 33 | return this.container.delete(el); 34 | } 35 | 36 | getRandomElement() { 37 | const randomElementIndex = Math.floor( 38 | Math.random() * (this.container.keys().length - 1) 39 | ); 40 | return this.container.keys()[randomElementIndex]; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /problems/javascript/ShoppingCartMinLimit.js: -------------------------------------------------------------------------------- 1 | //return the optimal number of items 2 | //O(n(log n)) runtime 3 | //O(n) space 4 | function productList(cartAmount, products, limit){ 5 | return products.sort((a, b) => { 6 | const goal = Math.abs(cartAmount - limit); 7 | const diffB = Math.abs(goal - b); 8 | const diffA = Math.abs(goal - a); 9 | 10 | if (diffB > diffA){ 11 | return -1; 12 | } else if (diffA > diffB){ 13 | return 1; 14 | } else { 15 | return b - a; 16 | } 17 | }); 18 | } 19 | let cartAmount= 20 20 | let limit = 25 21 | let products = [1000,6,6,4,10,1] 22 | //[10,6,4,1,1000] 23 | 24 | console.log(productList(cartAmount, products, limit)); 25 | -------------------------------------------------------------------------------- /problems/javascript/ThreeSum.js: -------------------------------------------------------------------------------- 1 | function findThreeSum(list, x) { 2 | for (let i = 0; i < list.length; i++){ 3 | const val = list[i]; 4 | const remainder = x - val; 5 | const isPair = findTwoSum(list.slice(i+1), remainder); 6 | if (isPair){ 7 | return true; 8 | } 9 | } 10 | return false; 11 | } 12 | 13 | 14 | function findTwoSum(list, x){ 15 | let seen = new Set(); 16 | for (let i = 0; i < list.length; i++){ 17 | const num = list[i]; 18 | const otherValue = x - num; 19 | if (seen.has(otherValue)){ 20 | return true; 21 | } 22 | seen.add(num); 23 | } 24 | return false; 25 | } 26 | function assert(result, expected, msg){ 27 | console.log(result, expected, msg); 28 | } 29 | assert(findThreeSum([1,2,2], 5), true, 'Should be true 1,2,3'); 30 | assert(findThreeSum([1,1,1], 4), false, 'Should be false, no nums'); 31 | assert(findThreeSum([1,2,4,4,8], 10), true, 'Should be true 2,4,4'); 32 | assert(findThreeSum([1,2,4,4,8], 14), true, 'Should be true 2,4,8'); 33 | assert(findThreeSum([1,2,4,4,8], 17), false, 'Should be false'); 34 | assert(findThreeSum([1,2,10,15,4], 7), true, '4,2,1') 35 | assert(findThreeSum([1,1,1], 2), false, 'Should be false') -------------------------------------------------------------------------------- /problems/javascript/Throttle.js: -------------------------------------------------------------------------------- 1 | function Cow(){}; 2 | 3 | Cow.prototype.moo = function(currentTime, ...rest){ 4 | console.log('from fn',currentTime); 5 | console.log(this.mooCall, ...rest); 6 | } 7 | const a = new Cow(); 8 | 9 | function throttle(fn, t = 500, context){ 10 | let i = 0; 11 | let id; 12 | return (...rest) => { 13 | if (i === 0) { 14 | fn.call(context, ...rest, ); 15 | id = setInterval(() => { 16 | if (i < t){ 17 | i++; 18 | } else { 19 | clearInterval(id); 20 | i = 0; 21 | } 22 | }, 1); 23 | } 24 | } 25 | } 26 | const throttledMoo = throttle(a.moo, undefined, {'mooCall': 'moooooo'}) 27 | throttledMoo(''); 28 | setInterval(() => { 29 | throttledMoo('wow', 'throttled babe'); 30 | }, 1); 31 | -------------------------------------------------------------------------------- /problems/javascript/TwoSum.js: -------------------------------------------------------------------------------- 1 | // [1] Given an array of integers, determine whether or not there exist two elements in the array (at different positions) whose sum is equal to some target value. Examples: [5, 4, 2, 4], 8 --> true [5, 1, 2, 4], 8 --> false 2 | // 3 | // function twoSum (array, targetSum) { 4 | // // { 5: true, 4: true, 2: true} -> return true; 5 | // // { 5: true, 1: true, 2: true, 4: true } -> return false 6 | // let seen = { }; 7 | // for (let integer of array) { 8 | // const otherNum = targetSum - integer; 9 | // if (seen[otherNum]) { 10 | // return true; 11 | // } else { 12 | // seen[integer] = true; 13 | // } 14 | // } 15 | // return false; 16 | // //O(n)time, O(n) memory 17 | // } 18 | // 19 | // function twoSumNaive(array, targetSum) { 20 | // for (let [integer, firstIndex] of array.entries()) { 21 | // const otherIdx = array.findIndex(targetSum - integer); 22 | // if (otherIdx && otherIdx !== firstIndex) { 23 | // return true; 24 | // } 25 | // } 26 | // return false; 27 | // //O(n^2 time) O(1) memory 28 | // } 29 | 30 | /** 31 | * @param {number[]} nums 32 | * @param {number} target 33 | * @return {number[]} 34 | return array indices 35 | */ 36 | //O(n) O(n) 37 | var twoSum = function(nums, target) { 38 | let nHash = {}; 39 | nums.forEach((num, i) => { 40 | nHash[num] = nHash[num] ? nHash[num].concat(i) : [i]; 41 | }); 42 | for (let num of nums){ 43 | const otherPair = target - num; 44 | const idxArrOtherPair = nHash[otherPair]; 45 | if (idxArrOtherPair){ 46 | for (let index of nHash[num]){ 47 | const otherIndex = idxArrOtherPair.find(i => i !== index); 48 | if (otherIndex !== undefined){ 49 | //we have found a unique pair 50 | return [index, otherIndex]; 51 | } 52 | } 53 | 54 | } 55 | } 56 | }; 57 | 58 | //O(n) O(n) but one pass; 59 | var twoSum = function(nums, target) { 60 | let nHash = new Map(); 61 | for (let i=0; i < nums.length; i++){ 62 | const num = nums[i]; 63 | const complement = target - num; 64 | const otherIdx = nHash.get(complement); 65 | if (otherIdx !== undefined && otherIdx !== i){ 66 | return [otherIdx, i]; 67 | } 68 | nHash.set(num, i); 69 | } 70 | }; 71 | 72 | console.log(twoSum([2,7,11,15], 9)); 73 | -------------------------------------------------------------------------------- /problems/javascript/UniqueChars.js: -------------------------------------------------------------------------------- 1 | let str = `If you want to jumpstart the process of talking to us about this role, here’s a little challenge: write a program that outputs the largest unique set of characters that can be removed from this paragraph without letting its length drop below 50.` 2 | function outputUnique(str, limit=50){ 3 | let hash = {}; 4 | 5 | for (let char of str){ 6 | if (hash[char]){ 7 | hash[char] = hash[char] + 1; 8 | } else { 9 | hash[char] = 1; 10 | } 11 | } 12 | const orderedKeys = Object.keys(hash).sort((key1, key2) => { 13 | return hash[key1] - (hash[key2]); 14 | }); 15 | let currLength = str.length; 16 | let res = []; 17 | for (let key of orderedKeys){ 18 | let value = hash[key]; 19 | if ((currLength - value) >= limit){ 20 | res.push(key); 21 | currLength = currLength - value; 22 | } 23 | } 24 | return res; 25 | } 26 | -------------------------------------------------------------------------------- /problems/javascript/contFrequencyArray.js: -------------------------------------------------------------------------------- 1 | ``` 2 | We are given a list nums of integers representing a list compressed with run-length encoding. 3 | 4 | Consider each adjacent pair of elements [a, b] = [nums[2*i], nums[2*i+1]] (with i >= 0). For each such pair, there are a elements with value b in the decompressed list. 5 | 6 | Return the decompressed list. 7 | 8 | 9 | 10 | Example 1: 11 | 12 | Input: nums = [1,2,3,4] 13 | Output: [2,4,4,4] 14 | Explanation: The first pair [1,2] means we have freq = 1 and val = 2 so we generate the array [2]. 15 | The second pair [3,4] means we have freq = 3 and val = 4 so we generate [4,4,4]. 16 | At the end the concatenation [2] + [4,4,4] is [2,4,4,4]. 17 | 18 | 19 | Constraints: 20 | 21 | 2 <= nums.length <= 100 22 | nums.length % 2 == 0 23 | 1 <= nums[i] <= 100 24 | ``` 25 | /** 26 | * @param {number[]} nums 27 | * @return {number[]} 28 | */ 29 | var decompressRLElist = function(nums) { 30 | let i = 0; 31 | let j = 1; 32 | let array = []; 33 | while (nums[i]){ 34 | const freq = nums[i]; 35 | const num = nums[j]; 36 | let resultant = new Array(freq); 37 | resultant.fill(num); 38 | array = array.concat(resultant) 39 | i = i + 2; 40 | j = j + 2; 41 | } 42 | return array; 43 | }; 44 | -------------------------------------------------------------------------------- /problems/javascript/duolingoScript.js: -------------------------------------------------------------------------------- 1 | var arr = Array.from(document.querySelectorAll('.MrbBK')) 2 | arr = arr.filter(a => !a.classList.contains('ewiWc')); 3 | var allSections = Array.from(document.querySelectorAll('.Xpzj7')) 4 | var titles = arr.map(node => { 5 | let title = node.parentElement.parentElement.parentElement.parentElement.innerText; 6 | const section = node.closest('.Xpzj7'); 7 | const level = allSections.indexOf(section) + 1; 8 | title = title[0].match(/[0-9]/) ? title.slice(1).concat('(Unlocked)').trim() : title; 9 | return { level, title } 10 | }); 11 | var levelSeen = new Set(); 12 | var string = ''; 13 | for (let {level, title} of titles) { 14 | if (!levelSeen.has(level)){ 15 | string = string.concat("\n\nLevel:"+ level + '\n'); 16 | levelSeen.add(level); 17 | } 18 | string = string.concat('\n' + title); 19 | } 20 | -------------------------------------------------------------------------------- /problems/javascript/flattenArray.js: -------------------------------------------------------------------------------- 1 | const flattenRecursion = list => list.reduce( 2 | (a, b) => a.concat(Array.isArray(b) ? flattenRecursion(b) : b), [] 3 | ); 4 | 5 | const flattenArrayNoRecursion = list => { 6 | let transform = list; 7 | while (transform.some(a => Array.isArray(a))){ 8 | transform = normalFlatten(transform); 9 | } 10 | return transform 11 | } 12 | 13 | const normalFlatten = list => { 14 | return list.reduce((a, b) => { 15 | return a.concat(b); 16 | }, []); 17 | }; 18 | 19 | 20 | //test helper 21 | const assertArray = function(fn, argument, expectation, message) { 22 | const result = fn(argument); 23 | const condition = JSON.stringify(result) === JSON.stringify(expectation); 24 | console.log(`\n${message}`) 25 | if (!condition) { 26 | throw Error(`Assertion failed for ${fn.name} called with ${JSON.stringify(argument)}:\n 27 | result: ${JSON.stringify(result)}\n 28 | expected: ${JSON.stringify(expectation)}\n 29 | `); 30 | } 31 | console.log(`Assertion passed for ${fn.name} called with ${JSON.stringify(argument)}: \n 32 | result: ${JSON.stringify(result)} 33 | expected: ${JSON.stringify(expectation)} 34 | `) 35 | }; 36 | // let's assume we have arrays 37 | const crazyArray = [[[[3,[[[[[[[3,[[[[3,[3,[3,[3]]]]]]]]]]]]]]]]]]; 38 | 39 | let fnsToBeTested = [flattenRecursion, flattenArrayNoRecursion]; 40 | for (let fn of fnsToBeTested){ 41 | const tests = [ 42 | { 43 | message: "it can handle empty", 44 | argument: [], 45 | result: [] 46 | }, 47 | { 48 | message: "it can handle no strings", 49 | argument: ['ajasbchjasbhdjbashjd'], 50 | result: ['ajasbchjasbhdjbashjd'] 51 | }, 52 | { 53 | message: "it can handle nested inside with multiple data types", 54 | argument: [ 3, "SDSdkjahfkjcansfkjsands",[[[[[[[[[[[[]]]]]]]]]]]], 3, 3, 3, 3 ], 55 | result: [3,"SDSdkjahfkjcansfkjsands",3,3,3,3] 56 | }, 57 | { 58 | message: "it can crazy nested", 59 | argument: crazyArray, 60 | result: [ 3, 3, 3, 3, 3, 3 ] 61 | }, 62 | { 63 | message: "it can handle objects with array", 64 | argument: [{}, {'a': '1'}, {'b': ['yay']}], 65 | result: [{}, {'a': '1'}, {'b': ['yay']}], 66 | } 67 | ] 68 | 69 | for (let {argument, result, message} of tests) { 70 | assertArray(fn, argument, result, message) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /problems/javascript/formatObjects.js: -------------------------------------------------------------------------------- 1 | const str = `photo.jpg, Warsaw, 2013-09-05 14:08:15 2 | john.png, London, 2015-06-20 15:13:22 3 | myFriends.png, Warsaw, 2013-09-05 14:07:13 4 | Eiffel.jpg, Paris, 2015-07-23 08:03:02 5 | pisatower.jpg, Paris, 2015-07-22 23:59:59 6 | BOB.jpg, London, 2015-08-05 00:02:03 7 | notredame.png, Paris, 2015-09-01 12:00:00 8 | me.jpg, Warsaw, 2013-09-06 15:40:22 9 | a.png, Warsaw, 2016-02-13 13:33:50 10 | b.jpg, Warsaw, 2016-01-02 15:12:22 11 | c.jpg, Warsaw, 2016-01-02 14:34:30 12 | d.jpg, Warsaw, 2016-01-02 15:15:01 13 | e.png, Warsaw, 2016-01-02 09:49:09 14 | f.png, Warsaw, 2016-01-02 10:55:32 15 | g.jpg, Warsaw, 2016-02-29 22:13:11` 16 | 17 | function solution(str) { 18 | // write your code in JavaScript (Node.js 8.9.4) 19 | const arrPhotos = str.split("\n"); 20 | let cityMap = {}; 21 | arrPhotos.forEach((str, idx) => { 22 | let [oName, city, timestamp] = str.split(", "); 23 | let obj = { 24 | oName, 25 | city, 26 | oIdx: idx, 27 | timestamp, 28 | unix: new Date(timestamp).getTime()/1000 29 | } 30 | cityMap[city] = cityMap[city] ? cityMap[city].concat(obj) : [obj]; 31 | }) 32 | for (let [city, v] of Object.entries(cityMap)){ 33 | const sortedCity = v.sort((a, b) => compare(a.unix, b.unix)); 34 | const maxLength = cityMap[city].length; 35 | const maxDigits = numDigits(maxLength); 36 | cityMap[city] = cityMap[city].map((o, i) => { 37 | const { oName } = o; 38 | const extension = oName.split(".")[1]; 39 | return { 40 | ...o, 41 | newName: `${city}${pad(i+1, maxDigits)}.${extension}` 42 | } 43 | }) 44 | } 45 | let combinedStr = Object.values(cityMap) 46 | .reduce((acc, val) => acc.concat(val), []) 47 | .sort((a, b) => compare(a.oIdx, b.oIdx)) 48 | .map(o => o.newName) 49 | .join("\n"); 50 | return combinedStr; 51 | } 52 | function compare(fieldA, fieldB){ 53 | if (fieldA < fieldB) { 54 | return -1; 55 | } else if (fieldA > fieldB) { 56 | return 1; 57 | } else { 58 | return 0 59 | } 60 | } 61 | function pad(num, size) { 62 | var s = num+""; 63 | while (s.length < size) s = "0" + s; 64 | return s; 65 | } 66 | 67 | function numDigits(x) { 68 | return Math.max(Math.floor(Math.log10(Math.abs(x))), 0) + 1; 69 | } 70 | console.log(solution(str)); 71 | -------------------------------------------------------------------------------- /problems/javascript/generateBalancedSubstring.js: -------------------------------------------------------------------------------- 1 | // Given an inputString, generate the *shortest* possible continuous 2 | // That contains both a lowercase char and an upperCaseChar 3 | 4 | function solution(inputString) { 5 | let inputArr = inputString.split(""); 6 | // write your code in JavaScript (Node.js 8.9.4) 7 | 8 | //first we generate smaller and smaller windows 9 | //this contains the smallest and smallest result 10 | const result = []; 11 | for (let i=0; i< inputArr.length; i++) { 12 | let windows = toWindows(inputArr, inputArr.length - i); 13 | for (let w of windows) { 14 | let isStringBalanced = checkBalanced(w); 15 | if (isStringBalanced) { 16 | result.push(w.join("")); 17 | } 18 | } 19 | } 20 | // after this loops runs, we select the non empty string 21 | if (result.length === 0) { 22 | return -1; 23 | } 24 | let string = result.reduce((a, b) => a.length <= b.length ? a : b); 25 | return string ? (string.length) : -1 26 | } 27 | 28 | function toWindows(inputArray, size) { 29 | return Array.from ( 30 | {length: inputArray.length - (size - 1)}, //get the appropriate length 31 | (_, index) => inputArray.slice(index, index+size) 32 | ) 33 | } 34 | 35 | function checkBalanced(str){ 36 | const lowerCaseChar = new Set(); 37 | const upperCaseChar = new Set(); 38 | for (let char of str) { 39 | if (char.toUpperCase() === char){ 40 | upperCaseChar.add(char); 41 | } else { 42 | lowerCaseChar.add(char); 43 | } 44 | } 45 | let ucA = Array.from(upperCaseChar); 46 | let lcA = Array.from(lowerCaseChar); 47 | let ucS = ucA.sort().map(a => a.toLowerCase()).join(""); 48 | let lcS = lcA.sort().join(""); 49 | if (ucS === lcS){ 50 | return true; 51 | } 52 | return false; 53 | } 54 | -------------------------------------------------------------------------------- /problems/javascript/maxLength.js: -------------------------------------------------------------------------------- 1 | // 1. Longest Subarray 2 | 3 | // A subarray of array a is defined as a contiguous block of a's elements having a length that is less than or equal to the length of the array. For example, the subarrays of array a = [1, 2, 3] are [1], [2], [3], [1, 2], [2, 3], and [1, 2, 3]. Given an integer, k = 3, the subarrays having elements that sum to a number ≤ k are [1], [2], and [1, 2]. The longest of these subarrays is [1, 2], which has a length of 2. Given an array, a, determine its longest subarray that sums to less than or equal to a given value k. 4 | 5 | // Function Description 6 | 7 | // Complete the function maxLength in the editor below. The function must return an integer that represents the length of the longest subarray of a that sums to a number ≤ k. 8 | 9 | // maxLength has the following parameter(s): 10 | 11 | // a[a[0],...a[n-1]]: an array of integers 12 | 13 | // k: an integer 14 | 15 | // Constraints 16 | 17 | // 1 ≤ n ≤ 105 18 | // 1 ≤ a[i] ≤ 103 19 | // 1 ≤ k ≤ 109 20 | 21 | // Input Format For Custom Testing 22 | // Sample Case 0 23 | 24 | // Sample Input For Custom Testing 25 | 26 | // Sample Input 0 27 | 28 | // 3 29 | // 1 30 | // 2 31 | // 3 32 | // 4 33 | 34 | // Sample Output 0 35 | 36 | // 2 37 | 38 | // Explanation 0 39 | 40 | // The subarrays of [1, 2, 3] having elements that sum to a number ≤ (k = 4) are [1], [2], [3], and [1, 2]. The longest of these is [1, 2], which has a length of 2. Return 2 as the answer. 41 | 42 | // Sample Case 1 43 | 44 | // Sample Input For Custom Testing 45 | 46 | // Sample Input 1 47 | 48 | // 4 49 | // 3 50 | // 1 51 | // 2 52 | // 1 53 | // 4 54 | 55 | // Sample Output 1 56 | 57 | // 3 58 | 59 | // Explanation 1 60 | 61 | // The subarrays of [3, 1, 2, 1] having elements that sum to a number ≤ (k = 4) are [3], [1], [2], [1], [3, 1], [1, 2], [2, 1], and [1, 2, 1]. The longest of these is [1, 2, 1], which has a length of 3. Return 3 as the answer. 62 | 63 | //unoptimal answer 64 | // 65 | 66 | /* 67 | * Complete the 'maxLength' function below. 68 | * 69 | * The function is expected to return an INTEGER. 70 | * The function accepts following parameters: 71 | * 1. INTEGER_ARRAY a 72 | * 2. INTEGER k 73 | */ 74 | 75 | function sumArray(el) { 76 | return el.reduce((accumulator, currentValue) => accumulator + currentValue); 77 | } 78 | 79 | function maxLength(a, k) { 80 | let output = []; 81 | function generateSubArrays(a, start, end) { 82 | console.log("being run"); 83 | if (end === a.length) { 84 | return; 85 | } else if (start > end) { 86 | generateSubArrays(a, 0, end + 1); 87 | } else { 88 | output.push(a.slice(start, end + 1)); 89 | generateSubArrays(a, start + 1, end); 90 | } 91 | } 92 | generateSubArrays(a, 0, 0); 93 | console.log(output); 94 | let longestLength = null; 95 | output.forEach((el, index) => { 96 | const arraySum = sumArray(el); 97 | if (arraySum <= k) { 98 | const elLength = el.length; 99 | if (longestLength === null) { 100 | longestLength = elLength; 101 | } else { 102 | longestLength = elLength > longestLength ? elLength : longestLength; 103 | } 104 | } 105 | }); 106 | return longestLength; 107 | } 108 | 109 | // optimal answer 110 | 111 | ("use strict"); 112 | 113 | const fs = require("fs"); 114 | 115 | process.stdin.resume(); 116 | process.stdin.setEncoding("utf-8"); 117 | 118 | let inputString = ""; 119 | let currentLine = 0; 120 | 121 | process.stdin.on("data", function (inputStdin) { 122 | inputString += inputStdin; 123 | }); 124 | 125 | process.stdin.on("end", function () { 126 | inputString = inputString.split("\n"); 127 | 128 | main(); 129 | }); 130 | 131 | function readLine() { 132 | return inputString[currentLine++]; 133 | } 134 | 135 | /* 136 | * Complete the 'maxLength' function below. 137 | * 138 | * The function is expected to return an INTEGER. 139 | * The function accepts following parameters: 140 | * 1. INTEGER_ARRAY a 141 | * 2. INTEGER k 142 | */ 143 | 144 | function maxLength(a, k) { 145 | let maxLen = -1; 146 | let sum = 0; 147 | let start = 0; 148 | let end = 0; 149 | for (let i = 0; i < a.length; i++) { 150 | sum += a[i]; 151 | end = i; 152 | while (sum > k) { 153 | sum -= a[start]; 154 | start++; 155 | } 156 | maxLen = Math.max(maxLen, end - start + 1); 157 | } 158 | return maxLen; 159 | } 160 | -------------------------------------------------------------------------------- /problems/javascript/routesFromCities.js: -------------------------------------------------------------------------------- 1 | function findRoutes(routes) { 2 | const reversedRouteMap = routes.reduce((acc, el) => { 3 | const [des1, des2] = el; 4 | acc[des2] = des1; 5 | return acc; 6 | }, {}); 7 | const routeMap = new Map(routes); 8 | const initialCities = routes.map((iten) => { 9 | const [des1, des2] = iten; 10 | return des1; 11 | }); 12 | let from = initialCities.find((c) => !reversedRouteMap[c]); 13 | let output = []; 14 | while (routeMap.size !== 0) { 15 | output.push(from); 16 | const destination = routeMap.get(from); 17 | routeMap.delete(from); 18 | from = destination; 19 | } 20 | output.push(from); 21 | return output.join(", "); 22 | } 23 | 24 | // let assert = require('chai').assert; 25 | // describe("Follow That Spy", function() { 26 | // it('Should return the correct route', function() { 27 | // assert.deepEqual(findRoutes([["MNL", "TAG"], ["CEB", "TAC"], ["TAG", "CEB"], ["TAC", "BOR"]]), "MNL, TAG, CEB, TAC, BOR"); 28 | // assert.deepEqual(findRoutes([["Chicago", "Winnipeg"], ["Halifax", "Montreal"], ["Montreal", "Toronto"], ["Toronto", "Chicago"], ["Winnipeg", "Seattle"]]), "Halifax, Montreal, Toronto, Chicago, Winnipeg, Seattle"); 29 | // }); 30 | // assert.deepEqual(findRoutes([["USA","BRA"],["JPN","PHL"],["BRA","UAE"],["UAE","JPN"]]), "USA, BRA, UAE, JPN, PHL"); 31 | // }); 32 | -------------------------------------------------------------------------------- /problems/javascript/subtasksList.js: -------------------------------------------------------------------------------- 1 | const tasks = [ 2 | { id: 2, subTaskOf: 1, description: 'Brew coffee' }, 3 | { id: 3, subTaskOf: 2, description: 'Boil water' }, 4 | { id: 1, subTaskOf: null, description: 'Make breakfast' }, 5 | { id: 4, subTaskOf: 2, description: 'Grind coffee beans' }, 6 | { id: 5, subTaskOf: 2, description: 'Pour water over coffee grounds' } 7 | ]; 8 | 9 | class Task { 10 | constructor({id, description, subTaskOf}){ 11 | this.id = id; 12 | this.subTaskOf = subTaskOf; 13 | this.description = description; 14 | } 15 | 16 | addChildren(task){ 17 | if (!this.subTasks){ 18 | this.subTasks = []; 19 | } 20 | this.subTasks.push(task); 21 | } 22 | includesTask(task){ 23 | if (this.subTasks) { 24 | return this.subTasks.find(subTask => subTask.id === task.id); 25 | } else { 26 | return false; 27 | } 28 | } 29 | } 30 | 31 | function groupTasks(tasks) { 32 | const taskMap = {}; 33 | tasks.forEach(({id, subTaskOf}) => { 34 | if (!taskMap[id]){ 35 | taskMap[id] = []; 36 | } 37 | if (!taskMap[subTaskOf]) { 38 | taskMap[subTaskOf] = []; 39 | } 40 | if (subTaskOf){ 41 | taskMap[subTaskOf].push(id); 42 | } 43 | }); 44 | let resp = {}; 45 | let taskClasses = tasks.map(task => new Task(task)); 46 | for (let [key, subtaskIds] of Object.entries(taskMap)){ 47 | if (subtaskIds.length){ 48 | let subTasks = subtaskIds.map(tId => { 49 | let foundTask = taskClasses.find(task => { 50 | return String(task.id) === String(tId) 51 | }); 52 | return foundTask; 53 | }); 54 | let taskToAddSubChildrenTo = taskClasses.find(task => String(task.id) === String(key)); 55 | subTasks.forEach((subTask) => { 56 | taskToAddSubChildrenTo.addChildren(subTask); 57 | }); 58 | } 59 | } 60 | const a = taskClasses.filter((task) => { 61 | return !taskClasses.find(t => t.includesTask(task)); 62 | })[0]; 63 | const stringify = JSON.stringify(a); 64 | return JSON.parse(stringify); 65 | } 66 | 67 | console.log(groupTasks(tasks)); 68 | -------------------------------------------------------------------------------- /problems/javascript/toWindows.js: -------------------------------------------------------------------------------- 1 | function solution(inputArr) { 2 | // write your code in JavaScript (Node.js 8.9.4) 3 | 4 | //first we generate smaller and smaller windows 5 | //this contains the smallest and smallest result 6 | for (let i=0; i< inputArr.length; i++) { 7 | let windows = toWindows(inputArr, inputArr.length - i); 8 | for (let w of windows) { 9 | if (checkTwo(w)){ 10 | return w.length; 11 | } 12 | } 13 | } 14 | // after this loop runs, means we have no strings length, just return 1. 15 | return 1; 16 | } 17 | 18 | function toWindows(inputArray, size) { 19 | return Array.from ( 20 | {length: inputArray.length - (size - 1)}, //get the appropriate length 21 | (_, index) => inputArray.slice(index, index+size) 22 | ) 23 | } 24 | 25 | 26 | 27 | function checkTwo(arr){ 28 | const set = new Set(arr); 29 | if (set.size > 0 && set.size <= 2) { 30 | return true; 31 | } 32 | return false 33 | } 34 | console.log(solution([4, 2, 2, 4, 2])) 35 | console.log(solution([1, 2, 3, 2])) 36 | console.log(solution([0, 5, 4, 4, 5, 12])) 37 | console.log(solution([4,4])); 38 | console.log(solution([4])); 39 | -------------------------------------------------------------------------------- /problems/javascript/treesandgraphs.js: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;} 3 | {\colortbl;\red255\green255\blue255;} 4 | \margl1440\margr1440\vieww10800\viewh8400\viewkind0 5 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 6 | 7 | \f0\fs24 \cf0 //Write a node class\ 8 | \ 9 | function Node(options) \{\ 10 | this.children = [];\ 11 | this.value = options.value || nil;\ 12 | \}\ 13 | \ 14 | //Given a list of numbers, construct a binary search tree\ 15 | \ 16 | function BinaryNode(option)\{\ 17 | this.right = options.right || nil;\ 18 | this.left = options.left || nil;\ 19 | this.value = options.value || nil;\ 20 | \}\ 21 | \ 22 | \ 23 | var array = [1,2,3,19,22,34,5,6,300];\ 24 | \ 25 | function constructTree(array) \{\ 26 | let sortedArray = array.sort();\ 27 | let lastNode = nil;\ 28 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 29 | \cf0 let nextNode = nil;\ 30 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 31 | \cf0 array.forEach((element, idx) => \{\ 32 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 33 | \cf0 nextNode = new BinaryNode(); \ 34 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 35 | \cf0 nextNode.left = lastNode;\ 36 | nextNode.value = element;\ 37 | if (lastNode) \{\ 38 | lastNode.right = nextNode; \ 39 | \}\ 40 | lastNode = nextNode;\ 41 | \});\ 42 | return nextNode;\ 43 | \}\ 44 | \ 45 | function inOrderTraversal(root) \{\ 46 | var node = root;\ 47 | if (node !== null) \{\ 48 | inOrderTraversal(node.left);\ 49 | visit(node);\ 50 | inOrderTraversal(node.right);\ 51 | \}\ 52 | \}\ 53 | \ 54 | function visit(node) \{\ 55 | console.log(node.value);\ 56 | \}\ 57 | } -------------------------------------------------------------------------------- /problems/python/prime_factors.py: -------------------------------------------------------------------------------- 1 | import math 2 | def check_divisibility(a, b): 3 | """checks if b divides a 4 | 5 | Args: 6 | a (int): dividend 7 | b (int): divisor 8 | 9 | Returns: 10 | answer (str): either 'divisible' or 'not divisibile'""" 11 | 12 | float_version = float(a)/b 13 | int_version = a/b 14 | if float_version == int_version: 15 | answer = "divisible" 16 | else: 17 | answer = "not divisible" 18 | return answer 19 | 20 | def find_factors(number): 21 | """ Clumsiy finds prime factors of number 22 | 23 | 24 | """ 25 | 26 | i = 2 27 | prod = 1 28 | factors = [] 29 | sqrt = math.sqrt(number) 30 | num = number 31 | 32 | while i < num: 33 | div = check_divisbility(number, i) 34 | if div == 'divisible': 35 | factors.append(i) 36 | number /= i 37 | prod *= i 38 | recurse = find_factors(number) 39 | 40 | #I recurse here because it prevents us wasting time playing with large numbers 41 | for fac in recurse: 42 | factors.append(fac) 43 | number /= fac 44 | prod *= fac 45 | #stop if we find a factor greater tha sqrt(number) 46 | if i >= sqrt: 47 | break 48 | #make sure we're not looking once we find all the factors 49 | if prod == num: 50 | break 51 | else: 52 | if i> sqrt: 53 | if len(factors)==0: 54 | factors.append(num) 55 | prod *= num 56 | else: 57 | print i 58 | recurse = find_factors(number) 59 | for fac in recurse: 60 | factors.append(fac) 61 | prod *= fac 62 | if prod == num: 63 | break 64 | i = i+1 65 | if prod != num: 66 | raise ValueError ("This isn't right") 67 | return factors 68 | -------------------------------------------------------------------------------- /problems/ruby/binary.rb: -------------------------------------------------------------------------------- 1 | def binary(num) 2 | arr = [] 3 | until num == 0 4 | arr << num % 2 5 | num /= 2 6 | end 7 | arr.push(0).reverse!.join("") 8 | end 9 | #2n + 2 form 10 | 11 | def fac_rec(num) 12 | return nil if num < 1 13 | return num if num == 1 14 | num * fac_rec(num - 1) 15 | end 16 | #O(n) memory time because the stack frame, the execution stack contains multiple calls to fac_rec 17 | #O(n execution time) 18 | 19 | 20 | 21 | def factorial(num) 22 | return nil if num < 1 23 | return num if num == 1 24 | result = 1 25 | (1..num).each do |fac| 26 | result *= fac 27 | end 28 | result 29 | end 30 | 31 | #tail-recursion, is where the stack routine contains only calls to current active functions. 32 | #some ruby implementations like YARV support sub-routine-optimization 33 | #you can turn it on manually by going into Ruby MRI... 34 | 35 | def fac_tail(num, prod = 1) 36 | return prod if num == 1 37 | return fac_tail(num - 1, num * prod) 38 | end 39 | 40 | #this has O(n) implementation but still O(n) memory because the call stack 41 | 42 | #find the maximum pstrings of an array 43 | 44 | def max_psub(str) 45 | hash = {} 46 | #find substrings 47 | (0...str.length).each do |i| 48 | (i...str.length).each do |j| 49 | substr = str[(i..j)] 50 | hash[substr] = substr.length 51 | end 52 | end 53 | max = hash.values.max 54 | max_arr = hash.select{|k,v| v == max}.keys 55 | arr = [] 56 | max_arr.each do |maxsub| 57 | (1...maxsub.length).each do |k| 58 | dup = maxsub.dup 59 | dup.slice!(k) 60 | arr << dup 61 | end 62 | end 63 | #initialize 64 | max_psub = arr[0] 65 | (0...max_psub.length).each do |l| 66 | arr.each do |psub| 67 | if psub[l] > max_psub[l] 68 | max_psub = psub 69 | end 70 | return max_psub if max_psub != arr[0] 71 | end 72 | end 73 | max_psub 74 | end 75 | 76 | ##(O(n)) solutoon 77 | def simple_psub_max(str) 78 | dup = str.dup 79 | dup.slice!(1) 80 | max = dup 81 | (2...str.length - 1).each do |j| 82 | dup = str.dup 83 | dup.slice!(j) 84 | pstring = dup 85 | if pstring[j] > max[j] && pstring.length == max.length 86 | max = pstring 87 | end 88 | end 89 | max 90 | end 91 | 92 | p max_psub("hello") 93 | p simple_psub_max("hello") 94 | 95 | 96 | def max_unique_psub(str) 97 | psub_arr = [str[str.length - 1]] 98 | 99 | (str.length - 2).downto(0) do |i| 100 | next if str[i] < psub_arr.last 101 | # this is amortized O(1) time. 102 | psub_arr << str[i] 103 | end 104 | 105 | psub = psub_arr.reverse.join("") 106 | psub 107 | end 108 | 109 | p max_unique_psub("hello") 110 | -------------------------------------------------------------------------------- /problems/ruby/binary_search.rb: -------------------------------------------------------------------------------- 1 | def binary_search(value, array) 2 | -------------------------------------------------------------------------------- /problems/ruby/braces.rb: -------------------------------------------------------------------------------- 1 | def closed_properly?(string) 2 | openers_to_closers_map = { "(" => ")", "{" => "}", "[" => "]"} 3 | 4 | openers = openers_to_closers_map.keys 5 | closers = openers_to_closers_map.values 6 | 7 | 8 | openers_stack = [] 9 | (0...string.length).each do |index| 10 | char = string[index] 11 | p char 12 | if openers.include?(char) 13 | openers_stack << char 14 | elsif closers.include?(char) 15 | last_unclosed_opener = openers_stack.pop 16 | p last_unclosed_opener 17 | return false if char != openers_to_closers_map[last_unclosed_opener] 18 | end 19 | end 20 | openers_stack.empty? 21 | end 22 | -------------------------------------------------------------------------------- /problems/ruby/bst.rb: -------------------------------------------------------------------------------- 1 | def bst?(T) 2 | root = T.root 3 | nodes = [[root, nil, nil]] 4 | until nodes.empty? 5 | node, lower_bound, upper_bound = nodes.pop 6 | 7 | if (lower_bound && node.value < lower_bound) || (upper_bound && node.value > upper_bound) 8 | return false 9 | end 10 | 11 | if node.left 12 | nodes << [node.left, lower_bound, node.value] 13 | elsif node.right 14 | nodes << [node.right, node.value, upper_bound] 15 | end 16 | end 17 | true 18 | end 19 | -------------------------------------------------------------------------------- /problems/ruby/caesar_cipher.rb: -------------------------------------------------------------------------------- 1 | def caesar_cipher(str, amount) 2 | letters = ("a".."z").to_a 3 | shift = ("a".."z").to_a.shift(amount) 4 | caesar = ("a".."z").to_a.slice(amount, 25) + shift 5 | p caesar 6 | hash = Hash[letters.zip(caesar)] 7 | (0...str.length).each do |j| 8 | char = str[j] 9 | if hash[char] 10 | str[j] = hash[char] 11 | end 12 | end 13 | str 14 | end 15 | 16 | ##O(n)/O(1) 17 | p caesar_cipher("this is a cute little string", 3) 18 | -------------------------------------------------------------------------------- /problems/ruby/circular_array.rb: -------------------------------------------------------------------------------- 1 | #Determine whether a circular array of relative indices has only one cycle 2 | #[0] is a cycle 3 | #[1,-2,1] is a cycle 4 | #[1] is not a cycle 5 | #[3] is not a cycle 6 | #[-1] is a cycle 7 | #[1, -3] is a cycle 8 | #[1,-3,1] is not a cycle 9 | def one_cycle?(arr) 10 | current_position = 0 11 | element = 0 12 | cycle = false 13 | while element < arr.length 14 | break if arr[current_position].nil? 15 | current_position = arr[current_position] + current_position 16 | if current_position == 0 && cycle == false || element == arr.length - 1 && arr[current_position] == arr[0] 17 | cycle = true 18 | elsif current_position == 0 && cycle 19 | return false 20 | end 21 | element += 1 22 | end 23 | cycle 24 | end 25 | 26 | p one_cycle?([1, -2, 1]) 27 | -------------------------------------------------------------------------------- /problems/ruby/common_substr.rb: -------------------------------------------------------------------------------- 1 | #naive approach 2 | 3 | def naive_common_substrings(str1, str2) 4 | str1sub = [] 5 | (0...str1.length).each do |index| 6 | (index + 1...str1.length).each do |j| 7 | str1sub << str1[(index..j)] 8 | end 9 | end 10 | str2sub = [] 11 | (0...str2.length).each do |index| 12 | (index + 1...str2.length).each do |j| 13 | str2sub << str2[(index..j)] 14 | end 15 | end 16 | common = str2sub.select{|sub| str1sub.include?(sub)}.max_by{ |word| word.length } 17 | common 18 | end 19 | #O(n) with O(2) memory, of form 2n^2; 20 | #we must remove the additional strings here too, so a better solution is to skip some of the iterations by asking when we build a string whether that substr is in str2 21 | 22 | 23 | def efficient_common_substrings(str1, str2) 24 | max = "" 25 | (0...str1.length).each do |index| 26 | (index + 1...str1.length).each do |j| 27 | substr = str1[(index..j)] 28 | if (max.length <= substr.length) && str2.include?(substr) 29 | max = substr 30 | end 31 | end 32 | end 33 | max 34 | end 35 | #this is a bit better, but we are explicitly considering all substrings, what if we wrote it a different way? 36 | #let's say that once we have a substring, we only want potential substrings that are at least n+1 greater, and only these do we build 37 | 38 | def best_substrings(str1, str2) 39 | #keep track of the starting index, initialize 40 | longest = "" 41 | start_idx = 0 42 | while start_idx < str1.length 43 | #doesn’t matter what we pick here 44 | #now, we will only consider at least n+1 greater than the longest string length 45 | len = longest.length + 1 46 | #make sure still in scope of str, right? 47 | #this inner while loop is a lot like insertion sort, in that it only checks for substrings if there exists the possibility that there is a bigger one 48 | while (start_idx + len) <= str1.length 49 | end_idx = start_idx + len 50 | substr = str1[(start_idx..end_idx)] 51 | longest = substr if str2.include?(substr) 52 | #let’s check the rest of them that begin at the current index 53 | len += 1 54 | end 55 | #let’s say we left the loop, and we haven’t found anyone, then we need to move to the next idx 56 | start_idx += 1 57 | end 58 | longest 59 | end 60 | 61 | #let’s see if this works 62 | #at the beginning, the longest str is an empty string 63 | #propagation, we build substrings of a given length...going through all substrings of a given start_idx. and replacing them by longest. the loop will find the biggest substring but does it terminate 64 | #termination 65 | #start_idx < str1.length this condition will happen once we have either built the longest substring possible for a given start index 66 | #if we fail, then what will happen is that the start_index will be the last digit (i.e. str1.length - 1 67 | 68 | 69 | 70 | 71 | p efficient_common_substrings("cats", "catsarecool") 72 | p naive_common_substrings("cats", "catsarecool") 73 | p best_substrings("cats", "catsok") 74 | -------------------------------------------------------------------------------- /problems/ruby/counting_sort.rb: -------------------------------------------------------------------------------- 1 | def angry_sort(scores, high_score) 2 | sorted_array = [] 3 | 4 | #the index corresponds to the score, and the value corresponds to the count 5 | scores_count = Array.new(high_score + 1) {0} 6 | scores.each { |score| scores_count[score] += 1 } 7 | 8 | scores_count.each_with_index do |count, score| 9 | next if count == 0 10 | count.times do 11 | sorted_array << score 12 | end 13 | end 14 | 15 | sorted_array 16 | end 17 | 18 | p angry_sort([2,32,32434,4324,234,35,323543,532,52335,12,12,3,4,4,123,42,3232], 323543) 19 | -------------------------------------------------------------------------------- /problems/ruby/delete_node.rb: -------------------------------------------------------------------------------- 1 | ##this is for a singly-linked list, where 2 | ##ruby is a garbage-collected language, which means that we don't have to manually free 3 | ##up the mmeory 4 | ##O(1) memory 5 | 6 | 7 | def delete_node(node) 8 | ##if there is no next node 9 | if node.next 10 | neighbor = node.next 11 | node.value = neighbor.value 12 | node.next = neighbor.next 13 | else 14 | node = nil 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /problems/ruby/digital_root.rb: -------------------------------------------------------------------------------- 1 | def digital_root(num) 2 | current_number = num 3 | sum = 0 4 | while current_number >= 1 5 | sum += current_number % 10 6 | current_number /= 10 7 | end 8 | sum 9 | #O(n) / O(1) where n is number of digits 10 | end 11 | -------------------------------------------------------------------------------- /problems/ruby/digits.rb: -------------------------------------------------------------------------------- 1 | def digits(pos, number) 2 | places = [] 3 | until number == 0 4 | digit = number % 10 5 | places << digit 6 | number /= 10 7 | end 8 | places[pos] 9 | end 10 | 11 | p digits(5, 0123456789) 12 | -------------------------------------------------------------------------------- /problems/ruby/dutch_flag.rb: -------------------------------------------------------------------------------- 1 | def dutch_flag(arr) 2 | output = [] 3 | counts = Hash.new {|k, v| k[v] = 0} 4 | array.each {|val| counts[val] += 1} 5 | numbers = counts.keys.sort! 6 | numbers.each{|num| counts[num].times { output << num } } 7 | output 8 | end 9 | 10 | def sort_balls(balls) 11 | red_idx = balls.length - 1 12 | blue_idx = 0 13 | green_idx = 0 14 | while blue_idx <= red_idx 15 | if balls[blue_idx] == "R" 16 | balls[blue_idx], balls[red_idx] = balls[red_idx], balls[blue_idx] 17 | red_idx -= 1 18 | elsif balls[blue_idx] == "B" 19 | balls[blue_idx], balls[green_idx] = balls[green_idx], balls[blue_idx] 20 | green_idx += 1 21 | blue_idx += 1 22 | elsif balls[blue_idx] == "G" 23 | blue_idx += 1 24 | end 25 | end 26 | balls 27 | end 28 | 29 | p sort_balls(["R", "G", "B", "R", "G", "B", "G", "G"]) 30 | -------------------------------------------------------------------------------- /problems/ruby/egg_drop.rb: -------------------------------------------------------------------------------- 1 | def find_optimal_floors(array) 2 | max = array[-2] 3 | 4 | current_floor = 0 5 | 6 | current_number = 1 7 | tries = 2 8 | max_tries = 100 9 | while true 10 | if current_number * tries > max && tries < max_tries 11 | current_floor = current_number if current_number > current_floor 12 | max_tries = tries 13 | current_number += 1 14 | tries = 1 15 | end 16 | tries += 1 17 | break if current_number == 100 18 | end 19 | current_floor 20 | end 21 | 22 | p find_optimal_floors((1..100).to_a) 23 | -------------------------------------------------------------------------------- /problems/ruby/fibs.rb: -------------------------------------------------------------------------------- 1 | def fibs 2 | end 3 | -------------------------------------------------------------------------------- /problems/ruby/file_dictionary.rb: -------------------------------------------------------------------------------- 1 | def file_dictionary(files) 2 | output = [] 3 | 4 | files.each do |file, item| 5 | if item.is_a?(Hash) 6 | folder = file 7 | nested_files = file_dictionary(item) 8 | nested_files.each { |nested_file| output << "#{folder}/#{nested_file}" } 9 | else 10 | output << file 11 | end 12 | end 13 | output 14 | end 15 | files = { 16 | 'a' => { 17 | 'b' => { 18 | 'c' => { 19 | 'd' => { 20 | 'e' => true 21 | }, 22 | 23 | 'f' => true 24 | } 25 | } 26 | } 27 | } 28 | p file_dictionary(files) 29 | -------------------------------------------------------------------------------- /problems/ruby/file_read.rb: -------------------------------------------------------------------------------- 1 | 2 | ##File.open, File.directory?, File.file?, Dir.new 3 | 4 | 5 | ##search on size, because all files may have different extensions and the contents may be long 6 | def search_files(dir = Dir.home, count = 3) 7 | return [] if count == 0 8 | files_size = {} 9 | p dir 10 | directory = Dir.new(dir) 11 | duplicates = [] 12 | directory.each do |file| 13 | file_not_readable = !File.readable?(File.join(dir, file)) 14 | next if file_not_readable 15 | a = File.open(File.join(dir, file)) 16 | next if a.nil? 17 | if File.directory?(a) && 18 | duplicates += search_files(a, count - 1) 19 | elsif File.file?(a) && File.readable?(a) 20 | next if a.nil? 21 | if files_size[a.size] 22 | file1 = files_size[a.size] 23 | file2 = a 24 | if File.ctime(file1) < File.ctime(file2) 25 | duplicates << [file1.path, file2.path] 26 | else 27 | duplicates << [file2.path, file1.path] 28 | end 29 | else 30 | files_size[a.size] = a 31 | end 32 | end 33 | end 34 | duplicates 35 | end 36 | 37 | p search_files 38 | -------------------------------------------------------------------------------- /problems/ruby/find_double.rb: -------------------------------------------------------------------------------- 1 | ##if an array is of form (1..n) then the series is triangular, the series begins with 1 and increases by 1 each time 2 | ##so, the extra value is the same as the sum of the series - the sum of the triangular series 3 | 4 | # def find_double(arr) 5 | # ##if the array is of form (1..n) then the number must appear twice in succession. 6 | # number = nil 7 | # arr.each do |value| 8 | # return value if value == number 9 | # number = value 10 | # end 11 | # 12 | # end 13 | 14 | def find_double(arr) 15 | 16 | triangular_sum = (arr[-1] ** 2 + arr[-1]) / 2 17 | sum = arr.inject(&:+) 18 | 19 | number = sum - triangular_sum 20 | end 21 | 22 | def find_repeat(array) 23 | floor = 1 24 | ceiling = array.length - 1 25 | while floor < ceiling 26 | midpoint = floor + ((ceiling - floor))/2 27 | lower_range_floor, lower_range_ceiling = floor, midpoint 28 | upper_range_floor, upper_range_ceiling = midpoint + 1, ceiling 29 | 30 | distinct_items = 0 31 | array.each_with_index do |val, idx| 32 | if idx <= lower_range_ceiling && idx >= lower_range_floor 33 | distinct_items += 1 34 | end 35 | end 36 | 37 | possible_integers = lower_range_ceiling - lower_range_floor + 1 38 | if distinct_items > possible_integers 39 | floor, ceiling = lower_range_floor, lower_range_ceiling 40 | else 41 | floor, ceiling = upper_range_floor, upper_range_ceiling 42 | end 43 | end 44 | array[floor] 45 | end 46 | 47 | p find_repeat([1,2,3,4,4,5,6]) 48 | -------------------------------------------------------------------------------- /problems/ruby/flight_lengths.rb: -------------------------------------------------------------------------------- 1 | require 'set' 2 | ##the condition here is movie1 + movie2 = flight_length, aka flight_length - movie(seen) 3 | def find_movies(flight_length, movie_lengths) 4 | checked_movies = Set.new 5 | movie_lengths.each{|movie| movie << checked_movies} 6 | movie_lengths.each do |movie1| 7 | if checked_movies.include?(flight_length - movie1) 8 | return true 9 | end 10 | end 11 | false 12 | end 13 | 14 | 15 | def find_movies_hash(flight_length, movie_lengths) 16 | checked_movies = {} 17 | movie_lengths.each do |movie| 18 | if checked_movies[movie] 19 | return true 20 | else 21 | checked_movies[flight_length - movie] = movie 22 | end 23 | end 24 | false 25 | end 26 | -------------------------------------------------------------------------------- /problems/ruby/has_cycle.rb: -------------------------------------------------------------------------------- 1 | def has_cycle?(node) 2 | current = node 3 | previous = nil 4 | 5 | until current == previous || current.nil? 6 | previous = current 7 | current = current.next 8 | end 9 | !!current 10 | end 11 | 12 | 13 | ##in this case, the graph is directed s.t. a node will return an array 14 | 15 | 16 | require ‘set’ 17 | def has_cycle_directed_graph?(node) 18 | seen = Set.new 19 | children = node.next 20 | 21 | children.each do |child| 22 | return false if children.empty? 23 | return true if seen.include?(child) 24 | seen << child 25 | children += child.next 26 | end 27 | nil 28 | end 29 | -------------------------------------------------------------------------------- /problems/ruby/in_place_shuffle.rb: -------------------------------------------------------------------------------- 1 | ##shuffle an array in place, with equal probability of any element appearing anywhere 2 | 3 | def shuffle_in_place(arr) 4 | clone = arr.dup 5 | last_idx = arr.length - 1 6 | arr.each_with_index do |value, index| 7 | left_random = rand(0..(index)) 8 | right_random = rand((index + 1)..last_idx) 9 | 10 | if left_random.nil? 11 | random = right_random 12 | elsif right_random.nil? 13 | random = left_random 14 | else 15 | random = ( rand(1) == 1 ? left_random : right_random ) 16 | end 17 | 18 | arr[index], arr[random] = arr[random], arr[index] 19 | end 20 | arr.sort == clone.sort 21 | end 22 | 23 | 24 | 25 | def keck_shuffle(arr) 26 | 27 | arr.each_with_index do |value, index| 28 | rand_idx = rand(index..(arr.length - 1)) 29 | 30 | arr[index], arr[rand_idx] = arr[rand_idx], arr[index] 31 | end 32 | arr 33 | end 34 | 35 | #no bias towards any side, but we only deal with integers that haven’t been placed yet 36 | #n - index possibilities of placement each time! 37 | 38 | 39 | p shuffle_in_place([634,45,32,4,3,2,34,4,4,5,6]) 40 | p keck_shuffle([5,3,2,4,1]) 41 | -------------------------------------------------------------------------------- /problems/ruby/k_consensus.rb: -------------------------------------------------------------------------------- 1 | ##[[1,2,2,3,45], [1,2,3,45], [1,2,6,7]] 2 | require 'set' 3 | 4 | def k_consensus(outer_array, k3) 5 | raise "consensus not possible" if k3 > outer_array.size 6 | #brute force, nested hash 7 | hash = Hash.new { |k, v| k[v] = Set.new} 8 | output = [] 9 | outer_array.each_with_index do |array, index| 10 | array.each do |val| 11 | hash[index] << val 12 | end 13 | end 14 | 15 | unique_counts = Hash.new { |k, v| k[v] = 0} 16 | hash.each do |array_index, set| 17 | set.each do |number| 18 | unique_counts[number] += 1 19 | end 20 | hash[array_index] = nil 21 | end 22 | output = [] 23 | 24 | unique_counts.each do |k, v| 25 | output << k if v >= k3 26 | end 27 | output 28 | end 29 | 30 | p k_consensus([[1,2,2,3,45], [1,2,3,45], [1,2,6,7]], 2) 31 | -------------------------------------------------------------------------------- /problems/ruby/kth_largest.rb: -------------------------------------------------------------------------------- 1 | def largest(node) 2 | until node.right.nil? 3 | node = node.right 4 | end 5 | node 6 | end 7 | 8 | def second_largest(node) 9 | current = node 10 | ##don’t go in if we don’t have a current 11 | while current 12 | ##are we about to hit a max 13 | if current.right.nil? && current.left 14 | return largest(current.left) 15 | end 16 | 17 | ##is there a rightmost element, are we ABOUT to hit a max, is it a max with no left 18 | if current.right && current.right.right.nil? && current.right.left.nil? 19 | return current 20 | end 21 | 22 | current = current.right 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /problems/ruby/largest_stack.rb: -------------------------------------------------------------------------------- 1 | class Stack 2 | def initialize 3 | self.store = [] 4 | end 5 | 6 | def push(item) 7 | self.store << item 8 | end 9 | 10 | def pop 11 | return "no elemenets" if self.store.empty? 12 | self.store.pop unless self.store.empty? 13 | end 14 | 15 | def peek 16 | return "no elements" if self.store.empty? 17 | end 18 | 19 | protected 20 | attr_accessor :store 21 | 22 | end 23 | 24 | 25 | class MaxStack < Stack 26 | 27 | def initialize 28 | self.max_stack = [] 29 | self.max = nil 30 | super 31 | end 32 | 33 | def get_max 34 | self.max = (self.max ? self.max : self.max_stack.last) 35 | end 36 | 37 | def push(item) 38 | if self.max.nil? || item > self.max 39 | self.max_stack << item 40 | self.max = item 41 | end 42 | super 43 | end 44 | 45 | def pop 46 | self.max_stack.pop 47 | self.max = nil 48 | super 49 | end 50 | 51 | 52 | protected 53 | attr_accessor :max, :max_stack 54 | end 55 | -------------------------------------------------------------------------------- /problems/ruby/link_reverse.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | ##if we have a head of a linked list then we can reverse by walking through the list and 4 | ##unless the node we are looking at has no next neighbor, then we set each prior value to ##each other 5 | 6 | def reverse_list(head) 7 | ##start with the head 8 | raise “no items in list” if head.nil? 9 | node = head 10 | 11 | raise “only items” if node.next.nil? 12 | 13 | child = node.next 14 | 15 | until child.next.nil? 16 | #set the child’s element to be the prior element 17 | child.next = node 18 | #walk down 19 | node = node.next 20 | child = node.next 21 | end 22 | 23 | child.next = node 24 | head.next = nil 25 | end 26 | 27 | end 28 | 29 | 30 | - 31 | #head -> 1 32 | #head <- 1 33 | #X-->O-->O 34 | 35 | #X<--O<--O 36 | -------------------------------------------------------------------------------- /problems/ruby/make_change.rb: -------------------------------------------------------------------------------- 1 | def make_best_change(target, coins = [25, 10, 5, 1]) 2 | return [] if target == 0 3 | # Can't make change if all the coins are too big. This is in case 4 | # the coins are so weird that there isn't a 1 cent piece. 5 | return nil if coins.none? { |coin| coin <= target } 6 | 7 | # Optimization: make sure coins are always sorted descending in 8 | # size. We'll see why later. 9 | coins = coins.sort.reverse 10 | 11 | best_change = nil 12 | 13 | ##begin the recursion 14 | coins.each_with_index do |coin, index| 15 | # can't use this coin, it's too big 16 | next if coin > target 17 | 18 | # use this coin 19 | remainder = target - coin 20 | 21 | # Find the best way to make change with the remainder (recursive 22 | # call). Why `coins.drop(index)`? This is an optimization. Because 23 | # we want to avoid double counting; imagine two ways to make 24 | # change for 6 cents: 25 | # (1) first use a nickle, then a penny 26 | # (2) first use a penny, then a nickle 27 | # To avoid double counting, we should require that we use *larger 28 | # coins first*. This is what `coins.drop(index)` enforces; if we 29 | # use a smaller coin, we can never go back to using larger coins 30 | # later. 31 | 32 | ##this makes it so that we attempt to make change with the best amount of times. 33 | best_remainder = make_change(remainder, coins.drop(index)) 34 | 35 | # We may not be able to make the remaining amount of change (e.g., 36 | # if coins doesn't have a 1cent piece), in which case we shouldn't 37 | # use this coin. 38 | next if best_remainder.nil? 39 | 40 | # Otherwise, the best way to make the change **using this coin**, 41 | # is the best way to make the remainder, plus this one coin. 42 | this_change = [coin] + best_remainder 43 | 44 | # Is this better than anything we've seen so far? 45 | if (best_change.nil? || (this_change.count < best_change.count)) 46 | best_change = this_change 47 | end 48 | end 49 | 50 | best_change 51 | end 52 | 53 | 54 | def make_best_change(amount, coins = [25,10,5,1]) 55 | return [] if amount == 0 56 | ##can't make change with this coin. this could happen if we don't have a 1 or something 57 | return nil if coins.none? { |coin| coin <= target} 58 | coins.sort!.reverse! 59 | best_change = nil 60 | 61 | coins.each_with_index do |coin, index| 62 | #attempt to make change with a coin 63 | next if coin > target 64 | remainder = target - coin 65 | best_combination = make_change(remainder, coins.drop(index)) 66 | next if best_combination.nil? 67 | this_change = [coin] + best_remainder 68 | if (best_change.nil? || (this_change.count < best_change.count)) 69 | best_change = this_change 70 | end 71 | end 72 | 73 | best_change 74 | end 75 | 76 | 77 | 78 | def make_efficient_change(amount, coins) 79 | return [] if amount == 0 80 | return nil if coins.none? {|coin| coin <= amount} 81 | #this will represent our potential combinations of change combinations 82 | coins.sort!.reverse! ##we want the coins in descending order, because we want to make change with the next biggest coin at a time. 83 | best_change = nil 84 | coins.each_with_index do |coin, index| 85 | next if coin > amount 86 | remainder = amount - coin 87 | best_remainder = make_efficient_change(remainder, coins.drop(index)) 88 | next if best_remainder.nil? 89 | this_combination = [coin] + best_remainder 90 | -------------------------------------------------------------------------------- /problems/ruby/make_cloud.rb: -------------------------------------------------------------------------------- 1 | ##given a string can we make a hash map with word counts, deal with punctuation and capitalization 2 | ##capitalization will be uncapitalized 3 | ##puncutation will be ignored 4 | ##here we make a hash map --> keys to values, really hash lookup/insertion is not O(1) --> collisions can make it O(n) 5 | 6 | 7 | ##scan(regexp) --> array, all match 8 | ##split(regexp) --> array, of characters separated by words 9 | ##gsub(regexp, sub) --> returns a new copy of the string 10 | 11 | def make_cloud(string) 12 | arr = string.scan(/[a-zA-Z]+-[a-zA-Z]+|[a-zA-Z]+/).map{|word| word.downcase} 13 | cloud = Hash.new {|k,v| k[v] = 0} 14 | 15 | until arr.empty? 16 | word = arr.pop 17 | cloud[word] += 1 18 | end 19 | 20 | cloud 21 | end 22 | 23 | p make_cloud("after after. AFTER") 24 | -------------------------------------------------------------------------------- /problems/ruby/matrix_search.rb: -------------------------------------------------------------------------------- 1 | def find_matrix?(matrix, string) 2 | first_letter = string[0] 3 | queue = [] 4 | n = matrix.size 5 | m = matrix.size 6 | matrix.each_with_index do |arr, idx| 7 | arr.each_with_index do |val, idx2| 8 | if val == first_letter 9 | queue << [idx,idx2,1, [[idx, idx2]], val] 10 | end 11 | end 12 | end 13 | dirx = [0,0, 1, -1] 14 | diry = [-1, 1, 0 , 0] 15 | 16 | until queue.empty? 17 | x,y,string_position, visited, value = queue.shift 18 | if string_position == string.size 19 | return true 20 | end 21 | 22 | (0..3).each do |i| 23 | di = dirx[i] + x 24 | dj = diry[i] + y 25 | if (di >= 0 && di < n && dj <= m && dj >= 0 && 26 | string[string_position] == matrix[di][dj] && !visited.include?([di, dj])) 27 | queue << [ di, dj, string_position + 1, visited + [[di, dj]], string[string_position] ] 28 | p queue 29 | 30 | end 31 | end 32 | 33 | end 34 | return false 35 | end 36 | 37 | 38 | p find_matrix?([["A", "B", "C", "E"], ["S", "F", "C", "S"], ["A", "D", "E", "E"]], "BCCE") 39 | -------------------------------------------------------------------------------- /problems/ruby/max_pair.rb: -------------------------------------------------------------------------------- 1 | ##hash-look-up-in Ruby is O(1) 2 | ## the average cost of look-up in a hash is independent of n, so it's a constant 3 | ##in ruby and js, a hash table is very important because it's the data structure. object methods are stored as a key with a pointer to the object or memory. 4 | ##the hash rules 5 | 6 | ##a set is another way of saying, have we seen an item before 7 | require 'set' 8 | 9 | def duplicates(array) #O(n) 10 | hash = {} 11 | array.each do |element| 12 | if hash[element] 13 | hash[element] += 1 14 | else 15 | hash[element] = 1 16 | end 17 | end 18 | hash.select{|k,v| v > 1}.keys 19 | end 20 | 21 | def two_sum(numbers) 22 | set = Set.new 23 | numbers.each { |el| set.add(el)} 24 | numbers.any { |el| set.include?(el)} 25 | end 26 | p duplicates([3,3,1,5,5]) 27 | 28 | 29 | def duplicates_set(array) #O(n) 30 | unseen = Set.new 31 | seen = Set.new 32 | array.each do |el| 33 | if unseen.include?(el) 34 | seen << el 35 | else 36 | unseen << el 37 | end 38 | end 39 | seen 40 | end 41 | 42 | #any two numbers sum to zero, if el + -el exist in the area 43 | 44 | def two_sum(arr) #o(ns) 45 | set = Set.new 46 | arr.each{|el| set << el} 47 | arr.select{|el| set.include?(-1 * el)} 48 | end 49 | 50 | def two_sum_k(arr, k) #O(n) 51 | set = Set.new 52 | arr.each{|el| set << el} 53 | potential_sums = arr.select{|el| set.include?(k - el)} 54 | #.map{|el| [el, k - el]} 55 | #now make sure they are unique pairs 56 | potential_sums.sort! 57 | seen = Set.new 58 | output = [] 59 | potential_sums.each do |el| 60 | if seen.include?(el) || seen.include?(k - el) 61 | else 62 | output << [el, k - el] 63 | seen << el 64 | seen << k - el 65 | end 66 | end 67 | output 68 | end 69 | 70 | p duplicates_set([3,3,1,5,5]) 71 | 72 | p two_sum([-1,0,1,3]) 73 | 74 | p two_sum_k([3,4,9,0,6, 15, -6],9) 75 | -------------------------------------------------------------------------------- /problems/ruby/max_profit.rb: -------------------------------------------------------------------------------- 1 | def max_profit(stock) 2 | max = 0 3 | buy = stock[0] 4 | stock.each do |curr_price| 5 | max = curr_price - buy if curr_price - buy > max 6 | buy = curr_price if buy > curr_price 7 | end 8 | max 9 | end 10 | -------------------------------------------------------------------------------- /problems/ruby/maximum_windows.rb: -------------------------------------------------------------------------------- 1 | def maximum_windows(arr, k) 2 | left_over_max = nil 3 | best_max = nil 4 | number_frame = 1 5 | output = [] 6 | arr.each_with_index do |val, index| 7 | if number_frame < (k) 8 | number_frame += 1 9 | if best_max.nil? 10 | best_max = val 11 | next 12 | elsif left_over_max.nil? 13 | left_over_max = val 14 | next 15 | elsif val >= best_max 16 | left_over_max = best_max 17 | best_max = val 18 | end 19 | elsif number_frame == k 20 | if val > best_max 21 | output << val 22 | left_over_max = best_max 23 | best_max = val 24 | else 25 | output << left_over_max 26 | left_over_max = nil 27 | end 28 | end 29 | end 30 | output 31 | end 32 | 33 | 34 | p maximum_windows([1,2,3,4,5,6],4) 35 | -------------------------------------------------------------------------------- /problems/ruby/move_zeros.rb: -------------------------------------------------------------------------------- 1 | def move_zeros(array) 2 | ##keep track of everything we flip 3 | zero_place = 0 4 | array_length = array.length 5 | ##"lazy" approach, keep track of the last zero_place switch. 6 | array.each_with_index do |value, index| 7 | ##counter will lag behind the minute there is a zero, and will allow values to bubble up 8 | if value != 0 9 | array[zero_place] = array[index] 10 | ##there is no zero here, but there may be at the next 11 | zero_places += 1 12 | end 13 | end 14 | #well we know where to zeros are 15 | (zero_places...array.length).each do |zero_index| 16 | array[zero_index] = 0 17 | end 18 | array 19 | end 20 | -------------------------------------------------------------------------------- /problems/ruby/multiple_merge.rb: -------------------------------------------------------------------------------- 1 | require 'algorithms' 2 | include Containers 3 | 4 | def merge(array1, array2) 5 | output = [] 6 | until array1.empty? || array2.empty? 7 | if array1[0] < array2[0] 8 | output << array1.shift 9 | else 10 | output << array2.shift 11 | end 12 | end 13 | output + array1 + array2 14 | end 15 | 16 | 17 | def k_merge(input_array) 18 | until input_array.size == 1 #runtime log k 19 | first_array = input_array.shift 20 | second_array = input_array.shift 21 | input_array << merge(first_array, second_array) #runtime log k * n 22 | end 23 | input_array.flatten 24 | end 25 | 26 | 27 | def k_merge_heap(input_array) 28 | lam = lambda { |x, y| (y <=> x) == 1 } 29 | pqueue = PriorityQueue.new(&lam) 30 | output = [] 31 | input_array.each_with_index do |array, index| 32 | val = array.shift 33 | pqueue.push([index, val], val) 34 | end 35 | 36 | until pqueue.empty? 37 | array_index, val = pqueue.pop 38 | output << val 39 | if input_array[array_index].length > 0 40 | new_val = input_array[array_index].shift 41 | pqueue.push([array_index, new_val], new_val) 42 | else 43 | input_array.each_with_index do |array, index| 44 | if array.length > 0 45 | val = array.shift 46 | pqueue.push([index, val], val) 47 | end 48 | end 49 | end 50 | 51 | end 52 | output 53 | end 54 | 55 | p k_merge_heap([[1,2,3,4,5,6],[7,8,9,9,10,12,13],[19,20]]) 56 | -------------------------------------------------------------------------------- /problems/ruby/n_choose_k.rb: -------------------------------------------------------------------------------- 1 | #SORT IS ASCENDING IN RUBY 2 | 3 | def max_value(cakes, capacity) 4 | cakes = cakes.sort_by{|el1| el1[1]/el1[0]}.reverse 5 | profit = 0 6 | current_capacity = capacity 7 | cakes.each do |cake| 8 | cake_weight = cake[0] 9 | cake_price = cake[1] 10 | max_cake = cake if cake_weight <= current_capacity 11 | if max_cake 12 | until cake_weight > current_capacity 13 | profit += cake_price 14 | current_capacity -= cake_weight 15 | end 16 | end 17 | end 18 | profit 19 | end 20 | 21 | 22 | ##assuming no sort 23 | 24 | def max_value_no_sort(cakes, capacity) 25 | profit = 0 26 | current_capacity = capacity 27 | 28 | last_cake = nil 29 | last_capacity = nil 30 | last_ratio = nil 31 | cakes.each do |cake| 32 | next if cake[0] == 0 || cake[1] == 0 33 | cake_weight = cake[0] 34 | cake_price = cake[1] 35 | cake_ratio = cake_price/cake_weight 36 | if (last_cake.nil? && current_capacity >= cake_weight) 37 | last_cake = cake 38 | last_capacity = current_capacity 39 | last_ratio = cake_ratio 40 | until current_capacity < cake_weight 41 | current_capacity -= cake_weight 42 | profit += cake_price 43 | end 44 | elsif (last_ratio && cake_ratio > last_ratio && capacity >= cake_weight) 45 | until current_capacity == last_capacity 46 | current_capacity += last_cake[0] 47 | profit -= last_cake[1] 48 | end 49 | last_cake = cake 50 | last_capacity = current_capacity 51 | last_ratio = cake_ratio 52 | until current_capacity < cake_weight 53 | current_capacity -= cake_weight 54 | profit += cake_price 55 | end 56 | end 57 | end 58 | last_cake = cakes[-1] 59 | last_cake_weight = last_cake[0] 60 | last_cake_price = last_cake[1] 61 | if (capacity - current_capacity) > last_cake_weight && last_cake_weight != 0 && last_cake_price != 0 62 | until current_capacity < last_cake_weight 63 | current_capacity -= last_cake_weight 64 | profit += last_cake_price 65 | end 66 | end 67 | profit 68 | end 69 | 70 | 71 | p max_value_no_sort([[7,60], [3, 90], [2, 15]], 20) 72 | -------------------------------------------------------------------------------- /problems/ruby/nested_parentheticals.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesusmaldonado/algorithms/73f0b633b55ae0e307bbba4932ad9a777e59e730/problems/ruby/nested_parentheticals.rb -------------------------------------------------------------------------------- /problems/ruby/next_largest_bst.rb: -------------------------------------------------------------------------------- 1 | def next_largest(node) 2 | right_node = node.right 3 | parent_node = node.parent 4 | return right_node if right_node && right_node.left.nil? 5 | while right_node.left 6 | right_node = right_node.left 7 | end 8 | 9 | return right_node if right_node 10 | until parent_node.parent.nil? || parent_node.val > node.val 11 | parent_node = parent_node.parent 12 | end 13 | 14 | return parent_node 15 | 16 | end 17 | -------------------------------------------------------------------------------- /problems/ruby/parenthesis_matcher.rb: -------------------------------------------------------------------------------- 1 | def find_matching_parenthesis(string, parenthesis_idx) 2 | opener = "(" 3 | closer = ")" 4 | open_parens = 1 5 | ((parenthesis_idx + 1)...string.length).each do |idx| 6 | character = string[idx] 7 | if character == opener 8 | open_parens += 1 9 | elsif character == closer 10 | open_parens -= 1 11 | end 12 | return idx if open_parens == 0 13 | end 14 | "this parenthesis is not closed" 15 | end 16 | 17 | 18 | 19 | 20 | 21 | p find_matching_parenthesis("(()(())()())", 1) 22 | -------------------------------------------------------------------------------- /problems/ruby/partition_palindrome.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jesusmaldonado/algorithms/73f0b633b55ae0e307bbba4932ad9a777e59e730/problems/ruby/partition_palindrome.rb -------------------------------------------------------------------------------- /problems/ruby/points.rb: -------------------------------------------------------------------------------- 1 | #[[2,2], [3,3], [0,0]] 2 | 3 | def find_points(arr, n) 4 | raise "n is greater than array size #{arr.size}" if n > arr.size 5 | 6 | distances = [] 7 | arr.each_with_index do |point, index| 8 | xcoord = point[0] 9 | ycoord = point[0] 10 | distance = Math.sqrt(xcoord**2 + ycoord**2) 11 | distances << [distance, index] 12 | end 13 | #wow did you know sort by wont take floats 14 | distances.sort_by!{ |x1, x2| x1 <=> x2 } 15 | distances[(0...n)].map{|point| point = arr[point[1]] } 16 | end 17 | 18 | #distances = [[sqrt(8), 0], [sqrt(18), 1], [0, 2]] 19 | #O(N log N) / O(N) 20 | 21 | def find_points_optimized(arr, n) 22 | raise "n is greater than array size #{arr.size}" if n > arr.size 23 | arr.sort_by!{ |point1, point2| Math.sqrt(point1[0]**2 + point1[1]**2) <=> Math.sqrt(point2[0]**2 + point2[1]**2) } 24 | arr[(0...n)] 25 | end 26 | 27 | #O(N log N) / O(n) 28 | 29 | ##O(N * n ) << O(N log N) for large N 30 | 31 | require 'set' 32 | def find_points_time(arr, n) 33 | raise "n is greater than array size #{arr.size}" if n > arr.size 34 | output = Set.new 35 | n.times do 36 | min = nil 37 | min_dist = nil 38 | arr.each do |val| 39 | dist= Math.sqrt(val[0] ** 2 + val[1] ** 2) 40 | if min.nil? && !output.include?(val) 41 | min = val 42 | min_dist = dist 43 | elsif min && dist < min_dist && !output.include?(val) 44 | min_dist = dist 45 | min = val 46 | end 47 | end 48 | output << min 49 | end 50 | output 51 | end 52 | 53 | #O (n * N) / O(n) 54 | 55 | p find_points_time([[1,2], [3,4]], 2) 56 | -------------------------------------------------------------------------------- /problems/ruby/powers_of_two.rb: -------------------------------------------------------------------------------- 1 | def powers_of_two 2 | while true 3 | n = rand(10..16) 4 | puts "Hey what's two to the #{n}" 5 | output = gets.chomp.to_i 6 | if 2 ** n == output 7 | puts "that's right" 8 | else 9 | puts "nope it's #{2 ** n}" 10 | end 11 | end 12 | end 13 | 14 | powers_of_two 15 | -------------------------------------------------------------------------------- /problems/ruby/print_tree.rb: -------------------------------------------------------------------------------- 1 | def print_tree(tree) 2 | queue = [tree.root] 3 | current_level, next_level = 1 , 0 4 | until queue.empty? 5 | node = queue.shift 6 | current_level -= 1 7 | puts “#{node.value}” 8 | if node.left 9 | queue << node.left 10 | next_level += 1 11 | end 12 | if node.right 13 | queue << node.right 14 | next_level += 1 15 | end 16 | if current_level == 0 17 | puts “\n” 18 | current_level, next_level = next_level, current_level 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /problems/ruby/products.rb: -------------------------------------------------------------------------------- 1 | 2 | def products(int_array) 3 | arr = [] 4 | (0...int_array.length).each do |index| 5 | clone = int_array.dup 6 | clone.delete_at(index) 7 | arr << clone.inject(&:*) 8 | end 9 | arr 10 | end 11 | 12 | 13 | def prods(arr) 14 | output = [] 15 | left = 1 16 | arr.each do |val| 17 | output << left 18 | left *= val 19 | end 20 | p output 21 | 22 | right = 1 23 | (arr.length - 1).downto(0) do |j| 24 | p j 25 | arr[j] 26 | output[j] *= right 27 | right *= arr[j] 28 | end 29 | 30 | p output 31 | end 32 | -------------------------------------------------------------------------------- /problems/ruby/queue_two_stacks.rb: -------------------------------------------------------------------------------- 1 | class Queue 2 | 3 | def initialize(value) 4 | in = Stack.new 5 | out = Stack.new 6 | enqueue(value) if value 7 | end 8 | 9 | def emqueue(value) 10 | in.push(queue) 11 | end 12 | 13 | def dequeue 14 | if (out.empty?) 15 | out.push(in.pop) until in.empty? 16 | out.pop 17 | else 18 | out.pop 19 | end 20 | end 21 | 22 | end 23 | -------------------------------------------------------------------------------- /problems/ruby/rectangle-intersection.rb: -------------------------------------------------------------------------------- 1 | def intersects(rect1, rect2) 2 | 3 | rect1rangex = (rect1[:x]..(rect1[:x] + rect1[:width])) 4 | rect2rangex = (rect2[:x]..(rect2[:x] + rect2[:width])) 5 | rect1rangey = (rect1[:y]..(rect1[:y] + rect1[:height])) 6 | rect2rangey = (rect2[:y]..(rect2[:y] + rect2[:height])) 7 | 8 | ##shapes must intersect in x and y 9 | xoverlap = rect1rangex.select{|i| rect2rangex.include?(i)} 10 | yoverlap = rect1rangey.select{|i| rect2rangey.include?(i)} 11 | 12 | ##both must overlap for intersection to occur 13 | return [] if xoverlap.empty? || yoverlap.empty? 14 | 15 | intersectx = xoverlap.min 16 | intersecty = yoverlap.min 17 | widthx = xoverlap[-1] - xoverlap[0] 18 | widthy = yoverlap[-1] - yoverlap[0] 19 | 20 | return [intersectx, intersecty] if widthx == 0 && widthy == 0 21 | 22 | rect = [[intersectx, intersecty], 23 | [intersectx + widthx, intersecty], 24 | [intersectx, intersecty + widthy], 25 | [intersectx + widthx, intersecty + widthy]] 26 | rect 27 | end 28 | 29 | r1 = {x: 1, y:1, width: 5, height: 5} 30 | r2 = {x: 0, y: 0, width: 7, height: 7} 31 | 32 | p intersects(r1, r2) 33 | 34 | ## x: (1..6), (0..7) => xoverlap = [(1..6)] 35 | ## y: (1..6), (0..7) => yoverlap = [(1..6)] 36 | 37 | 38 | ##partial overlap, this works 39 | ##touch at a point? we would want to return just the point, this currently returns 4 copies 40 | ##dont touch, we shortcircuit 41 | ##rect1range > rect2range? this works. rect1range == rect2range? this works. rect1range < ##rect2range? works. as long as the values are actually included 42 | ##should refactor to be O(1) time, ie only ask about max and minimum 43 | -------------------------------------------------------------------------------- /problems/ruby/recursion.rb: -------------------------------------------------------------------------------- 1 | def fib(n) 2 | return 0 if n == 0 3 | return 1 if n == 1 4 | fib(n - 1) + fib(n - 2) 5 | end 6 | 7 | 8 | ##can memoize to remove exponential 2^n calls 9 | 10 | def fibi(n) 11 | if n < 0 12 | raise "error, no negative series" 13 | end 14 | if n == 0 || n == 1 15 | return n 16 | end 17 | 18 | prev_prev = 0 19 | prev = 1 20 | current = 0 21 | 22 | (n - 1).times do 23 | current = prev + prev_prev 24 | prev_prev = prev 25 | prev = current 26 | end 27 | current 28 | end 29 | 30 | p fibi(4) 31 | -------------------------------------------------------------------------------- /problems/ruby/recursive_permutations.rb: -------------------------------------------------------------------------------- 1 | ##a permutation of a string is an ordering of a string's characters. 2 | ##the subproblem here is to break apart the string into the base case 3 | ##generate permutations of smaller strings 4 | # cat -> at -> a [string] → ta at -> cta tac atc cat tca act 5 | def rec_permutations(string) 6 | return [string] if string.length <= 1 7 | 8 | permutations = rec_permutations(string[1..(string.length - 1)]) 9 | 10 | container = [] 11 | permutations.each do |perm| 12 | (0..perm.length).each do |j| 13 | permutation=perm[0...j] + string[0] + perm[j..-1] 14 | container << permutation 15 | end 16 | end 17 | 18 | return container 19 | end 20 | 21 | p rec_permutations("heys") 22 | -------------------------------------------------------------------------------- /problems/ruby/replace_array.rb: -------------------------------------------------------------------------------- 1 | ###[1,2,3,4,5], 4 2 | def replace_array!(arr, k) 3 | current_k = 0 4 | arr.each_with_index do |val, index| 5 | if val != k 6 | arr[current_k] = arr[index] 7 | current_k += 1 8 | end 9 | end 10 | arr.slice!(current_k, arr.length - 1) 11 | arr.length 12 | end 13 | 14 | p replace_array!([1,2,3,4,5,6,6,6,6,6],4) 15 | -------------------------------------------------------------------------------- /problems/ruby/reverse_strings.rb: -------------------------------------------------------------------------------- 1 | 2 | def reverse_in_place(string) 3 | 4 | reverse_word!(string, 0, string.length - 1) 5 | 6 | word_idx = 0 7 | (0..string.length).each do |i| 8 | char = string[i] 9 | if char == " " || (i == string.length) 10 | reverse_word!(string, word_idx, i - 1) 11 | word_idx = i + 1 12 | end 13 | end 14 | 15 | string 16 | end 17 | 18 | def reverse_word!(string, start_idx, end_idx) 19 | while start_idx < end_idx 20 | string[start_idx], string[end_idx] = string[end_idx], string[start_idx] 21 | start_idx += 1 22 | end_idx -= 1 23 | end 24 | end 25 | 26 | p reverse_in_place("cat hats") 27 | # “cat hats” 28 | # “stah tac” 29 | # “hats tac” 30 | # “hats cat” 31 | # “ hi cats” 32 | # “stac ih “ 33 | # “cats ih “ 34 | # “cats hi “ 35 | -------------------------------------------------------------------------------- /problems/ruby/riffle.rb: -------------------------------------------------------------------------------- 1 | 2 | def is_riffle?(shuffled_deck, half1, half2) 3 | 4 | half2_idx = 0 5 | half1_idx = 0 6 | half1_allowed = !half1.empty? 7 | half2_allowed = !half2.empty? 8 | shuffled_deck.each do |card| 9 | return false unless (half2[half2_idx] == card) || (half1[half1_idx] == card) 10 | p card 11 | if half2[half2_idx] == card 12 | return false if half2_allowed == false #no funny stuff 13 | if half2[half2_idx + 1] 14 | half2_idx += 1 15 | else 16 | half2_allowed = false 17 | end 18 | elsif half1[half1_idx] == card 19 | return false if half1_allowed == false 20 | if half1[half1_idx + 1] 21 | half1_idx += 1 22 | else 23 | half1_allowed = false 24 | end 25 | end 26 | end 27 | true 28 | end 29 | 30 | p is_riffle?([6,8,9,1,7,2,3,4,5], [6,8,7,9], [1,2,3,4,5]) 31 | -------------------------------------------------------------------------------- /problems/ruby/rotation.rb: -------------------------------------------------------------------------------- 1 | def find_rotation_point(array) 2 | first_number = array[0] 3 | half = array.length / 2 4 | loop do 5 | prior_value = array[half - 1] 6 | left_value = array[half] 7 | right_value = array[half + 1] 8 | return right_value if left_value > right_value 9 | return left_value if prior_value > left_value 10 | return “no rotation point” if left_value == first_number 11 | 12 | if left_value < first_number 13 | half = half / 2 14 | else 15 | half = (half + array.length)/2 16 | end 17 | 18 | end 19 | end 20 | 21 | p find_rotation_point([7,8,9,1,2,3]) 22 | -------------------------------------------------------------------------------- /problems/ruby/string_permutation.rb: -------------------------------------------------------------------------------- 1 | #conditions for permutations,orderings of letters is that of any ordering there must be an even #or only odd number of letters 2 | #so civic is odd parity but only once, anna is even and has only even parity 3 | #so when we are computing the permutations of a string we should construct a parity map 4 | 5 | def permutations_palindrome?(string) 6 | parity_map = {} 7 | 8 | (0...string.length).each do |index| 9 | char = string[index] 10 | ##two scenarios, doesn’t exist, does exist -> ternary 11 | parity_map[char] = ( parity_map[char].nil? ? true : !parity_map[char] ) 12 | end 13 | 14 | #now we need to examine the parity. we neeed a flag inc ase any of these guys are 15 | #odd but still palindromes 16 | 17 | flag = false 18 | 19 | parity_map.values.each do |boolean_value| 20 | if boolean_value && flag 21 | return false 22 | elsif boolean_value 23 | #i.e. i’ll grant you a middle 24 | flag = true 25 | end 26 | end 27 | 28 | true 29 | end 30 | 31 | 32 | ##permutations essentially imply an O(n) runtime because the arrangement produces the permutation 33 | ##why store booleans? to avoid overflow. this can go through an arbitrarily large string 34 | ##the memory cost is extremeley efficient, ther eis a constant memory for all strings because there is only a set of certain letters 35 | -------------------------------------------------------------------------------- /problems/ruby/substringsv2.rb: -------------------------------------------------------------------------------- 1 | def substrings(str1,str2) 2 | max_initial_index = nil 3 | max_final_index = nil 4 | 5 | current_initial_index = nil 6 | current_final_index = nil 7 | (0...str1.length).each do |j| 8 | if str1[j] == str2[j] 9 | if current_initial_index.nil? 10 | current_initial_index = j 11 | current_final_index = j 12 | else 13 | current_final_index = j 14 | end 15 | else 16 | if current_initial_index 17 | if max_initial_index.nil? || (max_final_index - max_initial_index) > (current_final_index - current_initial_index) 18 | max_initial_index = current_initial_index 19 | max_final_index = current_final_index 20 | else 21 | current_initial_index = nil 22 | current_final_index = nil 23 | end 24 | end 25 | end 26 | end 27 | ##O(n) / O(1) 28 | str1[(max_initial_index..max_final_index)] 29 | end 30 | p substrings("thereisnone", "ther") 31 | -------------------------------------------------------------------------------- /problems/ruby/sum_rec.rb: -------------------------------------------------------------------------------- 1 | def sum_rec(arr) 2 | return arr[0] if arr.size == 1 3 | value = arr.shift 4 | value + sum_rec(arr) 5 | end 6 | 7 | 8 | #the disadvantage with this si that the array is destructively sorted 9 | 10 | def sum_rec(arr) 11 | return arr[0] if arr.size == 1 12 | arr[0] + sum_rec(arr.drop(1)) 13 | end 14 | 15 | #O(n) time, with O(n) memory 16 | #initiation 17 | #add the first element to a recursive call 18 | #propagation 19 | #keep recursing until the element is size 1 20 | #termination 21 | #once we hit 0, the recursive calls return values to subsequent calls, which are added together 22 | 23 | 24 | #this is a little beter because the array is not destructively recursed through 25 | 26 | 27 | def sum_recv2(nums) 28 | return nums[0] if nums.size == 1 29 | [nums[0] + sum_rec(nums.drop(1))] 30 | end 31 | -------------------------------------------------------------------------------- /problems/ruby/super_balanced.rb: -------------------------------------------------------------------------------- 1 | #bfs finds shortest path but not necessarily the quickest time to go to a target 2 | #dfs finds the target most quickly 3 | ##this is a dfs 4 | 5 | def balanced(root) 6 | steps_right = 0 7 | steps_left = 0 8 | potential_min = root 9 | potential_max = root 10 | 11 | loop do 12 | if potential_min.left 13 | potential_min = potential_min.left 14 | steps_left += 1 15 | end 16 | if potential_max.right 17 | potential_max = potential_max.right 18 | steps_right += 1 19 | end 20 | 21 | return false if Math.abs(steps_left - steps_right) > 1 22 | return true if potential_min.left.nil? && potential_max.right.nil? 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /problems/ruby/temp_tracker.rb: -------------------------------------------------------------------------------- 1 | 2 | class TempTracker 3 | 4 | attr_reader :max, :min, :mode, :mean 5 | 6 | def initialize(value) 7 | @store = Hash.new {|h, k| h[k] = 0} 8 | @max, @min, @mode, @mean= nil, nil, nil, nil 9 | @max_count = 0 10 | @counts = 0 11 | @mode = [] 12 | @sum = 0 13 | 14 | insert(value) if value 15 | end 16 | 17 | def insert(value) 18 | @store[value] += 1 19 | @counts += 1 20 | @sum += value 21 | update_attributes!(value) 22 | end 23 | 24 | def delete(value) 25 | raise “value does not exist” if @store[value] == 0 26 | @store[value] -= 1 27 | @counts -= 1 28 | @sum -= value 29 | end 30 | 31 | protected 32 | 33 | attr_accessor :store, :max_count, :sum 34 | attr_writer :max, :min, :mean, :mode 35 | 36 | def update_attributes!(value) 37 | @max = value if @max.nil? || @max < value 38 | @min = value if @min.nil? || @min > value 39 | @max_count = @store[value] if @max_count < @store[value] 40 | @mode = value if @max_count == @store[value] 41 | @mean = (@counts == 0 ? nil : @sum / @counts) 42 | end 43 | 44 | 45 | end 46 | 47 | 48 | ##TempTracker.new(5,4,2,1) 49 | -------------------------------------------------------------------------------- /problems/ruby/threesum.rb: -------------------------------------------------------------------------------- 1 | def high_three(arr) 2 | d3,d2h,d2l,arrmax,arrmin = arr[0],arr[0],arr[0],arr[0],arr[0] 3 | 4 | arr.each do |value| 5 | arrmax = value if value > arrmax 6 | arrmin = value if value < arrmin 7 | pdl, pdh = [arrmax * value, arrmin * value].minmax 8 | d2h = pdh if pdh > d2h 9 | d2l = pdl if pdl < d2l 10 | pth = [d2h * value, d2l * value].max 11 | d3 = pth if pth > d3 12 | end 13 | d3 14 | 15 | end 16 | p high_three([-10,-10,1,3,2]) 17 | -------------------------------------------------------------------------------- /problems/ruby/tuples.rb: -------------------------------------------------------------------------------- 1 | 2 | def tuple(arr) 3 | arr.sort!{|x,y| x[0] <=> y[0]} 4 | 5 | arr.each_with_index do |tuple, index| 6 | p index 7 | prev_tuple = arr[index - 1] if index - 1 > 0 8 | next_tuple = arr[index + 1] if index + 1 < arr.length 9 | 10 | if prev_tuple 11 | if tuple[0].between?(next_tuple[0], next_tuple[1]) 12 | tuple = [prev_tuple[0], tuple[1]] 13 | end 14 | end 15 | if next_tuple 16 | if tuple[1].between?(next_tuple[0], next_tuple[1]) 17 | tuple = [tuple[0], next_tuple[1]] 18 | end 19 | end 20 | p arr 21 | end 22 | p arr 23 | end 24 | 25 | 26 | tuple([[0,1],[3,5],[4,8], [10,12], [9, 10]]) 27 | -------------------------------------------------------------------------------- /problems/ruby/two_stacks.rb: -------------------------------------------------------------------------------- 1 | class ShittyStacks 2 | def initialize(size) 3 | self.store = Array.new(size) 4 | self.first_index = 0 5 | self.second_index = size - 1 6 | end 7 | 8 | def push1(el) 9 | raise “array1 overflows” if first_index == second_index 10 | self.store[first_index] = el 11 | first_index += 1 12 | end 13 | 14 | def push2(el) 15 | raise “array2 overflows” if first_index == second_index 16 | self.store[second_index] = el 17 | second_index -= 1 18 | end 19 | 20 | def pop1 21 | raise “no elements in stack1” if first_index = self.store.size 22 | self.store.delete_at(first_index) 23 | first_index -= 1 24 | end 25 | 26 | def peek1 27 | raise “no elements in stack 1” if self.store[first_index].nil? 28 | self.store[first_index] 29 | end 30 | 31 | def peek2 32 | raise “no elements in stack 2” if self.store[first_index].nil? 33 | self.store[second_index] 34 | end 35 | def pop2 36 | raise “no elements in stack 2” if second_index = self.store.size 37 | self.store.delete_at(second_index) 38 | second_index += 1 39 | end 40 | 41 | 42 | 43 | protected 44 | attr_accessor :store 45 | attr_reader :first_index, :second_index 46 | end 47 | -------------------------------------------------------------------------------- /problems/ruby/two_sum.rb: -------------------------------------------------------------------------------- 1 | #el1 + el2 = k => k - el1 = el2 2 | 3 | def two_sum(array, k) 4 | first_index = 0 5 | last_index = array.length - 1 6 | loop do 7 | first_number = array[first_index] 8 | second_number = array[last_index] 9 | return [first_number, second_number] if first_number + second_number == k 10 | raise "value does not exist" if first_index == last_index 11 | last_index -= 1 if first_number + second_number > k 12 | first_index += 1 if first_number + second_number < k 13 | end 14 | end 15 | #[1,2,3,4,5], 0 16 | 17 | p two_sum([1,2,3,4,5],12) 18 | -------------------------------------------------------------------------------- /problems/ruby/unique.rb: -------------------------------------------------------------------------------- 1 | 2 | def find_unique(array) 3 | hash = Hash.new { |k,v| k[v] = 0 } 4 | array.each do |val| 5 | hash[value] += 1 6 | end 7 | 8 | hash.select { |k, v| v == 1 }.keys[0] 9 | end 10 | 11 | ##so this is is o(n)/o(n) space 12 | #[1,999,999,2,2,4,2,3,1,3] 13 | #{ 1 => 2, 999 => 2, 2 => 3, 4 => 1, 3 => 2 }, {4 => 1}.keys[0] 14 | 15 | def find_unique_fast(array) 16 | uniq = 0 17 | array.each do |val| 18 | uniq ^= val 19 | end 20 | uniq 21 | end 22 | ##conditions: numrepeat % 2 == 0, only finds 1 uniq 23 | p find_unique_fast([23, 24, 23, 25, 25, 25, 25, 5, 5]) 24 | 25 | 26 | def find_duplicates 27 | -------------------------------------------------------------------------------- /problems/typescript/orderedWeight.ts: -------------------------------------------------------------------------------- 1 | interface WeightMap { 2 | [number: string]: number; 3 | } 4 | 5 | function constructWeightMap(stringArr: string[]): WeightMap { 6 | const weightMap: WeightMap = {}; 7 | for (let s of stringArr) { 8 | if (!weightMap[s]) { 9 | let digitsArrStr = s.split(""); 10 | for (let digitStr of digitsArrStr) { 11 | let num = Number(digitStr); 12 | weightMap[s] = (weightMap[s] || 0) + num; 13 | } 14 | } 15 | } 16 | return weightMap; 17 | } 18 | function sortStringsArray(stringArr: string[], weightMap: WeightMap) { 19 | return stringArr.sort((sA, sB) => { 20 | const diff = weightMap[sA] - weightMap[sB]; 21 | 22 | if (diff !== 0) { 23 | return diff; 24 | } else { 25 | return sA.localeCompare(sB); 26 | } 27 | }); 28 | } 29 | function orderWeight(str: string) { 30 | const stringArr = str.split(" "); 31 | const weightMap = constructWeightMap(stringArr); 32 | const sortedStringArray = sortStringsArray(stringArr, weightMap); 33 | return sortedStringArray.join(" "); 34 | } 35 | 36 | export class Challenge { 37 | static orderWeight(str: string): string { 38 | const stringArr = str.split(" "); 39 | const weightMap = constructWeightMap(stringArr); 40 | const sortedStringArray = sortStringsArray(stringArr, weightMap); 41 | return sortedStringArray.join(" "); 42 | } 43 | } 44 | 45 | // /// 46 | // /// 47 | // import {Challenge} from "./solution"; 48 | // import {assert} from "chai"; 49 | 50 | // describe("solution", function(){ 51 | // it("should handle basic tests", function() { 52 | // assert.strictEqual(Challenge.orderWeight("103 123 4444 99 2000"), "2000 103 123 4444 99") 53 | // assert.equal(Challenge.orderWeight("103 123 4444 99 2000"), "2000 103 123 4444 99") 54 | // assert.equal(Challenge.orderWeight("2000 10003 1234000 44444444 9999 11 11 22 123"), "11 11 2000 10003 22 123 1234000 44444444 9999"); 55 | // assert.equal(Challenge.orderWeight("2000 11 11"), '11 11 2000'); 56 | // assert.equal(Challenge.orderWeight("56 65 74 100 99 68 86 180 90"), "100 180 90 56 65 74 68 86 99"); 57 | // assert.equal(Challenge.orderWeight("14 500 5000 500000000000"), '14 500 5000 500000000000'); 58 | 59 | // }); 60 | // }); 61 | -------------------------------------------------------------------------------- /problems/typescript/rotatingStrings.ts: -------------------------------------------------------------------------------- 1 | function isStringEqual(first: string, second: string): boolean { 2 | return first === second; 3 | } 4 | 5 | function findRotations(first: string, secondStringArr: string[]) { 6 | let rotations = 0; 7 | while (rotations < secondStringArr.length) { 8 | rotations++; 9 | const removedChar = secondStringArr.shift()!; 10 | secondStringArr.push(removedChar); 11 | const secondString = secondStringArr.join(""); 12 | console.log(secondStringArr); 13 | if (isStringEqual(first, secondString)) { 14 | return rotations; 15 | } 16 | } 17 | return -1; 18 | } 19 | 20 | export const shiftedDiff = (first: string, second: string): number => { 21 | //immediately equal 22 | if (isStringEqual(first, second)) { 23 | return 0; 24 | } 25 | let secondStringArr = second.split(""); 26 | // quick character check if impossible 27 | for (let char of secondStringArr) { 28 | if (first.indexOf(char) === -1) { 29 | return -1; 30 | } 31 | } 32 | return findRotations(first, second.split("")); 33 | }; 34 | 35 | // 36 | // import chai, {assert} from "chai"; 37 | // import {shiftedDiff} from "./solution"; 38 | // chai.config.truncateThreshold = 0; 39 | // const loremIpsum = `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`; 40 | // const loremIpsumReversed = loremIpsum.split('').reverse().join(''); 41 | // const a = ` 42 | // m 43 | // u 44 | // l 45 | // t 46 | // i 47 | // l 48 | // i 49 | // n 50 | // e` 51 | 52 | // const b = ` 53 | // u 54 | // l 55 | // t 56 | // i 57 | // l 58 | // i 59 | // n 60 | // e 61 | // m` 62 | // describe("shiftedDiff", () => { 63 | // it("should work on basic tests", () => { 64 | // assert.equal(shiftedDiff("eecoff", "coffee"), 4); 65 | // assert.equal(shiftedDiff("Moose", "moose"), -1); 66 | // assert.equal(shiftedDiff("isn't", "'tisn"), 2); 67 | // assert.equal(shiftedDiff("Esham", "Esham"), 0); 68 | // assert.equal(shiftedDiff("coffee", "eecoff"), 2); 69 | // assert.equal(shiftedDiff(" ", " "), 0); 70 | // assert.equal(shiftedDiff("hoop", "pooh"), -1); 71 | // assert.equal(shiftedDiff(" ", " "), -1); 72 | // assert.equal(shiftedDiff("coffee", "eecoff"),2); 73 | // assert.equal(shiftedDiff("eecoff", "coffee"),4); 74 | // assert.equal(shiftedDiff("moose", "Moose"),-1); 75 | // assert.equal(shiftedDiff("isn't", "'tisn"),2); 76 | // assert.equal(shiftedDiff("Esham", "Esham"),0); 77 | // assert.equal(shiftedDiff("dog", "god"),-1); 78 | // }); 79 | // it('should give different numbers for different rotationss', () => { 80 | // assert.equal(shiftedDiff("fatigue", "efatigu"), 1); 81 | // assert.equal(shiftedDiff("fatigue", "iguefat"), 4); 82 | // }); 83 | // it('should handle non-compatible multi-line reversions', () => { 84 | // assert.equal(shiftedDiff(loremIpsum, loremIpsumReversed), -1); 85 | // }); 86 | // it('should handle multiline chars', () => { 87 | // assert.equal(shiftedDiff(a, b), 17); 88 | // }) 89 | // }); 90 | --------------------------------------------------------------------------------