diff --git a/README.md b/README.md index c93baf5df..7a8839913 100644 --- a/README.md +++ b/README.md @@ -7,45 +7,109 @@ List of Implementations: ``` . ├── array +│   ├── circular_counter.py +│   ├── flatten.py │   ├── garage.py -│   ├── house_robber.py -│   ├── longest_increasing_subsequence.py │   ├── longest_non_repeat.py +│   ├── merge_intervals.py +│   ├── missing_ranges.py │   ├── plus_one.py -│   └── wiggle_sort.py +│   ├── rotate_array.py +│   ├── summary_ranges.py +│   ├── three_sum.py +│   └── two_sum.py ├── backtrack │   ├── anagram.py +│   ├── array_sum_combinations.py │   ├── combination_sum.py +│   ├── expression_add_operators.py +│   ├── factor_combinations.py +│   ├── general_solution.md +│   ├── generate_abbreviations.py +│   ├── generate_parenthesis.py +│   ├── letter_combination.py │   ├── palindrome_partitioning.py +│   ├── pattern_match.py │   ├── permute.py │   ├── permute_unique.py │   ├── subsets.py │   └── subsets_unique.py ├── bfs -│   └── shortest_distance_from_all_buildings.py -├── divide-and-conquer -│   ├── expression-add-operators.py -│   └── the-skyline-problem.py +│   ├── shortest_distance_from_all_buildings.py +│   └── word_ladder.py +├── bit +│   ├── count_ones.py +│   ├── power_of_two.py +│   ├── reverse_bits.py +│   ├── single_number2.py +│   ├── single_number.py +│   └── subsets.py +├── design +│   ├── alarm_system.md +│   ├── all_o_one_ds.md +│   ├── calculator.md +│   ├── excel_table.md +│   ├── LRUcache.md +│   ├── nearby_drivers.md +│   ├── ride_sharing.md +│   ├── task_runner.md +│   └── twitter_feeds.md +├── dfs +│   ├── all_factors.py +│   ├── count_islands.py +│   ├── pacific_atlantic.py +│   ├── sudoku_solver.py +│   └── walls_and_gates.py ├── dp +│   ├── buy_sell_stock.py +│   ├── climbing_stairs.py +│   ├── combination_sum.py +│   ├── house_robber.py +│   ├── longest_increasing.py +│   ├── max_product_subarray.py │   ├── max_subarray.py +│   ├── num_decodings.py +│   ├── regex_matching.py │   └── word_break.py ├── graph +│   ├── clone_graph.py │   ├── find_path.py │   ├── graph.py │   └── traversal.py -├── hashtable -│   └── hashtable.py +├── heap +│   ├── merge_sorted_k_lists.py +│   ├── skyline.py +│   └── sliding_window_max.py ├── linkedlist +│   ├── add_two_numbers.py +│   ├── copy_random_pointer.py +│   ├── delete_node.py │   ├── first_cyclic_node.py +│   ├── is_cyclic.py │   ├── is_palindrome.py │   ├── kth_to_last.py │   ├── linkedlist.py -│   └── remove_duplicates.py +│   ├── remove_duplicates.py +│   ├── reverse.py +│   ├── rotate_list.py +│   └── swap_in_pairs.py +├── map +│   ├── hashtable.py +│   ├── longest_common_subsequence.py +│   ├── randomized_set.py +│   └── valid_sudoku.py +├── math +│   ├── generate_strobogrammtic.py +│   ├── is_strobogrammatic.py +│   ├── nth_digit.py +│   └── sqrt_precision_factor.py ├── matrix │   ├── bomb_enemy.py │   ├── matrix_rotation.txt -│   └── pacific_atlantic.py -├── out.txt +│   ├── rotate_image.py +│   ├── sparse_dot_vector.py +│   ├── sparse_mul.py +│   └── spiral_traversal.py ├── queue │   ├── __init__.py │   ├── max_sliding_window.py @@ -59,12 +123,17 @@ List of Implementations: │   ├── count_elem.py │   ├── first_occurance.py │   └── last_occurance.py -├── sorting +├── set +│   └── randomized_set.py +├── sort │   ├── insertion_sort.py +│   ├── meeting_rooms.py │   ├── merge_sort.py │   ├── quick_sort.py │   ├── selection_sort.py -│   └── sort_colors.py +│   ├── sort_colors.py +│   ├── topsort.py +│   └── wiggle_sort.py ├── stack │   ├── __init__.py │   ├── __init__.pyc @@ -72,43 +141,70 @@ List of Implementations: │   ├── __pycache__ │   │   ├── __init__.cpython-35.pyc │   │   └── stack.cpython-35.pyc +│   ├── simplify_path.py │   ├── stack.py -│   └── stack.pyc +│   ├── stack.pyc +│   └── valid_parenthesis.py ├── string +│   ├── add_binary.py +│   ├── breaking_bad.py │   ├── decode_string.py │   ├── encode_decode.py +│   ├── group_anagrams.py +│   ├── int_to_roman.py +│   ├── is_palindrome.py │   ├── license_number.py -│   ├── missing_ranges.py +│   ├── make_sentence.py +│   ├── multiply_strings.py +│   ├── one_edit_distance.py │   ├── rabin_karp.py │   ├── reverse_string.py │   ├── reverse_vowel.py │   ├── reverse_words.py +│   ├── roman_to_int.py │   └── word_squares.py -├── tests -│   └── test_stack.py +├── tmp +│   └── temporary.md ├── tree -│   ├── array2bst.py +│   ├── binary_tree_paths.py │   ├── bintree2list.py -│   ├── bst_closest_value.py -│   ├── BSTIterator.py +│   ├── bst +│   │   ├── array2bst.py +│   │   ├── bst_closest_value.py +│   │   ├── BSTIterator.py +│   │   ├── delete_node.py +│   │   ├── is_bst.py +│   │   ├── kth_smallest.py +│   │   ├── lowest_common_ancestor.py +│   │   ├── predecessor.py +│   │   ├── serialize_deserialize.py +│   │   ├── successor.py +│   │   └── unique_bst.py │   ├── deepest_left.py │   ├── invert_tree.py │   ├── is_balanced.py │   ├── is_subtree.py │   ├── is_symmetric.py │   ├── longest_consecutive.py +│   ├── lowest_common_ancestor.py │   ├── max_height.py │   ├── max_path_sum.py │   ├── min_height.py -│   ├── predecessor.py +│   ├── path_sum2.py +│   ├── path_sum.py +│   ├── pretty_print.py │   ├── same_tree.py -│   ├── successor.py -│   └── tree.py -└── trie - ├── add_and_search.py - └── trie.py - - +│   ├── traversal +│   │   ├── inorder.py +│   │   ├── level_order.py +│   │   └── zigzag.py +│   ├── tree.py +│   └── trie +│   ├── add_and_search.py +│   └── trie.py +├── tree.md +└── union-find + └── count_islands.py ``` diff --git a/array/circular_counter.py b/array/circular_counter.py new file mode 100644 index 000000000..dac8abef7 --- /dev/null +++ b/array/circular_counter.py @@ -0,0 +1,46 @@ +""" +There are people sitting in a circular fashion, +print every third member while removing them, +the next counter starts immediately after the member is removed. +Print till all the members are exhausted. + +For example: +Input: consider 123456789 members sitting in a circular fashion, +Output: 369485271 +""" + +a = ['1','2','3','4','5','6','7','8','9'] + +def josepheus(int_list, skip): + skip = skip - 1 #list starts with 0 index + idx = 0 + while len(int_list)>0: + idx = (skip+idx)%len(int_list) #hashing to keep changing the index to every 3rd + print int_list.pop(idx) + + +josepheus(a,3) + +""" +the reason for hashing is that we have to find the index of the item which needs to be removed. +So for e.g. if you iterate with the initial list of folks with every 3rd item eliminated: + +INPUT +int_list = 123456789 +skip = 3 + +While Iteration: + +int_list = 123456789 +len(int_list) = 9 +skip = 2 # as int_list starts from 0 +idx = (0 + 2) % 9 #here previous index was 0 +so 3rd element which is 3 in this case eliminated +int_list = 12456789 +len(int_list) = 8 +idx = (2 + 2) % 8 #here previous index was 2 +so 3rd element starting from 4th person which is 6 would be deleted. +and so on +The reason why we have to do this way is I am not putting the people who escape at the back of list so ideally in 2 while iteration the list should have been +45678912 and then no hashing needed to be done, which means you can directly remove the third element +""" diff --git a/array/flatten.py b/array/flatten.py new file mode 100644 index 000000000..87236fad1 --- /dev/null +++ b/array/flatten.py @@ -0,0 +1,60 @@ +""" +Implement Flatten Arrays. +Given an array that may contain nested arrays, +give a single resultant array. + +function flatten(input){ +} + +Example: + +Input: var input = [2, 1, [3, [4, 5], 6], 7, [8]]; +flatten(input); +Output: [2, 1, 3, 4, 5, 6, 7, 8] +""" + + +def list_flatten(l, a=None): + # check a + if a is None: + # initialize with empty list + a = [] + + for i in l: + if isinstance(i, list): + list_flatten(i, a) + else: + a.append(i) + return a + + +# stack version +# public static List flatten(List l) { +# List main = new ArrayList(); + # Stack> stack = new Stack>(); + # Stack indexes = new Stack(); + # stack.add(l); + # indexes.add(0); + # while (true) { + # if (stack.isEmpty()) + # break; + # int index1 = indexes.pop(); + # l = stack.pop(); + # for (int i = index1; i < l.size(); i++) { + # NestedList n = l.get(i); + # if (n.isInteger()) { + # main.add(n.value); + # } else { + # stack.add(l); + # indexes.add(i+1); + # l = n.list; + # stack.add(l); + # indexes.add(0); + # break; + + # } + # } + # } + + # return main; +# } diff --git a/array/summary_ranges.py b/array/summary_ranges.py new file mode 100644 index 000000000..3cbe5abf4 --- /dev/null +++ b/array/summary_ranges.py @@ -0,0 +1,27 @@ +""" +Given a sorted integer array without duplicates, +return the summary of its ranges. + +For example, given [0,1,2,4,5,7], return ["0->2","4->5","7"]. +""" + + +def summary_ranges(nums): + """ + :type nums: List[int] + :rtype: List[str] + """ + res = [] + if len(nums) == 1: + return [str(nums[0])] + i = 0 + while i < len(nums): + num = nums[i] + while i+1 < len(nums) and nums[i+1] - nums[i] == 1: + i += 1 + if nums[i] != num: + res.append(str(num) + "->" + str(nums[i])) + else: + res.append(str(num)) + i += 1 + return res diff --git a/backtrack/anagram.py b/backtrack/anagram.py index e69de29bb..35b596007 100644 --- a/backtrack/anagram.py +++ b/backtrack/anagram.py @@ -0,0 +1,44 @@ +def all_perms(elements): + if len(elements) <=1: + yield elements + else: + for perm in all_perms(elements[1:]): + for i in range(len(elements)): + yield perm[:i] + elements[0:1] + perm[i:] + +def all_perms(elements): + if len(elements) <=1: + return elements + else: + tmp = [] + for perm in all_perms(elements[1:]): + for i in range(len(elements)): + tmp.append(perm[:i] + elements[0:1] + perm[i:]) + return tmp + +word = "abc" +print list(all_perms(word)) + +def anagram(s1,s2): + c1 = [0]*26 + c2 = [0]*26 + + for i in range(len(s1)): + pos = ord(s1[i])-ord('a') + c1[pos] = c1[pos] + 1 + + for i in range(len(s2)): + pos = ord(s2[i])-ord('a') + c2[pos] = c2[pos] + 1 + + j = 0 + stillOK = True + while j<26 and stillOK: + if c1[j]==c2[j]: + j = j + 1 + else: + stillOK = False + + return stillOK + +print(anagram('apple','pleap')) diff --git a/backtrack/array_sum_combinations.py b/backtrack/array_sum_combinations.py new file mode 100644 index 000000000..b267083d7 --- /dev/null +++ b/backtrack/array_sum_combinations.py @@ -0,0 +1,86 @@ +""" +WAP to take one element from each of the array add it to the target sum. Print all those three-element combinations. + +/* +A = [1, 2, 3, 3] +B = [2, 3, 3, 4] +C = [1, 2, 2, 2] +target = 7 +*/ + +Result: +[[1, 2, 4], [1, 3, 3], [1, 3, 3], [1, 3, 3], [1, 3, 3], [1, 4, 2], [2, 2, 3], [2, 2, 3], [2, 3, 2], [2, 3, 2], [3, 2, 2], [3, 2, 2]] +""" + + +A = [1, 2, 3, 3] +B = [2, 3, 3, 4] +C = [1, 2, 2, 2] +target = 7 + +def construct_candidates(constructed_sofar): + global A,B,C + array = A + if 1 == len(constructed_sofar) : + array = B + elif 2 == len(constructed_sofar) : + array = C + return array + + +def over(constructed_sofar): + global target + sum = 0 + to_stop, reached_target = False, False + for elem in constructed_sofar: + sum += elem + if sum >= target or len(constructed_sofar) >= 3 : + to_stop = True + if sum == target and 3 == len(constructed_sofar): + reached_target = True + + return to_stop, reached_target + +def backtrack(constructed_sofar): + to_stop, reached_target = over(constructed_sofar) + if to_stop: + if reached_target : + print constructed_sofar + return + candidates = construct_candidates(constructed_sofar) + for candidate in candidates : + constructed_sofar.append(candidate) + backtrack(constructed_sofar[:]) + constructed_sofar.pop() +backtrack([]) + + +# Complexity: O(n(m+p)) + +# 1. Sort all the arrays - a,b,c. - This will improve average time complexity. +# 2. If c[i] < Sum, then look for Sum - c[i] in array a and b. When pair found, insert c[i], a[j] & b[k] into the result list. This can be done in O(n). +# 3. Keep on doing the above procedure while going through complete c array. + + +import itertools +from functools import partial +A = [1,2,3,3] +B = [2,3,3,4] +C = [1,2,2,2] +S = 7 + +def check_sum(N, *nums): + if sum(x for x in nums) == N: + return (True, nums) + else: + return (False, nums) + +pro = itertools.product(A,B,C) +func = partial(check_sum, S) +sums = list(itertools.starmap(func, pro)) + +res = set() +for s in sums: + if s[0] == True and s[1] not in res: + res.add(s[1]) +print res diff --git a/backtrack/combination_sum.py b/backtrack/combination_sum.py index e69de29bb..3ac87b6e9 100644 --- a/backtrack/combination_sum.py +++ b/backtrack/combination_sum.py @@ -0,0 +1,31 @@ +""" +Given a set of candidate numbers (C) (without duplicates) and a target number +(T), find all unique combinations in C where the candidate numbers sums to T. + +The same repeated number may be chosen from C unlimited number of times. + +Note: +All numbers (including target) will be positive integers. +The solution set must not contain duplicate combinations. +For example, given candidate set [2, 3, 6, 7] and target 7, +A solution set is: +[ + [7], + [2, 2, 3] +] +""" + +def combinationSum(self, candidates, target): + res = [] + candidates.sort() + self.dfs(candidates, target, 0, [], res) + return res + +def dfs(self, nums, target, index, path, res): + if target < 0: + return # backtracking + if target == 0: + res.append(path) + return + for i in range(index, len(nums)): + self.dfs(nums, target-nums[i], i, path+[nums[i]], res) diff --git a/backtrack/factor_combinations.py b/backtrack/factor_combinations.py new file mode 100644 index 000000000..787c1cf25 --- /dev/null +++ b/backtrack/factor_combinations.py @@ -0,0 +1,60 @@ +""" +Numbers can be regarded as product of its factors. For example, + +8 = 2 x 2 x 2; + = 2 x 4. +Write a function that takes an integer n and return all possible combinations of its factors. + +Note: +You may assume that n is always positive. +Factors should be greater than 1 and less than n. +Examples: +input: 1 +output: +[] +input: 37 +output: +[] +input: 12 +output: +[ + [2, 6], + [2, 2, 3], + [3, 4] +] +input: 32 +output: +[ + [2, 16], + [2, 2, 8], + [2, 2, 2, 4], + [2, 2, 2, 2, 2], + [2, 4, 4], + [4, 8] +] +""" + +# Iterative: + +def getFactors(self, n): + todo, combis = [(n, 2, [])], [] + while todo: + n, i, combi = todo.pop() + while i * i <= n: + if n % i == 0: + combis += combi + [i, n/i], + todo += (n/i, i, combi+[i]), + i += 1 + return combis + +# Recursive: + +def getFactors(self, n): + def factor(n, i, combi, combis): + while i * i <= n: + if n % i == 0: + combis += combi + [i, n/i], + factor(n/i, i, combi+[i], combis) + i += 1 + return combis + return factor(n, 2, [], []) diff --git a/backtrack/general_solution.md b/backtrack/general_solution.md new file mode 100644 index 000000000..0c0ee0c6f --- /dev/null +++ b/backtrack/general_solution.md @@ -0,0 +1,156 @@ +This structure might apply to many other backtracking questions, but here I am just going to demonstrate Subsets, Permutations, and Combination Sum. + +# Subsets : https://leetcode.com/problems/subsets/ + +public List> subsets(int[] nums) { + List> list = new ArrayList<>(); + Arrays.sort(nums); + backtrack(list, new ArrayList<>(), nums, 0); + return list; +} + +private void backtrack(List> list , List tempList, int [] nums, int start){ + list.add(new ArrayList<>(tempList)); + for(int i = start; i < nums.length; i++){ + tempList.add(nums[i]); + backtrack(list, tempList, nums, i + 1); + tempList.remove(tempList.size() - 1); + } +} + +# Subsets II (contains duplicates) : https://leetcode.com/problems/subsets-ii/ + +public List> subsetsWithDup(int[] nums) { + List> list = new ArrayList<>(); + Arrays.sort(nums); + backtrack(list, new ArrayList<>(), nums, 0); + return list; +} + +private void backtrack(List> list, List tempList, int [] nums, int start){ + list.add(new ArrayList<>(tempList)); + for(int i = start; i < nums.length; i++){ + if(i > start && nums[i] == nums[i-1]) continue; // skip duplicates + tempList.add(nums[i]); + backtrack(list, tempList, nums, i + 1); + tempList.remove(tempList.size() - 1); + } +} + +# Permutations : https://leetcode.com/problems/permutations/ + +public List> permute(int[] nums) { + List> list = new ArrayList<>(); + // Arrays.sort(nums); // not necessary + backtrack(list, new ArrayList<>(), nums); + return list; +} + +private void backtrack(List> list, List tempList, int [] nums){ + if(tempList.size() == nums.length){ + list.add(new ArrayList<>(tempList)); + } else{ + for(int i = 0; i < nums.length; i++){ + if(tempList.contains(nums[i])) continue; // element already exists, skip + tempList.add(nums[i]); + backtrack(list, tempList, nums); + tempList.remove(tempList.size() - 1); + } + } +} + +# Permutations II (contains duplicates) : https://leetcode.com/problems/permutations-ii/ + +public List> permuteUnique(int[] nums) { + List> list = new ArrayList<>(); + Arrays.sort(nums); + backtrack(list, new ArrayList<>(), nums, new boolean[nums.length]); + return list; +} + +private void backtrack(List> list, List tempList, int [] nums, boolean [] used){ + if(tempList.size() == nums.length){ + list.add(new ArrayList<>(tempList)); + } else{ + for(int i = 0; i < nums.length; i++){ + if(used[i] || i > 0 && nums[i] == nums[i-1] && !used[i - 1]) continue; + used[i] = true; + tempList.add(nums[i]); + backtrack(list, tempList, nums, used); + used[i] = false; + tempList.remove(tempList.size() - 1); + } + } +} + +# Combination Sum : https://leetcode.com/problems/combination-sum/ + +public List> combinationSum(int[] nums, int target) { + List> list = new ArrayList<>(); + Arrays.sort(nums); + backtrack(list, new ArrayList<>(), nums, target, 0); + return list; +} + +private void backtrack(List> list, List tempList, int [] nums, int remain, int start){ + if(remain < 0) return; + else if(remain == 0) list.add(new ArrayList<>(tempList)); + else{ + for(int i = start; i < nums.length; i++){ + tempList.add(nums[i]); + backtrack(list, tempList, nums, remain - nums[i], i); // not i + 1 because we can reuse same elements + tempList.remove(tempList.size() - 1); + } + } +} +# Combination Sum II (can't reuse same element) : https://leetcode.com/problems/combination-sum-ii/ + +public List> combinationSum2(int[] nums, int target) { + List> list = new ArrayList<>(); + Arrays.sort(nums); + backtrack(list, new ArrayList<>(), nums, target, 0); + return list; + +} + +private void backtrack(List> list, List tempList, int [] nums, int remain, int start){ + if(remain < 0) return; + else if(remain == 0) list.add(new ArrayList<>(tempList)); + else{ + for(int i = start; i < nums.length; i++){ + if(i > start && nums[i] == nums[i-1]) continue; // skip duplicates + tempList.add(nums[i]); + backtrack(list, tempList, nums, remain - nums[i], i + 1); + tempList.remove(tempList.size() - 1); + } + } +} + + +# Palindrome Partitioning : https://leetcode.com/problems/palindrome-partitioning/ + +public List> partition(String s) { + List> list = new ArrayList<>(); + backtrack(list, new ArrayList<>(), s, 0); + return list; +} + +public void backtrack(List> list, List tempList, String s, int start){ + if(start == s.length()) + list.add(new ArrayList<>(tempList)); + else{ + for(int i = start; i < s.length(); i++){ + if(isPalindrome(s, start, i)){ + tempList.add(s.substring(start, i + 1)); + backtrack(list, tempList, s, i + 1); + tempList.remove(tempList.size() - 1); + } + } + } +} + +public boolean isPalindrome(String s, int low, int high){ + while(low < high) + if(s.charAt(low++) != s.charAt(high--)) return false; + return true; +} diff --git a/backtrack/generate_abbreviations.py b/backtrack/generate_abbreviations.py new file mode 100644 index 000000000..929c33994 --- /dev/null +++ b/backtrack/generate_abbreviations.py @@ -0,0 +1,26 @@ +""" +given input word, return the list of abbreviations. +ex) +word => [1ord, w1rd, wo1d, w2d, 3d, w3 ... etc] +""" + + +def generate_abbreviations(word): + result = [] + backtrack(result, word, 0, 0, "") + return result + + +def backtrack(result, word, pos, count, cur): + if pos == len(word): + if count > 0: + cur += str(count) + result.append(cur) + return + + if count > 0: # add the current word + backtrack(result, word, pos+1, 0, cur+str(count)+word[pos]) + else: + backtrack(result, word, pos+1, 0, cur+word[pos]) + # skip the current word + backtrack(result, word, pos+1, count+1, cur) diff --git a/backtrack/palindrome_partitioning.py b/backtrack/palindrome_partitioning.py index e69de29bb..654079010 100644 --- a/backtrack/palindrome_partitioning.py +++ b/backtrack/palindrome_partitioning.py @@ -0,0 +1,29 @@ +# It looks like you need to be looking not for all palindromic substrings, but rather for all the ways you can divide the input string up into palindromic substrings. (There's always at least one way, since one-character substrings are always palindromes.) + +# Here's the way I've done it: + +def palindromic_substrings(s): + if not s: + return [[]] + results = [] + for i in range(len(s), 0, -1): + sub = s[:i] + if sub == sub[::-1]: + for rest in palindromic_substrings(s[i:]): + results.append([sub] + rest) + return results + +# There's two loops. +# The outer loop checks each length of initial substring (in descending length order) to see if it is a palindrome. If so, it recurses on the rest of the string and loops over the returned values, adding the initial substring to each item before adding it to the results. + +# A slightly more Pythonic approach would be to make a recursive generator: + +def palindromic_substrings(s): + if not s: + yield [] + return + for i in range(len(s), 0, -1): + sub = s[:i] + if sub == sub[::-1]: + for rest in palindromic_substrings(s[i:]): + yield [sub] + rest diff --git a/backtrack/pattern_match.py b/backtrack/pattern_match.py index 11d90c0f2..532dfb9c2 100644 --- a/backtrack/pattern_match.py +++ b/backtrack/pattern_match.py @@ -20,10 +20,10 @@ def pattern_match(pattern, string): :type string: str :rtype: bool """ - return DFS(pattern, string, {}) + return backtrack(pattern, string, {}) -def DFS(pattern, string, dic): +def backtrack(pattern, string, dic): print(dic) if len(pattern) == 0 and len(string) > 0: return False @@ -32,11 +32,11 @@ def DFS(pattern, string, dic): for end in range(1, len(string)-len(pattern)+2): if pattern[0] not in dic and string[:end] not in dic.values(): dic[pattern[0]] = string[:end] - if DFS(pattern[1:], string[end:], dic): + if backtrack(pattern[1:], string[end:], dic): return True del dic[pattern[0]] elif pattern[0] in dic and dic[pattern[0]] == string[:end]: - if DFS(pattern[1:], string[end:], dic): + if backtrack(pattern[1:], string[end:], dic): return True return False diff --git a/bit/power_of_two.py b/bit/power_of_two.py new file mode 100644 index 000000000..bdb629471 --- /dev/null +++ b/bit/power_of_two.py @@ -0,0 +1,11 @@ +""" +given an integer, write a function to determine if it is a power of two +""" + + +def is_power_of_two(n): + """ + :type n: int + :rtype: bool + """ + return n > 0 and not n & (n-1) diff --git a/design/LRUcache.md b/design/LRUcache.md new file mode 100644 index 000000000..b1fd61351 --- /dev/null +++ b/design/LRUcache.md @@ -0,0 +1,127 @@ +Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and put. + +get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1. +put(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item. + +Follow up: +Could you do both operations in O(1) time complexity? + +Example: +``` +LRUCache cache = new LRUCache( 2 /* capacity */ ); + +cache.put(1, 1); +cache.put(2, 2); +cache.get(1); // returns 1 +cache.put(3, 3); // evicts key 2 +cache.get(2); // returns -1 (not found) +cache.put(4, 4); // evicts key 1 +cache.get(1); // returns -1 (not found) +cache.get(3); // returns 3 +cache.get(4); // returns 4 +``` + +```python +class Node: + def __init__(self, k, v): + self.key = k + self.val = v + self.prev = None + self.next = None + +class LRUCache: + def __init__(self, capacity): + self.capacity = capacity + self.dic = dict() + self.head = Node(0, 0) + self.tail = Node(0, 0) + self.head.next = self.tail + self.tail.prev = self.head + + def get(self, key): + if key in self.dic: + n = self.dic[key] + self._remove(n) + self._add(n) + return n.val + return -1 + + def set(self, key, value): + if key in self.dic: + self._remove(self.dic[key]) + n = Node(key, value) + self._add(n) + self.dic[key] = n + if len(self.dic) > self.capacity: + n = self.head.next + self._remove(n) + del self.dic[n.key] + + def _remove(self, node): + p = node.prev + n = node.next + p.next = n + n.prev = p + + def _add(self, node): + p = self.tail.prev + p.next = node + self.tail.prev = node + node.prev = p + node.next = self.tail +``` + +Little Easier to read with comments + +```python +class LRUCache(object): + + def __init__(self, capacity): + self.capacity = capacity + self.head = LinkedNode(None,'head') + self.tail = LinkedNode(None,'tail') + self.head.next = self.tail # tail being most recent + self.tail.prev = self.head # head being oldest + self.data = {} + + def deleteNode(self,node): + assert(node is not self.head and node is not self.tail) + del self.data[node.key] + node.prev.next = node.next + node.next.prev = node.prev + del node + + def get(self,key): + if key not in self.data: + return -1 + node = self.data[key] + # take the node out + node.prev.next = node.next + node.next.prev = node.prev + # insert into most recent position + self.insertNew(node) + return node.value + + def put(self, key, value): + # remove old value if present + if key in self.data: + self.deleteNode(self.data[key]) + + # create new node + newNode = LinkedNode(key,value) + self.data[key] = newNode + + # if over limit, delete oldest node + if len(self.data)>self.capacity: + self.deleteNode(self.head.next) + + self.insertNew(newNode) + + def insertNew(self,newNode): + # insert new node into last position + last = self.tail.prev + last.next = newNode + self.tail.prev = newNode + newNode.next = self.tail + newNode.prev = last +``` diff --git a/design/alarm_system.md b/design/alarm_system.md new file mode 100644 index 000000000..8c3fdaab7 --- /dev/null +++ b/design/alarm_system.md @@ -0,0 +1 @@ +Design an alarm system for a driverless car diff --git a/design/all_o_one_ds.md b/design/all_o_one_ds.md new file mode 100644 index 000000000..46ba1ba3b --- /dev/null +++ b/design/all_o_one_ds.md @@ -0,0 +1,206 @@ +# Q +Implement a data structure supporting the following operations: + +Inc(Key) - Inserts a new key with value 1. Or increments an existing key by 1. Key is guaranteed to be a non-empty string. +Dec(Key) - If Key's value is 1, remove it from the data structure. Otherwise decrements an existing key by 1. If the key does not exist, this function does nothing. Key is guaranteed to be a non-empty string. +GetMaxKey() - Returns one of the keys with maximal value. If no element exists, return an empty string "". +GetMinKey() - Returns one of the keys with minimal value. If no element exists, return an empty string "". +Challenge: Perform all these in O(1) time complexity. + +# A +All Oone Data Structure https://leetcode.com/problems/all-oone-data-structure/ + +Data-Structures +https://goo.gl/photos/YLhF2qCcBwRpAn58A + +Node: A node type to support a doubly linked list. It is a container to hold a bag of keys. It supports: + +add_key(key): Add a key to the bag +remove_key(key): Remove a key from the bag +get_any_key(): Returns any random key from the bag. Returns None if bag is empty. +is_empty(): Returns true if the bag is empty +DoubleLinkedList + +The linked list is implemented using the idea of sentinel nodes, +i.e. we have two dummy nodes to represent head and tail. +Initially head.next points to tail and tail.prev points to head. +Using two dummy nodes dramatically simplifies the implementation. + +insert_after(x): Add a node after node x +insert_before(x): Add a node before node x +remove(x): Remove the node from the list +get_head(): Returns the reference to the real head node +get_tail() Returns the reference to the real tail node +node_freq: Hashmap with key as frequency and value as Node. +key_counter: Hashmap with key as input key and value as frequency of the key. + +Algorithm Idea + +A node in the doubly linked list represents a bucket containing a bag of words +with a certain frequency. The doubly linked list is maintained in a sorted order +with the head node containing words with the least frequency and the tail node +ontaining words with maximum frequency. +Using this list, getMaxKey and getMinKey can be implemented in O(1) by returning +any word contained in the tail and head respectively. +key_counter is hashmap which allows us to increment or decrement frequency of a +key in O(1). +node_freq is a hashmap which maps a frequency integer to the bucket node in the +linked list. Note that we initialize frequency 0 to head sentinel node. +Now, if we can maintain the sorted order of the linked list in O(1) while +performing the increment and decrement operations, we would have a working +solution! + +Increment Details + +While incrementing a key, we first update the key_counter ro reflect the new +frequency (cf) of the key. Then we test if there is already a bucket with cf +using node_freq hashmap. If not, then we need to add a bucket to the linkedlist. +To maintain the sorted invariant, this new bucket must be after the bucket for +frequency pf (cf-1). +Now unless pf is 0, we are guaranteed that a pf bucket already exists. +Therefore, either we add the new bucket after the head node or after the pf +bucket. Note that we initialize frequency 0 to head sentinel node. This allows +us to use "insert_after" API when previous frequency were zero. +pf bucket can be retrieved in O(1) using node_freq. insertion in doubly linked +list can be done in O(1) as well. +Once we have inserted, we add the key to the new bucket. +Finally we need to remove the key from the previous bucket if pf > 0 +(i.e. if a previous bucket exists). Again this can be done in O(1). +If the previous bucket becomes empty after removing the key, +then we need to also drop the entire bucket from the list. + +Decrement Details + +While decrementing a key, we first check if the key exisits in key_counter or +not. If not, then we simply return. if it does exist, we update the key_counter +to reflect the new frequency (cf) of the key. +If cf is 0, then we drop this key from the key counter. +If cf is not in node_freq and cf is not 0, then we need to add a new bucket in +the linked list such that the sorted invariant is maintained. +Again we are guaranteed to have pf bucket! +We add the key to the new bucket and remove it from +the previous bucket - O(1) operations. + +```python +from collections import defaultdict + +class Node(object): + def __init__(self): + self.key_set = set([]) + self.prev, self.nxt = None, None + + def add_key(self, key): + self.key_set.add(key) + + def remove_key(self, key): + self.key_set.remove(key) + + def get_any_key(self): + if self.key_set: + result = self.key_set.pop() + self.add_key(result) + return result + else: + return None + + def count(self): + return len(self.key_set) + + def is_empty(self): + return len(self.key_set) == 0 + + +class DoubleLinkedList(object): + def __init__(self): + self.head_node, self.tail_node = Node(), Node() + self.head_node.nxt, self.tail_node.prev = self.tail_node, self.head_node + return + + def insert_after(self, x): + node, temp = Node(), x.nxt + x.nxt, node.prev = node, x + node.nxt, temp.prev = temp, node + return node + + def insert_before(self, x): + return self.insert_after(x.prev) + + def remove(self, x): + prev_node = x.prev + prev_node.nxt, x.nxt.prev = x.nxt, prev_node + return + + def get_head(self): + return self.head_node.nxt + + def get_tail(self): + return self.tail_node.prev + + def get_sentinel_head(self): + return self.head_node + + def get_sentinel_tail(self): + return self.tail_node + +class AllOne(object): + def __init__(self): + """ + Initialize your data structure here. + """ + self.dll, self.key_counter = DoubleLinkedList(), defaultdict(int) + self.node_freq = {0:self.dll.get_sentinel_head()} + + def _rmv_key_pf_node(self, pf, key): + node = self.node_freq[pf] + node.remove_key(key) + if node.is_empty(): + self.dll.remove(node) + self.node_freq.pop(pf) + return + + def inc(self, key): + """ + Inserts a new key with value 1. Or increments an existing key by 1. + :type key: str + :rtype: void + """ + self.key_counter[key] += 1 + cf, pf = self.key_counter[key], self.key_counter[key]-1 + if cf not in self.node_freq: + # No need to test if pf = 0 since frequency zero points to sentinel node + self.node_freq[cf] = self.dll.insert_after(self.node_freq[pf]) + self.node_freq[cf].add_key(key) + if pf > 0: + self._rmv_key_pf_node(pf, key) + + def dec(self, key): + """ + Decrements an existing key by 1. If Key's value is 1, remove it from the data structure. + :type key: str + :rtype: void + """ + if key in self.key_counter: + self.key_counter[key] -= 1 + cf, pf = self.key_counter[key], self.key_counter[key]+1 + if self.key_counter[key] == 0: + self.key_counter.pop(key) + if cf != 0: + if cf not in self.node_freq: + self.node_freq[cf] = self.dll.insert_before(self.node_freq[pf]) + self.node_freq[cf].add_key(key) + self._rmv_key_pf_node(pf, key) + + def getMaxKey(self): + """ + Returns one of the keys with maximal value. + :rtype: str + """ + return self.dll.get_tail().get_any_key() if self.dll.get_tail().count() > 0 else "" + + def getMinKey(self): + """ + Returns one of the keys with Minimal value. + :rtype: str + """ + return self.dll.get_head().get_any_key() if self.dll.get_tail().count() > 0 else "" +``` diff --git a/design/calculator.md b/design/calculator.md new file mode 100644 index 000000000..463d2dbaf --- /dev/null +++ b/design/calculator.md @@ -0,0 +1,68 @@ +## Q +Implement a basic calculator, supporting operators such as +, -, *, / and operands like ( and ). + +For example: +`"(1+2) *10 -25/(1-7)" -> 34` + +## A +I think that maybe there is an error in the example above. The expression should be : (1+2)*10-24/(1-7) -> 34 +Here is some implementation, convert infix notation in reverse pollish notation and calculate it +``` +boolean isDigit(char ch) { + return ch >= '0' && ch <= '9'; + } + int calc(int op2, int op1, char ch) { + switch(ch) { + case '-': return op1 - op2; + case '+': return op1 + op2; + case '/': return op1 / op2; + case '*': return op1 * op2; + } + return 0; + } + boolean higherPriority(char op1, char op2) { + if ((op1 =='*') || (op1 =='/')) + return true; + if ((op2 =='+') || (op2 =='-')) + return true; + return false; + } + int simpleCalculator(String exp) { + Stack st = new Stack<>(); + Stack op = new Stack<>(); + int digit = 0; + boolean hasDigit = false; + for (int i = 0; i < exp.length(); i++) { + if (isDigit(exp.charAt(i))) { + hasDigit = true; + digit = digit*10 + (exp.charAt(i) - '0'); + } else { + if(hasDigit) { + hasDigit = false; + st.push(digit); + digit = 0; + } + if (exp.charAt(i) == '(') { + op.push('('); + } else if(exp.charAt(i) == ')') { + while (op.peek() != '(') { + st.push(calc(st.pop(), st.pop(), op.pop())); + } + op.pop(); + } else { + while (!op.isEmpty() && op.peek() != '(' && higherPriority(op.peek(), exp.charAt(i))) { + st.push(calc(st.pop(), st.pop(), op.pop())); + } + op.push(exp.charAt(i)); + } + } + } + if(hasDigit) + st.push(digit); + while(!op.isEmpty()) { + st.push(calc(st.pop(), st.pop(), op.pop())); + } + return st.peek(); + } +} +``` diff --git a/design/excel_table.md b/design/excel_table.md new file mode 100644 index 000000000..66824f781 --- /dev/null +++ b/design/excel_table.md @@ -0,0 +1,29 @@ +## Q + +Need to implement set and get operations. +Be aware that one cell may depend on other cells, +e.g. cells[1, 2] = cells[1, 1] + cell[1, 3] * 2 + +## A + +Could you give more details in the description and an example? + +For example, it is not clear how the inputs are given. +How is the spreadsheet represented? A 2d array of cells? Each cell contains a string? + +Input: +``` +[ + ["100", "A2*3"], + ["B2+A1", "200"] +] + +``` +Output: +``` +[ + ["100", "900"], + ["300", "200"] +] +``` +Is it like the above? And what about circular reference? What should the output be? diff --git a/design/nearby_drivers.md b/design/nearby_drivers.md new file mode 100644 index 000000000..901322e80 --- /dev/null +++ b/design/nearby_drivers.md @@ -0,0 +1 @@ +Design the backend architecture to show nearby drivers diff --git a/design/ride_sharing.md b/design/ride_sharing.md new file mode 100644 index 000000000..c3844f363 --- /dev/null +++ b/design/ride_sharing.md @@ -0,0 +1,23 @@ +Consider that the driver with one trip want to pick up some peoples in different locations like this: +String[] locations ={ +"person1, person2, person3, person4, person5", +" person6, person7, person8, person9", +"person10, person11, person12", +"person13, person14, person15",} +in each location there are different choice, so write a code present all possible way to pick up people in the different locations. +you can use every data structure needs. + + +This could be solved using minimum spanning tree concept + +***assume taxi is large enough for all passengers**** + +Arrange all the pickup locations as vertices of a graph along with the present location of the taxi as + +one of the vertex + +now start with the present location and add that edge with has lowest weight ,this means we have + +visited to the location which is nearby and pic all the passangers, then search for the next nearby location + +locations and so on until all the locations are visited once diff --git a/design/task_runner.md b/design/task_runner.md new file mode 100644 index 000000000..b6aa3b39d --- /dev/null +++ b/design/task_runner.md @@ -0,0 +1,52 @@ +## Q +Implement Task Runner. + +TaskRunner takes concurrency as it's input. +'concurrency' is the number of the tasks that the TaskRunner +can simultaneously execute. Keep pushing the tasks until the +concurrency is reached. Once the limit is reached, +wait for one of the tasks to be completed and then, execute other tasks. + +## A + +``` +'use strict'; +function exampleTask(done) { +setTimeout(done, 2000); +} + +class Runner { +constructor(num){ +this.maxNum = num; +this.counter = 0; +this.queue = []; +} + +push(callbackFn){ + this.queue.push(callbackFn); +} + +run(){ + var self = this; + if(this.queue.length > 0 && this.counter < this.maxNum){ + setTimeout(() => { + this.counter++; + let task = this.queue.shift(); + var done = function(){ + self.counter--; + console.log(`number at this moment:${self.counter}`) + self.run(); + } + task.call(this,done); + },0); + } +} +} + +var r = new Runner(3); +r.push(exampleTask) // run +r.push(exampleTask) // run +r.push(exampleTask) // run +r.push(exampleTask) // wait +r.run(); +``` diff --git a/design/twitter_feeds.md b/design/twitter_feeds.md new file mode 100644 index 000000000..f750e16b5 --- /dev/null +++ b/design/twitter_feeds.md @@ -0,0 +1,32 @@ +Design a Twitter feeds API. +How would you actually connect it from a mobile? +What happens behind the Twitter network? +how do the Trends get published? +From where does Twitter get the information for a particular trend(Eg: #Obama, #nfl) +and publish it out? +What protocol does it use? +How do you connect to Twitter API? +How does Twitter handle multiple connections? + + + +Twitter feeds API for 1 user: +The user has M friends and each friend has K new updates. +One possible way is to iterate over all friends and get all the new updates +and display that in feed. O(MK). +But iterating over all the friends is a costly operation, +so i should be getting the feeds from those people whose pics and feeds +i like or comment on or share. We have reduced the iteration on number of people. +We can also use collaborative filtering to decide - what kind of feeds i +would be interested in. Instead of doing all the computations on the fly, +some things can be done offline - for example, +store latest feeds from my friends after I was last active and when i come online, +show those in chronological order. + +Connect to Twitter API: Whenever a person gives her credentials +that means one connection is awarded to her. +Now if multiple connections are opened for the same user then according to +'last used' parameter certain connections can be closed. +Also if the user exceeds the limit of number of connections allowed then old connections should be closed. + +Twitter uses streaming API. I am not sure about twitter trends though. Can someone elaborate on that? diff --git a/dfs/count_islands.py b/dfs/count_islands.py new file mode 100644 index 000000000..5c4711526 --- /dev/null +++ b/dfs/count_islands.py @@ -0,0 +1,44 @@ +""" +Given a 2d grid map of '1's (land) and '0's (water), +count the number of islands. +An island is surrounded by water and is formed by +connecting adjacent lands horizontally or vertically. +You may assume all four edges of the grid are all surrounded by water. + +Example 1: + +11110 +11010 +11000 +00000 +Answer: 1 + +Example 2: + +11000 +11000 +00100 +00011 +Answer: 3 +""" + +def num_islands(grid): + count = 0 + for i, row in enumerate(grid): + for j, col in enumerate(grid[i]): + if col == '1': + DFS(grid, i, j) + count += 1 + return count + + +def DFS(grid, i, j): + if (i < 0 or i >= len(grid)) or (j < 0 or len(grid[0])): + return + if grid[i][j] != '1': + return + grid[i][j] = '0' + DFS(grid, i+1, j) + DFS(grid, i-1, j) + DFS(grid, i, j+1) + DFS(grid, i, j-1) diff --git a/matrix/pacific_atlantic.py b/dfs/pacific_atlantic.py similarity index 100% rename from matrix/pacific_atlantic.py rename to dfs/pacific_atlantic.py diff --git a/dfs/sudoku_solver.py b/dfs/sudoku_solver.py new file mode 100644 index 000000000..a6c1a3208 --- /dev/null +++ b/dfs/sudoku_solver.py @@ -0,0 +1,79 @@ +""" +It's similar to how human solve Sudoku. + +create a hash table (dictionary) val to store possible values in every location. +Each time, start from the location with fewest possible values, choose one value +from it and then update the board and possible values at other locations. +If this update is valid, keep solving (DFS). If this update is invalid (leaving +zero possible values at some locations) or this value doesn't lead to the +solution, undo the updates and then choose the next value. +Since we calculated val at the beginning and start filling the board from the +location with fewest possible values, the amount of calculation and thus the +runtime can be significantly reduced: + + +The run time is 48-68 ms on LeetCode OJ, which seems to be among the fastest +python solutions here. + + +The PossibleVals function may be further simplified/optimized, but it works just +fine for now. (it would look less lengthy if we are allowed to use numpy array +for the board lol). +""" + +def solveSudoku(self, board): + self.board = board + self.val = self.PossibleVals() + self.Solver() + +def PossibleVals(self): + a = "123456789" + d, val = {}, {} + for i in xrange(9): + for j in xrange(9): + ele = self.board[i][j] + if ele != ".": + d[("r", i)] = d.get(("r", i), []) + [ele] + d[("c", j)] = d.get(("c", j), []) + [ele] + d[(i//3, j//3)] = d.get((i//3, j//3), []) + [ele] + else: + val[(i,j)] = [] + for (i,j) in val.keys(): + inval = d.get(("r",i),[])+d.get(("c",j),[])+d.get((i/3,j/3),[]) + val[(i,j)] = [n for n in a if n not in inval ] + return val + +def Solver(self): + if len(self.val)==0: + return True + kee = min(self.val.keys(), key=lambda x: len(self.val[x])) + nums = self.val[kee] + for n in nums: + update = {kee:self.val[kee]} + if self.ValidOne(n, kee, update): # valid choice + if self.Solver(): # keep solving + return True + self.undo(kee, update) # invalid choice or didn't solve it => undo + return False + +def ValidOne(self, n, kee, update): + self.board[kee[0]][kee[1]] = n + del self.val[kee] + i, j = kee + for ind in self.val.keys(): + if n in self.val[ind]: + if ind[0]==i or ind[1]==j or (ind[0]/3,ind[1]/3)==(i/3,j/3): + update[ind] = n + self.val[ind].remove(n) + if len(self.val[ind])==0: + return False + return True + +def undo(self, kee, update): + self.board[kee[0]][kee[1]]="." + for k in update: + if k not in self.val: + self.val[k]= update[k] + else: + self.val[k].append(update[k]) + return None diff --git a/dfs/walls_and_gates.py b/dfs/walls_and_gates.py new file mode 100644 index 000000000..252cb7a1e --- /dev/null +++ b/dfs/walls_and_gates.py @@ -0,0 +1,22 @@ + + +# fill the empty room with distance to its nearest gate + + +def walls_and_gates(rooms): + for i in range(len(rooms)): + for j in range(len(rooms[0])): + if rooms[i][j] == 0: + DFS(rooms, i, j, 0) + + +def DFS(rooms, i, j, depth): + if (i < 0 or i >= len(rooms)) or (j < 0 or j >= len(rooms[0])): + return # out of bounds + if rooms[i][j] < depth: + return # crossed + rooms[i][j] = depth + DFS(rooms, i+1, j, depth+1) + DFS(rooms, i-1, j, depth+1) + DFS(rooms, i, j+1, depth+1) + DFS(rooms, i, j-1, depth+1) diff --git a/dp/regex_matching.py b/dp/regex_matching.py new file mode 100644 index 000000000..19ce37a2c --- /dev/null +++ b/dp/regex_matching.py @@ -0,0 +1,107 @@ +""" +Implement regular expression matching with support for '.' and '*'. + +'.' Matches any single character. +'*' Matches zero or more of the preceding element. + +The matching should cover the entire input string (not partial). + +The function prototype should be: +bool isMatch(const char *s, const char *p) + +Some examples: +isMatch("aa","a") → false +isMatch("aa","aa") → true +isMatch("aaa","aa") → false +isMatch("aa", "a*") → true +isMatch("aa", ".*") → true +isMatch("ab", ".*") → true +isMatch("aab", "c*a*b") → true +""" + +class Solution(object): + def isMatch(self, s, p): + m, n = len(s) + 1, len(p) + 1 + matches = [[False] * n for _ in range(m)] + + # Match empty string with empty pattern + matches[0][0] = True + + # Match empty string with .* + for i, element in enumerate(p[1:], 2): + matches[0][i] = matches[0][i - 2] and element == '*' + + for i, ss in enumerate(s, 1): + for j, pp in enumerate(p, 1): + if pp != '*': + # The previous character has matched and the current one + # has to be matched. Two possible matches: the same or . + matches[i][j] = matches[i - 1][j - 1] and \ + (ss == pp or pp == '.') + else: + # Horizontal look up [j - 2]. + # Not use the character before *. + matches[i][j] |= matches[i][j - 2] + + # Vertical look up [i - 1]. + # Use at least one character before *. + # p a b * + # s 1 0 0 0 + # a 0 1 0 1 + # b 0 0 1 1 + # b 0 0 0 ? + if ss == p[j - 2] or p[j - 2] == '.': + matches[i][j] |= matches[i - 1][j] + + return matches[-1][-1] + +class TestSolution(unittest.TestCase): + def test_none_0(self): + s = "" + p = "" + self.assertTrue(Solution().isMatch(s, p)) + + def test_none_1(self): + s = "" + p = "a" + self.assertFalse(Solution().isMatch(s, p)) + + def test_no_symbol_equal(self): + s = "abcd" + p = "abcd" + self.assertTrue(Solution().isMatch(s, p)) + + def test_no_symbol_not_equal_0(self): + s = "abcd" + p = "efgh" + self.assertFalse(Solution().isMatch(s, p)) + + def test_no_symbol_not_equal_1(self): + s = "ab" + p = "abb" + self.assertFalse(Solution().isMatch(s, p)) + + def test_symbol_0(self): + s = "" + p = "a*" + self.assertTrue(Solution().isMatch(s, p)) + + def test_symbol_1(self): + s = "a" + p = "ab*" + self.assertTrue(Solution().isMatch(s, p)) + + def test_symbol_2(self): + # E.g. + # s a b b + # p 1 0 0 0 + # a 0 1 0 0 + # b 0 0 1 0 + # * 0 1 1 1 + s = "abb" + p = "ab*" + self.assertTrue(Solution().isMatch(s, p)) + + +if __name__ == "__main__": + unittest.main() diff --git a/graph/clone_graph.py b/graph/clone_graph.py new file mode 100644 index 000000000..d2af1def6 --- /dev/null +++ b/graph/clone_graph.py @@ -0,0 +1,93 @@ +""" +Clone an undirected graph. Each node in the graph contains a label and a list +of its neighbors. + + +OJ's undirected graph serialization: +Nodes are labeled uniquely. + +We use # as a separator for each node, and , as a separator for node label and +each neighbor of the node. +As an example, consider the serialized graph {0,1,2#1,2#2,2}. + +The graph has a total of three nodes, and therefore contains three parts as +separated by #. + +First node is labeled as 0. Connect node 0 to both nodes 1 and 2. +Second node is labeled as 1. Connect node 1 to node 2. +Third node is labeled as 2. Connect node 2 to node 2 (itself), thus forming a +self-cycle. +Visually, the graph looks like the following: + + 1 + / \ + / \ + 0 --- 2 + / \ + \_/ +""" + + +# Definition for a undirected graph node +# class UndirectedGraphNode: +# def __init__(self, x): +# self.label = x +# self.neighbors = [] + + +# BFS +def cloneGraph1(self, node): + if not node: + return + nodeCopy = UndirectedGraphNode(node.label) + dic = {node: nodeCopy} + queue = collections.deque([node]) + while queue: + node = queue.popleft() + for neighbor in node.neighbors: + if neighbor not in dic: # neighbor is not visited + neighborCopy = UndirectedGraphNode(neighbor.label) + dic[neighbor] = neighborCopy + dic[node].neighbors.append(neighborCopy) + queue.append(neighbor) + else: + dic[node].neighbors.append(dic[neighbor]) + return nodeCopy + +# DFS iteratively +def cloneGraph2(self, node): + if not node: + return + nodeCopy = UndirectedGraphNode(node.label) + dic = {node: nodeCopy} + stack = [node] + while stack: + node = stack.pop() + for neighbor in node.neighbors: + if neighbor not in dic: + neighborCopy = UndirectedGraphNode(neighbor.label) + dic[neighbor] = neighborCopy + dic[node].neighbors.append(neighborCopy) + stack.append(neighbor) + else: + dic[node].neighbors.append(dic[neighbor]) + return nodeCopy + +# DFS recursively +def cloneGraph(self, node): + if not node: + return + nodeCopy = UndirectedGraphNode(node.label) + dic = {node: nodeCopy} + self.dfs(node, dic) + return nodeCopy + +def dfs(self, node, dic): + for neighbor in node.neighbors: + if neighbor not in dic: + neighborCopy = UndirectedGraphNode(neighbor.label) + dic[neighbor] = neighborCopy + dic[node].neighbors.append(neighborCopy) + self.dfs(neighbor, dic) + else: + dic[node].neighbors.append(dic[neighbor]) diff --git a/heap/sliding_window_max.py b/heap/sliding_window_max.py new file mode 100644 index 000000000..f88458a13 --- /dev/null +++ b/heap/sliding_window_max.py @@ -0,0 +1,41 @@ +""" +Given an array nums, there is a sliding window of size k +which is moving from the very left of the array to the very right. +You can only see the k numbers in the window. +Each time the sliding window moves right by one position. + +For example, +Given nums = [1,3,-1,-3,5,3,6,7], and k = 3. + +Window position Max +--------------- ----- +[1 3 -1] -3 5 3 6 7 3 + 1 [3 -1 -3] 5 3 6 7 3 + 1 3 [-1 -3 5] 3 6 7 5 + 1 3 -1 [-3 5 3] 6 7 5 + 1 3 -1 -3 [5 3 6] 7 6 + 1 3 -1 -3 5 [3 6 7] 7 +Therefore, return the max sliding window as [3,3,5,5,6,7]. +""" +import collections + + +def max_sliding_window(nums, k): + """ + :type nums: List[int] + :type k: int + :rtype: List[int] + """ + if not nums: + return nums + queue = collections.deque() + res = [] + for num in nums: + if len(queue) < k: + queue.append(num) + else: + res.append(max(queue)) + queue.popleft() + queue.append(num) + res.append(max(queue)) + return res diff --git a/linkedlist/copy_random_pointer.py b/linkedlist/copy_random_pointer.py new file mode 100644 index 000000000..7e2718bf5 --- /dev/null +++ b/linkedlist/copy_random_pointer.py @@ -0,0 +1,36 @@ +""" +A linked list is given such that each node contains an additional random +pointer which could point to any node in the list or null. + +Return a deep copy of the list. +""" + +class Solution: +# @param head, a RandomListNode +# @return a RandomListNode +def copyRandomList(self, head): + dic = dict() + m = n = head + while m: + dic[m] = RandomListNode(m.label) + m = m.next + while n: + dic[n].next = dic.get(n.next) + dic[n].random = dic.get(n.random) + n = n.next + return dic.get(head) + +#O(n) +class Solution: +# @param head, a RandomListNode +# @return a RandomListNode +def copyRandomList(self, head): + copy = collections.defaultdict(lambda: RandomListNode(0)) + copy[None] = None + node = head + while node: + copy[node].label = node.label + copy[node].next = copy[node.next] + copy[node].random = copy[node.random] + node = node.next + return copy[head] diff --git a/linkedlist/delete_node.py b/linkedlist/delete_node.py new file mode 100644 index 000000000..93e761fef --- /dev/null +++ b/linkedlist/delete_node.py @@ -0,0 +1,13 @@ +""" +Write a function to delete a node (except the tail) +in a singly linked list, given only access to that node. + +Supposed the linked list is 1 -> 2 -> 3 -> 4 and +you are given the third node with value 3, +the linked list should become 1 -> 2 -> 4 after calling your function. +""" + + +def delete_node(node): + node.val = node.next.val + node.next = node.next.next diff --git a/map/longest_common_subsequence.py b/map/longest_common_subsequence.py new file mode 100644 index 000000000..c9033e67b --- /dev/null +++ b/map/longest_common_subsequence.py @@ -0,0 +1,28 @@ +""" +Given string a and b, with b containing all distinct characters, +find the longest common subsequence's + +length. Expected complexity O(nlogn). +""" + + +def maxCommonSubString(S1,S2): + ##Assuming S2 has all unique chars + S2Dic = {S2[i]:i for i in xrange(len(S2))} + maxR = 0 + subS = '' + i = 0 + while i < len(S1): + if S1[i] in S2Dic: + j = S2Dic[S1[i]] + k = i + while j < len(S2) and k < len(S1) and S1[k] == S2[j]: + k += 1 + j += 1 + if k - i > maxR: + maxR = k-i + subS = S1[i:k] + i = k + else: + i += 1 + return subS diff --git a/map/valid_sudoku.py b/map/valid_sudoku.py new file mode 100644 index 000000000..e265d3b17 --- /dev/null +++ b/map/valid_sudoku.py @@ -0,0 +1,14 @@ +""" +Determine if a Sudoku is valid, according to: Sudoku Puzzles - The Rules. + +The Sudoku board could be partially filled, where empty cells are filled with +the character '.'. +""" + +def isValidSudoku(self, board): + seen = [] + for i, row in enumerate(board): + for j, c in enumerate(row): + if c != '.': + seen += [(c,j),(i,c),(i/3,j/3,c)] + return len(seen) == len(set(seen)) diff --git a/math/generate_strobogrammtic.py b/math/generate_strobogrammtic.py new file mode 100644 index 000000000..f5589ac7e --- /dev/null +++ b/math/generate_strobogrammtic.py @@ -0,0 +1,72 @@ +""" +A strobogrammatic number is a number that looks +the same when rotated 180 degrees (looked at upside down). + +Find all strobogrammatic numbers that are of length = n. + +For example, +Given n = 2, return ["11","69","88","96"]. +""" + + +def gen_strobogrammatic(n): + """ + :type n: int + :rtype: List[str] + """ + result = helper(n, n) + return result + + +def helper(n, length): + if n == 0: + return [""] + if n == 1: + return ["1", "0", "8"] + middles = helper(n-2, length) + result = [] + for middle in middles: + if n != length: + result.append("0" + middle + "0") + result.append("8" + middle + "8") + result.append("1" + middle + "1") + result.append("9" + middle + "6") + result.append("6" + middle + "9") + return result + + +def strobogrammaticInRange(low, high): + """ + :type low: str + :type high: str + :rtype: int + """ + res = [] + count = 0 + for i in range(len(low), len(high)+1): + res.extend(helper2(i, i)) + for perm in res: + if len(perm) == len(low) and int(perm) < int(low): + continue + elif len(perm) == len(high) and int(perm) > int(high): + continue + else: + count += 1 + return count + + +def helper2(self, n, length): + if n == 0: + return [""] + if n == 1: + return ["0", "8", "1"] + mids = helper(n-2, length) + res = [] + for mid in mids: + if n != length: + res.append("0"+mid+"0") + res.append("1"+mid+"1") + res.append("6"+mid+"9") + res.append("9"+mid+"6") + res.append("8"+mid+"8") + return res diff --git a/math/is_strobogrammatic.py b/math/is_strobogrammatic.py new file mode 100644 index 000000000..e42e87980 --- /dev/null +++ b/math/is_strobogrammatic.py @@ -0,0 +1,26 @@ +""" +A strobogrammatic number is a number that looks +the same when rotated 180 degrees (looked at upside down). + +Write a function to determine if a number is strobogrammatic. +The number is represented as a string. + +For example, the numbers "69", "88", and "818" are all strobogrammatic. +""" + + +def is_strobogrammatic(num): + """ + :type num: str + :rtype: bool + """ + comb = "00 11 88 69 96" + i = 0 + j = len(num) - 1 + while i <= j: + x = comb.find(num[i]+num[j]) + if x == -1: + return False + i += 1 + j -= 1 + return True diff --git a/math/nth_digit.py b/math/nth_digit.py new file mode 100644 index 000000000..c8ea5de5b --- /dev/null +++ b/math/nth_digit.py @@ -0,0 +1,20 @@ +""" +find nth digit +1. find the length of the number where the nth digit is from. +2. find the actual number where the nth digit is from +3. find the nth digit and return +""" + + +def find_nth_digit(n): + len = 1 + count = 9 + start = 1 + while n > len * count: + n -= len * count + len += 1 + count *= 10 + start *= 10 + start += (n-1) / len + s = str(start) + return int(s[(n-1) % len]) diff --git a/math/sqrt_precision_factor.py b/math/sqrt_precision_factor.py new file mode 100644 index 000000000..a0aef1c4b --- /dev/null +++ b/math/sqrt_precision_factor.py @@ -0,0 +1,20 @@ +""" +Given a positive integer N and a precision factor P, +write a square root function that produce an output +with a maximum error P from the actual square root of N. + +Example: +Given N = 5 and P = 0.001, can produce output O such that +2.235 < O > 2.237. Actual square root of 5 being 2.236. + +public static double squareRoot(int N, float P) { + double guess = N / 2; + + while( Math.abs( guess*guess - N ) > P) { + guess = ( guess + ( N / guess ) ) / 2; + } + return guess; +} +""" + + diff --git a/matrix/sparse_dot_vector.py b/matrix/sparse_dot_vector.py new file mode 100644 index 000000000..0cfceda7b --- /dev/null +++ b/matrix/sparse_dot_vector.py @@ -0,0 +1,12 @@ +""" +Suppose we have very large sparse vectors, which contains a lot of zeros and double . + +find a data structure to store them +get the dot product of them + + +In this case, we first have to store the sparse vector using hash map. +for example [3,0,0,5,6] -> (0,3) (3,5) (4,6) The key is each element's position and the value is the number. + +Then we have two hash tables, and we have to iterate through them to calculate the dot product +""" diff --git a/matrix/sparse_mul.py b/matrix/sparse_mul.py new file mode 100644 index 000000000..f87bcd27d --- /dev/null +++ b/matrix/sparse_mul.py @@ -0,0 +1,99 @@ +""" +Given two sparse matrices A and B, return the result of AB. + +You may assume that A's column number is equal to B's row number. + +Example: + +A = [ + [ 1, 0, 0], + [-1, 0, 3] +] + +B = [ + [ 7, 0, 0 ], + [ 0, 0, 0 ], + [ 0, 0, 1 ] +] + + + | 1 0 0 | | 7 0 0 | | 7 0 0 | +AB = | -1 0 3 | x | 0 0 0 | = | -7 0 3 | + | 0 0 1 | +""" + + +# Python solution without table (~156ms): +def multiply(self, A, B): + """ + :type A: List[List[int]] + :type B: List[List[int]] + :rtype: List[List[int]] + """ + if A is None or B is None: return None + m, n, l = len(A), len(A[0]), len(B[0]) + if len(B) != n: + raise Exception("A's column number must be equal to B's row number.") + C = [[0 for _ in range(l)] for _ in range(m)] + for i, row in enumerate(A): + for k, eleA in enumerate(row): + if eleA: + for j, eleB in enumerate(B[k]): + if eleB: C[i][j] += eleA * eleB + return C + + +# Python solution with only one table for B (~196ms): +def multiply(self, A, B): + """ + :type A: List[List[int]] + :type B: List[List[int]] + :rtype: List[List[int]] + """ + if A is None or B is None: return None + m, n, l = len(A), len(A[0]), len(B[0]) + if len(B) != n: + raise Exception("A's column number must be equal to B's row number.") + C = [[0 for _ in range(l)] for _ in range(m)] + tableB = {} + for k, row in enumerate(B): + tableB[k] = {} + for j, eleB in enumerate(row): + if eleB: tableB[k][j] = eleB + for i, row in enumerate(A): + for k, eleA in enumerate(row): + if eleA: + for j, eleB in tableB[k].iteritems(): + C[i][j] += eleA * eleB + return C + +# Python solution with two tables (~196ms): +def multiply(self, A, B): + """ + :type A: List[List[int]] + :type B: List[List[int]] + :rtype: List[List[int]] + """ + if A is None or B is None: return None + m, n = len(A), len(A[0]) + if len(B) != n: + raise Exception("A's column number must be equal to B's row number.") + l = len(B[0]) + table_A, table_B = {}, {} + for i, row in enumerate(A): + for j, ele in enumerate(row): + if ele: + if i not in table_A: table_A[i] = {} + table_A[i][j] = ele + for i, row in enumerate(B): + for j, ele in enumerate(row): + if ele: + if i not in table_B: table_B[i] = {} + table_B[i][j] = ele + C = [[0 for j in range(l)] for i in range(m)] + for i in table_A: + for k in table_A[i]: + if k not in table_B: continue + for j in table_B[k]: + C[i][j] += table_A[i][k] * table_B[k][j] + return C diff --git a/sort/meeting_rooms.py b/sort/meeting_rooms.py new file mode 100644 index 000000000..72ebb7d51 --- /dev/null +++ b/sort/meeting_rooms.py @@ -0,0 +1,21 @@ +""" +Given an array of meeting time intervals consisting of +start and end times [[s1,e1],[s2,e2],...] (si < ei), +determine if a person could attend all meetings. + +For example, +Given [[0, 30],[5, 10],[15, 20]], +return false. +""" + + +def can_attend_meetings(intervals): + """ + :type intervals: List[Interval] + :rtype: bool + """ + intervals = sorted(intervals, key=lambda x: x.start) + for i in range(1, len(intervals)): + if intervals[i].start < intervals[i-1].end: + return False + return True diff --git a/sort/topsort.py b/sort/topsort.py new file mode 100644 index 000000000..76cd43c11 --- /dev/null +++ b/sort/topsort.py @@ -0,0 +1,58 @@ +""" +Given a list of system packages, +some packages cannot be installed until the other packages are installed. +Provide a valid sequence to install all of the packages. + +e.g. +a relies on b +b relies on c + +then a valid sequence is [c, b, a] +""" + +depGraph = { + + "a" : [ "b" ], + "b" : [ "c" ], + "c" : [ 'e'], + 'e' : [ ], + "d" : [ ], + "f" : ["e" , "d"] +} + + +given = [ "b", "c", "a", "d", "e", "f" ] + +def retDeps(visited, start): + queue = [] + out = [] + queue.append(start) + while queue: + newNode = queue.pop(0) + if newNode not in visited: + visited.add(newNode) + for child in depGraph[newNode]: + queue.append(child) + out.append(child) + out.append(start) + return out + + +def retDepGraph(): + visited = set() + out = [] + # visited.add(given[0]) + for pac in given: + if pac in visited: + continue + visited.add(pac) + #out.append(pac) + if pac in depGraph: + # find all children + for child in depGraph[pac]: + if child in visited: + continue + out.extend(retDeps(visited, child)) + out.append(pac) + print(out) +retDepGraph() diff --git a/string/breaking_bad.py b/string/breaking_bad.py new file mode 100644 index 000000000..3248d9108 --- /dev/null +++ b/string/breaking_bad.py @@ -0,0 +1,82 @@ +""" +Given an api which returns an array of chemical names and an array of chemical +symbols, display the chemical names with their symbol surrounded by square +brackets: + +Ex: +Chemicals array: ['Amazon', 'Microsoft', 'Google'] +Symbols: ['I', 'Am', 'cro', 'Na', 'le', 'abc'] + +Output: +[Am]azon, Mi[cro]soft, Goog[le] + +If the chemical string matches more than one symbol, then choose the one with +longest length. (ex. 'Microsoft' matches 'i' and 'cro') + +My solution: +(I sorted the symbols array in descending order of length and ran loop over +chemicals array to find a symbol match(using indexOf in javascript) which +worked. But I din't make it through the interview, I am guessing my solution +was O(n2) and they expected an efficient algorithm. +""" + +chemicals = ['Amazon', 'Microsoft', 'Google'] +symbols = ['I', 'Am', 'cro', 'le', 'abc'] + +def match_symbol(chemicals, symbols): + import re + combined = [] + + for s in symbols: + for c in chemicals: + r = re.search(s, c) + if r: + combined.append(re.sub(s, "[{}]".format(s), c)) + + return combined + + +print match_symbol(chemicals, symbols) + +""" +One approach is to use a Trie for the dictionary (the symbols), and then match +brute force. The complexity will depend on the dictionary; +if all are suffixes of the other, it will be n*m +(where m is the size of the dictionary). For example, in Python: +""" + +from functools import reduce + +class TrieNode: + def __init__(self): + self.c = dict() + self.sym = None + +def bracket(words, symbols): + root = TrieNode() + for s in symbols: + t = root + for char in s: + if char not in t.c: + t.c[char] = TrieNode() + t = t.c[char] + t.sym = s + result = dict() + for word in words: + i = 0 + symlist = list() + while i < len(word): + j, t = i, root + while j < len(word) and word[j] in t.c: + t = t.c[word[j]] + if t.sym is not None: + symlist.append((j+1-len(t.sym), j+1, t.sym)) + j += 1 + i += 1 + if len(symlist) > 0: + sym = reduce(lambda x, y: x if x[1]-x[0] >= y[1]-y[0] else y, symlist) + result[word] = "{}[{}]{}".format(word[:sym[0]], sym[2], word[sym[1]:]) + return tuple(word if word not in result else result[word] for word in words) + +bracket(['amazon', 'microsoft', 'google'], ['i', 'am', 'cro', 'na', 'le', 'abc']) +>>> ('[am]azon', 'mi[cro]soft', 'goog[le]') diff --git a/string/group_anagrams.py b/string/group_anagrams.py new file mode 100644 index 000000000..1bf3c0a45 --- /dev/null +++ b/string/group_anagrams.py @@ -0,0 +1,29 @@ +""" +Given an array of strings, group anagrams together. + +For example, given: ["eat", "tea", "tan", "ate", "nat", "bat"], +Return: + +[ + ["ate", "eat","tea"], + ["nat","tan"], + ["bat"] +] +""" + + +class Solution(object): + def groupAnagrams(self, strs): + d = {} + ans = [] + k = 0 + for str in strs: + sstr = ''.join(sorted(str)) + if sstr not in d: + d[sstr] = k + k = k+1 + ans.append([]) + ans[-1].append(str) + else: + ans[d[sstr]].append(str) + return ans diff --git a/string/make_sentence.py b/string/make_sentence.py new file mode 100644 index 000000000..925db58b4 --- /dev/null +++ b/string/make_sentence.py @@ -0,0 +1,45 @@ +""" +For a given string and dictionary, how many sentences can you make from the +string, such that all the words are contained in the dictionary. + +// eg: for given string -> "appletablet" +// "apple", "tablet" +// "applet", "able", "t" +// "apple", "table", "t" +// "app", "let", "able", "t" + +// "applet", {app, let, apple, t, applet} => 3 +// "thing", {"thing"} -> 1 +""" + +// Example program +#include +#include +#include +#include + +using namespace std; + + +bool foo(string s, set dic, int &cnt){ + if(s.empty()) + return true; + + for(int i=0;i dic{"","app","let","t","apple","applet"}; + int cnt = 0; + foo(s,dic,cnt); + cout< +[(1.2, 1), (3.1, 2), (4.5, 1), (6.7, 0), (8.9, 1), (10.3, 0)] */ + + +• Sweeping line method + § record the time instance and it type: log in, log out + § Sort the time instances. Keep a variable to record the number of logged in users, number + § For a time instance, + □ if it is log-in type, number++, print +else number--, print + + +1. Constant time random access hash implementation + +2. Efficient elevator API + +3. Ransom note + +4. Median of k unsorted arrays + +5. Design of a task scheduler + +6. Custom comparator diff --git a/tree.md b/tree.md new file mode 100644 index 000000000..a4a593c65 --- /dev/null +++ b/tree.md @@ -0,0 +1,202 @@ +. +├── array +│   ├── circular_counter.py +│   ├── flatten.py +│   ├── garage.py +│   ├── longest_non_repeat.py +│   ├── merge_intervals.py +│   ├── missing_ranges.py +│   ├── plus_one.py +│   ├── rotate_array.py +│   ├── summary_ranges.py +│   ├── three_sum.py +│   └── two_sum.py +├── backtrack +│   ├── anagram.py +│   ├── array_sum_combinations.py +│   ├── combination_sum.py +│   ├── expression_add_operators.py +│   ├── factor_combinations.py +│   ├── general_solution.md +│   ├── generate_abbreviations.py +│   ├── generate_parenthesis.py +│   ├── letter_combination.py +│   ├── palindrome_partitioning.py +│   ├── pattern_match.py +│   ├── permute.py +│   ├── permute_unique.py +│   ├── subsets.py +│   └── subsets_unique.py +├── bfs +│   ├── shortest_distance_from_all_buildings.py +│   └── word_ladder.py +├── bit +│   ├── count_ones.py +│   ├── power_of_two.py +│   ├── reverse_bits.py +│   ├── single_number2.py +│   ├── single_number.py +│   └── subsets.py +├── design +│   ├── alarm_system.md +│   ├── all_o_one_ds.md +│   ├── calculator.md +│   ├── excel_table.md +│   ├── LRUcache.md +│   ├── nearby_drivers.md +│   ├── ride_sharing.md +│   ├── task_runner.md +│   └── twitter_feeds.md +├── dfs +│   ├── all_factors.py +│   ├── count_islands.py +│   ├── pacific_atlantic.py +│   ├── sudoku_solver.py +│   └── walls_and_gates.py +├── dp +│   ├── buy_sell_stock.py +│   ├── climbing_stairs.py +│   ├── combination_sum.py +│   ├── house_robber.py +│   ├── longest_increasing.py +│   ├── max_product_subarray.py +│   ├── max_subarray.py +│   ├── num_decodings.py +│   ├── regex_matching.py +│   └── word_break.py +├── graph +│   ├── clone_graph.py +│   ├── find_path.py +│   ├── graph.py +│   └── traversal.py +├── heap +│   ├── merge_sorted_k_lists.py +│   ├── skyline.py +│   └── sliding_window_max.py +├── linkedlist +│   ├── add_two_numbers.py +│   ├── copy_random_pointer.py +│   ├── delete_node.py +│   ├── first_cyclic_node.py +│   ├── is_cyclic.py +│   ├── is_palindrome.py +│   ├── kth_to_last.py +│   ├── linkedlist.py +│   ├── remove_duplicates.py +│   ├── reverse.py +│   ├── rotate_list.py +│   └── swap_in_pairs.py +├── map +│   ├── hashtable.py +│   ├── longest_common_subsequence.py +│   ├── randomized_set.py +│   └── valid_sudoku.py +├── math +│   ├── generate_strobogrammtic.py +│   ├── is_strobogrammatic.py +│   ├── nth_digit.py +│   └── sqrt_precision_factor.py +├── matrix +│   ├── bomb_enemy.py +│   ├── matrix_rotation.txt +│   ├── rotate_image.py +│   ├── sparse_dot_vector.py +│   ├── sparse_mul.py +│   └── spiral_traversal.py +├── queue +│   ├── __init__.py +│   ├── max_sliding_window.py +│   ├── moving_average.py +│   ├── queue.py +│   ├── reconstruct_queue.py +│   └── zigzagiterator.py +├── README.md +├── search +│   ├── binary_search.py +│   ├── count_elem.py +│   ├── first_occurance.py +│   └── last_occurance.py +├── set +│   └── randomized_set.py +├── sort +│   ├── insertion_sort.py +│   ├── meeting_rooms.py +│   ├── merge_sort.py +│   ├── quick_sort.py +│   ├── selection_sort.py +│   ├── sort_colors.py +│   ├── topsort.py +│   └── wiggle_sort.py +├── stack +│   ├── __init__.py +│   ├── __init__.pyc +│   ├── longest_abs_path.py +│   ├── __pycache__ +│   │   ├── __init__.cpython-35.pyc +│   │   └── stack.cpython-35.pyc +│   ├── simplify_path.py +│   ├── stack.py +│   ├── stack.pyc +│   └── valid_parenthesis.py +├── string +│   ├── add_binary.py +│   ├── breaking_bad.py +│   ├── decode_string.py +│   ├── encode_decode.py +│   ├── group_anagrams.py +│   ├── int_to_roman.py +│   ├── is_palindrome.py +│   ├── license_number.py +│   ├── make_sentence.py +│   ├── multiply_strings.py +│   ├── one_edit_distance.py +│   ├── rabin_karp.py +│   ├── reverse_string.py +│   ├── reverse_vowel.py +│   ├── reverse_words.py +│   ├── roman_to_int.py +│   └── word_squares.py +├── tmp +│   └── temporary.md +├── tree +│   ├── binary_tree_paths.py +│   ├── bintree2list.py +│   ├── bst +│   │   ├── array2bst.py +│   │   ├── bst_closest_value.py +│   │   ├── BSTIterator.py +│   │   ├── delete_node.py +│   │   ├── is_bst.py +│   │   ├── kth_smallest.py +│   │   ├── lowest_common_ancestor.py +│   │   ├── predecessor.py +│   │   ├── serialize_deserialize.py +│   │   ├── successor.py +│   │   └── unique_bst.py +│   ├── deepest_left.py +│   ├── invert_tree.py +│   ├── is_balanced.py +│   ├── is_subtree.py +│   ├── is_symmetric.py +│   ├── longest_consecutive.py +│   ├── lowest_common_ancestor.py +│   ├── max_height.py +│   ├── max_path_sum.py +│   ├── min_height.py +│   ├── path_sum2.py +│   ├── path_sum.py +│   ├── pretty_print.py +│   ├── same_tree.py +│   ├── traversal +│   │   ├── inorder.py +│   │   ├── level_order.py +│   │   └── zigzag.py +│   ├── tree.py +│   └── trie +│   ├── add_and_search.py +│   └── trie.py +├── tree.md +└── union-find + └── count_islands.py + +26 directories, 173 files diff --git a/tree/bst/delete_node.py b/tree/bst/delete_node.py new file mode 100644 index 000000000..7ee827daa --- /dev/null +++ b/tree/bst/delete_node.py @@ -0,0 +1,66 @@ +""" +Given a root node reference of a BST and a key, delete the node with the given key in the BST. Return the root node reference (possibly updated) of the BST. + +Basically, the deletion can be divided into two stages: + +Search for a node to remove. +If the node is found, delete the node. +Note: Time complexity should be O(height of tree). + +Example: + +root = [5,3,6,2,4,null,7] +key = 3 + + 5 + / \ + 3 6 + / \ \ +2 4 7 + +Given key to delete is 3. So we find the node with value 3 and delete it. + +One valid answer is [5,4,6,2,null,null,7], shown in the following BST. + + 5 + / \ + 4 6 + / \ +2 7 + +Another valid answer is [5,2,6,null,4,null,7]. + + 5 + / \ + 2 6 + \ \ + 4 7 +""" + +class Solution(object): + def deleteNode(self, root, key): + """ + :type root: TreeNode + :type key: int + :rtype: TreeNode + """ + if not root: return None + + if root.val == key: + if root.left: + # Find the right most leaf of the left sub-tree + left_right_most = root.left + while left_right_most.right: + left_right_most = left_right_most.right + # Attach right child to the right of that leaf + left_right_most.right = root.right + # Return left child instead of root, a.k.a delete root + return root.left + else: + return root.right + # If left or right child got deleted, the returned root is the child of the deleted node. + elif root.val > key: + root.left = self.deleteNode(root.left, key) + else: + root.right = self.deleteNode(root.right, key) + return root diff --git a/tree/bst/kth_smallest.py b/tree/bst/kth_smallest.py index 13a615365..20f892b58 100644 --- a/tree/bst/kth_smallest.py +++ b/tree/bst/kth_smallest.py @@ -12,3 +12,23 @@ def kth_smallest(root, k): break root = root.right return root.val + + +class Solution(object): + def kthSmallest(self, root, k): + """ + :type root: TreeNode + :type k: int + :rtype: int + """ + count = [] + self.helper(root, count) + return count[k-1] + + def helper(self, node, count): + if not node: + return + + self.helper(node.left, count) + count.append(node.val) + self.helper(node.right, count) diff --git a/tree/bst/lowest_common_ancestor.py b/tree/bst/lowest_common_ancestor.py new file mode 100644 index 000000000..a3bfad13f --- /dev/null +++ b/tree/bst/lowest_common_ancestor.py @@ -0,0 +1,37 @@ +""" +Given a binary search tree (BST), +find the lowest common ancestor (LCA) of two given nodes in the BST. + +According to the definition of LCA on Wikipedia: + “The lowest common ancestor is defined between two + nodes v and w as the lowest node in T that has both v and w + as descendants (where we allow a node to be a descendant of itself).” + + _______6______ + / \ + ___2__ ___8__ + / \ / \ + 0 _4 7 9 + / \ + 3 5 + +For example, the lowest common ancestor (LCA) of nodes 2 and 8 is 6. +Another example is LCA of nodes 2 and 4 is 2, +since a node can be a descendant of itself according to the LCA definition. +""" + + +def lowest_common_ancestor(root, p, q): + """ + :type root: Node + :type p: Node + :type q: Node + :rtype: Node + """ + while root: + if p.val > root.val < q.val: + root = root.right + elif p.val < root.val > q.val: + root = root.left + else: + return root diff --git a/tree/bst/serialize_deserialize.py b/tree/bst/serialize_deserialize.py new file mode 100644 index 000000000..602e2222d --- /dev/null +++ b/tree/bst/serialize_deserialize.py @@ -0,0 +1,26 @@ + + +def serialize(root): + def build_string(node): + if node: + vals.append(str(node.val)) + build_string(node.left) + build_string(node.right) + else: + vals.append("#") + vals = [] + build_string(root) + return " ".join(vals) + + +def deserialize(data): + def build_tree(): + val = next(vals) + if val == "#": + return None + node = TreeNode(int(val)) + node.left = build_tree() + node.right = build_tree() + return node + vals = iter(data.split()) + return build_tree() diff --git a/tree/lowest_common_ancestor.py b/tree/lowest_common_ancestor.py new file mode 100644 index 000000000..1702949f5 --- /dev/null +++ b/tree/lowest_common_ancestor.py @@ -0,0 +1,37 @@ +""" +Given a binary tree, find the lowest common ancestor +(LCA) of two given nodes in the tree. + +According to the definition of LCA on Wikipedia: + “The lowest common ancestor is defined between two nodes + v and w as the lowest node in T that has both v and w as + descendants + (where we allow a node to be a descendant of itself).” + + _______3______ + / \ + ___5__ ___1__ + / \ / \ + 6 _2 0 8 + / \ + 7 4 +For example, the lowest common ancestor (LCA) of nodes 5 and 1 is 3. +Another example is LCA of nodes 5 and 4 is 5, +since a node can be a descendant of itself according to the LCA definition. +""" + + +def LCA(root, p, q): + """ + :type root: TreeNode + :type p: TreeNode + :type q: TreeNode + :rtype: TreeNode + """ + if not root or root is p or root is q: + return root + left = LCA(root.left, p, q) + right = LCA(root.right, p, q) + if left and right: + return root + return left if left else right diff --git a/tree/pretty_print.py b/tree/pretty_print.py new file mode 100644 index 000000000..dc58ab3ec --- /dev/null +++ b/tree/pretty_print.py @@ -0,0 +1,20 @@ +# a -> Adam -> Book -> 4 +# b -> Bill -> Computer -> 5 + # -> TV -> 6 + # Jill -> Sports -> 1 +# c -> Bill -> Sports -> 3 +# d -> Adam -> Computer -> 3 + # Quin -> Computer -> 3 +# e -> Quin -> Book -> 5 + # -> TV -> 2 +# f -> Adam -> Computer -> 7 + +def treePrint(tree): + for key in tree: + print key, # comma prevents a newline character + treeElem = tree[key] # multiple lookups is expensive, even amortized O(1)! + for subElem in treeElem: + print " -> ", subElem, + if type(subElem) != str: # OP wants indenting after digits + print "\n " # newline and a space to match indenting + print "" # forces a newline diff --git a/union-find/count_islands.py b/union-find/count_islands.py new file mode 100644 index 000000000..15cd676ee --- /dev/null +++ b/union-find/count_islands.py @@ -0,0 +1,78 @@ +""" +A 2d grid map of m rows and n columns is initially filled with water. +We may perform an addLand operation which turns the water at position +(row, col) into a land. Given a list of positions to operate, +count the number of islands after each addLand operation. +An island is surrounded by water and is formed by connecting adjacent +lands horizontally or vertically. +You may assume all four edges of the grid are all surrounded by water. + +Given m = 3, n = 3, positions = [[0,0], [0,1], [1,2], [2,1]]. +Initially, the 2d grid grid is filled with water. +(Assume 0 represents water and 1 represents land). + +0 0 0 +0 0 0 +0 0 0 +Operation #1: addLand(0, 0) turns the water at grid[0][0] into a land. + +1 0 0 +0 0 0 Number of islands = 1 +0 0 0 +Operation #2: addLand(0, 1) turns the water at grid[0][1] into a land. + +1 1 0 +0 0 0 Number of islands = 1 +0 0 0 +Operation #3: addLand(1, 2) turns the water at grid[1][2] into a land. + +1 1 0 +0 0 1 Number of islands = 2 +0 0 0 +Operation #4: addLand(2, 1) turns the water at grid[2][1] into a land. + +1 1 0 +0 0 1 Number of islands = 3 +0 1 0 +""" + + +class Solution(object): + def numIslands2(self, m, n, positions): + ans = [] + islands = Union() + for p in map(tuple, positions): + islands.add(p) + for dp in (0, 1), (0, -1), (1, 0), (-1, 0): + q = (p[0] + dp[0], p[1] + dp[1]) + if q in islands.id: + islands.unite(p, q) + ans += [islands.count] + return ans + +class Union(object): + def __init__(self): + self.id = {} + self.sz = {} + self.count = 0 + + def add(self, p): + self.id[p] = p + self.sz[p] = 1 + self.count += 1 + + def root(self, i): + while i != self.id[i]: + self.id[i] = self.id[self.id[i]] + i = self.id[i] + return i + + def unite(self, p, q): + i, j = self.root(p), self.root(q) + if i == j: + return + if self.sz[i] > self.sz[j]: + i, j = j, i + self.id[i] = j + self.sz[j] += self.sz[i] + self.count -= 1