├── .coveragerc ├── .github └── workflows │ └── python-app.yml ├── .gitignore ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── MANIFEST.in ├── README.md ├── algorithms ├── __init__.py ├── arrays │ ├── __init__.py │ ├── delete_nth.py │ ├── flatten.py │ ├── garage.py │ ├── josephus.py │ ├── limit.py │ ├── longest_non_repeat.py │ ├── max_ones_index.py │ ├── merge_intervals.py │ ├── missing_ranges.py │ ├── move_zeros.py │ ├── n_sum.py │ ├── plus_one.py │ ├── remove_duplicates.py │ ├── rotate.py │ ├── summarize_ranges.py │ ├── three_sum.py │ ├── top_1.py │ ├── trimmean.py │ └── two_sum.py ├── automata │ ├── __init__.py │ └── dfa.py ├── backtrack │ ├── __init__.py │ ├── add_operators.py │ ├── anagram.py │ ├── array_sum_combinations.py │ ├── combination_sum.py │ ├── factor_combinations.py │ ├── find_words.py │ ├── generate_abbreviations.py │ ├── generate_parenthesis.py │ ├── letter_combination.py │ ├── palindrome_partitioning.py │ ├── pattern_match.py │ ├── permute.py │ ├── permute_unique.py │ ├── subsets.py │ └── subsets_unique.py ├── bfs │ ├── __init__.py │ ├── count_islands.py │ ├── maze_search.py │ ├── shortest_distance_from_all_buildings.py │ └── word_ladder.py ├── bit │ ├── __init__.py │ ├── add_bitwise_operator.py │ ├── binary_gap.py │ ├── bit_operation.py │ ├── bytes_int_conversion.py │ ├── count_flips_to_convert.py │ ├── count_ones.py │ ├── find_difference.py │ ├── find_missing_number.py │ ├── flip_bit_longest_sequence.py │ ├── has_alternative_bit.py │ ├── insert_bit.py │ ├── power_of_two.py │ ├── remove_bit.py │ ├── reverse_bits.py │ ├── single_number.py │ ├── single_number2.py │ ├── single_number3.py │ ├── subsets.py │ └── swap_pair.py ├── compression │ ├── __init__.py │ ├── elias.py │ ├── huffman_coding.py │ └── rle_compression.py ├── dfs │ ├── __init__.py │ ├── all_factors.py │ ├── count_islands.py │ ├── maze_search.py │ ├── pacific_atlantic.py │ ├── sudoku_solver.py │ └── walls_and_gates.py ├── distribution │ ├── __init__.py │ └── histogram.py ├── dp │ ├── __init__.py │ ├── buy_sell_stock.py │ ├── climbing_stairs.py │ ├── coin_change.py │ ├── combination_sum.py │ ├── edit_distance.py │ ├── egg_drop.py │ ├── fib.py │ ├── hosoya_triangle.py │ ├── house_robber.py │ ├── int_divide.py │ ├── job_scheduling.py │ ├── k_factor.py │ ├── knapsack.py │ ├── longest_common_subsequence.py │ ├── longest_increasing.py │ ├── matrix_chain_order.py │ ├── max_product_subarray.py │ ├── max_subarray.py │ ├── min_cost_path.py │ ├── num_decodings.py │ ├── planting_trees.py │ ├── regex_matching.py │ ├── rod_cut.py │ └── word_break.py ├── graph │ ├── __init__.py │ ├── all_pairs_shortest_path.py │ ├── bellman_ford.py │ ├── check_bipartite.py │ ├── check_digraph_strongly_connected.py │ ├── clone_graph.py │ ├── count_connected_number_of_component.py │ ├── cycle_detection.py │ ├── dijkstra.py │ ├── find_all_cliques.py │ ├── find_path.py │ ├── graph.py │ ├── markov_chain.py │ ├── maximum_flow.py │ ├── maximum_flow_bfs.py │ ├── maximum_flow_dfs.py │ ├── minimum_spanning_tree.py │ ├── path_between_two_vertices_in_digraph.py │ ├── prims_minimum_spanning.py │ ├── satisfiability.py │ ├── strongly_connected_components_kosaraju.py │ ├── tarjan.py │ ├── transitive_closure_dfs.py │ └── traversal.py ├── greedy │ ├── __init__.py │ └── max_contiguous_subsequence_sum.py ├── heap │ ├── __init__.py │ ├── binary_heap.py │ ├── k_closest_points.py │ ├── merge_sorted_k_lists.py │ ├── skyline.py │ └── sliding_window_max.py ├── linkedlist │ ├── __init__.py │ ├── add_two_numbers.py │ ├── copy_random_pointer.py │ ├── delete_node.py │ ├── first_cyclic_node.py │ ├── intersection.py │ ├── is_cyclic.py │ ├── is_palindrome.py │ ├── is_sorted.py │ ├── kth_to_last.py │ ├── linkedlist.py │ ├── merge_two_list.py │ ├── partition.py │ ├── remove_duplicates.py │ ├── remove_range.py │ ├── reverse.py │ ├── rotate_list.py │ └── swap_in_pairs.py ├── map │ ├── __init__.py │ ├── hashtable.py │ ├── is_anagram.py │ ├── is_isomorphic.py │ ├── longest_common_subsequence.py │ ├── longest_palindromic_subsequence.py │ ├── randomized_set.py │ ├── separate_chaining_hashtable.py │ ├── valid_sudoku.py │ └── word_pattern.py ├── maths │ ├── __init__.py │ ├── base_conversion.py │ ├── chinese_remainder_theorem.py │ ├── combination.py │ ├── cosine_similarity.py │ ├── decimal_to_binary_ip.py │ ├── diffie_hellman_key_exchange.py │ ├── euler_totient.py │ ├── extended_gcd.py │ ├── factorial.py │ ├── fft.py │ ├── find_order_simple.py │ ├── find_primitive_root_simple.py │ ├── gcd.py │ ├── generate_strobogrammtic.py │ ├── hailstone.py │ ├── is_strobogrammatic.py │ ├── krishnamurthy_number.py │ ├── magic_number.py │ ├── modular_exponential.py │ ├── modular_inverse.py │ ├── next_bigger.py │ ├── next_perfect_square.py │ ├── nth_digit.py │ ├── num_digits.py │ ├── num_perfect_squares.py │ ├── polynomial.py │ ├── power.py │ ├── prime_check.py │ ├── primes_sieve_of_eratosthenes.py │ ├── pythagoras.py │ ├── rabin_miller.py │ ├── recursive_binomial_coefficient.py │ ├── rsa.py │ ├── sqrt_precision_factor.py │ ├── summing_digits.py │ └── symmetry_group_cycle_index.py ├── matrix │ ├── __init__.py │ ├── bomb_enemy.py │ ├── cholesky_matrix_decomposition.py │ ├── copy_transform.py │ ├── count_paths.py │ ├── crout_matrix_decomposition.py │ ├── matrix_exponentiation.py │ ├── matrix_inversion.py │ ├── multiply.py │ ├── rotate_image.py │ ├── search_in_sorted_matrix.py │ ├── sort_matrix_diagonally.py │ ├── sparse_dot_vector.py │ ├── sparse_mul.py │ ├── spiral_traversal.py │ ├── sudoku_validator.py │ └── sum_sub_squares.py ├── ml │ └── nearest_neighbor.py ├── queues │ ├── __init__.py │ ├── max_sliding_window.py │ ├── moving_average.py │ ├── priority_queue.py │ ├── queue.py │ ├── reconstruct_queue.py │ └── zigzagiterator.py ├── search │ ├── __init__.py │ ├── binary_search.py │ ├── find_min_rotate.py │ ├── first_occurrence.py │ ├── interpolation_search.py │ ├── jump_search.py │ ├── last_occurrence.py │ ├── linear_search.py │ ├── next_greatest_letter.py │ ├── search_insert.py │ ├── search_range.py │ ├── search_rotate.py │ ├── ternary_search.py │ └── two_sum.py ├── set │ ├── __init__.py │ ├── find_keyboard_row.py │ ├── randomized_set.py │ └── set_covering.py ├── sort │ ├── __init__.py │ ├── bitonic_sort.py │ ├── bogo_sort.py │ ├── bubble_sort.py │ ├── bucket_sort.py │ ├── cocktail_shaker_sort.py │ ├── comb_sort.py │ ├── counting_sort.py │ ├── cycle_sort.py │ ├── exchange_sort.py │ ├── gnome_sort.py │ ├── heap_sort.py │ ├── insertion_sort.py │ ├── meeting_rooms.py │ ├── merge_sort.py │ ├── pancake_sort.py │ ├── pigeonhole_sort.py │ ├── quick_sort.py │ ├── radix_sort.py │ ├── selection_sort.py │ ├── shell_sort.py │ ├── sort_colors.py │ ├── stooge_sort.py │ ├── top_sort.py │ └── wiggle_sort.py ├── stack │ ├── __init__.py │ ├── is_consecutive.py │ ├── is_sorted.py │ ├── longest_abs_path.py │ ├── ordered_stack.py │ ├── remove_min.py │ ├── simplify_path.py │ ├── stack.py │ ├── stutter.py │ ├── switch_pairs.py │ └── valid_parenthesis.py ├── streaming │ ├── __init__.py │ ├── misra_gries.py │ └── one_sparse_recovery.py ├── strings │ ├── __init__.py │ ├── add_binary.py │ ├── atbash_cipher.py │ ├── breaking_bad.py │ ├── caesar_cipher.py │ ├── check_pangram.py │ ├── contain_string.py │ ├── count_binary_substring.py │ ├── decode_string.py │ ├── delete_reoccurring.py │ ├── domain_extractor.py │ ├── encode_decode.py │ ├── first_unique_char.py │ ├── fizzbuzz.py │ ├── group_anagrams.py │ ├── int_to_roman.py │ ├── is_palindrome.py │ ├── is_rotated.py │ ├── judge_circle.py │ ├── knuth_morris_pratt.py │ ├── license_number.py │ ├── longest_common_prefix.py │ ├── longest_palindromic_substring.py │ ├── make_sentence.py │ ├── merge_string_checker.py │ ├── min_distance.py │ ├── multiply_strings.py │ ├── one_edit_distance.py │ ├── panagram.py │ ├── rabin_karp.py │ ├── repeat_string.py │ ├── repeat_substring.py │ ├── reverse_string.py │ ├── reverse_vowel.py │ ├── reverse_words.py │ ├── roman_to_int.py │ ├── rotate.py │ ├── strip_url_params.py │ ├── strong_password.py │ ├── text_justification.py │ ├── unique_morse.py │ ├── validate_coordinates.py │ └── word_squares.py ├── tree │ ├── __init__.py │ ├── avl │ │ ├── __init__.py │ │ └── avl.py │ ├── b_tree.py │ ├── bin_tree_to_list.py │ ├── binary_tree_paths.py │ ├── bst │ │ ├── BSTIterator.py │ │ ├── array_to_bst.py │ │ ├── bst.py │ │ ├── bst_closest_value.py │ │ ├── count_left_node.py │ │ ├── delete_node.py │ │ ├── depth_sum.py │ │ ├── height.py │ │ ├── is_bst.py │ │ ├── kth_smallest.py │ │ ├── lowest_common_ancestor.py │ │ ├── num_empty.py │ │ ├── predecessor.py │ │ ├── serialize_deserialize.py │ │ ├── successor.py │ │ └── unique_bst.py │ ├── construct_tree_postorder_preorder.py │ ├── deepest_left.py │ ├── fenwick_tree │ │ └── fenwick_tree.py │ ├── invert_tree.py │ ├── is_balanced.py │ ├── is_subtree.py │ ├── is_symmetric.py │ ├── longest_consecutive.py │ ├── lowest_common_ancestor.py │ ├── max_height.py │ ├── max_path_sum.py │ ├── min_height.py │ ├── path_sum.py │ ├── path_sum2.py │ ├── pretty_print.py │ ├── red_black_tree │ │ └── red_black_tree.py │ ├── same_tree.py │ ├── segment_tree │ │ ├── iterative_segment_tree.py │ │ └── segment_tree.py │ ├── traversal │ │ ├── __init__.py │ │ ├── inorder.py │ │ ├── level_order.py │ │ ├── postorder.py │ │ ├── preorder.py │ │ └── zigzag.py │ ├── tree.py │ └── trie │ │ ├── add_and_search.py │ │ └── trie.py ├── unionfind │ └── count_islands.py └── unix │ ├── __init__.py │ └── path │ ├── full_path.py │ ├── join_with_slash.py │ ├── simplify_path.py │ └── split.py ├── docs ├── Makefile ├── make.bat ├── requirements.txt └── source │ ├── _static │ ├── algorithms_logo.png │ └── logo │ │ ├── 128pxblack.png │ │ ├── 128pxblack.svg │ │ ├── 128pxblue.png │ │ ├── 128pxblue.svg │ │ ├── 128pxorange.png │ │ ├── 128pxorange.svg │ │ ├── 256pxblack.png │ │ ├── 256pxblack.svg │ │ ├── 256pxblue.png │ │ ├── 256pxblue.svg │ │ ├── 256pxorange.png │ │ ├── 256pxorange.svg │ │ ├── 512pxblack.png │ │ ├── 512pxblack.svg │ │ ├── 512pxblue.png │ │ ├── 512pxblue.svg │ │ ├── 512pxorange.png │ │ ├── 512pxorange.svg │ │ ├── add │ │ ├── logotype1black.png │ │ ├── logotype1black.svg │ │ ├── logotype1blue.png │ │ ├── logotype1blue.svg │ │ ├── logotype1orange.png │ │ ├── logotype1orange.svg │ │ ├── logotype2black.png │ │ ├── logotype2black.svg │ │ ├── logotype2blue.png │ │ ├── logotype2blue.svg │ │ ├── logotype2orange.png │ │ └── logotype2orange.svg │ ├── arrays.rst │ ├── backtrack.rst │ ├── bfs.rst │ ├── bit.rst │ ├── conf.py │ ├── dfs.rst │ ├── dp.rst │ ├── examples.rst │ ├── graph.rst │ ├── heap.rst │ ├── index.rst │ ├── linkedlist.rst │ ├── map.rst │ ├── maths.rst │ ├── matrix.rst │ ├── queues.rst │ ├── search.rst │ ├── set.rst │ ├── sort.rst │ ├── stack.rst │ ├── strings.rst │ └── tree.rst ├── requirements.txt ├── setup.py ├── test_requirements.txt ├── tests ├── test_array.py ├── test_automata.py ├── test_backtrack.py ├── test_bfs.py ├── test_bit.py ├── test_compression.py ├── test_dfs.py ├── test_dp.py ├── test_graph.py ├── test_greedy.py ├── test_heap.py ├── test_histogram.py ├── test_iterative_segment_tree.py ├── test_linkedlist.py ├── test_map.py ├── test_maths.py ├── test_matrix.py ├── test_ml.py ├── test_monomial.py ├── test_polynomial.py ├── test_queues.py ├── test_search.py ├── test_set.py ├── test_sort.py ├── test_stack.py ├── test_streaming.py ├── test_strings.py ├── test_tree.py └── test_unix.py └── tox.ini /.coveragerc: -------------------------------------------------------------------------------- 1 | [report] 2 | omit = 3 | */python?.?/* 4 | */site-packages/nose/* 5 | *__init__* 6 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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/ -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | include LICENSE 3 | include algorithms/* 4 | -------------------------------------------------------------------------------- /algorithms/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/cad4754bc71742c2d6fcbd3b92ae74834d359844/algorithms/__init__.py -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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/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 | 11 | def remove_duplicates(array): 12 | new_array = [] 13 | 14 | for item in array: 15 | if item not in new_array: 16 | new_array.append(item) 17 | 18 | return new_array -------------------------------------------------------------------------------- /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 | 9 | from typing import List 10 | 11 | def summarize_ranges(array: List[int]) -> List[str]: 12 | res = [] 13 | if len(array) == 1: 14 | return [str(array[0])] 15 | it = iter(array) 16 | start = end = next(it) 17 | for num in it: 18 | if num - end == 1: 19 | end = num 20 | else: 21 | res.append((start, end) if start != end else (start,)) 22 | start = end = num 23 | res.append((start, end) if start != end else (start,)) 24 | return [f"{r[0]}-{r[1]}" if len(r) > 1 else str(r[0]) for r in res] 25 | 26 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/automata/__init__.py: -------------------------------------------------------------------------------- 1 | from .dfa import * 2 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/compression/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/cad4754bc71742c2d6fcbd3b92ae74834d359844/algorithms/compression/__init__.py -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /algorithms/distribution/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/cad4754bc71742c2d6fcbd3b92ae74834d359844/algorithms/distribution/__init__.py -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/greedy/__init__.py: -------------------------------------------------------------------------------- 1 | from .max_contiguous_subsequence_sum import * 2 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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/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/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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/matrix/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/cad4754bc71742c2d6fcbd3b92ae74834d359844/algorithms/matrix/__init__.py -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/set/__init__.py: -------------------------------------------------------------------------------- 1 | from .find_keyboard_row import * 2 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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/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/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/streaming/__init__.py: -------------------------------------------------------------------------------- 1 | from .one_sparse_recovery import * 2 | from .misra_gries import * 3 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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/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/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/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_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/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/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/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/tree/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/cad4754bc71742c2d6fcbd3b92ae74834d359844/algorithms/tree/__init__.py -------------------------------------------------------------------------------- /algorithms/tree/avl/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/cad4754bc71742c2d6fcbd3b92ae74834d359844/algorithms/tree/avl/__init__.py -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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/tree/traversal/__init__.py: -------------------------------------------------------------------------------- 1 | from .preorder import * 2 | from .postorder import * 3 | from .inorder import * 4 | -------------------------------------------------------------------------------- /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/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/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/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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /docs/source/_static/algorithms_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/cad4754bc71742c2d6fcbd3b92ae74834d359844/docs/source/_static/algorithms_logo.png -------------------------------------------------------------------------------- /docs/source/_static/logo/128pxblack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/cad4754bc71742c2d6fcbd3b92ae74834d359844/docs/source/_static/logo/128pxblack.png -------------------------------------------------------------------------------- /docs/source/_static/logo/128pxblack.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /docs/source/_static/logo/128pxblue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/cad4754bc71742c2d6fcbd3b92ae74834d359844/docs/source/_static/logo/128pxblue.png -------------------------------------------------------------------------------- /docs/source/_static/logo/128pxblue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /docs/source/_static/logo/128pxorange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/cad4754bc71742c2d6fcbd3b92ae74834d359844/docs/source/_static/logo/128pxorange.png -------------------------------------------------------------------------------- /docs/source/_static/logo/128pxorange.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /docs/source/_static/logo/256pxblack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/cad4754bc71742c2d6fcbd3b92ae74834d359844/docs/source/_static/logo/256pxblack.png -------------------------------------------------------------------------------- /docs/source/_static/logo/256pxblack.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /docs/source/_static/logo/256pxblue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/cad4754bc71742c2d6fcbd3b92ae74834d359844/docs/source/_static/logo/256pxblue.png -------------------------------------------------------------------------------- /docs/source/_static/logo/256pxblue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /docs/source/_static/logo/256pxorange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/cad4754bc71742c2d6fcbd3b92ae74834d359844/docs/source/_static/logo/256pxorange.png -------------------------------------------------------------------------------- /docs/source/_static/logo/256pxorange.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /docs/source/_static/logo/512pxblack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/cad4754bc71742c2d6fcbd3b92ae74834d359844/docs/source/_static/logo/512pxblack.png -------------------------------------------------------------------------------- /docs/source/_static/logo/512pxblack.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /docs/source/_static/logo/512pxblue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/cad4754bc71742c2d6fcbd3b92ae74834d359844/docs/source/_static/logo/512pxblue.png -------------------------------------------------------------------------------- /docs/source/_static/logo/512pxblue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /docs/source/_static/logo/512pxorange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/cad4754bc71742c2d6fcbd3b92ae74834d359844/docs/source/_static/logo/512pxorange.png -------------------------------------------------------------------------------- /docs/source/_static/logo/512pxorange.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /docs/source/_static/logo/add: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/source/_static/logo/logotype1black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/cad4754bc71742c2d6fcbd3b92ae74834d359844/docs/source/_static/logo/logotype1black.png -------------------------------------------------------------------------------- /docs/source/_static/logo/logotype1blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/cad4754bc71742c2d6fcbd3b92ae74834d359844/docs/source/_static/logo/logotype1blue.png -------------------------------------------------------------------------------- /docs/source/_static/logo/logotype1orange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/cad4754bc71742c2d6fcbd3b92ae74834d359844/docs/source/_static/logo/logotype1orange.png -------------------------------------------------------------------------------- /docs/source/_static/logo/logotype2black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/cad4754bc71742c2d6fcbd3b92ae74834d359844/docs/source/_static/logo/logotype2black.png -------------------------------------------------------------------------------- /docs/source/_static/logo/logotype2blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/cad4754bc71742c2d6fcbd3b92ae74834d359844/docs/source/_static/logo/logotype2blue.png -------------------------------------------------------------------------------- /docs/source/_static/logo/logotype2orange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/cad4754bc71742c2d6fcbd3b92ae74834d359844/docs/source/_static/logo/logotype2orange.png -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /docs/source/backtrack.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | algorithms.backtrack 5 | ==================== 6 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /docs/source/linkedlist.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | algorithms.linkedlist 5 | ===================== 6 | -------------------------------------------------------------------------------- /docs/source/map.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | algorithms.map 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/set.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | algorithms.set 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/strings.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | algorithms.string 5 | ================= 6 | -------------------------------------------------------------------------------- /docs/source/tree.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | algorithms.tree 5 | ================= 6 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keon/algorithms/cad4754bc71742c2d6fcbd3b92ae74834d359844/requirements.txt -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /test_requirements.txt: -------------------------------------------------------------------------------- 1 | flake8 2 | python-coveralls 3 | coverage 4 | nose 5 | pytest 6 | tox 7 | black 8 | -------------------------------------------------------------------------------- /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() -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------