├── .gitattributes ├── .gitignore ├── .pryrc ├── .rspec ├── .vscode ├── launch.json └── settings.json ├── Codepath ├── Week1-Warmup │ ├── java │ │ └── reverse.java │ └── python │ │ └── reverse.py ├── Week2-HashTables │ ├── java │ │ └── findPairs.java │ └── python │ │ └── findPairs.py ├── Week3-LinkedLists │ ├── java │ │ ├── addTwoNumbers.java │ │ ├── deleteDuplicates.java │ │ ├── getLength.java │ │ ├── mergeTwoLists.java │ │ ├── palindrome.java │ │ └── removeNthFromEnd.java │ └── python │ │ ├── addTwoNumbers.py │ │ ├── deleteDuplicates.py │ │ ├── getLength.py │ │ ├── mergeTwoLists.py │ │ ├── palindrome.py │ │ └── removeNthFromEnd.py ├── Week4-Review │ ├── java │ │ └── createDictionary.java │ └── python │ │ └── createDictionary.py ├── Week5-StacksAndQueues │ ├── java │ │ ├── QueueUsingStacks.java │ │ ├── StackUsingQueues.java │ │ ├── evaluatePostfix.java │ │ └── isStackSequenceValid.java │ └── python │ │ ├── QueueUsingStacks.py │ │ ├── StackUsingQueues.py │ │ ├── evaluatePostfix.py │ │ └── isStackSequenceValid.py └── Week6-StringsAndArrays │ ├── java │ ├── bigIntegerIncrement.java │ ├── encode.java │ └── subArraySort.java │ └── python │ ├── bigIntegerIncrement.py │ ├── encode.py │ └── subArraySort.py ├── Gemfile ├── Gemfile.lock ├── README.md ├── add_numbers_spec.rb ├── chVI ├── Ch VI Big O.txt ├── example12.java └── hi.rb ├── codesignal ├── postmates_spec.rb ├── practicetest.rb └── sum_divisible_by_k.rb ├── data_structures └── linked_list.test.js ├── diagonal_matrix.go ├── google ├── crossword.rb └── max_sum_path.rb ├── gtci └── 01-sliding-window │ ├── 01_avgs_of_subarrays.test.js │ ├── 02_max_sum_subarray.test.js │ ├── 03_smallest_subarray_of_given_sum.test.js │ ├── 04_longest_substring_with_k_distinct_chars.test.js │ ├── 05_fruits_into_baskets.test.js │ ├── 06_no_repeat_substring.test.js │ ├── 07_longest_repeating_substring.test.js │ ├── 08_longest_subarray_with_1s_after_replacement.test.js │ └── 09_permutation_of_pattern_in_string.test.js ├── hackerrank ├── 10-days-of-javascript │ ├── .vscode │ │ └── launch.json │ ├── 0-data-types.js │ ├── 1-factorial.js │ └── 1-let-and-const.js ├── 2d_array_ds.go ├── balanced_brackets.go ├── binary_tree_level_average.rb ├── count_triplets.go ├── cracking-the-coding-interview │ ├── arrays-left-rotation.js │ └── dp-coin-change.js ├── equalizing_array_elements.rb ├── find_substring_most_vowels.rb ├── left-rotation.js ├── logic_expression_spec.rb ├── make_anagram_test.go ├── mini-max-sum.js ├── minimum_swaps2.go ├── new_year_chaos.go ├── practice.sql ├── ransom_note.go ├── sherlock_and_anagrams.go ├── sherlock_and_the_valid_string.go └── two_strings.go ├── leetcode ├── calculate_time.rb ├── critical_connections_in_a_network_spec.rb ├── cut_off_trees_for_golf_spec.rb ├── cut_off_trees_spec.rb ├── easy │ ├── best_time_buy_sell_stock2.js │ ├── contains_duplicate.js │ ├── intersection_of_2_arrays_ii.js │ ├── maximum_depth_binary_tree.js │ ├── nearest_point_same_x_or_y_coordinate.js │ ├── remove_duplicates_from_sorted_array.js │ ├── reverse_linked_list.js │ ├── reverse_string.js │ ├── single_number.js │ ├── valid_anagram.js │ ├── valid_anagram.rb │ ├── valid_palindrome.js │ └── valid_parentheses.js ├── expressive_words.rb ├── find_words_that_can_be_formed_by_characters_spec.rb ├── greatest_common_divisor_strings.test.js ├── happy_number_spec.rb ├── k_closest_points_spec.rb ├── longest_increasing_path_in_a_matrix.rb ├── longest_palindrome_spec.rb ├── maximal_square_spec.rb ├── medium │ ├── reverse_integer.js │ └── rotate_array.js ├── most_common_word.rb ├── moving_average_spec.rb ├── number_of_dice_rolls_with_target_sum_spec.rb ├── number_of_equivalent_domino_pairs_spec.rb ├── prison_cells_after_n_days_spec.rb ├── product_of_array_except_self_spec.rb ├── relative_sort_array.rb ├── reverse_string_ii_spec.rb ├── robot_return_to_origin.rb ├── rotting_oranges_spec.rb ├── search_in_rotated_sorted_array_spec.rb ├── spiral_matrix_ii_spec.rb ├── string_transforms_into_another_string_spec.rb ├── subsequences_spec.rb ├── sudoku.rb ├── two_sum_iv_bst_spec.rb └── two_sum_spec.rb ├── order_courses_spec.rb ├── package-lock.json ├── package.json └── permutation.rb /.gitattributes: -------------------------------------------------------------------------------- 1 | *.rb linguist-detectable=false 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.pryrc: -------------------------------------------------------------------------------- 1 | if defined?(PryByebug) 2 | Pry.commands.alias_command 'c', 'continue' 3 | Pry.commands.alias_command 's', 'step' 4 | Pry.commands.alias_command 'n', 'next' 5 | Pry.commands.alias_command 'f', 'finish' 6 | end 7 | 8 | # Hit Enter to repeat last command 9 | Pry::Commands.command /^$/, "repeat last command" do 10 | _pry_.run_command Pry.history.to_a.last 11 | end 12 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Run RSpec - all", 5 | "type": "Ruby", 6 | "request": "launch", 7 | "cwd": "${workspaceRoot}", 8 | "program": "/Users/rgan/.rvm/gems/ruby-2.6.3/bin/rspec", 9 | "args": [ 10 | "--pattern", 11 | "${workspaceRoot}/*_spec.rb" 12 | ] 13 | }, 14 | { 15 | "name": "Debug RSpec - open spec file", 16 | "type": "Ruby", 17 | "request": "launch", 18 | "cwd": "${workspaceRoot}", 19 | "useBundler": true, 20 | "pathToBundler": "/Users/rgan/.rvm/rubies/ruby-2.6.3/bin/bundle", 21 | "pathToRDebugIDE": "/Users/rgan/.rvm/gems/ruby-2.6.3/bin/rdebug-ide", 22 | "debuggerPort": "1235", 23 | "program": "/Users/rgan/.rvm/gems/ruby-2.6.3/bin/rspec", 24 | "args": [ 25 | "${file}" 26 | ] 27 | }, 28 | { 29 | "type": "java", 30 | "name": "CodeLens (Launch) - Example12", 31 | "request": "launch", 32 | "mainClass": "Example12" 33 | }, 34 | { 35 | "type": "node", 36 | "request": "launch", 37 | "name": "Jest All", 38 | "program": "${workspaceFolder}/node_modules/.bin/jest", 39 | "args": [ 40 | "--runInBand" 41 | ], 42 | "console": "integratedTerminal", 43 | "internalConsoleOptions": "neverOpen", 44 | "disableOptimisticBPs": true, 45 | "windows": { 46 | "program": "${workspaceFolder}/node_modules/jest/bin/jest", 47 | } 48 | }, 49 | { 50 | "type": "node", 51 | "request": "launch", 52 | "name": "Jest Current File", 53 | "program": "${workspaceFolder}/node_modules/.bin/jest", 54 | "args": [ 55 | "--runTestsByPath", 56 | "${relativeFile}", 57 | "--config", 58 | "jest.config.js" 59 | ], 60 | "console": "integratedTerminal", 61 | "internalConsoleOptions": "neverOpen", 62 | "disableOptimisticBPs": true, 63 | "windows": { 64 | "program": "${workspaceFolder}/node_modules/jest/bin/jest", 65 | } 66 | } 67 | ] 68 | } 69 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "**/.classpath": true, 4 | "**/.project": true, 5 | "**/.settings": true, 6 | "**/.factorypath": true 7 | } 8 | } -------------------------------------------------------------------------------- /Codepath/Week1-Warmup/java/reverse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Problem: Write a function to reverse a string 3 | */ 4 | boolean reverse(String str) { 5 | char[] chars = str.toCharArray() 6 | int s = 0, e = chars.length - 1; 7 | while (s < e) { 8 | temp = chars[e] 9 | chars[e] = chars[s] 10 | chars[s] = temp 11 | s++; 12 | e--; 13 | } 14 | return String(chars); 15 | } 16 | -------------------------------------------------------------------------------- /Codepath/Week1-Warmup/python/reverse.py: -------------------------------------------------------------------------------- 1 | # Problem: Write a function to reverse a string 2 | def reverse(str): 3 | chars = list(str) 4 | s = 0; e = len(chars) - 1 5 | while s < e: 6 | chars[e], chars[s] = chars[s], chars[e] 7 | s += 1 8 | e -= 1 9 | return ''.join(chars) 10 | -------------------------------------------------------------------------------- /Codepath/Week2-HashTables/java/findPairs.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Problem: Given an array of size n and a number x, determine the first two elements in the array, if any, whose sum is exactly x. 3 | */ 4 | public int[] findPairs(int[] arr, int sum) { 5 | Set set = new HashSet(); 6 | for (int i=0; i < arr.length; i++) { 7 | if (set.contains(sum - arr[i])) { 8 | int[] pair = {arr[i], sum - arr[i]}; 9 | return pair; 10 | } else { 11 | set.add(arr[i]); 12 | } 13 | } 14 | return null; 15 | } 16 | 17 | // Time Complexity: O(n) 18 | // Space Complexity: O(n) 19 | -------------------------------------------------------------------------------- /Codepath/Week2-HashTables/python/findPairs.py: -------------------------------------------------------------------------------- 1 | # Problem: Given an array of size n and a number x, determine the first two elements in the array, if any, whose sum is exactly x. 2 | def findPairs(arr, sum): 3 | h = set() 4 | for i in range(0, len(arr)): 5 | if (sum - arr[i]) in h: 6 | return ([arr[i], sum - arr[i]]) 7 | else: 8 | h.add(arr[i]) 9 | return null 10 | 11 | # Time Complexity: O(n) 12 | # Space Complexity: O(n) 13 | -------------------------------------------------------------------------------- /Codepath/Week3-LinkedLists/java/addTwoNumbers.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Problem: You are given two non-empty linked lists representing two non-negative integers. 3 | * The digits are stored in reverse order and each of their nodes contain a single digit. 4 | * Add the two numbers and return it as a linked list. 5 | * You may assume the two numbers do not contain any leading zero, except the number 0 itself. 6 | * 7 | * Example: 8 | * Input: 2 -> 4 -> 3, 5 -> 6 -> 4 9 | * Output: 7 -> 0 -> 8 10 | * 11 | * Explanation: 342 + 465 = 807 12 | */ 13 | public ListNode addTwoNumbers(ListNode l1, ListNode l2) { 14 | ListNode dummyHead = new ListNode(0); 15 | ListNode p = l1, q = l2, curr = dummyHead; 16 | int carry = 0; 17 | while (p != null || q != null) { 18 | int x = (p != null) ? p.val : 0; 19 | int y = (q != null) ? q.val : 0; 20 | int sum = carry + x + y; 21 | carry = sum / 10; 22 | curr.next = new ListNode(sum % 10); 23 | curr = curr.next; 24 | if (p != null) p = p.next; 25 | if (q != null) q = q.next; 26 | } 27 | if (carry > 0) { 28 | curr.next = new ListNode(carry); 29 | } 30 | return dummyHead.next; 31 | } 32 | -------------------------------------------------------------------------------- /Codepath/Week3-LinkedLists/java/deleteDuplicates.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Problem: Given a linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list. Can you do it without taking up extra memory? 3 | * 4 | * Examples: 5 | * Input: 1->2->3->3->4->4->5 6 | * Output: 1->2->5 7 | 8 | * Input: 1->1->1->2->3 9 | * Output: 2->3 10 | */ 11 | public ListNode deleteDuplicates(ListNode head) { 12 | ListNode dummyHead = new ListNode(0); 13 | dummyHead.next = head; 14 | ListNode prev = dummyHead; 15 | ListNode current = head; 16 | 17 | while (current != null) 18 | { 19 | while (current.next != null && 20 | prev.next.val == current.next.val) 21 | current = current.next; 22 | 23 | if (prev.next == current) 24 | prev = prev.next; 25 | else 26 | prev.next = current.next; 27 | 28 | current = current.next; 29 | } 30 | return dummyHead.next; 31 | } 32 | -------------------------------------------------------------------------------- /Codepath/Week3-LinkedLists/java/getLength.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Problem: Given a node, return the length of the linked list 3 | * 4 | * Examples: 5 | * Input: 1 ; Return: 1 6 | * Input 1->2->3 ; Return 3 7 | */ 8 | public class Main { 9 | public static void main(String[] args) { 10 | ListNode n0 = new ListNode(0); 11 | System.out.println("Test 1 passed: " + (getLength(n0) == 1)); 12 | 13 | ListNode n1 = new ListNode(1); 14 | ListNode n2 = new ListNode(2); 15 | n1.next = n2; 16 | System.out.println("Test 2 passed: " + (getLength(n1) == 2)); 17 | } 18 | 19 | public static int getLength(ListNode node) { 20 | int length = 0; 21 | while (node != null) { 22 | length++; 23 | node = node.next; 24 | } 25 | 26 | return length; 27 | } 28 | 29 | public static class ListNode { 30 | int data; 31 | ListNode next; 32 | 33 | ListNode(int data) { 34 | this.data = data; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Codepath/Week3-LinkedLists/java/mergeTwoLists.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Problem: Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists. 3 | * 4 | * Example: 5 | * Input: 1->2->4, 1->3->4 6 | * Output: 1->1->2->3->4->4 7 | */ 8 | public ListNode mergeTwoLists(ListNode l1, ListNode l2) { 9 | ListNode head = new ListNode(0); 10 | ListNode p = head; 11 | 12 | ListNode p1 = l1; 13 | ListNode p2 = l2; 14 | while(p1 != null && p2 != null){ 15 | if(p1.val < p2.val) { 16 | p.next = p1; 17 | p1 = p1.next; 18 | } else{ 19 | p.next = p2; 20 | p2 = p2.next; 21 | } 22 | p = p.next; 23 | } 24 | if(p1 != null) { 25 | p.next = p1; 26 | } 27 | if(p2 != null) { 28 | p.next = p2; 29 | } 30 | return head.next; 31 | } 32 | -------------------------------------------------------------------------------- /Codepath/Week3-LinkedLists/java/palindrome.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Given a singly linked list, determine if it is a palindrome. 3 | * 4 | * Example 1: 5 | * Input: 1->2 6 | * Output: false 7 | * 8 | * Example 2: 9 | * Input: 1->2->2->1 10 | * Output: true 11 | * 12 | **/ 13 | public class Main { 14 | public static void main(String[] args) { 15 | 16 | ListNode n1_1 = new ListNode(1); 17 | System.out.println("Test cases 1 passed: " + isPalindrome(n1_1)); 18 | 19 | ListNode n2_1 = new ListNode(1); 20 | ListNode n2_2 = new ListNode(2); 21 | n2_1.next = n2_2; 22 | System.out.println("test case 2 passed: " + !isPalindrome(n2_1)); 23 | 24 | ListNode n3_1 = new ListNode(1); 25 | ListNode n3_2 = new ListNode(2); 26 | ListNode n3_3 = new ListNode(3); 27 | n3_1.next = n3_2; 28 | n3_2.next = n3_3; 29 | System.out.println("test case 3 passed: " + !isPalindrome(n3_1)); 30 | 31 | ListNode n4_1 = new ListNode(1); 32 | ListNode n4_2 = new ListNode(2); 33 | ListNode n4_3 = new ListNode(1); 34 | n4_1.next = n4_2; 35 | n4_2.next = n4_3; 36 | System.out.println("test case 4 passed: " + isPalindrome(n4_1)); 37 | } 38 | 39 | public static boolean isPalindrome(ListNode node) { 40 | int fullLength = getLength(node); 41 | 42 | int[] firstHalf = new int[fullLength / 2]; 43 | int currentNode = 0; 44 | while (currentNode < (fullLength / 2)) { 45 | firstHalf[currentNode] = node.data; 46 | node = node.next; 47 | currentNode++; 48 | } 49 | 50 | // Skip the middle node if the list has an odd number of nodes 51 | if (fullLength % 2 == 1) { 52 | node = node.next; 53 | } 54 | 55 | while (currentNode > 0) { 56 | if (node.data != firstHalf[currentNode - 1]) { 57 | return false; 58 | } 59 | node = node.next; 60 | currentNode--; 61 | } 62 | 63 | if (currentNode != 0) { 64 | return false; 65 | } 66 | 67 | return true; 68 | } 69 | 70 | public static int getLength(ListNode node) { 71 | int length = 0; 72 | while (node != null) { 73 | length++; 74 | if (node.next == null) { 75 | return length; 76 | } 77 | node = node.next; 78 | } 79 | return length; 80 | } 81 | 82 | public static class ListNode { 83 | int data; 84 | ListNode next; 85 | 86 | ListNode(int data) { 87 | this.data = data; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Codepath/Week3-LinkedLists/java/removeNthFromEnd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Problem: Given a linked list, remove the n-th node from the end of list and return its head. 3 | * 4 | * Example: 5 | * Input: 1->2->3->4->5, n = 2 6 | * Output: 1->2->3->5 7 | * 8 | * Explanation: We remove the second node from the end, the node with value 4 9 | */ 10 | public ListNode removeNthFromEnd(ListNode head, int n) { 11 | ListNode dummy = new ListNode(0); 12 | dummy.next = head; 13 | ListNode first = dummy; 14 | ListNode second = dummy; 15 | // Advances first pointer so that the gap between first and second is n nodes apart 16 | for (int i = 1; i <= n + 1; i++) { 17 | first = first.next; 18 | } 19 | // Move first to the end, maintaining the gap 20 | while (first != null) { 21 | first = first.next; 22 | second = second.next; 23 | } 24 | second.next = second.next.next; 25 | return dummy.next; 26 | } 27 | -------------------------------------------------------------------------------- /Codepath/Week3-LinkedLists/python/addTwoNumbers.py: -------------------------------------------------------------------------------- 1 | # Problem: You are given two non-empty linked lists representing two non-negative integers. 2 | # The digits are stored in reverse order and each of their nodes contain a single digit. 3 | # Add the two numbers and return it as a linked list. 4 | # You may assume the two numbers do not contain any leading zero, except the number 0 itself. 5 | # 6 | # Example: 7 | # Input: 2 -> 4 -> 3, 5 -> 6 -> 4 8 | # Output: 7 -> 0 -> 8 9 | # 10 | # Explanation: 342 + 465 = 807 11 | def addTwoNumbers(l1: ListNode, l2: ListNode): 12 | dummyHead = ListNode(0); 13 | p = l1 14 | q = l2 15 | curr = dummyHead 16 | carry = 0; 17 | while (p != None or q != None): 18 | x = p.val if p != None else 0 19 | y = q.val if q != None else 0 20 | sum = carry + x + y; 21 | carry = sum // 10; 22 | curr.next = ListNode(sum % 10) 23 | curr = curr.next; 24 | if p != None: 25 | p = p.next; 26 | if q != None: 27 | q = q.next; 28 | 29 | if (carry > 0): 30 | curr.next = ListNode(carry); 31 | 32 | return dummyHead.next; 33 | -------------------------------------------------------------------------------- /Codepath/Week3-LinkedLists/python/deleteDuplicates.py: -------------------------------------------------------------------------------- 1 | # Problem: Given a linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list. Can you do it without taking up extra memory? 2 | # 3 | # Examples: 4 | # Input: 1->2->3->3->4->4->5 5 | # Output: 1->2->5 6 | # 7 | # Input: 1->1->1->2->3 8 | # Output: 2->3 9 | def deleteDuplicates(self, head: ListNode) -> ListNode: 10 | dummyHead = ListNode(0) 11 | dummyHead.next = head 12 | prev = dummyHead 13 | current = head 14 | 15 | while current != None: 16 | while (current.next != None and 17 | prev.next.val == current.next.val): 18 | current = current.next 19 | 20 | if prev.next == current: 21 | prev = prev.next 22 | else: 23 | prev.next = current.next 24 | 25 | current = current.next 26 | return dummyHead.next 27 | -------------------------------------------------------------------------------- /Codepath/Week3-LinkedLists/python/getLength.py: -------------------------------------------------------------------------------- 1 | # Problem: Given a node, return the length of the linked list 2 | # 3 | # Examples: 4 | # Input: 1 ; Return: 1 5 | # Input 1->2->3 ; Return 3 6 | # 7 | class ListNode: 8 | def __init__(self, node_data): 9 | self.data = node_data 10 | self.next = None 11 | 12 | def getLength(node): 13 | length = 0 14 | while (node != None): 15 | length += 1 16 | node = node.next 17 | 18 | return length 19 | 20 | class ListNodeLength: 21 | if __name__ == '__main__': 22 | n0 = ListNode(0) 23 | print(str(getLength(n0) == 1) + '\n') 24 | 25 | n1 = ListNode(1) 26 | n2 = ListNode(2) 27 | n1.next = n2 28 | print(str(getLength(n1) == 2) + '\n') 29 | -------------------------------------------------------------------------------- /Codepath/Week3-LinkedLists/python/mergeTwoLists.py: -------------------------------------------------------------------------------- 1 | # Problem: Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists. 2 | # 3 | # Example: 4 | # Input: 1->2->4, 1->3->4 5 | # Output: 1->1->2->3->4->4 6 | def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode: 7 | head = ListNode(0) 8 | p = head 9 | p1 = l1 10 | p2 = l2 11 | 12 | while p1 != None and p2 != None: 13 | if p1.val < p2.val: 14 | p.next = p1 15 | p1 = p1.next 16 | else: 17 | p.next = p2 18 | p2 = p2.next 19 | p = p.next 20 | 21 | if p1 != None: 22 | p.next = p1 23 | 24 | if p2 != None: 25 | p.next = p2 26 | 27 | return head.next 28 | -------------------------------------------------------------------------------- /Codepath/Week3-LinkedLists/python/palindrome.py: -------------------------------------------------------------------------------- 1 | # 2 | # Given a singly linked list, determine if it is a palindrome. 3 | # 4 | # Example 1: 5 | # Input: 1->2 6 | # Output: false 7 | # 8 | # Example 2: 9 | # Input: 1->2->2->1 10 | # Output: true 11 | class ListNode: 12 | def __init__(self, node_data): 13 | self.data = node_data 14 | self.next = None 15 | 16 | def isPalindrome(node): 17 | fullLength = getLength(node) 18 | 19 | firstHalf = [fullLength / 2] 20 | currentNode = 0 21 | while (currentNode < (fullLength / 2)): 22 | firstHalf[currentNode] = node.data 23 | node = node.next 24 | currentNode += 1 25 | 26 | # Skip the middle node if the list has an odd number of nodes 27 | if fullLength % 2: 28 | node = node.next 29 | 30 | while (currentNode > 0): 31 | if (node.data != firstHalf[currentNode-1]): 32 | return False 33 | node = node.next 34 | currentNode -= 1 35 | 36 | if (currentNode != 0): 37 | return False 38 | 39 | return True 40 | 41 | def getLength(node): 42 | length = 0; 43 | while (node != None): 44 | length += 1 45 | 46 | if (node.next == None): 47 | return length 48 | node = node.next 49 | return length 50 | 51 | class Main: 52 | if __name__ == '__main__': 53 | n1_1 = ListNode(1) 54 | print("Test cases 1 passed: " + str(isPalindrome(n1_1))) 55 | 56 | n2_1 = ListNode(1) 57 | n2_2 = ListNode(2) 58 | n2_1.next = n2_2 59 | print("test case 2 passed: " + str(not isPalindrome(n2_1))) 60 | 61 | n3_1 = ListNode(1) 62 | n3_2 = ListNode(2) 63 | n3_3 = ListNode(3) 64 | n3_1.next = n3_2 65 | n3_2.next = n3_3 66 | print("test case 3 passed: " + str(not isPalindrome(n3_1))) 67 | 68 | n4_1 = ListNode(1) 69 | n4_2 = ListNode(2) 70 | n4_3 = ListNode(1) 71 | n4_1.next = n4_2 72 | n4_2.next = n4_3 73 | print("test case 4 passed: " + str(isPalindrome(n4_1))) 74 | -------------------------------------------------------------------------------- /Codepath/Week3-LinkedLists/python/removeNthFromEnd.py: -------------------------------------------------------------------------------- 1 | # Problem: Given a linked list, remove the n-th node from the end of list and return its head. 2 | # 3 | # Example: 4 | # Input: 1->2->3->4->5, n = 2 5 | # Output: 1->2->3->5 6 | # 7 | # Explanation: We remove the second node from the end, the node with value 4 8 | def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode: 9 | dummy = ListNode(0) 10 | dummy.next = head 11 | first = dummy 12 | second = dummy 13 | # Advances first pointer so that the gap between first and second is n nodes apart 14 | for i in range(n+1): 15 | first = first.next 16 | # Move first to the end, maintaining the gap 17 | while (first != None): 18 | first = first.next 19 | second = second.next 20 | second.next = second.next.next 21 | return dummy.next 22 | -------------------------------------------------------------------------------- /Codepath/Week4-Review/java/createDictionary.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Problem: Design a method to find the number of occurrences of any given word in a book. 3 | * A word is represented as a String and a book is represented as a list of strings. 4 | * This method will be called multiple times. 5 | */ 6 | HashMap createDictionary(String[] book) { 7 | HashMap dictionary = new HashMap<>(); 8 | for (String word : book) { 9 | word = word.trim(); 10 | if (!word.equals("")) { 11 | word = word.toLowerCase(); 12 | if (!dictionary.containsKey(word)) { 13 | dictionary.put(word, 1); 14 | } 15 | dictionary.put(word, dictionary.get(word) + 1); 16 | } 17 | } 18 | return dictionary; 19 | } 20 | 21 | int getFrequency(String[] book, String word) { 22 | HashMap dictionary = createDictionary(book); 23 | if (dictionary == null || word == null) return -1; 24 | word = word.toLowerCase(); 25 | if (dictionary.containsKey(word)) { 26 | return dictionary.get(word); 27 | } 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /Codepath/Week4-Review/python/createDictionary.py: -------------------------------------------------------------------------------- 1 | # Problem: Design a method to find the number of occurrences of any given word in a book. 2 | # A word is represented as a String and a book is represented as a list of strings. 3 | # This method will be called multiple times. 4 | def createDictionary(book): 5 | if not book: 6 | return None 7 | dictionary = {} 8 | for word in book: 9 | word = word.strip() 10 | if word: 11 | word = word.lower() 12 | if word not in dictionary: 13 | dictionary[word] = 1 14 | else: 15 | dictionary[word] = dictionary[word] + 1 16 | return dictionary 17 | 18 | def getFrequency(book, word): 19 | dictionary = createDictionary(book) 20 | if not book or not word: 21 | return -1 22 | word = word.strip().lower() 23 | if word in dictionary: 24 | return dictionary[word] 25 | else: 26 | return 0 27 | -------------------------------------------------------------------------------- /Codepath/Week5-StacksAndQueues/java/QueueUsingStacks.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Problem: Implement a queue using instances of stack data structure and operations on them. 3 | */ 4 | class MyQueue { 5 | private Stack s1 = new Stack<>(); 6 | private Stack s2 = new Stack<>(); 7 | private int front; 8 | 9 | /** Push element x to the back of queue. */ 10 | public void enQueue(int x) { 11 | if (s1.empty()) 12 | front = x; 13 | s1.push(x); 14 | } 15 | 16 | /** Removes the element from in front of queue 17 | * and returns that element. */ 18 | public int deQueue() { 19 | if (s2.isEmpty()) { 20 | while (!s1.isEmpty()) 21 | s2.push(s1.pop()); 22 | } 23 | return s2.pop(); 24 | } 25 | 26 | public int peek() { 27 | if (!s2.isEmpty()) { 28 | return s2.peek(); 29 | } 30 | return front; 31 | } 32 | 33 | /** Returns whether the queue is empty. */ 34 | public boolean empty() { 35 | return s1.isEmpty() && s2.isEmpty(); 36 | } 37 | -------------------------------------------------------------------------------- /Codepath/Week5-StacksAndQueues/java/StackUsingQueues.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Problem: Given a Queue data structure that supports standard operations like enqueue() and dequeue(), 3 | * implement a Stack data structure using only instances of Queue and queue operations allowed on the instances. 4 | */ 5 | static class Stack 6 | { 7 | private Queue q1 = new LinkedList<>(); 8 | private Queue q2 = new LinkedList<>(); 9 | private int top; 10 | 11 | public void push(int x) { 12 | q1.add(x); 13 | top = x; 14 | } 15 | 16 | public void pop() { 17 | while (q1.size() > 1) { 18 | top = q1.remove(); 19 | q2.add(top); 20 | } 21 | q1.remove(); 22 | Queue temp = q1; 23 | q1 = q2; 24 | q2 = temp; 25 | } 26 | 27 | public int top() { 28 | return top; 29 | } 30 | 31 | public boolean empty() { 32 | return q1.isEmpty(); 33 | -------------------------------------------------------------------------------- /Codepath/Week5-StacksAndQueues/java/evaluatePostfix.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Problem: Write a function to evaluate the value of an expression in postfix notation represented by a string with numbers between 0 and 9 and operators separated by whitespace. The expression supports 4 binary operators ‘+’, ‘*’, ‘-’ and ‘/’ 3 | * 4 | * Example: 5 | * Input: "5 1 2 + 4 * + 3 -" 6 | * Output: 14 7 | 8 | * The expression is evaluated as follows: 9 | 5 3 4 * + 3 - 10 | 5 12 + 3 - 11 | 17 3 - 12 | 14 13 | */ 14 | public int evaluatePostfix(String exp) { 15 | Stack stack = new Stack<>(); 16 | 17 | for(int i = 0; i < exp.length(); i++) { 18 | char c = exp.charAt(i); 19 | if (Character.isDigit(c)) { 20 | stack.push(c - '0'); 21 | } else if (Character.isSpace(c)) { 22 | continue; 23 | } else { 24 | int val1 = stack.pop(); 25 | int val2 = stack.pop(); 26 | switch(c) { 27 | case '+': 28 | stack.push(val2 + val1); 29 | break; 30 | 31 | case '-': 32 | stack.push(val2 - val1); 33 | break; 34 | 35 | case '/': 36 | stack.push(val2 / val1); 37 | break; 38 | 39 | case '*': 40 | stack.push(val2 * val1); 41 | break; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Codepath/Week5-StacksAndQueues/java/isStackSequenceValid.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Problem: Given push and pop sequences with distinct values, check if this could have been the result of a sequence of push and pop operations on an initially empty stack. 3 | * Example: 4 | * Input: pushed = [1, 2, 3, 4, 5], popped = [4, 5, 3, 2, 1] 5 | * Output: True 6 | 7 | * Following sequence can be performed: 8 | * push(1), push(2), push(3), push(4), pop() -> 4, 9 | * push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1 10 | 11 | * pushed = [1, 2, 3, 4, 5], popped = [4, 3, 5, 1, 2] 12 | * Output: False 13 | 14 | * Explanation: 1 can't be popped before 2. 15 | */ 16 | boolean isStackSequenceValid(int pushed[], int popped[]) 17 | { 18 | if(pushed.length != popped.length) 19 | return false; 20 | int len = pushed.length; 21 | 22 | int j = 0; 23 | Stack st = new Stack<>(); 24 | 25 | for (int i = 0; i < pushed.length; i++) { 26 | st.push(pushed[i]); 27 | while (!st.empty() && j < len && 28 | st.peek() == popped[j]) { 29 | st.pop(); 30 | j++; 31 | } 32 | } 33 | return j == len; 34 | } 35 | -------------------------------------------------------------------------------- /Codepath/Week5-StacksAndQueues/python/QueueUsingStacks.py: -------------------------------------------------------------------------------- 1 | # Problem: Implement a queue using instances of stack data structure and operations on them. 2 | class MyQueue: 3 | 4 | s1 = [] 5 | s2 = [] 6 | front = -1 7 | 8 | def enQueue(self, x): 9 | if (len(self.s1) == 0): 10 | front = x 11 | self.s1.append(x) 12 | 13 | def deQueue(self): 14 | if len(self.s2) == 0: 15 | while(len(self.s1) != 0): 16 | self.s2.append(self.s1.pop()) 17 | return self.s2.pop() 18 | 19 | def peek(self): 20 | if len(self.s2) != 0: 21 | return self.s2[0] 22 | return self.front 23 | 24 | def empty(self): 25 | return len(self.s2) == 0 and len(self.s1) == 0 26 | -------------------------------------------------------------------------------- /Codepath/Week5-StacksAndQueues/python/StackUsingQueues.py: -------------------------------------------------------------------------------- 1 | # Problem: Given a Queue data structure that supports standard operations like enqueue() and dequeue(), 2 | # implement a Stack data structure using only instances of Queue and queue operations allowed on the instances. 3 | import queue 4 | class MyStack: 5 | q1 = queue.Queue() 6 | q2 = queue.Queue() 7 | top = -1 8 | 9 | def push(self, x): 10 | self.q1.put(x); 11 | self.top = x; 12 | 13 | def pop(self): 14 | while (self.q1.qsize() > 1): 15 | self.top = self.q1.get() 16 | self.q2.put(self.top) 17 | self.q1.get(); 18 | temp = self.q1; 19 | self.q1 = self.q2; 20 | self.q2 = temp; 21 | 22 | def top(self): 23 | return top 24 | 25 | def empty(self): 26 | return self.q1.empty() 27 | -------------------------------------------------------------------------------- /Codepath/Week5-StacksAndQueues/python/evaluatePostfix.py: -------------------------------------------------------------------------------- 1 | # Problem: Write a function to evaluate the value of an expression in postfix notation represented by a string with numbers between 0 and 9 and operators separated by whitespace. The expression supports 4 binary operators ‘+’, ‘*’, ‘-’ and ‘/’ 2 | # 3 | # Example: 4 | # Input: "5 1 2 + 4 * + 3 -" 5 | # Output: 14 6 | # 7 | # The expression is evaluated as follows: 8 | # 5 3 4 * + 3 - 9 | # 5 12 + 3 - 10 | # 17 3 - 11 | # 14 12 | def evaluatePostfix(exp): 13 | stack = [] 14 | # Iterate over the expression for conversion 15 | for i in exp: 16 | if i.isdigit(): 17 | stack.append(i) 18 | elif i.isspace(): 19 | continue 20 | else: 21 | val1 = stack.pop() 22 | val2 = stack.pop() 23 | stack.append(str(eval(val2 + i + val1))) 24 | 25 | return int(stack.pop()) 26 | -------------------------------------------------------------------------------- /Codepath/Week5-StacksAndQueues/python/isStackSequenceValid.py: -------------------------------------------------------------------------------- 1 | # Given push and pop sequences with distinct values, check if this could have been the result of a sequence of push and pop operations on an initially empty stack. 2 | # 3 | # Example: 4 | # Input: pushed = [1, 2, 3, 4, 5], popped = [4, 5, 3, 2, 1] 5 | # Output: True 6 | # 7 | # Following sequence can be performed: 8 | # push(1), push(2), push(3), push(4), pop() -> 4, 9 | # push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1 10 | # 11 | # pushed = [1, 2, 3, 4, 5], popped = [4, 3, 5, 1, 2] 12 | # Output: False 13 | # 14 | # Expanation: 1 can't be popped before 2. 15 | def isStackSequenceValid(pushed, popped): 16 | if len(pushed) != len(popped): 17 | return False 18 | j = 0 19 | stack = [] 20 | 21 | for x in pushed: 22 | stack.append(x) 23 | 24 | while stack and j < len(popped) and stack[-1] == popped[j]: 25 | stack.pop() 26 | j = j + 1 27 | 28 | return j == len(popped) 29 | -------------------------------------------------------------------------------- /Codepath/Week6-StringsAndArrays/java/bigIntegerIncrement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Problem: Implement a function to increment an arbitrary precision integer represented in the form of an array, where each element in the array corresponds to a digit. 3 | * 4 | * Examples: 5 | * Input: [1,2,3] 6 | * Output: [1,2,4] 7 | 8 | * Explanation: 123 + 1 = 124 9 | * 10 | * Input: [5,8,9] 11 | * Output: [5,9,0] 12 | 13 | * Explanation: 589 + 1 = 590 14 | */ 15 | public int[] bigIntegerIncrement(int[] arr) 16 | { 17 | int len = arr.length; 18 | boolean allNines = true; 19 | for(int i = 0; i < len; i++) 20 | { 21 | if(arr[i] != 9) 22 | { 23 | allNines = false; 24 | break; 25 | } 26 | } 27 | 28 | int[] result = (allNines == true) ? new int[len + 1] : new int[len]; 29 | 30 | int sum = arr[len - 1] + 1; 31 | int carry = sum / 10; 32 | result[result.length - 1] = sum % 10; 33 | 34 | int ptr1 = len - 2; 35 | int ptr2 = result.length - 2; 36 | 37 | while(ptr1 >= 0) 38 | { 39 | sum = arr[ptr1] + carry; 40 | carry = sum / 10; 41 | result[ptr2] = sum % 10; 42 | ptr1--; 43 | ptr2--; 44 | } 45 | 46 | if(allNines) 47 | result[0] = 1; 48 | 49 | return result; 50 | } 51 | -------------------------------------------------------------------------------- /Codepath/Week6-StringsAndArrays/java/encode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Problem: Given an input string, write a function that returns the run-length encoded string for the input string. 3 | * 4 | * Example: 5 | * Input: "wwwwaaadexxxxxx" 6 | * Output: "w4a3d1e1x6" 7 | */ 8 | public String encode(String s) 9 | { 10 | char[] arr = s.toCharArray(); 11 | StringBuilder sb = new StringBuilder(); 12 | 13 | int i = 0, j = 1; 14 | while(i < arr.length) 15 | { 16 | int count = 1; 17 | while(j < arr.length && arr[i] == arr[j]) 18 | { 19 | j++; 20 | count++; 21 | } 22 | 23 | if(count > 1) 24 | { 25 | sb.append(count); 26 | sb.append(arr[i]); 27 | } 28 | else{ 29 | sb.append(arr[i]); 30 | } 31 | 32 | if(sb.length() > s.length()) 33 | { 34 | break; 35 | } 36 | 37 | i = j; 38 | j = i + 1; 39 | } 40 | 41 | return sb.toString(); 42 | } 43 | -------------------------------------------------------------------------------- /Codepath/Week6-StringsAndArrays/java/subArraySort.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Problem: Given an array, find the length of the smallest subarray in it which when sorted will sort the whole array. 3 | * 4 | * Example 1: 5 | * Input: [1, 2, 5, 3, 7, 10, 9, 12] 6 | * Output: 5 7 | * Explanation: We need to sort only the subarray [5, 3, 7, 10, 9] to make the whole array sorted 8 | * 9 | * Example 2: 10 | * Input: [1, 3, 2, 0, -1, 7, 10] 11 | * Output: 5 12 | * Explanation: We need to sort only the subarray [1, 3, 2, 0, -1] to make the whole array sorted 13 | */ 14 | public int sort(int[] arr) 15 | { 16 | //Step 1: Initialize the two Pointers 17 | int low = 0, high = arr.length - 1; 18 | 19 | // Step 2: find the first number out of sorting order from the beginning 20 | while (low < arr.length - 1 && arr[low] <= arr[low + 1]) 21 | low++; 22 | 23 | if (low == arr.length - 1) // if the array is sorted 24 | return 0; 25 | 26 | // Step 3: find the first number out of sorting order from the end 27 | while (high > 0 && arr[high] >= arr[high - 1]) 28 | high--; 29 | 30 | // Step 4: find the maximum and minimum of the subarray 31 | int subarrayMax = Integer.MIN_VALUE, subarrayMin = Integer.MAX_VALUE; 32 | for (int k = low; k <= high; k++) { 33 | subarrayMax = Math.max(subarrayMax, arr[k]); 34 | subarrayMin = Math.min(subarrayMin, arr[k]); 35 | } 36 | 37 | // Step 5: extend the subarray to include any number which is bigger than the minimum of the subarray 38 | while (low > 0 && arr[low - 1] > subarrayMin) 39 | low--; 40 | 41 | // Step 6: extend the subarray to include any number which is smaller than the maximum of the subarray 42 | while (high < arr.length - 1 && arr[high + 1] < subarrayMax) 43 | high++; 44 | 45 | return high - low + 1; 46 | } 47 | -------------------------------------------------------------------------------- /Codepath/Week6-StringsAndArrays/python/bigIntegerIncrement.py: -------------------------------------------------------------------------------- 1 | # Problem: Implement a function to increment an arbitrary precision integer represented in the form of an array, where each element in the array corresponds to a digit. 2 | # 3 | # Examples: 4 | # Input: [1,2,3] 5 | # Output: [1,2,4] 6 | # 7 | # Explanation: 123 + 1 = 124 8 | # 9 | # Input: [5,8,9] 10 | # Output: [5,9,0] 11 | # 12 | # Explanation: 589 + 1 = 590 13 | def bigIntegerIncrement(arr): 14 | arrLen = len(arr) 15 | allNines = true 16 | for i in arr: 17 | if i != 9: 18 | allNines = false 19 | break 20 | 21 | result = [None]*(arrLen + 1) if allNines else [None]*arrLen 22 | 23 | sum = arr[arrLen - 1] + 1 24 | carry = sum / 10 25 | result[len(result) - 1] = sum % 10 26 | 27 | ptr1 = arrLen - 2 28 | ptr2 = len(result) - 2 29 | 30 | while ptr1 >= 0: 31 | sum = arr[ptr1] + carry 32 | carry = sum / 10 33 | result[ptr2] = sum % 10 34 | ptr1 -= 1 35 | ptr2 -= 1 36 | 37 | if allNines: 38 | result[0] = 1 39 | 40 | return result 41 | -------------------------------------------------------------------------------- /Codepath/Week6-StringsAndArrays/python/encode.py: -------------------------------------------------------------------------------- 1 | # Problem: Given an input string, write a function that returns the run-length encoded string for the input string. 2 | # 3 | # Example: 4 | # Input: "wwwwaaadexxxxxx" 5 | # Output: "w4a3d1e1x6" 6 | def compute(s): 7 | arr = list(s) 8 | result = [] 9 | 10 | i = 0 11 | j = 1 12 | while i < len(arr): 13 | count = 1 14 | while j < len(arr) and arr[i] == arr[j]: 15 | j += 1 16 | count += 1 17 | 18 | if count > 1: 19 | result.append(count) 20 | result.append(arr[i]) 21 | else: 22 | result.append(arr[i]) 23 | 24 | if len(result) > len(s): 25 | break 26 | 27 | i = j 28 | j = i + 1 29 | 30 | return ''.join(arr) 31 | -------------------------------------------------------------------------------- /Codepath/Week6-StringsAndArrays/python/subArraySort.py: -------------------------------------------------------------------------------- 1 | # Problem: Given an array, find the length of the smallest subarray in it which when sorted will sort the whole array. 2 | # 3 | # Example 1: 4 | # Input: [1, 2, 5, 3, 7, 10, 9, 12] 5 | # Output: 5 6 | # Explanation: We need to sort only the subarray [5, 3, 7, 10, 9] to make the whole array sorted 7 | # 8 | # Example 2: 9 | # Input: [1, 3, 2, 0, -1, 7, 10] 10 | # Output: 5 11 | # Explanation: We need to sort only the subarray [1, 3, 2, 0, -1] to make the whole array sorted 12 | def sort(arr): 13 | low = 0 14 | high = len(arr) - 1 15 | # find the first number out of sorting order from the beginning 16 | while low < len(arr) - 1 and arr[low] <= arr[low + 1]: 17 | low += 1 18 | 19 | if low == len(arr) - 1: # if the array is sorted 20 | return 0 21 | 22 | # find the first number out of sorting order from the end 23 | while high > 0 and arr[high] >= arr[high - 1]: 24 | high -= 1 25 | 26 | # find the maximum and minimum of the subarray 27 | subarrayMax = -float('inf') 28 | subarrayMin = float('inf') 29 | for k in range(low, high): 30 | subarrayMax = max(subarrayMax, arr[k]) 31 | subarrayMin = min(subarrayMin, arr[k]) 32 | 33 | # extend the subarray to include any number which is bigger than the minimum of the subarray 34 | while low > 0 and arr[low - 1] > subarrayMin: 35 | low -= 1 36 | # extend the subarray to include any number which is smaller than the maximum of the subarray 37 | while high < len(arr) - 1 and arr[high + 1] < subarrayMax: 38 | high += 1 39 | 40 | return high - low + 1 41 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rspec' 4 | gem 'ruby-debug-ide' 5 | gem 'debase' 6 | gem 'pry-byebug' 7 | gem 'algorithms' 8 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | algorithms (0.6.1) 5 | byebug (11.1.1) 6 | coderay (1.1.2) 7 | debase (0.2.4.1) 8 | debase-ruby_core_source (>= 0.10.2) 9 | debase-ruby_core_source (0.10.7) 10 | diff-lcs (1.3) 11 | method_source (0.9.2) 12 | pry (0.12.2) 13 | coderay (~> 1.1.0) 14 | method_source (~> 0.9.0) 15 | pry-byebug (3.8.0) 16 | byebug (~> 11.0) 17 | pry (~> 0.10) 18 | rake (13.0.1) 19 | rspec (3.7.0) 20 | rspec-core (~> 3.7.0) 21 | rspec-expectations (~> 3.7.0) 22 | rspec-mocks (~> 3.7.0) 23 | rspec-core (3.7.0) 24 | rspec-support (~> 3.7.0) 25 | rspec-expectations (3.7.0) 26 | diff-lcs (>= 1.2.0, < 2.0) 27 | rspec-support (~> 3.7.0) 28 | rspec-mocks (3.7.0) 29 | diff-lcs (>= 1.2.0, < 2.0) 30 | rspec-support (~> 3.7.0) 31 | rspec-support (3.7.0) 32 | ruby-debug-ide (0.7.0) 33 | rake (>= 0.8.1) 34 | 35 | PLATFORMS 36 | ruby 37 | 38 | DEPENDENCIES 39 | algorithms 40 | debase 41 | pry-byebug 42 | rspec 43 | ruby-debug-ide 44 | 45 | BUNDLED WITH 46 | 1.17.3 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Answers/Notes to [Cracking the Coding Interview, 6th Ed.](http://www.crackingthecodinginterview.com) 2 | 3 | **See all my LinkedIn [Data Structures & Algorithms posts](https://www.linkedin.com/search/results/all/?keywords=%23raymonddsa&sid=4YJ).** 4 | 5 | Join our Slack group **[LinkedIn Junior Engineers](https://bit.ly/3EKzyDm)**. 6 | 7 | We don't have weekly meetings now. If I have time in the future, I may restart this study group. 8 | 9 | --------------- 10 | See all my **[LeetCode solutions](https://github.com/rayning0/ctci/tree/master/leetcode)**. 11 | My **[Codility solutions](https://github.com/rayning0/codility)**. 12 | My **[Project Euler solutions](https://github.com/rayning0/ProjectEuler-and-Algorithms/tree/master/euler)**. 13 | My **[HackerRank solutions](https://github.com/rayning0/ctci/tree/master/hackerrank)**. 14 | 15 | In January 2020, we began solving the 150 coding questions from this course: **[Grokking the Coding Interview](https://www.educative.io/courses/grokking-the-coding-interview)**, as I explain [here](https://www.linkedin.com/posts/raymond-gan-0ba8011_grokking-the-coding-interview-patterns-for-activity-6630206560280944641-HO8Q) and [here](https://linkedin-jr-engineers.slack.com/archives/CDDPLUHQC/p1576004249125300). Buy the course for $79 to see it all. It's optional to buy the course. **[See all my coding answers for it](https://github.com/rayning0/ctci/tree/master/gtci)**. 16 | 17 | --------------- 18 | See [our YouTube channel](https://www.youtube.com/channel/UCXd8p77ZB0sQbtk6mDyeqkw/about) for video of our group meetings. I don't update this much. You should come to our meetings. 19 | 20 | See my 2.5 hour YouTube video interview: **[How to be competitive for software jobs](https://youtu.be/WqFOTeiSeEY)**, which covers **[my 5 LinkedIn articles for bootcamp grads](https://www.linkedin.com/in/raymond-gan-i-do-not-seek-a-job-0ba8011/recent-activity/posts/)**. 21 | 22 | This group codes through company code challenge problems and all 189 coding interview questions in Gayle McDowell's book "Cracking the Coding Interview" together. We’ll try to solve 1+ company code challenge per meeting. To post code challenges for our group to solve, put them in our **[private Zoom Slack channel](https://linkedin-jr-engineers.slack.com/archives/GN87TNSH0)**. Use any computer language you wish. 23 | 24 | Gayle McDowell's [crowdsourced CTCI solutions](https://github.com/careercup/CtCI-6th-Edition), by programming language. 25 | 26 | Most software engineers study this book to interview at FAANG companies: Facebook, Apple, Amazon, Netflix, Google, Microsoft, etc. 27 | 28 | I made these Slack channels for us. Please join them all: 29 | 30 | - [#system-design-interviews](https://linkedin-jr-engineers.slack.com/archives/CTTP68RPZ) System Design Interviews 31 | - [#leetcode](https://linkedin-jr-engineers.slack.com/messages/CL9UDQ4LS) LeetCode Problems 32 | - [#hackerrank](https://linkedin-jr-engineers.slack.com/archives/CR8EP9AMU) HackerRank Problems 33 | - [#project-euler](https://linkedin-jr-engineers.slack.com/archives/CQJ63PMEJ) Project Euler Problems 34 | - [#ctci-big-o](https://linkedin-jr-engineers.slack.com/messages/CKTPDEEN6/) Big O Time/Space Complexity. Chapter VI. 35 | - [#ctci-ch01](https://linkedin-jr-engineers.slack.com/messages/CL7AFTC6A/) Arrays, Strings, and Hashes 36 | - [#ctci-ch02](https://linkedin-jr-engineers.slack.com/messages/CL930575L/) Linked Lists 37 | - [#ctci-ch03](https://linkedin-jr-engineers.slack.com/messages/CL930EVPY/) Stacks and Queues 38 | - [#ctci-ch04](https://linkedin-jr-engineers.slack.com/messages/CL0HVSS49/) Trees and Graphs 39 | - [#ctci-ch08](https://linkedin-jr-engineers.slack.com/messages/CMY2R0VBK/) Recursion and Dynamic Programming 40 | - [#eloquentjavascript](https://linkedin-jr-engineers.slack.com/messages/CDD6ZFLTS/) on JavaScript and that book. 41 | - [#java](https://linkedin-jr-engineers.slack.com/archives/CQSD35KM1) Java 42 | - [#python](https://linkedin-jr-engineers.slack.com/archives/CR3DDHPHS) Python 43 | - [#ruby](https://linkedin-jr-engineers.slack.com/archives/CQR3NBUJE) Ruby 44 | 45 | - Use the [#pair-programming](https://linkedin-jr-engineers.slack.com/messages/CL6UDFVSR/) channel to ask for help or offer to work together with others. 46 | - Please [#introduce-yourself](https://linkedin-jr-engineers.slack.com/messages/CDG6ZSWMU/) and talk about your [#job-search](https://linkedin-jr-engineers.slack.com/messages/CDE3RK8QY/). Don't use the [#general](https://linkedin-jr-engineers.slack.com/messages/CDDPLUHQC/) channel to introduce yourself. 47 | 48 | CTCI's [official Facebook group](https://www.facebook.com/groups/ctciofficial/). 49 | 50 | CTCI's [official Slack group](https://crackinginterview.slack.com). 51 | 52 | [VS Code](https://code.visualstudio.com/docs/languages/java) is my code editor. I installed its [Java Extension Pack](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-pack). 53 | 54 | I cloned [Gayle's GitHub solutions](https://github.com/careercup/CtCI-6th-Edition) and opened the /Java folder in VS Code. It instantly told me what to install and let me start running/debugging code with breakpoints, with NO configuration! Super easy. 55 | 56 | --- 57 | 58 | If you forked this GitHub repo to your laptop, here's **how to easily keep your fork up to date with mine as I add new code:** 59 | 60 | In directory of your `/ctci` folder in the terminal, type: 61 | 62 | 1. `git remote add upstream git@github.com:rayning0/ctci.git` 63 | 64 | 2. `git fetch upstream` 65 | 3. `git pull upstream master` 66 | 4. `git push -f` 67 | 68 | Line 1 adds a [remote repo](https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes) called "upstream" to your fork, pointing to my GitHub repo. 69 | 70 | Lines 2-4 update what "upstream" means, pulls the latest code from my /ctci repo, then [force pushes](https://evilmartians.com/chronicles/git-push---force-and-how-to-deal-with-it) it up to YOUR repo. 71 | 72 | Since I don't want to type all these lines, I added them to my [`.bash_profile`](https://natelandau.com/my-mac-osx-bash_profile/): 73 | 74 | ``` 75 | function grau { # update fork (part 1) 76 | git remote add upstream $1 77 | } 78 | 79 | function gupdate { # update fork (part 2) 80 | git fetch upstream 81 | git pull upstream master 82 | git push -f 83 | } 84 | ``` 85 | 86 | You only need to type `grau git@github.com:rayning0/ctci.git` once. 87 | 88 | In the future, as I add new code to my /ctci repo, just `cd` to YOUR /ctci fork of mine and type `gupdate`. It will automatically pull my latest code. 89 | 90 | If you don't have a `.bash_profile`, create this file in your home directory. After editing it, run the file by doing [`source ~/.bash_profile`](https://stackoverflow.com/questions/4608187/how-to-reload-bash-profile-from-the-command-line). 91 | 92 | **By Raymond Gan** 93 | -------------------------------------------------------------------------------- /add_numbers_spec.rb: -------------------------------------------------------------------------------- 1 | # Add Big Numbers 2 | # QUESTION: Take 2 positive numbers as strings and return their sum. 3 | # Must work for VERY LARGE positive numbers. 4 | # You may not just convert strings to numbers and add them. 5 | 6 | # STRATEGY: 7 | # 1. Split string by decimal point ('.') to separate integer from decimal part. Get 2 arrays of characters. One for integer. One for decimals. 8 | # 2. Loop through each character and, based on index in array, multiply its value by the corresponding power of 10. 9 | # 3. Keep a running sum of each. 10 | 11 | # Better answer than mine, which does NOT require 'bigdecimal'. It carries the 10s and adds them: 12 | # https://www.geeksforgeeks.org/sum-two-large-numbers/ 13 | 14 | # Ex: '512.34' + '25.67' 15 | 16 | # 512.34 = 17 | # 5 * 10**2 + 18 | # 1 * 10**1 + 19 | # 2 * 10**0 + 20 | # 3 * 10**-1 + 21 | # 4 * 10**-2 22 | 23 | # index exponent 24 | # ________________ 25 | # 0 2 26 | # 1 1 27 | # 2 0 28 | 29 | # 'bigdecimal' gives us PERFECT PRECISION arithmetic for long decimal numbers: 30 | # https://www.rubyguides.com/2016/07/numbers-in-ruby/ 31 | require 'bigdecimal' 32 | 33 | def add(num1, num2) 34 | int1, dec1 = num1.split('.') 35 | int2, dec2 = num2.split('.') 36 | sum = 0 37 | 38 | max_length = [int1.length, int2.length, dec1.length, dec2.length].max 39 | 40 | max_length.times do |dec_index| 41 | int_index = -1 - dec_index 42 | int1 = '0' + int1 if int1[int_index].nil? 43 | int2 = '0' + int2 if int2[int_index].nil? 44 | dec1 += '0' if dec1[dec_index].nil? 45 | dec2 += '0' if dec2[dec_index].nil? 46 | puts "int1: #{int1}, int2: #{int2} | dec1: #{dec1}, dec2: #{dec2}" 47 | 48 | sum += (BigDecimal(int1[int_index]) + BigDecimal(int2[int_index])) * 10**dec_index 49 | puts "dec_index: #{dec_index}, int_index: #{int_index}" 50 | puts "int_sum: #{sum}" 51 | 52 | sum += (BigDecimal(dec1[dec_index]) + BigDecimal(dec2[dec_index])) * 10**int_index 53 | puts "int_sum + dec_sum: #{sum}\n\n" 54 | end 55 | puts "----------------------" 56 | sum 57 | end 58 | 59 | describe '#add' do 60 | it 'adds 2 small numbers' do 61 | expect(add('512.349', '25.67')).to eq 538.019 62 | expect(add('25.67', '512.349')).to eq 538.019 63 | end 64 | 65 | it 'adds 2 medium sized numbers' do 66 | expect(add('23423.97655', '0.3668558')).to eq 23424.3434058 67 | end 68 | 69 | it 'adds 2 super big numbers, beyond normal size limits' do 70 | a = '95745735635754675467.4452345234546' 71 | b = '5464564596999.5534733' 72 | puts "sum of BigDecimals: #{BigDecimal(a) + BigDecimal(b)}" 73 | # sum = 95745741100319272466.9987078234546 74 | expect(add(a, b)).to eq(BigDecimal(a) + BigDecimal(b)) 75 | end 76 | end 77 | 78 | # OUTPUT: 79 | # 80 | # int1: 512, int2: 25 | dec1: 349, dec2: 67 81 | # dec_index: 0, int_index: -1 82 | # int_sum: 0.7e1 83 | # int_sum + dec_sum: 0.79e1 84 | 85 | # int1: 512, int2: 25 | dec1: 349, dec2: 67 86 | # dec_index: 1, int_index: -2 87 | # int_sum: 0.379e2 88 | # int_sum + dec_sum: 0.3801e2 89 | 90 | # int1: 512, int2: 025 | dec1: 349, dec2: 670 91 | # dec_index: 2, int_index: -3 92 | # int_sum: 0.53801e3 93 | # int_sum + dec_sum: 0.538019e3 94 | 95 | # ---------------------- 96 | # int1: 25, int2: 512 | dec1: 67, dec2: 349 97 | # dec_index: 0, int_index: -1 98 | # int_sum: 0.7e1 99 | # int_sum + dec_sum: 0.79e1 100 | 101 | # int1: 25, int2: 512 | dec1: 67, dec2: 349 102 | # dec_index: 1, int_index: -2 103 | # int_sum: 0.379e2 104 | # int_sum + dec_sum: 0.3801e2 105 | 106 | # int1: 025, int2: 512 | dec1: 670, dec2: 349 107 | # dec_index: 2, int_index: -3 108 | # int_sum: 0.53801e3 109 | # int_sum + dec_sum: 0.538019e3 110 | 111 | # ---------------------- 112 | # int1: 23423, int2: 0 | dec1: 97655, dec2: 3668558 113 | # dec_index: 0, int_index: -1 114 | # int_sum: 0.3e1 115 | # int_sum + dec_sum: 0.42e1 116 | 117 | # int1: 23423, int2: 00 | dec1: 97655, dec2: 3668558 118 | # dec_index: 1, int_index: -2 119 | # int_sum: 0.242e2 120 | # int_sum + dec_sum: 0.2433e2 121 | 122 | # int1: 23423, int2: 000 | dec1: 97655, dec2: 3668558 123 | # dec_index: 2, int_index: -3 124 | # int_sum: 0.42433e3 125 | # int_sum + dec_sum: 0.424342e3 126 | 127 | # int1: 23423, int2: 0000 | dec1: 97655, dec2: 3668558 128 | # dec_index: 3, int_index: -4 129 | # int_sum: 0.3424342e4 130 | # int_sum + dec_sum: 0.34243433e4 131 | 132 | # int1: 23423, int2: 00000 | dec1: 97655, dec2: 3668558 133 | # dec_index: 4, int_index: -5 134 | # int_sum: 0.234243433e5 135 | # int_sum + dec_sum: 0.234243434e5 136 | 137 | # int1: 023423, int2: 000000 | dec1: 976550, dec2: 3668558 138 | # dec_index: 5, int_index: -6 139 | # int_sum: 0.234243434e5 140 | # int_sum + dec_sum: 0.23424343405e5 141 | 142 | # int1: 0023423, int2: 0000000 | dec1: 9765500, dec2: 3668558 143 | # dec_index: 6, int_index: -7 144 | # int_sum: 0.23424343405e5 145 | # int_sum + dec_sum: 0.234243434058e5 146 | 147 | # ---------------------- 148 | # sum of BigDecimals: 0.957457411003192724669987078234546e20 149 | # int1: 95745735635754675467, int2: 5464564596999 | dec1: 4452345234546, dec2: 5534733 150 | # dec_index: 0, int_index: -1 151 | # int_sum: 0.16e2 152 | # int_sum + dec_sum: 0.169e2 153 | 154 | # int1: 95745735635754675467, int2: 5464564596999 | dec1: 4452345234546, dec2: 5534733 155 | # dec_index: 1, int_index: -2 156 | # int_sum: 0.1669e3 157 | # int_sum + dec_sum: 0.16699e3 158 | 159 | # int1: 95745735635754675467, int2: 5464564596999 | dec1: 4452345234546, dec2: 5534733 160 | # dec_index: 2, int_index: -3 161 | # int_sum: 0.146699e4 162 | # int_sum + dec_sum: 0.1466998e4 163 | 164 | # int1: 95745735635754675467, int2: 5464564596999 | dec1: 4452345234546, dec2: 5534733 165 | # dec_index: 3, int_index: -4 166 | # int_sum: 0.12466998e5 167 | # int_sum + dec_sum: 0.124669986e5 168 | 169 | # int1: 95745735635754675467, int2: 5464564596999 | dec1: 4452345234546, dec2: 5534733 170 | # dec_index: 4, int_index: -5 171 | # int_sum: 0.1724669986e6 172 | # int_sum + dec_sum: 0.1724669987e6 173 | 174 | # int1: 95745735635754675467, int2: 5464564596999 | dec1: 4452345234546, dec2: 5534733 175 | # dec_index: 5, int_index: -6 176 | # int_sum: 0.12724669987e7 177 | # int_sum + dec_sum: 0.1272466998707e7 178 | 179 | # int1: 95745735635754675467, int2: 5464564596999 | dec1: 4452345234546, dec2: 5534733 180 | # dec_index: 6, int_index: -7 181 | # int_sum: 0.9272466998707e7 182 | # int_sum + dec_sum: 0.92724669987078e7 183 | 184 | # int1: 95745735635754675467, int2: 5464564596999 | dec1: 4452345234546, dec2: 55347330 185 | # dec_index: 7, int_index: -8 186 | # int_sum: 0.1192724669987078e9 187 | # int_sum + dec_sum: 0.11927246699870782e9 188 | 189 | # int1: 95745735635754675467, int2: 5464564596999 | dec1: 4452345234546, dec2: 553473300 190 | # dec_index: 8, int_index: -9 191 | # int_sum: 0.131927246699870782e10 192 | # int_sum + dec_sum: 0.1319272466998707823e10 193 | 194 | # int1: 95745735635754675467, int2: 5464564596999 | dec1: 4452345234546, dec2: 5534733000 195 | # dec_index: 9, int_index: -10 196 | # int_sum: 0.10319272466998707823e11 197 | # int_sum + dec_sum: 0.103192724669987078234e11 198 | 199 | # int1: 95745735635754675467, int2: 5464564596999 | dec1: 4452345234546, dec2: 55347330000 200 | # dec_index: 10, int_index: -11 201 | # int_sum: 0.1003192724669987078234e12 202 | # int_sum + dec_sum: 0.10031927246699870782345e12 203 | 204 | # int1: 95745735635754675467, int2: 5464564596999 | dec1: 4452345234546, dec2: 553473300000 205 | # dec_index: 11, int_index: -12 206 | # int_sum: 0.110031927246699870782345e13 207 | # int_sum + dec_sum: 0.1100319272466998707823454e13 208 | 209 | # int1: 95745735635754675467, int2: 5464564596999 | dec1: 4452345234546, dec2: 5534733000000 210 | # dec_index: 12, int_index: -13 211 | # int_sum: 0.11100319272466998707823454e14 212 | # int_sum + dec_sum: 0.111003192724669987078234546e14 213 | 214 | # int1: 95745735635754675467, int2: 05464564596999 | dec1: 44523452345460, dec2: 55347330000000 215 | # dec_index: 13, int_index: -14 216 | # int_sum: 0.411003192724669987078234546e14 217 | # int_sum + dec_sum: 0.411003192724669987078234546e14 218 | 219 | # int1: 95745735635754675467, int2: 005464564596999 | dec1: 445234523454600, dec2: 553473300000000 220 | # dec_index: 14, int_index: -15 221 | # int_sum: 0.7411003192724669987078234546e15 222 | # int_sum + dec_sum: 0.7411003192724669987078234546e15 223 | 224 | # int1: 95745735635754675467, int2: 0005464564596999 | dec1: 4452345234546000, dec2: 5534733000000000 225 | # dec_index: 15, int_index: -16 226 | # int_sum: 0.57411003192724669987078234546e16 227 | # int_sum + dec_sum: 0.57411003192724669987078234546e16 228 | 229 | # int1: 95745735635754675467, int2: 00005464564596999 | dec1: 44523452345460000, dec2: 55347330000000000 230 | # dec_index: 16, int_index: -17 231 | # int_sum: 0.457411003192724669987078234546e17 232 | # int_sum + dec_sum: 0.457411003192724669987078234546e17 233 | 234 | # int1: 95745735635754675467, int2: 000005464564596999 | dec1: 445234523454600000, dec2: 553473300000000000 235 | # dec_index: 17, int_index: -18 236 | # int_sum: 0.7457411003192724669987078234546e18 237 | # int_sum + dec_sum: 0.7457411003192724669987078234546e18 238 | 239 | # int1: 95745735635754675467, int2: 0000005464564596999 | dec1: 4452345234546000000, dec2: 5534733000000000000 240 | # dec_index: 18, int_index: -19 241 | # int_sum: 0.57457411003192724669987078234546e19 242 | # int_sum + dec_sum: 0.57457411003192724669987078234546e19 243 | 244 | # int1: 95745735635754675467, int2: 00000005464564596999 | dec1: 44523452345460000000, dec2: 55347330000000000000 245 | # dec_index: 19, int_index: -20 246 | # int_sum: 0.957457411003192724669987078234546e20 247 | # int_sum + dec_sum: 0.957457411003192724669987078234546e20 -------------------------------------------------------------------------------- /chVI/Ch VI Big O.txt: -------------------------------------------------------------------------------- 1 | See Gayle McDowell's explanation of Big O: https://www.youtube.com/watch?v=v4cd1O4zkGw 2 | 3 | SPACE COMPLEXITY: See total size of NEW variables we're allocating. Ignore space taken by inputs. 4 | 5 | - Simple recursive call: O(n) time, O(n) space 6 | - pairSumSequence(): O(n) time, O(1) space 7 | 8 | TIME COMPLEXITY. Big O runtime (asymptotic): 9 | 10 | - Time to paint fence that's w x h meters: O(wh) 11 | 12 | See Gayle's clear explanation of Quicksort: https://www.youtube.com/watch?v=SLauY6PpjW4 13 | Quicksort (https://en.wikipedia.org/wiki/Quicksort, https://www.geeksforgeeks.org/quick-sort/) 14 | Best Case: O(n) 15 | Worst Case: O(n^2) 16 | Expected Case: O(n log n) 17 | 18 | Drop the Constants: O(2n) is same as O(n) 19 | Drop non-dominant exponential terms: 20 | - O(n^2 + n) is same as O(n^2) 21 | - O(n + log n) is same as O(n) 22 | - O(5 * 2^n + 1000 * n^100) is same as O(2^n) 23 | 24 | Ranked worst to best: 25 | O(n!) > O(2^n) > O(n^2) > O(n log n) > O(n)<--linear time > O(log n) > O(1)<--constant time 26 | 27 | Multipart algorithms: Add vs. Multiply: 28 | - If algorithm is "do A, then when all done, do B", you ADD runtimes: O(A + B) 29 | - If algorithm is "do A for each time you do B", you MULTIPLE runtimes: O(A * B) 30 | 31 | Amortized Time: 32 | https://medium.com/@satorusasozaki/amortized-time-in-the-time-complexity-of-an-algorithm-6dd9a5d38045 33 | 34 | Amortized time expresses time complexity when an algorithm has the very bad time complexity only once in a while besides the time complexity that happens most of time. Example: An ArrayList, a data structure that contains an array that gets extended when it fills up. Other definition from Stack Overflow is average time taken per operation, if you do many operations. 35 | 36 | When ArrayList hits its array capacity, it creates new array with double the size of old array and copies all old array items to the new array. In ArrayList, two time complexities exist: O(1) for insertion and O(n) to copy old to new bigger array. 37 | 38 | If internal array capacity starts with 1, capacities will be 1, 2, 4, 8, 16,...X when it hits capacity and gets doubled. It takes 1, 2, 4, 8 16,…X items to copy into the new array. So time complexity will be 1 + 2 + 4 + 8 + 16 +… X. If you think backwards from X, this will be X + X/2 + X/4 + X/8… + 1 = approximately 2X. 39 | 40 | Thus: X insertions take O(2X) time, and the amortized time for each insertion is O(1). 41 | 42 | https://en.wikipedia.org/wiki/Amortized_analysis#Dynamic_array 43 | 44 | https://mortoray.com/2014/08/11/what-is-amortized-time/ 45 | Amortized time looks at an algorithm from the viewpoint of total running time rather than individual operations. We don’t care how long one insert takes, but rather the average time of all the calls to insert. 46 | 47 | Log N Runtimes: 48 | - Ex: binary search 49 | - Whenever the number of elements in the problem space get HALVED in each loop, it's probably O(log n) 50 | - Ex: Finding element in balanced binary search tree takes O(log n) 51 | - Whether it's base 2 or base 10 does not matter, because they are proportional to each other. 52 | 53 | Recursive Runtimes: 54 | - Ex: Recursive Fibonacci sequence with 2 branches: O(2^n) 55 | - When recursive function makes multiple calls, it's O(branches^depth) 56 | __________ 57 | Ex 1: 2 consecutive loops. O(2n) = O(n) time, O(1) space 58 | Ex 2: 2 nested loops, 2nd loop starts at index 0: O(n^2) time, O(1) space 59 | Ex 3: 2 nested loops, 2nd loop starts at index i + 1: O(n^2) time, O(1) space 60 | Ex 4: 2 nested loops, 2 different arrays: O(ab) time, O(1) space 61 | Ex 5: 3 nested loops, 3rd loop has constant size: O(ab) time, O(1) space 62 | Ex 6: Reversing an array: O(n) time, O(1) space 63 | Ex 7: O(n + p), O(2n), O(n + log n) all same as O(n) 64 | 65 | Ex 8: Take array of strings, sort each string, then sort the full array. TRICKY!!! 66 | Time complexity: O(as(log s + log a)). I'm not sure about space complexity. Quicksort takes O(log(n)) space. 67 | My best guess of Big O space complexity is O(s + log a): https://linkedin-jr-engineers.slack.com/archives/CKTPDEEN6/p1562963534004400 68 | 69 | Ex 9: Sum of value of all nodes in balanced binary search tree: O(n) time, O(1) space 70 | Ex 10: Is a number prime? O(sqrt(n)) time, O(1) space 71 | Ex 11: Recursive factorial (n!) function: O(n) time, O(1) space 72 | -------------------------------------------------------------------------------- /chVI/example12.java: -------------------------------------------------------------------------------- 1 | // Count all permutations of a string 2 | class Example12 { 3 | public static void permutation(String str) { 4 | permutation(str, ""); 5 | } 6 | 7 | public static void permutation(String str, String prefix) { 8 | if (str.length() == 0) { 9 | System.out.println(prefix); 10 | } else { 11 | for (int i = 0; i < str.length(); i++) { 12 | String rem = str.substring(0, i) + str.substring(i + 1); 13 | System.out.println(i + ": " + str.substring(0, i) + ", " + str.substring(i + 1)); 14 | System.out.println(rem + ", " + prefix + str.charAt(i)); 15 | permutation(rem, prefix + str.charAt(i)); 16 | } 17 | } 18 | } 19 | 20 | public static void main(String args[]) { 21 | permutation("cat"); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /chVI/hi.rb: -------------------------------------------------------------------------------- 1 | # procedure BFS(G,start_v): 2 | # let Q be a queue 3 | # label start_v as discovered 4 | # Q.enqueue(start_v) 5 | # while Q is not empty 6 | # v = Q.dequeue() 7 | # if v is the goal: 8 | # return v 9 | # for all edges from v to w in G.adjacentEdges(v) do 10 | # if w is not labeled as discovered: 11 | # label w as discovered 12 | # w.parent = v 13 | # Q.enqueue(w) 14 | 15 | def run 16 | a = [ 17 | [1, 0, 0, 1, 0], 18 | [1, 0, 1, 0, 0], 19 | [0, 0, 1, 0, 1], 20 | [1, 0, 1, 0, 1], 21 | [1, 0, 1, 1, 0] 22 | ] 23 | 24 | @height = a.length 25 | @width = a.first.length 26 | visited = Array.new(@height) { Array.new(@width) } 27 | 28 | starting_node = [0, 0] 29 | queue = [] # element of 2D array we're currently evaluating 30 | queue << starting_node 31 | river_length = 0 32 | got_zero = false 33 | rivers = [] 34 | 35 | while queue.any? 36 | current_node = queue.shift 37 | 38 | node_and_adjacents(current_node).each do |adjacent_node| 39 | next if adjacent_node.nil? 40 | x, y = adjacent_node.first, adjacent_node.last 41 | next if visited[x][y] 42 | puts "node: #{adjacent_node}" 43 | 44 | puts "a[#{x}][#{y}] = #{a[x][y]}" 45 | if a[x][y] == 1 46 | river_length += 1 47 | got_zero = false 48 | else 49 | if got_zero == true && river_length > 0 # river has ende 50 | rivers << river_length 51 | got_zero = false 52 | river_length = 0 53 | binding.pry 54 | else 55 | got_zero = true 56 | end 57 | end 58 | 59 | queue << adjacent_node 60 | visited[adjacent_node.first][adjacent_node.last] = true 61 | end 62 | end 63 | puts "river_length: #{river_length}" 64 | end 65 | 66 | # returns all adjacent nodes if in bounds 67 | def node_and_adjacents(node) 68 | x = node.first 69 | y = node.last 70 | 71 | right = x + 1 > @width - 1 ? nil : [x + 1, y] 72 | # left = x - 1 < 0 ? nil : [x - 1, y] 73 | down_value = y + 1 > @height - 1 ? nil : [x, y + 1] 74 | # up_value = y - 1 < 0 ? nil : [x, y - 1] 75 | 76 | # [node, left, right, up_value, down_value] 77 | [node, right, down_value] 78 | end 79 | 80 | run 81 | -------------------------------------------------------------------------------- /codesignal/postmates_spec.rb: -------------------------------------------------------------------------------- 1 | def tsort(arr) 2 | arr.sort_by{ |word, num| num } 3 | end 4 | 5 | describe '#tsort' do 6 | it 'sorts tuples by 2nd value' do 7 | expect(tsort([['for', 24], ['Geeks', 8], ['Geeks', 30]])).to eq([['Geeks', 8], ['for', 24], ['Geeks', 30]]) 8 | expect(tsort([['452', 10], ['256', 5], ['100', 20], ['135', 15]])).to eq [['256', 5], ['452', 10], ['135', 15], ['100', 20]] 9 | end 10 | end 11 | 12 | hash = { 13 | 'a': 'apple', 14 | 'b': 'dog', 15 | 'c': {'d': 'dog'}, 16 | 'e': 'dog', 17 | 'f': {'g': {'h': 'dog'}} 18 | } 19 | 20 | # Given hash and value, give list of all keys with that value. If nested, keys should be separated by a dot. 21 | def hashkeys(hash, val, ans = [], keylist = nil) 22 | hash.each do |k, v| 23 | if v.is_a?(Hash) 24 | keylist = addkey(k, keylist) 25 | hashkeys(v, val, ans, keylist) 26 | keylist = nil 27 | elsif v == val 28 | keylist = addkey(k, keylist) 29 | end 30 | if keylist 31 | ans << keylist 32 | keylist = nil 33 | end 34 | end 35 | 36 | ans 37 | end 38 | 39 | def addkey(k, keylist) 40 | if keylist.nil? 41 | k.to_s 42 | else 43 | keylist + '.' + k.to_s 44 | end 45 | end 46 | 47 | describe '#hashkeys' do 48 | it 'given hash and value, gives list of all keys with that value' do 49 | expect(hashkeys(hash, 'dog')).to eq ['b', 'c.d', 'e', 'f.g.h'] 50 | end 51 | end 52 | 53 | # Given sorted list of Unix epoch time deliveries for customers, return UUIDs for customers with 3+ deliveries in any 30 day period. 54 | def busy_customers(deliveries) 55 | secs_in_month = 60 * 60 * 24 * 30 56 | hash = {} # {id: [time difference, # of deliveries]} 57 | ans = [] 58 | deliveries.each do |id, time| 59 | if hash[id] 60 | last_time, delivery_num = hash[id] 61 | if time - last_time < secs_in_month 62 | delivery_num += 1 63 | hash[id][1] = delivery_num 64 | next 65 | end 66 | end 67 | 68 | hash[id] = [time, 1] 69 | end 70 | 71 | hash.each do |id, arr| 72 | ans << id if arr[1] >= 3 73 | end 74 | 75 | ans 76 | end 77 | 78 | describe '#busy_customers' do 79 | it 'Given list of deliveries (at Unix epoch times), returns customer IDs with 3+ deliveries in any 30 day period' do 80 | deliveries = [ 81 | ['1', 1586182280], 82 | ['2', 1586227080], 83 | ['1', 1590460680], 84 | ['1', 1591670280], 85 | ['2', 1591905480], 86 | ['3', 1592005480], 87 | ['1', 1592707080], 88 | ['3', 1592805480], 89 | ['2', 1592907080], 90 | ['3', 1594521480], 91 | ] 92 | expect(busy_customers(deliveries)).to eq ['1', '3'] 93 | end 94 | end 95 | 96 | # XOR: a ^ a ^ b = a ^ b ^ a = b ^ a ^ a = b 97 | # a ^ a ^ a = a 98 | def good_tuples(nums) 99 | count = 0 100 | (0..nums.size - 3).each do |i| 101 | first, second, third = nums[i], nums[i + 1], nums[i + 2] 102 | next if 3 * first == first + second + third 103 | count += 1 if [first, second, third].include?(first ^ second ^ third) 104 | 105 | # next if nums[i] == nums[i + 1] && nums[i + 1] == nums[i + 2] 106 | # next if nums[i] != nums[i + 1] && nums[i + 1] != nums[i + 2] && nums[i] != nums[i + 2] 107 | end 108 | 109 | count 110 | end 111 | 112 | describe '#good_tuples' do 113 | it 'returns # of good tuples, or # of consecutive triplets with exactly 2 duplicate numbers' do 114 | expect(good_tuples([4,4,6,4,2,2,2,3])).to eq 4 115 | # [446], [464], [422], [223] 116 | end 117 | end 118 | 119 | # https://leetcode.com/discuss/interview-question/506181/Postmates-or-OA-2020-or-Close-Strings 120 | def close_strings(str1, str2) 121 | freq1, freq2 = freq_hash(str1), freq_hash(str2) 122 | freq1 = freq1.sort_by {|char, freq| freq} 123 | freq2 = freq2.sort_by {|char, freq| freq} 124 | freq1.size.times do |i| 125 | return false if freq1[i][1] != freq2[i][1] 126 | end 127 | 128 | true 129 | end 130 | 131 | def freq_hash(str) 132 | f = {} 133 | str.each_char do |char| 134 | f[char] ? f[char] += 1 : f[char] = 1 135 | end 136 | f 137 | end 138 | 139 | describe '#close_strings' do 140 | it 'returns T or F, if we may convert one string to another' do 141 | expect(close_strings("abbzccc", "babzzcz")).to eq true 142 | expect(close_strings("abcbdb", "bbbcca")).to eq false 143 | expect(close_strings("abbbzcc", "babzzcz")).to eq true 144 | expect(close_strings("abbbzcf", "babzzcz")).to eq false 145 | expect(close_strings("abbzzca", "babzzcz")).to eq false 146 | end 147 | end 148 | 149 | # Given time window (start, finish) in which courier works and array of start/finish times of deliveries. 150 | # Find max number of deliveries courier can make, with no overlapping times. 151 | 152 | # Answer: First sort intervals by finish time value. Then insert first interval into answer and continue considering each interval in turn: If current interval begins after the previous interval ends, then they do not overlap and we can append the current interval to answer. 153 | 154 | # Time complexity: n log n, from sort 155 | def no_overlap(time_window, deliveries) 156 | ans = [] 157 | earliest, latest = time_window 158 | deliveries.sort_by!{ |start, finish| finish } 159 | deliveries.each do |start, finish| 160 | next if start < earliest || finish > latest 161 | if ans.empty? 162 | ans = [[start, finish]] 163 | elsif ans.last[1] < start 164 | ans << [start, finish] 165 | end 166 | end 167 | 168 | ans 169 | # ans.size 170 | end 171 | 172 | describe '#no_overlap' do 173 | it 'finds max # of deliveries with no overlapping times' do 174 | expect(no_overlap([1, 50], [[1, 5], [10, 25], [17, 21], [30, 60], [11, 16]])).to eq [[1,5], [11,16], [17,21]] 175 | expect(no_overlap([2, 50], [[1, 5], [10, 25], [17, 21], [30, 60], [11, 16]])).to eq [[11,16], [17,21]] 176 | expect(no_overlap([1, 50], [[1, 12], [10, 25], [17, 21], [14, 16], [30, 60], [11, 13], [5, 9]])).to eq [[5, 9], [11, 13], [14, 16], [17, 21]] 177 | expect(no_overlap([1, 50], [[1, 5], [10, 25], [17, 21], [30, 50], [11, 16]])).to eq [[1,5], [11,16], [17,21], [30, 50]] 178 | end 179 | end 180 | -------------------------------------------------------------------------------- /codesignal/practicetest.rb: -------------------------------------------------------------------------------- 1 | def mutateTheArray(n, a) 2 | b = [] 3 | a.size.times do |i| 4 | first = i == 0 ? 0 : a[i - 1] 5 | last = i == a.size - 1 ? 0 : a[i + 1] 6 | b[i] = first + a[i] + last 7 | end 8 | b 9 | end 10 | 11 | def countTinyPairs(a, b, k) 12 | ans = 0 13 | a.size.times do |i| 14 | ans += 1 if (a[i].to_s + b[a.size - 1 - i].to_s).to_i < k 15 | end 16 | ans 17 | end 18 | 19 | def meanGroups(a) 20 | h = {} 21 | a.each_with_index do |arr, i| 22 | mean = arr.inject(&:+).to_f / arr.size 23 | h[mean] ? h[mean] << i : h[mean] = [i] 24 | end 25 | h.values 26 | end 27 | 28 | def alternatingSort(a) 29 | return true if a.size == 1 30 | alt_i = 0 31 | b = [a[alt_i]] 32 | sign = -1 33 | (a.size - 1).downto(1).each do |i| 34 | sign *= -1 35 | alt_i += sign * i 36 | b << a[alt_i] 37 | end 38 | 39 | if b == b.sort 40 | (0..b.size - 2).each do |i| 41 | return false if b[i] == b[i + 1] 42 | end 43 | true 44 | else 45 | false 46 | end 47 | end 48 | 49 | 50 | def mergeStrings(s1, s2) 51 | ans = '' 52 | i, j = 0, 0 53 | s1, s2 = s2, s1 if s1.size > s2.size 54 | freq1, freq2 = freq_hash(s1), freq_hash(s2) 55 | while j < s2.size 56 | char1, char2 = s1[i], s2[j] 57 | if char1.nil? 58 | ans += s2[j..-1] 59 | break 60 | elsif char2.nil? 61 | ans += s1[i..-1] 62 | break 63 | end 64 | 65 | if freq1[char1] < freq2[char2] 66 | ans += char1 67 | i += 1 68 | elsif freq1[char1] > freq2[char2] 69 | ans += char2 70 | j += 1 71 | elsif char1 < char2 72 | ans += char1 73 | i += 1 74 | elsif char1 > char2 75 | ans += char2 76 | j += 1 77 | else 78 | ans += char1 79 | i += 1 80 | j += 1 81 | end 82 | end 83 | ans += s1[i..-1] if i < s1.size 84 | ans 85 | end 86 | 87 | def freq_hash(str) 88 | f = {} 89 | str.each_char do |char| 90 | f[char] ? f[char] += 1 : f[char] = 1 91 | end 92 | f 93 | end 94 | 95 | # Given integer array, return sum of every array[i] concatenated with array[j]. 96 | # For example: arr[1,2,3] would give -> 11+12+13+21+22+23+31+32+33 97 | 98 | # For a = [10,2], the output should be concatentationSum(a) = 1344 99 | # - a[0] + a[0] = 10 + 10 = 1010 100 | # - a[0] + a[1] = 10 + 2 = 102 101 | # - a[1] + a[0] = 2 + 10 = 210 102 | # - a[1] + a[1] = 2 + 2 = 22 103 | # So the sum is equal to 1010 + 102 + 210 + 22 = 1344 104 | 105 | def concatenations_sum(a) 106 | ans = 0 107 | a.size.times do |i| 108 | a.size.times do |j| 109 | ans += (a[i].to_s + a[j].to_s).to_i 110 | end 111 | end 112 | ans 113 | end 114 | 115 | # p concatenations_sum([10, 2]) is 1344 116 | 117 | #---------------- 118 | # 28/30 tests passed. Execution time limit exceeded on test 29: Program exceeded the execution time limit. Make sure that it completes execution in a few seconds for any possible input. 119 | def hashMap(queryType, query) 120 | h = {} 121 | sum = 0 122 | query.size.times do |i| 123 | qt, q = queryType[i], query[i] 124 | case qt 125 | when 'insert' 126 | key, val = q 127 | h[key] = val 128 | when 'addToValue' 129 | h = add_to_value(h, q[0]) 130 | when 'addToKey' 131 | h = add_to_key(h, q[0]) 132 | when 'get' 133 | sum += h[q[0]] 134 | end 135 | end 136 | sum 137 | end 138 | 139 | def add_to_value(h, num) 140 | h.each do |key, val| 141 | h[key] += num 142 | end 143 | h 144 | end 145 | 146 | def add_to_key(h, num) 147 | h2 = {} 148 | h.each do |key, val| 149 | h2[key + num] = val 150 | end 151 | h2 152 | end 153 | #---------------- 154 | -------------------------------------------------------------------------------- /codesignal/sum_divisible_by_k.rb: -------------------------------------------------------------------------------- 1 | def sum_divisible_by_k(a, k) 2 | ways = 0 3 | freq = Array.new(k, 0) 4 | 5 | a.each do |num| 6 | rem = num % k 7 | if rem != 0 8 | ways += freq[k - rem] 9 | else 10 | ways += freq[0] 11 | end 12 | freq[rem] += 1 13 | end 14 | 15 | ways 16 | end 17 | 18 | p s([1, 2, 3, 4, 5], 3) 19 | -------------------------------------------------------------------------------- /diagonal_matrix.go: -------------------------------------------------------------------------------- 1 | /* Given a 2D matrix, print all elements of the given matrix in diagonal order. 2 | For example, given this 5 X 4 matrix: 3 | 4 | 1 2 3 4 5 | 5 6 7 8 6 | 9 10 11 12 7 | 13 14 15 16 8 | 17 18 19 20 9 | Diagonal printing of the above matrix is 10 | 11 | 1 12 | 5 2 13 | 9 6 3 14 | 13 10 7 4 15 | 17 14 11 8 16 | 18 15 12 17 | 19 16 18 | 20 19 | 20 | Coordinates: 21 | 0,0 22 | 1,0 0,1 23 | 2,0 1,1 0,2 24 | 3,0 2,1 1,2 0,3 25 | 4,0 3,1 2,2 1,3 26 | 4,1 3,2 2,3 27 | 4,2 3,3 28 | 4,3 29 | */ 30 | 31 | package main 32 | 33 | import "fmt" 34 | 35 | func main() { 36 | a := [][]int{ 37 | {1, 2, 3, 4}, 38 | {5, 6, 7, 8}, 39 | {9, 10, 11, 12}, 40 | {13, 14, 15, 16}, 41 | {17, 18, 19, 20}, 42 | } 43 | diag(a) 44 | } 45 | 46 | func diag(a [][]int) { 47 | height, width := len(a), len(a[0]) 48 | line := []int{} 49 | 50 | for row := 0; row < height-1; row++ { 51 | r := row 52 | for col := 0; col <= row; col++ { 53 | line = append(line, a[r][col]) 54 | r -= 1 55 | } 56 | fmt.Println(line) 57 | line = []int{} 58 | } 59 | 60 | for row := 0; row < height-1; row++ { 61 | r := height - 1 62 | for col := row; col < width; col++ { 63 | line = append(line, a[r][col]) 64 | r -= 1 65 | } 66 | fmt.Println(line) 67 | line = []int{} 68 | } 69 | } 70 | 71 | // Output: 72 | // [1] 73 | // [5 2] 74 | // [9 6 3] 75 | // [13 10 7 4] 76 | // [17 14 11 8] 77 | // [18 15 12] 78 | // [19 16] 79 | // [20] 80 | -------------------------------------------------------------------------------- /google/crossword.rb: -------------------------------------------------------------------------------- 1 | # Given a nxn crossword puzzle and a word, return earliest [row, col] where you can fit that word. Fill in word right or down. Part of word may be filled in already. Length of space must exactly equal length of word. 2 | # word = "tent" 3 | 4 | # A _ # T _ N S # _ # 5 | _ # T _ _ T # _ _ _ # col = [1, 2] 6 | _ # _ _ # # # _ # _ 7 | _ # _ _ # # # _ # _ 8 | _ # S _ # # # _ # _ 9 | # ... 10 | # row, col = 1 11 | 12 | # _ _ _ _ 13 | 14 | # Strategy: 15 | # 1. Solve this for 1 row only. Solve for horizontally only. 16 | # 2. Transpose the matrix. Then we can use same code on transposed matrix to solve it vertically. 17 | 18 | # Solve for 1 row: 19 | # Test cases: Word = "test" 20 | # OK. All same length as word 21 | # 1. _ _ _ _ All blanks. Left and right of word must be either "#" or border. 22 | # 2. T _ N _ >=1 of characters are in word 23 | 24 | # Not OK: 25 | # 1. _ _ T E S T _ _ Can't start word from middle 26 | # 2. T _ _ S Can't have 1 or more letters not matching letters in word 27 | # 3. Can't have "#" in word 28 | # 4. T E S T _ W # 29 | 30 | def crossword(arr, word) 31 | earliest_col = -1 32 | ans = [] 33 | arr.each_with_index do |row, i| 34 | earliest_col = solve_horiz(row, word) 35 | ans = [i, earliest_col] 36 | break 37 | end 38 | 39 | tarr = transpose arr 40 | tarr.each_with_index do |col, i| 41 | earliest_row = solve_horiz(row, word) 42 | if earliest_row <= ans[0] && i <= ans[1] 43 | ans = [earliest_row, i] 44 | end 45 | break 46 | end 47 | 48 | ans 49 | end 50 | 51 | def solve_horiz(row, word) 52 | return [-1, -1] if word.size > row.size 53 | 54 | row.size.times do |col| 55 | if col == 0 && row[0] == '_' || row[0] == word[0] 56 | (0..word.size - 1).each do |i| 57 | next if row[i] == word[i] 58 | end 59 | return col if word.size < row.size && row[word.size] == '#' 60 | return col 61 | elsif row[col] == '#' && col + 1 < row.size && (row[col + 1] == word[0] || row[col + 1] == '_') 62 | while col + 1 < row.size do 63 | next if row[col + 1] == word[i - col] || row[i + 1] == '_' 64 | col += 1 65 | end 66 | end 67 | end 68 | end 69 | 70 | def transpose(arr) 71 | arr = [[4, -5, 2], [-3, 8, 10], [2, -4, 6]] 72 | p arr.transpose 73 | end 74 | 75 | p transpose(arr) 76 | -------------------------------------------------------------------------------- /google/max_sum_path.rb: -------------------------------------------------------------------------------- 1 | # Q: Given a nxn matrix of integers, find max sum of integers you can get by 2 | # taking path from upper left (0, 0) to bottom right, where you can only move to right or down. 3 | 4 | # 4 -5 2 5 | # -3 8 10 6 | # 2 -4 6 7 | 8 | # 1. Use BFS. Make 2nd nxn matrix filled with -infinity to track sums. 9 | # 2. While row, col != [n - 1, n - 1], loop 10 | # 3. Move right or down, with boundary checking get_neighbors(). Fill in sum matrix as we go with max sum we can get at that cell so far. 11 | # 4. When have conflict, like (4 - 5 + 8) vs (4 - 3 + 8), take max and stick in sums matrix. Memoization. 12 | 13 | arr = [[4, -5, 2], [-3, 8, 10], [2, -4, 6]] 14 | 15 | # queue = [[1, 1]] 16 | # row, col = 2, 0 17 | 18 | # nrow, ncol = 2, 1 19 | # FROM TO 20 | # [current value of sums[nrow, ncol], sums[row, col] + arr[nrow][ncol]].max 21 | # sums = [7, 1 + 8].max = 9 22 | # 4 -1 0 23 | # 1 9 0 24 | # 0 0 0 25 | 26 | # Time: O(n * n), Space: O(n * n) 27 | def max_sum_path(arr) 28 | queue = [[0, 0]] 29 | n = arr.size 30 | sums = Array.new(n) { |cell| Array.new(n, -100) } 31 | sums[0][0] = arr[0][0] 32 | 33 | while !queue.empty? do 34 | row, col = queue.shift 35 | directions = [[0, 1], [1, 0]] 36 | directions.each do |drow, dcol| 37 | nrow, ncol = row + drow, col + dcol 38 | if nrow.between?(0, n - 1) && ncol.between?(0, n - 1) 39 | sums[nrow][ncol] = [sums[nrow][ncol], sums[row][col] + arr[nrow][ncol]].max 40 | queue << [nrow, ncol] 41 | end 42 | end 43 | end 44 | sums[n - 1][n - 1] 45 | end 46 | 47 | p max_sum_path(arr) 48 | -------------------------------------------------------------------------------- /gtci/01-sliding-window/01_avgs_of_subarrays.test.js: -------------------------------------------------------------------------------- 1 | // https://www.educative.io/courses/grokking-the-coding-interview/7D5NNZWQ8Wr 2 | // Given array, find average of all contiguous subarrays of size ‘K’ in it. 3 | 4 | // Time and space complexity: O(n). 5 | function find_averages_of_subarrays(K, arr) { 6 | const result = [] 7 | let [currentSum, windowStart] = [0, 0] 8 | 9 | for (let windowEnd = 0; windowEnd < arr.length; windowEnd++) { 10 | currentSum += arr[windowEnd] 11 | if (windowEnd >= K - 1) { 12 | result.push(currentSum / K) 13 | currentSum -= arr[windowStart] 14 | windowStart++ 15 | } 16 | } 17 | 18 | return result 19 | } 20 | 21 | describe("Find the average of all contiguous subarrays of size K", () => { 22 | it("K = 5 and array = [1, 3, 2, 6, -1, 4, 1, 8, 2]", () => { 23 | expect( 24 | find_averages_of_subarrays(5, [1, 3, 2, 6, -1, 4, 1, 8, 2]) 25 | ).toEqual([2.2, 2.8, 2.4, 3.6, 2.8]) 26 | }) 27 | 28 | it("K = 4 and array = [3, 1, -2, 8, 9, -10, -3, 8, 9]", () => { 29 | expect( 30 | find_averages_of_subarrays(4, [3, 1, -2, 8, 9, -10, -3, 8, 9]) 31 | ).toEqual([2.5, 4, 1.25, 1, 1, 1]) 32 | }) 33 | }) 34 | // ______________ 35 | // OUTPUT: 36 | // i: 0, currentSum: 1 37 | // i: 1, currentSum: 4 38 | // i: 2, currentSum: 6 39 | // i: 3, currentSum: 12 40 | // i: 4, currentSum: 11 41 | // i: 4, arr[i]: -1, arr[start]: 3 42 | // new currentSum: 10 43 | // result: 2.2 44 | // i: 5, currentSum: 14 45 | // i: 5, arr[i]: 4, arr[start]: 2 46 | // new currentSum: 11 47 | // result: 2.2,2.8 48 | // i: 6, currentSum: 12 49 | // i: 6, arr[i]: 1, arr[start]: 6 50 | // new currentSum: 10 51 | // result: 2.2,2.8,2.4 52 | // i: 7, currentSum: 18 53 | // i: 7, arr[i]: 8, arr[start]: -1 54 | // new currentSum: 12 55 | // result: 2.2,2.8,2.4,3.6 56 | // i: 8, currentSum: 14 57 | // i: 8, arr[i]: 2, arr[start]: 4 58 | // new currentSum: 15 59 | // result: 2.2,2.8,2.4,3.6,2.8 60 | // Averages of subarrays of size K: 2.2,2.8,2.4,3.6,2.8 61 | -------------------------------------------------------------------------------- /gtci/01-sliding-window/02_max_sum_subarray.test.js: -------------------------------------------------------------------------------- 1 | // https://www.educative.io/courses/grokking-the-coding-interview/JPKr0kqLGNP 2 | // Given array of positive numbers and positive number k, 3 | // find max sum of any contiguous subarray of size k 4 | 5 | // Time complexity: O(n). Space complexity: O(1). 6 | function solution(k, arr) { 7 | let [maxSum, sum, windowStart] = [0, 0, 0] 8 | 9 | for (let windowEnd = 0; windowEnd < arr.length; windowEnd++) { 10 | sum += arr[windowEnd] 11 | if (windowEnd >= k - 1) { 12 | maxSum = Math.max(maxSum, sum) 13 | sum -= arr[windowStart] 14 | windowStart++ 15 | } 16 | } 17 | 18 | return maxSum 19 | } 20 | 21 | describe("solution()", () => { 22 | it("finds max sum of continguous subarray of size k", () => { 23 | expect(solution(3, [2, 1, 5, 1, 3, 2])).toEqual(9) 24 | expect(solution(2, [2, 3, 4, 1, 5])).toEqual(7) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /gtci/01-sliding-window/03_smallest_subarray_of_given_sum.test.js: -------------------------------------------------------------------------------- 1 | // https://www.educative.io/courses/grokking-the-coding-interview/7XMlMEQPnnQ 2 | // Given array of positive numbers and positive number s, find length of smallest 3 | // contiguous subarray whose sum >= s. Return 0 if no such subarray exists. 4 | 5 | // Time complexity: O(n). Space complexity: O(1). 6 | function solution(s, arr) { 7 | let minLength = arr.length + 1 8 | let [sum, windowStart] = [0, 0] 9 | 10 | for (let windowEnd = 0; windowEnd < arr.length; windowEnd++) { 11 | sum += arr[windowEnd] 12 | 13 | // In each step, shrink sliding window as small as possible, till sum < s: 14 | 15 | // We do two things in each step: 16 | // - Keep track of smallest length so far 17 | // - Subtract window's first element from current sum 18 | while (sum >= s) { 19 | const length = windowEnd - windowStart + 1 20 | minLength = Math.min(minLength, length) 21 | sum -= arr[windowStart] 22 | windowStart++ 23 | } 24 | } 25 | 26 | if (minLength == arr.length + 1) { 27 | return 0 28 | } 29 | 30 | return minLength 31 | } 32 | 33 | describe("solution()", () => {; 34 | it("finds length of smallest subarray with a sum >= s", () => { 35 | expect(solution(7, [2, 1, 5, 2, 3, 2])).toEqual(2) 36 | expect(solution(7, [2, 1, 5, 2, 8])).toEqual(1) 37 | expect(solution(8, [3, 4, 1, 1, 6])).toEqual(3) 38 | }) 39 | 40 | it("returns 0 if no smallest subarray found", () => { 41 | expect(solution(100, [3, 4, 1, 1, 6])).toEqual(0) 42 | }) 43 | }) 44 | -------------------------------------------------------------------------------- /gtci/01-sliding-window/04_longest_substring_with_k_distinct_chars.test.js: -------------------------------------------------------------------------------- 1 | // https://www.educative.io/courses/grokking-the-coding-interview/YQQwQMWLx80 2 | // Find length of longest substring in string with <= k distinct characters. 3 | 4 | // Time complexity: O(n) where ‘n’ is the number of characters in input string. The outer for loop runs for all characters and inner while loop processes each character only once, thus time complexity = O(n + n), or O(n). 5 | 6 | // Space Complexity: O(k), since we store a max of ‘k + 1’ characters in the HashMap. 7 | 8 | // Time complexity: O(n). Space complexity: O(k). 9 | function solution(str, k) { 10 | let [maxLength, start] = [0, 0] 11 | 12 | // A Hashmap in JavaScript is an Object. 13 | // Difference between Map and Object in JavaScript: 14 | // https://medium.com/front-end-weekly/es6-map-vs-object-what-and-when-b80621932373 15 | let freq = {} // frequency hash: # of times each character is in string 16 | 17 | for (let end = 0; end < str.length; end++) { 18 | let char = str[end] 19 | freq[char] ? freq[char]++ : freq[char] = 1 20 | 21 | // shrink sliding window until # of distinct chars == k 22 | while (Object.keys(freq).length > k) { 23 | let startChar = str[start] 24 | freq[startChar]-- 25 | if (freq[startChar] == 0) { 26 | delete freq[startChar] 27 | } 28 | start++ 29 | } 30 | 31 | const length = end - start + 1 32 | maxLength = Math.max(maxLength, length) 33 | 34 | // My Original Answer: 35 | 36 | // distinctChars = Object.keys(freq).length 37 | // if (distinctChars <= k) { 38 | // const length = end - start + 1 39 | // maxLength = Math.max(maxLength, length) 40 | // } else { 41 | // let startChar = str[start] 42 | // freq[startChar]-- 43 | // if (freq[startChar] == 0) { 44 | // delete freq[startChar] 45 | // } 46 | // start++ 47 | // } 48 | } 49 | 50 | return maxLength 51 | } 52 | 53 | describe("solution()", () => { 54 | it("finds length of longest substring with <= k distinct characters", () => { 55 | expect(solution("cabaaaaabbbc", 2)).toEqual(10) 56 | expect(solution("araaci", 2)).toEqual(4) 57 | expect(solution("araaci", 1)).toEqual(2) 58 | expect(solution("cbbebi", 3)).toEqual(5) 59 | expect(solution("cbbebi", 10)).toEqual(6) 60 | }) 61 | }) 62 | -------------------------------------------------------------------------------- /gtci/01-sliding-window/05_fruits_into_baskets.test.js: -------------------------------------------------------------------------------- 1 | // https://www.educative.io/courses/grokking-the-coding-interview/Bn2KLlOR0lQ 2 | // Each character represents a fruit tree. Given 2 baskets, put max # of fruits in 3 | // each basket. Each basket may have only 1 fruit type. 4 | // Once you've started you can’t skip a tree. (Fruits picked must be contiguous.) 5 | // Give max total # of fruits in both baskets. 6 | 7 | // Time complexity: O(n + n) = O(n) 8 | // Space complexity: O(1). We have max of 3 types of fruits in freq hashmap. 9 | 10 | // Time complexity: O(n). Space complexity: O(1). 11 | function solution(arr) { 12 | let [maxTotalFruits, start] = [0, 0] 13 | let freq = {} // frequency hash: # of fruits by basket 14 | 15 | for (let end = 0; end < arr.length; end++) { 16 | let fruit = arr[end] 17 | freq[fruit] ? freq[fruit]++ : freq[fruit] = 1 18 | 19 | // shrink sliding window until # of distinct fruits == k 20 | while (Object.keys(freq).length > 2) { 21 | const startFruit = arr[start] 22 | freq[startFruit]-- 23 | if (freq[startFruit] == 0) { 24 | delete freq[startFruit] 25 | } 26 | start++ 27 | } 28 | 29 | let totalFruits = end - start + 1 // or Object.values(freq).reduce((a, b) => a + b) 30 | maxTotalFruits = Math.max(maxTotalFruits, totalFruits) 31 | } 32 | 33 | return maxTotalFruits 34 | } 35 | 36 | describe("solution()", () => { 37 | it("finds max total # of fruits in both baskets", () => { 38 | expect(solution(['A', 'B', 'C', 'A', 'C'])).toEqual(3) 39 | expect(solution(['A', 'B', 'C', 'B', 'B', 'C'], 2)).toEqual(5) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /gtci/01-sliding-window/06_no_repeat_substring.test.js: -------------------------------------------------------------------------------- 1 | // https://www.educative.io/courses/grokking-the-coding-interview/YMzBx1gE5EO 2 | // Find length of longest substring with no repeating characters. 3 | 4 | // Time complexity: O(n). Space complexity: O(1) -- limited by 256 unique characters 5 | function solution(str) { 6 | let [maxLength, start] = [0, 0] 7 | let freq = {} 8 | 9 | for (let end = 0; end < str.length; end++) { 10 | let char = str[end] 11 | 12 | if (freq[char]) { 13 | freq = {} 14 | freq[char] = 1 15 | start = end 16 | } else { 17 | freq[char] = 1 18 | let length = end - start + 1 19 | maxLength = Math.max(maxLength, length) 20 | } 21 | } 22 | 23 | return maxLength 24 | } 25 | 26 | describe("solution()", () => { 27 | it("finds length of longest substring with distinct characters", () => { 28 | expect(solution("aabccbb")).toEqual(3) 29 | expect(solution("abbbb")).toEqual(2) 30 | expect(solution("abccde")).toEqual(3) 31 | expect(solution("abccdeaba")).toEqual(5) 32 | }) 33 | }) 34 | 35 | // Course Answer: 36 | 37 | // function non_repeat_substring(str) { 38 | // let windowStart = 0, 39 | // maxLength = 0, 40 | // charIndexMap = {}; 41 | 42 | // // try to extend the range [windowStart, windowEnd] 43 | // for (let windowEnd = 0; windowEnd < str.length; windowEnd++) { 44 | // const rightChar = str[windowEnd]; 45 | // // if the map already contains the 'rightChar', shrink the window from the beginning so that 46 | // // we have only one occurrence of 'rightChar' 47 | // if (rightChar in charIndexMap) { 48 | // // this is tricky; in the current window, we will not have any 'rightChar' after its previous index 49 | // // and if 'windowStart' is already ahead of the last index of 'rightChar', we'll keep 'windowStart' 50 | // windowStart = Math.max(windowStart, charIndexMap[rightChar] + 1); 51 | // } 52 | // // insert the 'rightChar' into the map 53 | // charIndexMap[str[windowEnd]] = windowEnd; 54 | // // remember the maximum length so far 55 | // maxLength = Math.max(maxLength, windowEnd - windowStart + 1); 56 | // } 57 | // return maxLength; 58 | // } 59 | -------------------------------------------------------------------------------- /gtci/01-sliding-window/07_longest_repeating_substring.test.js: -------------------------------------------------------------------------------- 1 | // https://www.educative.io/courses/grokking-the-coding-interview/R8DVgjq78yR 2 | // Given string with lowercase letters, if you may replace no more than k letters 3 | // with any letter, find length of longest repeating substring after replacement. 4 | 5 | // Time complexity: O(n). Space complexity: O(1), because freq may have 6 | // up to max of 26 letters as keys O(26), or just O(1). 7 | function solution(str, k) { 8 | let [maxLength, start, maxFreq] = [0, 0, 0] 9 | let freq = {} 10 | 11 | for (let end = 0; end < str.length; end++) { 12 | let rightChar = str[end] 13 | freq[rightChar] ? freq[rightChar]++ : freq[rightChar] = 1 14 | maxFreq = Math.max(maxFreq, freq[rightChar]) 15 | 16 | let length = end - start + 1 17 | // console.log(`end: ${end}. substring: "${str.substring(start, end + 1)}"`) 18 | // console.log(`freq: ${JSON.stringify(freq)}`) 19 | // console.log(`rightChar: ${rightChar}, freq[${rightChar}]: ${freq[rightChar]}, length: ${length}, maxFreq: ${maxFreq}`) 20 | 21 | // If substring length - maxFreq > k, we've gone beyond limit where 22 | // we can replace non-rightChars to make a repeating substring. 23 | if (length - maxFreq > k) { 24 | // console.log(`length - maxFreq: ${length - maxFreq} > k (${k})`) 25 | leftChar = str[start] 26 | freq[leftChar]-- // Since leftChar won't be in our window, drop its freq by 1 27 | start++ // Shrink sliding window by 1 28 | 29 | // console.log(`New start: ${start}. New freq: ${JSON.stringify(freq)}`) 30 | } 31 | 32 | length = end - start + 1 33 | maxLength = Math.max(maxLength, length) 34 | // console.log(`maxLength: ${maxLength}\n`) 35 | } 36 | 37 | return maxLength 38 | } 39 | 40 | // same answer with no comments 41 | function solution2(str, k) { 42 | let [maxLength, maxFreq, start] = [0, 0, 0] 43 | let freq = {} 44 | 45 | for (let end = 0; end < str.length; end++) { 46 | let rightChar = str[end] 47 | freq[rightChar] ? freq[rightChar]++ : freq[rightChar] = 1 48 | maxFreq = Math.max(maxFreq, freq[rightChar]) 49 | 50 | let length = end - start + 1 51 | if (length - maxFreq > k) { 52 | leftChar = str[start] 53 | freq[leftChar]-- 54 | start++ 55 | } 56 | 57 | length = end - start + 1 58 | maxLength = Math.max(maxLength, length) 59 | } 60 | 61 | return maxLength 62 | } 63 | 64 | describe("solution()", () => { 65 | it("finds length of longest substring with repeating characters, after replacement", () => { 66 | expect(solution("aabccbb", 2)).toEqual(5) 67 | expect(solution("abbcb", 1)).toEqual(4) 68 | expect(solution("abccde", 1)).toEqual(3) 69 | expect(solution("abccdeacafgca", 3)).toEqual(6) 70 | }) 71 | }) 72 | 73 | // ------aabccbb--- (k = 2)---- 74 | 75 | // end: 0. substring: "a" 76 | // freq: { "a": 1 } 77 | // rightChar: a, freq[a]: 1, length: 1, maxFreq: 1 78 | // maxLength: 1 79 | 80 | // end: 1. substring: "aa" 81 | // freq: { "a": 2 } 82 | // rightChar: a, freq[a]: 2, length: 2, maxFreq: 2 83 | // maxLength: 2 84 | 85 | // end: 2. substring: "aab" 86 | // freq: { "a": 2, "b": 1 } 87 | // rightChar: b, freq[b]: 1, length: 3, maxFreq: 2 88 | // maxLength: 3 89 | 90 | // end: 3. substring: "aabc" 91 | // freq: { "a": 2, "b": 1, "c": 1 } 92 | // rightChar: c, freq[c]: 1, length: 4, maxFreq: 2 93 | // maxLength: 4 94 | 95 | // end: 4. substring: "aabcc" 96 | // freq: { "a": 2, "b": 1, "c": 2 } 97 | // rightChar: c, freq[c]: 2, length: 5, maxFreq: 2 98 | // length - maxFreq: 3 > k(2) 99 | // New start: 1. New freq: { "a": 1, "b": 1, "c": 2 } 100 | // maxLength: 4 101 | 102 | // end: 5. substring: "abccb" 103 | // freq: { "a": 1, "b": 2, "c": 2 } 104 | // rightChar: b, freq[b]: 2, length: 5, maxFreq: 2 105 | // length - maxFreq: 3 > k(2) 106 | // New start: 2. New freq: { "a": 0, "b": 2, "c": 2 } 107 | // maxLength: 4 108 | 109 | // end: 6. substring: "bccbb" 110 | // freq: { "a": 0, "b": 3, "c": 2 } 111 | // rightChar: b, freq[b]: 3, length: 5, maxFreq: 3 112 | // maxLength: 5 113 | 114 | // 5 115 | // ------abccdeacafgca--- (k = 3)-- - 116 | 117 | // end: 0. substring: "a" 118 | // freq: { "a": 1 } 119 | // rightChar: a, freq[a]: 1, length: 1, maxFreq: 1 120 | // maxLength: 1 121 | 122 | // end: 1. substring: "ab" 123 | // freq: { "a": 1, "b": 1 } 124 | // rightChar: b, freq[b]: 1, length: 2, maxFreq: 1 125 | // maxLength: 2 126 | 127 | // end: 2. substring: "abc" 128 | // freq: { "a": 1, "b": 1, "c": 1 } 129 | // rightChar: c, freq[c]: 1, length: 3, maxFreq: 1 130 | // maxLength: 3 131 | 132 | // end: 3. substring: "abcc" 133 | // freq: { "a": 1, "b": 1, "c": 2 } 134 | // rightChar: c, freq[c]: 2, length: 4, maxFreq: 2 135 | // maxLength: 4 136 | 137 | // end: 4. substring: "abccd" 138 | // freq: { "a": 1, "b": 1, "c": 2, "d": 1 } 139 | // rightChar: d, freq[d]: 1, length: 5, maxFreq: 2 140 | // maxLength: 5 141 | 142 | // end: 5. substring: "abccde" 143 | // freq: { "a": 1, "b": 1, "c": 2, "d": 1, "e": 1 } 144 | // rightChar: e, freq[e]: 1, length: 6, maxFreq: 2 145 | // length - maxFreq: 4 > k(3) 146 | // New start: 1. New freq: { "a": 0, "b": 1, "c": 2, "d": 1, "e": 1 } 147 | // maxLength: 5 148 | 149 | // end: 6. substring: "bccdea" 150 | // freq: { "a": 1, "b": 1, "c": 2, "d": 1, "e": 1 } 151 | // rightChar: a, freq[a]: 1, length: 6, maxFreq: 2 152 | // length - maxFreq: 4 > k(3) 153 | // New start: 2. New freq: { "a": 1, "b": 0, "c": 2, "d": 1, "e": 1 } 154 | // maxLength: 5 155 | 156 | // end: 7. substring: "ccdeac" 157 | // freq: { "a": 1, "b": 0, "c": 3, "d": 1, "e": 1 } 158 | // rightChar: c, freq[c]: 3, length: 6, maxFreq: 3 159 | // maxLength: 6 160 | 161 | // end: 8. substring: "ccdeaca" 162 | // freq: { "a": 2, "b": 0, "c": 3, "d": 1, "e": 1 } 163 | // rightChar: a, freq[a]: 2, length: 7, maxFreq: 3 164 | // length - maxFreq: 4 > k(3) 165 | // New start: 3. New freq: { "a": 2, "b": 0, "c": 2, "d": 1, "e": 1 } 166 | // maxLength: 6 167 | 168 | // end: 9. substring: "cdeacaf" 169 | // freq: { "a": 2, "b": 0, "c": 2, "d": 1, "e": 1, "f": 1 } 170 | // rightChar: f, freq[f]: 1, length: 7, maxFreq: 3 171 | // length - maxFreq: 4 > k(3) 172 | // New start: 4. New freq: { "a": 2, "b": 0, "c": 1, "d": 1, "e": 1, "f": 1 } 173 | // maxLength: 6 174 | 175 | // end: 10. substring: "deacafg" 176 | // freq: { "a": 2, "b": 0, "c": 1, "d": 1, "e": 1, "f": 1, "g": 1 } 177 | // rightChar: g, freq[g]: 1, length: 7, maxFreq: 3 178 | // length - maxFreq: 4 > k(3) 179 | // New start: 5. New freq: { "a": 2, "b": 0, "c": 1, "d": 0, "e": 1, "f": 1, "g": 1 } 180 | // maxLength: 6 181 | 182 | // end: 11. substring: "eacafgc" 183 | // freq: { "a": 2, "b": 0, "c": 2, "d": 0, "e": 1, "f": 1, "g": 1 } 184 | // rightChar: c, freq[c]: 2, length: 7, maxFreq: 3 185 | // length - maxFreq: 4 > k(3) 186 | // New start: 6. New freq: { "a": 2, "b": 0, "c": 2, "d": 0, "e": 0, "f": 1, "g": 1 } 187 | // maxLength: 6 188 | 189 | // end: 12. substring: "acafgca" 190 | // freq: { "a": 3, "b": 0, "c": 2, "d": 0, "e": 0, "f": 1, "g": 1 } 191 | // rightChar: a, freq[a]: 3, length: 7, maxFreq: 3 192 | // length - maxFreq: 4 > k(3) 193 | // New start: 7. New freq: { "a": 2, "b": 0, "c": 2, "d": 0, "e": 0, "f": 1, "g": 1 } 194 | // maxLength: 6 195 | -------------------------------------------------------------------------------- /gtci/01-sliding-window/08_longest_subarray_with_1s_after_replacement.test.js: -------------------------------------------------------------------------------- 1 | // https://www.educative.io/courses/grokking-the-coding-interview/B6VypRxPolJ 2 | // Given array with 0s and 1s, if you may replace no more than k 0's with 1's, 3 | // find length of longest contiguous subarray with all 1's. 4 | 5 | // Time complexity: O(n). Space complexity: O(1). 6 | function solution(arr, k) { 7 | let [maxLength, maxOnesCount, start] = [0, 0, 0] 8 | 9 | for (let end = 0; end < arr.length; end++) { 10 | if (arr[end] == 1) { 11 | maxOnesCount++ 12 | } 13 | 14 | let length = end - start + 1 15 | if (length - maxOnesCount > k) { 16 | if (arr[start] == 1) { 17 | maxOnesCount-- 18 | } 19 | start++ 20 | } 21 | 22 | length = end - start + 1 23 | maxLength = Math.max(maxLength, length) 24 | } 25 | 26 | return maxLength 27 | } 28 | 29 | describe("solution()", () => { 30 | it("finds length of longest contiguous subarray with all 1's", () => { 31 | expect(solution([0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1], 2)).toEqual(6) 32 | expect(solution([0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1], 3)).toEqual(9) 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /gtci/01-sliding-window/09_permutation_of_pattern_in_string.test.js: -------------------------------------------------------------------------------- 1 | // https://www.educative.io/courses/grokking-the-coding-interview/N8vB7OVYo2D 2 | // Given string and a pattern, find if string contains any permutation of the pattern. 3 | 4 | // Time complexity: O(n + m). n = str length. m = pattern length. 5 | // Space complexity: O(m) since in worst case, all pattern's distinct chars may be in HashMap 6 | function solution(str, pattern) { 7 | let [start, matched] = [0, 0] 8 | let patternFreq = {} 9 | 10 | for (let i = 0; i < pattern.length; i++) { 11 | patternFreq[pattern[i]] ? patternFreq[pattern[i]]++ : patternFreq[pattern[i]] = 1 12 | } 13 | 14 | for (let end = 0; end < str.length; end++) { 15 | rightChar = str[end] 16 | 17 | if (rightChar in patternFreq) { 18 | patternFreq[rightChar]-- 19 | 20 | // If matched all copies of 1 char from pattern 21 | if (patternFreq[rightChar] == 0) { 22 | matched++ 23 | } 24 | } 25 | 26 | // If match all distinct chars from pattern, we're done! 27 | if (matched == Object.keys(patternFreq).length) { 28 | return true 29 | } 30 | 31 | // If right side of window > pattern length, shrink window for next loop 32 | if (end >= pattern.length - 1) { 33 | leftChar = str[start] 34 | start++ 35 | 36 | // If character leaving window was part of pattern: 37 | // 1. Add it back to frequency HashMap 38 | // 2. Subtract 1 from matched 39 | if (leftChar in patternFreq) { 40 | if (patternFreq[leftChar] === 0) { 41 | matched-- 42 | } 43 | patternFreq[leftChar]++ 44 | } 45 | } 46 | } 47 | 48 | return false 49 | } 50 | 51 | describe("solution()", () => { 52 | it("finds if string contains any permutation of a pattern", () => { 53 | expect(solution("oicbedbcaf", "abc")).toEqual(true) 54 | expect(solution("oidbcaf", "abc")).toEqual(true) 55 | expect(solution("odicf", "dc")).toEqual(false) 56 | expect(solution("bcdxabcdy", "bcdyabcdx")).toEqual(true) 57 | expect(solution("aaacb", "abc")).toEqual(true) 58 | }) 59 | }) 60 | 61 | //----My Slow Answer-------- 62 | 63 | // Time complexity: O(n * m). n = str size. m = pattern size. 64 | // Space complexity: O(m) 65 | function slow_solution(str, pattern) { 66 | let [hasPermutation, start, maxLength] = [false, 0, 0] 67 | let [patternFreq, oldPatternFreq] = [{}, {}] 68 | 69 | for (let i = 0; i < pattern.length; i++) { 70 | patternFreq[pattern[i]] ? patternFreq[pattern[i]]++ : patternFreq[pattern[i]] = 1 71 | } 72 | Object.assign(oldPatternFreq, patternFreq) 73 | 74 | for (let end = 0; end < str.length; end++) { 75 | rightChar = str[end] 76 | 77 | if (patternFreq[rightChar]) { 78 | patternFreq[rightChar]-- 79 | } else { 80 | start = end + 1 81 | Object.assign(patternFreq, oldPatternFreq) // Weakness: This is O(m)! 82 | } 83 | 84 | const length = end - start + 1 85 | maxLength = Math.max(maxLength, length) 86 | 87 | if (maxLength == pattern.length) { 88 | hasPermutation = true 89 | break 90 | } 91 | } 92 | 93 | return hasPermutation 94 | } 95 | 96 | // Output to my slow answer: 97 | // string: oicbedbcaf, pattern: abc 98 | 99 | // patternFreq: { "a": 1, "b": 1, "c": 1 } 100 | // rightChar: o 101 | // start: 0, end: 0, substring: o 102 | // rightChar not in pattern 103 | // maxLength: 0, length: 0 104 | 105 | // patternFreq: { "a": 1, "b": 1, "c": 1 } 106 | // rightChar: i 107 | // start: 1, end: 1, substring: i 108 | // rightChar not in pattern 109 | // maxLength: 0, length: 0 110 | 111 | // patternFreq: { "a": 1, "b": 1, "c": 1 } 112 | // rightChar: c 113 | // start: 2, end: 2, substring: c 114 | // rightChar in pattern 115 | // maxLength: 1, length: 1 116 | 117 | // patternFreq: { "a": 1, "b": 1, "c": 0 } 118 | // rightChar: b 119 | // start: 2, end: 3, substring: cb 120 | // rightChar in pattern 121 | // maxLength: 2, length: 2 122 | 123 | // patternFreq: { "a": 1, "b": 0, "c": 0 } 124 | // rightChar: e 125 | // start: 2, end: 4, substring: cbe 126 | // rightChar not in pattern 127 | // maxLength: 2, length: 0 128 | 129 | // patternFreq: { "a": 1, "b": 1, "c": 1 } 130 | // rightChar: d 131 | // start: 5, end: 5, substring: d 132 | // rightChar not in pattern 133 | // maxLength: 2, length: 0 134 | 135 | // patternFreq: { "a": 1, "b": 1, "c": 1 } 136 | // rightChar: b 137 | // start: 6, end: 6, substring: b 138 | // rightChar in pattern 139 | // maxLength: 2, length: 1 140 | 141 | // patternFreq: { "a": 1, "b": 0, "c": 1 } 142 | // rightChar: c 143 | // start: 6, end: 7, substring: bc 144 | // rightChar in pattern 145 | // maxLength: 2, length: 2 146 | 147 | // patternFreq: { "a": 1, "b": 0, "c": 0 } 148 | // rightChar: a 149 | // start: 6, end: 8, substring: bca 150 | // rightChar in pattern 151 | // maxLength: 3, length: 3 152 | -------------------------------------------------------------------------------- /hackerrank/10-days-of-javascript/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "program": "${workspaceFolder}/0-data-types.js" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /hackerrank/10-days-of-javascript/0-data-types.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | process.stdin.resume(); 4 | process.stdin.setEncoding('utf-8'); 5 | 6 | let inputString = ''; 7 | let currentLine = 0; 8 | 9 | process.stdin.on('data', inputStdin => { 10 | inputString += inputStdin; 11 | }); 12 | 13 | process.stdin.on('end', _ => { 14 | inputString = inputString.trim().split('\n').map(string => { 15 | return string.trim(); 16 | }); 17 | 18 | main(); 19 | }); 20 | 21 | function readLine() { 22 | return inputString[currentLine++]; 23 | } 24 | 25 | /** 26 | * The variables 'firstInteger', 'firstDecimal', and 'firstString' are declared for you -- do not modify them. 27 | * Print three lines: 28 | * 1. The sum of 'firstInteger' and the Number representation of 'secondInteger'. 29 | * 2. The sum of 'firstDecimal' and the Number representation of 'secondDecimal'. 30 | * 3. The concatenation of 'firstString' and 'secondString' ('firstString' must be first). 31 | * 32 | * Parameter(s): 33 | * secondInteger - The string representation of an integer. 34 | * secondDecimal - The string representation of a floating-point number. 35 | * secondString - A string consisting of one or more space-separated words. 36 | **/ 37 | function performOperation(secondInteger, secondDecimal, secondString) { 38 | // Declare a variable named 'firstInteger' and initialize with integer value 4. 39 | const firstInteger = 4; 40 | 41 | // Declare a variable named 'firstDecimal' and initialize with floating-point value 4.0. 42 | const firstDecimal = 4.0; 43 | 44 | // Declare a variable named 'firstString' and initialize with the string "HackerRank". 45 | const firstString = 'HackerRank '; 46 | 47 | // Write code that uses console.log to print the sum of the 'firstInteger' and 'secondInteger' (converted to a Number type) on a new line. 48 | console.log(firstInteger + parseInt(secondInteger)); 49 | 50 | // Write code that uses console.log to print the sum of 'firstDecimal' and 'secondDecimal' (converted to a Number type) on a new line. 51 | console.log(firstDecimal + parseFloat(secondDecimal)); 52 | 53 | // Write code that uses console.log to print the concatenation of 'firstString' and 'secondString' on a new line. The variable 'firstString' must be printed first. 54 | console.log(firstString + secondString); 55 | } 56 | 57 | function main() { 58 | const secondInteger = readLine(); 59 | const secondDecimal = readLine(); 60 | const secondString = readLine(); 61 | 62 | performOperation(secondInteger, secondDecimal, secondString); 63 | } 64 | -------------------------------------------------------------------------------- /hackerrank/10-days-of-javascript/1-factorial.js: -------------------------------------------------------------------------------- 1 | function factorial(n) { 2 | // Both factorial functions: O(n) time complexity 3 | 4 | // recursive: O(n) space complexity 5 | // if (n <= 1) { 6 | // return 1; 7 | // } 8 | // return n * factorial(n - 1); 9 | 10 | // iterative: O(1) space complexity 11 | var ans = 1; 12 | for (var i = 1; i <= n; i++) { 13 | ans *= i; 14 | } 15 | return ans; 16 | } 17 | -------------------------------------------------------------------------------- /hackerrank/10-days-of-javascript/1-let-and-const.js: -------------------------------------------------------------------------------- 1 | function main() { 2 | // Write your code here. Read input using 'readLine()' and print output using 'console.log()'. 3 | const PI = Math.PI; 4 | let r = parseFloat(readLine()); 5 | // Print the area of the circle: 6 | console.log(PI * r * r); 7 | // Print the perimeter of the circle: 8 | console.log(2 * PI * r); 9 | // ... 10 | } 11 | -------------------------------------------------------------------------------- /hackerrank/2d_array_ds.go: -------------------------------------------------------------------------------- 1 | // https://www.hackerrank.com/challenges/2d-array/problem 2 | 3 | func hourglassSum(arr [][]int32) int32 { 4 | var maxsum int32 = -63 // lowest a sum can go, with seven -9's 5 | var sum int32 = -63 6 | for row := 0; row <= len(arr) - 3; row++ { 7 | for col := 0; col <= len(arr[0]) - 3; col++ { 8 | sum = arr[row][col] + arr[row][col+1] + arr[row][col+2] + 9 | arr[row+1][col+1] + 10 | arr[row+2][col] + arr[row+2][col+1] + arr[row+2][col+2] 11 | if sum > maxsum { 12 | maxsum = sum 13 | } 14 | } 15 | } 16 | 17 | return maxsum 18 | } 19 | -------------------------------------------------------------------------------- /hackerrank/balanced_brackets.go: -------------------------------------------------------------------------------- 1 | // https://www.hackerrank.com/challenges/balanced-brackets/problem 2 | 3 | func isBalanced(s string) string { 4 | stack := []string{} 5 | hash := map[string]string{ 6 | "(": ")", 7 | "[": "]", 8 | "{": "}", 9 | } 10 | var top string 11 | 12 | for _, char := range strings.Split(s, "") { 13 | if len(stack) > 0 { 14 | top = stack[len(stack)-1] 15 | } else { 16 | top = "" 17 | } 18 | if hash[top] == char { 19 | stack = stack[:len(stack)-1] // pop off stack 20 | } else { 21 | stack = append(stack, char) 22 | } 23 | } 24 | if len(stack) > 0 { 25 | return "NO" 26 | } 27 | return "YES" 28 | } 29 | -------------------------------------------------------------------------------- /hackerrank/binary_tree_level_average.rb: -------------------------------------------------------------------------------- 1 | # Return array with average value of each binary tree level 2 | 3 | class Node 4 | attr_accessor :left, :right, :val 5 | 6 | def initialize(val) 7 | @val = val 8 | @left = nil 9 | @right = nil 10 | end 11 | end 12 | 13 | t = Node.new(4) 14 | t.left = Node.new(7) 15 | t.right = Node.new(9) 16 | t.left.left = Node.new(10) 17 | t.right.right = Node.new(6) 18 | t.left.right = Node.new(2) 19 | t.left.right.right = Node.new(6) 20 | t.left.right.right.left = Node.new(2) 21 | 22 | # 4 23 | # / \ 24 | # 7 9 25 | # / \ \ 26 | # 10 2 6 27 | # \ 28 | # 6 29 | # / 30 | # 2 31 | 32 | # use BFS 33 | def average_tree_level(t) 34 | return [] if t.nil? 35 | avgs = [] 36 | sum = 0 37 | queue = [t] 38 | while !queue.empty? do 39 | level_size = queue.size 40 | current_level = [] 41 | puts "level_size: #{level_size}, queue: #{queue.map{|q| q.val}}, avgs: #{avgs}" 42 | level_size.times do 43 | node = queue.shift 44 | current_level << node.val 45 | if node.left 46 | queue << node.left 47 | end 48 | if node.right 49 | queue << node.right 50 | end 51 | end 52 | avgs << current_level 53 | sum = 0 54 | end 55 | 56 | avgs 57 | end 58 | 59 | p average_tree_level(t) 60 | 61 | # avgs << 4 / 1 62 | # (7 + 9) / 2 loop 1 time 63 | # (10 + 2 + 6) / 3 loop 2 times 64 | # 6 / 1 loop 3 times 65 | # 2 / 1 loop 1 time 66 | -------------------------------------------------------------------------------- /hackerrank/count_triplets.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "os" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | // Best explanation: https://www.youtube.com/watch?v=tBFZMaWP0W8 13 | // Complete the countTriplets function below. 14 | func countTriplets(arr []int, r int) int { 15 | count := 0 16 | rfreq, lfreq := make(map[int]int), make(map[int]int) 17 | for _, num := range arr { 18 | rfreq[num]++ 19 | } 20 | 21 | for i := 0; i < len(arr); i++ { 22 | mid := arr[i] 23 | cleft, cright := 0, 0 24 | rfreq[mid]-- 25 | if mid%r == 0 && lfreq[mid/r] > 0 { 26 | cleft = lfreq[mid/r] 27 | } 28 | if rfreq[mid*r] > 0 { 29 | cright = rfreq[mid*r] 30 | } 31 | count += cleft * cright 32 | lfreq[mid]++ 33 | } 34 | return count 35 | } 36 | 37 | func main() { 38 | reader := bufio.NewReaderSize(os.Stdin, 16*1024*1024) 39 | 40 | stdout, err := os.Create(os.Getenv("OUTPUT_PATH")) 41 | checkError(err) 42 | 43 | defer stdout.Close() 44 | 45 | writer := bufio.NewWriterSize(stdout, 16*1024*1024) 46 | 47 | nr := strings.Split(strings.TrimSpace(readLine(reader)), " ") 48 | 49 | nTemp, err := strconv.Atoi(nr[0]) 50 | // nTemp, err := strconv.ParseInt(nr[0], 10, 64) 51 | checkError(err) 52 | n := int(nTemp) 53 | 54 | r, err := strconv.Atoi(nr[1]) 55 | // r, err := strconv.ParseInt(nr[1], 10, 64) 56 | checkError(err) 57 | 58 | arrTemp := strings.Split(strings.TrimSpace(readLine(reader)), " ") 59 | 60 | var arr []int 61 | 62 | for i := 0; i < int(n); i++ { 63 | arrItem, err := strconv.Atoi(arrTemp[i]) 64 | // arrItem, err := strconv.ParseInt(arrTemp[i], 10, 64) 65 | checkError(err) 66 | arr = append(arr, arrItem) 67 | } 68 | 69 | ans := countTriplets(arr, r) 70 | 71 | fmt.Fprintf(writer, "%d\n", ans) 72 | 73 | writer.Flush() 74 | } 75 | 76 | func readLine(reader *bufio.Reader) string { 77 | str, _, err := reader.ReadLine() 78 | if err == io.EOF { 79 | return "" 80 | } 81 | 82 | return strings.TrimRight(string(str), "\r\n") 83 | } 84 | 85 | func checkError(err error) { 86 | if err != nil { 87 | panic(err) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /hackerrank/cracking-the-coding-interview/arrays-left-rotation.js: -------------------------------------------------------------------------------- 1 | // Both are same: 2 | // https://www.hackerrank.com/challenges/ctci-array-left-rotation/problem 3 | // https://www.hackerrank.com/challenges/array-left-rotation/problem 4 | 5 | function main() { 6 | var n_temp = readLine().split(' '); 7 | var n = parseInt(n_temp[0]); 8 | var k = parseInt(n_temp[1]); 9 | a = readLine().split(' '); 10 | a = a.map(Number); 11 | let ans; 12 | if (k == n) { 13 | ans = a; 14 | } 15 | ans = a.slice(k).concat(a.slice(0, k)); 16 | console.log(ans.join(' ')); 17 | } 18 | -------------------------------------------------------------------------------- /hackerrank/cracking-the-coding-interview/dp-coin-change.js: -------------------------------------------------------------------------------- 1 | // https://www.hackerrank.com/challenges/ctci-coin-change/problem 2 | 3 | function main() { 4 | var n_temp = readLine().split(' '); 5 | var n = parseInt(n_temp[0]); //target sum 6 | var m = parseInt(n_temp[1]); //# of coins 7 | coins = readLine().split(' '); 8 | coins = coins.map(Number); 9 | 10 | let ways = new Array(n + 1).fill(0); 11 | ways[0] = 1; 12 | coins.forEach((coin) => { 13 | for (let sum = coin; sum <= n; sum++) { 14 | ways[sum] += ways[sum - coin]; 15 | } 16 | }); 17 | console.log(ways[n]); 18 | } 19 | -------------------------------------------------------------------------------- /hackerrank/equalizing_array_elements.rb: -------------------------------------------------------------------------------- 1 | #!/bin/ruby 2 | 3 | require 'json' 4 | require 'stringio' 5 | 6 | # Complete the 'minOperations' function below. 7 | # 8 | # The function is expected to return an INTEGER. 9 | # The function accepts following parameters: 10 | # 1. INTEGER_ARRAY arr 11 | # 2. INTEGER threshold 12 | # 3. INTEGER d 13 | 14 | def minOperations(arr, threshold, d) 15 | ops = 0 16 | fr = freq(arr) 17 | max_equals = fr.values.sort_by{|k,v| k}.last[0] 18 | return 0 if max_equals >= threshold 19 | 20 | while max_equals < threshold 21 | ops += 1 22 | arr.size.times do |i| 23 | arr[i] /= d 24 | num = arr[i] 25 | fr[num] ? fr[num] = [fr[num][0] + 1, fr[num][1] + ops] : fr[num] = [1, ops] 26 | return fr[num][1] if fr[num][0] >= threshold 27 | end 28 | end 29 | ops 30 | end 31 | 32 | def freq(a) 33 | fr = {} 34 | a.each do |num| 35 | fr[num] ? fr[num][0] += 1 : fr[num] = [1, 0] 36 | end 37 | fr 38 | end 39 | 40 | fptr = File.open(ENV['OUTPUT_PATH'], 'w') 41 | -------------------------------------------------------------------------------- /hackerrank/left-rotation.js: -------------------------------------------------------------------------------- 1 | // Both are same: 2 | // https://www.hackerrank.com/challenges/array-left-rotation/problem 3 | // https://www.hackerrank.com/challenges/ctci-array-left-rotation/problem 4 | 5 | function leftRotation(a, d) { 6 | if (d == a.length) { 7 | return a; 8 | } 9 | return a.slice(d).concat(a.slice(0, d)); 10 | } 11 | -------------------------------------------------------------------------------- /hackerrank/logic_expression_spec.rb: -------------------------------------------------------------------------------- 1 | def eval(exp) 2 | stack = [] 3 | num, op = '', '' 4 | 5 | exp.each_char do |char| 6 | # next if [' ', ','].include?(char) 7 | if ['[', '&', '|', '!', '0', '1'].include?(char) 8 | stack << char 9 | elsif char == ']' 10 | if stack[-2] == '[' 11 | break 12 | elsif stack[-2] == '!' 13 | num = stack.pop 14 | stack.pop 15 | stack.pop 16 | stack << simple_eval('!', num) 17 | else 18 | num2 = stack.pop 19 | num1 = stack.pop 20 | op = stack.pop 21 | stack.pop 22 | return 'Error: First value is not logic operator' if op == '[' 23 | stack << simple_eval(op, num1, num2) 24 | end 25 | end 26 | end 27 | 28 | stack.pop 29 | end 30 | 31 | def to_bool(num) 32 | num.to_i == 1 ? true : false 33 | end 34 | 35 | def to_string(bool) 36 | bool ? '1' : '0' 37 | end 38 | 39 | def simple_eval(op, num1, num2 = nil) 40 | case op 41 | when '&' 42 | to_string(to_bool(num1) && to_bool(num2)) 43 | when '|' 44 | to_string(to_bool(num1) || to_bool(num2)) 45 | when '!' 46 | to_string(!to_bool(num1)) 47 | else 48 | 'Error: First value is not logic operator' 49 | end 50 | end 51 | 52 | describe '#eval' do 53 | it 'evaluates expression with no nesting' do 54 | expect(eval('[&, 1, 0]')).to eq '0' 55 | expect(eval('[&, 0, 0]')).to eq '0' 56 | expect(eval('[&, 1, 1]')).to eq '1' 57 | expect(eval('[!, 1]')).to eq '0' 58 | expect(eval('[!, 0]')).to eq '1' 59 | expect(eval('[|, 1, 0]')).to eq '1' 60 | expect(eval('[|, 1, 1]')).to eq '1' 61 | expect(eval('[|, 0, 0]')).to eq '0' 62 | expect(eval('[1, 0]')).to eq 'Error: First value is not logic operator' 63 | end 64 | 65 | it 'evaluates 1 level of nesting' do 66 | expect(eval('[&, 1, [!, 0]]')).to eq '1' 67 | expect(eval('[|, [&, 1, [!, 0]], 0]')).to eq '1' 68 | end 69 | 70 | it 'evaluates 2 levels of nesting' do 71 | expect(eval('[|, [&, 1, [!, 0]], [!, [|, [|, 1, 0], [!, 1]]]]')).to eq '1' 72 | 73 | # OUTPUT: 74 | # ["["] 75 | # ["[", "|"] 76 | # ["[", "|", "["] 77 | # ["[", "|", "[", "&"] 78 | # ["[", "|", "[", "&", "1"] 79 | # ["[", "|", "[", "&", "1", "["] 80 | # ["[", "|", "[", "&", "1", "[", "!"] 81 | # ["[", "|", "[", "&", "1", "[", "!", "0"] 82 | # ["[", "|", "[", "&", "1", "1"] 83 | # ["[", "|", "1"] 84 | # ["[", "|", "1", "["] 85 | # ["[", "|", "1", "[", "!"] 86 | # ["[", "|", "1", "[", "!", "["] 87 | # ["[", "|", "1", "[", "!", "[", "|"] 88 | # ["[", "|", "1", "[", "!", "[", "|", "["] 89 | # ["[", "|", "1", "[", "!", "[", "|", "[", "|"] 90 | # ["[", "|", "1", "[", "!", "[", "|", "[", "|", "1"] 91 | # ["[", "|", "1", "[", "!", "[", "|", "[", "|", "1", "0"] 92 | # ["[", "|", "1", "[", "!", "[", "|", "1"] 93 | # ["[", "|", "1", "[", "!", "[", "|", "1", "["] 94 | # ["[", "|", "1", "[", "!", "[", "|", "1", "[", "!"] 95 | # ["[", "|", "1", "[", "!", "[", "|", "1", "[", "!", "1"] 96 | # ["[", "|", "1", "[", "!", "[", "|", "1", "0"] 97 | # ["[", "|", "1", "[", "!", "1"] 98 | # ["[", "|", "1", "0"] 99 | # ["1"] 100 | end 101 | end 102 | -------------------------------------------------------------------------------- /hackerrank/make_anagram_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | // https://www.hackerrank.com/challenges/ctci-making-anagrams 10 | 11 | // Given two strings that may or not be of same length, find 12 | // min # of character deletions needed to make both into anagrams. 13 | // Any characters can be deleted from either string. 14 | 15 | // Answer with maps: 16 | func makeAnagram(a string, b string) int32 { 17 | var total int32 = 0 18 | freqA := make(map[rune]int32) 19 | freqB := make(map[rune]int32) 20 | chars := make(map[rune]bool) 21 | 22 | for _, char := range a { 23 | freqA[char]++ 24 | chars[char] = true 25 | } 26 | // r := rune('s'), string(r) => "s" 27 | 28 | for _, char := range b { 29 | freqB[char]++ 30 | chars[char] = true 31 | } 32 | 33 | for char := range chars { 34 | if freqA[char] > freqB[char] { 35 | total += freqA[char] - freqB[char] 36 | } else { 37 | total += freqB[char] - freqA[char] 38 | } 39 | } 40 | 41 | return total 42 | } 43 | 44 | // Answer with arrays: 45 | // func makeAnagram(a string, b string) int32 { 46 | // var total int32 = 0 47 | // var freqA [26]int32 48 | // var freqB [26]int32 49 | // chars := make(map[rune]bool) 50 | 51 | // // r := rune('s'), string(r) => "s" 52 | 53 | // for _, char := range a { 54 | // i := charToIndex(char) 55 | // freqA[i]++ 56 | // chars[char] = true 57 | // } 58 | 59 | // for _, char := range b { 60 | // i := charToIndex(char) 61 | // freqB[i]++ 62 | // chars[char] = true 63 | // } 64 | 65 | // for char := range chars { 66 | // i := charToIndex(char) 67 | // if freqA[i] > freqB[i] { 68 | // total += freqA[i] - freqB[i] 69 | // } else { 70 | // total += freqB[i] - freqA[i] 71 | // } 72 | // } 73 | 74 | // return total 75 | // } 76 | 77 | // 'a' = 0, 'b' = 1,...'z' = 25 78 | // func charToIndex(char rune) int { 79 | // return int(rune(char) - 97) 80 | // } 81 | 82 | func TestAnagram(t *testing.T) { 83 | tests := []struct { 84 | a string 85 | b string 86 | expected int32 87 | }{ 88 | {"cde", "dcf", 2}, 89 | {"cde", "abc", 4}, 90 | {"mississippi", "assistant", 12}, 91 | } 92 | for _, test := range tests { 93 | assert.Equal(t, test.expected, makeAnagram(test.a, test.b)) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /hackerrank/mini-max-sum.js: -------------------------------------------------------------------------------- 1 | // https://www.hackerrank.com/challenges/mini-max-sum/problem 2 | 3 | function miniMaxSum(arr) { 4 | const reducer = (accumulator, currentValue) => accumulator + currentValue; 5 | const total = arr.reduce(reducer); 6 | let min = max = total - arr[0]; 7 | arr.forEach((num) => { 8 | let sum = total - num; 9 | if (sum > max) { 10 | max = sum; 11 | } else if (sum < min) { 12 | min = sum; 13 | } 14 | }); 15 | console.log(`${min} ${max}`); 16 | } 17 | -------------------------------------------------------------------------------- /hackerrank/minimum_swaps2.go: -------------------------------------------------------------------------------- 1 | // https://www.hackerrank.com/challenges/minimum-swaps-2/problem 2 | // Best video explanation: https://www.youtube.com/watch?v=JrzIgYS3SqM 3 | 4 | // 1 => 2 => 4 => 3 => 1 5 | 6 | // Complete the minimumSwaps function below. 7 | func minimumSwaps(arr []int32) int32 { 8 | ans := int32(0) 9 | visited := make(map[int32]bool) 10 | 11 | for i := int32(0); i < int32(len(arr)); i++ { 12 | if visited[i] || arr[i] == int32(i + 1) { 13 | visited[i] = true 14 | continue 15 | } 16 | cycleLength := int32(1) 17 | nextIndex := arr[i] - 1 18 | 19 | for nextIndex != i { 20 | visited[nextIndex] = true 21 | cycleLength += 1 22 | nextIndex = arr[nextIndex] - 1 23 | } 24 | ans += cycleLength - 1 25 | } 26 | return ans 27 | } 28 | -------------------------------------------------------------------------------- /hackerrank/new_year_chaos.go: -------------------------------------------------------------------------------- 1 | // https://www.hackerrank.com/challenges/new-year-chaos/problem 2 | func minimumBribes(q []int32) { 3 | swaps := 0 4 | r := make([]int32, len(q)) 5 | start := int32(1) 6 | for i := range r { 7 | r[i] = start 8 | start++ 9 | } 10 | // fmt.Println(q) 11 | for i := int32(0); i < int32(len(q)); i++ { 12 | if q[i] == r[i] { 13 | continue 14 | } 15 | if len(q) > 1 && i < int32(len(q)) - 1 && q[i] == r[i+1] { 16 | swaps++ 17 | r[i], r[i+1] = r[i+1], r[i] 18 | // fmt.Println(r, swaps) 19 | continue 20 | } else if len(q) > 2 && i < int32(len(q)) - 2 && q[i] == r[i + 2] { 21 | swaps += 2 22 | r[i + 2] = r[i + 1] 23 | r[i + 1] = r[i] 24 | r[i] = q[i] 25 | // fmt.Println(r, swaps) 26 | continue 27 | } else if len(q) > 3 && i < int32(len(q)) - 3 && q[i] >= r[i + 3] { 28 | fmt.Println("Too chaotic") 29 | return 30 | } 31 | 32 | } 33 | fmt.Println(swaps) 34 | } 35 | -------------------------------------------------------------------------------- /hackerrank/practice.sql: -------------------------------------------------------------------------------- 1 | -- https://www.hackerrank.com/challenges/revising-the-select-query/problem 2 | 3 | select 4 | * 5 | from 6 | city 7 | where 8 | population > 100000 9 | and countrycode = 'USA' 10 | 11 | -- https://www.hackerrank.com/challenges/revising-the-select-query-2/problem 12 | select 13 | name 14 | from 15 | city 16 | where 17 | population > 120000 18 | and countrycode = 'USA' 19 | 20 | -- https://www.hackerrank.com/challenges/asian-population/problem 21 | 22 | select 23 | sum(city.population) 24 | from 25 | city 26 | inner join country on city.countrycode = country.code 27 | where 28 | country.continent = 'Asia' 29 | 30 | -- The PADS 31 | -- https://www.hackerrank.com/challenges/the-pads/problem 32 | 33 | select 34 | concat(name, '(', left(occupation, 1), ')') 35 | from 36 | occupations 37 | order by 38 | name; 39 | 40 | select 41 | concat( 42 | 'There are a total of ', 43 | count(*), 44 | ' ', 45 | lower(occupation), 46 | 's.' 47 | ) 48 | from 49 | occupations 50 | group by 51 | occupation 52 | order by 53 | count(*), 54 | occupation; 55 | 56 | -- SQL Project Planning 57 | -- https://www.hackerrank.com/challenges/sql-projects/problem 58 | 59 | SET 60 | sql_mode = ''; 61 | SELECT 62 | Start_Date, 63 | End_Date 64 | FROM 65 | ( 66 | SELECT 67 | Start_Date 68 | FROM 69 | Projects 70 | WHERE 71 | Start_Date NOT IN ( 72 | SELECT 73 | End_Date 74 | FROM 75 | Projects 76 | ) 77 | ) a, 78 | ( 79 | SELECT 80 | End_Date 81 | FROM 82 | Projects 83 | WHERE 84 | End_Date NOT IN ( 85 | SELECT 86 | Start_Date 87 | FROM 88 | Projects 89 | ) 90 | ) b 91 | WHERE 92 | Start_Date < End_Date 93 | GROUP BY 94 | Start_Date 95 | ORDER BY 96 | DATEDIFF(End_Date, Start_Date), 97 | Start_Date 98 | 99 | -- https://www.hackerrank.com/challenges/weather-observation-station-18/problem 100 | 101 | select 102 | round( 103 | abs(min(lat_n) - max(lat_n)) + abs(min(long_w) - max(long_w)), 104 | 4 105 | ) 106 | from 107 | station 108 | 109 | -- https://www.hackerrank.com/challenges/symmetric-pairs/problem 110 | 111 | select 112 | f1.x, 113 | f1.y 114 | from 115 | functions f1 116 | join functions f2 117 | where 118 | f1.y = f2.x 119 | and f1.x = f2.y 120 | and f1.x < f1.y 121 | 122 | union 123 | 124 | select 125 | x, 126 | y 127 | from 128 | functions 129 | where 130 | x = y 131 | group by 132 | x, 133 | y 134 | having 135 | count(*) > 1 136 | order by 137 | x; 138 | -------------------------------------------------------------------------------- /hackerrank/ransom_note.go: -------------------------------------------------------------------------------- 1 | // https://www.hackerrank.com/challenges/ctci-ransom-note/problem 2 | package main 3 | 4 | import ( 5 | "bufio" 6 | "fmt" 7 | "io" 8 | "os" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | // checkMagazine has the following parameters: 14 | // magazine: an array of strings, each a word in the magazine 15 | // note: an array of strings, each a word in the ransom note 16 | 17 | // Complete the checkMagazine function below. 18 | func checkMagazine(magazine []string, note []string) { 19 | fMag := make(map[string]int) 20 | for _, word := range magazine { 21 | fMag[word]++ 22 | } 23 | for _, word := range note { 24 | if fMag[word] > 0 { 25 | fMag[word]-- 26 | } else { 27 | fmt.Println("No") 28 | return 29 | } 30 | } 31 | 32 | fmt.Println("Yes") 33 | } 34 | 35 | func main() { 36 | reader := bufio.NewReaderSize(os.Stdin, 1024 * 1024) 37 | 38 | mn := strings.Split(readLine(reader), " ") 39 | 40 | mTemp, err := strconv.ParseInt(mn[0], 10, 64) 41 | checkError(err) 42 | m := int32(mTemp) 43 | 44 | nTemp, err := strconv.ParseInt(mn[1], 10, 64) 45 | checkError(err) 46 | n := int32(nTemp) 47 | 48 | magazineTemp := strings.Split(readLine(reader), " ") 49 | 50 | var magazine []string 51 | 52 | for i := 0; i < int(m); i++ { 53 | magazineItem := magazineTemp[i] 54 | magazine = append(magazine, magazineItem) 55 | } 56 | 57 | noteTemp := strings.Split(readLine(reader), " ") 58 | 59 | var note []string 60 | 61 | for i := 0; i < int(n); i++ { 62 | noteItem := noteTemp[i] 63 | note = append(note, noteItem) 64 | } 65 | 66 | checkMagazine(magazine, note) 67 | } 68 | 69 | func readLine(reader *bufio.Reader) string { 70 | str, _, err := reader.ReadLine() 71 | if err == io.EOF { 72 | return "" 73 | } 74 | 75 | return strings.TrimRight(string(str), "\r\n") 76 | } 77 | 78 | func checkError(err error) { 79 | if err != nil { 80 | panic(err) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /hackerrank/sherlock_and_anagrams.go: -------------------------------------------------------------------------------- 1 | // https://www.hackerrank.com/challenges/sherlock-and-anagrams/problem 2 | // Loop through all possible substrings and make substring frequency map. 3 | // Each substring's frequency has "n choose 2" possible pairs. Loop through each frequency to find sum of all "frequency choose 2". 4 | 5 | func sherlockAndAnagrams(s string) int { 6 | ssmap := make(map[[26]int]int) // substring frequency map 7 | 8 | // loop through all possible substrings 9 | for start := 0; start < len(s); start++ { 10 | for end := start; end < len(s); end++ { 11 | substr := s[start:end + 1] 12 | ssmap[freqArray(substr)]++ 13 | } 14 | } 15 | delete(ssmap, freqArray(s)) // don't count original string s 16 | 17 | numPairs := 0 18 | for _, freq := range ssmap { 19 | // Each substring's frequency has "n choose 2" pair combinations. 20 | // combination("n choose 2") = n!/2!(n-2)! = n(n-1)/2 21 | numPairs += freq * (freq - 1) / 2 22 | } 23 | 24 | return numPairs 25 | } 26 | 27 | func charToIndex(char rune) int { 28 | return int(rune(char) - 97) 29 | } 30 | func freqArray(s string) [26]int { 31 | var freq [26]int 32 | for _, char := range s { 33 | i := charToIndex(char) 34 | freq[i]++ 35 | } 36 | return freq 37 | } 38 | 39 | // O(n): linear 40 | // func isAnagram(s, t string) bool { 41 | // if len(s) != len(t) { 42 | // return false 43 | // } 44 | // freq := make(map[rune]int) 45 | // runes := []rune(s) 46 | // runet := []rune(t) 47 | // for i := 0; i < len(runes); i++ { 48 | // schar := runes[i] 49 | // tchar := runet[i] 50 | // freq[schar]++ 51 | // freq[tchar]-- 52 | // } 53 | // for _, val := range freq { 54 | // if val != 0 { 55 | // return false 56 | // } 57 | // } 58 | // return true 59 | // } 60 | 61 | // import "strings" 62 | // import "sort" 63 | 64 | // // O(n log n) 65 | // func isAnagram(s, t string) bool { 66 | // if len(s) != len(t) { 67 | // return false 68 | // } 69 | // sArray := strings.Split(s, "") 70 | // tArray := strings.Split(t, "") 71 | // sort.Strings(sArray) 72 | // sort.Strings(tArray) 73 | 74 | // return strings.Join(sArray, "") == strings.Join(tArray, "") 75 | // } 76 | -------------------------------------------------------------------------------- /hackerrank/sherlock_and_the_valid_string.go: -------------------------------------------------------------------------------- 1 | // https://www.hackerrank.com/challenges/sherlock-and-valid-string/problem 2 | // Valid if: 3 | // -All letters have same frequency freq 4 | // -All have the same frequency freq, except 1 character with frequency of 1 or freq + 1 5 | func isValid(s string) string { 6 | if len(s) == 1 { 7 | return "YES" 8 | } 9 | f := make(map[rune]int) // freq of each letter 10 | f2 := make(map[int]int) // freq of each frequency above 11 | 12 | for _, char := range s { 13 | f[char]++ 14 | } 15 | for _, freq := range f { 16 | f2[freq]++ 17 | } 18 | if len(f2) == 1 { // all letters have same frequency 19 | return "YES" 20 | } 21 | if len(f2) > 2 { // letters have at least 3 different frequencies 22 | return "NO" 23 | } 24 | 25 | maxff := 0 26 | commonFreq := 0 27 | for num, ff := range f2 { 28 | if ff > maxff { 29 | maxff = ff 30 | commonFreq = num 31 | } 32 | } 33 | 34 | for num, ff := range f2 { 35 | if ff != maxff { 36 | if ff == 1 && (num == 1 || num == commonFreq + 1) { 37 | return "YES" 38 | } 39 | } 40 | } 41 | 42 | return "NO" 43 | } 44 | -------------------------------------------------------------------------------- /hackerrank/two_strings.go: -------------------------------------------------------------------------------- 1 | // https://www.hackerrank.com/challenges/two-strings/problem 2 | func twoStrings(s1 string, s2 string) string { 3 | f1 := make(map[rune]int) 4 | for _, char := range s1 { 5 | f1[char]++ 6 | } 7 | for _, char := range s2 { 8 | if f1[char] > 0 { 9 | return "YES" 10 | } 11 | } 12 | return "NO" 13 | } 14 | -------------------------------------------------------------------------------- /leetcode/calculate_time.rb: -------------------------------------------------------------------------------- 1 | def calculate_time(keyboard, word) 2 | hash = {} 3 | keyboard.split('').each_with_index do |char, i| 4 | hash[char] = i 5 | end 6 | start = 0 7 | time = 0 8 | 9 | word.each_char do |char| 10 | time += (hash[char] - start).abs 11 | start = hash[char] 12 | end 13 | time 14 | end 15 | -------------------------------------------------------------------------------- /leetcode/cut_off_trees_spec.rb: -------------------------------------------------------------------------------- 1 | # https://leetcode.com/problems/cut-off-trees-for-golf-event/ 2 | # @param {Integer[][]} forest 3 | # @return {Integer} 4 | class Tree 5 | attr_accessor :f, :visited, :steps, :trees_cut 6 | 7 | def initialize(forest) 8 | @f = forest 9 | @steps = 0 10 | @visited = Array.new(forest.size) { |cell| Array.new(forest[0].size, '-') } 11 | end 12 | 13 | def print_grid(array) 14 | puts array.map { |row| row.join(' ') } 15 | puts 16 | end 17 | 18 | def cut 19 | row, col = 0, 0 20 | rowend = f.size - 1 21 | colend = f[0].size - 1 22 | nopath = false 23 | total_trees = num_trees(f) 24 | puts "total trees: #{total_trees}" 25 | @visited[0][0] = '+' 26 | if f[0][0] > 1 27 | @trees_cut = 1 28 | @f[0][0] = 1 29 | else 30 | @trees_cut = 0 31 | end 32 | 33 | while trees_cut < total_trees do 34 | steps_old = steps 35 | print_grid(visited) 36 | puts "row: #{row}, col: #{col}" 37 | puts "steps: #{steps}, trees_cut: #{trees_cut}" 38 | 39 | col += 1 40 | if col.between?(0, colend) && visited[row][col] == '-' && f[row][col] > 0 41 | step_forward(row, col) 42 | else 43 | col -= 1 44 | end 45 | 46 | row += 1 47 | if row.between?(0, rowend) && visited[row][col] == '-' && f[row][col] > 0 48 | step_forward(row, col) 49 | else 50 | row -= 1 51 | end 52 | 53 | col -= 1 54 | if col.between?(0, colend) && visited[row][col] == '-' && f[row][col] > 0 55 | step_forward(row, col) 56 | else 57 | col += 1 58 | end 59 | 60 | row -= 1 61 | cell = f[row][col] 62 | if row.between?(0, rowend) && visited[row][col] == '-' && f[row][col] > 0 63 | step_forward(row, col) 64 | else 65 | row += 1 66 | end 67 | 68 | if steps == steps_old 69 | nopath = true 70 | break 71 | end 72 | end 73 | 74 | return -1 if nopath 75 | puts "final steps: #{steps}" 76 | steps 77 | end 78 | 79 | def step_forward(row, col) 80 | @steps += 1 81 | if f[row][col] > 1 # a tree is there 82 | @trees_cut += 1 83 | @f[row][col] = 1 84 | print_grid(f) 85 | end 86 | @visited[row][col] = '+' 87 | end 88 | 89 | def num_trees(f) 90 | trees = 0 91 | f.each do |row| 92 | row.each do |cell| 93 | trees += 1 if cell > 1 94 | end 95 | end 96 | trees 97 | end 98 | end 99 | 100 | def cut_off_tree(forest) 101 | puts '--------------------------------' 102 | Tree.new(forest).cut 103 | end 104 | 105 | describe '#cut_off_tree' do 106 | context 'for 3x3 matrix' do 107 | it 'cuts trees clockwise' do 108 | forest = [ 109 | [1,2,3], 110 | [0,0,4], 111 | [7,6,5] 112 | ] 113 | expect(cut_off_tree(forest)).to eq 6 114 | end 115 | 116 | it 'returns -1 if stuck' do 117 | forest = [ 118 | [1,2,3], 119 | [0,0,0], 120 | [7,6,5] 121 | ] 122 | expect(cut_off_tree(forest)).to eq -1 123 | end 124 | 125 | it 'cuts trees clockwise, including starting cell' do 126 | forest = [ 127 | [2,3,4], 128 | [0,0,5], 129 | [8,7,6] 130 | ] 131 | expect(cut_off_tree(forest)).to eq 6 132 | end 133 | 134 | it '' do 135 | forest = [ 136 | [54581641,64080174,24346381,69107959], 137 | [86374198,61363882,68783324,79706116], 138 | [ 668150,92178815,89819108,94701471], 139 | [83920491,22724204,46281641,47531096], 140 | [89078499,18904913,25462145,60813308] 141 | ] 142 | 143 | expect(cut_off_tree(forest)).to eq 57 144 | end 145 | end 146 | end 147 | -------------------------------------------------------------------------------- /leetcode/easy/best_time_buy_sell_stock2.js: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/ 2 | // https://leetcode.com/submissions/detail/654472613/ 3 | // Runtime: 72 ms, faster than 81.54% of JavaScript online submissions for Best Time to Buy and Sell Stock II. 4 | // Memory Usage: 42.2 MB, less than 45.47% of JavaScript online submissions for Best Time to Buy and Sell Stock II. 5 | // Time to write code: 1:02 min. (Original time to code: 50 min) 6 | // O(n) time, O(1) space 7 | // 3 companies asking this: Amazon, Facebook, Microsoft 8 | 9 | /** 10 | * @param {number[]} prices 11 | * @return {number} 12 | */ 13 | let maxProfit = function(prices) { 14 | let profitTotal = 0 15 | 16 | for (let i = 0; i < prices.length - 1; i++) { 17 | if (prices[i] < prices[i + 1]) { 18 | profitTotal = profitTotal + prices[i + 1] - prices[i] 19 | } 20 | } 21 | 22 | return profitTotal 23 | } 24 | -------------------------------------------------------------------------------- /leetcode/easy/contains_duplicate.js: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/contains-duplicate/ 2 | // https://leetcode.com/submissions/detail/678259398/ 3 | // Time to code: 5:50 min 4 | /** 5 | * @param {number[]} nums 6 | * @return {boolean} 7 | */ 8 | // O(n) time, O(n) space 9 | let containsDuplicate = function (nums) { 10 | let hash = {} 11 | for (let num of nums) { 12 | if (hash[num]) { 13 | return true 14 | } else { 15 | hash[num] = true 16 | } 17 | } 18 | return false 19 | } 20 | -------------------------------------------------------------------------------- /leetcode/easy/intersection_of_2_arrays_ii.js: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/intersection-of-two-arrays-ii/ 2 | // https://leetcode.com/submissions/detail/678295919/ 3 | 4 | /** 5 | * @param {number[]} nums1 6 | * @param {number[]} nums2 7 | * @return {number[]} 8 | */ 9 | // O(n + m) time, O(min(n, m)) space 10 | // Companies: Amazon, Facebook, Yahoo 11 | // Time to code: 20:41 min 12 | 13 | let intersect = function(nums1, nums2) { 14 | [freq, ans] = [{}, []] 15 | 16 | // force num1 to be smaller array 17 | if (nums2.length < nums1.length) { 18 | [nums1, nums2] = [nums2, nums1] 19 | } 20 | 21 | // frequency hash 22 | for (let num of nums1) { 23 | if (freq[num]) { 24 | freq[num]++ 25 | } else { 26 | freq[num] = 1 27 | } 28 | } 29 | 30 | for (let num of nums2) { 31 | if (freq[num] > 0) { 32 | ans.push(num) 33 | freq[num]-- 34 | } 35 | } 36 | 37 | return ans 38 | } 39 | -------------------------------------------------------------------------------- /leetcode/easy/maximum_depth_binary_tree.js: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/maximum-depth-of-binary-tree/ 2 | // https://leetcode.com/submissions/detail/654945067/ 3 | // Runtime: 96 ms, faster than 54.45% of JavaScript online submissions for Maximum Depth of Binary Tree. 4 | // Memory Usage: 46 MB, less than 7.86% of JavaScript online submissions for Maximum Depth of Binary Tree. 5 | // Time to write code: 48 secs. (Original time: 26 min) 6 | // 3 companies asking this: LinkedIn, Google, Amazon 7 | 8 | // Time complexity: we visit each node exactly once = O(n) 9 | // Space complexity: in the worst case, the tree is completely unbalanced, 10 | // e.g. each node has only left child node, the recursion call would occur N times 11 | // (the height of the tree), therefore storage to keep the call stack would be O(n). 12 | // But in best case (the tree is completely balanced), the height of the tree would be log(n). 13 | // Therefore, the space complexity (with balanced tree) = O(log n) 14 | 15 | /** 16 | * Definition for a binary tree node. 17 | * function TreeNode(val, left, right) { 18 | * this.val = (val===undefined ? 0 : val) 19 | * this.left = (left===undefined ? null : left) 20 | * this.right = (right===undefined ? null : right) 21 | * } 22 | */ 23 | /** 24 | * @param {TreeNode} root 25 | * @return {number} 26 | */ 27 | 28 | // Use Depth-First Search + Recursion: 29 | let maxDepth = function(root) { 30 | // console.log(`val: ${root?.val}, L: ${root?.left}, R: ${root?.right}`) 31 | 32 | if (root === null) return 0 33 | // if (root.left === null || root.right === null) return 1 will NOT work, 34 | // since for example of [1,null,2], it exits tree before going down to level 2 35 | 36 | return 1 + Math.max(maxDepth(root.left), maxDepth(root.right)) 37 | } 38 | 39 | For input [3,9,20,null,null,15,7], output: 40 | 41 | val: 3, L: [object Object], R: [object Object] 42 | val: 9, L: null, R: null 43 | val: undefined, L: undefined, R: undefined 44 | val: undefined, L: undefined, R: undefined 45 | val: 20, L: [object Object], R: [object Object] 46 | val: 15, L: null, R: null 47 | val: undefined, L: undefined, R: undefined 48 | val: undefined, L: undefined, R: undefined 49 | val: 7, L: null, R: null 50 | val: undefined, L: undefined, R: undefined 51 | val: undefined, L: undefined, R: undefined 52 | _____________ 53 | // Approach #2: Tail Recursion + BFS 54 | 55 | // Tail recursion: https://www.interviewkickstart.com/learn/tail-recursion 56 | // https://www.geeksforgeeks.org/tail-recursion/ 57 | // https://stackoverflow.com/a/37010/2175188 58 | 59 | // One might have noticed that the above recursion solution is probably not the most optimal one in terms of the space complexity, and in the extreme case the overhead of call stack might even lead to stack overflow. 60 | 61 | // To address the issue, one can tweak the solution a bit to make it tail recursion, which is a specific form of recursion where the recursive call is the last action in the function. 62 | 63 | // The benefit of having tail recursion, is that for certain programming languages (e.g. C++) the compiler could optimize the memory allocation of call stack by reusing the same space for every recursive call, rather than creating the space for each one. As a result, one could obtain the constant space complexity \mathcal{O}(1)O(1) for the overhead of the recursive calls. 64 | 65 | // Here is a sample C++ solution. Note that the optimization of tail recursion is not supported by Python or Java. 66 | 67 | class Solution { 68 | 69 | private: 70 | // The queue that contains the next nodes to visit, 71 | // along with the level/depth that each node is located. 72 | queue> next_items; 73 | int max_depth = 0; 74 | 75 | /** 76 | * A tail recursion function to calculate the max depth 77 | * of the binary tree. 78 | */ 79 | int next_maxDepth() { 80 | 81 | if (next_items.size() == 0) { 82 | return max_depth; 83 | } 84 | 85 | auto next_item = next_items.front(); 86 | next_items.pop(); 87 | 88 | auto next_node = next_item.first; 89 | auto next_level = next_item.second + 1; 90 | 91 | max_depth = max(max_depth, next_level); 92 | 93 | // Add the nodes to visit in the following recursive calls. 94 | if (next_node->left != NULL) { 95 | next_items.push(make_pair(next_node->left, next_level)); 96 | } 97 | if (next_node->right != NULL) { 98 | next_items.push(make_pair(next_node->right, next_level)); 99 | } 100 | 101 | // The last action should be the ONLY recursive call 102 | // in the tail-recursion function. 103 | return next_maxDepth(); 104 | } 105 | 106 | public: 107 | int maxDepth(TreeNode* root) { 108 | if (root == NULL) return 0; 109 | 110 | // clear the previous queue. 111 | std::queue> empty; 112 | std::swap(next_items, empty); 113 | max_depth = 0; 114 | 115 | // push the root node into the queue to kick off the next visit. 116 | next_items.push(make_pair(root, 0)); 117 | 118 | return next_maxDepth(); 119 | } 120 | }; 121 | -------------------------------------------------------------------------------- /leetcode/easy/nearest_point_same_x_or_y_coordinate.js: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/find-nearest-point-that-has-the-same-x-or-y-coordinate/ 2 | // https://leetcode.com/submissions/detail/669787506/ 3 | // Runtime: 142 ms, faster than 40.59 % of JavaScript online submissions for Find Nearest Point That Has the Same X or Y Coordinate. 4 | // Memory Usage: 51.3 MB, less than 10.89 % of JavaScript online submissions for Find Nearest Point That Has the Same X or Y Coordinate. 5 | // O(n) time, O(1) space 6 | // Time to write code: 3:49 min. (Original time to code: 22 min) 7 | 8 | /** 9 | * @param {number} x 10 | * @param {number} y 11 | * @param {number[][]} points 12 | * @return {number} 13 | */ 14 | 15 | let nearestValidPoint = function (x, y, points) { 16 | let minIndex = -1 17 | let minDist = 10000 18 | 19 | for (let i in points) { 20 | let point = points[i] 21 | 22 | if (isValid(x, y, point)) { 23 | let manDist = manhattanDist(x, y, point[0], point[1]) 24 | 25 | if (manDist < minDist) { 26 | minDist = manDist 27 | minIndex = i 28 | } 29 | } 30 | } 31 | 32 | return minIndex 33 | } 34 | 35 | function manhattanDist(x1, y1, x2, y2) { 36 | return Math.abs(x1 - x2) + Math.abs(y1 - y2) 37 | } 38 | 39 | function isValid(x, y, point) { 40 | return (x === point[0] || y === point[1]) ? true : false 41 | } 42 | -------------------------------------------------------------------------------- /leetcode/easy/remove_duplicates_from_sorted_array.js: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/remove-duplicates-from-sorted-array/ 2 | // 3 companies asking this: Facebook, Amazon, Adobe 3 | // Use 2 pointers 4 | 5 | // Better Answer: 6 | // https://leetcode.com/submissions/detail/654421102/ 7 | // Runtime: 76 ms, faster than 92.27% of JavaScript online submissions for Remove Duplicates from Sorted Array. 8 | // Memory Usage: 44.3 MB, less than 55.52% of JavaScript online submissions for Remove Duplicates from Sorted Array. 9 | // O(n) time, O(1) space 10 | // Time to write code: 1:39 min 11 | let removeDuplicates = function(nums) { 12 | let [i, insertIndex] = [1, 1] 13 | 14 | while (i < nums.length) { 15 | if (nums[i] !== nums[i - 1]) { 16 | nums[insertIndex] = nums[i] 17 | insertIndex++ 18 | } 19 | // console.log(`insertIndex: ${insertIndex}, i: ${i}, nums[i]: ${nums[i]}`) 20 | // console.log(nums) 21 | 22 | i++ 23 | } 24 | 25 | return insertIndex 26 | } 27 | 28 | OUTPUT: 29 | 30 | insertIndex: 1, i: 1, nums[i]: 0 31 | [0, 0, 1, 1, 1, 2, 2, 3, 3, 4] 32 | 33 | insertIndex: 2, i: 2, nums[i]: 1 34 | [0, 1, 1, 1, 1, 2, 2, 3, 3, 4] 35 | 36 | insertIndex: 2, i: 3, nums[i]: 1 37 | [0, 1, 1, 1, 1, 2, 2, 3, 3, 4] 38 | 39 | insertIndex: 2, i: 4, nums[i]: 1 40 | [0, 1, 1, 1, 1, 2, 2, 3, 3, 4] 41 | 42 | insertIndex: 3, i: 5, nums[i]: 2 43 | [0, 1, 2, 1, 1, 2, 2, 3, 3, 4] 44 | 45 | insertIndex: 3, i: 6, nums[i]: 2 46 | [0, 1, 2, 1, 1, 2, 2, 3, 3, 4] 47 | 48 | insertIndex: 4, i: 7, nums[i]: 3 49 | [0, 1, 2, 3, 1, 2, 2, 3, 3, 4] 50 | 51 | insertIndex: 4, i: 8, nums[i]: 3 52 | [0, 1, 2, 3, 1, 2, 2, 3, 3, 4] 53 | 54 | insertIndex: 5, i: 9, nums[i]: 4 55 | [0, 1, 2, 3, 4, 2, 2, 3, 3, 4] 56 | ____________________________________ 57 | // First Try: 58 | // https://leetcode.com/submissions/detail/654411753/ 59 | // Runtime: 72 ms, beats 95.5% of JS submissions. Memory Usage: 45.7 MB, beats 6.5% of JS submissions 60 | // O(n) time, O(1) space 61 | // Time to write code: 1 hour 62 | /** 63 | * @param {number[]} nums 64 | * @return {number} 65 | */ 66 | let removeDuplicates = function(nums) { 67 | if (nums.length === 1) return 1 68 | let [k, i, j] = [1, 0, 1] 69 | let newNum = nums[i] 70 | 71 | while (i < nums.length - 1 && j < nums.length) { 72 | while (newNum === nums[j]) { 73 | // nums[j] = '_' 74 | j++ 75 | } 76 | 77 | newNum = nums[j] 78 | // console.log(`i: ${i}, firstNum: ${nums[i]}, j: ${j}, newNum: ${newNum}, k: ${k}`) 79 | 80 | if (j < nums.length) { 81 | nums[i + 1] = newNum 82 | k++ 83 | } 84 | 85 | // console.log(nums) 86 | i++ 87 | j++ 88 | } 89 | 90 | return k 91 | } 92 | 93 | OUTPUT: 94 | 95 | i: 0, firstNum: 0, j: 2, newNum: 1, k: 1 96 | [0, 1, 1, 1, 1, 2, 2, 3, 3, 4] 97 | 98 | i: 1, firstNum: 1, j: 5, newNum: 2, k: 2 99 | [0, 1, 2, '_', '_', 2, 2, 3, 3, 4] 100 | 101 | i: 2, firstNum: 2, j: 7, newNum: 3, k: 3 102 | [0, 1, 2, 3, '_', 2, '_', 3, 3, 4] 103 | 104 | i: 3, firstNum: 3, j: 9, newNum: 4, k: 4 105 | [0, 1, 2, 3, 4, 2, '_', 3, '_', 4] 106 | -------------------------------------------------------------------------------- /leetcode/easy/reverse_linked_list.js: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/reverse-linked-list/ 2 | // See all other linked list functions in detail: https://github.com/rayning0/ctci/blob/master/data_structures/linked_list.test.js 3 | // 3 companies asking this: Microsoft, Bloomberg, Amazon 4 | 5 | /** 6 | * Definition for singly-linked list. 7 | * function ListNode(val, next) { 8 | * this.val = (val===undefined ? 0 : val) 9 | * this.next = (next===undefined ? null : next) 10 | * } 11 | */ 12 | /** 13 | * @param {ListNode} head 14 | * @return {ListNode} 15 | */ 16 | 17 | // iterative 18 | // https://leetcode.com/submissions/detail/654329569/ 19 | // Runtime: 107 ms (beats 36% of JS submissions). Memory: 44.6 MB (beats 11% of JS submissions) 20 | // O(n) time, O(1) space 21 | // Time to write code: 54 secs 22 | let reverseList = function(head) { 23 | let [prev, curr] = [null, head] 24 | 25 | while (curr) { 26 | next = curr.next 27 | curr.next = prev 28 | prev = curr 29 | curr = next 30 | } 31 | return prev 32 | } 33 | 34 | // recursive 35 | // https://leetcode.com/submissions/detail/654344962/ 36 | // O(n) time, O(1) space 37 | // Finding Big O for recursive functions: https://stackoverflow.com/questions/13467674/determining-complexity-for-recursive-functions-big-o-notation 38 | // Time to write code: 57 secs 39 | let reverseList = function(curr) { 40 | if (curr === null || curr.next === null) return curr 41 | 42 | let newHead = reverseList(curr.next) 43 | curr.next.next = curr // means next.next = previous, points back to curr 44 | curr.next = null 45 | 46 | return newHead 47 | } 48 | -------------------------------------------------------------------------------- /leetcode/easy/reverse_string.js: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/reverse-string/ 2 | // https://leetcode.com/submissions/detail/678695773/ 3 | 4 | /** 5 | * @param {character[]} s 6 | * @return {void} Do not return anything, modify s in-place instead. 7 | */ 8 | // 2 pointers: O(n) time, O(1) space 9 | // Companies: Microsoft, Amazon, Apple 10 | // Time to code: 2:56 min 11 | let reverseString = function(s) { 12 | const len = s.length 13 | for (let i = 0; i < len / 2; i++) { 14 | [s[i], s[len - 1 - i]] = [s[len - 1 - i], s[i]] // swap letters 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /leetcode/easy/single_number.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {number} 4 | */ 5 | // https://leetcode.com/problems/single-number/ 6 | // https://leetcode.com/submissions/detail/678275871/ 7 | // Bit manipulation with XOR 8 | // O(n) time, O(1) space 9 | // Companies: Amazon, Microsoft, Bloomberg 10 | 11 | // 0 ^ a = a 12 | // a ^ a = 0 13 | // a ^ b ^ a = (a ^ a) ^ b = 0 ^ b = b 14 | // So XOR all bits together to find unique number 15 | 16 | let singleNumber = function(nums) { 17 | bitProduct = 0 18 | for (let num of nums) { 19 | bitProduct ^= num 20 | } 21 | return bitProduct 22 | } 23 | 24 | // O(n) time, O(n) space 25 | // Time to code: 3:10 min 26 | let singleNumber = function(nums) { 27 | freq = {} 28 | for (let num of nums) { 29 | if (freq[num]) { 30 | freq[num]++ 31 | } else { 32 | freq[num] = 1 33 | } 34 | } 35 | 36 | for (let num in freq) { 37 | if (freq[num] == 1) { 38 | return num 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /leetcode/easy/valid_anagram.js: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/valid-anagram/ 2 | // https://leetcode.com/submissions/detail/654303346/ 3 | // Time to write code: 1:53 min. (Original time to code: 15 min) 4 | // O(n) time, O(n) space 5 | // 3 companies asking this: Bloomberg, Microsoft, Facebook 6 | 7 | let isAnagram = function(s, t) { 8 | if (s.length !== t.length) return false 9 | let freq = {} 10 | 11 | for (let i in s) { 12 | freq[s[i]] ? freq[s[i]]++ : freq[s[i]] = 1 13 | freq[t[i]] ? freq[t[i]]-- : freq[t[i]] = -1 14 | } 15 | 16 | for (let char of t) { 17 | if (freq[char] !== 0) return false 18 | } 19 | 20 | return true 21 | } 22 | -------------------------------------------------------------------------------- /leetcode/easy/valid_anagram.rb: -------------------------------------------------------------------------------- 1 | # https://leetcode.com/problems/valid-anagram/ 2 | # Runtime: 72 ms, faster than 79.57% of Ruby online submissions for Valid Anagram. 3 | # Memory Usage: 9.9 MB, less than 100.00% of Ruby online submissions for Valid Anagram. 4 | def is_anagram(s, t) 5 | return false if s.size != t.size 6 | freq = Hash.new(0) 7 | s.size.times do |i| 8 | schar, tchar = s[i], t[i] 9 | freq[schar] += 1 10 | freq[tchar] -= 1 11 | end 12 | 13 | freq.each do |k, v| 14 | return false if v != 0 15 | end 16 | 17 | true 18 | end 19 | -------------------------------------------------------------------------------- /leetcode/easy/valid_palindrome.js: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/valid-palindrome/ 2 | // https://leetcode.com/submissions/detail/678682789/ 3 | 4 | /** 5 | * @param {string} s 6 | * @return {boolean} 7 | */ 8 | // Best answer: 2 pointers 9 | // O(n) time, O(1) space 10 | // Companies: Facebook, Microsoft, Amazon 11 | // Time to code: 18:39 min. Longest time was figuring out regex. 12 | let isPalindrome = function(s) { 13 | // Regex reference: https://regex101.com 14 | // \W Matches anything other than a letter, digit or underscore. Same as [^a-zA-Z0-9_]. Thus add "_" to answer. 15 | // g modifier: global. All matches (don't return after first match) 16 | // m modifier: multi line. Causes ^ and $ to match the begin/end of each line (not only begin/end of string) 17 | s = s.toLowerCase().replace(/[\W_]+/gm, '') // delete all punctuation + spaces 18 | 19 | for (let i = 0; i < s.length / 2; i++) { 20 | if (s[i] !== s[s.length - 1 - i]) return false 21 | } 22 | return true 23 | } 24 | 25 | // Simplest answer, but uses O(n) time, O(n) space. 26 | // Unfortunately, JavaScript won't let you easily check equality of 2 arrays: 27 | /// https://www.30secondsofcode.org/articles/s/javascript-array-comparison 28 | // Thus I must rejoin them back to strings 29 | let isPalindrome = function(s) { 30 | s = s.toLowerCase().replace(/[\W_]+/gm, '') // delete all punctuation + spaces 31 | 32 | return (s === s.split('').reverse().join('')) ? true : false 33 | } 34 | -------------------------------------------------------------------------------- /leetcode/easy/valid_parentheses.js: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/valid-parentheses/ 2 | // https://leetcode.com/submissions/detail/654875488/ 3 | // Runtime: 64 ms, faster than 91.72% of JavaScript online submissions for Valid Parentheses. 4 | // Memory Usage: 42.7 MB, less than 15.41% of JavaScript online submissions for Valid Parentheses. 5 | // O(n) time, O(n) space 6 | // Time to write code: 5:13 min 7 | // 3 companies asking this: Amazon, LinkedIn, Microsoft 8 | /** 9 | * @param {string} s 10 | * @return {boolean} 11 | */ 12 | let isValid = function(s) { 13 | const parens = {'(': ')', '[': ']', '{': '}'} 14 | sarray = s.split('') 15 | let stack = [sarray.shift()] // remove first char of string 16 | 17 | for (let char of sarray) { 18 | top = stack[stack.length - 1] 19 | parens[top] === char ? stack.pop() : stack.push(char) 20 | } 21 | 22 | return stack.length === 0 ? true : false 23 | } 24 | -------------------------------------------------------------------------------- /leetcode/expressive_words.rb: -------------------------------------------------------------------------------- 1 | # https://leetcode.com/problems/expressive-words/ 2 | def expressive_words(s, words) 3 | numwords = 0 4 | words.each do |word| 5 | i, j = 0, 0 6 | ok = false 7 | while i < s.size do 8 | if s[i] && word[j].nil? 9 | ok = false 10 | break 11 | end 12 | scommon, word_common = 0, 0 13 | sindex, jindex = i, j 14 | while s[sindex] == word[j] do 15 | scommon += 1 16 | sindex += 1 17 | end 18 | while s[i] == word[jindex] do 19 | word_common += 1 20 | jindex += 1 21 | end 22 | 23 | if scommon == 0 || word_common > scommon 24 | ok = false 25 | break 26 | end 27 | 28 | if scommon == word_common || scommon >= 3 29 | i += scommon 30 | j += word_common 31 | ok = true 32 | end 33 | 34 | if scommon < 3 && scommon != word_common 35 | ok = false 36 | break 37 | end 38 | 39 | ok = true 40 | end 41 | 42 | numwords += 1 if ok 43 | end 44 | 45 | numwords 46 | end 47 | # a 48 | # scommon = 1 49 | # i = 4 50 | # s = "aaabaa" 51 | # word = "ab" 52 | 53 | # a 54 | # wcommon = 1 55 | # j = 2 56 | 57 | s = "yyrrrrrjaappoooyybbbebbbbriiiiiyyynnnvvwtwwwwwooeeexxxxxkkkkkaaaaauuuu" 58 | words = ["yrrjjappooybbebriiyynvvwtwwoeexkauu","yrjjappooybbebrriyynnvwwttwoeexkaauu","yyrrjapoybbebriiynnvvwwtwoeexkaauu","yyrjappooyybebbrriyynnvwttwwooeexxkkau","yrjaapooybbebrriyynnvvwwttwooexkaau","yyrjjapooyybeebbrriiyynvwwttwoexxkau","yrrjaappoyybbeebbriynnvwwtwooexxkauu","yrrjjaapooybebriynnvvwwttwwooexkaau","yyrrjjappooyybebriiyynvvwttwoeexxkkaau","yrrjaappooybbebrriynvwwtwooeexkau","yyrjjaapooyybebrriiynvvwttwwooeexxkkaau","yyrrjappooyybbebriyynnvwwttwwoeexkkauu","yyrrjjaapoyybbeebriiyynnvwwtwwooexkkaau","yrjjaappooybbeebriiyynnvwwtwwoexkau","yrrjjappoyybbeebbrriiyynnvwttwwooexxkkaauu","yyrrjjapooyybbebbrriyynvwtwoexxkkaauu","yyrrjappoybebrriynvwwttwooeexkkauu","yrrjaappooybbeebriiyynnvvwwttwoexxkauu","yrrjapoybebbrriyynvvwwttwwoexkaau","yyrrjjapoybbeebbrriynnvwwtwwooexkaauu","yyrrjjapooyybbeebbriyynnvwtwwoexkaau","yrjjaapooyybebriynnvwwttwooeexxkkaauu","yyrjjaapooybbebbriiynvvwttwwoexxkkauu","yrjjaapooyybeebbriiyynvvwwttwoeexxkau","yrjjappooyybbebbrriiynvvwtwooeexxkkau","yyrrjjapoyybbebbrriiyynvwwtwwoexxkkaau","yrjjapooyybbeebriyynnvvwwtwoeexkkau","yrjapooyybebriiynnvvwwtwwooeexkauu","yyrjaapoyybbebbrriynnvwtwwoeexkauu","yrrjjappoybeebrriiynvvwwtwwoeexxkkaau","yrrjjapoybbeebrriiyynnvwwttwwoexxkaau","yyrrjaapoybeebrriiyynvwttwwooeexkauu","yyrjapoybbeebbrriyynnvvwwttwwooeexkaauu","yyrjappooybebrriiynvwtwwoeexxkaauu","yrrjjappooybebrriynnvvwttwooexkau","yrjjaapoybbeebbriiynnvvwttwooexkauu","yyrrjapooyybbeebriiyynnvvwtwwoeexxkaauu","yyrjjaappooybeebbrriiyynnvvwwtwwoeexkkau","yrrjappoyybbeebrriiynvvwwtwwoeexxkauu","yrjapooyybeebriiyynvvwttwwooeexxkaauu","yrjjappooyybbebbriiynnvwwtwooeexxkauu","yyrrjjappooybbeebbriyynnvwtwwooexxkkau","yyrrjjaapooybebriiyynvwwtwooeexxkkaauu","yrjjappooyybbeebbriiyynvwwtwwoeexkkau","yrrjjappooybbebrriiynvvwwtwwoexxkkaau","yrjjapooybebbriyynnvvwwttwwooeexxkkaau","yyrjjapoyybebbrriynvvwwttwoexkauu","yyrjappoyybebriiynnvvwttwwoexxkaauu","yyrjaapoybbeebriyynvvwwttwoeexkau","yrjjaappooyybbebbriiynnvvwtwooexxkau","yyrjjaappooyybbebrriiyynvvwttwooexkau","yrjjappoybbeebriyynnvvwwttwwooexxkkaau","yyrrjaapooybbebbriiyynnvwwtwwooexxkkaauu","yrrjaapooybbeebrriynnvvwwtwoeexxkkauu","yrjjaappooyybeebbrriyynnvvwttwwoexxkkauu","yrrjapooyybebriyynnvwwttwooeexkau","yyrjjaapooyybeebrriiynnvvwwttwoeexxkkau","yrjappooybebriyynnvvwttwwooeexkau","yrrjjaappoyybebbrriiyynvwwtwooexxkauu","yrjjappooybeebriynnvwwtwoeexkaauu","yrjaappoybbebbriiynnvwwttwooexxkaau","yyrrjappooyybeebbriiyynvwwttwwoexxkau","yyrjappoyybbeebrriynvwtwoeexkaau","yrrjjaapooybbeebbriyynvwwtwooeexkkaau","yrjapoybebbrriiynvwttwwoeexxkaau","yrjapooybebbrriiynnvwwtwwoexxkaau","yrrjjaappoybeebbriiyynvwwtwooexxkkaauu","yrjappooybeebrriynvwwtwooeexkaauu","yrrjaapooybeebbriiynvvwtwwoexxkkaauu","yyrrjaappooyybebbrriiyynvwwtwwooexxkkau","yyrjaappoybbeebriynnvvwwtwwooeexkaauu","yyrjaappooyybbebbriynvvwwttwwooexkauu","yrjappooybeebbrriiynnvwttwwooexkkau","yrrjjappooyybebbriiyynnvvwttwwoexkkau","yrrjjaapooybeebbriynnvvwwtwooexkaau","yyrjjappoybeebbrriiynnvwtwwoexkaauu","yyrjjaapoybbebbrriiyynnvvwtwwoexkaau","yyrrjjaappoyybbebbriyynvwwtwwooeexkkaau","yrrjjaappooybbebriiyynvvwttwwooexxkau","yyrjjaapoyybebriiynnvwtwwooeexkauu","yrrjjappoyybeebbriiyynnvwttwoexkkau","yrjjappoyybbebbrriynnvvwttwwooeexkkaauu","yyrjappooybeebrriiynnvwwttwwooexxkkaauu","yrrjaappoybbeebrriyynnvvwwtwwooeexxkaauu","yyrjaappooybeebbriiynvwttwoexxkkauu","yyrrjjapooyybbeebbrriyynvwttwwooeexxkkau","yrrjapoybbebbrriiynvwtwwoeexxkaau","yyrrjapoybbeebbriiyynnvvwttwooexkkauu","yyrjaapooyybebbrriiyynnvvwwtwooeexkkauu","yyrrjjaappoybbeebrriyynnvwwtwwoexkkaauu","yyrjappooybbeebrriiyynvwwttwwoexkkau","yyrjaapooyybebbriiyynnvvwwtwoeexkkaau","yyrrjjappoyybbeebbriiyynvwtwooexxkaauu","yrrjjaapoyybbeebriynvvwtwwoexxkaau","yyrrjjapoybbebbrriyynnvwwtwoeexxkkaau","yyrrjapooyybebrriiyynvwttwwooeexxkkauu","yrjappooyybebriiynnvwwtwoeexkkaauu","yrjjaapooyybeebriiynvwtwooexkauu","yyrrjjapoybeebbrriiynnvwttwwoexkaau","yyrrjaappoyybebbrriiyynvwwtwooeexkaau"] 59 | 60 | puts expressive_words(s, words) 61 | -------------------------------------------------------------------------------- /leetcode/find_words_that_can_be_formed_by_characters_spec.rb: -------------------------------------------------------------------------------- 1 | # https://leetcode.com/problems/find-words-that-can-be-formed-by-characters 2 | def count_characters(words, chars) 3 | freq = {} 4 | good_lengths = 0 5 | chars.each_char do |char| 6 | freq[char] ? freq[char] += 1 : freq[char] = 1 7 | end 8 | 9 | words.each do |word| 10 | copy = freq.dup 11 | can_be_made = true 12 | word.each_char do |char| 13 | if !copy[char] || copy[char] < 1 14 | can_be_made = false 15 | break 16 | else 17 | copy[char] -= 1 18 | end 19 | end 20 | good_lengths += word.length if can_be_made 21 | end 22 | 23 | good_lengths 24 | end 25 | 26 | describe "#count_characters" do 27 | it 'gives sum of lengths of all strings from words array that chars can make' do 28 | expect(count_characters(["cat","bt","cat","tree"], "atach")).to eq 6 29 | expect(count_characters(["hello","world","leetcode"], "welldonehoneyr")).to eq 10 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /leetcode/greatest_common_divisor_strings.test.js: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/greatest-common-divisor-of-strings/ 2 | // str1, str2 must be multiple of output, but they don't need to be multiples of each other 3 | // Runtime: 52 ms, faster than 90.73 % of JavaScript online submissions for Greatest Common Divisor of Strings. 4 | // Memory Usage: 35.4 MB, less than 100.00 % of JavaScript online submissions for Greatest Common Divisor of Strings. 5 | 6 | // Euclidean algorithm for Greatest Common Divisor: 7 | // https://www.khanacademy.org/computing/computer-science/cryptography/modarithmetic/a/the-euclidean-algorithm 8 | // If A = 0 then GCD(A, B) = B, since the GCD(0, B) = B. 9 | // If B = 0 then GCD(A, B) = A, since the GCD(A, 0) = A. 10 | // GCD(A, B) = GCD(B, A % B) 11 | 12 | // Ex: gcd(100, 60) = gcd(60, 100 % 60 = 40) = gcd(40, 60 % 40 = 20) = gcd(20, 40 % 20 = 0) = 20 13 | 14 | function gcd(a, b) { 15 | if (a == 0) return b 16 | if (b == 0) return a 17 | if (b > a) [a, b] = [b, a] 18 | 19 | return gcd(b, a % b) 20 | } 21 | 22 | // Time: O(n). Space: O(1). 23 | function gcd_strings(str1, str2) { 24 | let [longer, shorter] = (str1.length > str2.length) ? [str1, str2] : [str2, str1] 25 | let gcd2 = gcd(str1.length, str2.length) 26 | // console.log(`GCD: ${gcd2}`) 27 | 28 | for (let i = 0; i < longer.length; i++) { 29 | j = i % gcd2 // lets index of shorter string start over from 0 when i >= shorter.length 30 | // console.log(`i: ${i}, longer[i]: ${longer[i]}, j: ${j}, shorter[j]: ${shorter[j]}`) 31 | if (longer[i] != shorter[j]) { return "" } 32 | } 33 | 34 | return shorter.substring(0, gcd2) 35 | } 36 | 37 | describe ("#gcd_strings", () => { 38 | it("returns GCD string", () => { 39 | expect(gcd_strings("A", "AAA")).toEqual("A") 40 | expect(gcd_strings("AAA", "AAA")).toEqual("AAA") 41 | expect(gcd_strings("FFB", "FFBFFB")).toEqual("FFB") 42 | expect(gcd_strings("LEET", "CODE")).toEqual("") 43 | expect(gcd_strings("CAB", "BABBAB")).toEqual("") 44 | expect(gcd_strings("A", "A")).toEqual("A") 45 | expect(gcd_strings("A", "B")).toEqual("") 46 | expect(gcd_strings("ABCABC", "ABC")).toEqual("ABC") 47 | expect(gcd_strings("ABCABD", "ABC")).toEqual("") 48 | expect(gcd_strings("BAB", "BABXBAB")).toEqual("") 49 | expect(gcd_strings("BAB", "BABAB")).toEqual("") 50 | expect(gcd_strings("ABA", "ABABA")).toEqual("") 51 | expect(gcd_strings("ABABAB", "ABAB")).toEqual("AB") 52 | expect(gcd_strings("ABCABDABCABD", "ABCABD")).toEqual("ABCABD") 53 | expect(gcd_strings("TAUXXTAUXXTAUXXTAUXXTAUXX", "TAUXXTAUXXTAUXXTAUXXTAUXXTAUXXTAUXXTAUXXTAUXX")).toEqual("TAUXX") 54 | expect(gcd_strings("FFBNXKSTFFBNXKSTFFBNXKSTFFBNXKSTFFBNXKST", 55 | "FFBNXKSTFFBNXKSTFFBNXKSTFFBNXKSTFFBNXKSTFFBNXKSTFFBNXKSTFFBNXKSTFFBNXKST")).toEqual("FFBNXKST") 56 | }) 57 | }) 58 | 59 | // expect(gcd_strings("ABCABD", "ABC")).toEqual("") 60 | // GCD: 3 61 | // i: 0, longer[i]: A, j: 0, shorter[j]: A 62 | // i: 1, longer[i]: B, j: 1, shorter[j]: B 63 | // i: 2, longer[i]: C, j: 2, shorter[j]: C 64 | // i: 3, longer[i]: A, j: 0, shorter[j]: A 65 | // i: 4, longer[i]: B, j: 1, shorter[j]: B 66 | // i: 5, longer[i]: D, j: 2, shorter[j]: C 67 | 68 | // expect(gcd_strings("ABABAB", "ABAB")).toEqual("AB") 69 | // GCD: 2 70 | // i: 0, longer[i]: A, j: 0, shorter[j]: A 71 | // i: 1, longer[i]: B, j: 1, shorter[j]: B 72 | // i: 2, longer[i]: A, j: 0, shorter[j]: A 73 | // i: 3, longer[i]: B, j: 1, shorter[j]: B 74 | // i: 4, longer[i]: A, j: 0, shorter[j]: A 75 | // i: 5, longer[i]: B, j: 1, shorter[j]: B 76 | 77 | // expect(gcd_strings("ABCABDABCABD", "ABCABD")).toEqual("ABCABD") 78 | // GCD: 6 79 | // i: 0, longer[i]: A, j: 0, shorter[j]: A 80 | // i: 1, longer[i]: B, j: 1, shorter[j]: B 81 | // i: 2, longer[i]: C, j: 2, shorter[j]: C 82 | // i: 3, longer[i]: A, j: 3, shorter[j]: A 83 | // i: 4, longer[i]: B, j: 4, shorter[j]: B 84 | // i: 5, longer[i]: D, j: 5, shorter[j]: D 85 | // i: 6, longer[i]: A, j: 0, shorter[j]: A 86 | // i: 7, longer[i]: B, j: 1, shorter[j]: B 87 | // i: 8, longer[i]: C, j: 2, shorter[j]: C 88 | // i: 9, longer[i]: A, j: 3, shorter[j]: A 89 | // i: 10, longer[i]: B, j: 4, shorter[j]: B 90 | // i: 11, longer[i]: D, j: 5, shorter[j]: D 91 | -------------------------------------------------------------------------------- /leetcode/happy_number_spec.rb: -------------------------------------------------------------------------------- 1 | # https://leetcode.com/problems/happy-number/ 2 | # Runtime: 28 ms, faster than 98.08% of Ruby online submissions for Happy Number. 3 | # Memory Usage: 9.2 MB, less than 100.00% of Ruby online submissions for Happy Number. 4 | 5 | def is_happy(n) 6 | sums = {} 7 | 8 | while n != 1 do 9 | sum = 0 10 | 11 | while n != 0 do 12 | last_digit = n % 10 13 | sum += last_digit * last_digit 14 | n /= 10 # remove last digit 15 | end 16 | 17 | # n.to_s.each_char do |digit| 18 | # d = digit.to_i 19 | # sum += d * d 20 | # end 21 | 22 | if sums[sum] 23 | return false 24 | else 25 | sums[sum] = true 26 | end 27 | 28 | n = sum 29 | end 30 | 31 | true 32 | end 33 | 34 | describe '#is_happy' do 35 | it 'tells if number is happy' do 36 | expect(is_happy(19)).to eq true 37 | expect(is_happy(7)).to eq true 38 | expect(is_happy(2)).to eq false 39 | expect(is_happy(9)).to eq false 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /leetcode/k_closest_points_spec.rb: -------------------------------------------------------------------------------- 1 | # https://leetcode.com/problems/k-closest-points-to-origin/ 2 | # https://www.educative.io/courses/grokking-the-coding-interview/3YxNVYwNR5p 3 | # https://medium.com/basecs/learning-to-love-heaps-cef2b273a238 4 | # https://www.hackerearth.com/practice/data-structures/trees/heapspriority-queues/tutorial/ 5 | # https://www.youtube.com/watch?v=t0Cq6tVNRBA 6 | # Runtime: 220 ms, faster than 100.00% of Ruby online submissions for K Closest Points to Origin. 7 | # Memory Usage: 12.1 MB, less than 100.00% of Ruby online submissions for K Closest Points to Origin. 8 | 9 | # O(n log n) 10 | def k_closest(points, k) 11 | points.sort_by{|x, y| x*x + y*y}.first(k) 12 | end 13 | 14 | # http://kanwei.github.io/algorithms/classes/Containers/MinHeap.html 15 | require 'algorithms' 16 | include Containers 17 | 18 | # O(n log n + k log k) = O(n log n) 19 | def k_closest2(points, k) 20 | heap = MinHeap.new 21 | points.each do |x, y| 22 | d = x*x + y*y 23 | heap.push(d, [x, y]) 24 | end 25 | ans = [] 26 | k.times do 27 | ans << heap.pop 28 | end 29 | ans 30 | end 31 | 32 | describe '#k_closest' do 33 | it 'finds k closest points to origin (0,0)' do 34 | expect(k_closest([[0,1],[1,0]], 2)).to eq [[0,1], [1,0]] 35 | expect(k_closest([[1,3],[-2,2]], 1)).to eq [[-2,2]] 36 | 37 | now = Time.now 38 | expect(k_closest([[3,3],[5,-1],[-2,4]], 2)).to eq [[3,3],[-2,4]] 39 | puts Time.now - now 40 | # 1.2e-05 secs 41 | 42 | now = Time.now 43 | expect(k_closest2([[3,3],[5,-1],[-2,4]], 2)).to eq [[3,3],[-2,4]] 44 | puts Time.now - now 45 | # 8.0e-05 secs (heap is slower) 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /leetcode/longest_increasing_path_in_a_matrix.rb: -------------------------------------------------------------------------------- 1 | # https://leetcode.com/problems/longest-increasing-path-in-a-matrix/ 2 | # @param {Integer[][]} matrix 3 | # @return {Integer} 4 | def longest_increasing_path(matrix) 5 | maxlength = 0 6 | return 0 if matrix.empty? || matrix[0].empty? 7 | @height, @width = matrix.size, matrix[0].size 8 | lengths = Array.new(@height) {|cell| Array.new(@width, 0)} 9 | 10 | @height.times do |row| 11 | @width.times do |col| 12 | length = dfs(row, col, matrix, lengths) 13 | maxlength = [maxlength, length].max 14 | # puts "overall maxlength #{maxlength}" 15 | end 16 | end 17 | 18 | maxlength 19 | end 20 | 21 | def pp(matrix) 22 | matrix.each do |row| 23 | p row 24 | end 25 | end 26 | 27 | # Depth-First Search 28 | def dfs(row, col, matrix, lengths) 29 | return lengths[row][col] if lengths[row][col] > 0 30 | directions = [[0, 1], [0, -1], [1, 0], [-1, 0]] 31 | 32 | val = matrix[row][col] 33 | # puts "[#{row}, #{col}]" 34 | 35 | directions.each do |drow, dcol| 36 | nrow, ncol = row + drow, col + dcol 37 | if ncol.between?(0, @width - 1) && nrow.between?(0, @height - 1) && matrix[nrow][ncol] > val 38 | lengths[row][col] = [lengths[row][col], dfs(nrow, ncol, matrix, lengths)].max 39 | # puts "new cell: [#{nrow}, #{ncol}]" 40 | # pp(lengths) 41 | end 42 | end 43 | 44 | lengths[row][col] += 1 45 | lengths[row][col] 46 | end 47 | -------------------------------------------------------------------------------- /leetcode/longest_palindrome_spec.rb: -------------------------------------------------------------------------------- 1 | # https://leetcode.com/problems/longest-palindromic-substring/ 2 | # Runtime: 972 ms, faster than 60.50% of Ruby online submissions for Longest Palindromic Substring. 3 | # Memory Usage: 9.3 MB, less than 100.00% of Ruby online submissions for Longest Palindromic Substring. 4 | # O(n * n) time complexity. O(n) space complexity. 5 | def longest(s) 6 | maxstring = '' 7 | 8 | s.size.times do |i| # 0 to s.size - 1 9 | 2.times do |j| # 0 to 1. 0 for odd length palindromes, 1 for even length. 10 | left = i 11 | right = i + j 12 | 13 | while left >= 0 && s[left] == s[right] do 14 | left -= 1 15 | right += 1 16 | end 17 | 18 | substring = s[left + 1..right - 1] 19 | # puts substring 20 | if substring.size > maxstring.size 21 | maxstring = substring 22 | end 23 | end 24 | end 25 | 26 | # puts '------------------------' 27 | maxstring 28 | end 29 | 30 | describe '#longest_palindrome' do 31 | it 'finds longest palindrome substring' do 32 | expect(longest("aba")).to eq 'aba' 33 | expect(longest("babad")).to eq 'bab' 34 | expect(longest("cbbd")).to eq 'bb' 35 | expect(longest("asdkasdfkxzclxlweieieisweiews")).to eq 'sweiews' 36 | end 37 | end 38 | 39 | # JavaScript: 40 | 41 | # var longestPalindrome = function(s) { 42 | # var max = ''; 43 | # for (var i = 0; i < s.length; i++) { 44 | # for (var j = 0; j < 2; j++) { 45 | # var left = i; 46 | # var right = i + j; 47 | # while (s[left] && s[left] === s[right]) { 48 | # left--; 49 | # right++; 50 | # } 51 | # var length = right - left + 1; 52 | # if (length - 2 > max.length) { 53 | # max = s.substring(left + 1, right); 54 | # } 55 | # } 56 | # } 57 | # return max; 58 | # }; 59 | 60 | # OUTPUT: 61 | 62 | # a 63 | # aba 64 | # a 65 | # ------------------------ 66 | # b 67 | # bab 68 | # aba 69 | # a 70 | # d 71 | # ------------------------ 72 | # c 73 | # b 74 | # bb 75 | # b 76 | # d 77 | # ------------------------ 78 | # a 79 | # s 80 | # d 81 | # k 82 | # a 83 | # s 84 | # d 85 | # f 86 | # k 87 | # x 88 | # z 89 | # c 90 | # l 91 | # lxl 92 | # l 93 | # w 94 | # e 95 | # eie 96 | # eieie 97 | # ieiei 98 | # iei 99 | # i 100 | # s 101 | # w 102 | # e 103 | # sweiews 104 | # e 105 | # w 106 | # s 107 | -------------------------------------------------------------------------------- /leetcode/maximal_square_spec.rb: -------------------------------------------------------------------------------- 1 | def maximal_square(arr) 2 | max_square_size, square_size = 0, 0 3 | 4 | arr.size.times do |row| 5 | arr[0].size.times do |col| 6 | square_size = scan_square(row, col, arr) 7 | max_square_size = [max_square_size, square_size].max 8 | end 9 | end 10 | 11 | max_square_size 12 | end 13 | 14 | def scan_square(row, col, arr) 15 | height = arr.size 16 | width = arr[0].size 17 | side = 1 18 | square_size = 0 19 | 20 | while col + side - 1 < width && row + side - 1 < height do 21 | (row..row + side - 1).each do |r| 22 | (col..col + side - 1).each do |c| 23 | return square_size * square_size if arr[r][c] == '0' 24 | end 25 | end 26 | square_size += 1 27 | 28 | side += 1 29 | end 30 | 31 | square_size * square_size 32 | end 33 | 34 | describe "#maximal_square" do 35 | # it '' do 36 | # arr = [ 37 | # ["1","0","1","1","1"], 38 | # ["1","0","1","1","1"], 39 | # ["1","1","1","1","1"], 40 | # ["1","1","1","1","1"] 41 | # ] 42 | # expect(scan_square(2, 0, arr)).to eq 4 43 | # end 44 | it "returns biggest square with only 1's" do 45 | expect(maximal_square([ 46 | ["0","0"], 47 | ["0","0"], 48 | ])).to eq 0 49 | expect(maximal_square([ 50 | ["1","0","1","0","0"], 51 | ["1","0","1","1","1"], 52 | ["1","1","1","1","1"], 53 | ["1","0","0","1","0"] 54 | ])).to eq 4 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /leetcode/medium/reverse_integer.js: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/reverse-integer/ 2 | // https://leetcode.com/submissions/detail/678734546/ 3 | 4 | /** 5 | * @param {number} x 6 | * @return {number} 7 | */ 8 | // BEST answer: 9 | // O(log n) time, O(1) space. x has roughly log(x) digits (base 10). 10 | // Runtime: 60 ms, faster than 99.57% of JavaScript online submissions for Reverse Integer. 11 | // Memory Usage: 43.8 MB, less than 72.48% of JavaScript online submissions for Reverse Integer. 12 | // Companies: Adobe, Amazon, Google 13 | let reverse = function(x) { 14 | let r = 0 15 | let temp = Math.abs(x) 16 | 17 | while (temp > 0) { 18 | let last_digit = temp % 10 19 | 20 | // Math.trunc() returns only integer part. Ignores decimal part. 21 | // Faster than (temp - last_digit) / 10 22 | temp = Math.trunc(temp / 10) 23 | 24 | r = r * 10 + last_digit 25 | } 26 | 27 | if (x >= 0) { 28 | if (r <= 2**31 - 1) return r 29 | } else { 30 | if (-r >= -(2**31)) return -r 31 | } 32 | return 0 33 | } 34 | 35 | // Example of reversing integer with Answer 1: 36 | temp = x = 512 37 | r = 0 38 | 39 | last_digit = 512 % 10 = 2 40 | temp = Math.trunc(512 / 10) = 51 41 | r = 0 * 10 + 2 = 2 42 | 43 | last_digit = 51 % 10 = 1 44 | temp = Math.trunc(51 / 10) = 5 45 | r = 2 * 10 + 1 = 21 46 | 47 | last_digit = 5 % 10 = 5 48 | temp = Math.trunc(5 / 10) = 0 49 | r = 21 * 10 + 5 = 215 <----REVERSED!!! 50 | 51 | _______________________________________ 52 | // Easiest to remember, but worse Big O 53 | // O(log n) time, O(n) space 54 | // Time to code: 17:15 min 55 | let reverse = function(x) { 56 | let r = parseInt(String(Math.abs(x)).split('').reverse().join('')) 57 | 58 | if (x >= 0) { 59 | if (r <= 2**31 - 1) return r 60 | } else { 61 | if (-r >= -(2**31)) return -r 62 | } 63 | return 0 64 | } 65 | -------------------------------------------------------------------------------- /leetcode/medium/rotate_array.js: -------------------------------------------------------------------------------- 1 | // https://leetcode.com/problems/rotate-array/ 2 | // https://leetcode.com/submissions/detail/678248500/ 3 | // 3 companies asking it: Amazon, Microsoft, Facebook 4 | /** 5 | * @param {number[]} nums 6 | * @param {number} k 7 | * @return {void} Do not return anything, modify nums IN-PLACE instead!!! 8 | */ 9 | 10 | // 1. BEST! O(n) time, O(1) space 11 | let rotate = function(nums, k) { 12 | nums.reverse() 13 | k = k % nums.length 14 | rev(nums, 0, k - 1) 15 | rev(nums, k, nums.length - 1) 16 | } 17 | 18 | function rev(nums, start, end) { 19 | while (start < end) { 20 | [nums[start], nums[end]] = [nums[end], nums[start]] 21 | start++ 22 | end-- 23 | } 24 | } 25 | 26 | 27 | // 2. Simpler 28 | // O(n) time, O(n) space 29 | let rotate = function(nums, k) { 30 | let temp = [...nums] 31 | for (let i = 0; i < nums.length; i++) { 32 | let dest = whereItGoes(i, k, nums.length) 33 | nums[dest] = temp[i] 34 | } 35 | } 36 | 37 | function whereItGoes(start, k, length) { 38 | k = k % length 39 | if (start + k < length) { 40 | return start + k 41 | } else { 42 | return start + k - length 43 | } 44 | } 45 | 46 | // Simplest, but this FAILS due to new LeetCode case that exceeds time limit 47 | let rotate = function(nums, k) { 48 | k = k % nums.length 49 | while (k > 0) { 50 | nums.unshift(nums.pop()) 51 | k-- 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /leetcode/most_common_word.rb: -------------------------------------------------------------------------------- 1 | # @param {String} paragraph 2 | # @param {String[]} banned 3 | # @return {String} 4 | def most_common_word(paragraph, banned) 5 | freq = Hash.new(0) 6 | mostfreqword = '' 7 | maxfreq = 0 8 | paragraph = paragraph.downcase.gsub(/[!?',;.]/, ' ') 9 | paragraph.split.each do |word| 10 | freq[word] += 1 11 | end 12 | 13 | freq.each do |word, f| 14 | next if banned.include?(word) 15 | if f > maxfreq 16 | maxfreq = f 17 | mostfreqword = word 18 | end 19 | end 20 | 21 | mostfreqword 22 | end 23 | -------------------------------------------------------------------------------- /leetcode/moving_average_spec.rb: -------------------------------------------------------------------------------- 1 | # https://leetcode.com/problems/moving-average-from-data-stream/ 2 | 3 | class MovingAverage 4 | def initialize(size) 5 | @size = size 6 | 7 | # use class variable since next instances of m must remember arr 8 | @@arr = [] 9 | end 10 | 11 | def next(val) 12 | @@arr.empty? ? @@arr = [val] : @@arr << val 13 | @@arr.shift if @@arr.size > @size 14 | sum = @@arr.reduce(:+).to_f 15 | sum / @@arr.size 16 | end 17 | end 18 | 19 | # Your MovingAverage object will be instantiated and called as such: 20 | # obj = MovingAverage.new(size) 21 | # param_1 = obj.next(val) 22 | 23 | describe "#MovingAverage" do 24 | m = MovingAverage.new(3) 25 | 26 | it 'returns moving average for window size 3, as we add more data' do 27 | expect(m.next(1)).to eq 1 28 | expect(m.next(10)).to eq 5.5 29 | expect(m.next(3)).to eq 14.0 / 3 30 | expect(m.next(5)).to eq 6 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /leetcode/number_of_dice_rolls_with_target_sum_spec.rb: -------------------------------------------------------------------------------- 1 | # https://leetcode.com/problems/number-of-dice-rolls-with-target-sum/ 2 | # Dynamic programming 3 | # Runtime: 520 ms, faster than 60.00% of Ruby online submissions for Number of Dice Rolls With Target Sum. 4 | # Memory Usage: 9.7 MB, less than 100.00% of Ruby online submissions for Number of Dice Rolls With Target Sum. 5 | 6 | def dice_rolls(d, f, target) 7 | dp = Array.new(d).map{ |cell| Array.new(target + 1, 0) } 8 | 9 | # fill first row 10 | (1..target).each do |cell| 11 | dp[0][cell] = 1 if cell <= f 12 | end 13 | 14 | (1..d - 1).each do |row| 15 | (1..target).each do |col| 16 | (1..f).each do |face_val| 17 | dp[row][col] += dp[row - 1][col - face_val] if col - face_val >= 0 18 | end 19 | end 20 | end 21 | 22 | dp[d - 1][target] % (10**9 + 7) 23 | end 24 | 25 | describe "#dice_rolls" do 26 | it 'finds # of ways to roll dice so sum == target' do 27 | expect(dice_rolls(1, 6, 3)).to eq 1 28 | expect(dice_rolls(2, 6, 7)).to eq 6 29 | expect(dice_rolls(2, 5, 10)).to eq 1 30 | expect(dice_rolls(1, 2, 3)).to eq 0 31 | expect(dice_rolls(3, 5, 7)).to eq 15 32 | expect(dice_rolls(3, 7, 5)).to eq 6 33 | expect(dice_rolls(30, 30, 500)).to eq 222616187 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /leetcode/number_of_equivalent_domino_pairs_spec.rb: -------------------------------------------------------------------------------- 1 | # https://leetcode.com/problems/number-of-equivalent-domino-pairs/ 2 | # Runtime: 116 ms, faster than 57.14% of Ruby online submissions for Number of Equivalent Domino Pairs. 3 | # Memory Usage: 11.5 MB, less than 100.00% of Ruby online submissions for Number of Equivalent Domino Pairs. 4 | 5 | # Time complexity: O(n). Space complexity: O(n). 6 | def domino_pairs(dominoes) 7 | freq = Hash.new(0) 8 | pairs = 0 9 | 10 | dominoes.each do |domino| 11 | if domino[1] < domino[0] 12 | freq[[domino[1], domino[0]]] += 1 13 | else 14 | freq[domino] += 1 15 | end 16 | end 17 | # puts "freq: #{freq}" 18 | 19 | freq.keys.each do |domino| 20 | if freq[domino] > 1 21 | pairs += nChoose2(freq[domino]) 22 | # puts "domino: #{domino}, freq[domino]: #{freq[domino]}, nChoose2: #{nChoose2(freq[domino])}" 23 | end 24 | end 25 | 26 | # puts "pairs: #{pairs}\n\n" 27 | pairs 28 | end 29 | 30 | # Algebra: "n choose 2" = # of combinations of n, chosen 2 at a time 31 | # https://www.mathwords.com/c/combination_formula.htm 32 | # http://reddit.com/r/learnmath/comments/3bg21t/what_exactly_is_n_choose_2_in_probability/ 33 | # C(n, 2) = n!/(2!(n - 2)!) = n (n - 1) / 2 34 | def nChoose2(n) 35 | n * (n - 1) / 2 36 | end 37 | 38 | describe "#domino_pairs" do 39 | it 'finds # of equivalent domino pairs' do 40 | expect(domino_pairs([[1,2],[2,1],[3,4],[5,6]])).to eq 1 41 | expect(domino_pairs([[1,2],[1,2],[1,1],[1,2],[2,2]])).to eq 3 42 | expect(domino_pairs([[1,1],[2,2],[1,1],[1,2],[1,2],[1,1]])).to eq 4 43 | end 44 | end 45 | 46 | # freq: {[1, 2]=>2, [3, 4]=>1, [5, 6]=>1} 47 | # domino: [1, 2], freq[domino]: 2, nChoose2: 1 48 | # pairs: 1 49 | 50 | # freq: {[1, 2]=>3, [1, 1]=>1, [2, 2]=>1} 51 | # domino: [1, 2], freq[domino]: 3, nChoose2: 3 52 | # pairs: 3 53 | 54 | # freq: {[1, 1]=>3, [2, 2]=>1, [1, 2]=>2} 55 | # domino: [1, 1], freq[domino]: 3, nChoose2: 3 56 | # domino: [1, 2], freq[domino]: 2, nChoose2: 1 57 | # pairs: 4 58 | -------------------------------------------------------------------------------- /leetcode/prison_cells_after_n_days_spec.rb: -------------------------------------------------------------------------------- 1 | # https://leetcode.com/problems/prison-cells-after-n-days/ 2 | # Runtime: 32 ms, faster than 91.67% of Ruby online submissions. 3 | # Memory Usage: 9.6 MB, less than 100.00% of Ruby online submissions. 4 | 5 | def prison_after_n_days(cells, n) 6 | result = Array.new(8, 0) 7 | # n %= 14 8 | # n = 14 if n == 0 9 | n.times do |i| 10 | (1..6).each do |col| 11 | result[col] = cells[col - 1] == cells[col + 1] ? 0 : 1 12 | 13 | # Alternate XOR answer. Runtime 36 ms. 14 | # result[col] = 1 - cells[col - 1] ^ cells[col + 1] 15 | end 16 | cells = result.dup 17 | end 18 | 19 | result 20 | end 21 | 22 | describe "#prison_after_n_days" do 23 | it 'gives state of prison after n days' do 24 | expect(prison_after_n_days([1,0,0,0,0,1,0,0], 1)).to eq [0,1,0,0,1,0,1,0] 25 | expect(prison_after_n_days([1,1,1,0,1,1,1,1], 2)).to eq [0,0,0,0,0,1,1,0] 26 | 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /leetcode/product_of_array_except_self_spec.rb: -------------------------------------------------------------------------------- 1 | # https://leetcode.com/problems/product-of-array-except-self/ 2 | # Runtime: 64 ms, faster than 83.43% of Ruby online submissions for Product of Array Except Self. 3 | # Memory Usage: 13.1 MB, less than 100.00% of Ruby online submissions for Product of Array Except Self. 4 | 5 | def product_except_self(nums) 6 | answer = Array.new(nums.size) 7 | leftprod, rightprod = 1, 1 8 | 9 | nums.size.times do |i| 10 | answer[i] = leftprod 11 | leftprod *= nums[i] 12 | end 13 | # Ex: For nums.size = 4, 14 | # answer[0] = 1 15 | # answer[1] = nums[0] 16 | # answer[2] = nums[0] * nums[1] 17 | # answer[3] = nums[0] * nums[1] * nums[2] 18 | 19 | 20 | (nums.size - 2).downto(0).each do |i| 21 | rightprod *= nums[i + 1] 22 | answer[i] *= rightprod 23 | end 24 | # answer[2] = answer[2] * nums[3] 25 | # answer[1] = answer[1] * nums[2] * nums[3] 26 | # answer[0] = answer[0] * nums[1] * nums[2] * nums[3] 27 | 28 | answer 29 | end 30 | 31 | describe "#product_except_self" do 32 | it 'gets product of all nums in array except num at that index' do 33 | expect(product_except_self([5, 6, 2, 10])).to eq [120,100,300,60] 34 | expect(product_except_self([1,2,3,4])).to eq [24,12,8,6] 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /leetcode/relative_sort_array.rb: -------------------------------------------------------------------------------- 1 | # https://leetcode.com/problems/relative-sort-array/ 2 | # Runtime: 36 ms, faster than 66.67% of Ruby online submissions for Relative Sort Array. 3 | # Memory Usage: 9.4 MB, less than 100.00% of Ruby online submissions for Relative Sort Array. 4 | def relative_sort_array(arr1, arr2) 5 | output = [] 6 | arr1.sort! 7 | f1, f2 = {}, {} 8 | arr1.each do |num| 9 | f1[num] ? f1[num] += 1 : f1[num] = 1 10 | end 11 | arr2.each do |num| 12 | f2[num] ? f2[num] += 1 : f2[num] = 1 13 | end 14 | 15 | f2.each do |num, freq| 16 | if f1[num] 17 | f1[num].times do |i| 18 | output << num 19 | f1.delete(num) 20 | end 21 | end 22 | end 23 | 24 | f1.each do |num, freq| 25 | freq.times do |i| 26 | output << num 27 | end 28 | end 29 | 30 | output 31 | end 32 | -------------------------------------------------------------------------------- /leetcode/reverse_string_ii_spec.rb: -------------------------------------------------------------------------------- 1 | # https://leetcode.com/problems/reverse-string-ii/ 2 | # Runtime: 32 ms, faster than 100.00% of Ruby online submissions for Reverse String II. 3 | # Memory Usage: 14.7 MB, less than 100.00% of Ruby online submissions for Reverse String II. 4 | 5 | def reverse_str(s, k) 6 | return s.reverse if k >= s.length 7 | i, reverse = 0, 1 8 | 9 | while i < s.length do 10 | if reverse == 1 11 | if i + k > s.length - 1 12 | newstr = s[i..s.length - 1].reverse 13 | else 14 | newstr = s[i..i + k - 1].reverse + s[i + k..s.length - 1] 15 | end 16 | 17 | s = i == 0 ? newstr : s[0..i - 1] + newstr 18 | end 19 | 20 | reverse *= -1 21 | i += k 22 | end 23 | 24 | s 25 | end 26 | 27 | describe "#reverse_string ii" do 28 | it 'reverse the first k characters for every 2k characters counting from start of string' do 29 | expect(reverse_str("abcdefg", 2)).to eq "bacdfeg" 30 | expect(reverse_str("abcd", 4)).to eq "dcba" 31 | expect(reverse_str("abcdefg", 8)).to eq "gfedcba" 32 | 33 | expect(reverse_str("hyzqyljrnigxvdtneasepfahmtyhlohwxmkqcdfehybknvdmfrfvtbsovjbdhevlfxpdaovjgunjqlimjkfnqcqnajmebeddqsgl", 39)).to eq "fdcqkmxwholhytmhafpesaentdvxginrjlyqzyhehybknvdmfrfvtbsovjbdhevlfxpdaovjgunjqllgsqddebemjanqcqnfkjmi" 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /leetcode/robot_return_to_origin.rb: -------------------------------------------------------------------------------- 1 | # https://leetcode.com/problems/robot-return-to-origin/ 2 | def judge_circle(moves) 3 | dir = {'R' => [0, 1], 'L' => [0, -1], 'D' => [1, 0], 'U' => [-1, 0]} 4 | row, col = 0, 0 5 | moves.each_char do |move| 6 | drow, dcol = dir[move] 7 | row += drow 8 | col += dcol 9 | end 10 | 11 | return true if row == 0 && col == 0 12 | false 13 | end 14 | -------------------------------------------------------------------------------- /leetcode/rotting_oranges_spec.rb: -------------------------------------------------------------------------------- 1 | # https://leetcode.com/problems/rotting-oranges/ 2 | # Runtime: 40 ms, faster than 88.42% of Ruby online submissions for Rotting Oranges. 3 | # Memory Usage: 9.6 MB, less than 100.00% of Ruby online submissions for Rotting Oranges. 4 | 5 | # BFS strategy: 6 | # 1. Find all rotten oranges and total fresh oranges. Take first cell off queue. 7 | # 2. While rotten orange queue is NOT empty 8 | # For each cell, search neighbors in 4 directions (right, left, down, up) 9 | # If neighbor == fresh 10 | # 5. make it rotten 11 | # 6. add to queue [neighbor_row, neighbor_col, depth + 1] 12 | # 7. subtract 1 from # of fresh oranges 13 | # 9. Return depth if fresh == 0 14 | # 10. Return -1 15 | 16 | FRESH, ROTTEN = 1, 2 17 | 18 | # Time complexity: O(n * m). Space complexity: O(n * m). 19 | def find_rotten_fresh(arr) 20 | rotten, fresh = [], 0 21 | arr.size.times do |row| 22 | arr[0].size.times do |col| 23 | rotten << [row, col, 0] if arr[row][col] == ROTTEN 24 | fresh += 1 if arr[row][col] == FRESH 25 | end 26 | end 27 | [rotten, fresh] 28 | end 29 | 30 | def oranges(arr) 31 | depth = 0 32 | queue, fresh = find_rotten_fresh(arr) # queue has only ROTTEN tomatoes 33 | height, width = arr.size, arr[0].size 34 | directions = [[0, 1], [0, -1], [1, 0], [-1, 0]] 35 | max_depth = -1 36 | 37 | while !queue.empty? do 38 | row, col, depth = queue.shift 39 | max_depth = [max_depth, depth].max 40 | 41 | directions.each do |drow, dcol| 42 | nrow = row + drow 43 | ncol = col + dcol 44 | 45 | if ncol.between?(0, width - 1) && nrow.between?(0, height - 1) && arr[nrow][ncol] == FRESH 46 | arr[nrow][ncol] = ROTTEN 47 | queue << [nrow, ncol, depth + 1] 48 | fresh -= 1 49 | end 50 | end 51 | end 52 | 53 | return depth if fresh == 0 54 | -1 55 | end 56 | 57 | describe "#oranges" do 58 | it 'makes oranges rotten according to rules' do 59 | expect(oranges([[2,1,1],[1,1,0],[0,1,1]])).to eq 4 60 | expect(oranges([[2,1,1],[0,1,1],[1,0,1]])).to eq -1 61 | expect(oranges([[0,2]])).to eq 0 62 | expect(oranges([[2,1,0,2]])).to eq 1 63 | expect(oranges([[1,2,1,1,2,1,1]])).to eq 2 64 | end 65 | end 66 | 67 | # original_queue_size: 1. rotten queue: [[0, 0]] 68 | # cell[0, 0] = 2. queue: []. fresh: 6 69 | # neighbor[0, 1] = 1 70 | # ** [0, 1] turns rotten ** 71 | # neighbor[1, 0] = 1 72 | # ** [1, 0] turns rotten ** 73 | # depth: 1 74 | # [2, 2, 1] 75 | # [2, 1, 0] 76 | # [0, 1, 1] 77 | 78 | # original_queue_size: 2. rotten queue: [[0, 1], [1, 0]] 79 | # cell[0, 1] = 2. queue: [[1, 0]]. fresh: 4 80 | # neighbor[0, 2] = 1 81 | # ** [0, 2] turns rotten ** 82 | # neighbor[0, 0] = 2 83 | # neighbor[1, 1] = 1 84 | # ** [1, 1] turns rotten ** 85 | # cell[1, 0] = 2. queue: [[0, 2], [1, 1]]. fresh: 2 86 | # neighbor[1, 1] = 2 87 | # neighbor[2, 0] = 0 88 | # neighbor[0, 0] = 2 89 | # depth: 2 90 | # [2, 2, 2] 91 | # [2, 2, 0] 92 | # [0, 1, 1] 93 | 94 | # original_queue_size: 2. rotten queue: [[0, 2], [1, 1]] 95 | # cell[0, 2] = 2. queue: [[1, 1]]. fresh: 2 96 | # neighbor[0, 1] = 2 97 | # neighbor[1, 2] = 0 98 | # cell[1, 1] = 2. queue: []. fresh: 2 99 | # neighbor[1, 2] = 0 100 | # neighbor[1, 0] = 2 101 | # neighbor[2, 1] = 1 102 | # ** [2, 1] turns rotten ** 103 | # neighbor[0, 1] = 2 104 | # depth: 3 105 | # [2, 2, 2] 106 | # [2, 2, 0] 107 | # [0, 2, 1] 108 | 109 | # original_queue_size: 1. rotten queue: [[2, 1]] 110 | # cell[2, 1] = 2. queue: []. fresh: 1 111 | # neighbor[2, 2] = 1 112 | # ** [2, 2] turns rotten ** 113 | # neighbor[2, 0] = 0 114 | # neighbor[1, 1] = 2 115 | # depth: 4 116 | # [2, 2, 2] 117 | # [2, 2, 0] 118 | # [0, 2, 2] 119 | 120 | # original_queue_size: 1. rotten queue: [[2, 2]] 121 | # cell[2, 2] = 2. queue: []. fresh: 0 122 | # neighbor[2, 1] = 2 123 | # neighbor[1, 2] = 0 124 | #----------- 125 | # original_queue_size: 1. rotten queue: [[0, 0]] 126 | # cell[0, 0] = 2. queue: []. fresh: 6 127 | # neighbor[0, 1] = 1 128 | # ** [0, 1] turns rotten ** 129 | # neighbor[1, 0] = 0 130 | # depth: 1 131 | # [2, 2, 1] 132 | # [0, 1, 1] 133 | # [1, 0, 1] 134 | 135 | # original_queue_size: 1. rotten queue: [[0, 1]] 136 | # cell[0, 1] = 2. queue: []. fresh: 5 137 | # neighbor[0, 2] = 1 138 | # ** [0, 2] turns rotten ** 139 | # neighbor[0, 0] = 2 140 | # neighbor[1, 1] = 1 141 | # ** [1, 1] turns rotten ** 142 | # depth: 2 143 | # [2, 2, 2] 144 | # [0, 2, 1] 145 | # [1, 0, 1] 146 | 147 | # original_queue_size: 2. rotten queue: [[0, 2], [1, 1]] 148 | # cell[0, 2] = 2. queue: [[1, 1]]. fresh: 3 149 | # neighbor[0, 1] = 2 150 | # neighbor[1, 2] = 1 151 | # ** [1, 2] turns rotten ** 152 | # cell[1, 1] = 2. queue: [[1, 2]]. fresh: 2 153 | # neighbor[1, 2] = 2 154 | # neighbor[1, 0] = 0 155 | # neighbor[2, 1] = 0 156 | # neighbor[0, 1] = 2 157 | # depth: 3 158 | # [2, 2, 2] 159 | # [0, 2, 2] 160 | # [1, 0, 1] 161 | 162 | # original_queue_size: 1. rotten queue: [[1, 2]] 163 | # cell[1, 2] = 2. queue: []. fresh: 2 164 | # neighbor[1, 1] = 2 165 | # neighbor[2, 2] = 1 166 | # ** [2, 2] turns rotten ** 167 | # neighbor[0, 2] = 2 168 | # depth: 4 169 | # [2, 2, 2] 170 | # [0, 2, 2] 171 | # [1, 0, 2] 172 | 173 | # original_queue_size: 1. rotten queue: [[2, 2]] 174 | # cell[2, 2] = 2. queue: []. fresh: 1 175 | # neighbor[2, 1] = 0 176 | # neighbor[1, 2] = 2 177 | #----------- 178 | # original_queue_size: 1. rotten queue: [[0, 1]] 179 | # cell[0, 1] = 2. queue: []. fresh: 0 180 | # neighbor[0, 0] = 0 181 | #----------- 182 | # original_queue_size: 2. rotten queue: [[0, 0], [0, 3]] 183 | # cell[0, 0] = 2. queue: [[0, 3]]. fresh: 1 184 | # neighbor[0, 1] = 1 185 | # ** [0, 1] turns rotten ** 186 | # cell[0, 3] = 2. queue: [[0, 1]]. fresh: 0 187 | # neighbor[0, 2] = 0 188 | # depth: 1 189 | # [2, 2, 0, 2] 190 | 191 | # original_queue_size: 1. rotten queue: [[0, 1]] 192 | # cell[0, 1] = 2. queue: []. fresh: 0 193 | # neighbor[0, 2] = 0 194 | # neighbor[0, 0] = 2 195 | #----------- 196 | # original_queue_size: 2. rotten queue: [[0, 1], [0, 4]] 197 | # cell[0, 1] = 2. queue: [[0, 4]]. fresh: 5 198 | # neighbor[0, 2] = 1 199 | # ** [0, 2] turns rotten ** 200 | # neighbor[0, 0] = 1 201 | # ** [0, 0] turns rotten ** 202 | # cell[0, 4] = 2. queue: [[0, 2], [0, 0]]. fresh: 3 203 | # neighbor[0, 5] = 1 204 | # ** [0, 5] turns rotten ** 205 | # neighbor[0, 3] = 1 206 | # ** [0, 3] turns rotten ** 207 | # depth: 1 208 | # [2, 2, 2, 2, 2, 2, 1] 209 | 210 | # original_queue_size: 4. rotten queue: [[0, 2], [0, 0], [0, 5], [0, 3]] 211 | # cell[0, 2] = 2. queue: [[0, 0], [0, 5], [0, 3]]. fresh: 1 212 | # neighbor[0, 3] = 2 213 | # neighbor[0, 1] = 2 214 | # cell[0, 0] = 2. queue: [[0, 5], [0, 3]]. fresh: 1 215 | # neighbor[0, 1] = 2 216 | # cell[0, 5] = 2. queue: [[0, 3]]. fresh: 1 217 | # neighbor[0, 6] = 1 218 | # ** [0, 6] turns rotten ** 219 | # neighbor[0, 4] = 2 220 | # cell[0, 3] = 2. queue: [[0, 6]]. fresh: 0 221 | # neighbor[0, 4] = 2 222 | # neighbor[0, 2] = 2 223 | # depth: 2 224 | # [2, 2, 2, 2, 2, 2, 2] 225 | 226 | # original_queue_size: 1. rotten queue: [[0, 6]] 227 | # cell[0, 6] = 2. queue: []. fresh: 0 228 | # neighbor[0, 5] = 2 229 | -------------------------------------------------------------------------------- /leetcode/search_in_rotated_sorted_array_spec.rb: -------------------------------------------------------------------------------- 1 | # https://leetcode.com/problems/search-in-rotated-sorted-array/ 2 | # Video explanation: https://www.youtube.com/watch?v=QdVrY3stDD4 3 | # Runtime: 36 ms, faster than 61.33% of Ruby online submissions for Search in Rotated Sorted Array. 4 | # Memory Usage: 9.6 MB, less than 100.00% of Ruby online submissions for Search in Rotated Sorted Array. 5 | 6 | # @param {Integer[]} nums 7 | # @param {Integer} target 8 | # @return {Integer} 9 | 10 | # O(log n) time complexity. O(1) space complexity. 11 | def search(nums, target) 12 | return -1 if nums.size == 0 13 | left = 0 14 | right = nums.size - 1 15 | 16 | # find index of smallest element 17 | while left < right do 18 | mid = (left + right) / 2 19 | if nums[mid] > nums[right] 20 | left = mid + 1 21 | else 22 | right = mid 23 | end 24 | end 25 | # puts "smallest element: #{left}" 26 | 27 | if target.between?(nums[left], nums[nums.size - 1]) 28 | right = nums.size - 1 # search to right of smallest element 29 | else 30 | right = left # search to left of smallest element 31 | left = 0 32 | end 33 | 34 | # normal binary search 35 | while left <= right do 36 | mid = (left + right) / 2 37 | if target == nums[mid] 38 | return mid 39 | elsif target > nums[mid] 40 | left = mid + 1 41 | else 42 | right = mid - 1 43 | end 44 | end 45 | 46 | -1 47 | end 48 | 49 | describe '#search' do 50 | it 'finds index of target value in rotated sorted array' do 51 | expect(search([], 0)).to eq -1 52 | expect(search([3,1], 3)).to eq 0 53 | expect(search([5,1,3], 5)).to eq 0 54 | expect(search([4,5,6,7,0,1,2], 0)).to eq 4 55 | expect(search([4,5,6,7,0,1,2], 3)).to eq -1 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /leetcode/spiral_matrix_ii_spec.rb: -------------------------------------------------------------------------------- 1 | # https://leetcode.com/problems/spiral-matrix-ii/ 2 | def print_matrix(arr) 3 | arr.each do |r| 4 | puts r.each { |p| p }.join(" ") 5 | end 6 | end 7 | 8 | def spiral_matrix(n) 9 | matrix = Array.new(n){ |cell| Array.new(n, 0) } 10 | count = 1 11 | col_start, col_end = 0, n - 1 12 | row_start, row_end = 0, n - 1 13 | 14 | while col_start <= col_end && row_start <= row_end do 15 | 16 | # right 17 | (col_start..col_end).each do |col| 18 | # puts "row_start: #{row_start}, col: #{col}" 19 | matrix[row_start][col] = count 20 | count += 1 21 | end 22 | row_start += 1 23 | 24 | # down 25 | (row_start..row_end).each do |row| 26 | # puts "row: #{row}, col_end: #{col_end}" 27 | matrix[row][col_end] = count 28 | count += 1 29 | end 30 | col_end -= 1 31 | 32 | # left 33 | col_end.downto(col_start).each do |col| 34 | # puts "row_end: #{row_end}, col: #{col}" 35 | matrix[row_end][col] = count 36 | count += 1 37 | end 38 | row_end -= 1 39 | 40 | # up 41 | row_end.downto(row_start).each do |row| 42 | # puts "row: #{row}, col_start: #{col_start}" 43 | matrix[row][col_start] = count 44 | count += 1 45 | end 46 | col_start += 1 47 | end 48 | 49 | matrix 50 | end 51 | 52 | describe '#spiral_matrix' do 53 | it 'creates 2x2 spiral matrix' do 54 | expect(spiral_matrix(2)).to eq [[1, 2], 55 | [4, 3]] 56 | end 57 | 58 | it 'creates 3x3 spiral matrix' do 59 | expect(spiral_matrix(3)).to eq [[1, 2, 3], 60 | [8, 9, 4], 61 | [7, 6, 5]] 62 | end 63 | 64 | it 'creates 4x4 spiral matrix' do 65 | expect(spiral_matrix(4)).to eq [[ 1, 2, 3, 4], 66 | [12,13,14, 5], 67 | [11,16,15, 6], 68 | [10, 9, 8, 7]] 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /leetcode/string_transforms_into_another_string_spec.rb: -------------------------------------------------------------------------------- 1 | # https://leetcode.com/problems/string-transforms-into-another-string/ 2 | # Runtime: 32 ms, faster than 100.00% of Ruby online submissions for String Transforms Into Another String. 3 | # Memory Usage: 9.9 MB, less than 100.00% of Ruby online submissions for String Transforms Into Another String. 4 | # O(n) time, O(26) space 5 | def convert?(str1, str2) 6 | return true if str1 == str2 7 | str1_dictionary = {} # str1 character maps to str2 character, like cryptography. 8 | str2_set = Set[] # Instead of Set, we may use hash and only look at keys 9 | str1.size.times do |i| 10 | if str1_dictionary[str1[i]] 11 | return false if str1_dictionary[str1[i]] != str2[i] 12 | else 13 | str1_dictionary[str1[i]] = str2[i] 14 | str2_set.add(str2[i]) 15 | end 16 | end 17 | 18 | # If str1 dictionary uses all 26 possible letters but str2 does NOT, 19 | # it means more than 1 str1 letter maps to the SAME str2 letter. 20 | # Thus for any unconverted str1 letters, we may use 21 | if str1_dictionary.keys.size == 26 22 | return str2_set.size < 26 ? true : false 23 | end 24 | 25 | true 26 | end 27 | 28 | # Imagine an alphabet of a-e (5 letters). 29 | 30 | # str1 dictionary = {a: f, b: e, c: d, e: d, g: e, h: d} 31 | 32 | # str1 = "bggbehcaaa" (uses 6 unique chars: a, b, c, e, g, h) 33 | # str2 = "eeeedddfff" (uses 3 unique chars) 34 | # bggbchcaaa 35 | # a => f 36 | # bggbehcfff 37 | # e => d 38 | bggbdhcfff 39 | # b => e 40 | eggedhcfff 41 | # c => d 42 | eggedhdfff 43 | # g => e 44 | # eeeedhdfff 45 | # h => d 46 | # eeeedddfff 47 | It's possible to convert from str1 to str2 if: 48 | 1. >1 char from str1 maps to same chat in str2 49 | 2. str1 and str2 don't share any characters 50 | 51 | # abcde 52 | # ecabd 53 | 54 | # ebcde 55 | # eccde 56 | # eaade 57 | # eaabe 58 | # daabd 59 | 60 | describe "#convert" do 61 | it 'can convert str1 to str2' do 62 | expect(convert?("aabcc", "ccdee")).to eq true 63 | expect(convert?("leetcode", "codeleet")).to eq false 64 | expect(convert?("abca", "dced")).to eq true 65 | expect(convert?("ab", "ba")).to eq true 66 | expect(convert?("aa", "cd")).to eq false 67 | expect(convert?("ab", "aa")).to eq true 68 | expect(convert?("abcdefghijklmnopqrstuvwxyz", "bcdefghijklmnopqrstuvwxyza")).to eq false 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /leetcode/subsequences_spec.rb: -------------------------------------------------------------------------------- 1 | # 2 pointers 2 | def subsequence?(s, t) 3 | i, j = 0, 0 4 | matches = 0 5 | 6 | while i < s.size && j < t.size do 7 | if s[i] == t[j] 8 | matches += 1 9 | i += 1 10 | end 11 | 12 | j += 1 13 | end 14 | 15 | return true if s.size == matches 16 | false 17 | end 18 | 19 | describe "#subsequence" do 20 | it 'returns if s is subsequence of t' do 21 | expect(subsequence?('abc', 'ahbgdc')).to eq true 22 | expect(subsequence?('axc', 'ahbgdc')).to eq false 23 | expect(subsequence?('', 'ahbgdc')).to eq true 24 | expect(subsequence?('abc', '')).to eq false 25 | expect(subsequence?('', '')).to eq true 26 | expect(subsequence?('abc', 'adabc')).to eq true 27 | expect(subsequence?('abc', 'aabc')).to eq true 28 | expect(subsequence?('aabc', 'abc')).to eq false 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /leetcode/sudoku.rb: -------------------------------------------------------------------------------- 1 | # Sudoku Solver: https://leetcode.com/problems/sudoku-solver/. 2 | # Good video solution guide (Python): https://www.youtube.com/watch?v=lK4N8E6uNr4 3 | # STRATEGY: 4 | # 1. Write find_empty(a), to find next empty cell in board a. 5 | # 2. Write valid?(a, num, row, col), to return T/F if num is valid choice at [row, col]. 6 | # Check 3 ways: horizontally, vertically, and in the right 3x3 box of [row, col]. 7 | 8 | # 3. Recursive answer sudoku(a): 9 | # a. Find row, col of next empty cell. If it's [nil, nil], return T. We're done! 10 | # b. Loop num from 1 to 9 (all possible values): 11 | # i. If num is valid at [row, col] of next empty cell: 12 | # - set empty cell to num 13 | # - return T if no more empty cells 14 | # 15 | # If either of 2 things happen, we BACKTRACK (https://www.javatpoint.com/backtracking-introduction): 16 | # 1. Run out of numbers (1-9) to fill the cell 17 | # 2. Board runs out of empty cells for the num we're trying 18 | # See comments below for what BACKTRACKING means. 19 | require 'pp' 20 | 21 | a = [["5","3",".",".","7",".",".",".","."], 22 | ["6",".",".","1","9","5",".",".","."], 23 | [".","9","8",".",".",".",".","6","."], 24 | ["8",".",".",".","6",".",".",".","3"], 25 | ["4",".",".","8",".","3",".",".","1"], 26 | ["7",".",".",".","2",".",".",".","6"], 27 | [".","6",".",".",".",".","2","8","."], 28 | [".",".",".","4","1","9",".",".","5"], 29 | [".",".",".",".","8",".",".","7","9"]] 30 | 31 | def find_empty(a) 32 | a.size.times do |row| 33 | a[0].size.times do |col| 34 | return [row, col] if a[row][col] == '.' 35 | end 36 | end 37 | 38 | [nil, nil] 39 | end 40 | 41 | def valid?(a, num, row, col) 42 | a[row].each do |col_val| 43 | return false if col_val == num.to_s 44 | end 45 | 46 | a.size.times do |r| 47 | return false if a[r][col] == num.to_s 48 | end 49 | 50 | # If row = 0 to 2, row / 3 * 3 = 0 51 | # If row = 3 to 5, row / 3 * 3 = 3 52 | # If row = 6 to 8, row / 3 * 3 = 6 53 | rstart = row / 3 * 3 54 | cstart = col / 3 * 3 55 | 56 | (rstart..rstart + 2).each do |r| 57 | (cstart..cstart + 2).each do |c| 58 | return false if a[r][c] == num.to_s 59 | end 60 | end 61 | 62 | true 63 | end 64 | 65 | def sudoku(a) 66 | row, col = find_empty(a) 67 | puts "Next empty cell: [#{row}, #{col}]" 68 | return true if row.nil? && col.nil? # We're done! 69 | 70 | (1..9).each do |num| 71 | if valid?(a, num, row, col) 72 | a[row][col] = num.to_s 73 | puts "Put #{num} into empty cell [#{row}, #{col}]" 74 | return true if sudoku(a) 75 | 76 | # Only hits this line in 2 cases: 77 | # 1. We run out of numbers (1-9) to fill that cell 78 | # 2. Board runs out of empty cells for the num we're trying 79 | 80 | # If one of these is true, code will BACKTRACK: 81 | # - It makes cell empty again ('.') 82 | # - It goes up to previous recursive loop and tries next possible num (1-9) 83 | puts "No more numbers to try or empty cells. Backtracking...Making [#{row}, #{col}] empty again." 84 | a[row][col] = '.' 85 | end 86 | end 87 | 88 | false 89 | end 90 | 91 | pp a 92 | puts '-------------------------------------' 93 | sudoku(a) 94 | pp a 95 | 96 | # OUTPUT: 97 | # [["5", "3", ".", ".", "7", ".", ".", ".", "."], 98 | # ["6", ".", ".", "1", "9", "5", ".", ".", "."], 99 | # [".", "9", "8", ".", ".", ".", ".", "6", "."], 100 | # ["8", ".", ".", ".", "6", ".", ".", ".", "3"], 101 | # ["4", ".", ".", "8", ".", "3", ".", ".", "1"], 102 | # ["7", ".", ".", ".", "2", ".", ".", ".", "6"], 103 | # [".", "6", ".", ".", ".", ".", "2", "8", "."], 104 | # [".", ".", ".", "4", "1", "9", ".", ".", "5"], 105 | # [".", ".", ".", ".", "8", ".", ".", "7", "9"]] 106 | # ------------------------------------- 107 | # [["5", "3", "4", "6", "7", "8", "9", "1", "2"], 108 | # ["6", "7", "2", "1", "9", "5", "3", "4", "8"], 109 | # ["1", "9", "8", "3", "4", "2", "5", "6", "7"], 110 | # ["8", "5", "9", "7", "6", "1", "4", "2", "3"], 111 | # ["4", "2", "6", "8", "5", "3", "7", "9", "1"], 112 | # ["7", "1", "3", "9", "2", "4", "8", "5", "6"], 113 | # ["9", "6", "1", "5", "3", "7", "2", "8", "4"], 114 | # ["2", "8", "7", "4", "1", "9", "6", "3", "5"], 115 | # ["3", "4", "5", "2", "8", "6", "1", "7", "9"]] 116 | 117 | # Uncommenting above print statements: 118 | # Next empty cell: [0, 2] 119 | # Put 1 into empty cell [0, 2] 120 | # Next empty cell: [0, 3] 121 | # Put 2 into empty cell [0, 3] 122 | # Next empty cell: [0, 5] 123 | # Put 4 into empty cell [0, 5] 124 | # Next empty cell: [0, 6] 125 | # Put 8 into empty cell [0, 6] 126 | # Next empty cell: [0, 7] 127 | # Put 9 into empty cell [0, 7] 128 | # Next empty cell: [0, 8] 129 | # No more numbers to try or empty cells. Backtracking...Making [0, 7] empty again. 130 | # No more numbers to try or empty cells. Backtracking...Making [0, 6] empty again. 131 | # Put 9 into empty cell [0, 6] 132 | # Next empty cell: [0, 7] 133 | # No more numbers to try or empty cells. Backtracking...Making [0, 6] empty again. 134 | # No more numbers to try or empty cells. Backtracking...Making [0, 5] empty again. 135 | # Put 6 into empty cell [0, 5] 136 | # Next empty cell: [0, 6] 137 | # Put 4 into empty cell [0, 6] 138 | # Next empty cell: [0, 7] 139 | # Put 9 into empty cell [0, 7] 140 | # Next empty cell: [0, 8] 141 | # Put 8 into empty cell [0, 8] 142 | # Next empty cell: [1, 1] 143 | # Put 2 into empty cell [1, 1] 144 | # Next empty cell: [1, 2] 145 | # Put 4 into empty cell [1, 2] 146 | # Next empty cell: [1, 6] 147 | # Put 3 into empty cell [1, 6] 148 | # Next empty cell: [1, 7] 149 | # No more numbers to try or empty cells. Backtracking...Making [1, 6] empty again. 150 | # Put 7 into empty cell [1, 6] 151 | # Next empty cell: [1, 7] 152 | # Put 3 into empty cell [1, 7] 153 | # Next empty cell: [1, 8] 154 | # No more numbers to try or empty cells. Backtracking...Making [1, 7] empty again. 155 | # No more numbers to try or empty cells. Backtracking...Making [1, 6] empty again. 156 | # No more numbers to try or empty cells. Backtracking...Making [1, 2] empty again. 157 | # Put 7 into empty cell [1, 2] 158 | # Next empty cell: [1, 6] 159 | # Put 3 into empty cell [1, 6] 160 | # Next empty cell: [1, 7] 161 | # No more numbers to try or empty cells. Backtracking...Making [1, 6] empty again. 162 | # No more numbers to try or empty cells. Backtracking...Making [1, 2] empty again. 163 | # No more numbers to try or empty cells. Backtracking...Making [1, 1] empty again. 164 | # Put 4 into empty cell [1, 1] 165 | # Next empty cell: [1, 2] 166 | # Put 2 into empty cell [1, 2] 167 | # Next empty cell: [1, 6] 168 | # Put 3 into empty cell [1, 6] 169 | # Next empty cell: [1, 7] 170 | # No more numbers to try or empty cells. Backtracking...Making [1, 6] empty again. 171 | # Put 7 into empty cell [1, 6] 172 | # Next empty cell: [1, 7] 173 | # Put 3 into empty cell [1, 7] 174 | # Next empty cell: [1, 8] 175 | # No more numbers to try or empty cells. Backtracking...Making [1, 7] empty again. 176 | # No more numbers to try or empty cells. Backtracking...Making [1, 6] empty again. 177 | # No more numbers to try or empty cells. Backtracking...Making [1, 2] empty again. 178 | # Put 7 into empty cell [1, 2] 179 | # Next empty cell: [1, 6] 180 | # Put 3 into empty cell [1, 6] 181 | # Next empty cell: [1, 7] 182 | # Put 2 into empty cell [1, 7] 183 | # Next empty cell: [1, 8] 184 | # No more numbers to try or empty cells. Backtracking...Making [1, 7] empty again. 185 | # No more numbers to try or empty cells. Backtracking...Making [1, 6] empty again. 186 | # No more numbers to try or empty cells. Backtracking...Making [1, 2] empty again. 187 | # No more numbers to try or empty cells. Backtracking...Making [1, 1] empty again. 188 | # Put 7 into empty cell [1, 1] 189 | # Next empty cell: [1, 2] 190 | # Put 2 into empty cell [1, 2] 191 | # Next empty cell: [1, 6] 192 | # Put 3 into empty cell [1, 6] 193 | # Next empty cell: [1, 7] 194 | # No more numbers to try or empty cells. Backtracking...Making [1, 6] empty again. 195 | # No more numbers to try or empty cells. Backtracking...Making [1, 2] empty again. 196 | # Put 4 into empty cell [1, 2] 197 | # Next empty cell: [1, 6] 198 | # Put 3 into empty cell [1, 6] 199 | # Next empty cell: [1, 7] 200 | # Put 2 into empty cell [1, 7] 201 | # Next empty cell: [1, 8] 202 | # No more numbers to try or empty cells. Backtracking...Making [1, 7] empty again. 203 | # No more numbers to try or empty cells. Backtracking...Making [1, 6] empty again. 204 | # No more numbers to try or empty cells. Backtracking...Making [1, 2] empty again. 205 | # No more numbers to try or empty cells. Backtracking...Making [1, 1] empty again. 206 | # No more numbers to try or empty cells. Backtracking...Making [0, 8] empty again. 207 | # No more numbers to try or empty cells. Backtracking...Making [0, 7] empty again. 208 | # No more numbers to try or empty cells. Backtracking...Making [0, 6] empty again. 209 | # Put 8 into empty cell [0, 6] 210 | # Next empty cell: [0, 7] 211 | # Put 4 into empty cell [0, 7] 212 | # Next empty cell: [0, 8] 213 | # No more numbers to try or empty cells. Backtracking...Making [0, 7] empty again. 214 | # Put 9 into empty cell [0, 7] 215 | # Next empty cell: [0, 8] 216 | # Put 4 into empty cell [0, 8] 217 | # ...etc... -------------------------------------------------------------------------------- /leetcode/two_sum_iv_bst_spec.rb: -------------------------------------------------------------------------------- 1 | # https://leetcode.com/problems/two-sum-iv-input-is-a-bst/ 2 | # Runtime: 52 ms, faster than 100.00% of Ruby online submissions for Two Sum IV - Input is a BST. 3 | # Memory Usage: 10.2 MB, less than 100.00% of Ruby online submissions for Two Sum IV - Input is a BST. 4 | 5 | # Time complexity: O(n). Space complexity: O(n). n = # of nodes. 6 | class TreeNode 7 | attr_accessor :val, :left, :right 8 | 9 | def initialize(val) 10 | @val = val 11 | @left, @right = nil, nil 12 | end 13 | end 14 | 15 | root = TreeNode.new(5) 16 | root.left = TreeNode.new(3) 17 | root.left.left = TreeNode.new(2) 18 | root.left.right = TreeNode.new(4) 19 | root.right = TreeNode.new(6) 20 | root.right.right = TreeNode.new(7) 21 | 22 | # @param {TreeNode} root 23 | # @param {Integer} k 24 | # @return {Boolean} 25 | def find_target(root, k) 26 | @k, @hash = k, {} 27 | 28 | return true if find_diff(root) 29 | false 30 | end 31 | 32 | def find_diff(root) 33 | return if root.nil? 34 | diff = @k - root.val 35 | return true if @hash[root.val] 36 | @hash[diff] = true 37 | 38 | find_diff(root.left) || find_diff(root.right) 39 | end 40 | 41 | describe '#find_target' do 42 | it 'returns if there exists 2 elements in binary search tree so sum == target' do 43 | expect(find_target(root, 9)).to eq true 44 | expect(find_target(root, 28)).to eq false 45 | expect(find_target(root, 12)).to eq true 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /leetcode/two_sum_spec.rb: -------------------------------------------------------------------------------- 1 | # https://leetcode.com/problems/two-sum/ 2 | # Time complexity: O(n). Space complexity: O(n). 3 | def two_sum(nums, target) 4 | hash = {} 5 | nums.each_with_index do |num, i| 6 | diff = target - num 7 | if hash[diff] 8 | return [hash[diff], i] 9 | end 10 | hash[num] = i 11 | end 12 | end 13 | 14 | describe "#two_sum" do 15 | it 'returns indices of two numbers that add up to a specific target' do 16 | expect(two_sum([2, 7, 11, 15], 9)).to eq [0, 1] 17 | expect(two_sum([3, 3], 6)).to eq [0, 1] 18 | expect(two_sum([3, 2, 4], 6)).to eq [1, 2] 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /order_courses_spec.rb: -------------------------------------------------------------------------------- 1 | # Given college courses, each with array of prereqs, return possible order of 2 | # courses to take, so you always take prereqs before each related course 3 | 4 | # Depth-first search algorithm with stack 5 | 6 | def order(hash) 7 | ans, stack = [], [] 8 | 9 | hash.each do |id, pre| 10 | if pre == [] 11 | ans << id.to_s if !ans.include?(id.to_s) 12 | next 13 | end 14 | 15 | stack << id.to_s 16 | 17 | while !stack.empty? 18 | top = stack.last 19 | if !ans.include?(top) && hash[top.to_sym] == [] 20 | ans << stack.pop 21 | end 22 | 23 | hash[top.to_sym].each do |prereq| 24 | if !ans.include?(prereq) && !stack.include?(prereq) # not visited 25 | stack << prereq 26 | if !ans.include?(top) && prereq == [] 27 | ans << top 28 | end 29 | end 30 | end 31 | 32 | hash[top.to_sym] = [] # marks value of top as visited 33 | end 34 | end 35 | 36 | ans 37 | end 38 | 39 | describe '#order' do 40 | it 'returns articles in proper order, first showing prereqs' do 41 | expect(order({'A': [], 'B': [], 'C': []})).to eq ['A', 'B', 'C'] 42 | expect(order({'A': ['B'], 'B': [], 'C': []})).to eq ['B', 'A', 'C'] 43 | expect(order({'A': ['B', 'C'], 'B': [], 'C': []})).to eq ['C', 'B', 'A'] 44 | expect(order({'A': [], 'B': ['C', 'D'], 'C': ['D'], 'D': []})).to eq ['A', 'D', 'C', 'B'] 45 | expect(order({'A': ['E'], 'B': ['C', 'D', 'E'], 'C': ['D', 'E'], 'D': [], 'E': []})).to eq ['E', 'A', 'D', 'C', 'B'] 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "jest": { 3 | "testEnvironment": "node", 4 | "verbose": true 5 | }, 6 | "devDependencies": { 7 | "jest": "^27.5.1" 8 | }, 9 | "scripts": { 10 | "test": "jest" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /permutation.rb: -------------------------------------------------------------------------------- 1 | # print all permutations of a string 2 | # https://www.topcoder.com/generating-permutations/ 3 | # Also see my past answers: https://github.com/rayning0/codility/blob/master/permutation.rb 4 | 5 | # 2 swap algorithm. Faster! 6 | def permutation(str, n = str.length) 7 | if n == 1 8 | puts str 9 | else 10 | n.times do |i| 11 | str[i], str[n - 1] = str[n - 1], str[i] # swap str[i] with str[n-1] 12 | permutation(str, n - 1) 13 | str[i], str[n - 1] = str[n - 1], str[i] 14 | end 15 | end 16 | end 17 | 18 | # Decrease and Conquer: 19 | # 1. divide the problem into two parts: a sub-problem of size (n -1) and a single remaining element. 20 | # 2. solve the sub-problem of size (n-1) by a recursive call (or an iterative decreasing process). 21 | # 3. add the remaining individual element back into the sub-problem’s solution. 22 | def permute(a) 23 | return [a] if a.size < 2 24 | perms = [] 25 | a.each do |elem| 26 | permute(a - [elem]).each do |p| 27 | perms << ([elem] + p) 28 | end 29 | end 30 | perms 31 | end 32 | 33 | str = 'dear' 34 | 35 | now = Time.now 36 | permutation(str) 37 | puts "#{Time.now - now} secs. Faster algorithm" 38 | 39 | now = Time.now 40 | p permute(str.split('')).map(&:join) 41 | puts "#{Time.now - now} secs" 42 | 43 | # OUTPUT: 44 | 45 | # eard 46 | # aerd 47 | # ared 48 | # raed 49 | # erad 50 | # read 51 | # rade 52 | # arde 53 | # adre 54 | # dare 55 | # rdae 56 | # drae 57 | # erda 58 | # reda 59 | # rdea 60 | # drea 61 | # edra 62 | # dera 63 | # eadr 64 | # aedr 65 | # ader 66 | # daer 67 | # edar 68 | # dear 69 | # 7.7e-05 secs 70 | 71 | # ["dear", "dera", "daer", "dare", "drea", "drae", "edar", "edra", "eadr", "eard", "erda", "erad", "ader", "adre", "aedr", "aerd", "arde", "ared", "rdea", "rdae", "reda", "read", "rade", "raed"] 72 | # 0.000109 secs --------------------------------------------------------------------------------