├── requirements.txt ├── algorithms ├── __init__.py ├── tree │ ├── __init__.py │ ├── avl │ │ └── __init__.py │ ├── traversal │ │ ├── __init__.py │ │ ├── level_order.py │ │ ├── preorder.py │ │ ├── postorder.py │ │ └── zigzag.py │ ├── tree.py │ ├── bst │ │ ├── successor.py │ │ ├── predecessor.py │ │ ├── array_to_bst.py │ │ ├── BSTIterator.py │ │ ├── bst_closest_value.py │ │ ├── serialize_deserialize.py │ │ ├── is_bst.py │ │ ├── unique_bst.py │ │ └── lowest_common_ancestor.py │ ├── invert_tree.py │ ├── max_path_sum.py │ ├── binary_tree_paths.py │ ├── same_tree.py │ ├── pretty_print.py │ ├── bin_tree_to_list.py │ ├── is_balanced.py │ ├── longest_consecutive.py │ ├── trie │ │ └── trie.py │ ├── deepest_left.py │ └── lowest_common_ancestor.py ├── matrix │ ├── __init__.py │ ├── sum_sub_squares.py │ ├── rotate_image.py │ ├── search_in_sorted_matrix.py │ ├── multiply.py │ └── matrix_exponentiation.py ├── compression │ └── __init__.py ├── distribution │ ├── __init__.py │ └── histogram.py ├── automata │ ├── __init__.py │ └── dfa.py ├── set │ ├── __init__.py │ └── find_keyboard_row.py ├── streaming │ └── __init__.py ├── greedy │ └── __init__.py ├── queues │ ├── __init__.py │ ├── zigzagiterator.py │ ├── reconstruct_queue.py │ ├── moving_average.py │ └── max_sliding_window.py ├── unix │ ├── __init__.py │ └── path │ │ ├── full_path.py │ │ ├── join_with_slash.py │ │ ├── split.py │ │ └── simplify_path.py ├── bfs │ └── __init__.py ├── heap │ ├── __init__.py │ └── sliding_window_max.py ├── dfs │ ├── __init__.py │ └── count_islands.py ├── map │ ├── __init__.py │ ├── valid_sudoku.py │ ├── is_anagram.py │ ├── longest_common_subsequence.py │ ├── is_isomorphic.py │ └── longest_palindromic_subsequence.py ├── bit │ ├── power_of_two.py │ ├── add_bitwise_operator.py │ ├── remove_bit.py │ ├── reverse_bits.py │ ├── count_flips_to_convert.py │ ├── __init__.py │ ├── single_number.py │ ├── find_missing_number.py │ ├── bytes_int_conversion.py │ ├── swap_pair.py │ ├── flip_bit_longest_sequence.py │ ├── find_difference.py │ ├── single_number2.py │ ├── count_ones.py │ ├── bit_operation.py │ └── has_alternative_bit.py ├── maths │ ├── num_digits.py │ ├── prime_check.py │ ├── nth_digit.py │ ├── hailstone.py │ ├── sqrt_precision_factor.py │ ├── euler_totient.py │ ├── combination.py │ ├── modular_exponential.py │ ├── next_perfect_square.py │ ├── extended_gcd.py │ ├── is_strobogrammatic.py │ ├── pythagoras.py │ ├── find_order_simple.py │ ├── __init__.py │ ├── fft.py │ ├── modular_inverse.py │ ├── decimal_to_binary_ip.py │ ├── recursive_binomial_coefficient.py │ ├── cosine_similarity.py │ ├── summing_digits.py │ ├── magic_number.py │ ├── power.py │ ├── base_conversion.py │ └── factorial.py ├── linkedlist │ ├── __init__.py │ ├── is_sorted.py │ ├── is_cyclic.py │ ├── swap_in_pairs.py │ ├── linkedlist.py │ ├── reverse.py │ ├── rotate_list.py │ ├── merge_two_list.py │ ├── remove_range.py │ └── copy_random_pointer.py ├── strings │ ├── check_pangram.py │ ├── license_number.py │ ├── reverse_vowel.py │ ├── reverse_string.py │ ├── reverse_words.py │ ├── int_to_roman.py │ ├── delete_reoccurring.py │ ├── add_binary.py │ ├── roman_to_int.py │ ├── group_anagrams.py │ ├── rotate.py │ ├── repeat_substring.py │ ├── first_unique_char.py │ ├── atbash_cipher.py │ ├── contain_string.py │ ├── caesar_cipher.py │ ├── make_sentence.py │ ├── is_rotated.py │ ├── repeat_string.py │ ├── judge_circle.py │ ├── encode_decode.py │ ├── one_edit_distance.py │ ├── multiply_strings.py │ ├── domain_extractor.py │ ├── longest_palindromic_substring.py │ ├── panagram.py │ └── count_binary_substring.py ├── stack │ ├── __init__.py │ ├── valid_parenthesis.py │ ├── remove_min.py │ ├── simplify_path.py │ ├── is_sorted.py │ └── ordered_stack.py ├── dp │ ├── max_subarray.py │ ├── house_robber.py │ ├── __init__.py │ ├── rod_cut.py │ ├── climbing_stairs.py │ ├── knapsack.py │ ├── word_break.py │ ├── int_divide.py │ └── coin_change.py ├── graph │ ├── __init__.py │ ├── markov_chain.py │ ├── prims_minimum_spanning.py │ ├── check_bipartite.py │ └── all_pairs_shortest_path.py ├── sort │ ├── exchange_sort.py │ ├── wiggle_sort.py │ ├── gnome_sort.py │ ├── meeting_rooms.py │ ├── shell_sort.py │ ├── __init__.py │ ├── pigeonhole_sort.py │ ├── selection_sort.py │ ├── comb_sort.py │ ├── insertion_sort.py │ ├── pancake_sort.py │ ├── sort_colors.py │ ├── cocktail_shaker_sort.py │ ├── bogo_sort.py │ ├── bucket_sort.py │ ├── radix_sort.py │ ├── bubble_sort.py │ ├── stooge_sort.py │ ├── quick_sort.py │ └── bead_sort.py ├── search │ ├── linear_search.py │ ├── __init__.py │ ├── search_insert.py │ ├── last_occurrence.py │ ├── first_occurrence.py │ ├── search_range.py │ ├── find_min_rotate.py │ └── jump_search.py ├── backtrack │ ├── anagram.py │ ├── __init__.py │ ├── permute_unique.py │ ├── subsets_unique.py │ ├── generate_abbreviations.py │ ├── letter_combination.py │ ├── combination_sum.py │ ├── generate_parenthesis.py │ └── subsets.py ├── arrays │ ├── __init__.py │ ├── missing_ranges.py │ ├── two_sum.py │ ├── josephus.py │ ├── summarize_ranges.py │ ├── move_zeros.py │ ├── limit.py │ ├── remove_duplicates.py │ ├── trimmean.py │ ├── delete_nth.py │ ├── flatten.py │ ├── top_1.py │ ├── max_ones_index.py │ └── plus_one.py └── ml │ └── nearest_neighbor.py ├── docs ├── source │ ├── _static │ │ ├── logo │ │ │ ├── add │ │ │ ├── 128pxblue.png │ │ │ ├── 256pxblue.png │ │ │ ├── 512pxblue.png │ │ │ ├── 128pxblack.png │ │ │ ├── 128pxorange.png │ │ │ ├── 256pxblack.png │ │ │ ├── 256pxorange.png │ │ │ ├── 512pxblack.png │ │ │ ├── 512pxorange.png │ │ │ ├── logotype1black.png │ │ │ ├── logotype1blue.png │ │ │ ├── logotype2black.png │ │ │ ├── logotype2blue.png │ │ │ ├── logotype1orange.png │ │ │ ├── logotype2orange.png │ │ │ ├── 128pxblack.svg │ │ │ ├── 256pxblack.svg │ │ │ ├── 512pxblack.svg │ │ │ ├── 128pxblue.svg │ │ │ ├── 128pxorange.svg │ │ │ ├── 256pxblue.svg │ │ │ ├── 256pxorange.svg │ │ │ ├── 512pxblue.svg │ │ │ └── 512pxorange.svg │ │ └── algorithms_logo.png │ ├── bfs.rst │ ├── bit.rst │ ├── dfs.rst │ ├── dp.rst │ ├── examples.rst │ ├── map.rst │ ├── set.rst │ ├── graph.rst │ ├── heap.rst │ ├── maths.rst │ ├── matrix.rst │ ├── queues.rst │ ├── search.rst │ ├── sort.rst │ ├── stack.rst │ ├── tree.rst │ ├── strings.rst │ ├── backtrack.rst │ ├── linkedlist.rst │ ├── arrays.rst │ └── index.rst ├── requirements.txt ├── Makefile └── make.bat ├── MANIFEST.in ├── .coveragerc ├── test_requirements.txt ├── tests ├── test_set.py ├── test_histogram.py └── test_greedy.py ├── .gitignore ├── setup.py ├── .travis.yml ├── LICENSE ├── .github └── workflows │ └── python-app.yml └── tox.ini /requirements.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /algorithms/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /algorithms/tree/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /algorithms/matrix/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /algorithms/tree/avl/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/source/_static/logo/add: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /algorithms/compression/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /algorithms/distribution/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /algorithms/automata/__init__.py: -------------------------------------------------------------------------------- 1 | from .dfa import * 2 | -------------------------------------------------------------------------------- /algorithms/set/__init__.py: -------------------------------------------------------------------------------- 1 | from .find_keyboard_row import * 2 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | include LICENSE 3 | include algorithms/* 4 | -------------------------------------------------------------------------------- /algorithms/streaming/__init__.py: -------------------------------------------------------------------------------- 1 | from .one_sparse_recovery import * 2 | from .misra_gries import * 3 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [report] 2 | omit = 3 | */python?.?/* 4 | */site-packages/nose/* 5 | *__init__* 6 | -------------------------------------------------------------------------------- /algorithms/greedy/__init__.py: -------------------------------------------------------------------------------- 1 | from .max_contiguous_subsequence_sum import * 2 | from .gale_shapley import * -------------------------------------------------------------------------------- /test_requirements.txt: -------------------------------------------------------------------------------- 1 | flake8 2 | python-coveralls 3 | coverage 4 | nose 5 | pytest 6 | tox 7 | black 8 | -------------------------------------------------------------------------------- /docs/source/bfs.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | algorithms.bfs 5 | ================= 6 | -------------------------------------------------------------------------------- /docs/source/bit.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | algorithms.bit 5 | ================= 6 | -------------------------------------------------------------------------------- /docs/source/dfs.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | algorithms.dfs 5 | ================= 6 | -------------------------------------------------------------------------------- /docs/source/dp.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | algorithms.dp 5 | ================= 6 | -------------------------------------------------------------------------------- /docs/source/examples.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | Examples 5 | ================= 6 | -------------------------------------------------------------------------------- /docs/source/map.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | algorithms.map 5 | ================= 6 | -------------------------------------------------------------------------------- /docs/source/set.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | algorithms.set 5 | ================= 6 | -------------------------------------------------------------------------------- /algorithms/tree/traversal/__init__.py: -------------------------------------------------------------------------------- 1 | from .preorder import * 2 | from .postorder import * 3 | from .inorder import * 4 | -------------------------------------------------------------------------------- /docs/source/graph.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | algorithms.graph 5 | ================= 6 | -------------------------------------------------------------------------------- /docs/source/heap.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | algorithms.heap 5 | ================= 6 | -------------------------------------------------------------------------------- /docs/source/maths.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | algorithms.maths 5 | ================= 6 | -------------------------------------------------------------------------------- /docs/source/matrix.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | algorithms.matrix 5 | ================= 6 | -------------------------------------------------------------------------------- /docs/source/queues.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | algorithms.queue 5 | ================= 6 | -------------------------------------------------------------------------------- /docs/source/search.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | algorithms.search 5 | ================= 6 | -------------------------------------------------------------------------------- /docs/source/sort.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | algorithms.sort 5 | ================= 6 | -------------------------------------------------------------------------------- /docs/source/stack.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | algorithms.stack 5 | ================= 6 | -------------------------------------------------------------------------------- /docs/source/tree.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | algorithms.tree 5 | ================= 6 | -------------------------------------------------------------------------------- /docs/source/_static/logo/128pxblue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/HEAD/docs/source/_static/logo/128pxblue.png -------------------------------------------------------------------------------- /docs/source/_static/logo/256pxblue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/HEAD/docs/source/_static/logo/256pxblue.png -------------------------------------------------------------------------------- /docs/source/_static/logo/512pxblue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/HEAD/docs/source/_static/logo/512pxblue.png -------------------------------------------------------------------------------- /docs/source/strings.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | algorithms.string 5 | ================= 6 | -------------------------------------------------------------------------------- /docs/source/_static/algorithms_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/HEAD/docs/source/_static/algorithms_logo.png -------------------------------------------------------------------------------- /docs/source/_static/logo/128pxblack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/HEAD/docs/source/_static/logo/128pxblack.png -------------------------------------------------------------------------------- /docs/source/_static/logo/128pxorange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/HEAD/docs/source/_static/logo/128pxorange.png -------------------------------------------------------------------------------- /docs/source/_static/logo/256pxblack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/HEAD/docs/source/_static/logo/256pxblack.png -------------------------------------------------------------------------------- /docs/source/_static/logo/256pxorange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/HEAD/docs/source/_static/logo/256pxorange.png -------------------------------------------------------------------------------- /docs/source/_static/logo/512pxblack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/HEAD/docs/source/_static/logo/512pxblack.png -------------------------------------------------------------------------------- /docs/source/_static/logo/512pxorange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/HEAD/docs/source/_static/logo/512pxorange.png -------------------------------------------------------------------------------- /docs/source/backtrack.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | algorithms.backtrack 5 | ==================== 6 | -------------------------------------------------------------------------------- /docs/source/_static/logo/logotype1black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/HEAD/docs/source/_static/logo/logotype1black.png -------------------------------------------------------------------------------- /docs/source/_static/logo/logotype1blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/HEAD/docs/source/_static/logo/logotype1blue.png -------------------------------------------------------------------------------- /docs/source/_static/logo/logotype2black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/HEAD/docs/source/_static/logo/logotype2black.png -------------------------------------------------------------------------------- /docs/source/_static/logo/logotype2blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/HEAD/docs/source/_static/logo/logotype2blue.png -------------------------------------------------------------------------------- /docs/source/linkedlist.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | algorithms.linkedlist 5 | ===================== 6 | -------------------------------------------------------------------------------- /docs/source/_static/logo/logotype1orange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/HEAD/docs/source/_static/logo/logotype1orange.png -------------------------------------------------------------------------------- /docs/source/_static/logo/logotype2orange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/HEAD/docs/source/_static/logo/logotype2orange.png -------------------------------------------------------------------------------- /algorithms/queues/__init__.py: -------------------------------------------------------------------------------- 1 | from .queue import * 2 | from .max_sliding_window import * 3 | from .reconstruct_queue import * 4 | from .priority_queue import * 5 | -------------------------------------------------------------------------------- /algorithms/tree/tree.py: -------------------------------------------------------------------------------- 1 | class TreeNode: 2 | def __init__(self, val=0): 3 | self.val = val 4 | self.left = None 5 | self.right = None 6 | -------------------------------------------------------------------------------- /algorithms/unix/__init__.py: -------------------------------------------------------------------------------- 1 | from .path.join_with_slash import * 2 | from .path.full_path import * 3 | from .path.split import * 4 | from .path.simplify_path import * 5 | -------------------------------------------------------------------------------- /algorithms/bfs/__init__.py: -------------------------------------------------------------------------------- 1 | from .count_islands import * 2 | from .maze_search import * 3 | from .shortest_distance_from_all_buildings import * 4 | from .word_ladder import * 5 | -------------------------------------------------------------------------------- /algorithms/unix/path/full_path.py: -------------------------------------------------------------------------------- 1 | """ 2 | Get a full absolute path a file 3 | """ 4 | import os 5 | def full_path(file): 6 | return os.path.abspath(os.path.expanduser(file)) 7 | -------------------------------------------------------------------------------- /algorithms/heap/__init__.py: -------------------------------------------------------------------------------- 1 | from .binary_heap import * 2 | from .skyline import * 3 | from .sliding_window_max import * 4 | from .merge_sorted_k_lists import * 5 | from .k_closest_points import * 6 | -------------------------------------------------------------------------------- /algorithms/dfs/__init__.py: -------------------------------------------------------------------------------- 1 | from .all_factors import * 2 | from .count_islands import * 3 | from .pacific_atlantic import * 4 | from .sudoku_solver import * 5 | from .walls_and_gates import * 6 | from .maze_search import * 7 | -------------------------------------------------------------------------------- /algorithms/map/__init__.py: -------------------------------------------------------------------------------- 1 | from .hashtable import * 2 | from .separate_chaining_hashtable import * 3 | from .word_pattern import * 4 | from .is_isomorphic import * 5 | from .is_anagram import * 6 | from .longest_palindromic_subsequence import * 7 | -------------------------------------------------------------------------------- /algorithms/bit/power_of_two.py: -------------------------------------------------------------------------------- 1 | """ 2 | given an integer, write a function to determine if it is a power of two 3 | """ 4 | def is_power_of_two(n): 5 | """ 6 | :type n: int 7 | :rtype: bool 8 | """ 9 | return n > 0 and not n & (n-1) 10 | -------------------------------------------------------------------------------- /algorithms/tree/bst/successor.py: -------------------------------------------------------------------------------- 1 | def successor(root, node): 2 | succ = None 3 | while root: 4 | if node.val < root.val: 5 | succ = root 6 | root = root.left 7 | else: 8 | root = root.right 9 | return succ 10 | -------------------------------------------------------------------------------- /algorithms/tree/bst/predecessor.py: -------------------------------------------------------------------------------- 1 | def predecessor(root, node): 2 | pred = None 3 | while root: 4 | if node.val > root.val: 5 | pred = root 6 | root = root.right 7 | else: 8 | root = root.left 9 | return pred 10 | -------------------------------------------------------------------------------- /algorithms/tree/invert_tree.py: -------------------------------------------------------------------------------- 1 | # invert a binary tree 2 | 3 | def reverse(root): 4 | if root is None: 5 | return 6 | root.left, root.right = root.right, root.left 7 | if root.left: 8 | reverse(root.left) 9 | if root.right: 10 | reverse(root.right) 11 | -------------------------------------------------------------------------------- /algorithms/maths/num_digits.py: -------------------------------------------------------------------------------- 1 | """ 2 | num_digits() method will return the number of digits of a number in O(1) time using 3 | math.log10() method. 4 | """ 5 | 6 | import math 7 | 8 | def num_digits(n): 9 | n=abs(n) 10 | if n==0: 11 | return 1 12 | return int(math.log10(n))+1 13 | -------------------------------------------------------------------------------- /algorithms/linkedlist/__init__.py: -------------------------------------------------------------------------------- 1 | from .reverse import * 2 | from .is_sorted import * 3 | from .remove_range import * 4 | from .swap_in_pairs import * 5 | from .rotate_list import * 6 | from .is_cyclic import * 7 | from .merge_two_list import * 8 | from .is_palindrome import * 9 | from .copy_random_pointer import * 10 | -------------------------------------------------------------------------------- /algorithms/strings/check_pangram.py: -------------------------------------------------------------------------------- 1 | """ 2 | Algorithm that checks if a given string is a pangram or not 3 | """ 4 | 5 | def check_pangram(input_string): 6 | alphabet = "abcdefghijklmnopqrstuvwxyz" 7 | for ch in alphabet: 8 | if ch not in input_string.lower(): 9 | return False 10 | return True -------------------------------------------------------------------------------- /algorithms/stack/__init__.py: -------------------------------------------------------------------------------- 1 | from .stack import * 2 | from .is_consecutive import * 3 | from .is_sorted import * 4 | from .remove_min import * 5 | from .stutter import * 6 | from .switch_pairs import * 7 | from .valid_parenthesis import * 8 | from .simplify_path import * 9 | from .stack import * 10 | from .ordered_stack import * 11 | -------------------------------------------------------------------------------- /algorithms/dp/max_subarray.py: -------------------------------------------------------------------------------- 1 | 2 | def max_subarray(array): 3 | max_so_far = max_now = array[0] 4 | for i in range(1, len(array)): 5 | max_now = max(array[i], max_now + array[i]) 6 | max_so_far = max(max_so_far, max_now) 7 | return max_so_far 8 | 9 | a = [1, 2, -3, 4, 5, -7, 23] 10 | print(a) 11 | print(max_subarray(a)) 12 | -------------------------------------------------------------------------------- /algorithms/graph/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Collection of algorithms on graphs. 3 | """ 4 | 5 | from .tarjan import * 6 | from .check_bipartite import * 7 | from .maximum_flow import * 8 | from .maximum_flow_bfs import * 9 | from .maximum_flow_dfs import * 10 | from .all_pairs_shortest_path import * 11 | from .bellman_ford import * 12 | from .prims_minimum_spanning import * 13 | -------------------------------------------------------------------------------- /algorithms/bit/add_bitwise_operator.py: -------------------------------------------------------------------------------- 1 | """ 2 | The following code adds two positive integers without using the '+' operator. 3 | The code uses bitwise operations to add two numbers. 4 | 5 | Input: 2 3 6 | Output: 5 7 | """ 8 | def add_bitwise_operator(x, y): 9 | 10 | while y: 11 | carry = x & y 12 | x = x ^ y 13 | y = carry << 1 14 | return x 15 | -------------------------------------------------------------------------------- /algorithms/strings/license_number.py: -------------------------------------------------------------------------------- 1 | 2 | def license_number(key, k): 3 | res, alnum = [], [] 4 | for char in key: 5 | if char != "-": 6 | alnum.append(char) 7 | for i, char in enumerate(reversed(alnum)): 8 | res.append(char) 9 | if (i+1) % k == 0 and i != len(alnum)-1: 10 | res.append("-") 11 | return "".join(res[::-1]) 12 | -------------------------------------------------------------------------------- /docs/source/arrays.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | algorithms.arrays 5 | ================= 6 | 7 | .. automodule:: algorithms.arrays 8 | .. currentmodule:: algorithms.arrays 9 | 10 | longest_non_repeat 11 | ------------------ 12 | 13 | :hidden:`longest_non_repeat_v1` 14 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 15 | 16 | .. autofunction:: longest_non_repeat_v1 17 | -------------------------------------------------------------------------------- /tests/test_set.py: -------------------------------------------------------------------------------- 1 | from algorithms.set import ( 2 | find_keyboard_row 3 | ) 4 | 5 | import unittest 6 | 7 | 8 | class TestFindKeyboardRow(unittest.TestCase): 9 | def test_find_keyboard_row(self): 10 | self.assertEqual(["Alaska", "Dad"], 11 | find_keyboard_row(["Hello", "Alaska", 12 | "Dad", "Peace"])) 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | *.py[cod] 3 | *.iml 4 | *.xml 5 | .idea/ 6 | .cache/ 7 | .pytest_cache/ 8 | .coverage 9 | # Setuptools distribution folder. 10 | /dist/ 11 | # Python egg metadata, regenerated from source files by setuptools. 12 | /*.egg-info 13 | /*.egg 14 | # docs 15 | build/ 16 | pythonenv3.8/ 17 | .vscode/ 18 | # Ignoring the virtual Environment when using GitHub Codespaces 19 | .venv/ -------------------------------------------------------------------------------- /algorithms/sort/exchange_sort.py: -------------------------------------------------------------------------------- 1 | def exchange_sort(arr): 2 | """ 3 | Reference : https://en.wikipedia.org/wiki/Sorting_algorithm#Exchange_sort 4 | Complexity : O(n^2) 5 | """ 6 | arr_len = len(arr) 7 | for i in range(arr_len-1): 8 | for j in range(i+1, arr_len): 9 | if(arr[i] > arr[j]): 10 | arr[i], arr[j] = arr[j], arr[i] 11 | return arr 12 | -------------------------------------------------------------------------------- /algorithms/strings/reverse_vowel.py: -------------------------------------------------------------------------------- 1 | 2 | def reverse_vowel(s): 3 | vowels = "AEIOUaeiou" 4 | i, j = 0, len(s)-1 5 | s = list(s) 6 | while i < j: 7 | while i < j and s[i] not in vowels: 8 | i += 1 9 | while i < j and s[j] not in vowels: 10 | j -= 1 11 | s[i], s[j] = s[j], s[i] 12 | i, j = i + 1, j - 1 13 | return "".join(s) 14 | -------------------------------------------------------------------------------- /algorithms/tree/max_path_sum.py: -------------------------------------------------------------------------------- 1 | def max_path_sum(root): 2 | maximum = float("-inf") 3 | helper(root, maximum) 4 | return maximum 5 | 6 | 7 | def helper(root, maximum): 8 | if root is None: 9 | return 0 10 | left = helper(root.left, maximum) 11 | right = helper(root.right, maximum) 12 | maximum = max(maximum, left+right+root.val) 13 | return root.val + maximum 14 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | # Progress bars on iterators 2 | tqdm 3 | sphinx_rtd_theme 4 | 5 | # Downloading data and other files 6 | requests 7 | 8 | # Required for tests only: 9 | 10 | # Style-checking for PEP8 11 | flake8 12 | 13 | # Run unit tests 14 | pytest 15 | 16 | # Lets pytest find our code by automatically modifying PYTHONPATH 17 | pytest-pythonpath 18 | 19 | # Coverage statistics 20 | pytest-cov 21 | codecov 22 | -------------------------------------------------------------------------------- /algorithms/bit/remove_bit.py: -------------------------------------------------------------------------------- 1 | """ 2 | Remove_bit(num, i): remove a bit at specific position. 3 | For example: 4 | 5 | Input: num = 10101 (21) 6 | remove_bit(num, 2): output = 1001 (9) 7 | remove_bit(num, 4): output = 101 (5) 8 | remove_bit(num, 0): output = 1010 (10) 9 | """ 10 | 11 | def remove_bit(num, i): 12 | mask = num >> (i + 1) 13 | mask = mask << i 14 | right = ((1 << i) - 1) & num 15 | return mask | right 16 | -------------------------------------------------------------------------------- /algorithms/bit/reverse_bits.py: -------------------------------------------------------------------------------- 1 | """ 2 | Reverse bits of a given 32 bits unsigned integer. 3 | 4 | For example, given input 43261596 5 | (represented in binary as 00000010100101000001111010011100), 6 | return 964176192 7 | (represented in binary as 00111001011110000010100101000000). 8 | """ 9 | def reverse_bits(n): 10 | m = 0 11 | i = 0 12 | while i < 32: 13 | m = (m << 1) + (n & 1) 14 | n >>= 1 15 | i += 1 16 | return m 17 | -------------------------------------------------------------------------------- /algorithms/maths/prime_check.py: -------------------------------------------------------------------------------- 1 | def prime_check(n): 2 | """Return True if n is a prime number 3 | Else return False. 4 | """ 5 | 6 | if n <= 1: 7 | return False 8 | if n == 2 or n == 3: 9 | return True 10 | if n % 2 == 0 or n % 3 == 0: 11 | return False 12 | j = 5 13 | while j * j <= n: 14 | if n % j == 0 or n % (j + 2) == 0: 15 | return False 16 | j += 6 17 | return True 18 | -------------------------------------------------------------------------------- /algorithms/automata/dfa.py: -------------------------------------------------------------------------------- 1 | def DFA(transitions, start, final, string): 2 | 3 | num = len(string) 4 | num_final = len(final) 5 | cur = start 6 | 7 | for i in range(num): 8 | 9 | if transitions[cur][string[i]] is None: 10 | return False 11 | else: 12 | cur = transitions[cur][string[i]] 13 | 14 | for i in range(num_final): 15 | if cur == final[i]: 16 | return True 17 | return False 18 | -------------------------------------------------------------------------------- /algorithms/sort/wiggle_sort.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given an unsorted array nums, reorder it such that nums[0] < nums[1] > nums[2] < nums[3].... 3 | """ 4 | def wiggle_sort(nums): 5 | for i in range(len(nums)): 6 | if (i % 2 == 1) == (nums[i-1] > nums[i]): 7 | nums[i-1], nums[i] = nums[i], nums[i-1] 8 | 9 | if __name__ == "__main__": 10 | array = [3, 5, 2, 1, 6, 4] 11 | 12 | print(array) 13 | wiggle_sort(array) 14 | print(array) 15 | 16 | 17 | -------------------------------------------------------------------------------- /algorithms/search/linear_search.py: -------------------------------------------------------------------------------- 1 | """ 2 | Linear search works in any array. 3 | T(n): O(n) 4 | """ 5 | 6 | def linear_search(array, query): 7 | """ 8 | Find the index of the given element in the array. 9 | There are no restrictions on the order of the elements in the array. 10 | If the element couldn't be found, returns -1. 11 | """ 12 | for i, value in enumerate(array): 13 | if value == query: 14 | return i 15 | return -1 16 | -------------------------------------------------------------------------------- /algorithms/sort/gnome_sort.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Gnome Sort 4 | Best case performance is O(n) 5 | Worst case performance is O(n^2) 6 | 7 | """ 8 | 9 | 10 | def gnome_sort(arr): 11 | n = len(arr) 12 | index = 0 13 | while index < n: 14 | if index == 0 or arr[index] >= arr[index-1]: 15 | index = index + 1 16 | else: 17 | arr[index], arr[index-1] = arr[index-1], arr[index] 18 | index = index - 1 19 | return arr 20 | -------------------------------------------------------------------------------- /algorithms/tree/binary_tree_paths.py: -------------------------------------------------------------------------------- 1 | def binary_tree_paths(root): 2 | res = [] 3 | if root is None: 4 | return res 5 | dfs(res, root, str(root.val)) 6 | return res 7 | 8 | 9 | def dfs(res, root, cur): 10 | if root.left is None and root.right is None: 11 | res.append(cur) 12 | if root.left: 13 | dfs(res, root.left, cur+'->'+str(root.left.val)) 14 | if root.right: 15 | dfs(res, root.right, cur+'->'+str(root.right.val)) 16 | -------------------------------------------------------------------------------- /algorithms/bit/count_flips_to_convert.py: -------------------------------------------------------------------------------- 1 | """ 2 | Write a function to determine the minimal number of bits you would need to 3 | flip to convert integer A to integer B. 4 | For example: 5 | Input: 29 (or: 11101), 15 (or: 01111) 6 | Output: 2 7 | """ 8 | 9 | 10 | def count_flips_to_convert(a, b): 11 | 12 | diff = a ^ b 13 | 14 | # count number of ones in diff 15 | count = 0 16 | while diff: 17 | diff &= (diff - 1) 18 | count += 1 19 | return count 20 | -------------------------------------------------------------------------------- /algorithms/backtrack/anagram.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given two strings, determine if they are equal after reordering. 3 | 4 | Examples: 5 | "apple", "pleap" -> True 6 | "apple", "cherry" -> False 7 | """ 8 | 9 | 10 | def anagram(s1, s2): 11 | c1 = [0] * 26 12 | c2 = [0] * 26 13 | 14 | for c in s1: 15 | pos = ord(c)-ord('a') 16 | c1[pos] = c1[pos] + 1 17 | 18 | for c in s2: 19 | pos = ord(c)-ord('a') 20 | c2[pos] = c2[pos] + 1 21 | 22 | return c1 == c2 23 | -------------------------------------------------------------------------------- /algorithms/map/valid_sudoku.py: -------------------------------------------------------------------------------- 1 | """ 2 | Determine if a Sudoku is valid, according to: Sudoku Puzzles - The Rules. 3 | 4 | The Sudoku board could be partially filled, where empty cells are filled with 5 | the character '.'. 6 | """ 7 | 8 | def is_valid_sudoku(self, board): 9 | seen = [] 10 | for i, row in enumerate(board): 11 | for j, c in enumerate(row): 12 | if c != '.': 13 | seen += [(c,j),(i,c),(i/3,j/3,c)] 14 | return len(seen) == len(set(seen)) 15 | -------------------------------------------------------------------------------- /algorithms/strings/reverse_string.py: -------------------------------------------------------------------------------- 1 | def recursive(s): 2 | l = len(s) 3 | if l < 2: 4 | return s 5 | return recursive(s[l//2:]) + recursive(s[:l//2]) 6 | 7 | def iterative(s): 8 | r = list(s) 9 | i, j = 0, len(s) - 1 10 | while i < j: 11 | r[i], r[j] = r[j], r[i] 12 | i += 1 13 | j -= 1 14 | return "".join(r) 15 | 16 | def pythonic(s): 17 | r = list(reversed(s)) 18 | return "".join(r) 19 | 20 | def ultra_pythonic(s): 21 | return s[::-1] 22 | -------------------------------------------------------------------------------- /algorithms/strings/reverse_words.py: -------------------------------------------------------------------------------- 1 | 2 | def reverse(array, i, j): 3 | while i < j: 4 | array[i], array[j] = array[j], array[i] 5 | i += 1 6 | j -= 1 7 | 8 | 9 | def reverse_words(string): 10 | arr = string.strip().split() # arr is list of words 11 | n = len(arr) 12 | reverse(arr, 0, n-1) 13 | 14 | return " ".join(arr) 15 | 16 | 17 | if __name__ == "__main__": 18 | test = "I am keon kim and I like pizza" 19 | print(test) 20 | print(reverse_words(test)) 21 | -------------------------------------------------------------------------------- /algorithms/backtrack/__init__.py: -------------------------------------------------------------------------------- 1 | from .add_operators import * 2 | from .anagram import * 3 | from .array_sum_combinations import * 4 | from .combination_sum import * 5 | from .factor_combinations import * 6 | from .find_words import * 7 | from .generate_abbreviations import * 8 | from .generate_parenthesis import * 9 | from .letter_combination import * 10 | from .palindrome_partitioning import * 11 | from .pattern_match import * 12 | from .permute_unique import * 13 | from .permute import * 14 | from .subsets_unique import * 15 | from .subsets import * 16 | -------------------------------------------------------------------------------- /tests/test_histogram.py: -------------------------------------------------------------------------------- 1 | from algorithms.distribution.histogram import get_histogram 2 | 3 | import unittest 4 | 5 | 6 | class TestListsInHistogram(unittest.TestCase): 7 | def test_histogram(self): 8 | list_1 = [3, 3, 2, 1] 9 | list_2 = [2, 3, 5, 5, 5, 6, 4, 3, 7] 10 | 11 | self.assertEqual(get_histogram(list_1), {1: 1, 2: 1, 3: 2}) 12 | self.assertEqual(get_histogram(list_2), 13 | {2: 1, 3: 2, 4: 1, 5: 3, 6: 1, 7: 1}) 14 | 15 | 16 | if __name__ == '__main__': 17 | unittest.main() 18 | -------------------------------------------------------------------------------- /algorithms/search/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Collection of search algorithms: finding the needle in a haystack. 3 | """ 4 | 5 | from .binary_search import * 6 | from .ternary_search import * 7 | from .first_occurrence import * 8 | from .last_occurrence import * 9 | from .linear_search import * 10 | from .search_insert import * 11 | from .two_sum import * 12 | from .search_range import * 13 | from .find_min_rotate import * 14 | from .search_rotate import * 15 | from .jump_search import * 16 | from .next_greatest_letter import * 17 | from .interpolation_search import * 18 | -------------------------------------------------------------------------------- /algorithms/maths/nth_digit.py: -------------------------------------------------------------------------------- 1 | def find_nth_digit(n): 2 | """find the nth digit of given number. 3 | 1. find the length of the number where the nth digit is from. 4 | 2. find the actual number where the nth digit is from 5 | 3. find the nth digit and return 6 | """ 7 | length = 1 8 | count = 9 9 | start = 1 10 | while n > length * count: 11 | n -= length * count 12 | length += 1 13 | count *= 10 14 | start *= 10 15 | start += (n-1) / length 16 | s = str(start) 17 | return int(s[(n-1) % length]) 18 | -------------------------------------------------------------------------------- /algorithms/tree/bst/array_to_bst.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given an array where elements are sorted in ascending order, 3 | convert it to a height balanced BST. 4 | """ 5 | 6 | 7 | class TreeNode(object): 8 | def __init__(self, x): 9 | self.val = x 10 | self.left = None 11 | self.right = None 12 | 13 | 14 | def array_to_bst(nums): 15 | if not nums: 16 | return None 17 | mid = len(nums)//2 18 | node = TreeNode(nums[mid]) 19 | node.left = array_to_bst(nums[:mid]) 20 | node.right = array_to_bst(nums[mid+1:]) 21 | return node 22 | -------------------------------------------------------------------------------- /algorithms/linkedlist/is_sorted.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a linked list, is_sort function returns true if the list is in sorted 3 | (increasing) order and return false otherwise. An empty list is considered 4 | to be sorted. 5 | 6 | For example: 7 | Null :List is sorted 8 | 1 2 3 4 :List is sorted 9 | 1 2 -1 3 :List is not sorted 10 | """ 11 | def is_sorted(head): 12 | if not head: 13 | return True 14 | current = head 15 | while current.next: 16 | if current.val > current.next.val: 17 | return False 18 | current = current.next 19 | return True 20 | -------------------------------------------------------------------------------- /algorithms/arrays/__init__.py: -------------------------------------------------------------------------------- 1 | from .delete_nth import * 2 | from .flatten import * 3 | from .garage import * 4 | from .josephus import * 5 | from .longest_non_repeat import * 6 | from .max_ones_index import * 7 | from .merge_intervals import * 8 | from .missing_ranges import * 9 | from .move_zeros import * 10 | from .plus_one import * 11 | from .rotate import * 12 | from .summarize_ranges import * 13 | from .three_sum import * 14 | from .trimmean import * 15 | from .top_1 import * 16 | from .two_sum import * 17 | from .limit import * 18 | from .n_sum import * 19 | from .remove_duplicates import * -------------------------------------------------------------------------------- /algorithms/tree/bst/BSTIterator.py: -------------------------------------------------------------------------------- 1 | 2 | class BSTIterator: 3 | def __init__(self, root): 4 | self.stack = [] 5 | while root: 6 | self.stack.append(root) 7 | root = root.left 8 | 9 | def has_next(self): 10 | return bool(self.stack) 11 | 12 | def next(self): 13 | node = self.stack.pop() 14 | tmp = node 15 | if tmp.right: 16 | tmp = tmp.right 17 | while tmp: 18 | self.stack.append(tmp) 19 | tmp = tmp.left 20 | return node.val 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /algorithms/strings/int_to_roman.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given an integer, convert it to a roman numeral. 3 | Input is guaranteed to be within the range from 1 to 3999. 4 | """ 5 | 6 | def int_to_roman(num): 7 | """ 8 | :type num: int 9 | :rtype: str 10 | """ 11 | m = ["", "M", "MM", "MMM"]; 12 | c = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"]; 13 | x = ["", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"]; 14 | i = ["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"]; 15 | return m[num//1000] + c[(num%1000)//100] + x[(num%100)//10] + i[num%10]; 16 | -------------------------------------------------------------------------------- /algorithms/strings/delete_reoccurring.py: -------------------------------------------------------------------------------- 1 | """ 2 | QUESTION: Given a string as your input, delete any reoccurring 3 | character, and return the new string. 4 | 5 | This is a Google warmup interview question that was asked duirng phone screening 6 | at my university. 7 | """ 8 | 9 | # time complexity O(n) 10 | def delete_reoccurring_characters(string): 11 | seen_characters = set() 12 | output_string = '' 13 | for char in string: 14 | if char not in seen_characters: 15 | seen_characters.add(char) 16 | output_string += char 17 | return output_string 18 | 19 | -------------------------------------------------------------------------------- /algorithms/maths/hailstone.py: -------------------------------------------------------------------------------- 1 | """ 2 | Implementation of hailstone function which generates a sequence for some n by following these rules: 3 | * n == 1 : done 4 | * n is even : the next n = n/2 5 | * n is odd : the next n = 3n + 1 6 | """ 7 | 8 | def hailstone(n): 9 | """ 10 | Return the 'hailstone sequence' from n to 1 11 | n: The starting point of the hailstone sequence 12 | """ 13 | 14 | sequence = [n] 15 | while n > 1: 16 | if n%2 != 0: 17 | n = 3*n + 1 18 | else: 19 | n = int(n/2) 20 | sequence.append(n) 21 | return sequence 22 | -------------------------------------------------------------------------------- /algorithms/maths/sqrt_precision_factor.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a positive integer N and a precision factor P, 3 | it produces an output 4 | with a maximum error P from the actual square root of N. 5 | 6 | Example: 7 | Given N = 5 and P = 0.001, can produce output x such that 8 | 2.235 < x < 2.237. Actual square root of 5 being 2.236. 9 | """ 10 | 11 | 12 | def square_root(n, epsilon=0.001): 13 | """Return square root of n, with maximum absolute error epsilon""" 14 | guess = n / 2 15 | 16 | while abs(guess * guess - n) > epsilon: 17 | guess = (guess + (n / guess)) / 2 18 | 19 | return guess 20 | -------------------------------------------------------------------------------- /algorithms/strings/add_binary.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given two binary strings, 3 | return their sum (also a binary string). 4 | 5 | For example, 6 | a = "11" 7 | b = "1" 8 | Return "100". 9 | """ 10 | 11 | 12 | def add_binary(a, b): 13 | s = "" 14 | c, i, j = 0, len(a)-1, len(b)-1 15 | zero = ord('0') 16 | while (i >= 0 or j >= 0 or c == 1): 17 | if (i >= 0): 18 | c += ord(a[i]) - zero 19 | i -= 1 20 | if (j >= 0): 21 | c += ord(b[j]) - zero 22 | j -= 1 23 | s = chr(c % 2 + zero) + s 24 | c //= 2 25 | 26 | return s 27 | -------------------------------------------------------------------------------- /algorithms/arrays/missing_ranges.py: -------------------------------------------------------------------------------- 1 | """ 2 | Find missing ranges between low and high in the given array. 3 | Ex) [3, 5] lo=1 hi=10 => answer: [(1, 2), (4, 4), (6, 10)] 4 | """ 5 | 6 | def missing_ranges(arr, lo, hi): 7 | 8 | res = [] 9 | start = lo 10 | 11 | for n in arr: 12 | 13 | if n == start: 14 | start += 1 15 | elif n > start: 16 | res.append((start, n-1)) 17 | start = n + 1 18 | 19 | if start <= hi: # after done iterating thru array, 20 | res.append((start, hi)) # append remainder to list 21 | 22 | return res 23 | -------------------------------------------------------------------------------- /algorithms/strings/roman_to_int.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a roman numeral, convert it to an integer. 3 | Input is guaranteed to be within the range from 1 to 3999. 4 | """ 5 | 6 | 7 | def roman_to_int(s:"str")->"int": 8 | number = 0 9 | roman = {'M':1000, 'D':500, 'C': 100, 'L':50, 'X':10, 'V':5, 'I':1} 10 | for i in range(len(s)-1): 11 | if roman[s[i]] < roman[s[i+1]]: 12 | number -= roman[s[i]] 13 | else: 14 | number += roman[s[i]] 15 | return number + roman[s[-1]] 16 | 17 | 18 | if __name__ == "__main__": 19 | roman = "DCXXI" 20 | print(roman_to_int(roman)) 21 | -------------------------------------------------------------------------------- /algorithms/unix/path/join_with_slash.py: -------------------------------------------------------------------------------- 1 | """ 2 | Both URL and file path joins use slashes as dividers between their parts. 3 | For example: 4 | 5 | path/to/dir + file --> path/to/dir/file 6 | path/to/dir/ + file --> path/to/dir/file 7 | http://algorithms.com/ + part --> http://algorithms.com/part 8 | http://algorithms.com + part --> http://algorithms/part 9 | """ 10 | import os 11 | 12 | def join_with_slash(base, suffix): 13 | # Remove / trailing 14 | base = base.rstrip('/') 15 | # Remove / leading 16 | suffix = suffix.lstrip('/').rstrip() 17 | full_path = "{}/{}".format(base, suffix) 18 | return full_path 19 | -------------------------------------------------------------------------------- /algorithms/sort/meeting_rooms.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given an array of meeting time intervals consisting of 3 | start and end times [[s1,e1],[s2,e2],...] (si < ei), 4 | determine if a person could attend all meetings. 5 | 6 | For example, 7 | Given [[0, 30],[5, 10],[15, 20]], 8 | return false. 9 | """ 10 | 11 | 12 | def can_attend_meetings(intervals): 13 | """ 14 | :type intervals: List[Interval] 15 | :rtype: bool 16 | """ 17 | intervals = sorted(intervals, key=lambda x: x.start) 18 | for i in range(1, len(intervals)): 19 | if intervals[i].start < intervals[i - 1].end: 20 | return False 21 | return True 22 | -------------------------------------------------------------------------------- /algorithms/unix/path/split.py: -------------------------------------------------------------------------------- 1 | """ 2 | Splitting a path into 2 parts 3 | Example: 4 | Input: https://algorithms/unix/test.py (for url) 5 | Output: 6 | part[0]: https://algorithms/unix 7 | part[1]: test.py 8 | 9 | Input: algorithms/unix/test.py (for file path) 10 | Output: 11 | part[0]: algorithms/unix 12 | part[1]: test.py 13 | """ 14 | import os 15 | 16 | def split(path): 17 | parts = [] 18 | split_part = path.rpartition('/') 19 | # Takt the origin path without the last part 20 | parts.append(split_part[0]) 21 | # Take the last element of list 22 | parts.append(split_part[2]) 23 | return parts 24 | -------------------------------------------------------------------------------- /algorithms/sort/shell_sort.py: -------------------------------------------------------------------------------- 1 | def shell_sort(arr): 2 | ''' Shell Sort 3 | Complexity: O(n^2) 4 | ''' 5 | n = len(arr) 6 | # Initialize size of the gap 7 | gap = n//2 8 | 9 | while gap > 0: 10 | y_index = gap 11 | while y_index < len(arr): 12 | y = arr[y_index] 13 | x_index = y_index - gap 14 | while x_index >= 0 and y < arr[x_index]: 15 | arr[x_index + gap] = arr[x_index] 16 | x_index = x_index - gap 17 | arr[x_index + gap] = y 18 | y_index = y_index + 1 19 | gap = gap//2 20 | 21 | return arr 22 | -------------------------------------------------------------------------------- /algorithms/arrays/two_sum.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given an array of integers, return indices of the two numbers 3 | such that they add up to a specific target. 4 | 5 | You may assume that each input would have exactly one solution, 6 | and you may not use the same element twice. 7 | 8 | Example: 9 | Given nums = [2, 7, 11, 15], target = 9, 10 | 11 | Because nums[0] + nums[1] = 2 + 7 = 9, 12 | return (0, 1) 13 | """ 14 | 15 | 16 | def two_sum(array, target): 17 | dic = {} 18 | for i, num in enumerate(array): 19 | if num in dic: 20 | return dic[num], i 21 | else: 22 | dic[target - num] = i 23 | return None 24 | -------------------------------------------------------------------------------- /algorithms/strings/group_anagrams.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given an array of strings, group anagrams together. 3 | 4 | For example, given: ["eat", "tea", "tan", "ate", "nat", "bat"], 5 | Return: 6 | 7 | [ 8 | ["ate", "eat","tea"], 9 | ["nat","tan"], 10 | ["bat"] 11 | ] 12 | """ 13 | 14 | 15 | def group_anagrams(strs): 16 | d = {} 17 | ans = [] 18 | k = 0 19 | for str in strs: 20 | sstr = ''.join(sorted(str)) 21 | if sstr not in d: 22 | d[sstr] = k 23 | k += 1 24 | ans.append([]) 25 | ans[-1].append(str) 26 | else: 27 | ans[d[sstr]].append(str) 28 | return ans 29 | -------------------------------------------------------------------------------- /algorithms/maths/euler_totient.py: -------------------------------------------------------------------------------- 1 | """ 2 | Euler's totient function, also known as phi-function ϕ(n), 3 | counts the number of integers between 1 and n inclusive, 4 | which are coprime to n. 5 | (Two numbers are coprime if their greatest common divisor (GCD) equals 1). 6 | """ 7 | def euler_totient(n): 8 | """Euler's totient function or Phi function. 9 | Time Complexity: O(sqrt(n)).""" 10 | result = n 11 | for i in range(2, int(n ** 0.5) + 1): 12 | if n % i == 0: 13 | while n % i == 0: 14 | n //= i 15 | result -= result // i 16 | if n > 1: 17 | result -= result // n 18 | return result 19 | -------------------------------------------------------------------------------- /algorithms/bit/__init__.py: -------------------------------------------------------------------------------- 1 | from .add_bitwise_operator import * 2 | from .count_ones import * 3 | from .find_missing_number import * 4 | from .power_of_two import * 5 | from .reverse_bits import * 6 | from .single_number import * 7 | from .single_number2 import * 8 | from .single_number3 import * 9 | from .subsets import * 10 | from .bit_operation import * 11 | from .swap_pair import * 12 | from .find_difference import * 13 | from .has_alternative_bit import * 14 | from .insert_bit import * 15 | from .remove_bit import * 16 | from .count_flips_to_convert import * 17 | from .flip_bit_longest_sequence import * 18 | from .binary_gap import * 19 | from .bytes_int_conversion import * -------------------------------------------------------------------------------- /algorithms/maths/combination.py: -------------------------------------------------------------------------------- 1 | """ 2 | Functions to calculate nCr (ie how many ways to choose r items from n items) 3 | """ 4 | def combination(n, r): 5 | """This function calculates nCr.""" 6 | if n == r or r == 0: 7 | return 1 8 | return combination(n-1, r-1) + combination(n-1, r) 9 | 10 | def combination_memo(n, r): 11 | """This function calculates nCr using memoization method.""" 12 | memo = {} 13 | def recur(n, r): 14 | if n == r or r == 0: 15 | return 1 16 | if (n, r) not in memo: 17 | memo[(n, r)] = recur(n - 1, r - 1) + recur(n - 1, r) 18 | return memo[(n, r)] 19 | return recur(n, r) 20 | -------------------------------------------------------------------------------- /algorithms/strings/rotate.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a strings s and int k, return a string that rotates k times 3 | 4 | k can be any positive integer. 5 | 6 | For example, 7 | rotate("hello", 2) return "llohe" 8 | rotate("hello", 5) return "hello" 9 | rotate("hello", 6) return "elloh" 10 | rotate("hello", 7) return "llohe" 11 | rotate("hello", 102) return "lohel" 12 | 13 | """ 14 | def rotate(s, k): 15 | long_string = s * (k // len(s) + 2) 16 | if k <= len(s): 17 | return long_string[k:k + len(s)] 18 | else: 19 | return long_string[k-len(s):k] 20 | 21 | def rotate_alt(string, k): 22 | k = k % len(string) 23 | return string[k:] + string[:k] 24 | -------------------------------------------------------------------------------- /algorithms/linkedlist/is_cyclic.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a linked list, determine if it has a cycle in it. 3 | 4 | Follow up: 5 | Can you solve it without using extra space? 6 | """ 7 | class Node: 8 | 9 | def __init__(self, x): 10 | self.val = x 11 | self.next = None 12 | 13 | def is_cyclic(head): 14 | """ 15 | :type head: Node 16 | :rtype: bool 17 | """ 18 | if not head: 19 | return False 20 | runner = head 21 | walker = head 22 | while runner.next and runner.next.next: 23 | runner = runner.next.next 24 | walker = walker.next 25 | if runner == walker: 26 | return True 27 | return False 28 | -------------------------------------------------------------------------------- /algorithms/backtrack/permute_unique.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a collection of numbers that might contain duplicates, 3 | return all possible unique permutations. 4 | 5 | For example, 6 | [1,1,2] have the following unique permutations: 7 | [ 8 | [1,1,2], 9 | [1,2,1], 10 | [2,1,1] 11 | ] 12 | """ 13 | 14 | 15 | def permute_unique(nums): 16 | perms = [[]] 17 | for n in nums: 18 | new_perms = [] 19 | for l in perms: 20 | for i in range(len(l)+1): 21 | new_perms.append(l[:i]+[n]+l[i:]) 22 | if i < len(l) and l[i] == n: 23 | break # handles duplication 24 | perms = new_perms 25 | return perms 26 | -------------------------------------------------------------------------------- /algorithms/stack/valid_parenthesis.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a string containing just the characters 3 | '(', ')', '{', '}', '[' and ']', 4 | determine if the input string is valid. 5 | 6 | The brackets must close in the correct order, 7 | "()" and "()[]{}" are all valid but "(]" and "([)]" are not. 8 | """ 9 | 10 | 11 | def is_valid(s: str) -> bool: 12 | stack = [] 13 | dic = {")": "(", 14 | "}": "{", 15 | "]": "["} 16 | for char in s: 17 | if char in dic.values(): 18 | stack.append(char) 19 | elif char in dic: 20 | if not stack or dic[char] != stack.pop(): 21 | return False 22 | return not stack 23 | -------------------------------------------------------------------------------- /algorithms/sort/__init__.py: -------------------------------------------------------------------------------- 1 | from .bitonic_sort import * 2 | from .bogo_sort import * 3 | from .bubble_sort import * 4 | from .comb_sort import * 5 | from .counting_sort import * 6 | from .cycle_sort import * 7 | from .exchange_sort import * 8 | from .heap_sort import * 9 | from .insertion_sort import * 10 | from .merge_sort import * 11 | from .pancake_sort import * 12 | from .pigeonhole_sort import * 13 | from .quick_sort import * 14 | from .selection_sort import * 15 | from .top_sort import * 16 | from .bucket_sort import * 17 | from .shell_sort import * 18 | from .stooge_sort import * 19 | from .radix_sort import * 20 | from .gnome_sort import * 21 | from .cocktail_shaker_sort import * 22 | -------------------------------------------------------------------------------- /algorithms/strings/repeat_substring.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a non-empty string check if it can be constructed by taking 3 | a substring of it and appending multiple copies of the substring together. 4 | 5 | For example: 6 | Input: "abab" 7 | Output: True 8 | Explanation: It's the substring "ab" twice. 9 | 10 | Input: "aba" 11 | Output: False 12 | 13 | Input: "abcabcabcabc" 14 | Output: True 15 | Explanation: It's the substring "abc" four times. 16 | 17 | Reference: https://leetcode.com/problems/repeated-substring-pattern/description/ 18 | """ 19 | def repeat_substring(s): 20 | """ 21 | :type s: str 22 | :rtype: bool 23 | """ 24 | str = (s + s)[1:-1] 25 | return s in str 26 | -------------------------------------------------------------------------------- /algorithms/arrays/josephus.py: -------------------------------------------------------------------------------- 1 | """ 2 | There are people sitting in a circular fashion, 3 | print every third member while removing them, 4 | the next counter starts immediately after the member is removed. 5 | Print till all the members are exhausted. 6 | 7 | For example: 8 | Input: consider 123456789 members sitting in a circular fashion, 9 | Output: 369485271 10 | """ 11 | 12 | def josephus(int_list, skip): 13 | skip = skip - 1 # list starts with 0 index 14 | idx = 0 15 | len_list = (len(int_list)) 16 | while len_list > 0: 17 | idx = (skip + idx) % len_list # hash index to every 3rd 18 | yield int_list.pop(idx) 19 | len_list -= 1 20 | -------------------------------------------------------------------------------- /algorithms/maths/modular_exponential.py: -------------------------------------------------------------------------------- 1 | def modular_exponential(base, exponent, mod): 2 | """Computes (base ^ exponent) % mod. 3 | Time complexity - O(log n) 4 | Use similar to Python in-built function pow.""" 5 | if exponent < 0: 6 | raise ValueError("Exponent must be positive.") 7 | base %= mod 8 | result = 1 9 | 10 | while exponent > 0: 11 | # If the last bit is 1, add 2^k. 12 | if exponent & 1: 13 | result = (result * base) % mod 14 | exponent = exponent >> 1 15 | # Utilize modular multiplication properties to combine the computed mod C values. 16 | base = (base * base) % mod 17 | 18 | return result 19 | -------------------------------------------------------------------------------- /algorithms/maths/next_perfect_square.py: -------------------------------------------------------------------------------- 1 | """ 2 | This program will look for the next perfect square. 3 | Check the argument to see if it is a perfect square itself, if it is not then return -1 otherwise 4 | look for the next perfect square. 5 | for instance if you pass 121 then the script should return the next perfect square which is 144. 6 | """ 7 | 8 | def find_next_square(sq): 9 | root = sq ** 0.5 10 | if root.is_integer(): 11 | return (root + 1)**2 12 | return -1 13 | 14 | def find_next_square2(sq): 15 | """ Alternative method, works by evaluating anything non-zero as True (0.000001 --> True) """ 16 | root = sq**0.5 17 | return -1 if root % 1 else (root+1)**2 18 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = algorithms 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /algorithms/bit/single_number.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given an array of integers, every element appears 3 | twice except for one. Find that single one. 4 | 5 | NOTE: This also works for finding a number occurring odd 6 | number of times, where all the other numbers appear 7 | even number of times. 8 | 9 | Note: 10 | Your algorithm should have a linear runtime complexity. 11 | Could you implement it without using extra memory? 12 | """ 13 | def single_number(nums): 14 | """ 15 | Returns single number, if found. 16 | Else if all numbers appear twice, returns 0. 17 | :type nums: List[int] 18 | :rtype: int 19 | """ 20 | i = 0 21 | for num in nums: 22 | i ^= num 23 | return i 24 | -------------------------------------------------------------------------------- /algorithms/matrix/sum_sub_squares.py: -------------------------------------------------------------------------------- 1 | # Function to find sum of all 2 | # sub-squares of size k x k in a given 3 | # square matrix of size n x n 4 | def sum_sub_squares(matrix, k): 5 | n = len(matrix) 6 | result = [[0 for i in range(k)] for j in range(k)] 7 | 8 | if k > n: 9 | return 10 | for i in range(n - k + 1): 11 | l = 0 12 | for j in range(n - k + 1): 13 | sum = 0 14 | 15 | # Calculate and print sum of current sub-square 16 | for p in range(i, k + i): 17 | for q in range(j, k + j): 18 | sum += matrix[p][q] 19 | 20 | result[i][l] = sum 21 | l += 1 22 | 23 | return result 24 | -------------------------------------------------------------------------------- /algorithms/search/search_insert.py: -------------------------------------------------------------------------------- 1 | """ 2 | Helper methods for implementing insertion sort. 3 | """ 4 | 5 | def search_insert(array, val): 6 | """ 7 | Given a sorted array and a target value, return the index if the target is 8 | found. If not, return the index where it would be if it were inserted in order. 9 | 10 | For example: 11 | [1,3,5,6], 5 -> 2 12 | [1,3,5,6], 2 -> 1 13 | [1,3,5,6], 7 -> 4 14 | [1,3,5,6], 0 -> 0 15 | """ 16 | low = 0 17 | high = len(array) - 1 18 | while low <= high: 19 | mid = low + (high - low) // 2 20 | if val > array[mid]: 21 | low = mid + 1 22 | else: 23 | high = mid - 1 24 | return low 25 | -------------------------------------------------------------------------------- /algorithms/sort/pigeonhole_sort.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | https://en.wikipedia.org/wiki/Pigeonhole_sort 4 | 5 | Time complexity: O(n + Range) where n = number of elements and Range = possible values in the array 6 | 7 | Suitable for lists where the number of elements and key values are mostly the same. 8 | 9 | """ 10 | 11 | 12 | def pigeonhole_sort(arr): 13 | Max = max(arr) 14 | Min = min(arr) 15 | size = Max - Min + 1 16 | 17 | holes = [0]*size 18 | 19 | for i in arr: 20 | holes[i-Min] += 1 21 | 22 | i = 0 23 | for count in range(size): 24 | while holes[count] > 0: 25 | holes[count] -= 1 26 | arr[i] = count + Min 27 | i += 1 28 | return arr 29 | -------------------------------------------------------------------------------- /algorithms/sort/selection_sort.py: -------------------------------------------------------------------------------- 1 | def selection_sort(arr, simulation=False): 2 | """ Selection Sort 3 | Complexity: O(n^2) 4 | """ 5 | iteration = 0 6 | if simulation: 7 | print("iteration",iteration,":",*arr) 8 | 9 | for i in range(len(arr)): 10 | minimum = i 11 | 12 | for j in range(i + 1, len(arr)): 13 | # "Select" the correct value 14 | if arr[j] < arr[minimum]: 15 | minimum = j 16 | 17 | arr[minimum], arr[i] = arr[i], arr[minimum] 18 | 19 | if simulation: 20 | iteration = iteration + 1 21 | print("iteration",iteration,":",*arr) 22 | 23 | return arr 24 | -------------------------------------------------------------------------------- /tests/test_greedy.py: -------------------------------------------------------------------------------- 1 | from algorithms.greedy import ( 2 | max_contiguous_subsequence_sum, 3 | ) 4 | 5 | import unittest 6 | 7 | class TestMaxContiguousSubsequenceSum(unittest.TestCase): 8 | def test_max_contiguous_subsequence_sum(self): 9 | arr1 = [-2, 3, 8, -1, 4] 10 | arr2 = [-1, 1, 0] 11 | arr3 = [-1, -3, -4] 12 | arr4 = [-2, 3, 8, -12, 8, 4] 13 | 14 | self.assertEqual(max_contiguous_subsequence_sum(arr1), 14) 15 | self.assertEqual(max_contiguous_subsequence_sum(arr2), 1) 16 | self.assertEqual(max_contiguous_subsequence_sum(arr3), -1) 17 | self.assertEqual(max_contiguous_subsequence_sum(arr4), 12) 18 | 19 | if __name__ == '__main__': 20 | 21 | unittest.main() -------------------------------------------------------------------------------- /algorithms/sort/comb_sort.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | https://en.wikipedia.org/wiki/Comb_sort 4 | 5 | Worst-case performance: O(N^2) 6 | 7 | """ 8 | 9 | 10 | def comb_sort(arr): 11 | def swap(i, j): 12 | arr[i], arr[j] = arr[j], arr[i] 13 | 14 | n = len(arr) 15 | gap = n 16 | shrink = 1.3 17 | sorted = False 18 | while not sorted: 19 | gap = int(gap / shrink) 20 | if gap > 1: 21 | sorted = False 22 | else: 23 | gap = 1 24 | sorted = True 25 | 26 | i = 0 27 | while i + gap < n: 28 | if arr[i] > arr[i + gap]: 29 | swap(i, i + gap) 30 | sorted = False 31 | i = i + 1 32 | return arr 33 | -------------------------------------------------------------------------------- /algorithms/map/is_anagram.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given two strings s and t , write a function to determine if t is an anagram of s. 3 | 4 | Example 1: 5 | Input: s = "anagram", t = "nagaram" 6 | Output: true 7 | 8 | Example 2: 9 | Input: s = "rat", t = "car" 10 | Output: false 11 | 12 | Note: 13 | You may assume the string contains only lowercase alphabets. 14 | 15 | Reference: https://leetcode.com/problems/valid-anagram/description/ 16 | """ 17 | def is_anagram(s, t): 18 | """ 19 | :type s: str 20 | :type t: str 21 | :rtype: bool 22 | """ 23 | maps = {} 24 | mapt = {} 25 | for i in s: 26 | maps[i] = maps.get(i, 0) + 1 27 | for i in t: 28 | mapt[i] = mapt.get(i, 0) + 1 29 | return maps == mapt 30 | -------------------------------------------------------------------------------- /algorithms/search/last_occurrence.py: -------------------------------------------------------------------------------- 1 | """ 2 | Find last occurance of a number in a sorted array (increasing order) 3 | Approach- Binary Search 4 | T(n)- O(log n) 5 | """ 6 | def last_occurrence(array, query): 7 | """ 8 | Returns the index of the last occurance of the given element in an array. 9 | The array has to be sorted in increasing order. 10 | """ 11 | low, high = 0, len(array) - 1 12 | while low <= high: 13 | mid = (high + low) // 2 14 | if (array[mid] == query and mid == len(array)-1) or \ 15 | (array[mid] == query and array[mid+1] > query): 16 | return mid 17 | if array[mid] <= query: 18 | low = mid + 1 19 | else: 20 | high = mid - 1 21 | -------------------------------------------------------------------------------- /algorithms/maths/extended_gcd.py: -------------------------------------------------------------------------------- 1 | """ 2 | Provides extended GCD functionality for finding co-prime numbers s and t such that: 3 | num1 * s + num2 * t = GCD(num1, num2). 4 | Ie the coefficients of Bézout's identity. 5 | """ 6 | def extended_gcd(num1, num2): 7 | """Extended GCD algorithm. 8 | Return s, t, g 9 | such that num1 * s + num2 * t = GCD(num1, num2) 10 | and s and t are co-prime. 11 | """ 12 | 13 | old_s, s = 1, 0 14 | old_t, t = 0, 1 15 | old_r, r = num1, num2 16 | 17 | while r != 0: 18 | quotient = old_r / r 19 | 20 | old_r, r = r, old_r - quotient * r 21 | old_s, s = s, old_s - quotient * s 22 | old_t, t = t, old_t - quotient * t 23 | 24 | return old_s, old_t, old_r 25 | -------------------------------------------------------------------------------- /algorithms/dp/house_robber.py: -------------------------------------------------------------------------------- 1 | """ 2 | You are a professional robber planning to rob houses along a street. 3 | Each house has a certain amount of money stashed, 4 | the only constraint stopping you from robbing each of them 5 | is that adjacent houses have security system connected and 6 | it will automatically contact the police if two adjacent houses 7 | were broken into on the same night. 8 | 9 | Given a list of non-negative integers representing the amount of money 10 | of each house, determine the maximum amount of money you 11 | can rob tonight without alerting the police. 12 | """ 13 | 14 | 15 | def house_robber(houses): 16 | last, now = 0, 0 17 | for house in houses: 18 | last, now = now, max(last + house, now) 19 | return now 20 | -------------------------------------------------------------------------------- /algorithms/strings/first_unique_char.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a string, find the first non-repeating character in it and return it's 3 | index. If it doesn't exist, return -1. 4 | 5 | For example: 6 | s = "leetcode" 7 | return 0. 8 | 9 | s = "loveleetcode", 10 | return 2. 11 | 12 | Reference: https://leetcode.com/problems/first-unique-character-in-a-string/description/ 13 | """ 14 | def first_unique_char(s): 15 | """ 16 | :type s: str 17 | :rtype: int 18 | """ 19 | if (len(s) == 1): 20 | return 0 21 | ban = [] 22 | for i in range(len(s)): 23 | if all(s[i] != s[k] for k in range(i + 1, len(s))) == True and s[i] not in ban: 24 | return i 25 | else: 26 | ban.append(s[i]) 27 | return -1 28 | -------------------------------------------------------------------------------- /algorithms/arrays/summarize_ranges.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a sorted integer array without duplicates, 3 | return the summary of its ranges. 4 | 5 | For example, given [0, 1, 2, 4, 5, 7], return [(0, 2), (4, 5), (7, 7)]. 6 | """ 7 | 8 | from typing import List, Tuple 9 | 10 | 11 | def summarize_ranges(array: List[int]) -> List[Tuple[int, ...]]: 12 | res = [] 13 | if len(array) == 0: 14 | return [] 15 | if len(array) == 1: 16 | return [(array[0], array[0])] 17 | it = iter(array) 18 | start = end = next(it) 19 | for num in it: 20 | if num - end == 1: 21 | end = num 22 | else: 23 | res.append((start, end)) 24 | start = end = num 25 | res.append((start, end)) 26 | return res 27 | -------------------------------------------------------------------------------- /algorithms/dp/__init__.py: -------------------------------------------------------------------------------- 1 | from .buy_sell_stock import * 2 | from .climbing_stairs import * 3 | from .coin_change import * 4 | from .combination_sum import * 5 | from .edit_distance import * 6 | from .egg_drop import * 7 | from .fib import * 8 | from .hosoya_triangle import * 9 | from .house_robber import * 10 | from .job_scheduling import * 11 | from .knapsack import * 12 | from .longest_increasing import * 13 | from .matrix_chain_order import * 14 | from .max_product_subarray import * 15 | from .max_subarray import * 16 | from .min_cost_path import * 17 | from .num_decodings import * 18 | from .regex_matching import * 19 | from .rod_cut import * 20 | from .word_break import * 21 | from .int_divide import * 22 | from .k_factor import * 23 | from .planting_trees import * 24 | -------------------------------------------------------------------------------- /algorithms/sort/insertion_sort.py: -------------------------------------------------------------------------------- 1 | def insertion_sort(arr, simulation=False): 2 | """ Insertion Sort 3 | Complexity: O(n^2) 4 | """ 5 | 6 | iteration = 0 7 | if simulation: 8 | print("iteration",iteration,":",*arr) 9 | 10 | for i in range(len(arr)): 11 | cursor = arr[i] 12 | pos = i 13 | 14 | while pos > 0 and arr[pos - 1] > cursor: 15 | # Swap the number down the list 16 | arr[pos] = arr[pos - 1] 17 | pos = pos - 1 18 | # Break and do the final swap 19 | arr[pos] = cursor 20 | 21 | if simulation: 22 | iteration = iteration + 1 23 | print("iteration",iteration,":",*arr) 24 | 25 | return arr 26 | -------------------------------------------------------------------------------- /algorithms/strings/atbash_cipher.py: -------------------------------------------------------------------------------- 1 | """ 2 | Atbash cipher is mapping the alphabet to it's reverse. 3 | So if we take "a" as it is the first letter, we change it to the last - z. 4 | 5 | Example: 6 | Attack at dawn --> Zggzxp zg wzdm 7 | 8 | Complexity: O(n) 9 | """ 10 | 11 | def atbash(s): 12 | translated = "" 13 | for i in range(len(s)): 14 | n = ord(s[i]) 15 | 16 | if s[i].isalpha(): 17 | 18 | if s[i].isupper(): 19 | x = n - ord('A') 20 | translated += chr(ord('Z') - x) 21 | 22 | if s[i].islower(): 23 | x = n - ord('a') 24 | translated += chr(ord('z') - x) 25 | else: 26 | translated += s[i] 27 | return translated -------------------------------------------------------------------------------- /algorithms/strings/contain_string.py: -------------------------------------------------------------------------------- 1 | """ 2 | Implement strStr(). 3 | 4 | Return the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack. 5 | 6 | Example 1: 7 | Input: haystack = "hello", needle = "ll" 8 | Output: 2 9 | 10 | Example 2: 11 | Input: haystack = "aaaaa", needle = "bba" 12 | Output: -1 13 | Reference: https://leetcode.com/problems/implement-strstr/description/ 14 | """ 15 | def contain_string(haystack, needle): 16 | if len(needle) == 0: 17 | return 0 18 | if len(needle) > len(haystack): 19 | return -1 20 | for i in range(len(haystack)): 21 | if len(haystack) - i < len(needle): 22 | return -1 23 | if haystack[i:i+len(needle)] == needle: 24 | return i 25 | return -1 26 | -------------------------------------------------------------------------------- /algorithms/arrays/move_zeros.py: -------------------------------------------------------------------------------- 1 | """ 2 | Write an algorithm that takes an array and moves all of the zeros to the end, 3 | preserving the order of the other elements. 4 | move_zeros([false, 1, 0, 1, 2, 0, 1, 3, "a"]) 5 | returns => [false, 1, 1, 2, 1, 3, "a", 0, 0] 6 | 7 | The time complexity of the below algorithm is O(n). 8 | """ 9 | 10 | 11 | # False == 0 is True 12 | def move_zeros(array): 13 | result = [] 14 | zeros = 0 15 | 16 | for i in array: 17 | if i == 0 and type(i) != bool: # not using `not i` to avoid `False`, `[]`, etc. 18 | zeros += 1 19 | else: 20 | result.append(i) 21 | 22 | result.extend([0] * zeros) 23 | return result 24 | 25 | 26 | print(move_zeros([False, 1, 0, 1, 2, 0, 1, 3, "a"])) -------------------------------------------------------------------------------- /algorithms/search/first_occurrence.py: -------------------------------------------------------------------------------- 1 | """ 2 | Find first occurance of a number in a sorted array (increasing order) 3 | Approach- Binary Search 4 | T(n)- O(log n) 5 | """ 6 | def first_occurrence(array, query): 7 | """ 8 | Returns the index of the first occurance of the given element in an array. 9 | The array has to be sorted in increasing order. 10 | """ 11 | 12 | low, high = 0, len(array) - 1 13 | while low <= high: 14 | mid = low + (high-low)//2 #Now mid will be ininteger range 15 | #print("lo: ", lo, " hi: ", hi, " mid: ", mid) 16 | if low == high: 17 | break 18 | if array[mid] < query: 19 | low = mid + 1 20 | else: 21 | high = mid 22 | if array[low] == query: 23 | return low 24 | -------------------------------------------------------------------------------- /algorithms/tree/same_tree.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given two binary trees, write a function to check 3 | if they are equal or not. 4 | 5 | Two binary trees are considered equal if they are 6 | structurally identical and the nodes have the same value. 7 | """ 8 | 9 | 10 | def is_same_tree(tree_p, tree_q): 11 | if tree_p is None and tree_q is None: 12 | return True 13 | if tree_p is not None and tree_q is not None and tree_p.val == tree_q.val: 14 | return is_same_tree(tree_p.left, tree_q.left) and is_same_tree(tree_p.right, tree_q.right) 15 | return False 16 | 17 | # Time Complexity O(min(N,M)) 18 | # where N and M are the number of nodes for the trees. 19 | 20 | # Space Complexity O(min(height1, height2)) 21 | # levels of recursion is the mininum height between the two trees. 22 | -------------------------------------------------------------------------------- /algorithms/set/find_keyboard_row.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a List of words, return the words that can be typed using letters of 3 | alphabet on only one row's of American keyboard. 4 | 5 | For example: 6 | Input: ["Hello", "Alaska", "Dad", "Peace"] 7 | Output: ["Alaska", "Dad"] 8 | 9 | Reference: https://leetcode.com/problems/keyboard-row/description/ 10 | """ 11 | 12 | def find_keyboard_row(words): 13 | """ 14 | :type words: List[str] 15 | :rtype: List[str] 16 | """ 17 | keyboard = [ 18 | set('qwertyuiop'), 19 | set('asdfghjkl'), 20 | set('zxcvbnm'), 21 | ] 22 | result = [] 23 | for word in words: 24 | for key in keyboard: 25 | if set(word.lower()).issubset(key): 26 | result.append(word) 27 | return result 28 | -------------------------------------------------------------------------------- /algorithms/strings/caesar_cipher.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | Julius Caesar protected his confidential information by encrypting it using a cipher. 4 | Caesar's cipher shifts each letter by a number of letters. If the shift takes you 5 | past the end of the alphabet, just rotate back to the front of the alphabet. 6 | In the case of a rotation by 3, w, x, y and z would map to z, a, b and c. 7 | Original alphabet: abcdefghijklmnopqrstuvwxyz 8 | Alphabet rotated +3: defghijklmnopqrstuvwxyzabc 9 | """ 10 | def caesar_cipher(s, k): 11 | result = "" 12 | for char in s: 13 | n = ord(char) 14 | if 64 < n < 91: 15 | n = ((n - 65 + k) % 26) + 65 16 | if 96 < n < 123: 17 | n = ((n - 97 + k) % 26) + 97 18 | result = result + chr(n) 19 | return result 20 | -------------------------------------------------------------------------------- /algorithms/bit/find_missing_number.py: -------------------------------------------------------------------------------- 1 | """ 2 | Returns the missing number from a sequence of unique integers 3 | in range [0..n] in O(n) time and space. The difference between 4 | consecutive integers cannot be more than 1. If the sequence is 5 | already complete, the next integer in the sequence will be returned. 6 | 7 | For example: 8 | Input: nums = [4, 1, 3, 0, 6, 5, 2] 9 | Output: 7 10 | """ 11 | def find_missing_number(nums): 12 | 13 | missing = 0 14 | for i, num in enumerate(nums): 15 | missing ^= num 16 | missing ^= i + 1 17 | 18 | return missing 19 | 20 | 21 | def find_missing_number2(nums): 22 | 23 | num_sum = sum(nums) 24 | n = len(nums) 25 | total_sum = n*(n+1) // 2 26 | missing = total_sum - num_sum 27 | return missing 28 | -------------------------------------------------------------------------------- /algorithms/matrix/rotate_image.py: -------------------------------------------------------------------------------- 1 | """ 2 | You are given an n x n 2D mat representing an image. 3 | 4 | Rotate the image by 90 degrees (clockwise). 5 | 6 | Follow up: 7 | Could you do this in-place? 8 | """ 9 | 10 | 11 | # clockwise rotate 12 | # first reverse up to down, then swap the symmetry 13 | # 1 2 3 7 8 9 7 4 1 14 | # 4 5 6 => 4 5 6 => 8 5 2 15 | # 7 8 9 1 2 3 9 6 3 16 | 17 | def rotate(mat): 18 | if not mat: 19 | return mat 20 | mat.reverse() 21 | for i in range(len(mat)): 22 | for j in range(i): 23 | mat[i][j], mat[j][i] = mat[j][i], mat[i][j] 24 | return mat 25 | 26 | 27 | if __name__ == "__main__": 28 | mat = [[1, 2, 3], 29 | [4, 5, 6], 30 | [7, 8, 9]] 31 | print(mat) 32 | rotate(mat) 33 | print(mat) 34 | -------------------------------------------------------------------------------- /algorithms/sort/pancake_sort.py: -------------------------------------------------------------------------------- 1 | def pancake_sort(arr): 2 | """ 3 | Pancake_sort 4 | Sorting a given array 5 | mutation of selection sort 6 | 7 | reference: https://www.geeksforgeeks.org/pancake-sorting/ 8 | 9 | Overall time complexity : O(N^2) 10 | """ 11 | 12 | len_arr = len(arr) 13 | if len_arr <= 1: 14 | return arr 15 | for cur in range(len(arr), 1, -1): 16 | #Finding index of maximum number in arr 17 | index_max = arr.index(max(arr[0:cur])) 18 | if index_max+1 != cur: 19 | #Needs moving 20 | if index_max != 0: 21 | #reverse from 0 to index_max 22 | arr[:index_max+1] = reversed(arr[:index_max+1]) 23 | # Reverse list 24 | arr[:cur] = reversed(arr[:cur]) 25 | return arr 26 | -------------------------------------------------------------------------------- /algorithms/map/longest_common_subsequence.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given string a and b, with b containing all distinct characters, 3 | find the longest common sub sequence's length. 4 | 5 | Expected complexity O(n logn). 6 | """ 7 | 8 | 9 | def max_common_sub_string(s1, s2): 10 | # Assuming s2 has all unique chars 11 | s2dic = {s2[i]: i for i in range(len(s2))} 12 | maxr = 0 13 | subs = '' 14 | i = 0 15 | while i < len(s1): 16 | if s1[i] in s2dic: 17 | j = s2dic[s1[i]] 18 | k = i 19 | while j < len(s2) and k < len(s1) and s1[k] == s2[j]: 20 | k += 1 21 | j += 1 22 | if k - i > maxr: 23 | maxr = k-i 24 | subs = s1[i:k] 25 | i = k 26 | else: 27 | i += 1 28 | return subs 29 | -------------------------------------------------------------------------------- /algorithms/bit/bytes_int_conversion.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | 4 | def int_to_bytes_big_endian(num): 5 | bytestr = deque() 6 | while num > 0: 7 | # list.insert(0, ...) is inefficient 8 | bytestr.appendleft(num & 0xff) 9 | num >>= 8 10 | return bytes(bytestr) 11 | 12 | 13 | def int_to_bytes_little_endian(num): 14 | bytestr = [] 15 | while num > 0: 16 | bytestr.append(num & 0xff) 17 | num >>= 8 18 | return bytes(bytestr) 19 | 20 | 21 | def bytes_big_endian_to_int(bytestr): 22 | num = 0 23 | for b in bytestr: 24 | num <<= 8 25 | num += b 26 | return num 27 | 28 | 29 | def bytes_little_endian_to_int(bytestr): 30 | num = 0 31 | e = 0 32 | for b in bytestr: 33 | num += b << e 34 | e += 8 35 | return num 36 | -------------------------------------------------------------------------------- /algorithms/bit/swap_pair.py: -------------------------------------------------------------------------------- 1 | """ 2 | Swap_pair: A function swap odd and even bits in an integer with as few instructions 3 | as possible (Ex bit and bit 1 are swapped, bit 2 and bit 3 are swapped) 4 | 5 | For example: 6 | 22: 010110 --> 41: 101001 7 | 10: 1010 --> 5 : 0101 8 | """ 9 | 10 | """ 11 | We can approach this as operating on the odds bit first, and then the even bits. 12 | We can mask all odd bits with 10101010 in binary ('AA') then shift them right by 1 13 | Similarly, we mask all even bit with 01010101 in binary ('55') then shift them left 14 | by 1. Finally, we merge these two values by OR operation. 15 | """ 16 | def swap_pair(num): 17 | # odd bit arithmetic right shift 1 bit 18 | odd = (num & int('AAAAAAAA', 16)) >> 1 19 | # even bit left shift 1 bit 20 | even = (num & int('55555555', 16)) << 1 21 | return odd | even 22 | -------------------------------------------------------------------------------- /algorithms/queues/zigzagiterator.py: -------------------------------------------------------------------------------- 1 | class ZigZagIterator: 2 | def __init__(self, v1, v2): 3 | """ 4 | Initialize your data structure here. 5 | :type v1: List[int] 6 | :type v2: List[int] 7 | """ 8 | self.queue = [_ for _ in (v1, v2) if _] 9 | print(self.queue) 10 | 11 | def next(self): 12 | """ 13 | :rtype: int 14 | """ 15 | v = self.queue.pop(0) 16 | ret = v.pop(0) 17 | if v: 18 | self.queue.append(v) 19 | return ret 20 | 21 | def has_next(self): 22 | """ 23 | :rtype: bool 24 | """ 25 | if self.queue: 26 | return True 27 | return False 28 | 29 | 30 | l1 = [1, 2] 31 | l2 = [3, 4, 5, 6] 32 | it = ZigZagIterator(l1, l2) 33 | while it.has_next(): 34 | print(it.next()) 35 | -------------------------------------------------------------------------------- /algorithms/dp/rod_cut.py: -------------------------------------------------------------------------------- 1 | """A Dynamic Programming solution for Rod cutting problem 2 | """ 3 | 4 | INT_MIN = -32767 5 | 6 | def cut_rod(price): 7 | """ 8 | Returns the best obtainable price for a rod of length n and 9 | price[] as prices of different pieces 10 | """ 11 | n = len(price) 12 | val = [0]*(n+1) 13 | 14 | # Build the table val[] in bottom up manner and return 15 | # the last entry from the table 16 | for i in range(1, n+1): 17 | max_val = INT_MIN 18 | for j in range(i): 19 | max_val = max(max_val, price[j] + val[i-j-1]) 20 | val[i] = max_val 21 | 22 | return val[n] 23 | 24 | # Driver program to test above functions 25 | arr = [1, 5, 8, 9, 10, 17, 17, 20] 26 | print("Maximum Obtainable Value is " + str(cut_rod(arr))) 27 | 28 | # This code is contributed by Bhavya Jain 29 | -------------------------------------------------------------------------------- /algorithms/strings/make_sentence.py: -------------------------------------------------------------------------------- 1 | """ 2 | For a given string and dictionary, how many sentences can you make from the 3 | string, such that all the words are contained in the dictionary. 4 | 5 | eg: for given string -> "appletablet" 6 | "apple", "tablet" 7 | "applet", "able", "t" 8 | "apple", "table", "t" 9 | "app", "let", "able", "t" 10 | 11 | "applet", {app, let, apple, t, applet} => 3 12 | "thing", {"thing"} -> 1 13 | """ 14 | 15 | count = 0 16 | 17 | 18 | def make_sentence(str_piece, dictionaries): 19 | global count 20 | if len(str_piece) == 0: 21 | return True 22 | for i in range(0, len(str_piece)): 23 | prefix, suffix = str_piece[0:i], str_piece[i:] 24 | if prefix in dictionaries: 25 | if suffix in dictionaries or make_sentence(suffix, dictionaries): 26 | count += 1 27 | return True 28 | -------------------------------------------------------------------------------- /algorithms/arrays/limit.py: -------------------------------------------------------------------------------- 1 | """ 2 | Sometimes you need to limit array result to use. Such as you only need the 3 | value over 10 or, you need value under than 100. By use this algorithms, you 4 | can limit your array to specific value 5 | 6 | If array, Min, Max value was given, it returns array that contains values of 7 | given array which was larger than Min, and lower than Max. You need to give 8 | 'unlimit' to use only Min or Max. 9 | 10 | ex) limit([1,2,3,4,5], None, 3) = [1,2,3] 11 | 12 | Complexity = O(n) 13 | """ 14 | 15 | # tl:dr -- array slicing by value 16 | def limit(arr, min_lim=None, max_lim=None): 17 | if len(arr) == 0: 18 | return arr 19 | 20 | if min_lim is None: 21 | min_lim = min(arr) 22 | if max_lim is None: 23 | max_lim = max(arr) 24 | 25 | return list(filter(lambda x: (min_lim <= x <= max_lim), arr)) 26 | -------------------------------------------------------------------------------- /algorithms/bit/flip_bit_longest_sequence.py: -------------------------------------------------------------------------------- 1 | """ 2 | You have an integer and you can flip exactly one bit from a 0 to 1. 3 | Write code to find the length of the longest sequence of 1s you could create. 4 | For example: 5 | Input: 1775 ( or: 11011101111) 6 | Output: 8 7 | """ 8 | 9 | 10 | def flip_bit_longest_seq(num): 11 | 12 | curr_len = 0 13 | prev_len = 0 14 | max_len = 0 15 | 16 | while num: 17 | if num & 1 == 1: # last digit is 1 18 | curr_len += 1 19 | 20 | elif num & 1 == 0: # last digit is 0 21 | if num & 2 == 0: # second last digit is 0 22 | prev_len = 0 23 | else: 24 | prev_len = curr_len 25 | curr_len = 0 26 | 27 | max_len = max(max_len, prev_len + curr_len) 28 | num = num >> 1 # right shift num 29 | 30 | return max_len + 1 31 | -------------------------------------------------------------------------------- /algorithms/queues/reconstruct_queue.py: -------------------------------------------------------------------------------- 1 | # Suppose you have a random list of people standing in a queue. 2 | # Each person is described by a pair of integers (h, k), 3 | # where h is the height of the person and k is the number of people 4 | # in front of this person who have a height greater than or equal to h. 5 | # Write an algorithm to reconstruct the queue. 6 | 7 | # Note: 8 | # The number of people is less than 1,100. 9 | 10 | # Example 11 | 12 | # Input: 13 | # [[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]] 14 | 15 | # Output: 16 | # [[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]] 17 | 18 | def reconstruct_queue(people): 19 | """ 20 | :type people: List[List[int]] 21 | :rtype: List[List[int]] 22 | """ 23 | queue = [] 24 | people.sort(key=lambda x: (-x[0], x[1])) 25 | for h, k in people: 26 | queue.insert(k, [h, k]) 27 | return queue 28 | -------------------------------------------------------------------------------- /algorithms/strings/is_rotated.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given two strings s1 and s2, determine if s2 is a rotated version of s1. 3 | For example, 4 | is_rotated("hello", "llohe") returns True 5 | is_rotated("hello", "helol") returns False 6 | 7 | accepts two strings 8 | returns bool 9 | Reference: https://leetcode.com/problems/rotate-string/description/ 10 | """ 11 | 12 | def is_rotated(s1, s2): 13 | if len(s1) == len(s2): 14 | return s2 in s1 + s1 15 | else: 16 | return False 17 | 18 | """ 19 | Another solution: brutal force 20 | Complexity: O(N^2) 21 | """ 22 | def is_rotated_v1(s1, s2): 23 | if len(s1) != len(s2): 24 | return False 25 | if len(s1) == 0: 26 | return True 27 | 28 | for c in range(len(s1)): 29 | if all(s1[(c + i) % len(s1)] == s2[i] for i in range(len(s1))): 30 | return True 31 | return False 32 | -------------------------------------------------------------------------------- /algorithms/strings/repeat_string.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given two strings A and B, find the minimum number of times A has to be repeated such that B is a substring of it. If no such solution, return -1. 3 | 4 | For example, with A = "abcd" and B = "cdabcdab". 5 | 6 | Return 3, because by repeating A three times (“abcdabcdabcd”), B is a substring of it; and B is not a substring of A repeated two times ("abcdabcd"). 7 | 8 | Note: 9 | The length of A and B will be between 1 and 10000. 10 | 11 | Reference: https://leetcode.com/problems/repeated-string-match/description/ 12 | """ 13 | def repeat_string(A, B): 14 | count = 1 15 | tmp = A 16 | max_count = (len(B) / len(A)) + 1 17 | while not(B in tmp): 18 | tmp = tmp + A 19 | if (count > max_count): 20 | count = -1 21 | break 22 | count = count + 1 23 | 24 | return count 25 | -------------------------------------------------------------------------------- /algorithms/backtrack/subsets_unique.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a collection of integers that might contain duplicates, nums, 3 | return all possible subsets. 4 | 5 | Note: The solution set must not contain duplicate subsets. 6 | 7 | For example, 8 | If nums = [1,2,2], a solution is: 9 | 10 | [ 11 | [2], 12 | [1], 13 | [1,2,2], 14 | [2,2], 15 | [1,2], 16 | [] 17 | ] 18 | """ 19 | 20 | 21 | def subsets_unique(nums): 22 | 23 | def backtrack(res, nums, stack, pos): 24 | if pos == len(nums): 25 | res.add(tuple(stack)) 26 | else: 27 | # take 28 | stack.append(nums[pos]) 29 | backtrack(res, nums, stack, pos+1) 30 | stack.pop() 31 | 32 | # don't take 33 | backtrack(res, nums, stack, pos+1) 34 | 35 | res = set() 36 | backtrack(res, nums, [], 0) 37 | return list(res) 38 | -------------------------------------------------------------------------------- /algorithms/maths/is_strobogrammatic.py: -------------------------------------------------------------------------------- 1 | """ 2 | A strobogrammatic number is a number that looks 3 | the same when rotated 180 degrees (looked at upside down). 4 | 5 | Write a function to determine if a number is strobogrammatic. 6 | The number is represented as a string. 7 | 8 | For example, the numbers "69", "88", and "818" are all strobogrammatic. 9 | """ 10 | 11 | 12 | def is_strobogrammatic(num): 13 | """ 14 | :type num: str 15 | :rtype: bool 16 | """ 17 | comb = "00 11 88 69 96" 18 | i = 0 19 | j = len(num) - 1 20 | while i <= j: 21 | if comb.find(num[i]+num[j]) == -1: 22 | return False 23 | i += 1 24 | j -= 1 25 | return True 26 | 27 | 28 | def is_strobogrammatic2(num: str): 29 | """Another implementation.""" 30 | return num == num[::-1].replace('6', '#').replace('9', '6').replace('#', '9') 31 | -------------------------------------------------------------------------------- /algorithms/maths/pythagoras.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given the lengths of two of the three sides of a right angled triangle, this function returns the 3 | length of the third side. 4 | """ 5 | 6 | def pythagoras(opposite, adjacent, hypotenuse): 7 | """ 8 | Returns length of a third side of a right angled triangle. 9 | Passing "?" will indicate the unknown side. 10 | """ 11 | try: 12 | if opposite == str("?"): 13 | return ("Opposite = " + str(((hypotenuse**2) - (adjacent**2))**0.5)) 14 | if adjacent == str("?"): 15 | return ("Adjacent = " + str(((hypotenuse**2) - (opposite**2))**0.5)) 16 | if hypotenuse == str("?"): 17 | return ("Hypotenuse = " + str(((opposite**2) + (adjacent**2))**0.5)) 18 | return "You already know the answer!" 19 | except: 20 | raise ValueError("invalid argument(s) were given.") 21 | -------------------------------------------------------------------------------- /algorithms/sort/sort_colors.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given an array with n objects colored red, 3 | white or blue, sort them so that objects of the same color 4 | are adjacent, with the colors in the order red, white and blue. 5 | 6 | Here, we will use the integers 0, 1, and 2 to represent 7 | the color red, white, and blue respectively. 8 | 9 | Note: 10 | You are not suppose to use the library's sort function for this problem. 11 | """ 12 | 13 | 14 | def sort_colors(nums): 15 | i = j = 0 16 | for k in range(len(nums)): 17 | v = nums[k] 18 | nums[k] = 2 19 | if v < 2: 20 | nums[j] = 1 21 | j += 1 22 | if v == 0: 23 | nums[i] = 0 24 | i += 1 25 | 26 | 27 | if __name__ == "__main__": 28 | nums = [0, 1, 1, 1, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 2, 2] 29 | sort_colors(nums) 30 | print(nums) 31 | -------------------------------------------------------------------------------- /algorithms/stack/remove_min.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a stack, a function remove_min accepts a stack as a parameter 3 | and removes the smallest value from the stack. 4 | 5 | For example: 6 | bottom [2, 8, 3, -6, 7, 3] top 7 | After remove_min(stack): 8 | bottom [2, 8, 3, 7, 3] top 9 | 10 | """ 11 | 12 | 13 | def remove_min(stack): 14 | storage_stack = [] 15 | if len(stack) == 0: # Stack is empty 16 | return stack 17 | # Find the smallest value 18 | min = stack.pop() 19 | stack.append(min) 20 | for i in range(len(stack)): 21 | val = stack.pop() 22 | if val <= min: 23 | min = val 24 | storage_stack.append(val) 25 | # Back up stack and remove min value 26 | for i in range(len(storage_stack)): 27 | val = storage_stack.pop() 28 | if val != min: 29 | stack.append(val) 30 | return stack 31 | -------------------------------------------------------------------------------- /algorithms/tree/pretty_print.py: -------------------------------------------------------------------------------- 1 | # a -> Adam -> Book -> 4 2 | # b -> Bill -> Computer -> 5 3 | # -> TV -> 6 4 | # Jill -> Sports -> 1 5 | # c -> Bill -> Sports -> 3 6 | # d -> Adam -> Computer -> 3 7 | # Quin -> Computer -> 3 8 | # e -> Quin -> Book -> 5 9 | # -> TV -> 2 10 | # f -> Adam -> Computer -> 7 11 | 12 | from __future__ import print_function 13 | 14 | 15 | def tree_print(tree): 16 | for key in tree: 17 | print(key, end=' ') # end=' ' prevents a newline character 18 | tree_element = tree[key] # multiple lookups is expensive, even amortized O(1)! 19 | for subElem in tree_element: 20 | print(" -> ", subElem, end=' ') 21 | if type(subElem) != str: # OP wants indenting after digits 22 | print("\n ") # newline and a space to match indenting 23 | print() # forces a newline 24 | -------------------------------------------------------------------------------- /algorithms/sort/cocktail_shaker_sort.py: -------------------------------------------------------------------------------- 1 | def cocktail_shaker_sort(arr): 2 | """ 3 | Cocktail_shaker_sort 4 | Sorting a given array 5 | mutation of bubble sort 6 | 7 | reference: https://en.wikipedia.org/wiki/Cocktail_shaker_sort 8 | 9 | Worst-case performance: O(N^2) 10 | """ 11 | 12 | def swap(i, j): 13 | arr[i], arr[j] = arr[j], arr[i] 14 | 15 | n = len(arr) 16 | swapped = True 17 | while swapped: 18 | swapped = False 19 | for i in range(1, n): 20 | if arr[i - 1] > arr[i]: 21 | swap(i - 1, i) 22 | swapped = True 23 | if swapped == False: 24 | return arr 25 | swapped = False 26 | for i in range(n-1,0,-1): 27 | if arr[i - 1] > arr[i]: 28 | swap(i - 1, i) 29 | swapped = True 30 | return arr 31 | -------------------------------------------------------------------------------- /algorithms/tree/bst/bst_closest_value.py: -------------------------------------------------------------------------------- 1 | # Given a non-empty binary search tree and a target value, 2 | # find the value in the BST that is closest to the target. 3 | 4 | # Note: 5 | # Given target value is a floating point. 6 | # You are guaranteed to have only one unique value in the BST 7 | # that is closest to the target. 8 | 9 | 10 | # Definition for a binary tree node. 11 | # class TreeNode(object): 12 | # def __init__(self, x): 13 | # self.val = x 14 | # self.left = None 15 | # self.right = None 16 | 17 | def closest_value(root, target): 18 | """ 19 | :type root: TreeNode 20 | :type target: float 21 | :rtype: int 22 | """ 23 | a = root.val 24 | kid = root.left if target < a else root.right 25 | if not kid: 26 | return a 27 | b = closest_value(kid, target) 28 | return min((a,b), key=lambda x: abs(target-x)) 29 | -------------------------------------------------------------------------------- /algorithms/tree/bst/serialize_deserialize.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class TreeNode(object): 4 | def __init__(self, x): 5 | self.val = x 6 | self.left = None 7 | self.right = None 8 | 9 | 10 | def serialize(root): 11 | def build_string(node): 12 | if node: 13 | vals.append(str(node.val)) 14 | build_string(node.left) 15 | build_string(node.right) 16 | else: 17 | vals.append("#") 18 | vals = [] 19 | build_string(root) 20 | return " ".join(vals) 21 | 22 | 23 | def deserialize(data): 24 | def build_tree(): 25 | val = next(vals) 26 | if val == "#": 27 | return None 28 | node = TreeNode(int(val)) 29 | node.left = build_tree() 30 | node.right = build_tree() 31 | return node 32 | vals = iter(data.split()) 33 | return build_tree() 34 | -------------------------------------------------------------------------------- /algorithms/bit/find_difference.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given two strings s and t which consist of only lowercase letters. 3 | String t is generated by random shuffling string s and then add one more letter 4 | at a random position. Find the letter that was added in t. 5 | 6 | For example: 7 | Input: 8 | s = "abcd" 9 | t = "abecd" 10 | Output: 'e' 11 | 12 | Explanation: 13 | 'e' is the letter that was added. 14 | """ 15 | 16 | """ 17 | We use the characteristic equation of XOR. 18 | A xor B xor C = A xor C xor B 19 | If A == C, then A xor C = 0 20 | and then, B xor 0 = B 21 | """ 22 | def find_difference(s, t): 23 | ret = 0 24 | for ch in s + t: 25 | # ord(ch) return an integer representing the Unicode code point of that character 26 | ret = ret ^ ord(ch) 27 | # chr(i) Return the string representing a character whose Unicode code point is the integer i 28 | return chr(ret) 29 | -------------------------------------------------------------------------------- /algorithms/strings/judge_circle.py: -------------------------------------------------------------------------------- 1 | """ 2 | Initially, there is a Robot at position (0, 0). Given a sequence of its moves, 3 | judge if this robot makes a circle, which means it moves back to the original place. 4 | 5 | The move sequence is represented by a string. And each move is represent by a 6 | character. The valid robot moves are R (Right), L (Left), U (Up) and D (down). 7 | The output should be true or false representing whether the robot makes a circle. 8 | 9 | Example 1: 10 | Input: "UD" 11 | Output: true 12 | Example 2: 13 | Input: "LL" 14 | Output: false 15 | """ 16 | def judge_circle(moves): 17 | dict_moves = { 18 | 'U' : 0, 19 | 'D' : 0, 20 | 'R' : 0, 21 | 'L' : 0 22 | } 23 | for char in moves: 24 | dict_moves[char] = dict_moves[char] + 1 25 | return dict_moves['L'] == dict_moves['R'] and dict_moves['U'] == dict_moves['D'] 26 | -------------------------------------------------------------------------------- /algorithms/sort/bogo_sort.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | def bogo_sort(arr, simulation=False): 4 | """Bogo Sort 5 | Best Case Complexity: O(n) 6 | Worst Case Complexity: O(∞) 7 | Average Case Complexity: O(n(n-1)!) 8 | """ 9 | 10 | iteration = 0 11 | if simulation: 12 | print("iteration",iteration,":",*arr) 13 | 14 | def is_sorted(arr): 15 | #check the array is inorder 16 | i = 0 17 | arr_len = len(arr) 18 | while i+1 < arr_len: 19 | if arr[i] > arr[i+1]: 20 | return False 21 | i += 1 22 | 23 | 24 | return True 25 | while not is_sorted(arr): 26 | random.shuffle(arr) 27 | 28 | if simulation: 29 | iteration = iteration + 1 30 | print("iteration",iteration,":",*arr) 31 | 32 | return arr 33 | -------------------------------------------------------------------------------- /algorithms/dp/climbing_stairs.py: -------------------------------------------------------------------------------- 1 | """ 2 | You are climbing a stair case. 3 | It takes `steps` number of steps to reach to the top. 4 | 5 | Each time you can either climb 1 or 2 steps. 6 | In how many distinct ways can you climb to the top? 7 | 8 | Note: Given argument `steps` will be a positive integer. 9 | """ 10 | 11 | 12 | # O(n) space 13 | 14 | def climb_stairs(steps): 15 | """ 16 | :type steps: int 17 | :rtype: int 18 | """ 19 | arr = [1, 1] 20 | for _ in range(1, steps): 21 | arr.append(arr[-1] + arr[-2]) 22 | return arr[-1] 23 | 24 | 25 | # the above function can be optimized as: 26 | # O(1) space 27 | 28 | def climb_stairs_optimized(steps): 29 | """ 30 | :type steps: int 31 | :rtype: int 32 | """ 33 | a_steps = b_steps = 1 34 | for _ in range(steps): 35 | a_steps, b_steps = b_steps, a_steps + b_steps 36 | return a_steps 37 | -------------------------------------------------------------------------------- /algorithms/queues/moving_average.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | from collections import deque 3 | 4 | 5 | class MovingAverage(object): 6 | def __init__(self, size): 7 | """ 8 | Initialize your data structure here. 9 | :type size: int 10 | """ 11 | self.queue = deque(maxlen=size) 12 | 13 | def next(self, val): 14 | """ 15 | :type val: int 16 | :rtype: float 17 | """ 18 | self.queue.append(val) 19 | return sum(self.queue) / len(self.queue) 20 | 21 | 22 | # Given a stream of integers and a window size, 23 | # calculate the moving average of all integers in the sliding window. 24 | if __name__ == '__main__': 25 | m = MovingAverage(3) 26 | assert m.next(1) == 1 27 | assert m.next(10) == (1 + 10) / 2 28 | assert m.next(3) == (1 + 10 + 3) / 3 29 | assert m.next(5) == (10 + 3 + 5) / 3 30 | -------------------------------------------------------------------------------- /algorithms/stack/simplify_path.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given an absolute path for a file (Unix-style), simplify it. 3 | 4 | For example, 5 | path = "/home/", => "/home" 6 | path = "/a/./b/../../c/", => "/c" 7 | 8 | * Did you consider the case where path = "/../"? 9 | In this case, you should return "/". 10 | * Another corner case is the path might contain multiple slashes '/' together, 11 | such as "/home//foo/". In this case, you should ignore redundant 12 | slashes and return "/home/foo". 13 | """ 14 | def simplify_path(path): 15 | """ 16 | :type path: str 17 | :rtype: str 18 | """ 19 | skip = {'..', '.', ''} 20 | stack = [] 21 | paths = path.split('/') 22 | for tok in paths: 23 | if tok == '..': 24 | if stack: 25 | stack.pop() 26 | elif tok not in skip: 27 | stack.append(tok) 28 | return '/' + '/'.join(stack) 29 | -------------------------------------------------------------------------------- /algorithms/strings/encode_decode.py: -------------------------------------------------------------------------------- 1 | """ Design an algorithm to encode a list of strings to a string. 2 | The encoded mystring is then sent over the network and is decoded 3 | back to the original list of strings. 4 | """ 5 | 6 | # Implement the encode and decode methods. 7 | 8 | def encode(strs): 9 | """Encodes a list of strings to a single string. 10 | :type strs: List[str] 11 | :rtype: str 12 | """ 13 | res = '' 14 | for string in strs.split(): 15 | res += str(len(string)) + ":" + string 16 | return res 17 | 18 | def decode(s): 19 | """Decodes a single string to a list of strings. 20 | :type s: str 21 | :rtype: List[str] 22 | """ 23 | strs = [] 24 | i = 0 25 | while i < len(s): 26 | index = s.find(":", i) 27 | size = int(s[i:index]) 28 | strs.append(s[index+1: index+1+size]) 29 | i = index+1+size 30 | return strs -------------------------------------------------------------------------------- /algorithms/arrays/remove_duplicates.py: -------------------------------------------------------------------------------- 1 | """ 2 | This algorithm removes any duplicates from an array and returns a new array with those duplicates 3 | removed. 4 | 5 | For example: 6 | 7 | Input: [1, 1 ,1 ,2 ,2 ,3 ,4 ,4 ,"hey", "hey", "hello", True, True] 8 | Output: [1, 2, 3, 4, 'hey', 'hello'] 9 | 10 | Time Complexity: O(n) for hashable items, O(n²) worst case for unhashable items 11 | Space Complexity: O(n) for the seen set and result array 12 | """ 13 | 14 | from collections.abc import Hashable 15 | 16 | 17 | def remove_duplicates(array): 18 | seen = set() 19 | new_array = [] 20 | 21 | for item in array: 22 | if isinstance(item, Hashable): 23 | if item not in seen: 24 | seen.add(item) 25 | new_array.append(item) 26 | else: 27 | if item not in new_array: 28 | new_array.append(item) 29 | 30 | return new_array 31 | -------------------------------------------------------------------------------- /algorithms/backtrack/generate_abbreviations.py: -------------------------------------------------------------------------------- 1 | """ 2 | given input word, return the list of abbreviations. 3 | ex) 4 | word => ['word', 'wor1', 'wo1d', 'wo2', 'w1rd', 'w1r1', 'w2d', 'w3', '1ord', '1or1', '1o1d', '1o2', '2rd', '2r1', '3d', '4'] 5 | """ 6 | 7 | 8 | def generate_abbreviations(word): 9 | 10 | def backtrack(result, word, pos, count, cur): 11 | if pos == len(word): 12 | if count > 0: 13 | cur += str(count) 14 | result.append(cur) 15 | return 16 | 17 | if count > 0: # add the current word 18 | backtrack(result, word, pos+1, 0, cur+str(count)+word[pos]) 19 | else: 20 | backtrack(result, word, pos+1, 0, cur+word[pos]) 21 | # skip the current word 22 | backtrack(result, word, pos+1, count+1, cur) 23 | 24 | result = [] 25 | backtrack(result, word, 0, 0, "") 26 | return result 27 | -------------------------------------------------------------------------------- /algorithms/bit/single_number2.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given an array of integers, every element appears 3 | three times except for one, which appears exactly once. 4 | Find that single one. 5 | 6 | Note: 7 | Your algorithm should have a linear runtime complexity. 8 | Could you implement it without using extra memory? 9 | 10 | 11 | Solution: 12 | 32 bits for each integer. 13 | Consider 1 bit in it, the sum of each integer's corresponding bit 14 | (except for the single number) 15 | should be 0 if mod by 3. Hence, we sum the bits of all 16 | integers and mod by 3, 17 | the remaining should be the exact bit of the single number. 18 | In this way, you get the 32 bits of the single number. 19 | """ 20 | 21 | # Another awesome answer 22 | def single_number2(nums): 23 | ones, twos = 0, 0 24 | for i in range(len(nums)): 25 | ones = (ones ^ nums[i]) & ~twos 26 | twos = (twos ^ nums[i]) & ~ones 27 | return ones 28 | -------------------------------------------------------------------------------- /algorithms/linkedlist/swap_in_pairs.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a linked list, swap every two adjacent nodes 3 | and return its head. 4 | 5 | For example, 6 | Given 1->2->3->4, you should return the list as 2->1->4->3. 7 | 8 | Your algorithm should use only constant space. 9 | You may not modify the values in the list, 10 | only nodes itself can be changed. 11 | """ 12 | class Node(object): 13 | def __init__(self, x): 14 | self.val = x 15 | self.next = None 16 | 17 | def swap_pairs(head): 18 | if not head: 19 | return head 20 | start = Node(0) 21 | start.next = head 22 | current = start 23 | while current.next and current.next.next: 24 | first = current.next 25 | second = current.next.next 26 | first.next = second.next 27 | current.next = second 28 | current.next.next = first 29 | current = current.next.next 30 | return start.next 31 | -------------------------------------------------------------------------------- /algorithms/tree/traversal/level_order.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a binary tree, return the level order traversal of 3 | its nodes' values. (ie, from left to right, level by level). 4 | 5 | For example: 6 | Given binary tree [3,9,20,null,null,15,7], 7 | 3 8 | / \ 9 | 9 20 10 | / \ 11 | 15 7 12 | return its level order traversal as: 13 | [ 14 | [3], 15 | [9,20], 16 | [15,7] 17 | ] 18 | """ 19 | 20 | 21 | def level_order(root): 22 | ans = [] 23 | if not root: 24 | return ans 25 | level = [root] 26 | while level: 27 | current = [] 28 | new_level = [] 29 | for node in level: 30 | current.append(node.val) 31 | if node.left: 32 | new_level.append(node.left) 33 | if node.right: 34 | new_level.append(node.right) 35 | level = new_level 36 | ans.append(current) 37 | return ans 38 | -------------------------------------------------------------------------------- /algorithms/linkedlist/linkedlist.py: -------------------------------------------------------------------------------- 1 | # Pros 2 | # Linked Lists have constant-time insertions and deletions in any position, 3 | # in comparison, arrays require O(n) time to do the same thing. 4 | # Linked lists can continue to expand without having to specify 5 | # their size ahead of time (remember our lectures on Array sizing 6 | # from the Array Sequence section of the course!) 7 | 8 | # Cons 9 | # To access an element in a linked list, you need to take O(k) time 10 | # to go from the head of the list to the kth element. 11 | # In contrast, arrays have constant time operations to access 12 | # elements in an array. 13 | 14 | class DoublyLinkedListNode(object): 15 | def __init__(self, value): 16 | self.value = value 17 | self.next = None 18 | self.prev = None 19 | 20 | 21 | class SinglyLinkedListNode(object): 22 | def __init__(self, value): 23 | self.value = value 24 | self.next = None 25 | -------------------------------------------------------------------------------- /algorithms/sort/bucket_sort.py: -------------------------------------------------------------------------------- 1 | def bucket_sort(arr): 2 | ''' Bucket Sort 3 | Complexity: O(n^2) 4 | The complexity is dominated by nextSort 5 | ''' 6 | # The number of buckets and make buckets 7 | num_buckets = len(arr) 8 | buckets = [[] for bucket in range(num_buckets)] 9 | # Assign values into bucket_sort 10 | for value in arr: 11 | index = value * num_buckets // (max(arr) + 1) 12 | buckets[index].append(value) 13 | # Sort 14 | sorted_list = [] 15 | for i in range(num_buckets): 16 | sorted_list.extend(next_sort(buckets[i])) 17 | return sorted_list 18 | 19 | def next_sort(arr): 20 | # We will use insertion sort here. 21 | for i in range(1, len(arr)): 22 | j = i - 1 23 | key = arr[i] 24 | while arr[j] > key and j >= 0: 25 | arr[j+1] = arr[j] 26 | j = j - 1 27 | arr[j + 1] = key 28 | return arr 29 | -------------------------------------------------------------------------------- /algorithms/sort/radix_sort.py: -------------------------------------------------------------------------------- 1 | """ 2 | radix sort 3 | complexity: O(nk + n) . n is the size of input list and k is the digit length of the number 4 | """ 5 | def radix_sort(arr, simulation=False): 6 | position = 1 7 | max_number = max(arr) 8 | 9 | iteration = 0 10 | if simulation: 11 | print("iteration", iteration, ":", *arr) 12 | 13 | while position <= max_number: 14 | queue_list = [list() for _ in range(10)] 15 | 16 | for num in arr: 17 | digit_number = num // position % 10 18 | queue_list[digit_number].append(num) 19 | 20 | index = 0 21 | for numbers in queue_list: 22 | for num in numbers: 23 | arr[index] = num 24 | index += 1 25 | 26 | if simulation: 27 | iteration = iteration + 1 28 | print("iteration", iteration, ":", *arr) 29 | 30 | position *= 10 31 | return arr 32 | -------------------------------------------------------------------------------- /algorithms/maths/find_order_simple.py: -------------------------------------------------------------------------------- 1 | """ 2 | For positive integer n and given integer a that satisfies gcd(a, n) = 1, 3 | the order of a modulo n is the smallest positive integer k that satisfies 4 | pow (a, k) % n = 1. In other words, (a^k) ≡ 1 (mod n). 5 | Order of a certain number may or may not be exist. If not, return -1. 6 | 7 | Total time complexity O(nlog(n)): 8 | O(n) for iteration loop, 9 | O(log(n)) for built-in power function 10 | """ 11 | 12 | import math 13 | 14 | def find_order(a, n): 15 | """ 16 | Find order for positive integer n and given integer a that satisfies gcd(a, n) = 1. 17 | """ 18 | if (a == 1) & (n == 1): 19 | # Exception Handeling : 1 is the order of of 1 20 | return 1 21 | if math.gcd(a, n) != 1: 22 | print ("a and n should be relative prime!") 23 | return -1 24 | for i in range(1, n): 25 | if pow(a, i) % n == 1: 26 | return i 27 | return -1 28 | -------------------------------------------------------------------------------- /algorithms/matrix/search_in_sorted_matrix.py: -------------------------------------------------------------------------------- 1 | # 2 | # Search a key in a row wise and column wise sorted (non-decreasing) matrix. 3 | # m- Number of rows in the matrix 4 | # n- Number of columns in the matrix 5 | # T(n)- O(m+n) 6 | # 7 | 8 | 9 | def search_in_a_sorted_matrix(mat, m, n, key): 10 | i, j = m-1, 0 11 | while i >= 0 and j < n: 12 | if key == mat[i][j]: 13 | print('Key %s found at row- %s column- %s' % (key, i+1, j+1)) 14 | return 15 | if key < mat[i][j]: 16 | i -= 1 17 | else: 18 | j += 1 19 | print('Key %s not found' % (key)) 20 | 21 | 22 | def main(): 23 | mat = [ 24 | [2, 5, 7], 25 | [4, 8, 13], 26 | [9, 11, 15], 27 | [12, 17, 20] 28 | ] 29 | key = 13 30 | print(mat) 31 | search_in_a_sorted_matrix(mat, len(mat), len(mat[0]), key) 32 | 33 | 34 | if __name__ == '__main__': 35 | main() 36 | -------------------------------------------------------------------------------- /algorithms/arrays/trimmean.py: -------------------------------------------------------------------------------- 1 | """ 2 | When make reliable means, we need to neglect best and worst values. 3 | For example, when making average score on athletes we need this option. 4 | So, this algorithm affixes some percentage to neglect when making mean. 5 | For example, if you suggest 20%, it will neglect the best 10% of values 6 | and the worst 10% of values. 7 | 8 | This algorithm takes an array and percentage to neglect. After sorted, 9 | if index of array is larger or smaller than desired ratio, we don't 10 | compute it. 11 | 12 | Compleity: O(n) 13 | """ 14 | def trimmean(arr, per): 15 | ratio = per/200 16 | # /100 for easy calculation by *, and /2 for easy adaption to best and worst parts. 17 | cal_sum = 0 18 | # sum value to be calculated to trimmean. 19 | arr.sort() 20 | neg_val = int(len(arr)*ratio) 21 | arr = arr[neg_val:len(arr)-neg_val] 22 | for i in arr: 23 | cal_sum += i 24 | return cal_sum/len(arr) 25 | -------------------------------------------------------------------------------- /algorithms/sort/bubble_sort.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | https://en.wikipedia.org/wiki/Bubble_sort 4 | 5 | Worst-case performance: O(N^2) 6 | 7 | If you call bubble_sort(arr,True), you can see the process of the sort 8 | Default is simulation = False 9 | 10 | """ 11 | 12 | 13 | def bubble_sort(arr, simulation=False): 14 | def swap(i, j): 15 | arr[i], arr[j] = arr[j], arr[i] 16 | 17 | n = len(arr) 18 | swapped = True 19 | 20 | iteration = 0 21 | if simulation: 22 | print("iteration",iteration,":",*arr) 23 | x = -1 24 | while swapped: 25 | swapped = False 26 | x = x + 1 27 | for i in range(1, n-x): 28 | if arr[i - 1] > arr[i]: 29 | swap(i - 1, i) 30 | swapped = True 31 | if simulation: 32 | iteration = iteration + 1 33 | print("iteration",iteration,":",*arr) 34 | 35 | return arr 36 | -------------------------------------------------------------------------------- /algorithms/tree/bin_tree_to_list.py: -------------------------------------------------------------------------------- 1 | from tree.tree import TreeNode 2 | 3 | 4 | def bin_tree_to_list(root): 5 | """ 6 | type root: root class 7 | """ 8 | if not root: 9 | return root 10 | root = bin_tree_to_list_util(root) 11 | while root.left: 12 | root = root.left 13 | return root 14 | 15 | 16 | def bin_tree_to_list_util(root): 17 | if not root: 18 | return root 19 | if root.left: 20 | left = bin_tree_to_list_util(root.left) 21 | while left.right: 22 | left = left.right 23 | left.right = root 24 | root.left = left 25 | if root.right: 26 | right = bin_tree_to_list_util(root.right) 27 | while right.left: 28 | right = right.left 29 | right.left = root 30 | root.right = right 31 | return root 32 | 33 | 34 | def print_tree(root): 35 | while root: 36 | print(root.val) 37 | root = root.right 38 | -------------------------------------------------------------------------------- /docs/source/_static/logo/128pxblack.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /algorithms/linkedlist/reverse.py: -------------------------------------------------------------------------------- 1 | """ 2 | Reverse a singly linked list. For example: 3 | 4 | 1 --> 2 --> 3 --> 4 5 | After reverse: 6 | 4 --> 3 --> 2 --> 1 7 | """ 8 | # 9 | # Iterative solution 10 | # T(n)- O(n) 11 | # 12 | def reverse_list(head): 13 | """ 14 | :type head: ListNode 15 | :rtype: ListNode 16 | """ 17 | if not head or not head.next: 18 | return head 19 | prev = None 20 | while head: 21 | current = head 22 | head = head.next 23 | current.next = prev 24 | prev = current 25 | return prev 26 | 27 | 28 | # 29 | # Recursive solution 30 | # T(n)- O(n) 31 | # 32 | def reverse_list_recursive(head): 33 | """ 34 | :type head: ListNode 35 | :rtype: ListNode 36 | """ 37 | if head is None or head.next is None: 38 | return head 39 | p = head.next 40 | head.next = None 41 | revrest = reverse_list_recursive(p) 42 | p.next = head 43 | return revrest 44 | -------------------------------------------------------------------------------- /algorithms/distribution/histogram.py: -------------------------------------------------------------------------------- 1 | """ 2 | Histogram function. 3 | 4 | Histogram is an accurate representation of the distribution of numerical data. 5 | It is an estimate of the probability distribution of a continuous variable. 6 | https://en.wikipedia.org/wiki/Histogram 7 | 8 | Example: 9 | list_1 = [3, 3, 2, 1] 10 | :return {1: 1, 2: 1, 3: 2} 11 | 12 | list_2 = [2, 3, 5, 5, 5, 6, 4, 3, 7] 13 | :return {2: 1, 3: 2, 4: 1, 5: 3, 6: 1, 7: 1} 14 | """ 15 | 16 | 17 | def get_histogram(input_list: list) -> dict: 18 | """ 19 | Get histogram representation 20 | :param input_list: list with different and unordered values 21 | :return histogram: dict with histogram of input_list 22 | """ 23 | # Create dict to store histogram 24 | histogram = {} 25 | # For each list value, add one to the respective histogram dict position 26 | for i in input_list: 27 | histogram[i] = histogram.get(i, 0) + 1 28 | return histogram 29 | -------------------------------------------------------------------------------- /algorithms/arrays/delete_nth.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a list lst and a number N, create a new list 3 | that contains each number of the list at most N times without reordering. 4 | 5 | For example if N = 2, and the input is [1,2,3,1,2,1,2,3], you take [1,2,3,1,2], 6 | drop the next [1,2] since this would lead to 1 and 2 being in the result 3 times, and then take 3, 7 | which leads to [1,2,3,1,2,3] 8 | """ 9 | import collections 10 | 11 | 12 | # Time complexity O(n^2) 13 | def delete_nth_naive(array, n): 14 | ans = [] 15 | for num in array: 16 | if ans.count(num) < n: 17 | ans.append(num) 18 | return ans 19 | 20 | 21 | # Time Complexity O(n), using hash tables. 22 | def delete_nth(array, n): 23 | result = [] 24 | counts = collections.defaultdict(int) # keep track of occurrences 25 | 26 | for i in array: 27 | 28 | if counts[i] < n: 29 | result.append(i) 30 | counts[i] += 1 31 | 32 | return result 33 | -------------------------------------------------------------------------------- /algorithms/maths/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Collection of mathematical algorithms and functions. 3 | """ 4 | from .base_conversion import * 5 | from .decimal_to_binary_ip import * 6 | from .euler_totient import * 7 | from .extended_gcd import * 8 | from .factorial import * 9 | from .gcd import * 10 | from .generate_strobogrammtic import * 11 | from .is_strobogrammatic import * 12 | from .modular_exponential import * 13 | from .next_perfect_square import * 14 | from .prime_check import * 15 | from .primes_sieve_of_eratosthenes import * 16 | from .pythagoras import * 17 | from .rabin_miller import * 18 | from .rsa import * 19 | from .combination import * 20 | from .cosine_similarity import * 21 | from .find_order_simple import * 22 | from .find_primitive_root_simple import * 23 | from .diffie_hellman_key_exchange import * 24 | from .num_digits import * 25 | from .power import * 26 | from .magic_number import * 27 | from .krishnamurthy_number import * 28 | from .num_perfect_squares import * 29 | -------------------------------------------------------------------------------- /algorithms/maths/fft.py: -------------------------------------------------------------------------------- 1 | """ 2 | Implementation of the Cooley-Tukey, which is the most common FFT algorithm. 3 | 4 | Input: an array of complex values which has a size of N, where N is an integer power of 2 5 | Output: an array of complex values which is the discrete fourier transform of the input 6 | 7 | Example 1 8 | Input: [2.0+2j, 1.0+3j, 3.0+1j, 2.0+2j] 9 | Output: [8+8j, 2j, 2-2j, -2+0j] 10 | 11 | 12 | Pseudocode: https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm 13 | """ 14 | from cmath import exp, pi 15 | 16 | def fft(x): 17 | """ Recursive implementation of the Cooley-Tukey""" 18 | N = len(x) 19 | if N == 1: 20 | return x 21 | 22 | # get the elements at even/odd indices 23 | even = fft(x[0::2]) 24 | odd = fft(x[1::2]) 25 | 26 | y = [0 for i in range(N)] 27 | for k in range(N//2): 28 | q = exp(-2j*pi*k/N)*odd[k] 29 | y[k] = even[k] + q 30 | y[k + N//2] = even[k] - q 31 | 32 | return y 33 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | set SPHINXPROJ=algorithms 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 20 | echo.installed, then set the SPHINXBUILD environment variable to point 21 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 22 | echo.may add the Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /algorithms/dp/knapsack.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given the capacity of the knapsack and items specified by weights and values, 3 | return the maximum summarized value of the items that can be fit in the 4 | knapsack. 5 | 6 | Example: 7 | capacity = 5, items(value, weight) = [(60, 5), (50, 3), (70, 4), (30, 2)] 8 | result = 80 (items valued 50 and 30 can both be fit in the knapsack) 9 | 10 | The time complexity is O(n * m) and the space complexity is O(m), where n is 11 | the total number of items and m is the knapsack's capacity. 12 | """ 13 | 14 | 15 | class Item: 16 | 17 | def __init__(self, value, weight): 18 | self.value = value 19 | self.weight = weight 20 | 21 | 22 | def get_maximum_value(items, capacity): 23 | dp = [0] * (capacity + 1) 24 | for item in items: 25 | for cur_weight in reversed(range(item.weight, capacity+1)): 26 | dp[cur_weight] = max(dp[cur_weight], item.value + dp[cur_weight - item.weight]) 27 | return dp[capacity] 28 | 29 | -------------------------------------------------------------------------------- /algorithms/unix/path/simplify_path.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given an absolute path for a file (Unix-style), simplify it. 3 | 4 | For example, 5 | path = "/home/", => "/home" 6 | path = "/a/./b/../../c/", => "/c" 7 | 8 | Corner Cases: 9 | 10 | Did you consider the case where path = "/../"? 11 | In this case, you should return "/". 12 | Another corner case is the path might contain multiple slashes '/' together, such as "/home//foo/". 13 | In this case, you should ignore redundant slashes and return "/home/foo". 14 | 15 | Reference: https://leetcode.com/problems/simplify-path/description/ 16 | """ 17 | 18 | import os 19 | def simplify_path_v1(path): 20 | return os.path.abspath(path) 21 | 22 | def simplify_path_v2(path): 23 | stack, tokens = [], path.split("/") 24 | for token in tokens: 25 | if token == ".." and stack: 26 | stack.pop() 27 | elif token != ".." and token != "." and token: 28 | stack.append(token) 29 | return "/" + "/".join(stack) 30 | -------------------------------------------------------------------------------- /algorithms/maths/modular_inverse.py: -------------------------------------------------------------------------------- 1 | # extended_gcd(a, b) modified from 2 | # https://github.com/keon/algorithms/blob/master/algorithms/maths/extended_gcd.py 3 | 4 | def extended_gcd(a: int, b: int) -> [int, int, int]: 5 | """Extended GCD algorithm. 6 | Return s, t, g 7 | such that a * s + b * t = GCD(a, b) 8 | and s and t are co-prime. 9 | """ 10 | 11 | old_s, s = 1, 0 12 | old_t, t = 0, 1 13 | old_r, r = a, b 14 | 15 | while r != 0: 16 | quotient = old_r // r 17 | 18 | old_r, r = r, old_r - quotient * r 19 | old_s, s = s, old_s - quotient * s 20 | old_t, t = t, old_t - quotient * t 21 | 22 | return old_s, old_t, old_r 23 | 24 | 25 | def modular_inverse(a: int, m: int) -> int: 26 | """ 27 | Returns x such that a * x = 1 (mod m) 28 | a and m must be coprime 29 | """ 30 | 31 | s, _, g = extended_gcd(a, m) 32 | if g != 1: 33 | raise ValueError("a and m must be coprime") 34 | return s % m 35 | -------------------------------------------------------------------------------- /algorithms/search/search_range.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given an array of integers nums sorted in ascending order, find the starting 3 | and ending position of a given target value. If the target is not found in the 4 | array, return [-1, -1]. 5 | 6 | For example: 7 | Input: nums = [5,7,7,8,8,8,10], target = 8 8 | Output: [3,5] 9 | Input: nums = [5,7,7,8,8,8,10], target = 11 10 | Output: [-1,-1] 11 | """ 12 | def search_range(nums, target): 13 | """ 14 | :type nums: List[int] 15 | :type target: int 16 | :rtype: List[int] 17 | """ 18 | low = 0 19 | high = len(nums) - 1 20 | # breaks at low == high 21 | # both pointing to first occurence of target 22 | while low < high: 23 | mid = low + (high - low) // 2 24 | if target <= nums[mid]: 25 | high = mid 26 | else: 27 | low = mid + 1 28 | 29 | for j in range(len(nums) - 1, -1, -1): 30 | if nums[j] == target: 31 | return [low, j] 32 | 33 | return [-1, -1] 34 | -------------------------------------------------------------------------------- /algorithms/backtrack/letter_combination.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a digit string, return all possible letter 3 | combinations that the number could represent. 4 | 5 | A mapping of digit to letters (just like on the telephone buttons) is given below: 6 | 2: "abc" 7 | 3: "def" 8 | 4: "ghi" 9 | 5: "jkl" 10 | 6: "mno" 11 | 7: "pqrs" 12 | 8: "tuv" 13 | 9: "wxyz" 14 | 15 | Input:Digit string "23" 16 | Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]. 17 | """ 18 | 19 | 20 | def letter_combinations(digits): 21 | if digits == "": 22 | return [] 23 | kmaps = { 24 | "2": "abc", 25 | "3": "def", 26 | "4": "ghi", 27 | "5": "jkl", 28 | "6": "mno", 29 | "7": "pqrs", 30 | "8": "tuv", 31 | "9": "wxyz" 32 | } 33 | ans = [""] 34 | for num in digits: 35 | tmp = [] 36 | for an in ans: 37 | for char in kmaps[num]: 38 | tmp.append(an + char) 39 | ans = tmp 40 | return ans 41 | -------------------------------------------------------------------------------- /docs/source/_static/logo/256pxblack.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /algorithms/strings/one_edit_distance.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given two strings S and T, determine if they are both one edit distance apart. 3 | """ 4 | 5 | 6 | def is_one_edit(s, t): 7 | """ 8 | :type s: str 9 | :type t: str 10 | :rtype: bool 11 | """ 12 | if len(s) > len(t): 13 | return is_one_edit(t, s) 14 | if len(t) - len(s) > 1 or t == s: 15 | return False 16 | for i in range(len(s)): 17 | if s[i] != t[i]: 18 | return s[i+1:] == t[i+1:] or s[i:] == t[i+1:] 19 | return True 20 | 21 | 22 | def is_one_edit2(s, t): 23 | l1, l2 = len(s), len(t) 24 | if l1 > l2: 25 | return is_one_edit2(t, s) 26 | if len(t) - len(s) > 1 or t == s: 27 | return False 28 | for i in range(len(s)): 29 | if s[i] != t[i]: 30 | if l1 == l2: 31 | s = s[:i]+t[i]+s[i+1:] # modify 32 | else: 33 | s = s[:i]+t[i]+s[i:] # insertion 34 | break 35 | return s == t or s == t[:-1] 36 | -------------------------------------------------------------------------------- /algorithms/tree/is_balanced.py: -------------------------------------------------------------------------------- 1 | def is_balanced(root): 2 | return __is_balanced_recursive(root) 3 | 4 | 5 | def __is_balanced_recursive(root): 6 | """ 7 | O(N) solution 8 | """ 9 | return -1 != __get_depth(root) 10 | 11 | 12 | def __get_depth(root): 13 | """ 14 | return 0 if unbalanced else depth + 1 15 | """ 16 | if root is None: 17 | return 0 18 | left = __get_depth(root.left) 19 | right = __get_depth(root.right) 20 | if abs(left-right) > 1 or -1 in [left, right]: 21 | return -1 22 | return 1 + max(left, right) 23 | 24 | 25 | # def is_balanced(root): 26 | # """ 27 | # O(N^2) solution 28 | # """ 29 | # left = max_height(root.left) 30 | # right = max_height(root.right) 31 | # return abs(left-right) <= 1 and is_balanced(root.left) and 32 | # is_balanced(root.right) 33 | 34 | # def max_height(root): 35 | # if root is None: 36 | # return 0 37 | # return max(max_height(root.left), max_height(root.right)) + 1 38 | -------------------------------------------------------------------------------- /algorithms/tree/traversal/preorder.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Time complexity : O(n) 3 | ''' 4 | 5 | 6 | class Node: 7 | """ This is a class of Node """ 8 | 9 | def __init__(self, val, left=None, right=None): 10 | self.val = val 11 | self.left = left 12 | self.right = right 13 | 14 | 15 | def preorder(root): 16 | """ Function to Preorder """ 17 | res = [] 18 | if not root: 19 | return res 20 | stack = [] 21 | stack.append(root) 22 | while stack: 23 | root = stack.pop() 24 | res.append(root.val) 25 | if root.right: 26 | stack.append(root.right) 27 | if root.left: 28 | stack.append(root.left) 29 | return res 30 | 31 | def preorder_rec(root, res=None): 32 | """ Recursive Implementation """ 33 | if root is None: 34 | return [] 35 | if res is None: 36 | res = [] 37 | res.append(root.val) 38 | preorder_rec(root.left, res) 39 | preorder_rec(root.right, res) 40 | return res 41 | -------------------------------------------------------------------------------- /algorithms/arrays/flatten.py: -------------------------------------------------------------------------------- 1 | """ 2 | Implement Flatten Arrays. 3 | Given an array that may contain nested arrays, 4 | produce a single resultant array. 5 | """ 6 | from collections.abc import Iterable 7 | 8 | 9 | # return list 10 | def flatten(input_arr, output_arr=None): 11 | if output_arr is None: 12 | output_arr = [] 13 | for ele in input_arr: 14 | if not isinstance(ele, str) and isinstance(ele, Iterable): 15 | flatten(ele, output_arr) #tail-recursion 16 | else: 17 | output_arr.append(ele) #produce the result 18 | return output_arr 19 | 20 | 21 | # returns iterator 22 | def flatten_iter(iterable): 23 | """ 24 | Takes as input multi dimensional iterable and 25 | returns generator which produces one dimensional output. 26 | """ 27 | for element in iterable: 28 | if not isinstance(element, str) and isinstance(element, Iterable): 29 | yield from flatten_iter(element) 30 | else: 31 | yield element 32 | -------------------------------------------------------------------------------- /algorithms/stack/is_sorted.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a stack, a function is_sorted accepts a stack as a parameter and returns 3 | true if the elements in the stack occur in ascending increasing order from 4 | bottom, and false otherwise. That is, the smallest element should be at bottom 5 | 6 | For example: 7 | bottom [6, 3, 5, 1, 2, 4] top 8 | The function should return false 9 | bottom [1, 2, 3, 4, 5, 6] top 10 | The function should return true 11 | """ 12 | 13 | 14 | def is_sorted(stack): 15 | storage_stack = [] 16 | for i in range(len(stack)): 17 | if len(stack) == 0: 18 | break 19 | first_val = stack.pop() 20 | if len(stack) == 0: 21 | break 22 | second_val = stack.pop() 23 | if first_val < second_val: 24 | return False 25 | storage_stack.append(first_val) 26 | stack.append(second_val) 27 | 28 | # Backup stack 29 | for i in range(len(storage_stack)): 30 | stack.append(storage_stack.pop()) 31 | 32 | return True 33 | -------------------------------------------------------------------------------- /docs/source/_static/logo/512pxblack.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /algorithms/tree/traversal/postorder.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Time complexity : O(n) 3 | ''' 4 | 5 | class Node: 6 | 7 | def __init__(self, val, left=None, right=None): 8 | self.val = val 9 | self.left = left 10 | self.right = right 11 | 12 | 13 | def postorder(root): 14 | res_temp = [] 15 | res = [] 16 | if not root: 17 | return res 18 | stack = [] 19 | stack.append(root) 20 | while stack: 21 | root = stack.pop() 22 | res_temp.append(root.val) 23 | if root.left: 24 | stack.append(root.left) 25 | if root.right: 26 | stack.append(root.right) 27 | while res_temp: 28 | res.append(res_temp.pop()) 29 | return res 30 | 31 | # Recursive Implementation 32 | def postorder_rec(root, res=None): 33 | if root is None: 34 | return [] 35 | if res is None: 36 | res = [] 37 | postorder_rec(root.left, res) 38 | postorder_rec(root.right, res) 39 | res.append(root.val) 40 | return res 41 | 42 | -------------------------------------------------------------------------------- /algorithms/tree/traversal/zigzag.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a binary tree, return the zigzag level order traversal 3 | of its nodes' values. 4 | (ie, from left to right, then right to left 5 | for the next level and alternate between). 6 | 7 | For example: 8 | Given binary tree [3,9,20,null,null,15,7], 9 | 3 10 | / \ 11 | 9 20 12 | / \ 13 | 15 7 14 | return its zigzag level order traversal as: 15 | [ 16 | [3], 17 | [20,9], 18 | [15,7] 19 | ] 20 | """ 21 | 22 | 23 | def zigzag_level(root): 24 | res = [] 25 | if not root: 26 | return res 27 | level = [root] 28 | flag = 1 29 | while level: 30 | current = [] 31 | new_level = [] 32 | for node in level: 33 | current.append(node.val) 34 | if node.left: 35 | new_level.append(node.left) 36 | if node.right: 37 | new_level.append(node.right) 38 | level = new_level 39 | res.append(current[::flag]) 40 | flag *= -1 41 | return res 42 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import io 2 | from setuptools import find_packages, setup 3 | 4 | 5 | def long_description(): 6 | with io.open('README.md', 'r', encoding='utf-8') as f: 7 | readme = f.read() 8 | return readme 9 | 10 | 11 | setup(name='algorithms', 12 | version='0.1.4', 13 | description='Pythonic Data Structures and Algorithms', 14 | long_description=long_description(), 15 | long_description_content_type="text/markdown", 16 | url='https://github.com/keon/algorithms', 17 | author='Algorithms Team & Contributors', 18 | author_email="kwk236@gmail.com", 19 | license='MIT', 20 | packages=find_packages(exclude=('tests', 'tests.*')), 21 | classifiers=[ 22 | 'Programming Language :: Python :: 3', 23 | 'Programming Language :: Python :: 3.4', 24 | 'Programming Language :: Python :: 3.5', 25 | 'Programming Language :: Python :: 3.6', 26 | 'Programming Language :: Python :: 3.7', 27 | ], 28 | zip_safe=False) 29 | -------------------------------------------------------------------------------- /algorithms/bit/count_ones.py: -------------------------------------------------------------------------------- 1 | """ 2 | Write a function that takes an unsigned integer and 3 | returns the number of '1' bits it has 4 | (also known as the Hamming weight). 5 | 6 | For example, the 32-bit integer '11' has binary 7 | representation 00000000000000000000000000001011, 8 | so the function should return 3. 9 | 10 | T(n)- O(k) : k is the number of 1s present in binary representation. 11 | NOTE: this complexity is better than O(log n). 12 | e.g. for n = 00010100000000000000000000000000 13 | only 2 iterations are required. 14 | 15 | Number of loops is 16 | equal to the number of 1s in the binary representation.""" 17 | def count_ones_recur(n): 18 | """Using Brian Kernighan's Algorithm. (Recursive Approach)""" 19 | 20 | if not n: 21 | return 0 22 | return 1 + count_ones_recur(n & (n-1)) 23 | 24 | 25 | def count_ones_iter(n): 26 | """Using Brian Kernighan's Algorithm. (Iterative Approach)""" 27 | 28 | count = 0 29 | while n: 30 | n &= (n-1) 31 | count += 1 32 | return count 33 | -------------------------------------------------------------------------------- /algorithms/linkedlist/rotate_list.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a list, rotate the list to the right by k places, 3 | where k is non-negative. 4 | 5 | For example: 6 | Given 1->2->3->4->5->NULL and k = 2, 7 | return 4->5->1->2->3->NULL. 8 | """ 9 | 10 | # Definition for singly-linked list. 11 | # class ListNode(object): 12 | # def __init__(self, x): 13 | # self.val = x 14 | # self.next = None 15 | 16 | 17 | def rotate_right(head, k): 18 | """ 19 | :type head: ListNode 20 | :type k: int 21 | :rtype: ListNode 22 | """ 23 | if not head or not head.next: 24 | return head 25 | current = head 26 | length = 1 27 | # count length of the list 28 | while current.next: 29 | current = current.next 30 | length += 1 31 | # make it circular 32 | current.next = head 33 | k = k % length 34 | # rotate until length-k 35 | for i in range(length-k): 36 | current = current.next 37 | head = current.next 38 | current.next = None 39 | return head 40 | -------------------------------------------------------------------------------- /algorithms/dfs/count_islands.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a 2d grid map of '1's (land) and '0's (water), 3 | count the number of islands. 4 | An island is surrounded by water and is formed by 5 | connecting adjacent lands horizontally or vertically. 6 | You may assume all four edges of the grid are all surrounded by water. 7 | 8 | Example 1: 9 | 10 | 11110 11 | 11010 12 | 11000 13 | 00000 14 | Answer: 1 15 | 16 | Example 2: 17 | 18 | 11000 19 | 11000 20 | 00100 21 | 00011 22 | Answer: 3 23 | """ 24 | 25 | def num_islands(grid): 26 | count = 0 27 | for i in range(len(grid)): 28 | for j, col in enumerate(grid[i]): 29 | if col == 1: 30 | dfs(grid, i, j) 31 | count += 1 32 | return count 33 | 34 | 35 | def dfs(grid, i, j): 36 | if (i < 0 or i >= len(grid)) or (j < 0 or j >= len(grid[0])): 37 | return 38 | if grid[i][j] != 1: 39 | return 40 | grid[i][j] = 0 41 | dfs(grid, i+1, j) 42 | dfs(grid, i-1, j) 43 | dfs(grid, i, j+1) 44 | dfs(grid, i, j-1) 45 | -------------------------------------------------------------------------------- /docs/source/_static/logo/128pxblue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /docs/source/_static/logo/128pxorange.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /algorithms/backtrack/combination_sum.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a set of candidate numbers (C) (without duplicates) and a target number 3 | (T), find all unique combinations in C where the candidate numbers sums to T. 4 | 5 | The same repeated number may be chosen from C unlimited number of times. 6 | 7 | Note: 8 | All numbers (including target) will be positive integers. 9 | The solution set must not contain duplicate combinations. 10 | For example, given candidate set [2, 3, 6, 7] and target 7, 11 | A solution set is: 12 | [ 13 | [7], 14 | [2, 2, 3] 15 | ] 16 | """ 17 | 18 | 19 | def combination_sum(candidates, target): 20 | 21 | def dfs(nums, target, index, path, res): 22 | if target < 0: 23 | return # backtracking 24 | if target == 0: 25 | res.append(path) 26 | return 27 | for i in range(index, len(nums)): 28 | dfs(nums, target-nums[i], i, path+[nums[i]], res) 29 | 30 | res = [] 31 | candidates.sort() 32 | dfs(candidates, target, 0, [], res) 33 | return res 34 | -------------------------------------------------------------------------------- /algorithms/linkedlist/merge_two_list.py: -------------------------------------------------------------------------------- 1 | """ 2 | Merge two sorted linked lists and return it as a new list. The new list should 3 | be made by splicing together the nodes of the first two lists. 4 | 5 | For example: 6 | Input: 1->2->4, 1->3->4 7 | Output: 1->1->2->3->4->4 8 | """ 9 | class Node: 10 | 11 | def __init__(self, x): 12 | self.val = x 13 | self.next = None 14 | 15 | def merge_two_list(l1, l2): 16 | ret = cur = Node(0) 17 | while l1 and l2: 18 | if l1.val < l2.val: 19 | cur.next = l1 20 | l1 = l1.next 21 | else: 22 | cur.next = l2 23 | l2 = l2.next 24 | cur = cur.next 25 | cur.next = l1 or l2 26 | return ret.next 27 | 28 | # recursively 29 | def merge_two_list_recur(l1, l2): 30 | if not l1 or not l2: 31 | return l1 or l2 32 | if l1.val < l2.val: 33 | l1.next = merge_two_list_recur(l1.next, l2) 34 | return l1 35 | else: 36 | l2.next = merge_two_list_recur(l1, l2.next) 37 | return l2 38 | -------------------------------------------------------------------------------- /algorithms/linkedlist/remove_range.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a linked list, remove_range function accepts a starting and ending index 3 | as parameters and removes the elements at those indexes (inclusive) from the list 4 | 5 | For example: 6 | List: [8, 13, 17, 4, 9, 12, 98, 41, 7, 23, 0, 92] 7 | remove_range(list, 3, 8); 8 | List becomes: [8, 13, 17, 23, 0, 92] 9 | 10 | legal range of the list (0 < start index < end index < size of list). 11 | """ 12 | def remove_range(head, start, end): 13 | assert(start <= end) 14 | # Case: remove node at head 15 | if start == 0: 16 | for i in range(0, end+1): 17 | if head != None: 18 | head = head.next 19 | else: 20 | current = head 21 | # Move pointer to start position 22 | for i in range(0,start-1): 23 | current = current.next 24 | # Remove data until the end 25 | for i in range(0, end-start + 1): 26 | if current != None and current.next != None: 27 | current.next = current.next.next 28 | return head 29 | -------------------------------------------------------------------------------- /algorithms/tree/bst/is_bst.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a binary tree, determine if it is a valid binary search tree (BST). 3 | 4 | Assume a BST is defined as follows: 5 | 6 | The left subtree of a node contains only nodes 7 | with keys less than the node's key. 8 | The right subtree of a node contains only nodes 9 | with keys greater than the node's key. 10 | Both the left and right subtrees must also be binary search trees. 11 | Example 1: 12 | 2 13 | / \ 14 | 1 3 15 | Binary tree [2,1,3], return true. 16 | Example 2: 17 | 1 18 | / \ 19 | 2 3 20 | Binary tree [1,2,3], return false. 21 | """ 22 | 23 | def is_bst(root): 24 | """ 25 | :type root: TreeNode 26 | :rtype: bool 27 | """ 28 | 29 | stack = [] 30 | pre = None 31 | 32 | while root or stack: 33 | while root: 34 | stack.append(root) 35 | root = root.left 36 | root = stack.pop() 37 | if pre and root.val <= pre.val: 38 | return False 39 | pre = root 40 | root = root.right 41 | 42 | return True 43 | -------------------------------------------------------------------------------- /algorithms/strings/multiply_strings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given two non-negative integers num1 and num2 represented as strings, 3 | return the product of num1 and num2. 4 | 5 | Note: 6 | 7 | The length of both num1 and num2 is < 110. 8 | Both num1 and num2 contains only digits 0-9. 9 | Both num1 and num2 does not contain any leading zero. 10 | You must not use any built-in BigInteger library or convert 11 | the inputs to integer directly. 12 | """ 13 | 14 | 15 | def multiply(num1: "str", num2: "str") -> "str": 16 | interm = [] 17 | zero = ord('0') 18 | i_pos = 1 19 | for i in reversed(num1): 20 | j_pos = 1 21 | add = 0 22 | for j in reversed(num2): 23 | mult = (ord(i)-zero) * (ord(j)-zero) * j_pos * i_pos 24 | j_pos *= 10 25 | add += mult 26 | i_pos *= 10 27 | interm.append(add) 28 | return str(sum(interm)) 29 | 30 | 31 | if __name__ == "__main__": 32 | print(multiply("1", "23")) 33 | print(multiply("23", "23")) 34 | print(multiply("100", "23")) 35 | print(multiply("100", "10000")) 36 | -------------------------------------------------------------------------------- /docs/source/_static/logo/256pxblue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /docs/source/_static/logo/256pxorange.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /algorithms/strings/domain_extractor.py: -------------------------------------------------------------------------------- 1 | """ 2 | Write a function that when given a URL as a string, parses out just the domain name and returns it as a string. 3 | 4 | Examples: 5 | domain_name("http://github.com/SaadBenn") == "github" 6 | domain_name("http://www.zombie-bites.com") == "zombie-bites" 7 | domain_name("https://www.cnet.com") == "cnet" 8 | 9 | Note: The idea is not to use any built-in libraries such as re (regular expression) or urlparse except .split() built-in function 10 | """ 11 | 12 | # Non pythonic way 13 | def domain_name_1(url): 14 | #grab only the non http(s) part 15 | full_domain_name = url.split('//')[-1] 16 | #grab the actual one depending on the len of the list 17 | actual_domain = full_domain_name.split('.') 18 | 19 | # case when www is in the url 20 | if (len(actual_domain) > 2): 21 | return actual_domain[1] 22 | # case when www is not in the url 23 | return actual_domain[0] 24 | 25 | 26 | # pythonic one liner 27 | def domain_name_2(url): 28 | return url.split("//")[-1].split("www.")[-1].split(".")[0] 29 | 30 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. image:: /_static/algorithms_logo.png 2 | :target: https://github.com/keon/algorithms 3 | :scale: 50 % 4 | 5 | The :mod:`algorithms` package consists of 6 | minimal and clean example implementations of data structures and algorithms. 7 | 8 | .. toctree:: 9 | :maxdepth: 2 10 | :caption: Package Reference 11 | 12 | self 13 | algorithms.arrays 14 | algorithms.backtrack 15 | algorithms.bfs 16 | algorithms.bit 17 | algorithms.dfs 18 | algorithms.dp 19 | algorithms.graph 20 | algorithms.heap 21 | algorithms.linkedlist 22 | algorithms.map 23 | algorithms.maths 24 | algorithms.matrix 25 | algorithms.queues 26 | algorithms.search 27 | algorithms.set 28 | algorithms.sort 29 | algorithms.stack 30 | algorithms.strings 31 | algorithms.tree 32 | examples 33 | 34 | Indices and tables 35 | ================== 36 | 37 | * :ref:`genindex` 38 | * :ref:`modindex` 39 | -------------------------------------------------------------------------------- /algorithms/maths/decimal_to_binary_ip.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given an ip address in dotted-decimal representation, determine the 3 | binary representation. For example, 4 | decimal_to_binary(255.0.0.5) returns 11111111.00000000.00000000.00000101 5 | accepts string 6 | returns string 7 | """ 8 | 9 | def decimal_to_binary_util(val): 10 | """ 11 | Convert 8-bit decimal number to binary representation 12 | :type val: str 13 | :rtype: str 14 | """ 15 | bits = [128, 64, 32, 16, 8, 4, 2, 1] 16 | val = int(val) 17 | binary_rep = '' 18 | for bit in bits: 19 | if val >= bit: 20 | binary_rep += str(1) 21 | val -= bit 22 | else: 23 | binary_rep += str(0) 24 | 25 | return binary_rep 26 | 27 | def decimal_to_binary_ip(ip): 28 | """ 29 | Convert dotted-decimal ip address to binary representation with help of decimal_to_binary_util 30 | """ 31 | values = ip.split('.') 32 | binary_list = [] 33 | for val in values: 34 | binary_list.append(decimal_to_binary_util(val)) 35 | return '.'.join(binary_list) 36 | -------------------------------------------------------------------------------- /docs/source/_static/logo/512pxblue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /algorithms/search/find_min_rotate.py: -------------------------------------------------------------------------------- 1 | """ 2 | Suppose an array sorted in ascending order is rotated at some pivot unknown 3 | to you beforehand. (i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2). 4 | 5 | Find the minimum element. The complexity must be O(logN) 6 | 7 | You may assume no duplicate exists in the array. 8 | """ 9 | def find_min_rotate(array): 10 | """ 11 | Finds the minimum element in a sorted array that has been rotated. 12 | """ 13 | low = 0 14 | high = len(array) - 1 15 | while low < high: 16 | mid = (low + high) // 2 17 | if array[mid] > array[high]: 18 | low = mid + 1 19 | else: 20 | high = mid 21 | 22 | return array[low] 23 | 24 | def find_min_rotate_recur(array, low, high): 25 | """ 26 | Finds the minimum element in a sorted array that has been rotated. 27 | """ 28 | mid = (low + high) // 2 29 | if mid == low: 30 | return array[low] 31 | if array[mid] > array[high]: 32 | return find_min_rotate_recur(array, mid + 1, high) 33 | return find_min_rotate_recur(array, low, mid) 34 | -------------------------------------------------------------------------------- /docs/source/_static/logo/512pxorange.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /algorithms/sort/stooge_sort.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 3 | Stooge Sort 4 | Time Complexity : O(n2.709) 5 | Reference: https://www.geeksforgeeks.org/stooge-sort/ 6 | 7 | ''' 8 | 9 | 10 | 11 | def stoogesort(arr, l, h): 12 | if l >= h: 13 | return 14 | 15 | # If first element is smaller 16 | # than last, swap them 17 | if arr[l]>arr[h]: 18 | t = arr[l] 19 | arr[l] = arr[h] 20 | arr[h] = t 21 | 22 | # If there are more than 2 elements in 23 | # the array 24 | if h-l + 1 > 2: 25 | t = (int)((h-l + 1)/3) 26 | 27 | # Recursively sort first 2 / 3 elements 28 | stoogesort(arr, l, (h-t)) 29 | 30 | # Recursively sort last 2 / 3 elements 31 | stoogesort(arr, l + t, (h)) 32 | 33 | # Recursively sort first 2 / 3 elements 34 | # again to confirm 35 | stoogesort(arr, l, (h-t)) 36 | 37 | 38 | if __name__ == "__main__": 39 | array = [1,3,64,5,7,8] 40 | n = len(array) 41 | stoogesort(array, 0, n-1) 42 | for i in range(0, n): 43 | print(array[i], end = ' ') 44 | -------------------------------------------------------------------------------- /algorithms/arrays/top_1.py: -------------------------------------------------------------------------------- 1 | """ 2 | This algorithm receives an array and returns most_frequent_value 3 | Also, sometimes it is possible to have multiple 'most_frequent_value's, 4 | so this function returns a list. This result can be used to find a 5 | representative value in an array. 6 | 7 | This algorithm gets an array, makes a dictionary of it, 8 | finds the most frequent count, and makes the result list. 9 | 10 | For example: top_1([1, 1, 2, 2, 3, 4]) will return [1, 2] 11 | 12 | (TL:DR) Get mathematical Mode 13 | Complexity: O(n) 14 | """ 15 | def top_1(arr): 16 | values = {} 17 | #reserve each value which first appears on keys 18 | #reserve how many time each value appears by index number on values 19 | result = [] 20 | f_val = 0 21 | 22 | for i in arr: 23 | if i in values: 24 | values[i] += 1 25 | else: 26 | values[i] = 1 27 | 28 | f_val = max(values.values()) 29 | 30 | for i in values.keys(): 31 | if values[i] == f_val: 32 | result.append(i) 33 | else: 34 | continue 35 | 36 | return result 37 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | group: travis_latest 2 | dist: xenial # Travis CI's default distro 3 | language: python 4 | cache: 5 | directories: 6 | - $HOME/.cache/pip 7 | matrix: 8 | include: 9 | - python: 3.6 10 | env: TOX_ENV=py36 11 | - python: 3.7 12 | env: TOX_ENV=py37 13 | install: 14 | - pip install -r test_requirements.txt 15 | before_script: 16 | # run black to check if some files would need reformatting 17 | - black --check . || true 18 | # stop the build if there are Python syntax errors or undefined names 19 | - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 20 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 21 | - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 22 | script: 23 | # Check python install package 24 | - pip install -e . 25 | - tox -e $TOX_ENV 26 | # Check python uninstall package 27 | - pip uninstall -y algorithms 28 | notifications: 29 | on_success: change 30 | on_failure: change # `always` will be the setting once code changes slow down 31 | -------------------------------------------------------------------------------- /algorithms/strings/longest_palindromic_substring.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Given string s, find the longest palindromic substring. 3 | 4 | Example1: 5 | 6 | * input: "dasdasdasdasdasdadsa" 7 | * output: "asdadsa" 8 | 9 | Example2: 10 | 11 | * input: "acdbbdaa" 12 | * output: "dbbd" 13 | 14 | Manacher's algorithm 15 | 16 | ''' 17 | 18 | def longest_palindrome(s): 19 | if len(s) < 2: 20 | return s 21 | 22 | n_str = '#' + '#'.join(s) + '#' 23 | p = [0] * len(n_str) 24 | mx, loc = 0, 0 25 | index, maxlen = 0, 0 26 | for i in range(len(n_str)): 27 | if i < mx and 2 * loc - i < len(n_str): 28 | p[i] = min(mx - i, p[2 * loc - i]) 29 | else: 30 | p[i] = 1 31 | 32 | while p[i] + i < len(n_str) and i - p[i] >= 0 and n_str[ 33 | i - p[i]] == n_str[i + p[i]]: 34 | p[i] += 1 35 | 36 | if i + p[i] > mx: 37 | mx = i + p[i] 38 | loc = i 39 | 40 | if p[i] > maxlen: 41 | index = i 42 | maxlen = p[i] 43 | s = n_str[index - p[index] + 1:index + p[index]] 44 | return s.replace('#', '') 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Keon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /algorithms/tree/bst/unique_bst.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given n, how many structurally unique BST's 3 | (binary search trees) that store values 1...n? 4 | 5 | For example, 6 | Given n = 3, there are a total of 5 unique BST's. 7 | 8 | 1 3 3 2 1 9 | \ / / / \ \ 10 | 3 2 1 1 3 2 11 | / / \ \ 12 | 2 1 2 3 13 | """ 14 | 15 | 16 | """ 17 | Taking 1~n as root respectively: 18 | 1 as root: # of trees = F(0) * F(n-1) // F(0) == 1 19 | 2 as root: # of trees = F(1) * F(n-2) 20 | 3 as root: # of trees = F(2) * F(n-3) 21 | ... 22 | n-1 as root: # of trees = F(n-2) * F(1) 23 | n as root: # of trees = F(n-1) * F(0) 24 | 25 | So, the formulation is: 26 | F(n) = F(0) * F(n-1) + F(1) * F(n-2) + F(2) * F(n-3) + ... + F(n-2) * F(1) + F(n-1) * F(0) 27 | """ 28 | 29 | def num_trees(n): 30 | """ 31 | :type n: int 32 | :rtype: int 33 | """ 34 | dp = [0] * (n+1) 35 | dp[0] = 1 36 | dp[1] = 1 37 | for i in range(2, n+1): 38 | for j in range(i+1): 39 | dp[i] += dp[i-j] * dp[j-1] 40 | return dp[-1] 41 | -------------------------------------------------------------------------------- /algorithms/graph/markov_chain.py: -------------------------------------------------------------------------------- 1 | """ 2 | Implements a markov chain. Chains are described using a dictionary: 3 | 4 | my_chain = { 5 | 'A': {'A': 0.6, 6 | 'E': 0.4}, 7 | 'E': {'A': 0.7, 8 | 'E': 0.3} 9 | } 10 | """ 11 | 12 | import random 13 | 14 | def __choose_state(state_map): 15 | """ 16 | Choose the next state randomly 17 | """ 18 | choice = random.random() 19 | probability_reached = 0 20 | for state, probability in state_map.items(): 21 | probability_reached += probability 22 | if probability_reached > choice: 23 | return state 24 | return None 25 | 26 | def next_state(chain, current_state): 27 | """ 28 | Given a markov-chain, randomly chooses the next state given the current state. 29 | """ 30 | next_state_map = chain.get(current_state) 31 | return __choose_state(next_state_map) 32 | 33 | def iterating_markov_chain(chain, state): 34 | """ 35 | Yield a sequence of states given a markov chain and the initial state 36 | """ 37 | while True: 38 | state = next_state(chain, state) 39 | yield state 40 | -------------------------------------------------------------------------------- /algorithms/dp/word_break.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a non-empty string s and a dictionary wordDict 3 | containing a list of non-empty words, 4 | determine if word can be segmented into a space-separated 5 | sequence of one or more dictionary words. 6 | You may assume the dictionary does not contain duplicate words. 7 | 8 | For example, given 9 | word = "leetcode", 10 | dict = ["leet", "code"]. 11 | 12 | Return true because "leetcode" can be segmented as "leet code". 13 | 14 | word = abc word_dict = ["a","bc"] 15 | True False False False 16 | 17 | """ 18 | 19 | 20 | # TC: O(N^2) SC: O(N) 21 | def word_break(word, word_dict): 22 | """ 23 | :type word: str 24 | :type word_dict: Set[str] 25 | :rtype: bool 26 | """ 27 | dp_array = [False] * (len(word)+1) 28 | dp_array[0] = True 29 | for i in range(1, len(word)+1): 30 | for j in range(0, i): 31 | if dp_array[j] and word[j:i] in word_dict: 32 | dp_array[i] = True 33 | break 34 | return dp_array[-1] 35 | 36 | 37 | if __name__ == "__main__": 38 | STR = "keonkim" 39 | dic = ["keon", "kim"] 40 | 41 | print(word_break(str, dic)) 42 | -------------------------------------------------------------------------------- /algorithms/backtrack/generate_parenthesis.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given n pairs of parentheses, write a function to generate 3 | all combinations of well-formed parentheses. 4 | 5 | For example, given n = 3, a solution set is: 6 | 7 | [ 8 | "((()))", 9 | "(()())", 10 | "(())()", 11 | "()(())", 12 | "()()()" 13 | ] 14 | """ 15 | 16 | 17 | def generate_parenthesis_v1(n): 18 | def add_pair(res, s, left, right): 19 | if left == 0 and right == 0: 20 | res.append(s) 21 | return 22 | if right > 0: 23 | add_pair(res, s + ")", left, right - 1) 24 | if left > 0: 25 | add_pair(res, s + "(", left - 1, right + 1) 26 | 27 | res = [] 28 | add_pair(res, "", n, 0) 29 | return res 30 | 31 | 32 | def generate_parenthesis_v2(n): 33 | def add_pair(res, s, left, right): 34 | if left == 0 and right == 0: 35 | res.append(s) 36 | if left > 0: 37 | add_pair(res, s + "(", left - 1, right) 38 | if right > 0 and left < right: 39 | add_pair(res, s + ")", left, right - 1) 40 | 41 | res = [] 42 | add_pair(res, "", n, n) 43 | return res 44 | -------------------------------------------------------------------------------- /algorithms/matrix/multiply.py: -------------------------------------------------------------------------------- 1 | """ 2 | This algorithm takes two compatible two dimensional matrix 3 | and return their product 4 | Space complexity: O(n^2) 5 | Possible edge case: the number of columns of multiplicand not consistent with 6 | the number of rows of multiplier, will raise exception 7 | """ 8 | 9 | 10 | def multiply(multiplicand: list, multiplier: list) -> list: 11 | """ 12 | :type A: List[List[int]] 13 | :type B: List[List[int]] 14 | :rtype: List[List[int]] 15 | """ 16 | multiplicand_row, multiplicand_col = len( 17 | multiplicand), len(multiplicand[0]) 18 | multiplier_row, multiplier_col = len(multiplier), len(multiplier[0]) 19 | if(multiplicand_col != multiplier_row): 20 | raise Exception( 21 | "Multiplicand matrix not compatible with Multiplier matrix.") 22 | # create a result matrix 23 | result = [[0] * multiplier_col for i in range(multiplicand_row)] 24 | for i in range(multiplicand_row): 25 | for j in range(multiplier_col): 26 | for k in range(len(multiplier)): 27 | result[i][j] += multiplicand[i][k] * multiplier[k][j] 28 | return result 29 | -------------------------------------------------------------------------------- /algorithms/ml/nearest_neighbor.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | def distance(x,y): 4 | """[summary] 5 | HELPER-FUNCTION 6 | calculates the (eulidean) distance between vector x and y. 7 | 8 | Arguments: 9 | x {[tuple]} -- [vector] 10 | y {[tuple]} -- [vector] 11 | """ 12 | assert len(x) == len(y), "The vector must have same length" 13 | result = () 14 | sum = 0 15 | for i in range(len(x)): 16 | result += (x[i] -y[i],) 17 | for component in result: 18 | sum += component**2 19 | return math.sqrt(sum) 20 | 21 | 22 | def nearest_neighbor(x, tSet): 23 | """[summary] 24 | Implements the nearest neighbor algorithm 25 | 26 | Arguments: 27 | x {[tupel]} -- [vector] 28 | tSet {[dict]} -- [training set] 29 | 30 | Returns: 31 | [type] -- [result of the AND-function] 32 | """ 33 | assert isinstance(x, tuple) and isinstance(tSet, dict) 34 | current_key = () 35 | min_d = float('inf') 36 | for key in tSet: 37 | d = distance(x, key) 38 | if d < min_d: 39 | min_d = d 40 | current_key = key 41 | return tSet[current_key] 42 | -------------------------------------------------------------------------------- /algorithms/stack/ordered_stack.py: -------------------------------------------------------------------------------- 1 | # The stack remains always ordered such that the highest value 2 | # is at the top and the lowest at the bottom 3 | 4 | 5 | class OrderedStack: 6 | def __init__(self): 7 | self.items = [] 8 | 9 | def is_empty(self): 10 | return self.items == [] 11 | 12 | def push_t(self, item): 13 | self.items.append(item) 14 | 15 | # push method to maintain order when pushing new elements 16 | def push(self, item): 17 | temp_stack = OrderedStack() 18 | if self.is_empty() or item > self.peek(): 19 | self.push_t(item) 20 | else: 21 | while item < self.peek() and not self.is_empty(): 22 | temp_stack.push_t(self.pop()) 23 | self.push_t(item) 24 | while not temp_stack.is_empty(): 25 | self.push_t(temp_stack.pop()) 26 | 27 | def pop(self): 28 | if self.is_empty(): 29 | raise IndexError("Stack is empty") 30 | return self.items.pop() 31 | 32 | def peek(self): 33 | return self.items[len(self.items) - 1] 34 | 35 | def size(self): 36 | return len(self.items) 37 | -------------------------------------------------------------------------------- /algorithms/tree/longest_consecutive.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a binary tree, find the length of the longest consecutive sequence path. 3 | 4 | The path refers to any sequence of nodes from some starting node to any node 5 | in the tree along the parent-child connections. 6 | The longest consecutive path need to be from parent to child 7 | (cannot be the reverse). 8 | 9 | For example, 10 | 1 11 | \ 12 | 3 13 | / \ 14 | 2 4 15 | \ 16 | 5 17 | Longest consecutive sequence path is 3-4-5, so return 3. 18 | 2 19 | \ 20 | 3 21 | / 22 | 2 23 | / 24 | 1 25 | """ 26 | 27 | 28 | def longest_consecutive(root): 29 | """ 30 | :type root: TreeNode 31 | :rtype: int 32 | """ 33 | if root is None: 34 | return 0 35 | max_len = 0 36 | dfs(root, 0, root.val, max_len) 37 | return max_len 38 | 39 | 40 | def dfs(root, cur, target, max_len): 41 | if root is None: 42 | return 43 | if root.val == target: 44 | cur += 1 45 | else: 46 | cur = 1 47 | max_len = max(cur, max_len) 48 | dfs(root.left, cur, root.val+1, max_len) 49 | dfs(root.right, cur, root.val+1, max_len) 50 | -------------------------------------------------------------------------------- /algorithms/tree/trie/trie.py: -------------------------------------------------------------------------------- 1 | """ 2 | Implement a trie with insert, search, and startsWith methods. 3 | 4 | Note: 5 | You may assume that all inputs are consist of lowercase letters a-z. 6 | """ 7 | import collections 8 | 9 | 10 | class TrieNode: 11 | def __init__(self): 12 | self.children = collections.defaultdict(TrieNode) 13 | self.is_word = False 14 | 15 | 16 | class Trie: 17 | def __init__(self): 18 | self.root = TrieNode() 19 | 20 | def insert(self, word): 21 | current = self.root 22 | for letter in word: 23 | current = current.children[letter] 24 | current.is_word = True 25 | 26 | def search(self, word): 27 | current = self.root 28 | for letter in word: 29 | current = current.children.get(letter) 30 | if current is None: 31 | return False 32 | return current.is_word 33 | 34 | def starts_with(self, prefix): 35 | current = self.root 36 | for letter in prefix: 37 | current = current.children.get(letter) 38 | if current is None: 39 | return False 40 | return True 41 | 42 | -------------------------------------------------------------------------------- /algorithms/maths/recursive_binomial_coefficient.py: -------------------------------------------------------------------------------- 1 | def recursive_binomial_coefficient(n,k): 2 | """Calculates the binomial coefficient, C(n,k), with n>=k using recursion 3 | Time complexity is O(k), so can calculate fairly quickly for large values of k. 4 | 5 | >>> recursive_binomial_coefficient(5,0) 6 | 1 7 | 8 | >>> recursive_binomial_coefficient(8,2) 9 | 28 10 | 11 | >>> recursive_binomial_coefficient(500,300) 12 | 5054949849935535817667719165973249533761635252733275327088189563256013971725761702359997954491403585396607971745777019273390505201262259748208640 13 | 14 | """ 15 | 16 | if k>n: 17 | raise ValueError('Invalid Inputs, ensure that n >= k') 18 | #function is only defined for n>=k 19 | if k == 0 or n == k: 20 | #C(n,0) = C(n,n) = 1, so this is our base case. 21 | return 1 22 | if k > n/2: 23 | #C(n,k) = C(n,n-k), so if n/2 is sufficiently small, we can reduce the problem size. 24 | return recursive_binomial_coefficient(n,n-k) 25 | #else, we know C(n,k) = (n/k)C(n-1,k-1), so we can use this to reduce our problem size. 26 | return int((n/k)*recursive_binomial_coefficient(n-1,k-1)) 27 | -------------------------------------------------------------------------------- /algorithms/queues/max_sliding_window.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given an array and a number k 3 | Find the max elements of each of its sub-arrays of length k. 4 | 5 | Keep indexes of good candidates in deque d. 6 | The indexes in d are from the current window, they're increasing, 7 | and their corresponding nums are decreasing. 8 | Then the first deque element is the index of the largest window value. 9 | 10 | For each index i: 11 | 12 | 1. Pop (from the end) indexes of smaller elements (they'll be useless). 13 | 2. Append the current index. 14 | 3. Pop (from the front) the index i - k, if it's still in the deque 15 | (it falls out of the window). 16 | 4. If our window has reached size k, 17 | append the current window maximum to the output. 18 | """ 19 | 20 | import collections 21 | 22 | 23 | def max_sliding_window(arr, k): 24 | qi = collections.deque() # queue storing indexes of elements 25 | result = [] 26 | for i, n in enumerate(arr): 27 | while qi and arr[qi[-1]] < n: 28 | qi.pop() 29 | qi.append(i) 30 | if qi[0] == i - k: 31 | qi.popleft() 32 | if i >= k - 1: 33 | result.append(arr[qi[0]]) 34 | return result 35 | -------------------------------------------------------------------------------- /algorithms/tree/deepest_left.py: -------------------------------------------------------------------------------- 1 | # Given a binary tree, find the deepest node 2 | # that is the left child of its parent node. 3 | 4 | # Example: 5 | 6 | # 1 7 | # / \ 8 | # 2 3 9 | # / \ \ 10 | # 4 5 6 11 | # \ 12 | # 7 13 | # should return 4. 14 | 15 | from tree.tree import TreeNode 16 | 17 | 18 | class DeepestLeft: 19 | def __init__(self): 20 | self.depth = 0 21 | self.Node = None 22 | 23 | 24 | def find_deepest_left(root, is_left, depth, res): 25 | if not root: 26 | return 27 | if is_left and depth > res.depth: 28 | res.depth = depth 29 | res.Node = root 30 | find_deepest_left(root.left, True, depth + 1, res) 31 | find_deepest_left(root.right, False, depth + 1, res) 32 | 33 | 34 | if __name__ == '__main__': 35 | root = TreeNode(1) 36 | root.left = TreeNode(2) 37 | root.right = TreeNode(3) 38 | root.left.left = TreeNode(4) 39 | root.left.right = TreeNode(5) 40 | root.right.right = TreeNode(6) 41 | root.right.right.right = TreeNode(7) 42 | 43 | res = DeepestLeft() 44 | find_deepest_left(root, True, 1, res) 45 | if res.Node: 46 | print(res.Node.val) 47 | -------------------------------------------------------------------------------- /algorithms/dp/int_divide.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given positive integer decompose, find an algorithm to find the number of 3 | non-negative number division, or decomposition. 4 | 5 | The complexity is O(n^2). 6 | 7 | Example 1: 8 | Input: 4 9 | Output: 5 10 | Explaination: 11 | 4=4 12 | 4=3+1 13 | 4=2+2 14 | 4=2+1+1 15 | 4=1+1+1+1 16 | 17 | Example : 18 | Input: 7 19 | Output: 15 20 | Explaination: 21 | 7=7 22 | 7=6+1 23 | 7=5+2 24 | 7=5+1+1 25 | 7=4+3 26 | 7=4+2+1 27 | 7=4+1+1+1 28 | 7=3+3+1 29 | 7=3+2+2 30 | 7=3+2+1+1 31 | 7=3+1+1+1+1 32 | 7=2+2+2+1 33 | 7=2+2+1+1+1 34 | 7=2+1+1+1+1+1 35 | 7=1+1+1+1+1+1+1 36 | 37 | """ 38 | 39 | 40 | def int_divide(decompose): 41 | """Find number of decompositions from `decompose` 42 | 43 | decompose -- integer 44 | """ 45 | arr = [[0 for i in range(decompose + 1)] for j in range(decompose + 1)] 46 | arr[1][1] = 1 47 | for i in range(1, decompose + 1): 48 | for j in range(1, decompose + 1): 49 | if i < j: 50 | arr[i][j] = arr[i][i] 51 | elif i == j: 52 | arr[i][j] = 1 + arr[i][j - 1] 53 | else: 54 | arr[i][j] = arr[i][j - 1] + arr[i - j][j] 55 | return arr[decompose][decompose] 56 | -------------------------------------------------------------------------------- /algorithms/map/is_isomorphic.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given two strings s and t, determine if they are isomorphic. 3 | Two strings are isomorphic if the characters in s can be replaced to get t. 4 | All occurrences of a character must be replaced with another character while 5 | preserving the order of characters. No two characters may map to the same 6 | character but a character may map to itself. 7 | 8 | Example 1: 9 | Input: s = "egg", t = "add" 10 | Output: true 11 | 12 | Example 2: 13 | Input: s = "foo", t = "bar" 14 | Output: false 15 | 16 | Example 3: 17 | Input: s = "paper", t = "title" 18 | Output: true 19 | Reference: https://leetcode.com/problems/isomorphic-strings/description/ 20 | """ 21 | def is_isomorphic(s, t): 22 | """ 23 | :type s: str 24 | :type t: str 25 | :rtype: bool 26 | """ 27 | if len(s) != len(t): 28 | return False 29 | dict = {} 30 | set_value = set() 31 | for i in range(len(s)): 32 | if s[i] not in dict: 33 | if t[i] in set_value: 34 | return False 35 | dict[s[i]] = t[i] 36 | set_value.add(t[i]) 37 | else: 38 | if dict[s[i]] != t[i]: 39 | return False 40 | return True 41 | -------------------------------------------------------------------------------- /algorithms/tree/bst/lowest_common_ancestor.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a binary search tree (BST), 3 | find the lowest common ancestor (LCA) of two given nodes in the BST. 4 | 5 | According to the definition of LCA on Wikipedia: 6 | “The lowest common ancestor is defined between two 7 | nodes v and w as the lowest node in T that has both v and w 8 | as descendants (where we allow a node to be a descendant of itself).” 9 | 10 | _______6______ 11 | / \ 12 | ___2__ ___8__ 13 | / \ / \ 14 | 0 _4 7 9 15 | / \ 16 | 3 5 17 | 18 | For example, the lowest common ancestor (LCA) of nodes 2 and 8 is 6. 19 | Another example is LCA of nodes 2 and 4 is 2, 20 | since a node can be a descendant of itself according to the LCA definition. 21 | """ 22 | 23 | 24 | def lowest_common_ancestor(root, p, q): 25 | """ 26 | :type root: Node 27 | :type p: Node 28 | :type q: Node 29 | :rtype: Node 30 | """ 31 | while root: 32 | if p.val > root.val < q.val: 33 | root = root.right 34 | elif p.val < root.val > q.val: 35 | root = root.left 36 | else: 37 | return root 38 | -------------------------------------------------------------------------------- /algorithms/sort/quick_sort.py: -------------------------------------------------------------------------------- 1 | def quick_sort(arr, simulation=False): 2 | """ Quick sort 3 | Complexity: best O(n log(n)) avg O(n log(n)), worst O(N^2) 4 | """ 5 | 6 | iteration = 0 7 | if simulation: 8 | print("iteration",iteration,":",*arr) 9 | arr, _ = quick_sort_recur(arr, 0, len(arr) - 1, iteration, simulation) 10 | return arr 11 | 12 | def quick_sort_recur(arr, first, last, iteration, simulation): 13 | if first < last: 14 | pos = partition(arr, first, last) 15 | # Start our two recursive calls 16 | if simulation: 17 | iteration = iteration + 1 18 | print("iteration",iteration,":",*arr) 19 | 20 | _, iteration = quick_sort_recur(arr, first, pos - 1, iteration, simulation) 21 | _, iteration = quick_sort_recur(arr, pos + 1, last, iteration, simulation) 22 | 23 | return arr, iteration 24 | 25 | def partition(arr, first, last): 26 | wall = first 27 | for pos in range(first, last): 28 | if arr[pos] < arr[last]: # last is the pivot 29 | arr[pos], arr[wall] = arr[wall], arr[pos] 30 | wall += 1 31 | arr[wall], arr[last] = arr[last], arr[wall] 32 | return wall 33 | -------------------------------------------------------------------------------- /algorithms/arrays/max_ones_index.py: -------------------------------------------------------------------------------- 1 | """ 2 | Find the index of 0 to be replaced with 1 to get 3 | longest continuous sequence 4 | of 1s in a binary array. 5 | Returns index of 0 to be 6 | replaced with 1 to get longest 7 | continuous sequence of 1s. 8 | If there is no 0 in array, then 9 | it returns -1. 10 | 11 | e.g. 12 | let input array = [1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1] 13 | If we replace 0 at index 3 with 1, we get the longest continuous 14 | sequence of 1s in the array. 15 | So the function return => 3 16 | """ 17 | 18 | 19 | def max_ones_index(arr): 20 | 21 | n = len(arr) 22 | max_count = 0 23 | max_index = 0 24 | prev_zero = -1 25 | prev_prev_zero = -1 26 | 27 | for curr in range(n): 28 | 29 | # If current element is 0, 30 | # then calculate the difference 31 | # between curr and prev_prev_zero 32 | if arr[curr] == 0: 33 | if curr - prev_prev_zero > max_count: 34 | max_count = curr - prev_prev_zero 35 | max_index = prev_zero 36 | 37 | prev_prev_zero = prev_zero 38 | prev_zero = curr 39 | 40 | if n - prev_prev_zero > max_count: 41 | max_index = prev_zero 42 | 43 | return max_index 44 | -------------------------------------------------------------------------------- /algorithms/dp/coin_change.py: -------------------------------------------------------------------------------- 1 | """ 2 | Problem 3 | Given a value `value`, if we want to make change for `value` cents, and we have infinite 4 | supply of each of coins = {S1, S2, .. , Sm} valued `coins`, how many ways can we make the change? 5 | The order of `coins` doesn't matter. 6 | For example, for `value` = 4 and `coins` = [1, 2, 3], there are four solutions: 7 | [1, 1, 1, 1], [1, 1, 2], [2, 2], [1, 3]. 8 | So output should be 4. 9 | 10 | For `value` = 10 and `coins` = [2, 5, 3, 6], there are five solutions: 11 | 12 | [2, 2, 2, 2, 2], [2, 2, 3, 3], [2, 2, 6], [2, 3, 5] and [5, 5]. 13 | So the output should be 5. 14 | 15 | Time complexity: O(n * m) where n is the `value` and m is the number of `coins` 16 | Space complexity: O(n) 17 | """ 18 | 19 | def count(coins, value): 20 | """ Find number of combination of `coins` that adds upp to `value` 21 | 22 | Keyword arguments: 23 | coins -- int[] 24 | value -- int 25 | """ 26 | # initialize dp array and set base case as 1 27 | dp_array = [1] + [0] * value 28 | 29 | # fill dp in a bottom up manner 30 | for coin in coins: 31 | for i in range(coin, value+1): 32 | dp_array[i] += dp_array[i-coin] 33 | 34 | return dp_array[value] 35 | -------------------------------------------------------------------------------- /algorithms/graph/prims_minimum_spanning.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This Prim's Algorithm Code is for finding weight of minimum spanning tree 3 | of a connected graph. 4 | For argument graph, it should be a dictionary type such as: 5 | 6 | graph = { 7 | 'a': [ [3, 'b'], [8,'c'] ], 8 | 'b': [ [3, 'a'], [5, 'd'] ], 9 | 'c': [ [8, 'a'], [2, 'd'], [4, 'e'] ], 10 | 'd': [ [5, 'b'], [2, 'c'], [6, 'e'] ], 11 | 'e': [ [4, 'c'], [6, 'd'] ] 12 | } 13 | 14 | where 'a','b','c','d','e' are nodes (these can be 1,2,3,4,5 as well) 15 | ''' 16 | 17 | 18 | import heapq # for priority queue 19 | 20 | def prims_minimum_spanning(graph_used): 21 | """ 22 | Prim's algorithm to find weight of minimum spanning tree 23 | """ 24 | vis=[] 25 | heap=[[0,1]] 26 | prim = set() 27 | mincost=0 28 | 29 | while len(heap) > 0: 30 | cost, node = heapq.heappop(heap) 31 | if node in vis: 32 | continue 33 | 34 | mincost += cost 35 | prim.add(node) 36 | vis.append(node) 37 | 38 | for distance, adjacent in graph_used[node]: 39 | if adjacent not in vis: 40 | heapq.heappush(heap, [distance, adjacent]) 41 | 42 | return mincost 43 | -------------------------------------------------------------------------------- /algorithms/bit/bit_operation.py: -------------------------------------------------------------------------------- 1 | """ 2 | Fundamental bit operation: 3 | get_bit(num, i): get an exact bit at specific index 4 | set_bit(num, i): set a bit at specific index 5 | clear_bit(num, i): clear a bit at specific index 6 | update_bit(num, i, bit): update a bit at specific index 7 | """ 8 | 9 | """ 10 | This function shifts 1 over by i bits, creating a value being like 0001000. By 11 | performing an AND with num, we clear all bits other than the bit at bit i. 12 | Finally we compare that to 0 13 | """ 14 | def get_bit(num, i): 15 | return (num & (1 << i)) != 0 16 | 17 | """ 18 | This function shifts 1 over by i bits, creating a value being like 0001000. By 19 | performing an OR with num, only value at bit i will change. 20 | """ 21 | def set_bit(num, i): 22 | return num | (1 << i) 23 | 24 | """ 25 | This method operates in almost the reverse of set_bit 26 | """ 27 | def clear_bit(num, i): 28 | mask = ~(1 << i) 29 | return num & mask 30 | 31 | """ 32 | To set the ith bit to value, we first clear the bit at position i by using a 33 | mask. Then, we shift the intended value. Finally we OR these two numbers 34 | """ 35 | def update_bit(num, i, bit): 36 | mask = ~(1 << i) 37 | return (num & mask) | (bit << i) 38 | -------------------------------------------------------------------------------- /algorithms/maths/cosine_similarity.py: -------------------------------------------------------------------------------- 1 | """ 2 | Calculate cosine similarity between given two 1d list. 3 | Two list must have the same length. 4 | 5 | Example: 6 | cosine_similarity([1, 1, 1], [1, 2, -1]) # output : 0.47140452079103173 7 | """ 8 | import math 9 | 10 | 11 | def _l2_distance(vec): 12 | """ 13 | Calculate l2 distance from two given vectors. 14 | """ 15 | norm = 0. 16 | for element in vec: 17 | norm += element * element 18 | norm = math.sqrt(norm) 19 | return norm 20 | 21 | 22 | def cosine_similarity(vec1, vec2): 23 | """ 24 | Calculate cosine similarity between given two vectors 25 | :type vec1: list 26 | :type vec2: list 27 | """ 28 | if len(vec1) != len(vec2): 29 | raise ValueError("The two vectors must be the same length. Got shape " + str(len(vec1)) 30 | + " and " + str(len(vec2))) 31 | 32 | norm_a = _l2_distance(vec1) 33 | norm_b = _l2_distance(vec2) 34 | 35 | similarity = 0. 36 | 37 | # Calculate the dot product of two vectors 38 | for vec1_element, vec2_element in zip(vec1, vec2): 39 | similarity += vec1_element * vec2_element 40 | 41 | similarity /= (norm_a * norm_b) 42 | 43 | return similarity 44 | -------------------------------------------------------------------------------- /algorithms/bit/has_alternative_bit.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a positive integer, check whether it has alternating bits: namely, 3 | if two adjacent bits will always have different values. 4 | 5 | For example: 6 | Input: 5 7 | Output: True because the binary representation of 5 is: 101. 8 | 9 | Input: 7 10 | Output: False because the binary representation of 7 is: 111. 11 | 12 | Input: 11 13 | Output: False because the binary representation of 11 is: 1011. 14 | 15 | Input: 10 16 | Output: True because The binary representation of 10 is: 1010. 17 | """ 18 | 19 | # Time Complexity - O(number of bits in n) 20 | def has_alternative_bit(n): 21 | first_bit = 0 22 | second_bit = 0 23 | while n: 24 | first_bit = n & 1 25 | if n >> 1: 26 | second_bit = (n >> 1) & 1 27 | if not first_bit ^ second_bit: 28 | return False 29 | else: 30 | return True 31 | n = n >> 1 32 | return True 33 | 34 | # Time Complexity - O(1) 35 | def has_alternative_bit_fast(n): 36 | mask1 = int('aaaaaaaa', 16) # for bits ending with zero (...1010) 37 | mask2 = int('55555555', 16) # for bits ending with one (...0101) 38 | return mask1 == (n + (n ^ mask1)) or mask2 == (n + (n ^ mask2)) 39 | -------------------------------------------------------------------------------- /algorithms/maths/summing_digits.py: -------------------------------------------------------------------------------- 1 | """ 2 | Recently, I encountered an interview question whose description was as below: 3 | 4 | The number 89 is the first integer with more than one digit whose digits when raised up to 5 | consecutive powers give the same number. For example, 89 = 8**1 + 9**2 gives the number 89. 6 | 7 | The next number after 89 with this property is 135 = 1**1 + 3**2 + 5**3 = 135. 8 | 9 | Write a function that returns a list of numbers with the above property. The function will 10 | receive range as parameter. 11 | """ 12 | 13 | def sum_dig_pow(low, high): 14 | result = [] 15 | 16 | for number in range(low, high + 1): 17 | exponent = 1 # set to 1 18 | summation = 0 # set to 1 19 | number_as_string = str(number) 20 | 21 | tokens = list(map(int, number_as_string)) # parse the string into individual digits 22 | 23 | for k in tokens: 24 | summation = summation + (k ** exponent) 25 | exponent += 1 26 | 27 | if summation == number: 28 | result.append(number) 29 | return result 30 | 31 | 32 | # Some test cases: 33 | assert sum_dig_pow(1, 10) == [1, 2, 3, 4, 5, 6, 7, 8, 9] 34 | assert sum_dig_pow(1, 100) == [1, 2, 3, 4, 5, 6, 7, 8, 9, 89] 35 | -------------------------------------------------------------------------------- /algorithms/tree/lowest_common_ancestor.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a binary tree, find the lowest common ancestor 3 | (LCA) of two given nodes in the tree. 4 | 5 | According to the definition of LCA on Wikipedia: 6 | “The lowest common ancestor is defined between two nodes 7 | v and w as the lowest node in T that has both v and w as 8 | descendants 9 | (where we allow a node to be a descendant of itself).” 10 | 11 | _______3______ 12 | / \ 13 | ___5__ ___1__ 14 | / \ / \ 15 | 6 _2 0 8 16 | / \ 17 | 7 4 18 | For example, the lowest common ancestor (LCA) of nodes 5 and 1 is 3. 19 | Another example is LCA of nodes 5 and 4 is 5, 20 | since a node can be a descendant of itself according to the LCA definition. 21 | """ 22 | 23 | 24 | def lca(root, p, q): 25 | """ 26 | :type root: TreeNode 27 | :type p: TreeNode 28 | :type q: TreeNode 29 | :rtype: TreeNode 30 | """ 31 | if root is None or root is p or root is q: 32 | return root 33 | left = lca(root.left, p, q) 34 | right = lca(root.right, p, q) 35 | if left is not None and right is not None: 36 | return root 37 | return left if left else right 38 | -------------------------------------------------------------------------------- /algorithms/arrays/plus_one.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a non-negative number represented as an array of digits, 3 | adding one to each numeral. 4 | 5 | The digits are stored big-endian, such that the most significant 6 | digit is at the head of the list. 7 | """ 8 | 9 | 10 | def plus_one_v1(digits): 11 | """ 12 | :type digits: List[int] 13 | :rtype: List[int] 14 | """ 15 | digits[-1] = digits[-1] + 1 16 | res = [] 17 | ten = 0 18 | i = len(digits)-1 19 | while i >= 0 or ten == 1: 20 | summ = 0 21 | if i >= 0: 22 | summ += digits[i] 23 | if ten: 24 | summ += 1 25 | res.append(summ % 10) 26 | ten = summ // 10 27 | i -= 1 28 | return res[::-1] 29 | 30 | 31 | def plus_one_v2(digits): 32 | n = len(digits) 33 | for i in range(n-1, -1, -1): 34 | if digits[i] < 9: 35 | digits[i] += 1 36 | return digits 37 | digits[i] = 0 38 | digits.insert(0, 1) 39 | return digits 40 | 41 | 42 | def plus_one_v3(num_arr): 43 | 44 | for idx in reversed(list(enumerate(num_arr))): 45 | num_arr[idx[0]] = (num_arr[idx[0]] + 1) % 10 46 | if num_arr[idx[0]]: 47 | return num_arr 48 | return [1] + num_arr 49 | -------------------------------------------------------------------------------- /algorithms/map/longest_palindromic_subsequence.py: -------------------------------------------------------------------------------- 1 | def longest_palindromic_subsequence(s): 2 | 3 | k = len(s) 4 | olist = [0] * k # 申请长度为n的列表,并初始化 5 | nList = [0] * k # 同上 6 | logestSubStr = "" 7 | logestLen = 0 8 | 9 | for j in range(0, k): 10 | for i in range(0, j + 1): 11 | if j - i <= 1: 12 | if s[i] == s[j]: 13 | nList[i] = 1 # 当 j 时,第 i 个子串为回文子串 14 | len_t = j - i + 1 15 | if logestLen < len_t: # 判断长度 16 | logestSubStr = s[i:j + 1] 17 | logestLen = len_t 18 | else: 19 | if s[i] == s[j] and olist[i+1]: # 当j-i>1时,判断s[i]是否等于s[j],并判断当j-1时,第i+1个子串是否为回文子串 20 | nList[i] = 1 # 当 j 时,第 i 个子串为回文子串 21 | len_t = j - i + 1 22 | if logestLen < len_t: 23 | logestSubStr = s[i:j + 1] 24 | logestLen = len_t 25 | olist = nList # 覆盖旧的列表 26 | nList = [0] * k # 新的列表清空 27 | # ~ from icecream import ic 28 | # ~ ic(s, logestSubStr) 29 | return logestLen#, logestSubStr 30 | -------------------------------------------------------------------------------- /algorithms/maths/magic_number.py: -------------------------------------------------------------------------------- 1 | """ 2 | Magic Number 3 | A number is said to be a magic number, 4 | if summing the digits of the number and then recursively repeating this process for the given sum 5 | untill the number becomes a single digit number equal to 1. 6 | 7 | Example: 8 | Number = 50113 => 5+0+1+1+3=10 => 1+0=1 [This is a Magic Number] 9 | Number = 1234 => 1+2+3+4=10 => 1+0=1 [This is a Magic Number] 10 | Number = 199 => 1+9+9=19 => 1+9=10 => 1+0=1 [This is a Magic Number] 11 | Number = 111 => 1+1+1=3 [This is NOT a Magic Number] 12 | 13 | The following function checks for Magic numbers and returns a Boolean accordingly. 14 | """ 15 | 16 | def magic_number(n): 17 | """ Checks if n is a magic number """ 18 | total_sum = 0 19 | 20 | # will end when n becomes 0 21 | # AND 22 | # sum becomes single digit. 23 | while n > 0 or total_sum > 9: 24 | # when n becomes 0 but we have a total_sum, 25 | # we update the value of n with the value of the sum digits 26 | if n == 0: 27 | n = total_sum # only when sum of digits isn't single digit 28 | total_sum = 0 29 | total_sum += n % 10 30 | n //= 10 31 | 32 | # Return true if sum becomes 1 33 | return total_sum == 1 34 | -------------------------------------------------------------------------------- /algorithms/maths/power.py: -------------------------------------------------------------------------------- 1 | """ 2 | Performs exponentiation, similarly to the built-in pow() or ** functions. 3 | Allows also for calculating the exponentiation modulo. 4 | """ 5 | def power(a: int, n: int, mod: int = None): 6 | """ 7 | Iterative version of binary exponentiation 8 | 9 | Calculate a ^ n 10 | if mod is specified, return the result modulo mod 11 | 12 | Time Complexity : O(log(n)) 13 | Space Complexity : O(1) 14 | """ 15 | ans = 1 16 | while n: 17 | if n & 1: 18 | ans = ans * a 19 | a = a * a 20 | if mod: 21 | ans %= mod 22 | a %= mod 23 | n >>= 1 24 | return ans 25 | 26 | 27 | def power_recur(a: int, n: int, mod: int = None): 28 | """ 29 | Recursive version of binary exponentiation 30 | 31 | Calculate a ^ n 32 | if mod is specified, return the result modulo mod 33 | 34 | Time Complexity : O(log(n)) 35 | Space Complexity : O(log(n)) 36 | """ 37 | if n == 0: 38 | ans = 1 39 | elif n == 1: 40 | ans = a 41 | else: 42 | ans = power_recur(a, n // 2, mod) 43 | ans = ans * ans 44 | if n % 2: 45 | ans = ans * a 46 | if mod: 47 | ans %= mod 48 | return ans 49 | -------------------------------------------------------------------------------- /algorithms/heap/sliding_window_max.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given an array nums, there is a sliding window of size k 3 | which is moving from the very left of the array to the very right. 4 | You can only see the k numbers in the window. 5 | Each time the sliding window moves right by one position. 6 | 7 | For example, 8 | Given nums = [1,3,-1,-3,5,3,6,7], and k = 3. 9 | 10 | Window position Max 11 | --------------- ----- 12 | [1 3 -1] -3 5 3 6 7 3 13 | 1 [3 -1 -3] 5 3 6 7 3 14 | 1 3 [-1 -3 5] 3 6 7 5 15 | 1 3 -1 [-3 5 3] 6 7 5 16 | 1 3 -1 -3 [5 3 6] 7 6 17 | 1 3 -1 -3 5 [3 6 7] 7 18 | Therefore, return the max sliding window as [3,3,5,5,6,7]. 19 | """ 20 | import collections 21 | 22 | 23 | def max_sliding_window(nums, k): 24 | """ 25 | :type nums: List[int] 26 | :type k: int 27 | :rtype: List[int] 28 | """ 29 | if not nums: 30 | return nums 31 | queue = collections.deque() 32 | res = [] 33 | for num in nums: 34 | if len(queue) < k: 35 | queue.append(num) 36 | else: 37 | res.append(max(queue)) 38 | queue.popleft() 39 | queue.append(num) 40 | res.append(max(queue)) 41 | return res 42 | -------------------------------------------------------------------------------- /algorithms/maths/base_conversion.py: -------------------------------------------------------------------------------- 1 | """ 2 | Integer base conversion algorithm 3 | 4 | int_to_base(5, 2) return '101'. 5 | base_to_int('F', 16) return 15. 6 | 7 | """ 8 | 9 | import string 10 | 11 | def int_to_base(num, base): 12 | """ 13 | :type num: int 14 | :type base: int 15 | :rtype: str 16 | """ 17 | is_negative = False 18 | if num == 0: 19 | return '0' 20 | if num < 0: 21 | is_negative = True 22 | num *= -1 23 | digit = string.digits + string.ascii_uppercase 24 | res = '' 25 | while num > 0: 26 | res += digit[num % base] 27 | num //= base 28 | if is_negative: 29 | return '-' + res[::-1] 30 | return res[::-1] 31 | 32 | 33 | def base_to_int(str_to_convert, base): 34 | """ 35 | Note : You can use int() built-in function instead of this. 36 | :type str_to_convert: str 37 | :type base: int 38 | :rtype: int 39 | """ 40 | 41 | digit = {} 42 | for ind, char in enumerate(string.digits + string.ascii_uppercase): 43 | digit[char] = ind 44 | multiplier = 1 45 | res = 0 46 | for char in str_to_convert[::-1]: 47 | res += digit[char] * multiplier 48 | multiplier *= base 49 | return res 50 | -------------------------------------------------------------------------------- /algorithms/graph/check_bipartite.py: -------------------------------------------------------------------------------- 1 | """ 2 | Bipartite graph is a graph whose vertices can be divided into two disjoint and independent sets. 3 | (https://en.wikipedia.org/wiki/Bipartite_graph) 4 | """ 5 | 6 | def check_bipartite(adj_list): 7 | """ 8 | Determine if the given graph is bipartite. 9 | 10 | Time complexity is O(|E|) 11 | Space complexity is O(|V|) 12 | """ 13 | 14 | vertices = len(adj_list) 15 | 16 | # Divide vertexes in the graph into set_type 0 and 1 17 | # Initialize all set_types as -1 18 | set_type = [-1 for v in range(vertices)] 19 | set_type[0] = 0 20 | 21 | queue = [0] 22 | 23 | while queue: 24 | current = queue.pop(0) 25 | 26 | # If there is a self-loop, it cannot be bipartite 27 | if adj_list[current][current]: 28 | return False 29 | 30 | for adjacent in range(vertices): 31 | if adj_list[current][adjacent]: 32 | if set_type[adjacent] == set_type[current]: 33 | return False 34 | 35 | if set_type[adjacent] == -1: 36 | # set type of u opposite of v 37 | set_type[adjacent] = 1 - set_type[current] 38 | queue.append(adjacent) 39 | 40 | return True 41 | -------------------------------------------------------------------------------- /algorithms/search/jump_search.py: -------------------------------------------------------------------------------- 1 | """ 2 | Jump Search 3 | 4 | Find an element in a sorted array. 5 | """ 6 | 7 | import math 8 | 9 | def jump_search(arr,target): 10 | """ 11 | Worst-case Complexity: O(√n) (root(n)) 12 | All items in list must be sorted like binary search 13 | 14 | Find block that contains target value and search it linearly in that block 15 | It returns a first target value in array 16 | 17 | reference: https://en.wikipedia.org/wiki/Jump_search 18 | """ 19 | 20 | length = len(arr) 21 | block_size = int(math.sqrt(length)) 22 | block_prev = 0 23 | block= block_size 24 | 25 | # return -1 means that array doesn't contain target value 26 | # find block that contains target value 27 | 28 | if arr[length - 1] < target: 29 | return -1 30 | while block <= length and arr[block - 1] < target: 31 | block_prev = block 32 | block += block_size 33 | 34 | # find target value in block 35 | 36 | while arr[block_prev] < target : 37 | block_prev += 1 38 | if block_prev == min(block, length) : 39 | return -1 40 | 41 | # if there is target value in array, return it 42 | 43 | if arr[block_prev] == target : 44 | return block_prev 45 | return -1 46 | -------------------------------------------------------------------------------- /algorithms/sort/bead_sort.py: -------------------------------------------------------------------------------- 1 | """ 2 | Bead Sort (also known as Gravity Sort) is a natural sorting algorithm that simulates how beads would settle under gravity on an abacus. It is most useful for sorting positive integers, especially when the range of numbers isn't excessively large. However, it is not a comparison-based sort and is generally impractical for large inputs due to its reliance on physical modeling. 3 | Time Complexity 4 | - Best Case: O(n) if the numbers are already sorted 5 | - Average Case: O(n^2) because each bead needs to be placed and then fall under gravity 6 | - Worst Case: O(n^2) since each bead must "fall" individually 7 | """ 8 | 9 | def bead_sort(arr): 10 | if any(num < 0 for num in arr): 11 | raise ValueError("Bead sort only works with non-negative integers.") 12 | 13 | max_num = max(arr) if arr else 0 14 | grid = [[0] * len(arr) for _ in range(max_num)] 15 | 16 | # Drop beads (place beads in columns) 17 | for col, num in enumerate(arr): 18 | for row in range(num): 19 | grid[row][col] = 1 20 | 21 | # Let the beads "fall" (count beads in each row) 22 | for row in grid: 23 | sum_beads = sum(row) 24 | for col in range(len(arr)): 25 | row[col] = 1 if col < sum_beads else 0 26 | 27 | -------------------------------------------------------------------------------- /algorithms/strings/panagram.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a string, check whether it is a panagram or not. 3 | 4 | A panagram is a sentence that uses every letter at least once. 5 | 6 | The most famous example is: "he quick brown fox jumps over the lazy dog. 7 | 8 | Note: 9 | A panagram in one language isn't necessarily a panagram in another. This 10 | module assumes the english language. Hence, the Finnish panagram 11 | 'Törkylempijävongahdus' won't pass for a panagram despite being considered 12 | a perfect panagram in its language. However, the Swedish panagram 13 | 'Yxmördaren Julia Blomqvist på fäktning i Schweiz' will pass despite 14 | including letters not used in the english alphabet. This is because the 15 | Swedish alphabet only extends the Latin one. 16 | """ 17 | 18 | from string import ascii_lowercase 19 | 20 | def panagram(string): 21 | """ 22 | Returns whether the input string is an English panagram or not. 23 | 24 | Parameters: 25 | string (str): A sentence in the form of a string. 26 | 27 | Returns: 28 | A boolean with the result. 29 | """ 30 | letters = set(ascii_lowercase) 31 | for c in string: 32 | try: 33 | letters.remove(c.lower()) 34 | except: 35 | pass 36 | return len(letters) == 0 -------------------------------------------------------------------------------- /algorithms/maths/factorial.py: -------------------------------------------------------------------------------- 1 | """ 2 | Calculates the factorial with the added functionality of calculating it modulo mod. 3 | """ 4 | def factorial(n, mod=None): 5 | """Calculates factorial iteratively. 6 | If mod is not None, then return (n! % mod) 7 | Time Complexity - O(n)""" 8 | if not (isinstance(n, int) and n >= 0): 9 | raise ValueError("'n' must be a non-negative integer.") 10 | if mod is not None and not (isinstance(mod, int) and mod > 0): 11 | raise ValueError("'mod' must be a positive integer") 12 | result = 1 13 | if n == 0: 14 | return 1 15 | for i in range(2, n+1): 16 | result *= i 17 | if mod: 18 | result %= mod 19 | return result 20 | 21 | 22 | def factorial_recur(n, mod=None): 23 | """Calculates factorial recursively. 24 | If mod is not None, then return (n! % mod) 25 | Time Complexity - O(n)""" 26 | if not (isinstance(n, int) and n >= 0): 27 | raise ValueError("'n' must be a non-negative integer.") 28 | if mod is not None and not (isinstance(mod, int) and mod > 0): 29 | raise ValueError("'mod' must be a positive integer") 30 | if n == 0: 31 | return 1 32 | result = n * factorial(n - 1, mod) 33 | if mod: 34 | result %= mod 35 | return result 36 | -------------------------------------------------------------------------------- /algorithms/matrix/matrix_exponentiation.py: -------------------------------------------------------------------------------- 1 | def multiply(matA: list, matB: list) -> list: 2 | """ 3 | Multiplies two square matrices matA and matB of size n x n 4 | Time Complexity: O(n^3) 5 | """ 6 | n = len(matA) 7 | matC = [[0 for i in range(n)] for j in range(n)] 8 | 9 | for i in range(n): 10 | for j in range(n): 11 | for k in range(n): 12 | matC[i][j] += matA[i][k] * matB[k][j] 13 | 14 | return matC 15 | 16 | 17 | def identity(n: int) -> list: 18 | """ 19 | Returns the Identity matrix of size n x n 20 | Time Complexity: O(n^2) 21 | """ 22 | I = [[0 for i in range(n)] for j in range(n)] 23 | 24 | for i in range(n): 25 | I[i][i] = 1 26 | 27 | return I 28 | 29 | 30 | def matrix_exponentiation(mat: list, n: int) -> list: 31 | """ 32 | Calculates mat^n by repeated squaring 33 | Time Complexity: O(d^3 log(n)) 34 | d: dimension of the square matrix mat 35 | n: power the matrix is raised to 36 | """ 37 | if n == 0: 38 | return identity(len(mat)) 39 | elif n % 2 == 1: 40 | return multiply(matrix_exponentiation(mat, n - 1), mat) 41 | else: 42 | tmp = matrix_exponentiation(mat, n // 2) 43 | return multiply(tmp, tmp) 44 | -------------------------------------------------------------------------------- /.github/workflows/python-app.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a single version of Python 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: Python application 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up Python 3.10 20 | uses: actions/setup-python@v2 21 | with: 22 | python-version: "3.10" 23 | - name: Install dependencies 24 | run: | 25 | python -m pip install --upgrade pip 26 | pip install flake8 pytest 27 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 28 | - name: Lint with flake8 29 | run: | 30 | # stop the build if there are Python syntax errors or undefined names 31 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 32 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 33 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 34 | - name: Test with pytest 35 | run: | 36 | python -m pytest 37 | -------------------------------------------------------------------------------- /algorithms/graph/all_pairs_shortest_path.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a n*n adjacency array. 3 | it will give you all pairs shortest path length. 4 | use deepcopy to preserve the original information. 5 | 6 | Time complexity : O(E^3) 7 | 8 | example 9 | 10 | a = [[0 , 0.1 , 0.101, 0.142, 0.277], 11 | [0.465, 0 , 0.191, 0.192, 0.587], 12 | [0.245, 0.554, 0 , 0.333, 0.931], 13 | [1.032, 0.668, 0.656, 0 , 0.151], 14 | [0.867, 0.119, 0.352, 0.398, 0]] 15 | 16 | result 17 | 18 | [[0 , 0.1 , 0.101, 0.142, 0.277], 19 | [0.436, 0 , 0.191, 0.192, 0.343], 20 | [0.245, 0.345, 0 , 0.333, 0.484], 21 | [0.706, 0.27 , 0.461, 0 , 0.151], 22 | [0.555, 0.119, 0.31 , 0.311, 0]] 23 | 24 | """ 25 | import copy 26 | 27 | def all_pairs_shortest_path(adjacency_matrix): 28 | """ 29 | Given a matrix of the edge weights between respective nodes, returns a 30 | matrix containing the shortest distance distance between the two nodes. 31 | """ 32 | 33 | new_array = copy.deepcopy(adjacency_matrix) 34 | 35 | size = len(new_array) 36 | for k in range(size): 37 | for i in range(size): 38 | for j in range(size): 39 | if new_array[i][j] > new_array[i][k] + new_array[k][j]: 40 | new_array[i][j] = new_array[i][k] + new_array[k][j] 41 | 42 | return new_array 43 | -------------------------------------------------------------------------------- /algorithms/linkedlist/copy_random_pointer.py: -------------------------------------------------------------------------------- 1 | """ 2 | A linked list is given such that each node contains an additional random 3 | pointer which could point to any node in the list or null. 4 | 5 | Return a deep copy of the list. 6 | """ 7 | from collections import defaultdict 8 | 9 | 10 | class RandomListNode(object): 11 | def __init__(self, label): 12 | self.label = label 13 | self.next = None 14 | self.random = None 15 | 16 | 17 | def copy_random_pointer_v1(head): 18 | """ 19 | :type head: RandomListNode 20 | :rtype: RandomListNode 21 | """ 22 | dic = dict() 23 | m = n = head 24 | while m: 25 | dic[m] = RandomListNode(m.label) 26 | m = m.next 27 | while n: 28 | dic[n].next = dic.get(n.next) 29 | dic[n].random = dic.get(n.random) 30 | n = n.next 31 | return dic.get(head) 32 | 33 | 34 | # O(n) 35 | def copy_random_pointer_v2(head): 36 | """ 37 | :type head: RandomListNode 38 | :rtype: RandomListNode 39 | """ 40 | copy = defaultdict(lambda: RandomListNode(0)) 41 | copy[None] = None 42 | node = head 43 | while node: 44 | copy[node].label = node.label 45 | copy[node].next = copy[node.next] 46 | copy[node].random = copy[node.random] 47 | node = node.next 48 | return copy[head] 49 | -------------------------------------------------------------------------------- /algorithms/strings/count_binary_substring.py: -------------------------------------------------------------------------------- 1 | """ 2 | Give a string s, count the number of non-empty (contiguous) substrings that have 3 | the same number of 0's and 1's, and all the 0's and all the 1's in these substrings are grouped consecutively. 4 | 5 | Substrings that occur multiple times are counted the number of times they occur. 6 | Example 1: 7 | Input: "00110011" 8 | Output: 6 9 | Explanation: There are 6 substrings that have equal number of consecutive 1's and 0's: "0011", "01", "1100", "10", "0011", and "01". 10 | 11 | Notice that some of these substrings repeat and are counted the number of times they occur. 12 | 13 | Also, "00110011" is not a valid substring because all the 0's (and 1's) are not grouped together. 14 | 15 | Example 2: 16 | Input: "10101" 17 | Output: 4 18 | Explanation: There are 4 substrings: "10", "01", "10", "01" that have equal number of consecutive 1's and 0's. 19 | Reference: https://leetcode.com/problems/count-binary-substrings/description/ 20 | """ 21 | def count_binary_substring(s): 22 | cur = 1 23 | pre = 0 24 | count = 0 25 | for i in range(1, len(s)): 26 | if s[i] != s[i - 1]: 27 | count = count + min(pre, cur) 28 | pre = cur 29 | cur = 1 30 | else: 31 | cur = cur + 1 32 | count = count + min(pre, cur) 33 | return count 34 | -------------------------------------------------------------------------------- /algorithms/backtrack/subsets.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a set of distinct integers, nums, return all possible subsets. 3 | 4 | Note: The solution set must not contain duplicate subsets. 5 | 6 | For example, 7 | If nums = [1,2,3], a solution is: 8 | 9 | [ 10 | [3], 11 | [1], 12 | [2], 13 | [1,2,3], 14 | [1,3], 15 | [2,3], 16 | [1,2], 17 | [] 18 | ] 19 | """ 20 | 21 | 22 | def subsets(nums): 23 | """ 24 | O(2**n) 25 | """ 26 | def backtrack(res, nums, stack, pos): 27 | if pos == len(nums): 28 | res.append(list(stack)) 29 | else: 30 | # take nums[pos] 31 | stack.append(nums[pos]) 32 | backtrack(res, nums, stack, pos+1) 33 | stack.pop() 34 | # dont take nums[pos] 35 | backtrack(res, nums, stack, pos+1) 36 | 37 | res = [] 38 | backtrack(res, nums, [], 0) 39 | return res 40 | 41 | 42 | """ 43 | simplified backtrack 44 | 45 | def backtrack(res, nums, cur, pos): 46 | if pos >= len(nums): 47 | res.append(cur) 48 | else: 49 | backtrack(res, nums, cur+[nums[pos]], pos+1) 50 | backtrack(res, nums, cur, pos+1) 51 | """ 52 | 53 | 54 | # Iteratively 55 | def subsets_v2(nums): 56 | res = [[]] 57 | for num in sorted(nums): 58 | res += [item+[num] for item in res] 59 | return res 60 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = 3 | py35 4 | py36 5 | py37 6 | coverage 7 | 8 | [testenv] 9 | passenv = TRAVIS TRAVIS_* 10 | basepython = 11 | py35: python3.5 12 | py36: python3.6 13 | py37: python3.7 14 | deps = 15 | coverage 16 | coveralls 17 | commands = 18 | coverage run --source=tests,algorithms -m unittest discover tests 19 | coverage report -m 20 | coveralls 21 | 22 | [testenv:py35] 23 | passenv = CI TRAVIS TRAVIS_* 24 | basepython = 25 | python3.5 26 | deps = 27 | pytest 28 | commands = 29 | python3 -m unittest discover tests 30 | python3 -m pytest tests 31 | 32 | [testenv:py36] 33 | passenv = CI TRAVIS TRAVIS_* 34 | basepython = 35 | python3.6 36 | deps = 37 | pytest 38 | commands = 39 | python3 -m unittest discover tests 40 | python3 -m pytest tests 41 | 42 | [testenv:py37] 43 | passenv = CI TRAVIS TRAVIS_* 44 | basepython = 45 | python3.7 46 | deps = 47 | pytest 48 | commands = 49 | python3 -m unittest discover tests 50 | python3 -m pytest tests 51 | 52 | [testenv:coverage] 53 | passenv = CI TRAVIS TRAVIS_* 54 | skip_install = True 55 | basepython = 56 | python3.7 57 | commands = 58 | coverage run --source=tests,algorithms -m unittest discover tests 59 | coverage report -m 60 | coveralls 61 | deps = 62 | coverage 63 | coveralls 64 | --------------------------------------------------------------------------------