├── requirements.txt ├── pytest.ini ├── keep_tools_sharp.png ├── .travis.yml ├── challenges ├── array │ ├── max_subarray.py │ ├── max_consecutive_gap.cpp │ ├── array_duplicate.cpp │ ├── array_duplicate.py │ ├── sorted_array_intersection.py │ ├── max_consecutive_gap.py │ ├── min_steps_infinite_grid.py │ ├── pascal_triangle.py │ ├── wave_array.py │ ├── rotate_matrix.py │ ├── find_permutation.py │ ├── rearrange_array.py │ ├── min_steps_infinite_grid.cpp │ ├── zero_matrix.py │ ├── spiral_order.py │ ├── temp_tracker.py │ └── merge_overlapping_intervals.py ├── string │ ├── reverse_string_inplace.py │ ├── length_last_word.py │ ├── longest_common_prefix.py │ ├── needle_haystack.py │ ├── roman_to_integer.py │ ├── string_multiplication.py │ ├── palindrome_string.py │ ├── zigzag_string.py │ ├── compare_version_numbers.py │ ├── integer_to_roman.py │ └── longest_palindromic_substring.py ├── linked_list │ ├── merge_sorted_lists.py │ ├── reverse_linked_list.py │ ├── list_cycle.py │ ├── intersection_linked_list.py │ ├── partition_list.py │ ├── swap_list_nodes_in_pairs.py │ ├── add_two_numbers_list.py │ └── reverse_linked_list_partial.py ├── backtracking │ ├── modular_exponentiation.py │ ├── permutations.py │ ├── combinations.py │ ├── unique_permutations.py │ ├── letter_phone.py │ └── gen_parens.py ├── bit_manipulation │ ├── bit_single_number.py │ └── power_of_2.py ├── maps │ ├── majority_element.py │ ├── anagrams.py │ └── distinct_numbers_in_window.py ├── hashing │ ├── longest_substring_no_repeat.py │ ├── two_sum.py │ └── points_on_straight_line.py ├── greedy │ ├── trading_stock.py │ ├── bulbs.py │ └── mice_holes.py ├── math │ ├── greatest_common_divisor.py │ ├── excel_column_number.py │ ├── factorial_trailing_zeros.py │ ├── palindrome_integer.py │ └── two_egg_problem.py ├── two_pointers │ ├── container_most_water.py │ ├── diffk.py │ └── rectangle_intersection.py ├── binary_search │ ├── sqrt.py │ ├── binary_occurrence_search.py │ ├── power.py │ ├── sorted_insert_position.py │ ├── binary_occurrence_search.cpp │ ├── rotated_sorted_array_search.py │ ├── binary_matrix_search.py │ └── search_range.py ├── trees │ ├── max_tree_depth.py │ ├── kth_smallest_tree_elem.py │ ├── symmetric_binary_tree.py │ ├── preorder_traversal.py │ ├── postorder_traversal.py │ ├── invert_binary_tree.py │ ├── sorted_array_balanced_bst.py │ ├── sum_root_leaf_numbers.py │ ├── zigzag_level_bst_traversal.py │ ├── identical_binary_tree.py │ ├── min_tree_depth.py │ ├── order_people_heights.py │ ├── valid_bst.py │ ├── path_sum.py │ ├── inorder_traversal.py │ ├── merge_k_sorted_lists.py │ ├── balanced_binary_tree.py │ ├── count_inversions.py │ ├── flatten_binary_tree_linked_list.py │ ├── least_common_ancestor.py │ └── shortest_unique_prefix.py ├── stack │ ├── generate_parentheses.py │ ├── simplify_directory_path.py │ ├── nearest_smallest_element.py │ ├── min_stack.py │ └── evaluate_expression.py └── graphs │ ├── level_order.py │ └── black_shapes.py ├── CMakeLists.txt ├── .gitignore ├── LICENSE └── README.md /requirements.txt: -------------------------------------------------------------------------------- 1 | pytest==2.9.2 2 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | python_files=challenges/**/*.py 3 | -------------------------------------------------------------------------------- /keep_tools_sharp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukasmartinelli/sharpen/HEAD/keep_tools_sharp.png -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | install: "pip install -r requirements.txt" 5 | script: py.test challenges 6 | -------------------------------------------------------------------------------- /challenges/array/max_subarray.py: -------------------------------------------------------------------------------- 1 | def max_sub_array(A): 2 | max_sum = A[0] 3 | for i in range(0, len(A)): 4 | sum = 0 5 | for j in range(i, len(A)): 6 | sum += A[j] 7 | max_sum = max(max_sum, sum) 8 | 9 | return max_sum 10 | 11 | print(max_sub_array([-2,1,-3,4,-1,2,1,-5,4])) 12 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.11) 2 | project(sharpen) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -std=c++11") 5 | #---------------------------------------------------------------------- 6 | add_executable(array_duplicate challenges/array/array_duplicate.cpp) 7 | add_executable(max_consecutive_gap challenges/array/max_consecutive_gap.cpp) 8 | 9 | -------------------------------------------------------------------------------- /challenges/string/reverse_string_inplace.py: -------------------------------------------------------------------------------- 1 | def reverse(arr): 2 | low = 0 3 | high = len(arr) - 1 4 | 5 | while low < high: 6 | arr[low], arr[high] = arr[high], arr[low] 7 | low += 1 8 | high -= 1 9 | 10 | return arr 11 | 12 | 13 | def test_reverse(): 14 | assert reverse(['h', 'e', 'l', 'l']) == ['l', 'l', 'e', 'h'] 15 | assert reverse(['h', 'e', 'y', 'w', 'o']) == ['o', 'w', 'y', 'e', 'h'] 16 | -------------------------------------------------------------------------------- /challenges/linked_list/merge_sorted_lists.py: -------------------------------------------------------------------------------- 1 | class ListNode: 2 | def __init__(self, x): 3 | self.val = x 4 | self.next = None 5 | 6 | def __repr__(self): 7 | return ''.format(self.val, self.next) 8 | 9 | 10 | n1 = ListNode(0) 11 | n2 = ListNode(1) 12 | n3 = ListNode(2) 13 | n4 = ListNode(3) 14 | 15 | n1.next = n2 16 | n2.next = n3 17 | n3.next = n4 18 | 19 | linked_list = n1 20 | print((linked_list)) 21 | -------------------------------------------------------------------------------- /challenges/backtracking/modular_exponentiation.py: -------------------------------------------------------------------------------- 1 | def pow_mod(x, n, m): 2 | if n == 0: 3 | return 1 % m 4 | elif n % 2 == 0: 5 | y = pow_mod(x, n/2, m) 6 | return (y * y) % m 7 | else: 8 | y = x % m 9 | return (y * pow_mod(x, n-1, m)) % m 10 | 11 | 12 | def test_pow_mod(): 13 | assert pow_mod(0, 0, 1) == 0 14 | assert pow_mod(2, 3, 3) == 2 15 | assert pow_mod(71045970, 41535484, 64735492) == 20805472 16 | -------------------------------------------------------------------------------- /challenges/bit_manipulation/bit_single_number.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given an array of integers, every element appears twice except for one. 3 | Find that single one. 4 | """ 5 | import math 6 | 7 | 8 | def find_single_number(arr): 9 | single_number = 0 10 | 11 | for num in arr: 12 | mask = 1 << num 13 | single_number ^= mask 14 | 15 | return int(math.log(single_number, 2)) 16 | 17 | 18 | def test_find_single_number(): 19 | assert find_single_number([1, 2, 2, 3, 1]) == 3 20 | -------------------------------------------------------------------------------- /challenges/maps/majority_element.py: -------------------------------------------------------------------------------- 1 | import collections 2 | 3 | 4 | def majority_element(arr): 5 | """ 6 | Given an array of size n, find the majority element. 7 | The majority element is the element that appears 8 | more than floor(n/2) times. 9 | """ 10 | counts = collections.defaultdict(int) 11 | for elem in arr: 12 | counts[elem] += 1 13 | if counts[elem] > len(arr) / 2: 14 | return elem 15 | 16 | 17 | def test_majority_elemen(): 18 | assert majority_element([2, 1, 2]) == 2 19 | -------------------------------------------------------------------------------- /challenges/string/length_last_word.py: -------------------------------------------------------------------------------- 1 | def length_last_word(s): 2 | """ 3 | Given a string s consists of upper/lower-case alphabets and 4 | empty space characters ' ', return the length of last word in the string. 5 | """ 6 | s = s.strip() 7 | word_length = 0 8 | for c in s: 9 | if c == ' ': 10 | word_length = 0 11 | else: 12 | word_length += 1 13 | return word_length 14 | 15 | 16 | def test_length_last_word(): 17 | assert length_last_word('Hey man whats up') == 2 18 | assert length_last_word('Yo') == 2 19 | assert length_last_word('Yo yo man ') == 3 20 | assert length_last_word('') == 0 21 | -------------------------------------------------------------------------------- /challenges/hashing/longest_substring_no_repeat.py: -------------------------------------------------------------------------------- 1 | def length_longest_substring(text): 2 | max_length = start = 0 3 | used_chars = {} 4 | 5 | for i, c in enumerate(text): 6 | if c in used_chars and start <= used_chars[c]: 7 | start = used_chars[c] + 1 8 | else: 9 | max_length = max(max_length, i - start + 1) 10 | 11 | used_chars[c] = i 12 | 13 | return max_length 14 | 15 | 16 | def test_length_longest_substring(): 17 | assert length_longest_substring("") == 0 18 | assert length_longest_substring("u") == 1 19 | assert length_longest_substring("bbbbb") == 1 20 | assert length_longest_substring("abcabcbb") == 3 21 | -------------------------------------------------------------------------------- /challenges/string/longest_common_prefix.py: -------------------------------------------------------------------------------- 1 | def longest_common_prefix(strs): 2 | if len(strs) < 1: 3 | return '' 4 | 5 | def find_prefix(): 6 | for idx in range(0, len(strs[0])): 7 | expected_char = strs[0][idx] 8 | 9 | for str in strs[1:]: 10 | if len(str) <= idx or str[idx] != expected_char: 11 | return 12 | 13 | yield expected_char 14 | 15 | return ''.join(find_prefix()) 16 | 17 | 18 | def test_prefix(): 19 | assert longest_common_prefix([]) == '' 20 | assert longest_common_prefix(['a', 'b', 'c']) == '' 21 | assert longest_common_prefix(['abcd', 'abcd', 'aba']) == 'ab' 22 | -------------------------------------------------------------------------------- /challenges/array/max_consecutive_gap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int max_gap(std::vector elements) { 8 | std::sort(elements.begin(), elements.end()); 9 | int max {0}; 10 | for (auto it=elements.begin(); it!=elements.end(); ++it) { 11 | auto prev_it = std::prev(it); 12 | if (prev_it == elements.begin()) continue; 13 | const int prev_elem = *prev_it; 14 | const int cur_elem = *it; 15 | max = std::max(max, cur_elem - prev_elem); 16 | } 17 | return max; 18 | } 19 | 20 | int main() { 21 | std::vector e1 = {1, 10, 5}; 22 | assert(max_gap(e1) == 5); 23 | } 24 | -------------------------------------------------------------------------------- /challenges/greedy/trading_stock.py: -------------------------------------------------------------------------------- 1 | def max_profit_yesterday(stock_prices): 2 | max_profit = stock_prices[1] - stock_prices[0] 3 | min_price = stock_prices[0] 4 | 5 | for price in stock_prices[1:]: 6 | possibe_profit = price - min_price 7 | max_profit = max(possibe_profit, max_profit) 8 | min_price = min(min_price, price) 9 | 10 | return max_profit 11 | 12 | 13 | def test_max_profit_yesterday_normal(): 14 | stock_prices_yesterday = [10, 7, 5, 8, 3, 4, 10, 11, 2, 4, 5] 15 | assert max_profit_yesterday(stock_prices_yesterday) == 8 16 | 17 | 18 | def test_max_profit_yesterday_crash(): 19 | stock_prices_crash = [10, 7, 5, 1] 20 | assert max_profit_yesterday(stock_prices_crash) == -2 21 | -------------------------------------------------------------------------------- /challenges/backtracking/permutations.py: -------------------------------------------------------------------------------- 1 | def permute(numbers): 2 | numbers = set(numbers) 3 | def recursive_permutations(path): 4 | if len(path) == len(numbers): 5 | yield path 6 | 7 | remaining_choices = [num for num in numbers if num not in path] 8 | for num in remaining_choices: 9 | new_path = path + [num] 10 | for permuted_path in recursive_permutations(new_path): 11 | yield permuted_path 12 | 13 | return list(recursive_permutations([])) 14 | 15 | 16 | def test_permutations(): 17 | assert permute([1, 2, 3]) == [ 18 | [1, 2, 3], 19 | [1, 3, 2], 20 | [2, 1, 3], 21 | [2, 3, 1], 22 | [3, 1, 2], 23 | [3, 2, 1] 24 | ] 25 | -------------------------------------------------------------------------------- /challenges/math/greatest_common_divisor.py: -------------------------------------------------------------------------------- 1 | def greatest_common_divisor(m, n): 2 | """ 3 | Using the Euclidean algorithm 4 | https://www.khanacademy.org/computing/computer-science/cryptography/modarithmetic/a/the-euclidean-algorithm 5 | """ 6 | low = min(m, n) 7 | high = max(m, n) 8 | 9 | if low == 0: 10 | return high 11 | 12 | remainder = high % low 13 | return greatest_common_divisor(low, remainder) 14 | 15 | 16 | def test_greatest_common_divisor(): 17 | gcd = greatest_common_divisor 18 | assert gcd(6, 9) == 3 19 | assert gcd(9, 6) == 3 20 | assert gcd(14, 6) == 2 21 | assert gcd(1, 2) == 1 22 | assert gcd(1, 1) == 1 23 | assert gcd(1, 0) == 1 24 | assert gcd(2, 0) == 2 25 | assert gcd(270, 192) == 6 26 | -------------------------------------------------------------------------------- /challenges/string/needle_haystack.py: -------------------------------------------------------------------------------- 1 | def find_index(needle, haystack): 2 | if needle == "" or haystack == "": 3 | return -1 4 | 5 | # very primitive approach 6 | for first_idx in range(0, len(haystack) - len(needle) + 1): 7 | last_idx = first_idx + len(needle) 8 | if needle == haystack[first_idx:last_idx]: 9 | return first_idx 10 | 11 | return -1 12 | 13 | 14 | def test_find_index_not_found(): 15 | assert find_index("", "bbab") == -1 16 | assert find_index("", "") == -1 17 | assert find_index("a", "") == -1 18 | 19 | 20 | def test_find_index_found(): 21 | assert find_index("a", "bbab") == 2 22 | assert find_index("a", "baba") == 1 23 | assert find_index("a", "bbba") == 3 24 | assert find_index("baba", "babaab") == 0 25 | -------------------------------------------------------------------------------- /challenges/string/roman_to_integer.py: -------------------------------------------------------------------------------- 1 | symbol_lookup = { 2 | 'I': 1, 3 | 'V': 5, 4 | 'X': 10, 5 | 'L': 50, 6 | 'C': 100, 7 | 'D': 500, 8 | 'M': 1000 9 | } 10 | 11 | 12 | def roman_to_int(numeral): 13 | if len(numeral) == 0: 14 | return 0 15 | 16 | current_value = symbol_lookup.get(numeral[0], 0) 17 | 18 | if len(numeral) > 1: 19 | lookahead = symbol_lookup.get(numeral[1], 0) 20 | if lookahead > current_value: 21 | return roman_to_int(numeral[1:]) - current_value 22 | 23 | return current_value + roman_to_int(numeral[1:]) 24 | 25 | 26 | def test_roman_to_int(): 27 | assert roman_to_int('XIV') == 14 28 | assert roman_to_int('XX') == 20 29 | assert roman_to_int('MMXIV') == 2014 30 | assert roman_to_int('MCMXC') == 1990 31 | -------------------------------------------------------------------------------- /challenges/array/array_duplicate.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int find_duplicate(std::vector elements) { 7 | std::unordered_map lookup{}; 8 | 9 | for (auto it=elements.begin(); it!=elements.end(); ++it) { 10 | if (lookup.find(*it) != lookup.end()) { 11 | return *it; 12 | } else { 13 | lookup[*it] = true; 14 | } 15 | } 16 | return -1; 17 | } 18 | 19 | int main() { 20 | std::vector e1 = {3, 4, 1, 4, 5}; 21 | assert(find_duplicate(e1) == 4); 22 | 23 | std::vector e2 = {1, 2, 3, 4, 5, 1}; 24 | assert(find_duplicate(e2) == 1); 25 | 26 | std::vector e3 = {1, 2, 3}; 27 | assert(find_duplicate(e3) == -1); 28 | } 29 | -------------------------------------------------------------------------------- /challenges/two_pointers/container_most_water.py: -------------------------------------------------------------------------------- 1 | def container_most_water(heights): 2 | coords = zip(range(0, len(heights)), heights) 3 | i = 0 4 | j = len(coords) - 1 5 | 6 | max_area = 0 7 | while i != j: 8 | height = min(heights[i], heights[j]) 9 | width = abs(j - i) 10 | area = height * width 11 | print('({}, {}) <=> ({}, {}) => {}'.format(i, heights[i], j, heights[j], area)) 12 | 13 | if heights[i] <= heights[j]: 14 | i += 1 15 | elif heights[i] > heights[j]: 16 | j -= 1 17 | 18 | max_area = max(max_area, area) 19 | 20 | return max_area 21 | 22 | 23 | def test_container_most_water(): 24 | assert container_most_water([3, 4, 8, 10, 3, 6, 8, 3, 4, 1]) == 32 25 | assert container_most_water([1, 5, 4, 3]) == 6 26 | -------------------------------------------------------------------------------- /challenges/bit_manipulation/power_of_2.py: -------------------------------------------------------------------------------- 1 | def count_one_bits(num): 2 | one_bits = 0 3 | 4 | while num > 0: 5 | if num & 1: 6 | one_bits += 1 7 | num = num >> 1 8 | 9 | return one_bits 10 | 11 | 12 | def power_of_two(num): 13 | return count_one_bits(num) == 1 14 | 15 | 16 | def test_one_bits(): 17 | assert count_one_bits(1) == 1 18 | assert count_one_bits(2) == 1 19 | assert count_one_bits(3) == 2 20 | assert count_one_bits(4) == 1 21 | assert count_one_bits(5) == 2 22 | 23 | 24 | def test_power_of_two(): 25 | assert power_of_two(1) 26 | assert power_of_two(2) 27 | assert not power_of_two(3) 28 | assert power_of_two(4) 29 | assert not power_of_two(5) 30 | assert not power_of_two(6) 31 | assert power_of_two(8) 32 | assert power_of_two(16) 33 | assert power_of_two(32) 34 | -------------------------------------------------------------------------------- /challenges/binary_search/sqrt.py: -------------------------------------------------------------------------------- 1 | """Implement square root""" 2 | 3 | 4 | def binary_sqrt_search(num): 5 | first = 0 6 | last = num 7 | 8 | while first <= last: 9 | mid = (first + last) / 2 10 | 11 | pow2 = mid * mid 12 | if pow2 == num: 13 | return mid 14 | 15 | if pow2 < num: 16 | first = mid + 1 17 | 18 | if pow2 > num: 19 | last = mid - 1 20 | 21 | return last 22 | 23 | 24 | def test_binary_sqrt_search(): 25 | def sqrt(num): 26 | return binary_sqrt_search(num) 27 | 28 | assert sqrt(3) == 1 29 | assert sqrt(0) == 0 30 | assert sqrt(1) == 1 31 | assert sqrt(4) == 2 32 | assert sqrt(8) == 2 33 | assert sqrt(9) == 3 34 | assert sqrt(16) == 4 35 | assert sqrt(25) == 5 36 | assert sqrt(26) == 5 37 | assert sqrt(308634616) == 17567 38 | -------------------------------------------------------------------------------- /challenges/array/array_duplicate.py: -------------------------------------------------------------------------------- 1 | import collections 2 | 3 | 4 | def find_duplicate(arr): 5 | """ 6 | Find the first duplicate in an array of n + 1 integers between 1 and n. 7 | Find a repeating number in linear time using less than O(n) space and 8 | traversing the array only once. 9 | 10 | Given the input A. 11 | 12 | assert find_duplicate([3, 4, 1, 4, 1]) == 1 13 | """ 14 | counts = collections.defaultdict(int) 15 | 16 | for elem in arr: 17 | counts[elem] += 1 18 | 19 | if counts[elem] > 1: 20 | return elem 21 | 22 | 23 | def test_find_duplicate_in_single_element_list(): 24 | assert find_duplicate([4]) is None 25 | 26 | 27 | def test_find_duplicate_in_short_list(): 28 | assert find_duplicate([3, 4, 1, 4, 1]) == 4 29 | 30 | 31 | def test_find_duplicate_in_large_list(): 32 | assert find_duplicate(range(0, 200) + [4]) == 4 33 | -------------------------------------------------------------------------------- /challenges/array/sorted_array_intersection.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given 2 sorted arrays, find all the elements 3 | which occur in both the arrays. 4 | """ 5 | 6 | 7 | def intersection(arr_a, arr_b): 8 | i = 0 9 | j = 0 10 | 11 | while i < len(arr_a) and j < len(arr_b): 12 | # print('a[{}] == b[{}] -> {} == {}'.format(i, j, arr_a[i], arr_b[j])) 13 | if arr_a[i] == arr_b[j]: 14 | yield arr_a[i] 15 | i += 1 16 | j += 1 17 | elif arr_a[i] > arr_b[j]: 18 | j += 1 19 | elif arr_a[i] < arr_b[j]: 20 | i += 1 21 | 22 | 23 | assert list(intersection([1, 2, 3, 3, 4, 5, 6], [3, 3, 5])) == [3, 3, 5] 24 | assert list(intersection([1, 2, 3, 3, 4, 5, 6], [3, 5])) == [3, 5] 25 | assert list(intersection([], [])) == [] 26 | assert list(intersection([2], [-1, 0, 2])) == [2] 27 | assert list(intersection([1, 2, 3, 4], [2, 3, 4])) == [2, 3, 4] 28 | -------------------------------------------------------------------------------- /challenges/string/string_multiplication.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given two numbers represented as strings, 3 | return multiplication of the numbers as a string. 4 | """ 5 | import pytest 6 | 7 | 8 | def multiply(left, right): 9 | if len(left) > 1: 10 | raise ValueError("The left operand must be smaller than < 10") 11 | 12 | left_operand = int(left) 13 | take_over = 0 14 | # we can use dequeue later for inserting at start fast 15 | total = "" 16 | 17 | for c in right: 18 | right_operand = int(c) 19 | result = left_operand * right_operand 20 | 21 | keep = (result + take_over) % 10 22 | total = str(keep) + total 23 | take_over = result / 10 24 | 25 | if take_over > 0: 26 | total = str(take_over + int(total)) 27 | 28 | return total 29 | 30 | 31 | @pytest.mark.skip(reason="Not solved yet") 32 | def test_multiply(): 33 | assert multiply("7", "73") == "511" 34 | -------------------------------------------------------------------------------- /challenges/greedy/bulbs.py: -------------------------------------------------------------------------------- 1 | def switch_bulbs_on(bulbs): 2 | """ 3 | N light bulbs are connected by a wire. Each bulb has a switch 4 | associated with it, however due to faulty wiring, a switch also 5 | changes the state of all the bulbs to the right of current bulb. 6 | 7 | Given an initial state of all bulbs, find the minimum number of switches 8 | you have to press to turn on all the bulbs. You can press the same 9 | switch multiple times. 10 | """ 11 | min_switch_changes = 0 12 | flipped = False 13 | 14 | for b in bulbs: 15 | if (b == 0 and not flipped) or (b == 1 and flipped): 16 | flipped = not flipped 17 | min_switch_changes += 1 18 | return min_switch_changes 19 | 20 | 21 | def test_switch_bulbs_on(): 22 | assert switch_bulbs_on([0, 1, 0, 1]) == 4 23 | assert switch_bulbs_on([0, 0, 0, 0]) == 1 24 | assert switch_bulbs_on([0, 1, 0, 0]) == 3 25 | -------------------------------------------------------------------------------- /challenges/binary_search/binary_occurrence_search.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a sorted array of integers, find the number of occurrences 3 | of a given target value. 4 | """ 5 | 6 | 7 | def count_element(numbers, constraint): 8 | if len(numbers) == 0: 9 | return 0 10 | 11 | if len(numbers) == 1: 12 | if numbers[0] == constraint: 13 | return 1 14 | else: 15 | return 0 16 | 17 | half = len(numbers)/2 18 | if numbers[half] < constraint: 19 | return count_element(numbers[half:], constraint) 20 | elif numbers[half] > constraint: 21 | return count_element(numbers[:half], constraint) 22 | else: 23 | return 1 + count_element(numbers[:half], constraint) + count_element(numbers[half+1:], constraint) 24 | 25 | 26 | def test_count_element(): 27 | assert count_element([1, 1, 2, 3, 4, 5, 6], 1) == 2 28 | assert count_element([1, 1, 2, 3, 4, 5, 5, 5, 6], 5) == 3 29 | -------------------------------------------------------------------------------- /challenges/maps/anagrams.py: -------------------------------------------------------------------------------- 1 | import collections 2 | 3 | 4 | def anagram_id(word): 5 | return ''.join(sorted(word)) 6 | 7 | 8 | def anagrams(words): 9 | """ 10 | Given an array of strings, return all groups of strings that are anagrams. 11 | Represent a group by a list of integers representing the index in the original list. 12 | """ 13 | lookup = collections.OrderedDict() 14 | 15 | for idx, word in enumerate(words, start=1): 16 | word_id = anagram_id(word) 17 | 18 | if word_id in lookup: 19 | lookup[word_id] += [idx] 20 | else: 21 | lookup[word_id] = [idx] 22 | 23 | return lookup.values() 24 | 25 | 26 | def test_anagrams(): 27 | words = ["cat", "dog", "god", "tca"] 28 | assert anagrams(words) == [[1, 4], [2, 3]] 29 | 30 | 31 | def test_with_no_anagrams(): 32 | words = ["bub", "cat", "dog", "god", "tca"] 33 | assert anagrams(words) == [[1], [2, 5], [3, 4]] 34 | -------------------------------------------------------------------------------- /challenges/trees/max_tree_depth.py: -------------------------------------------------------------------------------- 1 | class TreeNode(): 2 | def __init__(self, x): 3 | self.val = x 4 | self.left = None 5 | self.right = None 6 | 7 | def __repr__(self): 8 | return ''.format(self.val) 9 | 10 | 11 | def max_depth(node, depth=0): 12 | if node is None: 13 | return depth 14 | 15 | return max( 16 | max_depth(node.left, depth+1), 17 | max_depth(node.right, depth+1) 18 | ) 19 | 20 | 21 | def create_test_tree(): 22 | n1 = TreeNode(1) 23 | n2 = TreeNode(2) 24 | n3 = TreeNode(3) 25 | n4 = TreeNode(4) 26 | n5 = TreeNode(5) 27 | n6 = TreeNode(6) 28 | 29 | n4.left = n2 30 | n4.right = n6 31 | 32 | n2.left = n1 33 | n2.right = n3 34 | 35 | n6.left = n5 36 | 37 | root = n4 38 | 39 | return root 40 | 41 | 42 | def test_postorder_larger_tree(): 43 | root = create_test_tree() 44 | assert max_depth(root) == 3 45 | -------------------------------------------------------------------------------- /challenges/array/max_consecutive_gap.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given an unsorted array. 3 | 4 | elements = [1, 10, 5] 5 | 6 | Find the maximum difference between the 7 | successive elements in its sorted form. 8 | 9 | # max difference is between 5 and 10 10 | assert max_gap(elements) == 5 11 | 12 | You may assume that all the elements in the array are 13 | non-negative integers and fit in the 32-bit signed integer range. 14 | You may also assume that the difference will not overflow. 15 | """ 16 | 17 | 18 | def max_gap(numbers): 19 | # O(n * log(n)) 20 | numbers = sorted(numbers) 21 | # O(n) 22 | max_diff = 0 23 | for prev, cur in zip(numbers, numbers[1:]): 24 | diff = abs(cur - prev) 25 | max_diff = max(max_diff, diff) 26 | 27 | return max_diff 28 | 29 | 30 | def test_max_gap(): 31 | assert max_gap([1]) == 0 32 | assert max_gap([1, 10]) == 9 33 | assert max_gap([7, 5]) == 2 34 | assert max_gap([1, 10, 5]) == 5 35 | -------------------------------------------------------------------------------- /challenges/backtracking/combinations.py: -------------------------------------------------------------------------------- 1 | def combinations(n, k): 2 | """ 3 | Given two integers n and k, return all possible combinations of k numbers 4 | out of 1 2 3 ... n. 5 | 6 | n = 4 7 | k = 2 8 | 9 | [ 10 | [1,2], 11 | [1,3], 12 | [1,4], 13 | [2,3], 14 | [2,4], 15 | [3,4], 16 | ] 17 | """ 18 | def find_combination(combination, nums): 19 | if len(combination) >= k: 20 | yield combination 21 | return 22 | 23 | for i, n in enumerate(nums): 24 | for c in find_combination(combination + [n], nums[i+1:]): 25 | yield c 26 | 27 | return list(find_combination([], range(1, n + 1))) 28 | 29 | 30 | def test_combinations(): 31 | assert combinations(4, 2) == [ 32 | [1, 2], 33 | [1, 3], 34 | [1, 4], 35 | [2, 3], 36 | [2, 4], 37 | [3, 4], 38 | ] 39 | -------------------------------------------------------------------------------- /challenges/backtracking/unique_permutations.py: -------------------------------------------------------------------------------- 1 | def permute(numbers): 2 | 3 | def all_permutations(nums): 4 | if len(nums) == 1: 5 | yield nums 6 | 7 | previous_nums = {} 8 | for idx, num in enumerate(nums): 9 | if num not in previous_nums: 10 | previous_nums[num] = True 11 | copy = list(nums) 12 | copy.pop(idx) 13 | 14 | for permutations in all_permutations(copy): 15 | yield [num] + permutations 16 | 17 | return list(all_permutations(numbers)) 18 | 19 | 20 | def test_permutations(): 21 | assert permute([1, 2, 3]) == [ 22 | [1, 2, 3], 23 | [1, 3, 2], 24 | [2, 1, 3], 25 | [2, 3, 1], 26 | [3, 1, 2], 27 | [3, 2, 1], 28 | ] 29 | 30 | 31 | def test_permutations_with_duplicates(): 32 | assert permute([1, 1, 3]) == [ 33 | [1, 1, 3], 34 | [1, 3, 1], 35 | [3, 1, 1], 36 | ] 37 | -------------------------------------------------------------------------------- /challenges/binary_search/power.py: -------------------------------------------------------------------------------- 1 | """ 2 | Implement pow(x, n) % d. 3 | In other words, given x, n and d, 4 | find x^n % d. 5 | """ 6 | 7 | 8 | def custom_pow(x, y): 9 | def multiply(a, b): 10 | return a * b 11 | return reduce(multiply, [x] * y, 1) 12 | 13 | 14 | def find_pow(x, n, d): 15 | if n == 0: 16 | return 1 % d 17 | 18 | if x == 0: 19 | return 0 20 | 21 | n1 = n / 2 22 | n2 = n - n1 23 | 24 | pow1 = custom_pow(x, n1) 25 | if n2 > n1: 26 | pow2 = pow1 * x 27 | else: 28 | pow2 = pow1 29 | 30 | remainder1 = pow1 % d 31 | remainder2 = pow2 % d 32 | 33 | return (remainder1 * remainder2) % d 34 | 35 | 36 | def test_custom_pow(): 37 | assert custom_pow(3, 4) == pow(3, 4) 38 | 39 | 40 | def test_find_pow(): 41 | assert find_pow(2, 3, 3) == 2 42 | assert find_pow(4, 8, 5) == 1 43 | assert find_pow(7, 9, 4) == 3 44 | assert find_pow(0, 0, 1) == 0 45 | assert find_pow(-1, 1, 20) == 19 46 | -------------------------------------------------------------------------------- /challenges/trees/kth_smallest_tree_elem.py: -------------------------------------------------------------------------------- 1 | class TreeNode(): 2 | def __init__(self, x): 3 | self.val = x 4 | self.left = None 5 | self.right = None 6 | 7 | def __repr__(self): 8 | return ''.format(self.val) 9 | 10 | 11 | def inorder(node): 12 | if not node: 13 | return 14 | 15 | for n in inorder(node.left): 16 | yield n 17 | 18 | yield node 19 | 20 | for n in inorder(node.right): 21 | yield n 22 | 23 | 24 | def kth_smallest(root, k): 25 | for pos, node in enumerate(inorder(root), start=1): 26 | if pos == k: 27 | return node 28 | 29 | return None 30 | 31 | 32 | def test_kth_smallest(): 33 | n1 = TreeNode(1) 34 | n2 = TreeNode(2) 35 | n3 = TreeNode(3) 36 | 37 | n2.left = n1 38 | n2.right = n3 39 | 40 | root = n2 41 | assert kth_smallest(root, k=1) == n1 42 | assert kth_smallest(root, k=2) == n2 43 | assert kth_smallest(root, k=3) == n3 44 | -------------------------------------------------------------------------------- /challenges/stack/generate_parentheses.py: -------------------------------------------------------------------------------- 1 | def matching_parens(a, b): 2 | return ( 3 | (a == '(' and b == ')') or 4 | (a == '{' and b == '}') or 5 | (a == '[' and b == ']') 6 | ) 7 | 8 | 9 | def has_valid_parens(text): 10 | stack = [] 11 | 12 | for c in text: 13 | if c in ['(', '[', '{']: 14 | stack.append(c) 15 | elif c in [')', ']', '}']: 16 | closing_parens = c 17 | 18 | try: 19 | opening_parens = stack.pop() 20 | except IndexError: 21 | return False 22 | 23 | if not matching_parens(opening_parens, closing_parens): 24 | return False 25 | 26 | return len(stack) == 0 27 | 28 | 29 | def test_paren(): 30 | assert not has_valid_parens('([') 31 | assert has_valid_parens('([{}])') 32 | assert has_valid_parens('(([]){})') 33 | assert not has_valid_parens('([)') 34 | assert not has_valid_parens('((((([{()}[]]]{{{[]}}})))))') 35 | -------------------------------------------------------------------------------- /challenges/string/palindrome_string.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a string, determine if it is a palindrome, considering only alphanumeric 3 | characters and ignoring cases. 4 | 5 | For example 'A man, a plan, a canal: Panama' is a palindrome 6 | while 'race a car' is not. 7 | """ 8 | 9 | 10 | def is_palindrome(sentence): 11 | def normalize(): 12 | alphabet = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 13 | 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 14 | 'w', 'x', 'y', 'z'] 15 | digits = [str(digit) for digit in range(0, 10)] 16 | alphanum = set(alphabet + digits) 17 | 18 | for c in sentence: 19 | c = c.lower() 20 | if c in alphanum: 21 | yield c 22 | 23 | normalized_sentence = list(normalize()) 24 | return normalized_sentence == list(reversed(normalized_sentence)) 25 | 26 | 27 | def test_is_palindrome(): 28 | assert is_palindrome("A man, a plan, a canal: Panama") 29 | -------------------------------------------------------------------------------- /challenges/array/min_steps_infinite_grid.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | def cover_points(points): 4 | """ 5 | Find the minimum number of steps to cover a sequence of points in the 6 | order they need to be covered. 7 | The points are in an infinite 2D grid and one can move in any of the 8 | 8 directions. 9 | 10 | points = [(0,0, (2,2), (0,5)] 11 | 12 | 0 1 2 13 | 0 * 14 | 1 \ 15 | 2 * 16 | 3 / 17 | 4 | 18 | 5 * 19 | """ 20 | moves = 0 21 | for prev_point, point in zip(points, points[1:]): 22 | prev_x, prev_y = prev_point 23 | x, y = point 24 | 25 | x_dist = abs(x - prev_x) 26 | y_dist = abs(y - prev_y) 27 | moves += max(x_dist, y_dist) 28 | 29 | return moves 30 | 31 | 32 | def test_min_steps_cover_points(): 33 | assert cover_points([(0, 0), (-2, 2)]) == 2 34 | assert cover_points([(0, 0), (-4, 2), (0, -2)]) == 8 35 | assert cover_points([(0, 0), (1, 1), (5, 2)]) == 5 36 | -------------------------------------------------------------------------------- /challenges/hashing/two_sum.py: -------------------------------------------------------------------------------- 1 | def two_sum(numbers, target): 2 | lookup = {} 3 | for idx, num in enumerate(numbers, start=1): 4 | needed_summand = target - num 5 | # print('Num {} -> need summand {}'.format(num, needed_summand), lookup) 6 | 7 | if needed_summand in lookup: 8 | return [lookup[needed_summand], idx] 9 | elif num not in lookup: 10 | lookup[num] = idx 11 | 12 | return [] 13 | 14 | 15 | def test_two_sum(): 16 | assert two_sum([2, 7, 11, 15], 9) == [1, 2] 17 | assert two_sum([2, 7, 11, 15], 7) == [] 18 | assert two_sum([2, 7, 11, 15], 8) == [] 19 | assert two_sum([2, 7, 11, 15], 17) == [1, 4] 20 | 21 | 22 | def test_two_sum_multiple_possiblities(): 23 | assert two_sum([1, 7, 8, 2], 9) == [1, 3] 24 | 25 | def test_two_sum_negative(): 26 | nums = [4, 7, -4, 2, 2, 2, 3, -5, -3, 9, -4, 9, -7, 27 | 7, -1, 9, 9, 4, 1, -4, -2, 3, -3, -5, 4, -7, 28 | 7, 9, -4, 4, -8] 29 | 30 | assert two_sum(nums, -3) == [4, 8] 31 | -------------------------------------------------------------------------------- /challenges/trees/symmetric_binary_tree.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a binary tree, check whether it 3 | is a mirror of itself(symmetric around its center) 4 | 5 | 6 | Symmetric binary tree. 7 | 8 | 1 9 | / \ 10 | 2 2 11 | / \ / \ 12 | 3 4 4 3 13 | 14 | Not symmetric binary tree. 15 | 16 | 1 17 | / \ 18 | 2 2 19 | \ \ 20 | 3 3 21 | """ 22 | 23 | 24 | class TreeNode(): 25 | def __init__(self, x): 26 | self.val = x 27 | self.left = None 28 | self.right = None 29 | 30 | def __repr__(self): 31 | return ''.format(self.val) 32 | 33 | 34 | def is_symmetric(root): 35 | 36 | def compare_nodes(n1, n2): 37 | if n1 is None or n2 is None: 38 | return n1 == n2 39 | elif n1.left != n2.right: 40 | return False 41 | else: 42 | return compare_nodes(n1.left, n2.right) and compare_nodes(n1.right, n2.left) 43 | 44 | return root is None or compare_nodes(root.left, root.right) 45 | -------------------------------------------------------------------------------- /challenges/stack/simplify_directory_path.py: -------------------------------------------------------------------------------- 1 | import collections 2 | 3 | 4 | def simplify_unix_path(path): 5 | dotstack = collections.deque() 6 | segments = path.split('/') 7 | simplified_segments = collections.deque() 8 | 9 | while len(segments) > 0: 10 | segment = segments.pop() 11 | if segment == '.' or segment == '': 12 | continue 13 | elif segment == '..': 14 | dotstack.append(segment) 15 | else: 16 | try: 17 | dotstack.pop() 18 | except IndexError: 19 | simplified_segments.appendleft(segment) 20 | 21 | return '/' + '/'.join(simplified_segments) 22 | 23 | 24 | def test_path(): 25 | assert simplify_unix_path('/a/../b/../') == '/' 26 | assert simplify_unix_path('/a/./b/../../c') == '/c' 27 | assert simplify_unix_path('/') == '/' 28 | assert simplify_unix_path('/a/../b/../c/../d') == '/d' 29 | assert simplify_unix_path('/var///log') == '/var/log' 30 | assert simplify_unix_path('/var/log/') == '/var/log' 31 | -------------------------------------------------------------------------------- /challenges/trees/preorder_traversal.py: -------------------------------------------------------------------------------- 1 | class TreeNode(): 2 | def __init__(self, x): 3 | self.val = x 4 | self.left = None 5 | self.right = None 6 | 7 | def __repr__(self): 8 | return ''.format(self.val) 9 | 10 | 11 | def preorder(root): 12 | if root is None: 13 | return 14 | 15 | yield root.val 16 | 17 | for n in preorder(root.left): 18 | yield n 19 | 20 | for n in preorder(root.right): 21 | yield n 22 | 23 | 24 | def create_test_tree(): 25 | n1 = TreeNode(1) 26 | n2 = TreeNode(2) 27 | n3 = TreeNode(3) 28 | n4 = TreeNode(4) 29 | n5 = TreeNode(5) 30 | n6 = TreeNode(6) 31 | n7 = TreeNode(7) 32 | 33 | n4.left = n2 34 | n4.right = n6 35 | 36 | n2.left = n1 37 | n2.right = n3 38 | 39 | n6.left = n5 40 | n6.right = n7 41 | 42 | root = n4 43 | 44 | return root 45 | 46 | 47 | def test_postorder_larger_tree(): 48 | root = create_test_tree() 49 | assert list(preorder(root)) == [4, 2, 1, 3, 6, 5, 7] 50 | -------------------------------------------------------------------------------- /challenges/linked_list/reverse_linked_list.py: -------------------------------------------------------------------------------- 1 | class ListNode: 2 | def __init__(self, x): 3 | self.val = x 4 | self.next = None 5 | 6 | def __repr__(self): 7 | return ''.format(self.val, self.next) 8 | 9 | def values(self): 10 | cursor = self 11 | while cursor is not None: 12 | yield cursor.val 13 | cursor = cursor.next 14 | 15 | 16 | def reverse_linked_list(head): 17 | prev = head 18 | cur = head.next 19 | head.next = None 20 | 21 | while cur is not None: 22 | tmp = cur.next 23 | cur.next = prev 24 | prev = cur 25 | cur = tmp 26 | 27 | return prev 28 | 29 | 30 | def test_reverse_linked_list(): 31 | n1 = ListNode(0) 32 | n2 = ListNode(1) 33 | n3 = ListNode(2) 34 | n4 = ListNode(3) 35 | 36 | n1.next = n2 37 | n2.next = n3 38 | n3.next = n4 39 | 40 | linked_list = n1 41 | reversed_list = reverse_linked_list(linked_list) 42 | 43 | assert list(reversed_list.values()) == [3, 2, 1, 0] 44 | -------------------------------------------------------------------------------- /challenges/trees/postorder_traversal.py: -------------------------------------------------------------------------------- 1 | class TreeNode(): 2 | def __init__(self, x): 3 | self.val = x 4 | self.left = None 5 | self.right = None 6 | 7 | def __repr__(self): 8 | return ''.format(self.val) 9 | 10 | 11 | def postorder(root): 12 | if root is None: 13 | return 14 | 15 | for n in postorder(root.left): 16 | yield n 17 | 18 | for n in postorder(root.right): 19 | yield n 20 | 21 | yield root.val 22 | 23 | 24 | def create_test_tree(): 25 | n1 = TreeNode(1) 26 | n2 = TreeNode(2) 27 | n3 = TreeNode(3) 28 | n4 = TreeNode(4) 29 | n5 = TreeNode(5) 30 | n6 = TreeNode(6) 31 | n7 = TreeNode(7) 32 | 33 | n4.left = n2 34 | n4.right = n6 35 | 36 | n2.left = n1 37 | n2.right = n3 38 | 39 | n6.left = n5 40 | n6.right = n7 41 | 42 | root = n4 43 | 44 | return root 45 | 46 | 47 | def test_postorder_larger_tree(): 48 | root = create_test_tree() 49 | assert list(postorder(root)) == [1, 3, 2, 5, 7, 6, 4] 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | *.swo 9 | *.swp 10 | 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *,cover 48 | .hypothesis/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | 57 | # Sphinx documentation 58 | docs/_build/ 59 | 60 | # PyBuilder 61 | target/ 62 | 63 | #Ipython Notebook 64 | .ipynb_checkpoints 65 | -------------------------------------------------------------------------------- /challenges/array/pascal_triangle.py: -------------------------------------------------------------------------------- 1 | def pascal_triangle(num_rows): 2 | """ 3 | Calculate the pascal triangle for a given number of rows. 4 | """ 5 | def get_elem(arr, idx, default): 6 | if idx < 0 or idx >= len(arr): 7 | return default 8 | else: 9 | return arr[idx] 10 | 11 | def generate_columns(previous_row, num_columns): 12 | for col_idx in range(0, num_columns): 13 | yield ( 14 | get_elem(previous_row, col_idx-1, 0) + 15 | get_elem(previous_row, col_idx, 0) 16 | ) 17 | 18 | if num_rows == 0: 19 | return [] 20 | 21 | rows = [[1]] 22 | 23 | for row_idx in range(1, num_rows): 24 | prev = rows[row_idx-1] 25 | rows.append(list(generate_columns(prev, row_idx+1))) 26 | 27 | return rows 28 | 29 | 30 | def test_pascal_triangle(): 31 | triangle = pascal_triangle(5) 32 | assert triangle == [ 33 | [1], 34 | [1, 1], 35 | [1, 2, 1], 36 | [1, 3, 3, 1], 37 | [1, 4, 6, 4, 1] 38 | ] 39 | -------------------------------------------------------------------------------- /challenges/math/excel_column_number.py: -------------------------------------------------------------------------------- 1 | COLUMN_ENCODING = { 2 | 'A': 1, 3 | 'B': 2, 4 | 'C': 3, 5 | 'D': 4, 6 | 'E': 5, 7 | 'F': 6, 8 | 'G': 7, 9 | 'H': 8, 10 | 'I': 9, 11 | 'J': 10, 12 | 'K': 11, 13 | 'L': 12, 14 | 'M': 13, 15 | 'N': 14, 16 | 'O': 15, 17 | 'P': 16, 18 | 'Q': 17, 19 | 'R': 18, 20 | 'S': 19, 21 | 'T': 20, 22 | 'U': 21, 23 | 'V': 22, 24 | 'W': 23, 25 | 'X': 24, 26 | 'Y': 25, 27 | 'Z': 26 28 | } 29 | 30 | 31 | def column_number(column_name): 32 | """ 33 | Given a column title as appears in an Excel sheet, 34 | return its corresponding column number. 35 | """ 36 | num = 0 37 | for c, n in zip(column_name, reversed(range(0, len(column_name)))): 38 | num += COLUMN_ENCODING[c] * pow(len(COLUMN_ENCODING), n) 39 | 40 | return num 41 | 42 | 43 | def test_column_number(): 44 | assert column_number('A') == 1 45 | assert column_number('Z') == 26 46 | assert column_number('AA') == 27 47 | assert column_number('AB') == 28 48 | assert column_number('AAB') == 704 49 | -------------------------------------------------------------------------------- /challenges/array/wave_array.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given an array of integers, sort the array into a wave 3 | like array and return it. 4 | 5 | Arrange the elements into a sequence such that `a1 >= a2 <= a3 >= a4 <= a5` 6 | """ 7 | 8 | 9 | def wave(numbers): 10 | # O(n log n) 11 | sorted_numbers = sorted(numbers) 12 | 13 | # Go through numbers 14 | # On uneven idx choose the previous number 15 | # On even idx choose the next number 16 | 17 | for idx in range(0, len(numbers)): 18 | if idx % 2 == 0: 19 | if idx == len(numbers) - 1: 20 | yield sorted_numbers[idx] 21 | else: 22 | yield sorted_numbers[idx+1] 23 | else: 24 | yield sorted_numbers[idx-1] 25 | 26 | 27 | def test_wave(): 28 | numbers = [4, 3, 2, 1] 29 | assert list(wave(numbers)) == [2, 1, 4, 3] 30 | 31 | 32 | def test_longer_wave(): 33 | numbers = [8, 4, 6, 7, 3, 2, 1, 5] 34 | assert list(wave(numbers)) == [2, 1, 4, 3, 6, 5, 8, 7] 35 | 36 | 37 | def test_uneven_wave(): 38 | numbers = [4, 3, 2, 1, 5] 39 | assert list(wave(numbers)) == [2, 1, 4, 3, 5] 40 | -------------------------------------------------------------------------------- /challenges/greedy/mice_holes.py: -------------------------------------------------------------------------------- 1 | def assign_mice_to_holes(mice, holes): 2 | """ 3 | There are N mice and N holes are placed in a straight line. 4 | Each hole can accomodate only 1 mouse. 5 | A mouse can stay at his position, move one step right from x to x + 1, 6 | or move one step left from x to x - 1. Any of these moves 7 | consumes 1 minute. 8 | Assign mice to holes so that the time when the last mouse gets inside 9 | a hole is minimized. 10 | """ 11 | mice = sorted(mice) 12 | holes = sorted(holes) 13 | 14 | max_time = 0 15 | 16 | for m, h in zip(mice, holes): 17 | max_time = max(max_time, abs(m - h)) 18 | 19 | return max_time 20 | 21 | 22 | def test_assign_mices_to_holes(): 23 | assert assign_mice_to_holes([4, -4, 2], [4, 0, 5]) == 4 24 | assert assign_mice_to_holes([-4, 2, 4], [-20, -18, -19]) == 22 25 | assert assign_mice_to_holes([ 26 | 35, 72, -95, -54, 89, 5, 23, 13, 38, 20, 66, 27 | 26, 63, 44, -50, -23, -85, -72, -47, -96 28 | ], [ 29 | 24, 45, -36, 26, -43, 80, -45, 19, -93, -92, -70, 30 | -54, -63, -9, -62, 36, 96, -68, -75, -57 31 | ]) == 63 32 | -------------------------------------------------------------------------------- /challenges/math/factorial_trailing_zeros.py: -------------------------------------------------------------------------------- 1 | """ 2 | Find the trailing zeros in factorial. 3 | Given an integer n, return the number of trailing zeroes in n!. 4 | """ 5 | 6 | 7 | def factorial(n): 8 | "Time complexity is O(n)" 9 | result = n 10 | while n > 1: 11 | n -= 1 12 | result *= n 13 | return result 14 | 15 | 16 | def trailing_zeros(n): 17 | "Time complexity is O(n) in worst case" 18 | zeros = 0 19 | while n % 10 == 0: 20 | n /= 10 21 | zeros += 1 22 | return zeros 23 | 24 | 25 | def trailing_zeros_factorial(n): 26 | zeros = 0 27 | exponent = 1 28 | 29 | while n / pow(5, exponent) > 0: 30 | zeros += n / pow(5, exponent) 31 | exponent += 1 32 | 33 | return zeros 34 | 35 | 36 | def test_factorial(): 37 | assert factorial(5) == 5 * 4 * 3 * 2 * 1 38 | 39 | 40 | def test_trailing_zeros(): 41 | assert trailing_zeros(120) == 1 42 | assert trailing_zeros(500) == 2 43 | assert trailing_zeros(factorial(5)) == 1 44 | assert trailing_zeros_factorial(5) == 1 45 | assert trailing_zeros_factorial(7927) == 1979 46 | assert trailing_zeros_factorial(4617) == 1151 47 | -------------------------------------------------------------------------------- /challenges/stack/nearest_smallest_element.py: -------------------------------------------------------------------------------- 1 | def nearest_smallest_element(arr): 2 | """ 3 | Given an array arr, find the nearest smaller element for each element. 4 | The index of the smaller element must be smaller than the current element. 5 | """ 6 | smaller_numbers = [] 7 | 8 | def nearest(n): 9 | def find_previous_num(): 10 | for previous_num in reversed(smaller_numbers): 11 | if previous_num < n: 12 | return previous_num 13 | return -1 14 | 15 | def append_smaller_number_before_preceding_big(n): 16 | while len(smaller_numbers) > 0 and smaller_numbers[-1] > n: 17 | smaller_numbers.pop() 18 | smaller_numbers.append(n) 19 | 20 | previous_num = find_previous_num() 21 | append_smaller_number_before_preceding_big(n) 22 | return previous_num 23 | 24 | return [nearest(n) for n in arr] 25 | 26 | 27 | def test_nearest_smallest_element(): 28 | assert nearest_smallest_element([4, 5, 2, 10, 12, 11] ) == [-1, 4, -1, 2, 10, 10] 29 | assert nearest_smallest_element([4]) == [-1] 30 | assert nearest_smallest_element([]) == [] 31 | -------------------------------------------------------------------------------- /challenges/string/zigzag_string.py: -------------------------------------------------------------------------------- 1 | """" 2 | The string 'ABCD' is written in a zigzag pattern on a given number 3 | of rows like this. 4 | 5 | A....C.. 6 | ...B....D 7 | 8 | Write the code that will take a string and make this conversion given a 9 | number of rows. 10 | """ 11 | 12 | 13 | def convert_text_zigzag_pattern(text, num_rows): 14 | if num_rows == 1: 15 | return text 16 | 17 | def zigzag(): 18 | for i in xrange(0, num_rows): 19 | yield i 20 | 21 | for i in reversed(xrange(1, num_rows - 1)): 22 | yield i 23 | 24 | for i in zigzag(): 25 | yield i 26 | 27 | def distribute_chars_on_rows(): 28 | rows = [[] for _ in range(0, num_rows)] 29 | for char, row_idx in zip(text, zigzag()): 30 | arr = rows[row_idx] 31 | arr.append(char) 32 | return [''.join(r) for r in rows] 33 | 34 | return ''.join(distribute_chars_on_rows()) 35 | 36 | 37 | def test_convert_text_zigzag_pattern(): 38 | assert convert_text_zigzag_pattern("HEYMAN", 1) == "HEYMAN" 39 | assert convert_text_zigzag_pattern("PAYPALISHIRING", 3) == "PAHNAPLSIIGYIR" 40 | assert convert_text_zigzag_pattern("ABCD", 2) == "ACBD" 41 | -------------------------------------------------------------------------------- /challenges/trees/invert_binary_tree.py: -------------------------------------------------------------------------------- 1 | class TreeNode(): 2 | def __init__(self, x): 3 | self.val = x 4 | self.left = None 5 | self.right = None 6 | 7 | def __repr__(self): 8 | return ''.format(self.val) 9 | 10 | 11 | def invert_tree(node): 12 | """ 13 | Swap the left with the right subtree to invert the binary tree 14 | """ 15 | if node is None: 16 | return node 17 | 18 | node.left = invert_tree(node.left) 19 | node.right = invert_tree(node.right) 20 | 21 | node.left, node.right = node.right, node.left 22 | 23 | return node 24 | 25 | 26 | def test_invert_tree(): 27 | n1 = TreeNode(1) 28 | n2 = TreeNode(2) 29 | n3 = TreeNode(3) 30 | n4 = TreeNode(4) 31 | n5 = TreeNode(5) 32 | n6 = TreeNode(6) 33 | n7 = TreeNode(7) 34 | 35 | n1.left = n2 36 | n1.right = n3 37 | 38 | n2.left = n4 39 | n2.right = n5 40 | 41 | n3.left = n6 42 | n3.right = n7 43 | 44 | root = invert_tree(n1) 45 | 46 | assert root == n1 47 | assert root.left == n3 48 | assert root.right == n2 49 | 50 | assert n3.left == n7 51 | assert n3.right == n6 52 | 53 | assert n2.left == n5 54 | assert n2.right == n4 55 | -------------------------------------------------------------------------------- /challenges/array/rotate_matrix.py: -------------------------------------------------------------------------------- 1 | def rotate_matrix(matrix): 2 | """ 3 | Rotate the n x n matrix 90 degrees clockwise. 4 | 5 | Given the original matrix. 6 | 7 | initial_matrix = [ 8 | [1, 2], 9 | [3, 4] 10 | ] 11 | 12 | The rotated matrix should look like this. 13 | 14 | rotated_matrix = [ 15 | [3, 1], 16 | [4, 2] 17 | ] 18 | """ 19 | n = len(matrix) 20 | new_matrix = [[0] * n for i in range(0, n)] 21 | 22 | def calculate_new_matrix_index(old_y, old_x): 23 | return (old_x, n - 1 - old_y) 24 | 25 | for y in range(0, n): 26 | for x in range(0, n): 27 | new_y, new_x = calculate_new_matrix_index(y, x) 28 | new_matrix[new_y][new_x] = matrix[y][x] 29 | 30 | return new_matrix 31 | 32 | 33 | def test_rotate_matrix(): 34 | initial_matrix = [ 35 | [1, 2, 3, 4], 36 | [5, 6, 7, 8], 37 | [9, 10, 11, 12], 38 | [13, 14, 15, 16] 39 | ] 40 | 41 | rotated_matrix = [ 42 | [13, 9, 5, 1], 43 | [14, 10, 6, 2], 44 | [15, 11, 7, 3], 45 | [16, 12, 8, 4] 46 | ] 47 | assert rotate_matrix(initial_matrix) == rotated_matrix 48 | -------------------------------------------------------------------------------- /challenges/array/find_permutation.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a positive integer n and a string s consisting only letters D or I, 3 | you have to find any permutation of first n positive integer that satisfy 4 | the given input string. 5 | 6 | D means the next number is smaller, while I means the next number is greater. 7 | """ 8 | 9 | import collections 10 | 11 | 12 | def find_permutation(s, n): 13 | choices = collections.deque(range(1, n + 1)) 14 | 15 | def next_is_smaller(c): 16 | return c == 'D' 17 | 18 | def next_is_greater(c): 19 | return c == 'I' 20 | 21 | for condition in s: 22 | if next_is_smaller(condition): 23 | yield choices.pop() 24 | elif next_is_greater(condition): 25 | yield choices.popleft() 26 | 27 | yield choices.pop() 28 | 29 | 30 | def test_permutations(): 31 | assert list(find_permutation('IDIDI', 6)) == [1, 6, 2, 5, 3, 4] 32 | assert list(find_permutation('I', 2)) == [1, 2] 33 | assert list(find_permutation('ID', 3)) == [1, 3, 2] 34 | assert list(find_permutation('IDD', 4)) == [1, 4, 3, 2] 35 | assert list(find_permutation('IDDI', 5)) == [1, 5, 4, 2, 3] 36 | assert list(find_permutation('DIDI', 5)) == [5, 1, 4, 2, 3] 37 | assert list(find_permutation('IDDID', 6)) == [1, 6, 5, 2, 4, 3] 38 | -------------------------------------------------------------------------------- /challenges/linked_list/list_cycle.py: -------------------------------------------------------------------------------- 1 | class ListNode: 2 | def __init__(self, x): 3 | self.val = x 4 | self.next = None 5 | 6 | def __repr__(self): 7 | return ''.format(self.val, self.next) 8 | 9 | 10 | class LinkedList(ListNode): 11 | def __init__(self, arr): 12 | 13 | nodes = [ListNode(v) for v in arr] 14 | for i in range(1, len(nodes)): 15 | nodes[i-1].next = nodes[i] 16 | 17 | head = nodes[0] 18 | 19 | self.val = head.val 20 | self.next = head.next 21 | 22 | 23 | def detect_cycle(head): 24 | node_lookup = {} 25 | 26 | cursor = head 27 | while cursor: 28 | if id(cursor.next) in node_lookup: 29 | return cursor.next 30 | 31 | node_lookup[id(cursor)] = cursor 32 | cursor = cursor.next 33 | 34 | print(len(node_lookup)) 35 | 36 | return None 37 | 38 | 39 | def test_small_cycle(): 40 | n1 = ListNode(1) 41 | n2 = ListNode(2) 42 | n3 = ListNode(3) 43 | n4 = ListNode(4) 44 | 45 | n1.next = n2 46 | n2.next = n3 47 | n3.next = n4 48 | n4.next = n3 # introduce loop 49 | 50 | assert detect_cycle(n1) == n3 51 | 52 | 53 | def test_no_cycle(): 54 | large_list = LinkedList(range(0, 100000)) 55 | assert detect_cycle(large_list) is None 56 | -------------------------------------------------------------------------------- /challenges/two_pointers/diffk.py: -------------------------------------------------------------------------------- 1 | def diff_possible(numbers, k): 2 | """ 3 | Given a list of sorted integers and a non negative 4 | integer k, find if there exists 2 indicies i and j 5 | such that A[i] - A[j] = k, i != j 6 | """ 7 | if k < 0: 8 | raise ValueError('k can not be non negative') 9 | 10 | # Find k since as long as i is not larger than k 11 | # we do not even need to compare 12 | if numbers[-1] < k: 13 | return False 14 | 15 | start_i = 0 16 | while start_i < len(numbers): 17 | if numbers[start_i] >= k: 18 | break 19 | else: 20 | start_i += 1 21 | 22 | for i in range(start_i, len(numbers)): 23 | needed_num = numbers[i] - k 24 | for j in reversed(range(0, i)): 25 | if numbers[j] == needed_num: 26 | return True 27 | elif numbers[j] < needed_num: 28 | # All hope is lost, we can never reach k again 29 | break 30 | return False 31 | 32 | 33 | def test_diff_possible(): 34 | assert diff_possible([0, 1, 1, 5, 6, 7, 8, 12, 22], 10) 35 | assert diff_possible([1, 3, 5], 4) 36 | assert not diff_possible([1, 3, 5], 3) 37 | assert diff_possible([1, 2, 10, 15], 5) 38 | assert not diff_possible([1, 2, 2], 5) 39 | -------------------------------------------------------------------------------- /challenges/array/rearrange_array.py: -------------------------------------------------------------------------------- 1 | def rearrange_array(arr): 2 | """ 3 | Rearrange a given array so that arr[i] becomes arr[arr[i]] 4 | with O(1) extra space. The values in the array will become the new indizes. 5 | 6 | Time complexity: O(n + n) 7 | Space complexity: O(1) 8 | """ 9 | n = len(arr) 10 | 11 | def is_encoded(num): 12 | return num >= n 13 | 14 | def encode(original_value, new_value): 15 | return n + n * original_value + new_value 16 | 17 | def decode(num): 18 | return ((num / n) - 1, num % n) 19 | 20 | def decode_new(num): 21 | return decode(num)[1] 22 | 23 | def decode_original(num): 24 | if is_encoded(num): 25 | return decode(num)[0] 26 | else: 27 | return num 28 | 29 | for i in range(0, len(arr)): 30 | new_value = decode_original(arr[arr[i]]) 31 | arr[i] = encode(arr[i], new_value) 32 | 33 | for i in range(0, len(arr)): 34 | arr[i] = decode_new(arr[i]) 35 | 36 | return arr 37 | 38 | 39 | def test_rearrange_array(): 40 | assert rearrange_array([0, 1]) == [0, 1] 41 | assert rearrange_array([1, 0]) == [0, 1] 42 | assert rearrange_array([2, 0, 1, 4, 3, 6, 5]) == [1, 2, 0, 3, 4, 5, 6] 43 | assert rearrange_array([0]) == [0] 44 | assert rearrange_array([]) == [] 45 | -------------------------------------------------------------------------------- /challenges/trees/sorted_array_balanced_bst.py: -------------------------------------------------------------------------------- 1 | class TreeNode(): 2 | def __init__(self, x): 3 | self.val = x 4 | self.left = None 5 | self.right = None 6 | 7 | def __repr__(self): 8 | return ''.format(self.val) 9 | 10 | 11 | def sorted_array_to_bst(arr): 12 | """ 13 | Given a sorted array turn it into a balanced binary tree 14 | """ 15 | if len(arr) == 0: 16 | return None 17 | 18 | mid = len(arr) / 2 19 | node = TreeNode(arr[mid]) 20 | node.left = sorted_array_to_bst(arr[:mid]) 21 | node.right = sorted_array_to_bst(arr[mid+1:]) 22 | 23 | return node 24 | 25 | 26 | def test_uneven_bst(): 27 | nums = [1, 2, 3] 28 | bst = sorted_array_to_bst(nums) 29 | 30 | assert bst.val == 2 31 | assert bst.left.val == 1 32 | assert bst.right.val == 3 33 | 34 | 35 | def test_is_bst(): 36 | nums = [1, 2, 3, 4, 5, 6] 37 | bst = sorted_array_to_bst(nums) 38 | 39 | assert bst.val == 4 40 | 41 | n2 = bst.left 42 | n6 = bst.right 43 | 44 | assert n2.val == 2 45 | assert n2.left.val == 1 46 | assert n2.right.val == 3 47 | 48 | assert n6.val == 6 49 | assert n6.left.val == 5 50 | assert n6.right is None 51 | 52 | 53 | def test_single_node_bst(): 54 | nums = [1] 55 | bst = sorted_array_to_bst(nums) 56 | assert bst.val == 1 57 | -------------------------------------------------------------------------------- /challenges/string/compare_version_numbers.py: -------------------------------------------------------------------------------- 1 | """ 2 | Compare version strings are non-empty and contain only digits 3 | and the . character. 4 | 5 | Example of ordering: 6 | 0.1 < 1.1 < 1.2 < 1.13 < 1.13.4 7 | """ 8 | 9 | 10 | def compare_version(version1, version2): 11 | 12 | def revisions(v): 13 | if len(v.strip()) == 0: 14 | return [0] 15 | 16 | return [int(digits) for digits in v.split('.')] 17 | 18 | def ensure_revision_length(r, length): 19 | return r + (length - len(r)) * [0] 20 | 21 | revisions1 = revisions(version1) 22 | revisions2 = revisions(version2) 23 | 24 | max_len = max(len(revisions1), len(revisions2)) 25 | revisions1 = ensure_revision_length(revisions1, max_len) 26 | revisions2 = ensure_revision_length(revisions2, max_len) 27 | 28 | for r1, r2 in zip(revisions1, revisions2): 29 | if r1 > r2: 30 | return 1 31 | elif r1 < r2: 32 | return -1 33 | 34 | return 0 35 | 36 | 37 | def test_compare_version(): 38 | assert compare_version('1', '1.0') == 0 39 | assert compare_version('1', '') == 1 40 | assert compare_version('1', '1') == 0 41 | assert compare_version('1.1', '1') == 1 42 | assert compare_version('0.1', '1.1') == -1 43 | assert compare_version('1.13', '1.13.4') == -1 44 | assert compare_version('2.5', '2.5.1') == -1 45 | -------------------------------------------------------------------------------- /challenges/string/integer_to_roman.py: -------------------------------------------------------------------------------- 1 | 2 | def int_to_roman(num): 3 | symbols = [] 4 | roman_literals = [ 5 | (1000, 'M'), 6 | (500, 'D'), 7 | (100, 'C'), 8 | (50, 'L'), 9 | (10, 'X'), 10 | (5, 'V'), 11 | (1, 'I') 12 | ] 13 | 14 | for i in range(0, len(roman_literals)): 15 | value, symbol = roman_literals[i] 16 | 17 | count = num / value 18 | symbols += count * [symbol] 19 | num = num % value 20 | 21 | numeral = ''.join(symbols) 22 | print(numeral) 23 | numeral = numeral.replace('DCCCC', 'CM') # 900 24 | numeral = numeral.replace('CCCC', 'CD') # 400 25 | 26 | numeral = numeral.replace('LXXXX', 'XC') # 90 27 | numeral = numeral.replace('XXXX', 'XL') # 40 28 | 29 | numeral = numeral.replace('VIIII', 'IX') # 9 30 | numeral = numeral.replace('IIII', 'IV') # 4 31 | 32 | return numeral 33 | 34 | 35 | def test_int_to_roman(): 36 | assert int_to_roman(1990) == 'MCMXC' 37 | assert int_to_roman(4) == 'IV' 38 | assert int_to_roman(6) == 'VI' 39 | assert int_to_roman(10) == 'X' 40 | assert int_to_roman(9) == 'IX' 41 | assert int_to_roman(1) == 'I' 42 | assert int_to_roman(5) == 'V' 43 | assert int_to_roman(14) == 'XIV' 44 | assert int_to_roman(20) == 'XX' 45 | assert int_to_roman(2014) == 'MMXIV' 46 | assert int_to_roman(1490) == 'MCDXC' 47 | -------------------------------------------------------------------------------- /challenges/backtracking/letter_phone.py: -------------------------------------------------------------------------------- 1 | digit_lookup = { 2 | '0': ['0'], 3 | '1': ['1'], 4 | '2': ['a', 'b', 'c'], 5 | '3': ['d', 'e', 'f'], 6 | '4': ['g', 'h', 'i'], 7 | '5': ['j', 'k', 'l'], 8 | '6': ['m', 'n', 'o'], 9 | '7': ['p', 'q', 'r', 's'], 10 | '8': ['t', 'u', 'v'], 11 | '9': ['w', 'x', 'y', 'z'] 12 | } 13 | 14 | 15 | def letter_combinations(digits): 16 | return list(possible_combination(digits, [])) 17 | 18 | 19 | def possible_combination(digits, combination): 20 | if len(digits) > 0: 21 | digit = digits[0] 22 | 23 | for letter in digit_lookup[digit]: 24 | new_combination = combination + [letter] 25 | for c in possible_combination(digits[1:], new_combination): 26 | yield ''.join(c) 27 | else: 28 | yield ''.join(combination) 29 | 30 | 31 | def test_combination(): 32 | assert letter_combinations("23") == [ 33 | "ad", "ae", "af", "bd", "be", 34 | "bf", "cd", "ce", "cf" 35 | ] 36 | 37 | assert letter_combinations("1589") == [ 38 | "1jtw", "1jtx", "1jty", "1jtz", "1juw", "1jux", "1juy", "1juz", 39 | "1jvw", "1jvx", "1jvy", "1jvz", "1ktw", "1ktx", "1kty", "1ktz", 40 | "1kuw", "1kux", "1kuy", "1kuz", "1kvw", "1kvx", "1kvy", "1kvz", 41 | "1ltw", "1ltx", "1lty", "1ltz", "1luw", "1lux", "1luy", "1luz", 42 | "1lvw", "1lvx", "1lvy", "1lvz" 43 | ] 44 | -------------------------------------------------------------------------------- /challenges/trees/sum_root_leaf_numbers.py: -------------------------------------------------------------------------------- 1 | class TreeNode(): 2 | def __init__(self, x): 3 | self.val = x 4 | self.left = None 5 | self.right = None 6 | 7 | def __repr__(self): 8 | return ''.format(self.val) 9 | 10 | 11 | def sum_root_leaf_paths(root): 12 | paths = root_leaf_paths(root, []) 13 | 14 | def number_from_path(p): 15 | digits = [str(node.val) for node in p] 16 | return int(''.join(digits)) 17 | 18 | numbers = [number_from_path(p) for p in paths] 19 | return sum(numbers) % 1003 20 | 21 | 22 | def root_leaf_paths(node, path): 23 | if node is None: 24 | return 25 | 26 | if node.left is None and node.right is None: 27 | yield path + [node] 28 | 29 | for p in root_leaf_paths(node.left, path + [node]): 30 | yield p 31 | 32 | for p in root_leaf_paths(node.right, path + [node]): 33 | yield p 34 | 35 | 36 | def test_sum_root_leaf_paths(): 37 | n1 = TreeNode(1) 38 | n2 = TreeNode(2) 39 | n3 = TreeNode(3) 40 | n4 = TreeNode(4) 41 | n5 = TreeNode(5) 42 | n6 = TreeNode(6) 43 | 44 | n4.left = n2 45 | n4.right = n6 46 | 47 | n2.left = n1 48 | n2.right = n3 49 | 50 | n6.left = n5 51 | 52 | root = n4 53 | 54 | assert sum_root_leaf_paths(root) == 306 55 | 56 | 57 | def test_sum_root_leaf_paths_single_node(): 58 | assert sum_root_leaf_paths(TreeNode(3)) == 3 59 | -------------------------------------------------------------------------------- /challenges/math/palindrome_integer.py: -------------------------------------------------------------------------------- 1 | """ 2 | Determine whether an integer is a palindrome. Do this without extra space. 3 | 4 | A palindrome integer is an integer x for which reverse(x) = x where reverse(x) 5 | is x with its digit reversed. Negative numbers are not palindromic. 6 | 7 | For example the integer 12321 is palindromic while 12312 is not. 8 | """ 9 | 10 | 11 | def integer_len(n): 12 | length = 1 13 | while n / 10 > 0: 14 | n /= 10 15 | length += 1 16 | return length 17 | 18 | 19 | def is_integer_palindrom(n): 20 | if n < 0: 21 | return False 22 | 23 | length = integer_len(n) 24 | while True: 25 | if length < 2: 26 | return True 27 | 28 | first_digit = n / (pow(10, length - 1)) 29 | last_digit = n % 10 30 | 31 | if first_digit == last_digit: 32 | n -= first_digit * pow(10, length - 1) 33 | n /= 10 34 | length -= 2 35 | else: 36 | return False 37 | 38 | 39 | def test_integer_len(): 40 | assert integer_len(3) == 1 41 | assert integer_len(12345) == 5 42 | 43 | 44 | def test_is_integer_palindrom_for_negative_number(): 45 | assert not is_integer_palindrom(-1) 46 | 47 | 48 | def test_is_integer_palindrom(): 49 | assert not is_integer_palindrom(12341) 50 | assert is_integer_palindrom(123321) 51 | assert is_integer_palindrom(5412145) 52 | assert is_integer_palindrom(11) 53 | -------------------------------------------------------------------------------- /challenges/binary_search/sorted_insert_position.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a sorted array and a target value, return the index if the target is 3 | found. If not, return the index where it would be if it were inserted in order. 4 | 5 | You may assume no duplicates in the array. 6 | """ 7 | 8 | def search_insert(arr, target): 9 | first = 0 10 | last = len(arr) - 1 11 | mid = 0 12 | 13 | while first <= last: 14 | mid = (first + last) / 2 15 | if arr[mid] == target: 16 | return mid 17 | elif arr[mid] < target: 18 | first = mid + 1 19 | elif arr[mid] > target: 20 | last = mid - 1 21 | 22 | insertion_point = mid 23 | while insertion_point < len(arr) and arr[insertion_point] < target: 24 | insertion_point += 1 25 | 26 | return insertion_point 27 | 28 | 29 | def test_binary_search(): 30 | arr = [1, 3, 5, 6] 31 | assert search_insert(arr, 3) == 1 32 | arr = [1, 3, 4, 5, 6] 33 | assert search_insert(arr, 6) == 4 34 | 35 | 36 | def test_insertion(): 37 | arr = [1] 38 | assert search_insert(arr, 2) == 1 39 | 40 | arr = [] 41 | assert search_insert(arr, 2) == 0 42 | 43 | arr = [3] 44 | assert search_insert(arr, 2) == 0 45 | 46 | arr = [1, 3] 47 | assert search_insert(arr, 3) == 1 48 | 49 | arr = [1, 3, 5, 6] 50 | assert search_insert(arr, 2) == 1 51 | 52 | arr = [1, 3, 5, 6] 53 | assert search_insert(arr, 7) == 4 54 | -------------------------------------------------------------------------------- /challenges/stack/min_stack.py: -------------------------------------------------------------------------------- 1 | class MinStack: 2 | """ 3 | Implement a stack that supports push, pop, top and getMin in O(1) time. 4 | """ 5 | def __init__(self): 6 | self.elements = [] 7 | 8 | def push(self, x): 9 | "O(1)" 10 | if len(self.elements) == 0: 11 | self.elements.append((x, x)) 12 | else: 13 | self.elements.append((x, min(x, self.getMin()))) 14 | 15 | def pop(self): 16 | "O(1)" 17 | if len(self.elements) > 0: 18 | return self.elements.pop()[0] 19 | 20 | def top(self): 21 | "O(1)" 22 | if len(self.elements) > 0: 23 | return self.elements[-1][0] 24 | else: 25 | return -1 26 | 27 | def getMin(self): 28 | "O(1)" 29 | if len(self.elements) > 0: 30 | return self.elements[-1][1] 31 | else: 32 | return -1 33 | 34 | 35 | def test_min_stack(): 36 | stack = MinStack() 37 | 38 | stack.pop() 39 | assert stack.top() == -1 40 | assert stack.getMin() == -1 41 | 42 | stack.push(10) 43 | stack.push(3) 44 | assert stack.getMin() == 3 45 | stack.pop() 46 | assert stack.getMin() == 10 47 | 48 | stack.push(5) 49 | assert stack.getMin() == 5 50 | 51 | stack.push(6) 52 | assert stack.getMin() == 5 53 | 54 | stack.push(3) 55 | assert stack.getMin() == 3 56 | stack.pop() 57 | assert stack.getMin() == 5 58 | -------------------------------------------------------------------------------- /challenges/trees/zigzag_level_bst_traversal.py: -------------------------------------------------------------------------------- 1 | import collections 2 | 3 | 4 | class TreeNode(): 5 | def __init__(self, x): 6 | self.val = x 7 | self.left = None 8 | self.right = None 9 | 10 | def __repr__(self): 11 | return ''.format(self.val) 12 | 13 | 14 | def inorder_level(node, level=0): 15 | if not node: 16 | return 17 | 18 | for rslt in inorder_level(node.left, level=level+1): 19 | yield rslt 20 | 21 | yield level, node 22 | 23 | for rslt in inorder_level(node.right, level=level+1): 24 | yield rslt 25 | 26 | 27 | def zigzag(): 28 | yield True 29 | yield False 30 | 31 | 32 | def zigzag_level_order(root): 33 | levels = collections.defaultdict(collections.deque) 34 | 35 | for level, node in inorder_level(root): 36 | if level % 2 == 0: 37 | levels[level].append(node.val) 38 | else: 39 | levels[level].appendleft(node.val) 40 | 41 | for key in levels.keys(): 42 | levels[key] = list(levels[key]) 43 | 44 | return levels.values() 45 | 46 | 47 | def test_zigzag_level_order(): 48 | n3 = TreeNode(3) 49 | n9 = TreeNode(9) 50 | n20 = TreeNode(20) 51 | n15 = TreeNode(15) 52 | n7 = TreeNode(7) 53 | 54 | n3.left = n9 55 | n3.right = n20 56 | n20.left = n15 57 | n20.right = n7 58 | 59 | root = n3 60 | assert zigzag_level_order(root) == [[3], [20, 9], [15, 7]] 61 | -------------------------------------------------------------------------------- /challenges/string/longest_palindromic_substring.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a string S, find the longest palindromic substring in S. 3 | """ 4 | 5 | 6 | import itertools 7 | import collections 8 | 9 | 10 | def is_palindrome(word): 11 | for c1, c2 in itertools.izip(word, reversed(word)): 12 | if c1 != c2: 13 | return False 14 | return True 15 | 16 | 17 | def longest_palindromic_substring(sentence): 18 | reverse_lookup = collections.defaultdict(list) 19 | max_palindrome = sentence[0] 20 | 21 | # build up lookup O(n) 22 | for i in reversed(range(0, len(sentence))): 23 | c = sentence[i] 24 | reverse_lookup[c].append(i) 25 | 26 | # start going through 27 | for i in range(0, len(sentence)): 28 | c1 = sentence[i] 29 | for j in reverse_lookup[c1]: 30 | if j > i and j + 1 - i > len(max_palindrome): 31 | c2 = sentence[j] 32 | if c1 == c2: 33 | word = sentence[i:j+1] 34 | if is_palindrome(word): 35 | max_palindrome = word 36 | 37 | return max_palindrome 38 | 39 | 40 | def test_longest_palindromic_substring(): 41 | lps = longest_palindromic_substring 42 | assert lps('aaaabaaa') == 'aaabaaa' 43 | assert lps('faxalaxabx') == 'axalaxa' 44 | assert lps('hululxlaluh') == 'ulu' 45 | assert is_palindrome('aba') 46 | assert not is_palindrome('abac') 47 | assert lps('aba') == 'aba' 48 | assert lps('ab') == 'a' 49 | -------------------------------------------------------------------------------- /challenges/array/min_steps_infinite_grid.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | typedef std::tuple t_point; 9 | 10 | int dist(t_point p1, t_point p2) { 11 | const auto prev_x = std::get<0>(p1); 12 | const auto prev_y = std::get<1>(p1); 13 | 14 | const auto cur_x = std::get<0>(p2); 15 | const auto cur_y = std::get<1>(p2); 16 | 17 | const auto dist_x = std::abs(cur_x - prev_x); 18 | const auto dist_y = std::abs(cur_y - prev_y); 19 | 20 | return std::max(dist_x, dist_y); 21 | } 22 | 23 | int cover_points(const std::vector points) { 24 | int min_steps {0}; 25 | 26 | for (auto it=points.begin(); it!=points.end(); ++it) { 27 | if(it == points.begin()) continue; 28 | 29 | min_steps += dist(*std::prev(it, 1), *it); 30 | } 31 | return min_steps; 32 | } 33 | 34 | int main() { 35 | std::vector e1 = { 36 | std::make_tuple(0, 0), 37 | std::make_tuple(-2, 2) 38 | }; 39 | assert(cover_points(e1) == 2); 40 | 41 | std::vector e2 = { 42 | std::make_tuple(0, 0), 43 | std::make_tuple(-4, 2), 44 | std::make_tuple(0, -2) 45 | }; 46 | assert(cover_points(e2) == 8); 47 | 48 | std::vector e3 = { 49 | std::make_tuple(0, 0), 50 | std::make_tuple(1, 1), 51 | std::make_tuple(5, 2) 52 | }; 53 | assert(cover_points(e3) == 5); 54 | } 55 | -------------------------------------------------------------------------------- /challenges/graphs/level_order.py: -------------------------------------------------------------------------------- 1 | class TreeNode(): 2 | def __init__(self, x): 3 | self.val = x 4 | self.left = None 5 | self.right = None 6 | 7 | def __repr__(self): 8 | return ''.format(self.val) 9 | 10 | 11 | def level_order(root): 12 | 13 | def level_order_nodes(): 14 | levels = [[root]] 15 | 16 | while True: 17 | next_level = [] 18 | for node in levels[-1]: 19 | if node.left: 20 | next_level.append(node.left) 21 | if node.right: 22 | next_level.append(node.right) 23 | 24 | if len(next_level) > 0: 25 | levels.append(next_level) 26 | else: 27 | break 28 | 29 | return levels 30 | 31 | # Transform nodes to ints 32 | # TODO: This shouldn't be necessary 33 | levels = level_order_nodes() 34 | for level in levels: 35 | for idx, node in enumerate(level): 36 | level[idx] = node.val 37 | 38 | return levels 39 | 40 | 41 | def test_level_order(): 42 | n1 = TreeNode(1) 43 | n2 = TreeNode(2) 44 | n3 = TreeNode(3) 45 | n4 = TreeNode(4) 46 | n5 = TreeNode(5) 47 | n6 = TreeNode(6) 48 | 49 | n4.left = n2 50 | n4.right = n6 51 | n2.left = n1 52 | n2.right = n3 53 | n6.left = n5 54 | root = n4 55 | 56 | assert level_order(root) == [ 57 | [4], 58 | [2, 6], 59 | [1, 3, 5], 60 | ] 61 | -------------------------------------------------------------------------------- /challenges/binary_search/binary_occurrence_search.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | typedef std::vector::size_type idx_t; 7 | 8 | int count_adjacent_same(std::vector elements, idx_t start_idx) { 9 | int count {0}; 10 | 11 | for(auto i = start_idx; i != 0; i--) { 12 | if(elements[i] == elements[start_idx]) { 13 | count += 1; 14 | } else { 15 | break; 16 | } 17 | } 18 | 19 | for(auto i = start_idx+1; i < elements.size(); i++) { 20 | if(elements[i] == elements[start_idx]) { 21 | count += 1; 22 | } else { 23 | break; 24 | } 25 | } 26 | return count; 27 | } 28 | 29 | int count_element(std::vector elements, int element) { 30 | idx_t lower_idx {0}; 31 | idx_t upper_idx {elements.size()}; 32 | 33 | while(upper_idx > lower_idx) { 34 | auto mid_idx = (upper_idx+lower_idx)/2; 35 | auto mid = elements[mid_idx]; 36 | if (mid == element) { 37 | return count_adjacent_same(elements, mid_idx); 38 | } else if (mid < element) { 39 | lower_idx = mid_idx; 40 | } else if (mid > element) { 41 | upper_idx = mid_idx; 42 | } 43 | } 44 | return -1; 45 | } 46 | 47 | int main() { 48 | std::vector e1 = {1, 1, 2, 3, 4, 5, 6}; 49 | assert(count_element(e1, 3) == 1); 50 | 51 | std::vector e2 = {1, 1, 2, 2, 4, 5, 5, 5, 6}; 52 | assert(count_element(e2, 5) == 3); 53 | } 54 | -------------------------------------------------------------------------------- /challenges/trees/identical_binary_tree.py: -------------------------------------------------------------------------------- 1 | class TreeNode(): 2 | def __init__(self, x): 3 | self.val = x 4 | self.left = None 5 | self.right = None 6 | 7 | def __repr__(self): 8 | return ''.format(self.val) 9 | 10 | def __eq__(self, other): 11 | return eq_node(self, other) 12 | 13 | 14 | def is_same_tree(tree_a, tree_b): 15 | "Given two binary trees ensure they have the same nodes." 16 | return compare_recursive(tree_a, tree_b) 17 | 18 | 19 | def compare_recursive(a, b): 20 | if not a and not b: 21 | return True 22 | 23 | if a and not b: 24 | return False 25 | 26 | if not a and b: 27 | return False 28 | 29 | if a.val != b.val: 30 | return False 31 | 32 | return compare_recursive(a.left, b.left) and compare_recursive(a.right, b.right) 33 | 34 | 35 | def test_is_same_tree(): 36 | def create_tree(): 37 | n1 = TreeNode(1) 38 | n2 = TreeNode(2) 39 | n3 = TreeNode(3) 40 | 41 | n1.left = n2 42 | n1.right = n3 43 | return n1 44 | 45 | assert is_same_tree(create_tree(), create_tree()) 46 | 47 | 48 | def test_is_not_same_tree(): 49 | def create_tree_1(): 50 | n1 = TreeNode(1) 51 | n3 = TreeNode(3) 52 | 53 | n1.right = n3 54 | return n1 55 | 56 | def create_tree_2(): 57 | n1 = TreeNode(1) 58 | n2 = TreeNode(2) 59 | 60 | n1.left = n2 61 | return n1 62 | 63 | assert not is_same_tree(create_tree_1(), create_tree_2()) 64 | -------------------------------------------------------------------------------- /challenges/trees/min_tree_depth.py: -------------------------------------------------------------------------------- 1 | class TreeNode(): 2 | def __init__(self, x): 3 | self.val = x 4 | self.left = None 5 | self.right = None 6 | 7 | def __repr__(self): 8 | return ''.format(self.val) 9 | 10 | 11 | def min_depth(node, depth=1): 12 | if node.left is None and node.right is None: 13 | return depth 14 | 15 | if node.left is None: 16 | return min_depth(node.right, depth+1) 17 | 18 | if node.right is None: 19 | return min_depth(node.left, depth+1) 20 | 21 | return min( 22 | min_depth(node.left, depth+1), 23 | min_depth(node.right, depth+1) 24 | ) 25 | 26 | 27 | def create_test_tree(): 28 | n1 = TreeNode(1) 29 | n2 = TreeNode(2) 30 | n3 = TreeNode(3) 31 | n4 = TreeNode(4) 32 | n5 = TreeNode(5) 33 | n6 = TreeNode(6) 34 | 35 | n4.left = n2 36 | n4.right = n6 37 | 38 | n2.left = n1 39 | n2.right = n3 40 | 41 | n6.left = n5 42 | 43 | root = n4 44 | 45 | return root 46 | 47 | 48 | def test_min_depth(): 49 | root = create_test_tree() 50 | assert min_depth(root) == 3 51 | 52 | 53 | def test_min_depth_no_left_side(): 54 | n2 = TreeNode(2) 55 | n3 = TreeNode(3) 56 | 57 | n2.right = n3 58 | root = n2 59 | 60 | assert min_depth(root) == 2 61 | 62 | 63 | def test_min_depth_leaf_node(): 64 | n1 = TreeNode(1) 65 | n2 = TreeNode(2) 66 | n3 = TreeNode(3) 67 | 68 | n1.left = n2 69 | n2.right = n3 70 | root = n1 71 | 72 | assert min_depth(root) == 3 73 | -------------------------------------------------------------------------------- /challenges/binary_search/rotated_sorted_array_search.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | def binary_search_rotated(arr, n): 5 | first = 0 6 | last = len(arr) - 1 7 | 8 | while first <= last: 9 | mid = (first + last) / 2 10 | print('Search from arr[{}] to arr[{}] with mid arr[{}]'.format(first, last, mid)) 11 | 12 | if arr[mid] == n: 13 | return mid 14 | # is left sorted 15 | elif arr[first] < arr[mid]: 16 | if n >= arr[first] and n < arr[mid]: 17 | last = mid - 1 18 | else: 19 | first = mid + 1 20 | # is right sorted 21 | else: 22 | if n > arr[mid] and n <= arr[last]: 23 | first = mid + 1 24 | else: 25 | last = mid - 1 26 | 27 | return -1 28 | 29 | 30 | @pytest.mark.skip(reason="Not solved yet") 31 | def test_binary_search_rotated(): 32 | assert binary_search_rotated([ 33 | 101, 103, 106, 109, 158, 164, 182, 187, 202, 34 | 205, 2, 3, 32, 57, 69, 74, 81, 99, 100 35 | ], 202) == 8 36 | assert binary_search_rotated([4, 5, 3], 3) == 2 37 | assert binary_search_rotated([5, 4], 4) == 1 38 | assert binary_search_rotated([4, 6, 7, 8, 9, 0, 2], 9) == 4 39 | assert binary_search_rotated([8, 9, 0, 2, 4, 6, 7], 9) == 1 40 | assert binary_search_rotated([8, 9, 0, 2, 4, 6, 7], 6) == 5 41 | assert binary_search_rotated([8, 9, 0, 2, 4, 6, 7], 10) == -1 42 | assert binary_search_rotated([4, 5, 6, 7, 0, 1, 2], 1) == 5 43 | assert binary_search_rotated([4], 1) == -1 44 | -------------------------------------------------------------------------------- /challenges/trees/order_people_heights.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | def order_people_heights(heights, in_fronts): 4 | """ 5 | You are given a list of people. Each person has a height and how many 6 | before it are taller. 7 | 8 | heights = [5, 3, 2, 6, 1, 4] 9 | people_in_front = [0, 1, 2, 0, 3, 2] 10 | 11 | And this is what it look like 12 | 13 | x 14 | x x 15 | x x x 16 | x x x x 17 | x x x x x 18 | x x x x x x 19 | 20 | 0 1 2 0 3 2 21 | 22 | Order people heights that they fulfill their people in front constraint. 23 | 24 | ordered_heights = [5, 3, 2, 6, 1, 4] 25 | 26 | x 27 | x x 28 | x x x 29 | x x x x 30 | x x x x x 31 | x x x x x x 32 | 33 | 0 1 2 3 0 2 34 | 35 | """ 36 | people = sorted(zip(heights, in_fronts), key=lambda p: p[0]) 37 | ordered_people = [None] * len(people) 38 | 39 | for height, in_front in people: 40 | if people[in_front] is None: 41 | people[in_front] = height 42 | else: 43 | empty_slots = [i for i, h in enumerate(ordered_people) if h is None] 44 | i = empty_slots[in_front] 45 | ordered_people[i] = height 46 | 47 | return ordered_people 48 | 49 | 50 | def test_order_people_heights(): 51 | heights = [5, 3, 2, 6, 1, 4] 52 | in_fronts = [0, 1, 2, 0, 3, 2] 53 | assert order_people_heights(heights, in_fronts) == [5, 3, 2, 1, 6, 4] 54 | -------------------------------------------------------------------------------- /challenges/maps/distinct_numbers_in_window.py: -------------------------------------------------------------------------------- 1 | class DistinctDict(): 2 | def __init__(self, start=[]): 3 | self.uniques = {} 4 | 5 | def add(self, num): 6 | print('Add', num) 7 | if num in self.uniques: 8 | self.uniques[num] = self.uniques[num] + 1 9 | else: 10 | self.uniques[num] = 1 11 | 12 | def remove(self, num): 13 | print('Remove', num) 14 | count = self.uniques[num] 15 | 16 | if count > 1: 17 | self.uniques[num] = count - 1 18 | else: 19 | del self.uniques[num] 20 | 21 | def distinct_numbers(self): 22 | print('Distincts', self.uniques) 23 | return len(self.uniques) 24 | 25 | 26 | def distinct_numbers_in_windows(numbers, k): 27 | if k == 0 or k > len(numbers): 28 | return 29 | 30 | distinct_dict = DistinctDict(numbers[:k]) 31 | 32 | for i, num in enumerate(numbers): 33 | if i < k - 1: 34 | distinct_dict.add(num) 35 | elif i == k - 1: 36 | distinct_dict.add(num) 37 | yield distinct_dict.distinct_numbers() 38 | else: 39 | oldest_num = numbers[i - k] 40 | distinct_dict.remove(oldest_num) 41 | distinct_dict.add(num) 42 | yield distinct_dict.distinct_numbers() 43 | 44 | 45 | def test_d_nums(): 46 | nums = [1, 2, 1, 3, 4, 3] 47 | assert list(distinct_numbers_in_windows(nums, k=3)) == [2, 3, 3, 2] 48 | nums = [6, 3, 2, 2, 4, 3, 6, 3, 1, 2, 3] 49 | assert list(distinct_numbers_in_windows(nums, k=4)) == [3, 3, 3, 4, 3, 3, 4, 3] 50 | -------------------------------------------------------------------------------- /challenges/trees/valid_bst.py: -------------------------------------------------------------------------------- 1 | class TreeNode(): 2 | def __init__(self, x): 3 | self.val = x 4 | self.left = None 5 | self.right = None 6 | 7 | def __repr__(self): 8 | return ''.format(self.val) 9 | 10 | 11 | def inorder(node): 12 | if not node: 13 | return 14 | 15 | for n in inorder(node.left): 16 | yield n 17 | 18 | yield node 19 | 20 | for n in inorder(node.right): 21 | yield n 22 | 23 | 24 | def subtree_greater(node, val): 25 | nodes = [n.val for n in inorder(node)] 26 | if len(nodes) > 0: 27 | return min(nodes) > val 28 | 29 | return True 30 | 31 | 32 | def subtree_lesser(node, val): 33 | nodes = [n.val for n in inorder(node)] 34 | if len(nodes) > 0: 35 | return max(nodes) < val 36 | 37 | return True 38 | 39 | 40 | def is_valid_bst(node): 41 | if not node: 42 | return True 43 | 44 | valid = subtree_lesser(node.left, node.val) and subtree_greater(node.right, node.val) 45 | return valid and is_valid_bst(node.left) and is_valid_bst(node.right) 46 | 47 | 48 | def test_is_invalid_bst(): 49 | n1 = TreeNode(1) 50 | n2 = TreeNode(2) 51 | n3 = TreeNode(3) 52 | n4 = TreeNode(4) 53 | 54 | n2.left = n1 55 | n2.right = n3 56 | n3.left = n4 57 | 58 | assert not is_valid_bst(n2) 59 | 60 | 61 | def test_is_valid_bst(): 62 | n1 = TreeNode(1) 63 | n2 = TreeNode(2) 64 | n3 = TreeNode(3) 65 | n4 = TreeNode(4) 66 | 67 | n2.left = n1 68 | n2.right = n3 69 | n3.right = n4 70 | 71 | assert is_valid_bst(n2) 72 | -------------------------------------------------------------------------------- /challenges/trees/path_sum.py: -------------------------------------------------------------------------------- 1 | class TreeNode(): 2 | def __init__(self, x): 3 | self.val = x 4 | self.left = None 5 | self.right = None 6 | 7 | def __repr__(self): 8 | return ''.format(self.val) 9 | 10 | 11 | def path_sum(root, target_sum): 12 | """ 13 | Given a binary tree and a sum, determine if the tree has 14 | a root-to-leaf path such that adding up all the values along 15 | the path equals the given sum. 16 | """ 17 | 18 | def is_leaf(node): 19 | return node.left is None and node.right is None 20 | 21 | def leaf_nodes(node, parent_path_sum): 22 | if node is None: 23 | return 24 | 25 | new_sum = parent_path_sum + node.val 26 | if is_leaf(node): 27 | yield (node, new_sum) 28 | 29 | for n in leaf_nodes(node.left, new_sum): 30 | yield n 31 | 32 | for n in leaf_nodes(node.right, new_sum): 33 | yield n 34 | 35 | for node, path_sum in leaf_nodes(root, 0): 36 | if path_sum == target_sum: 37 | return True 38 | 39 | return False 40 | 41 | 42 | def test_invert_tree(): 43 | n1 = TreeNode(5) 44 | n2 = TreeNode(4) 45 | n3 = TreeNode(8) 46 | n4 = TreeNode(11) 47 | n5 = TreeNode(7) 48 | n6 = TreeNode(2) 49 | n7 = TreeNode(13) 50 | n8 = TreeNode(4) 51 | n9 = TreeNode(1) 52 | 53 | n1.left = n2 54 | n1.right = n3 55 | 56 | n2.left = n4 57 | n4.left = n5 58 | n4.right = n6 59 | 60 | n3.left = n7 61 | n3.right = n8 62 | n8.right = n9 63 | 64 | assert path_sum(n1, 22) 65 | -------------------------------------------------------------------------------- /challenges/binary_search/binary_matrix_search.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | # @param A : list of list of integers 3 | # @param B : integer 4 | # @return an integer 5 | def searchMatrix(self, A, B): 6 | pass 7 | 8 | 9 | def binary_search_exists(numbers, num): 10 | "O(log n) where n is len(numbers)" 11 | first = 0 12 | last = len(numbers) - 1 13 | 14 | while first <= last: 15 | mid = (first + last) / 2 16 | if numbers[mid] == num: 17 | return True 18 | if numbers[mid] < num: 19 | first = mid + 1 20 | if numbers[mid] > num: 21 | last = mid - 1 22 | 23 | return False 24 | 25 | 26 | def binary_search_matrix(matrix, num): 27 | if len(matrix) == 0: 28 | return False 29 | 30 | first = 0 31 | last = len(matrix) - 1 32 | 33 | while first <= last: 34 | mid = (first + last) / 2 35 | 36 | if matrix[mid][0] <= num <= matrix[mid][-1]: 37 | return binary_search_exists(matrix[mid], num) 38 | 39 | if matrix[mid][0] < num > matrix[mid][-1]: 40 | first = mid + 1 41 | 42 | if matrix[mid][0] > num: 43 | last = mid - 1 44 | 45 | return False 46 | 47 | 48 | 49 | print(binary_search_exists([1, 2, 3, 4, 5, 6, 7, 8, 9], 10)) 50 | print(binary_search_exists([1, 2, 3, 4, 5, 6, 7, 8, 9], 7)) 51 | 52 | print(binary_search_matrix([ 53 | [1, 3, 5, 7], 54 | [10, 11, 16, 20], 55 | [23, 30, 34, 50] 56 | ], 23)) 57 | print(binary_search_matrix([ 58 | [1], 59 | [10], 60 | [23] 61 | ], 1)) 62 | print(binary_search_matrix([ 63 | [1] 64 | ], 1)) 65 | print(binary_search_matrix([ 66 | [1, 3, 5, 7] 67 | ], 5)) 68 | -------------------------------------------------------------------------------- /challenges/array/zero_matrix.py: -------------------------------------------------------------------------------- 1 | def zero_matrix(matrix): 2 | """ 3 | Given an m x n matrix of 0s and 1s, if an element is 0, 4 | set its entire row and column to 0. 5 | 6 | Given a matrix A as input. 7 | 8 | A = [[1, 0, 1], 9 | [1, 1, 1], 10 | [1, 1, 1]] 11 | 12 | On returning the matrix A should have the rows and columns 13 | containing the initial 0 all set to 0. 14 | 15 | A = [[0, 0, 0], 16 | [1, 0, 1], 17 | [1, 0, 1]] 18 | """ 19 | m = len(matrix) 20 | n = len(matrix[0]) 21 | 22 | # Space complexity is O(m) + O(n) 23 | zero_columns = {} 24 | zero_rows = {} 25 | 26 | # Time complexity is O(m * n) 27 | for y, row in enumerate(matrix): 28 | for x, col in enumerate(row): 29 | if col == 0: 30 | zero_rows[y] = True 31 | zero_columns[x] = True 32 | 33 | for y in zero_rows.keys(): 34 | for x in range(0, n): 35 | matrix[y][x] = 0 36 | 37 | for x in zero_columns.keys(): 38 | for y in range(0, m): 39 | matrix[y][x] = 0 40 | 41 | return matrix 42 | 43 | 44 | def test_zero_matrix_single_value(): 45 | assert zero_matrix([[0]]) == [[0]] 46 | 47 | 48 | def test_zero_matrix_two_rows_three_cols(): 49 | assert zero_matrix([ 50 | [1, 1, 1], 51 | [1, 1, 0] 52 | ]) == [ 53 | [1, 1, 0], 54 | [0, 0, 0] 55 | ] 56 | 57 | 58 | def test_zero_matrix_three_rows_cols(): 59 | assert zero_matrix([ 60 | [1, 1, 1], 61 | [0, 1, 0], 62 | [1, 1, 1] 63 | ]) == [ 64 | [0, 1, 0], 65 | [0, 0, 0], 66 | [0, 1, 0] 67 | ] 68 | -------------------------------------------------------------------------------- /challenges/trees/inorder_traversal.py: -------------------------------------------------------------------------------- 1 | class TreeNode(): 2 | def __init__(self, x): 3 | self.val = x 4 | self.left = None 5 | self.right = None 6 | 7 | def __repr__(self): 8 | return ''.format(self.val) 9 | 10 | 11 | def inorder2(node): 12 | nodes = [] 13 | 14 | cursor = node 15 | nodes.append(cursor) 16 | 17 | if cursor.left is None: 18 | yield cursor 19 | 20 | if cursor.right: 21 | yield cursor.ri 22 | node.left 23 | 24 | 25 | def inorder(root): 26 | 27 | def inorder_recursive(node): 28 | if not node: 29 | return 30 | 31 | for n in inorder_recursive(node.left): 32 | yield n 33 | 34 | yield node 35 | 36 | for n in inorder_recursive(node.right): 37 | yield n 38 | 39 | return [n.val for n in inorder_recursive(root)] 40 | 41 | 42 | def test_inorder(): 43 | n1 = TreeNode(1) 44 | n2 = TreeNode(2) 45 | n3 = TreeNode(3) 46 | 47 | n1.left = n2 48 | n1.right = n3 49 | 50 | assert inorder(n1) == [2, 1, 3] 51 | 52 | 53 | def test_inorder_imbalanced(): 54 | n1 = TreeNode(1) 55 | n2 = TreeNode(2) 56 | n3 = TreeNode(3) 57 | 58 | n1.right = n2 59 | n2.left = n3 60 | 61 | assert inorder(n1) == [1, 3, 2] 62 | 63 | 64 | def test_inorder_larger_tree(): 65 | n1 = TreeNode(1) 66 | n2 = TreeNode(2) 67 | n3 = TreeNode(3) 68 | n4 = TreeNode(4) 69 | n5 = TreeNode(5) 70 | n6 = TreeNode(6) 71 | n7 = TreeNode(7) 72 | 73 | n4.left = n2 74 | n4.right = n6 75 | 76 | n2.left = n1 77 | n2.right = n3 78 | 79 | n6.left = n5 80 | n6.right = n7 81 | 82 | root = n4 83 | assert inorder(root) == [1, 2, 3, 4, 5, 6, 7] 84 | -------------------------------------------------------------------------------- /challenges/array/spiral_order.py: -------------------------------------------------------------------------------- 1 | def spiral_order(matrix): 2 | """ 3 | Return m * n matrix in spiral order. 4 | 5 | Given the following matrix. 6 | 7 | matrix = [ 8 | [1, 2, 3], 9 | [4, 5, 6], 10 | [7, 8, 9] 11 | ] 12 | 13 | The spiral order of the matrix. 14 | 15 | spiral_order = [1, 2, 3, 6, 9, 8, 7, 4, 5] 16 | """ 17 | m = len(matrix) 18 | if m == 0: 19 | return 20 | 21 | n = len(matrix[0]) 22 | if n == 0: 23 | return 24 | 25 | if n == 1: 26 | for row in matrix: 27 | yield row[0] 28 | return 29 | 30 | if m == n == 1: 31 | yield matrix[0][0] 32 | return 33 | 34 | # Upper outer shell 35 | for column in matrix[0]: 36 | yield column 37 | 38 | # Right outer shell 39 | for row in matrix[1:-1]: 40 | yield row[-1] 41 | 42 | if m > 1: 43 | # Lower outer shell 44 | for column in reversed(matrix[-1]): 45 | yield column 46 | 47 | # Left outer shell 48 | for row in list(reversed(matrix))[1:-1]: 49 | yield row[0] 50 | 51 | # Peel off the shell 52 | inner_matrix = [row[1:-1] for row in matrix[1:-1]] 53 | for num in spiral_order(inner_matrix): 54 | yield num 55 | 56 | 57 | def test_spiral_order_single_elem(): 58 | assert list(spiral_order([ 59 | [1] 60 | ])) == [1] 61 | 62 | 63 | def test_spiral_order_single_row(): 64 | assert list(spiral_order([ 65 | [1], 66 | [2], 67 | [3] 68 | ])) == [1, 2, 3] 69 | 70 | 71 | def test_spiral_order(): 72 | assert list(spiral_order([ 73 | [1, 2, 3], 74 | [4, 5, 6], 75 | [7, 8, 9] 76 | ])) == [1, 2, 3, 6, 9, 8, 7, 4, 5] 77 | -------------------------------------------------------------------------------- /challenges/backtracking/gen_parens.py: -------------------------------------------------------------------------------- 1 | def generate_parenthesis(n): 2 | """ 3 | Given n pairs of parentheses, write a function to generate all 4 | combinations of well-formed parentheses. 5 | 6 | For n=3 the solution is: 7 | 8 | "((()))", "(()())", "(())()", "()(())", "()()()" 9 | 10 | For each opening bracket we can either start a new opening 11 | bracket or close it and track back. 12 | """ 13 | if n == 0: 14 | return [] 15 | 16 | def all_combinations(braces, opening=0, closing=0): 17 | if len(braces) >= 2*n: 18 | yield braces 19 | return 20 | 21 | if opening < n: 22 | for c in all_combinations(braces + ["("], opening+1, closing): 23 | yield c 24 | 25 | if closing < opening: 26 | for c in all_combinations(braces + [")"], opening, closing+1): 27 | yield c 28 | 29 | return ["".join(p) for p in all_combinations([])] 30 | 31 | 32 | def test_generate_parenthesis(): 33 | assert generate_parenthesis(0) == [] 34 | assert generate_parenthesis(1) == ["()"] 35 | assert generate_parenthesis(2) == ["(())", "()()"] 36 | assert generate_parenthesis(3) == [ 37 | "((()))", 38 | "(()())", 39 | "(())()", 40 | "()(())", 41 | "()()()" 42 | ] 43 | 44 | assert len(generate_parenthesis(4)) == 14 45 | assert generate_parenthesis(4) == [ 46 | "(((())))", 47 | "((()()))", 48 | "((())())", 49 | "((()))()", 50 | "(()(()))", 51 | "(()()())", 52 | "(()())()", 53 | "(())(())", 54 | "(())()()", 55 | "()((()))", 56 | "()(()())", 57 | "()(())()", 58 | "()()(())", 59 | "()()()()" 60 | ] 61 | -------------------------------------------------------------------------------- /challenges/two_pointers/rectangle_intersection.py: -------------------------------------------------------------------------------- 1 | class Rectangle: 2 | def __init__(self, left_x, bottom_y, width, height): 3 | self.left_x = left_x 4 | self.bottom_y = bottom_y 5 | self.width = width 6 | self.height = height 7 | 8 | def intersection(rect1, rect2): 9 | # Find intersection on x range 10 | x1 = rect1.left_x 11 | x2 = rect1.left_x + rect1.width 12 | 13 | _x1 = rect2.left_x 14 | _x2 = rect2.left_x + rect2.width 15 | 16 | """ 17 | x1---------r1-------x2 18 | _x1---r2---_x2 19 | """ 20 | 21 | # What do I need to do to get coords from the intersection of x1 22 | if _x1 <= x1: 23 | i1 = x1 24 | if _x1 > x1: 25 | i1 = _x1 26 | # 27 | # 28 | # 8-----r1-----12 29 | # 5-----r2---------12 30 | 31 | if _x2 <= x2: 32 | i2 = _x2 33 | if _x2 > x2: 34 | i2 = x2 35 | # 36 | # 37 | # 8-----r1-----13 38 | # 5-----r2------11 39 | print('X axis intersection: {}'.format((i1, i2))) 40 | 41 | # Find intersection on x range 42 | y1 = rect1.bottom_y 43 | y2 = rect1.bottom_y + rect1.height 44 | 45 | _y1 = rect2.bottom_y 46 | _y2 = rect2.bottom_y + rect2.height 47 | 48 | # What do I need to do to get coords from the intersection of y1 49 | if _y1 <= y1: 50 | j1 = y1 51 | if _y1 > y1: 52 | j1 = _y1 53 | # 54 | # 55 | # 8-----r1-----12 56 | # 5-----r2---------12 57 | 58 | if _y2 <= y2: 59 | j2 = _y2 60 | if _y2 > y2: 61 | j2 = y2 62 | 63 | print('Y axis intersection: {}'.format((j1, j2))) 64 | 65 | 66 | rect1 = Rectangle(1, 5, width=10, height=4) 67 | rect2 = Rectangle(2, 5, width=10, height=4) 68 | intersection(rect1, rect2) 69 | 70 | rect3 = Rectangle(1, 5, width=1, height=1) 71 | rect4 = Rectangle(7, 8, width=1, height=1) 72 | intersection(rect3, rect4) 73 | 74 | -------------------------------------------------------------------------------- /challenges/binary_search/search_range.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a sorted array of integers, find the starting and ending position 3 | of a given target value. 4 | """ 5 | 6 | 7 | def search_last_occurrence(arr, target): 8 | first = 0 9 | last = len(arr) - 1 10 | 11 | found = -1 12 | while first <= last: 13 | mid = (first + last) / 2 14 | if arr[mid] == target: 15 | found = mid 16 | first = mid + 1 17 | if arr[mid] < target: 18 | first = mid + 1 19 | if arr[mid] > target: 20 | last = mid - 1 21 | 22 | return found 23 | 24 | 25 | def search_first_occurrence(arr, target): 26 | first = 0 27 | last = len(arr) - 1 28 | 29 | found = -1 30 | while first <= last: 31 | mid = (first + last) / 2 32 | if arr[mid] == target: 33 | found = mid 34 | last = mid - 1 35 | if arr[mid] < target: 36 | first = mid + 1 37 | if arr[mid] > target: 38 | last = mid - 1 39 | 40 | return found 41 | 42 | 43 | def search_range(arr, target): 44 | left = search_first_occurrence(arr, target) 45 | right = search_last_occurrence(arr, target) 46 | 47 | return [left, right] 48 | 49 | 50 | def test_first_search_occurrence(): 51 | arr = [5, 7, 7, 7, 7, 7, 8, 8, 10] 52 | assert search_first_occurrence(arr, 7) == 1 53 | assert search_first_occurrence(arr, 8) == 6 54 | 55 | 56 | def test_last_search_occurrence(): 57 | arr = [5, 7, 7, 7, 7, 7, 8, 8, 10] 58 | assert search_last_occurrence(arr, 7) == 5 59 | assert search_last_occurrence(arr, 8) == 7 60 | 61 | 62 | def test_search_range(): 63 | arr = [5, 7, 7, 8, 8, 10] 64 | assert search_range(arr, 4) == [-1, -1] 65 | assert search_range(arr, 5) == [0, 0] 66 | assert search_range(arr, 7) == [1, 2] 67 | assert search_range(arr, 8) == [3, 4] 68 | assert search_range(arr, 10) == [5, 5] 69 | -------------------------------------------------------------------------------- /challenges/graphs/black_shapes.py: -------------------------------------------------------------------------------- 1 | import collections 2 | 3 | 4 | def adjacent_black_fields(matrix, row_idx, col_idx): 5 | adjacent = [(row_idx + 1, col_idx), (row_idx - 1, col_idx), 6 | (row_idx, col_idx + 1), (row_idx, col_idx - 1)] 7 | 8 | def is_within_matrix(row_idx, col_idx): 9 | row_count = len(matrix) 10 | col_count = len(matrix[0]) 11 | return 0 <= row_idx < row_count and 0 <= col_idx < col_count 12 | 13 | def is_black(row_idx, col_idx): 14 | return matrix[row_idx][col_idx] == 'X' 15 | 16 | return [f for f in adjacent if is_within_matrix(f[0], f[1]) and 17 | is_black(f[0], f[1])] 18 | 19 | 20 | def find_black_fields(matrix): 21 | for row_idx, row in enumerate(matrix): 22 | for col_idx, field in enumerate(row): 23 | if field == 'X': 24 | yield (row_idx, col_idx) 25 | 26 | 27 | def count_black_shapes(matrix): 28 | part_of_shape = {} 29 | 30 | def is_part_of_shape(row_idx, col_idx): 31 | return (row_idx, col_idx) in part_of_shape 32 | 33 | def mark_shape(row_idx, col_idx): 34 | part_of_shape[(row_idx, col_idx)] = True 35 | 36 | for row_idx, col_idx in adjacent_black_fields(matrix, row_idx, col_idx): 37 | if not is_part_of_shape(row_idx, col_idx): 38 | mark_shape(row_idx, col_idx) 39 | 40 | shape_count = 0 41 | for row_idx, col_idx in find_black_fields(matrix): 42 | if not is_part_of_shape(row_idx, col_idx): 43 | shape_count += 1 44 | mark_shape(row_idx, col_idx) 45 | 46 | return shape_count 47 | 48 | 49 | def test_single_black_shape(): 50 | matrix = ['XXX', 'XXX', 'XXX'] 51 | assert count_black_shapes(matrix) == 1 52 | 53 | 54 | def test_multipel_black_shape(): 55 | matrix = ['OOOXOOO', 56 | 'OOXXOXO', 57 | 'OXOOOXO'] 58 | assert count_black_shapes(matrix) == 3 59 | -------------------------------------------------------------------------------- /challenges/trees/merge_k_sorted_lists.py: -------------------------------------------------------------------------------- 1 | import heapq 2 | 3 | 4 | class ListNode: 5 | def __init__(self, x): 6 | self.val = x 7 | self.next = None 8 | 9 | def __eq__(self, other): 10 | return self.val == other.val and self.next == other.next 11 | 12 | def __repr__(self): 13 | return ''.format(self.val, self.next) 14 | 15 | 16 | class LinkedList(ListNode): 17 | def __init__(self, arr): 18 | 19 | nodes = [ListNode(v) for v in arr] 20 | for i in range(1, len(nodes)): 21 | nodes[i-1].next = nodes[i] 22 | 23 | head = nodes[0] 24 | 25 | self.val = head.val 26 | self.next = head.next 27 | 28 | 29 | class LinkedListHeap(): 30 | def __init__(self, lists): 31 | self.heap = [] 32 | 33 | for list_head in lists: 34 | heapq.heappush(self.heap, (list_head.val, list_head)) 35 | 36 | def pop_min(self): 37 | """Get and remove node with min value""" 38 | try: 39 | _, min_node = heapq.heappop(self.heap) 40 | except IndexError: 41 | return None 42 | 43 | next_node = min_node.next 44 | if next_node: 45 | heapq.heappush(self.heap, (next_node.val, next_node)) 46 | 47 | return min_node 48 | 49 | 50 | def merge_k_lists(lists): 51 | heap = LinkedListHeap(lists) 52 | head = ListNode('dummy') 53 | tail = head 54 | 55 | min_node = heap.pop_min() 56 | while min_node: 57 | min_node.next = None 58 | tail.next = min_node 59 | tail = tail.next 60 | min_node = heap.pop_min() 61 | 62 | return head.next 63 | 64 | 65 | def test_merge_k_sorted_lists(): 66 | a = LinkedList([1, 10, 20]) 67 | b = LinkedList([4, 11, 13]) 68 | c = LinkedList([3, 8, 9]) 69 | 70 | lists = [a, b, c] 71 | 72 | merged_list = merge_k_lists(lists) 73 | assert merged_list == LinkedList([1, 3, 4, 8, 9, 10, 11, 13, 20]) 74 | -------------------------------------------------------------------------------- /challenges/trees/balanced_binary_tree.py: -------------------------------------------------------------------------------- 1 | class TreeNode(): 2 | def __init__(self, x): 3 | self.val = x 4 | self.left = None 5 | self.right = None 6 | 7 | def __repr__(self): 8 | return ''.format(self.val) 9 | 10 | 11 | def is_leaf(node): 12 | return node.left is None and node.right is None 13 | 14 | 15 | def tree_height(node): 16 | if node is None: 17 | return 0 18 | elif is_leaf(node): 19 | return 1 20 | else: 21 | left_height = tree_height(node.left) 22 | right_height = tree_height(node.right) 23 | return max(left_height, right_height) + 1 24 | 25 | 26 | def is_balanced(root): 27 | if root is None: 28 | return True 29 | 30 | left_height = tree_height(root.left) 31 | right_height = tree_height(root.right) 32 | 33 | balanced = abs(left_height - right_height) <= 1 34 | print(balanced, left_height, right_height, root) 35 | return balanced and is_balanced(root.left) and is_balanced(root.right) 36 | 37 | 38 | def test_is_balanced(): 39 | n1 = TreeNode(1) 40 | n2 = TreeNode(2) 41 | n3 = TreeNode(3) 42 | 43 | n1.left = n2 44 | n1.right = n3 45 | 46 | assert is_balanced(n1) 47 | 48 | 49 | def test_is_not_balanced(): 50 | n1 = TreeNode(1) 51 | n2 = TreeNode(2) 52 | n3 = TreeNode(3) 53 | 54 | n3.left = n2 55 | n2.left = n1 56 | 57 | assert not is_balanced(n3) 58 | 59 | 60 | def test_single_node_tree_is_balanced(): 61 | n1 = TreeNode(1) 62 | assert is_balanced(n1) 63 | 64 | 65 | def test_two_node_tree_is_balanced(): 66 | n1 = TreeNode(1) 67 | n2 = TreeNode(2) 68 | n1.right = n2 69 | assert is_balanced(n1) 70 | 71 | 72 | def test_equal_height_of_root_is_inbalanced(): 73 | n1 = TreeNode(1) 74 | n2 = TreeNode(2) 75 | n4 = TreeNode(4) 76 | n5 = TreeNode(5) 77 | 78 | n1.left = n2 79 | n1.right = n4 80 | 81 | n4.right = n5 82 | 83 | assert is_balanced(n1) 84 | -------------------------------------------------------------------------------- /challenges/array/temp_tracker.py: -------------------------------------------------------------------------------- 1 | import collections 2 | 3 | 4 | class TempTracker: 5 | """ 6 | Temperature tracker to calculate mean, mode, min, max of temperature 7 | events. Favor speeding up the getter functions over speeding 8 | up the insert() function. 9 | """ 10 | def __init__(self): 11 | self._max_temp = 0 12 | self._min_temp = 0 13 | self._temp_sum = 0 14 | self._temp_count = 0 15 | self._temps = collections.defaultdict(lambda: 0) 16 | 17 | def __str__(self): 18 | return '\n'.join([ 19 | 'Max: ' + str(self.max), 20 | 'Min: ' + str(self.min), 21 | 'Mean: ' + str(self.mean), 22 | 'Mode: ' + str(self.mode), 23 | ]) 24 | 25 | def insert(self, temp): 26 | "O(1)" 27 | self._max_temp = max(self._max_temp, temp) 28 | self._min_temp = min(self._min_temp, temp) 29 | 30 | self._temps[temp] += 1 31 | self._temp_sum += temp 32 | self._temp_count += 1 33 | 34 | @property 35 | def min(self): 36 | "O(1)" 37 | return self._min_temp 38 | 39 | @property 40 | def max(self): 41 | "O(1)" 42 | return self._max_temp 43 | 44 | @property 45 | def mean(self): 46 | "O(1)" 47 | return self._temp_sum / self._temp_count 48 | 49 | @property 50 | def mode(self): 51 | "O(n)" 52 | mode = self._temps.keys()[0] 53 | max_count = 0 54 | for temp, count in self._temps.items(): 55 | if count > max_count: 56 | max_count = count 57 | mode = temp 58 | 59 | return mode 60 | 61 | 62 | def test_temptracker(): 63 | tracker = TempTracker() 64 | tracker.insert(1) 65 | tracker.insert(3) 66 | tracker.insert(3) 67 | tracker.insert(4) 68 | tracker.insert(5) 69 | tracker.insert(-2) 70 | 71 | assert tracker.max == 5 72 | assert tracker.min == -2 73 | assert tracker.mean == 2 74 | assert tracker.mode == 3 75 | -------------------------------------------------------------------------------- /challenges/trees/count_inversions.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given an array A, count the number of inversions in the array. 3 | Formally speaking, two elements A[i] and A[j] 4 | form an inversion if A[i] > A[j] and i < j 5 | """ 6 | 7 | 8 | class TreeNode(): 9 | def __init__(self, val, idx): 10 | self.val = val 11 | self.idx = idx 12 | self.left = None 13 | self.right = None 14 | 15 | def __repr__(self): 16 | return ''.format(self.val, self.idx) 17 | 18 | def insert(self, num, idx): 19 | if num < self.val: 20 | if not self.left: 21 | self.left = TreeNode(num, idx) 22 | else: 23 | self.left.insert(num, idx) 24 | else: 25 | if not self.right: 26 | self.right = TreeNode(num, idx) 27 | else: 28 | self.right.insert(num, idx) 29 | 30 | 31 | def find_lower(node, num): 32 | if not node: 33 | return 34 | 35 | if node.val > num: 36 | for n in find_lower(node.left, num): 37 | yield n 38 | else: 39 | yield node 40 | 41 | for n in find_lower(node.left, num): 42 | yield n 43 | for n in find_lower(node.right, num): 44 | yield n 45 | 46 | 47 | def prepare_bin_tree(numbers): 48 | root = TreeNode(numbers[0], 0) 49 | for idx, num in enumerate(numbers[1:], start=1): 50 | root.insert(num, idx) 51 | 52 | return root 53 | 54 | 55 | def inversions(numbers): 56 | # O(n * log n) 57 | tree = prepare_bin_tree(numbers) 58 | 59 | for idx, num in enumerate(numbers): 60 | for node in find_lower(tree, num): 61 | if node.idx > idx and node.val < num: 62 | yield num, node.val 63 | 64 | 65 | def test_insert(): 66 | nums = [2, 4, 1, 3, 5] 67 | tree = prepare_bin_tree(nums) 68 | 69 | assert tree.val == 2 70 | assert tree.left.val == 1 71 | assert tree.right.val == 4 72 | assert tree.right.left.val == 3 73 | assert tree.right.right.val == 5 74 | 75 | 76 | def test_inversions(): 77 | nums = [2, 4, 1, 3, 5] 78 | assert list(inversions(nums)) == [(2, 1), (4, 1), (4, 3)] 79 | -------------------------------------------------------------------------------- /challenges/stack/evaluate_expression.py: -------------------------------------------------------------------------------- 1 | """ 2 | Evaluate the value of an arithmetic expression in Reverse Polish Notation. 3 | Valid operators are +, -, *, /. Each operand may be an integer or 4 | another expression. 5 | 6 | ["2", "1", "+", "3", "*"] -> ((2 + 1) * 3) -> 9 7 | ["4", "13", "5", "/", "+"] -> (4 + (13 / 5)) -> 6 8 | """ 9 | 10 | 11 | def is_operand(expr): 12 | return expr in ['+', '-', '/', '*'] 13 | 14 | 15 | def eval(left, right, op): 16 | if op == '+': 17 | return left + right 18 | elif op == "-": 19 | return left - right 20 | elif op == "/": 21 | return left / right 22 | elif op == "*": 23 | return left * right 24 | else: 25 | raise ValueError('Operand {} not supported'.format(op)) 26 | 27 | 28 | def eval_reverse_polish_notation(notation): 29 | numbers = [] 30 | for expr in notation: 31 | if is_operand(expr): 32 | right = numbers.pop() 33 | left = numbers.pop() 34 | result = eval(left, right, expr) 35 | numbers.append(result) 36 | else: 37 | numbers.append(int(expr)) 38 | 39 | return numbers.pop() 40 | 41 | 42 | eval_rpn = eval_reverse_polish_notation 43 | 44 | 45 | def test_simple_expression(): 46 | assert eval_rpn(["2", "1", "+"]) == 3 47 | 48 | 49 | def test_reducable_expression(): 50 | assert eval_rpn(["2", "1", "+", "3", "*"]) == 9 51 | 52 | 53 | def test_complex_reducable_expression(): 54 | assert eval_rpn(["4", "13", "5", "/", "+"]) == 6 55 | assert eval_rpn(["4", "13", "3", "+", "5", "/", "+"]) == 7 56 | 57 | 58 | def test_long_expression(): 59 | assert eval_rpn([ 60 | "-2", "-1", "2", "+", "-1", "-", "-", "2", "-2", "1", 61 | "-", "+", "-", "-2", "-2", "-", "-1", "2", "-2", "-", "-2", "-1", "+", 62 | "1", "1", "-", "-1", "+", "1", "*", "*", "2", "+", "*", "-", "-2", "1", 63 | "-2", "*", "+", "-2", "*", "1", "*", "-", "*", "*"]) == 0 64 | 65 | 66 | def test_minus_expression(): 67 | assert eval_rpn([ 68 | "1", "-1", "2", "+", "2", "-", "-1", "1", "*", "-", "-2", "*", "*", 69 | "2", "-2", "-2", "*", "-", "+", "-2", "1", "-", "1", "-", "*", "2", 70 | "-", "-1", "2", "1", "*", "*", "-"]) == 8 71 | -------------------------------------------------------------------------------- /challenges/linked_list/intersection_linked_list.py: -------------------------------------------------------------------------------- 1 | class ListNode: 2 | def __init__(self, val): 3 | self.val = val 4 | self.next = None 5 | 6 | def __repr__(self): 7 | return 'Node<{},{}>'.format(self.val, self.next) 8 | 9 | 10 | def linked_list_len(head): 11 | length = 0 12 | while head is not None: 13 | length += 1 14 | head = head.next 15 | return length 16 | 17 | 18 | def fast_forward_linked_list(head, offset): 19 | for _ in xrange(0, offset): 20 | head = head.next 21 | 22 | return head 23 | 24 | 25 | def fast_forward_larger_list(list_a, list_b): 26 | len_a = linked_list_len(list_a) 27 | len_b = linked_list_len(list_b) 28 | 29 | min_length = min(len_a, len_b) 30 | offset = abs(len_a - len_b) 31 | 32 | if len_a > len_b: 33 | list_a = fast_forward_linked_list(list_a, offset) 34 | elif len_b > len_a: 35 | list_b = fast_forward_linked_list(list_b, offset) 36 | 37 | return min_length, list_a, list_b 38 | 39 | 40 | def find_intersection_node(list_a, list_b): 41 | min_length, head_a, head_b = fast_forward_larger_list(list_a, list_b) 42 | 43 | if head_a == head_b: 44 | return head_a 45 | 46 | for _ in xrange(0, min_length): 47 | if head_a.next == head_b.next: 48 | return head_a.next 49 | else: 50 | head_a = head_a.next 51 | head_b = head_b.next 52 | 53 | return None 54 | 55 | 56 | def test_normal_intersection(): 57 | c1 = ListNode(5) 58 | c2 = ListNode(6) 59 | c3 = ListNode(7) 60 | 61 | c1.next = c2 62 | c2.next = c3 63 | 64 | a1 = ListNode(0) 65 | a2 = ListNode(1) 66 | 67 | a1.next = a2 68 | a2.next = c1 69 | 70 | b1 = ListNode(2) 71 | b2 = ListNode(3) 72 | b3 = ListNode(4) 73 | 74 | b1.next = b2 75 | b2.next = b3 76 | b3.next = c1 77 | 78 | assert fast_forward_linked_list(b1, 1) == b2 79 | assert linked_list_len(c1) == 3 80 | assert linked_list_len(a1) == 5 81 | assert linked_list_len(b1) == 6 82 | assert find_intersection_node(a1, b1) == c1 83 | 84 | 85 | def test_one_way_intersection(): 86 | c1 = ListNode(5) 87 | c2 = ListNode(6) 88 | c3 = ListNode(7) 89 | 90 | a1 = ListNode(0) 91 | 92 | a1.next = c1 93 | b1 = c1 94 | 95 | assert find_intersection_node(a1, b1) == c1 96 | -------------------------------------------------------------------------------- /challenges/trees/flatten_binary_tree_linked_list.py: -------------------------------------------------------------------------------- 1 | class TreeNode(): 2 | def __init__(self, x): 3 | self.val = x 4 | self.left = None 5 | self.right = None 6 | 7 | def __repr__(self): 8 | return ''.format(self.val) 9 | 10 | 11 | def flatten(root): 12 | n = root 13 | while True: 14 | if n.left is not None: 15 | last_leaf = find_last_leaf(n.left) 16 | last_leaf.right = n.right 17 | 18 | n.right = n.left 19 | n.left = None 20 | 21 | n = n.right 22 | elif n.right is not None: 23 | n = n.right 24 | else: 25 | break 26 | 27 | return root 28 | 29 | 30 | def find_last_leaf(n): 31 | if n.right is not None: 32 | return find_last_leaf(n.right) 33 | elif n.left is not None: 34 | return find_last_leaf(n.left) 35 | else: 36 | return n 37 | 38 | 39 | def test_flatten_left_side_tree(): 40 | n1 = TreeNode(1) 41 | n2 = TreeNode(2) 42 | n3 = TreeNode(3) 43 | 44 | n1.left = n2 45 | n2.left = n3 46 | 47 | n1 = flatten(n1) 48 | 49 | assert n1.left is None and n1.right == n2 50 | assert n2.left is None and n2.right == n3 51 | assert n3.left is None and n3.right is None 52 | 53 | 54 | def test_flatten_tree(): 55 | n1 = TreeNode(1) 56 | n2 = TreeNode(2) 57 | n3 = TreeNode(3) 58 | n4 = TreeNode(4) 59 | n5 = TreeNode(5) 60 | n6 = TreeNode(6) 61 | 62 | n1.left = n2 63 | n1.right = n5 64 | 65 | n2.left = n3 66 | n2.right = n4 67 | 68 | n5.right = n6 69 | 70 | n1 = flatten(n1) 71 | 72 | assert n1.left is None 73 | assert n1.right == n2 74 | 75 | assert n2.right == n3 76 | assert n2.left is None 77 | 78 | assert n3.left is None 79 | assert n3.right == n4 80 | 81 | assert n4.left is None 82 | assert n4.right == n5 83 | 84 | assert n5.left is None 85 | assert n5.right == n6 86 | 87 | assert n6.left is None and n6.right is None 88 | 89 | 90 | 91 | def test_find_last_leaf(): 92 | n1 = TreeNode(1) 93 | n2 = TreeNode(2) 94 | n3 = TreeNode(3) 95 | n4 = TreeNode(4) 96 | 97 | n1.left = n2 98 | n1.right = n4 99 | 100 | n2.right = n3 101 | 102 | assert find_last_leaf(n1) == n4 103 | assert find_last_leaf(n2) == n3 104 | -------------------------------------------------------------------------------- /challenges/math/two_egg_problem.py: -------------------------------------------------------------------------------- 1 | """ 2 | A building has 100 floors. One of the floors is the highest floor an 3 | egg can be dropped from without breaking. 4 | 5 | If an egg is dropped from above that floor, it will break. 6 | If it is dropped from that floor or below, it will be completely 7 | undamaged and you can drop the egg again. 8 | 9 | Given two eggs, find the highest floor an egg can be dropped from 10 | without breaking, with as few drops as possible. 11 | """ 12 | import pytest 13 | 14 | 15 | class Floor(object): 16 | def __init__(self, floor_number, breaks=False): 17 | self.floor_number = floor_number 18 | self.breaks = breaks 19 | 20 | def __repr__(self): 21 | return ''.format(self.floor_number, self.breaks) 22 | 23 | 24 | def building(floors=100, breaking_floor=56): 25 | def make_floor(i): 26 | return Floor(i, breaks=i >= breaking_floor) 27 | 28 | return [make_floor(i) for i in range(1, 101)] 29 | 30 | 31 | def find_highest_floor(floors): 32 | 33 | # Since we must find a solution we always need the 'backup' egg to 34 | # solve the problem in worst time 35 | def ascending_drop_egg_until_breaks(start_idx): 36 | for i in range(start_idx, len(floors)): 37 | if floors[i].breaks: 38 | return i 39 | 40 | # This frees us up to do probing with the first egg 41 | def drop_egg_until_breaks(previous_guess): 42 | guess = previous_guess + (previous_guess / 2) 43 | "Use binary search until first egg breaks" 44 | print('Check if egg breaks at floor {}'.format(guess)) 45 | 46 | if floors[guess].breaks: 47 | return previous_guess 48 | else: 49 | return drop_egg_until_breaks(guess) 50 | 51 | half = len(floors) / 2 52 | if floors[half].breaks: 53 | return ascending_drop_egg_until_breaks(0) 54 | 55 | first_breaking_floor = drop_egg_until_breaks(half) 56 | print('Egg breaks at floor {}'.format(first_breaking_floor)) 57 | 58 | return ascending_drop_egg_until_breaks(first_breaking_floor) 59 | 60 | 61 | @pytest.mark.skip(reason="Not solved yet") 62 | def test_find_highest_floor(): 63 | building1 = building(breaking_floor=54) 64 | assert find_highest_floor(building1) == 53 65 | 66 | building2 = building(breaking_floor=25) 67 | assert find_highest_floor(building2) == 24 68 | 69 | building3 = building(breaking_floor=100) 70 | assert find_highest_floor(building3) == 99 71 | -------------------------------------------------------------------------------- /challenges/hashing/points_on_straight_line.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import collections 3 | 4 | 5 | def calculate_slope(p1, p2): 6 | x1, y1 = p1 7 | x2, y2 = p2 8 | 9 | if x2 - x1 == 0: 10 | return sys.maxint 11 | else: 12 | return float(y2 - y1) / float(x2 - x1) 13 | 14 | 15 | def max_points_on_straight_line(points): 16 | if len(points) < 2: 17 | return len(points) 18 | 19 | unique_points = collections.Counter(points) 20 | points = unique_points.keys() 21 | slopes = collections.defaultdict(collections.Counter) 22 | 23 | for idx, point in enumerate(points): 24 | for other_point in points[idx+1:]: 25 | slope = calculate_slope(point, other_point) 26 | print(point, other_point, slope) 27 | 28 | other_point_count = unique_points[other_point] 29 | slopes[point][slope] += other_point_count 30 | slopes[other_point][slope] += other_point_count 31 | 32 | max_points = 0 33 | for p, count in unique_points.items(): 34 | counter = slopes[p] 35 | 36 | print(p, counter) 37 | 38 | try: 39 | _, most_common_count = counter.most_common(1)[0] 40 | except IndexError: 41 | most_common_count = 0 42 | 43 | max_points = max(max_points, most_common_count + count) 44 | 45 | return max_points 46 | 47 | 48 | def test_minus_points(): 49 | assert max_points_on_straight_line([(0, 0), (1, 1), (-1, -1)]) == 3 50 | 51 | 52 | def test_max_points(): 53 | assert max_points_on_straight_line([]) == 0 54 | assert max_points_on_straight_line([(1, 1)]) == 1 55 | assert max_points_on_straight_line([(1, 1), (3, 3)]) == 2 56 | assert max_points_on_straight_line([(1, 0), (2, 0), (3, 0), (3, 3), (3, 4)]) == 3 57 | 58 | 59 | def test_zero_division(): 60 | assert max_points_on_straight_line([(1, 0), (2, 0), (3, 0)]) == 3 61 | 62 | 63 | def test_same_x_axis(): 64 | assert max_points_on_straight_line([(1, 0), (1, 4), (1, -1)]) == 3 65 | 66 | 67 | def test_same_points(): 68 | assert max_points_on_straight_line([(1, 1), (1, 1), (1, 1), (1, 1), (1, 1)]) == 5 69 | 70 | 71 | def test_negative_difference(): 72 | assert max_points_on_straight_line([(4, -4), (8, -4), (-4, -4)]) == 3 73 | assert max_points_on_straight_line([(0, 0), (1, 1), (-1, -1)]) == 3 74 | 75 | 76 | def test_foo(): 77 | ls = [(-6, -17), (5, -16), (-18, -17), (2, -4), (5, -13), (-2, 20)] 78 | assert max_points_on_straight_line(ls) == 2 79 | -------------------------------------------------------------------------------- /challenges/linked_list/partition_list.py: -------------------------------------------------------------------------------- 1 | class ListNode: 2 | def __init__(self, val): 3 | self.val = val 4 | self.next = None 5 | 6 | def __repr__(self): 7 | return 'Node<{},{}>'.format(self.val, self.next) 8 | 9 | 10 | class ListNodePartition: 11 | def __init__(self): 12 | self.head = None 13 | self.tail = self.head 14 | 15 | def append(self, node): 16 | node.next = None 17 | if self.tail: 18 | self.tail.next = node 19 | self.tail = node 20 | else: 21 | self.head = node 22 | self.tail = self.head 23 | 24 | def join(self, other_partition): 25 | if self.tail: 26 | self.tail.next = other_partition.head 27 | return self.head 28 | else: 29 | return other_partition.head 30 | 31 | 32 | def partition_list(head, pivot): 33 | part_right = ListNodePartition() 34 | part_left = ListNodePartition() 35 | 36 | while head: 37 | node = head 38 | head = head.next 39 | 40 | if node.val >= pivot: 41 | part_right.append(node) 42 | else: 43 | part_left.append(node) 44 | 45 | return part_left.join(part_right) 46 | 47 | 48 | def test_unequal_partitions(): 49 | n1 = ListNode(4) 50 | n2 = ListNode(5) 51 | n3 = ListNode(6) 52 | 53 | n1.next = n2 54 | n2.next = n3 55 | 56 | head = partition_list(n1, 3) 57 | 58 | assert head == n1 59 | head = head.next 60 | assert head == n2 61 | head = head.next 62 | assert head == n3 63 | 64 | 65 | def test_no_elem_list(): 66 | assert partition_list(None, 1) is None 67 | 68 | 69 | def test_one_elem_list(): 70 | n1 = ListNode(1) 71 | head = partition_list(n1, 1) 72 | assert head == n1 73 | assert head.next is None 74 | 75 | 76 | def test_normal_partition(): 77 | n1 = ListNode(1) 78 | n2 = ListNode(4) 79 | n3 = ListNode(3) 80 | n4 = ListNode(2) 81 | n5 = ListNode(5) 82 | n6 = ListNode(2) 83 | 84 | n1.next = n2 85 | n2.next = n3 86 | n3.next = n4 87 | n4.next = n5 88 | n5.next = n6 89 | 90 | head = partition_list(n1, 3) 91 | print(head) 92 | assert head == n1 93 | head = head.next 94 | assert head == n4 95 | head = head.next 96 | assert head == n6 97 | head = head.next 98 | assert head == n2 99 | head = head.next 100 | assert head == n3 101 | head = head.next 102 | assert head == n5 103 | -------------------------------------------------------------------------------- /challenges/linked_list/swap_list_nodes_in_pairs.py: -------------------------------------------------------------------------------- 1 | class ListNode: 2 | def __init__(self, val): 3 | self.val = val 4 | self.next = None 5 | 6 | def __repr__(self): 7 | return 'Node<{},{}>'.format(self.val, self.next) 8 | 9 | 10 | def swap_nodes_in_pairs(head): 11 | # Keep new head around to return it at last 12 | if head.next: 13 | new_head = head.next 14 | else: 15 | return head 16 | 17 | while head is not None: 18 | first_node = head 19 | second_node = first_node.next 20 | third_node = second_node.next if second_node else None 21 | forth_node = third_node.next if third_node else None 22 | 23 | if second_node: 24 | second_node.next = first_node 25 | 26 | if forth_node: 27 | first_node.next = forth_node 28 | else: 29 | first_node.next = third_node 30 | 31 | head = third_node 32 | 33 | return new_head 34 | 35 | 36 | def test_one_node_swap(): 37 | a1 = ListNode(1) 38 | assert swap_nodes_in_pairs(a1) == a1 39 | 40 | 41 | def test_swap(): 42 | a1 = ListNode(1) 43 | a2 = ListNode(2) 44 | a3 = ListNode(3) 45 | a4 = ListNode(4) 46 | a5 = ListNode(5) 47 | a6 = ListNode(6) 48 | 49 | a1.next = a2 50 | a2.next = a3 51 | a3.next = a4 52 | a4.next = a5 53 | a5.next = a6 54 | 55 | swapped_head = swap_nodes_in_pairs(a1) 56 | assert swapped_head == a2 57 | 58 | swapped_head = swapped_head.next 59 | assert swapped_head == a1 60 | 61 | swapped_head = swapped_head.next 62 | assert swapped_head == a4 63 | 64 | swapped_head = swapped_head.next 65 | assert swapped_head == a3 66 | 67 | swapped_head = swapped_head.next 68 | assert swapped_head == a6 69 | 70 | swapped_head = swapped_head.next 71 | assert swapped_head == a5 72 | 73 | 74 | def test_uneven_swap(): 75 | a1 = ListNode(1) 76 | a2 = ListNode(2) 77 | a3 = ListNode(3) 78 | a4 = ListNode(4) 79 | a5 = ListNode(5) 80 | 81 | a1.next = a2 82 | a2.next = a3 83 | a3.next = a4 84 | a4.next = a5 85 | 86 | swapped_head = swap_nodes_in_pairs(a1) 87 | assert swapped_head == a2 88 | 89 | swapped_head = swapped_head.next 90 | assert swapped_head == a1 91 | 92 | swapped_head = swapped_head.next 93 | assert swapped_head == a4 94 | 95 | swapped_head = swapped_head.next 96 | assert swapped_head == a3 97 | 98 | swapped_head = swapped_head.next 99 | assert swapped_head == a5 100 | 101 | 102 | def test_uneven_small_swap(): 103 | a1 = ListNode(1) 104 | a2 = ListNode(2) 105 | a3 = ListNode(3) 106 | 107 | a1.next = a2 108 | a2.next = a3 109 | 110 | swapped_head = swap_nodes_in_pairs(a1) 111 | assert swapped_head == a2 112 | swapped_head = swapped_head.next 113 | assert swapped_head == a1 114 | swapped_head = swapped_head.next 115 | assert swapped_head == a3 116 | -------------------------------------------------------------------------------- /challenges/linked_list/add_two_numbers_list.py: -------------------------------------------------------------------------------- 1 | def zip_longest_linked_lists(a, b): 2 | default = ListNode(0) 3 | while a or b: 4 | if a and b: 5 | yield a, b 6 | a = a.next 7 | b = b.next 8 | elif a: 9 | yield a, default 10 | a = a.next 11 | elif b: 12 | yield default, b 13 | b = b.next 14 | 15 | 16 | def add_numbers(term_a, term_b): 17 | results = AdditionLinkedList() 18 | 19 | for node_a, node_b in zip_longest_linked_lists(term_a, term_b): 20 | results.add(node_a, node_b) 21 | 22 | results.add_carryover() 23 | 24 | return results.head 25 | 26 | 27 | class AdditionLinkedList(): 28 | 29 | def __init__(self): 30 | self.head = None 31 | self.tail = self.head 32 | self.carryover = 0 33 | 34 | def add(self, term_a, term_b): 35 | result = term_a.val + term_b.val + self.carryover 36 | self.carryover = result / 10 37 | digit = result % 10 38 | 39 | new_node = ListNode(digit) 40 | self.append(new_node) 41 | 42 | def add_carryover(self): 43 | if self.carryover > 0: 44 | self.append(ListNode(self.carryover)) 45 | self.carryover = 0 46 | 47 | def append(self, new_node): 48 | if self.tail: 49 | self.tail.next = new_node 50 | self.tail = self.tail.next 51 | else: 52 | self.head = new_node 53 | self.tail = self.head 54 | 55 | 56 | class ListNode: 57 | def __init__(self, x): 58 | self.val = x 59 | self.next = None 60 | 61 | def __eq__(self, other): 62 | return self.val == other.val and self.next == other.next 63 | 64 | def __repr__(self): 65 | return ''.format(self.val, self.next) 66 | 67 | 68 | class LinkedList(ListNode): 69 | def __init__(self, arr): 70 | 71 | nodes = [ListNode(v) for v in arr] 72 | for i in range(1, len(nodes)): 73 | nodes[i-1].next = nodes[i] 74 | 75 | head = nodes[0] 76 | 77 | self.val = head.val 78 | self.next = head.next 79 | 80 | 81 | def test_simple_addition(): 82 | a = LinkedList([2, 4, 3]) 83 | b = LinkedList([3, 5, 2]) 84 | assert add_numbers(a, b) == LinkedList([5, 9, 5]) 85 | 86 | 87 | def test_addition_carryover(): 88 | a = LinkedList([2, 4, 3]) 89 | b = LinkedList([5, 6, 4]) 90 | assert add_numbers(a, b) == LinkedList([7, 0, 8]) 91 | 92 | 93 | def test_addition_multi_carryover(): 94 | a = LinkedList([9, 3, 5]) 95 | b = LinkedList([2, 8, 9]) 96 | assert add_numbers(a, b) == LinkedList([1, 2, 5, 1]) 97 | 98 | 99 | def test_add_unequal_numbers(): 100 | a = LinkedList([9, 9, 1]) 101 | b = LinkedList([1]) 102 | assert add_numbers(a, b) == LinkedList([0, 0, 2]) 103 | 104 | c = LinkedList([1, 5]) 105 | d = LinkedList([5, 5, 9, 9]) 106 | assert add_numbers(c, d) == LinkedList([6, 0, 0, 0, 1]) 107 | -------------------------------------------------------------------------------- /challenges/array/merge_overlapping_intervals.py: -------------------------------------------------------------------------------- 1 | class Interval: 2 | "Interval spans from start over to end" 3 | def __init__(self, s=0, e=0): 4 | self.start = s 5 | self.end = e 6 | 7 | def merge(self, other): 8 | "Merge interval with other interval and return merged interval" 9 | return Interval( 10 | min(self.start, other.start), 11 | max(self.end, other.end) 12 | ) 13 | 14 | def __repr__(self): 15 | return '({},{})'.format(self.start, self.end) 16 | 17 | 18 | def merge_overlapping_intervals(intervals): 19 | """ 20 | Given a collection of intervals, merge all overlapping intervals. 21 | 22 | Given the list of intervals A. 23 | 24 | A = [Interval(1, 3), Interval(2, 6), 25 | Interval(8, 10), Interval(15, 18)] 26 | 27 | The function should return the list of merged intervals. 28 | 29 | A = [Interval(1, 6), Interval(8, 10), Interval(15, 18)] 30 | """ 31 | intervals.sort(key=lambda i: i.start) 32 | if len(intervals) == 0: 33 | return intervals 34 | 35 | merged_intervals = [intervals.pop(0)] 36 | 37 | # O(n) 38 | while len(intervals) > 0: 39 | prev_interval = merged_intervals[-1] 40 | interval = intervals.pop(0) 41 | 42 | if prev_interval.end >= interval.start: 43 | merged_intervals[-1] = prev_interval.merge(interval) 44 | else: 45 | merged_intervals.append(interval) 46 | 47 | return merged_intervals 48 | 49 | 50 | def test_merge_overlapping_intervals(): 51 | intervals = [(47, 76), (51, 99), (28, 78), (54, 94), 52 | (12, 72), (31, 72), (12, 55), (24, 40), 53 | (59, 79), (41, 100), (46, 99), (5, 27), 54 | (13, 23), (9, 69), (39, 75), (51, 53), 55 | (81, 98), (14, 14), (27, 89), (73, 78), 56 | (28, 35), (19, 30), (39, 87), (60, 94), 57 | (71, 90), (9, 44), (56, 79), (58, 70), 58 | (25, 76), (18, 46), (14, 96), (43, 95), 59 | (70, 77), (13, 59), (52, 91), (47, 56), 60 | (63, 67), (28, 39), (51, 92), (30, 66), 61 | (4, 4), (29, 92), (58, 90), (6, 20), 62 | (31, 93), (52, 75), (41, 41), (64, 89), 63 | (64, 66), (24, 90), (25, 46), (39, 49), 64 | (15, 99), (50, 99), (9, 34), (58, 96), 65 | (85, 86), (13, 68), (45, 57), (55, 56), 66 | (60, 74), (89, 98), (23, 79), (16, 59), 67 | (56, 57), (54, 85), (16, 29), (72, 86), 68 | (10, 45), (6, 25), (19, 55), (21, 21), 69 | (17, 83), (49, 86), (67, 84), (8, 48), 70 | (63, 85), (5, 31), (43, 48), (57, 62), 71 | (22, 68), (48, 92), (36, 77), (27, 63), 72 | (39, 83), (38, 54), (31, 69), (36, 65), 73 | (52, 68)] 74 | 75 | intervals = [Interval(i[0], i[1]) for i in intervals] 76 | merged_intervals = merge_overlapping_intervals(intervals) 77 | merged_intervals = [(i.start, i.end) for i in merged_intervals] 78 | assert merged_intervals == [(4, 4), (5, 100)] 79 | -------------------------------------------------------------------------------- /challenges/trees/least_common_ancestor.py: -------------------------------------------------------------------------------- 1 | """ 2 | Find the lowest common ancestor in an unordered binary tree given two 3 | values in the tree. 4 | 5 | _______3______ 6 | / \ 7 | ___5__ ___1__ 8 | / \ / \ 9 | 6 _2_ 0 8 10 | / \ 11 | 7 4 12 | 13 | For the above tree, the least common ancestor of nodes 5 and 0 is 3. 14 | """ 15 | 16 | 17 | class TreeNode(): 18 | def __init__(self, x): 19 | self.val = x 20 | self.left = None 21 | self.right = None 22 | 23 | def __repr__(self): 24 | return ''.format(self.val) 25 | 26 | 27 | def least_common_ancestor(root, a, b): 28 | # find a in O(log n) 29 | # find b in O(log n) 30 | # compare paths of a and b to find a common ancestor 31 | # this works because there are no duplicates 32 | 33 | path_a = find_path(root, a, []) 34 | path_b = find_path(root, b, []) 35 | return find_least_common_ancestor_in_paths(path_a, path_b) 36 | 37 | 38 | def find_path(node, val, previous_path): 39 | if node is None: 40 | return [] 41 | 42 | if node.val == val: 43 | return previous_path + [node.val] 44 | 45 | left_path = find_path(node.left, val, previous_path + [node.val]) 46 | if len(left_path) > 0: 47 | return left_path 48 | 49 | right_path = find_path(node.right, val, previous_path + [node.val]) 50 | if len(right_path) > 0: 51 | return right_path 52 | 53 | return [] 54 | 55 | 56 | def find_least_common_ancestor_in_paths(path_a, path_b): 57 | # 2 * O(n) 58 | lookup = set(path_a) 59 | 60 | # O(n) * O(1) 61 | for val in reversed(path_b): 62 | if val in lookup: 63 | return val 64 | 65 | return None 66 | 67 | 68 | def create_test_tree(): 69 | n3 = TreeNode(3) 70 | 71 | n5 = TreeNode(5) 72 | n3.left = n5 73 | n5.left = TreeNode(6) 74 | 75 | n2 = TreeNode(2) 76 | n5.right = n2 77 | n2.left = TreeNode(7) 78 | n2.right = TreeNode(4) 79 | 80 | n1 = TreeNode(1) 81 | n3.right = n1 82 | 83 | n1.left = TreeNode(0) 84 | n1.right = TreeNode(8) 85 | 86 | return n3 87 | 88 | 89 | def test_find_path(): 90 | root = create_test_tree() 91 | assert find_path(root, 5, []) == [3, 5] 92 | assert find_path(root, 7, []) == [3, 5, 2, 7] 93 | assert find_path(root, 0, []) == [3, 1, 0] 94 | assert find_path(root, 10, []) == [] 95 | 96 | 97 | def test_find_least_common_ancestor_in_paths(): 98 | assert find_least_common_ancestor_in_paths([3, 4, 1, 2], [3, 4]) == 4 99 | assert find_least_common_ancestor_in_paths([3, 1, 0, 8], [3, 5, 2, 4]) == 3 100 | assert find_least_common_ancestor_in_paths([3, 5, 2, 4], [3, 5, 6]) == 5 101 | 102 | 103 | def test_find_least_common_ancestor(): 104 | root = create_test_tree() 105 | assert least_common_ancestor(root, 5, 1) == 3 106 | assert least_common_ancestor(root, 5, 4) == 5 107 | assert least_common_ancestor(root, 2, 0) == 3 108 | assert least_common_ancestor(root, 7, 4) == 2 109 | -------------------------------------------------------------------------------- /challenges/linked_list/reverse_linked_list_partial.py: -------------------------------------------------------------------------------- 1 | class ListNode: 2 | def __init__(self, x): 3 | self.val = x 4 | self.next = None 5 | 6 | def __eq__(self, other): 7 | return self.val == other.val and self.next == other.next 8 | 9 | def __repr__(self): 10 | return ''.format(self.val, self.next) 11 | 12 | 13 | class LinkedList(ListNode): 14 | def __init__(self, arr): 15 | 16 | nodes = [ListNode(v) for v in arr] 17 | for i in range(1, len(nodes)): 18 | nodes[i-1].next = nodes[i] 19 | 20 | head = nodes[0] 21 | 22 | self.val = head.val 23 | self.next = head.next 24 | 25 | 26 | def reverse_sublist(succeeding_tail, sublist_len): 27 | if succeeding_tail.next: 28 | sublist_head, sublist_tail, rest = reverse_linked_list(succeeding_tail.next, sublist_len) 29 | 30 | # Relink the reversed sublist if 31 | succeeding_tail.next = sublist_head 32 | sublist_tail.next = rest 33 | 34 | 35 | def reverse_linked_list_positional(head, m, n): 36 | """ 37 | Reverse a linked list from position m to n. 38 | """ 39 | cursor = head 40 | 41 | if m == 1: 42 | dummy = ListNode("Dummy") 43 | dummy.next = cursor 44 | reverse_sublist(dummy, n + 1 - m) 45 | return dummy.next 46 | 47 | pos = 1 48 | while cursor: 49 | pos += 1 50 | if pos < m: 51 | cursor = cursor.next 52 | else: 53 | break 54 | 55 | reverse_sublist(cursor, n + 1 - m) 56 | 57 | return head 58 | 59 | 60 | def reverse_linked_list(head, max_node_count): 61 | """ 62 | Reverse linked list but only up to max_node_count. 63 | Return the new head and tail of the linked list. 64 | """ 65 | if not head: 66 | return None, None, None 67 | 68 | count = 1 69 | first_node = head 70 | second_node = head.next 71 | head.next = None # Make sure list has no cycle 72 | 73 | while second_node and count < max_node_count: 74 | third_node = second_node.next 75 | second_node.next = first_node 76 | 77 | first_node = second_node 78 | second_node = third_node 79 | 80 | count += 1 81 | 82 | rest = second_node 83 | tail = head # Old head is the new tail 84 | head = first_node # Head is now the last node 85 | 86 | return head, tail, rest 87 | 88 | 89 | def test_reverse_linked_list(): 90 | a = LinkedList([5, 4, 3, 2, 1]) 91 | head, tail, rest = reverse_linked_list(a, 5) 92 | assert head == LinkedList([1, 2, 3, 4, 5]) 93 | assert tail == ListNode(5) 94 | assert rest is None 95 | 96 | 97 | def test_reverse_linked_list_partially(): 98 | a = LinkedList([7, 6, 5, 4, 3, 2, 1]) 99 | head, tail, rest = reverse_linked_list(a, 4) 100 | assert head == LinkedList([4, 5, 6, 7]) 101 | assert tail == ListNode(7) 102 | assert rest == LinkedList([3, 2, 1]) 103 | 104 | 105 | def test_reverse_linked_list_positional(): 106 | a = LinkedList([1, 2, 3, 4, 5]) 107 | head = reverse_linked_list_positional(a, m=2, n=4) 108 | assert head == LinkedList([1, 4, 3, 2, 5]) 109 | 110 | 111 | def test_reverse_linked_list_short(): 112 | a = LinkedList([1]) 113 | head = reverse_linked_list_positional(a, m=1, n=1) 114 | assert head == LinkedList([1]) 115 | 116 | b = LinkedList([1, 2]) 117 | head = reverse_linked_list_positional(b, m=1, n=2) 118 | assert head == LinkedList([2, 1]) 119 | -------------------------------------------------------------------------------- /challenges/trees/shortest_unique_prefix.py: -------------------------------------------------------------------------------- 1 | """ 2 | Find shortest unique prefix to represent each word in the list. 3 | 4 | Input: [zebra, dog, duck, dove] 5 | Output: {z, dog, du, dov} 6 | 7 | where we can see that 8 | zebra = z 9 | dog = dog 10 | duck = du 11 | dove = dov 12 | 13 | Approach: Build a trie from the words first. 14 | 15 | root 16 | / \ 17 | zebra d 18 | / / \ 19 | uck o 20 | / \ 21 | ve g 22 | """ 23 | 24 | 25 | def insert(node, word): 26 | if len(word) == 0: 27 | return 28 | 29 | for child in node.children: 30 | if child.char[0] == word[0]: 31 | insert(child, word[1:]) 32 | return 33 | 34 | if len(node.char) > 1: 35 | replacement_node = TrieNode(node.char[1:]) 36 | node.children.append(replacement_node) 37 | node.char = node.char[0] 38 | insert(node, word) 39 | return 40 | 41 | new_node = TrieNode(word) 42 | node.children.append(new_node) 43 | 44 | 45 | def find_prefix(node, prefix, word): 46 | for child in node.children: 47 | if child.char == word: 48 | return prefix + [node.char, child.char[0]] 49 | 50 | if child.char[0] == word[0]: 51 | return find_prefix(child, prefix + [node.char], word[1:]) 52 | 53 | return [] 54 | 55 | 56 | class TrieNode(): 57 | def __init__(self, c): 58 | self.char = c 59 | self.children = [] 60 | 61 | def __repr__(self): 62 | return ''.format(self.char) 63 | 64 | 65 | def create_trie(words): 66 | root = TrieNode('') 67 | for word in words: 68 | insert(root, word) 69 | return root 70 | 71 | 72 | def shortest_unique_prefix(words): 73 | trie = create_trie(words) 74 | return [''.join(find_prefix(trie, prefix=[], word=w)) for w in words] 75 | 76 | 77 | def test_shortest_unique_prefix_no_words(): 78 | assert shortest_unique_prefix([]) == [] 79 | 80 | 81 | def test_shortest_unique_prefix_one_word(): 82 | assert shortest_unique_prefix(['zebra']) == ['z'] 83 | 84 | 85 | def test_shortest_unique_prefix(): 86 | words = ['zebra', 'dog', 'duck', 'dove'] 87 | assert shortest_unique_prefix(words) == ['z', 'dog', 'du', 'dov'] 88 | 89 | 90 | def test_shortest_unique_prefix_same_start(): 91 | words = ['bearcat', 'bert'] 92 | assert shortest_unique_prefix(words) == ['bea', 'ber'] 93 | 94 | 95 | def test_create_trie_same_start(): 96 | words = ['bearcat', 'bert'] 97 | trie = create_trie(words) 98 | 99 | assert len(trie.children) == 1 100 | assert trie.children[0].char == 'b' 101 | assert trie.children[0].children[0].char == 'e' 102 | assert trie.children[0].children[0].children[0].char == 'arcat' 103 | assert trie.children[0].children[0].children[1].char == 'rt' 104 | 105 | 106 | def test_create_trie(): 107 | words = ['zebra', 'dog', 'duck', 'dove'] 108 | trie = create_trie(words) 109 | 110 | assert len(trie.children) == 2 111 | assert trie.children[0].char == 'zebra' 112 | assert trie.children[1].char == 'd' 113 | 114 | assert len(trie.children[0].children) == 0 115 | assert len(trie.children[1].children) == 2 116 | 117 | assert trie.children[1].children[0].char == 'o' 118 | assert trie.children[1].children[1].char == 'uck' 119 | 120 | assert trie.children[1].children[0].children[0].char == 'g' 121 | assert trie.children[1].children[0].children[1].char == 've' 122 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | CC0 1.0 Universal 2 | 3 | Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an "owner") of an original work of 8 | authorship and/or a database (each, a "Work"). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific 12 | works ("Commons") that the public can reliably and without fear of later 13 | claims of infringement build upon, modify, incorporate in other works, reuse 14 | and redistribute as freely as possible in any form whatsoever and for any 15 | purposes, including without limitation commercial purposes. These owners may 16 | contribute to the Commons to promote the ideal of a free culture and the 17 | further production of creative, cultural and scientific works, or to gain 18 | reputation or greater distribution for their Work in part through the use and 19 | efforts of others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation 22 | of additional consideration or compensation, the person associating CC0 with a 23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work 25 | and publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights ("Copyright and 31 | Related Rights"). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 34 | i. the right to reproduce, adapt, distribute, perform, display, communicate, 35 | and translate a Work; 36 | 37 | ii. moral rights retained by the original author(s) and/or performer(s); 38 | 39 | iii. publicity and privacy rights pertaining to a person's image or likeness 40 | depicted in a Work; 41 | 42 | iv. rights protecting against unfair competition in regards to a Work, 43 | subject to the limitations in paragraph 4(a), below; 44 | 45 | v. rights protecting the extraction, dissemination, use and reuse of data in 46 | a Work; 47 | 48 | vi. database rights (such as those arising under Directive 96/9/EC of the 49 | European Parliament and of the Council of 11 March 1996 on the legal 50 | protection of databases, and under any national implementation thereof, 51 | including any amended or successor version of such directive); and 52 | 53 | vii. other similar, equivalent or corresponding rights throughout the world 54 | based on applicable law or treaty, and any national implementations thereof. 55 | 56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright 59 | and Related Rights and associated claims and causes of action, whether now 60 | known or unknown (including existing as well as future claims and causes of 61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 62 | duration provided by applicable law or treaty (including future time 63 | extensions), (iii) in any current or future medium and for any number of 64 | copies, and (iv) for any purpose whatsoever, including without limitation 65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes 66 | the Waiver for the benefit of each member of the public at large and to the 67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver 68 | shall not be subject to revocation, rescission, cancellation, termination, or 69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work 70 | by the public as contemplated by Affirmer's express Statement of Purpose. 71 | 72 | 3. Public License Fallback. Should any part of the Waiver for any reason be 73 | judged legally invalid or ineffective under applicable law, then the Waiver 74 | shall be preserved to the maximum extent permitted taking into account 75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver 76 | is so judged Affirmer hereby grants to each affected person a royalty-free, 77 | non transferable, non sublicensable, non exclusive, irrevocable and 78 | unconditional license to exercise Affirmer's Copyright and Related Rights in 79 | the Work (i) in all territories worldwide, (ii) for the maximum duration 80 | provided by applicable law or treaty (including future time extensions), (iii) 81 | in any current or future medium and for any number of copies, and (iv) for any 82 | purpose whatsoever, including without limitation commercial, advertising or 83 | promotional purposes (the "License"). The License shall be deemed effective as 84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the 85 | License for any reason be judged legally invalid or ineffective under 86 | applicable law, such partial invalidity or ineffectiveness shall not 87 | invalidate the remainder of the License, and in such case Affirmer hereby 88 | affirms that he or she will not (i) exercise any of his or her remaining 89 | Copyright and Related Rights in the Work or (ii) assert any associated claims 90 | and causes of action with respect to the Work, in either case contrary to 91 | Affirmer's express Statement of Purpose. 92 | 93 | 4. Limitations and Disclaimers. 94 | 95 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 96 | surrendered, licensed or otherwise affected by this document. 97 | 98 | b. Affirmer offers the Work as-is and makes no representations or warranties 99 | of any kind concerning the Work, express, implied, statutory or otherwise, 100 | including without limitation warranties of title, merchantability, fitness 101 | for a particular purpose, non infringement, or the absence of latent or 102 | other defects, accuracy, or the present or absence of errors, whether or not 103 | discoverable, all to the greatest extent permissible under applicable law. 104 | 105 | c. Affirmer disclaims responsibility for clearing rights of other persons 106 | that may apply to the Work or any use thereof, including without limitation 107 | any person's Copyright and Related Rights in the Work. Further, Affirmer 108 | disclaims responsibility for obtaining any necessary consents, permissions 109 | or other rights required for any use of the Work. 110 | 111 | d. Affirmer understands and acknowledges that Creative Commons is not a 112 | party to this document and has no duty or obligation with respect to this 113 | CC0 or use of the Work. 114 | 115 | For more information, please see 116 | 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sharpening my programming skills 2 | 3 | mbtoolbox 4 | 5 | This is a collection of small (< 1h) Python challenges 6 | to keep your Python skills sharp. 7 | 8 | ## Challenges 9 | 10 | The challenges are explained in detail 11 | in the comments of the corresponding Python files. 12 | 13 | ### Arrays 14 | 15 | - [x] **[zero_matrix.py](challenges/array/zero_matrix.py)**: Zero entire column and row in 2D matrix if element is `0`. 16 | - [x] **[merge_overlapping_intervals.py](challenges/array/merge_overlapping_intervals.py)**: Merge overlapping intervals in array together. 17 | - [x] **[rotate_matrix.py](challenges/array/rotate_matrix.py)**: Rotate 2D matrix 90 degrees. 18 | - [x] **[spiral_order.py](challenges/array/spiral_order.py)**: Return a 2D matrix in spiral order. 19 | - [x] **[temp_tracker.py](challenges/array/temp_tracker.py)**: Temperature tracker to calculate mean, mode, min, max of temperature events. 20 | - [x] **[max_consecutive_gap.py](challenges/array/max_consecutive_gap.py)**: Find max consecutive gap of sorted form of unsorted numbers. 21 | - [x] **[find_permutation.py](challenges/array/find_permutation.py)**: Find first permutation based on two conditions. 22 | - [x] **[pascal_triangle.py](challenges/array/pascal_triangle.py)**: Generate the first n rows of the Pascal's triangle. 23 | - [x] **[wave_array.py](challenges/array/wave_array.py)**: Sort array in a wave-like pattern. 24 | - [x] **[sorted_array_intersection.py](challenges/array/sorted_array_intersection.py)**: Find elements that occur in both of the two sorted arrays. 25 | - [x] **[rearrange_array.py](challenges/array/rearrange_array.py)**: Rearrange array so that the values become the indizes. 26 | - [x] **[min_steps_infinite_grid.py](challenges/array/min_steps_infinite_grid.py)**: Cover given points in 2D grid in minimum steps. 27 | - [ ] **[max_subarray.py](challenges/array/max_subarray.py)**: Calculate the max sum of a subarray. 28 | - [ ] **[array_duplicate.py](challenges/array/array_duplicate.py)**: Find the first duplicate in an array of integers. 29 | 30 | ### Math 31 | 32 | - [x] **[palindrome_integer.py](challenges/math/palindrome_integer.py)**: Check if integer is palindrome without converting it to text. 33 | - [x] **[excel_column_number.py](challenges/math/excel_column_number.py)**: Return column number based on Excel encoded column title. 34 | - [x] **[factorial_trailing_zeros.py](challenges/math/factorial_trailing_zeros.py)**: Count trailing zeros in factorial (`n!`). 35 | - [x] **[greatest_common_divisor.py](challenges/math/greatest_common_divisor.py)**: Greatest common divisor of two numbers. 36 | - [ ] **[two_egg_problem.py](challenges/math/two_egg_problem.py)**: Solve the [Two Egg Problem](http://datagenetics.com/blog/july22012/index.html) 37 | 38 | ### Strings 39 | 40 | - [x] **[palindrome_string.py](challenges/string/palindrome_string.py)**: Check if sentence is a palindrome. 41 | - [x] **[needle_haystack.py](challenges/string/needle_haystack.py)**: Find substring in a string (without fancy KMP or Boyer-Moore). 42 | - [x] **[zigzag_string.py](challenges/string/zigzag_string.py)**: Print text in a zigzag pattern to the screen. 43 | - [x] **[compare_version_numbers.py](challenges/string/compare_version_numbers.py)**: Compare version strings like v1.1 and v1.2. 44 | - [x] **[longest_palindromic_substring.py](challenges/string/longest_palindromic_substring.py)**: Find longest palindrome substring. 45 | - [x] **[roman_to_integer.py](challenges/string/roman_to_integer.py)**: Calculate integer from roman literals. 46 | - [x] **[integer_to_roman.py](challenges/string/integer_to_roman.py)**: Create roman literal from integer. 47 | - [x] **[length_last_word.py](challenges/string/length_last_word.py)**: Calculate length of last word in sentence. 48 | - [x] **[longest_common_prefix.py](challenges/string/longest_common_prefix.py)**: Find longest common prefix in all strings. 49 | - [x] **[reverse_string_inplace.py](challenges/string/reverse_string_inplace.py)**: Reverse string or array in place. 50 | - [ ] **[string_multiplication.py](challenges/string/string_multiplication.py)**: Multiply two numbers represented as strings without big integer libraries. 51 | 52 | ### Bit Manipulation 53 | 54 | - [x] **[bit_single_number.py](challenges/bit_manipulation/bit_single_number.py)**: Find single number in array of pairs. 55 | - [x] **[power_of_2.py](challenges/bit_manipulation/power_of_2.py)**: Check if number is power of 2. 56 | 57 | ### Two Pointers 58 | 59 | - [x] **[diffk.py](challenges/two_pointers/diffk.py)**: Find two values in sorted array with a difference of k. 60 | - [x] **[container_most_water.py](challenges/two_pointers/container_most_water.py)**: Based on vertical lines on a x-axis find the lines having the largest area. 61 | - [ ] **[rectangle_intersection.py](challenges/two_pointers/rectangle_intersection.py)**: Find intersection of two rectangles. 62 | 63 | ### Binary Search 64 | 65 | - [ ] **[binary_matrix_search.py](challenges/binary_search/binary_matrix_search.py)**: Test for existence of element in matrix using binary search. 66 | - [ ] **[rotated_sorted_array_search.py](challenges/binary_search/rotated_sorted_array_search.py)**: A sorted array is rotated at one place :interrobang:. 67 | - [x] **[sqrt.py](challenges/binary_search/sqrt.py)**: Calculate square root using binary search. 68 | - [x] **[sorted_insert_position.py](challenges/binary_search/sorted_insert_position.py)**: Return found index of element or index where to insert it. 69 | - [x] **[search_range.py](challenges/binary_search/search_range.py)**: Find range (start and end index) of a value in a sorted list. 70 | - [x] **[binary_occurrence_search.py](challenges/binary_search/binary_occurrence_search.py)**: Count number of occurences of target value in sorted list. 71 | - [x] **[power.py](challenges/binary_search/power.py)**: Implement `pow(x, n) % d`. 72 | 73 | ### Stack 74 | 75 | - [ ] **[generate_parentheses.py](challenges/stack/generate_parentheses.py)**: Validate parentheses 76 | - [ ] **[min_stack.py](challenges/stack/min_stack.py)**: Create a min stack preserving order of elements 77 | - [ ] **[nearest_smallest_element.py](challenges/stack/nearest_smallest_element.py)**: Find nearest element in array 78 | - [ ] **[simplify_directory_path.py](challenges/stack/simplify_directory_path.py)**: Simplify UNIX path 79 | - [x] **[evaluate_expression.py](challenges/stack/evaluate_expression.py)**: Evaluate the value of an arithmetic expression in Reverse Polish Notation. 80 | 81 | 82 | ### Linked Lists 83 | 84 | - [ ] **[interesection_linked_list.py](challenges/linked_list/interesection_linked_list.py)**: Find intersection of two linked lists 85 | - [ ] **[swap_list_nodes_in_pairs.py](challenges/linked_list/swap_list_nodes_in_pairs.py)**: Swap nodes in linked list in pairs 86 | - [ ] **[merge_sorted_lists.py](challenges/linked_list/merge_sorted_lists.py)**: Merge sorted linked lists 87 | - [ ] **[partition_list.py](challenges/linked_list/partition_list.py)**: Partition linked list at pivot 88 | - [ ] **[add_two_numbers_list.py](challenges/linked_list/add_two_numbers_list.py)**: Add to numbers as linked lists 89 | - [ ] **[list_cycle.py](challenges/linked_list/list_cycle.py)**: Detect cycle in linked list 90 | - [x] **[reverse_linked_list.py](challenges/linked_list/reverse_linked_list.py)**: Reverse linked list 91 | - [x] **[reverse_linked_list_partial.py](challenges/linked_list/reverse_linked_list_partial.py)**: Reverse linked list but only up to a certain node count. 92 | 93 | ### Backtracking / Recursion 94 | 95 | - [ ] **[modular_exponentiation.py](challenges/backtracking/modular_exponentiation.py)**: Modular exponentiation is a type of exponentiation performed over a modulus. 96 | - [ ] **[permutations.py](challenges/backtracking/permutations.py)**: Generate all permutations in a list 97 | - [ ] **[unique_permutations.py](challenges/backtracking/unique_permutations.py)**: Calculate unique permutations 98 | - [ ] **[letter_phone.py](challenges/backtracking/letter_phone.py)**: Generate all possible digits on letter phone 99 | - [ ] **[gen_parens.py](challenges/backtracking/gen_parens.py)**: Given n pairs of parentheses generate all combinations of well formed parentheses. 100 | - [x] **[combinations.py](challenges/backtracking/combinations.py)**: Given n numbers generate all possible k combinations 101 | 102 | 103 | ### Hashing 104 | 105 | - [ ] **[two_sum.py](challenges/hashing/two_sum.py)**: Given an array of integers, find two numbers such that they add up to a specific target number 106 | - [ ] **[points_on_straight_line.py](challenges/hashing/points_on_straight_line.py)**: Given n points on 2D pane calculate max number of points on one straight line 107 | - [ ] **[longest_substring_no_repeat.py](challenges/hashing/longest_substring_no_repeat.py)**: Calculate longest substring with unique characters 108 | 109 | ### Maps 110 | - [x] **[anagrams.py](challenges/maps/anagrams.py)**: Given list of words return all words that are anagram of a given string. 111 | - [ ] **[distinct_numbers_in_window.py](challenges/hashing/distinct_numbers_in_window.py)**: Given list of integers return the distinct numbers in window of size k. 112 | - [x] **[majority_element.py](challenges/backtracking/majority_element.py)**: Given an array find the majority element 113 | 114 | ### Trees 115 | 116 | - [x] **[identical_binary_tree.py](challenges/trees/identical_binary_tree.py)**: Compare two binary trees 117 | - [x] **[inorder_traversal.py](challenges/trees/inorder_traversal.py)**: Binary tree inorder traversal 118 | - [x] **[postorder_traversal.py](challenges/trees/postorder_traversal.py)**: Binary tree postorder traversal 119 | - [x] **[preorder_traversal.py](challenges/trees/preorder_traversal.py)**: Binary tree preorder traversal 120 | - [x] **[sorted_array_balanced_bst.py](challenges/trees/sorted_array_balanced_bst.py)**: Turn sorted array into balanced BST 121 | - [x] **[invert_binary_tree.py](challenges/trees/invert_binary_tree.py)**: Invert binary tree 122 | - [ ] **[path_sum.py](challenges/trees/path_sum.py)**: Compare path sum of leaf nodes 123 | - [ ] **[flatten_binary_tree_linked_list.py](challenges/trees/flatten_binary_tree_linked_list.py)**: Flatten a binary tree into a right sided linked list 124 | - [ ] **[balanced_binary_tree.py](challenges/trees/balanced_binary_tree.py)**: Check if a tree is balanced 125 | - [ ] **[kth_smallest_tree_elem.py](challenges/trees/kth_smallest_tree_elem.py)**: Find the kth smallest element in a binary search tree 126 | - [ ] **[valid_bst.py](challenges/trees/valid_bst.py)**: Check if binary tree is valid BST 127 | - [ ] **[zigzag_level_bst_traversal.py](challenges/trees/zigzag_level_bst_traversal.py)**: Traverse BST in zig ziag level order 128 | - [ ] **[max_tree_depth.py](challenges/trees/max_tree_depth.py)**: Longest path to leaf 129 | - [ ] **[min_tree_depth.py](challenges/trees/min_tree_depth.py)**: Shortest path to leaf 130 | - [ ] **[sum_root_leaf_numbers.py](challenges/trees/sum_root_leaf_numbers.py)**: Sum all root-to-leaf paths (node values are digits) 131 | - [ ] **[merge_k_sorted_lists.py](challenges/trees/merge_k_sorted_lists.py)**: Merge k sorted lists into one 132 | - [ ] **[count_inversions.py](challenges/trees/count_inversions.py)**: Count the number of inversions in an array. 133 | - [ ] **[symmetric_binary_tree.py](challenges/trees/symmetric_binary_tree.py)**: Check if two binary trees are symmetric. 134 | - [x] **[least_common_ancestor.py](challenges/trees/least_common_ancestor.py)**: Find the least common ancestor of two values in an unordered binary tree. 135 | - [x] **[shortest_unique_prefix.py](challenges/trees/shortest_unique_prefix.py)**: Find the shortest unique prefix for each word in a set of words. 136 | - [x] **[order_people_heights.py](challenges/trees/order_people_heights.py)**: Order people with unique heights by the constraint how many taller people are standing in front of them. 137 | 138 | ### Graphs 139 | - [ ] **[level_order.py](challenges/graphs/level_order.py)**: Traverse tree in level order (BFS) 140 | - [ ] **[black_shapes.py](challenges/graphs/black_shapes.py)**: Given a board of black and white fields find the number of black connected shapes 141 | 142 | Greedy 143 | ------ 144 | 145 | - [x] **[trading_stock.py](challenges/greedy/trading_stock.py)**: Figure out max profit given the stock prices of yesterday. 146 | - [x] **[bulbs.py](challenges/greedy/bulbs.py)**: Given wrong wired light bulbs find the min number of switches to press to turn on all the bulbs. 147 | - [x] **[mice_holes.py](challenges/greedy/mice_holes.py)**: Given M mice and N holes on a straight line find min number of moves to assign all mice to the holes. 148 | --------------------------------------------------------------------------------