diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 000000000..a5f7fcee8 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,5 @@ +[report] +omit = + */python?.?/* + */site-packages/nose/* + *__init__* diff --git a/.gitignore b/.gitignore index 43ae0e2a6..99f920b84 100755 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,15 @@ __pycache__/ *.py[cod] +*.iml +*.xml +.idea/ +.cache/ +.pytest_cache/ +.coverage +# Setuptools distribution folder. +/dist/ +# Python egg metadata, regenerated from source files by setuptools. +/*.egg-info +/*.egg +# docs +build/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..e9a4450a2 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,29 @@ +group: travis_latest +language: python +cache: + directories: + - $HOME/.cache/pip +matrix: + include: + - python: 3.4 + env: TOX_ENV=py34 + - python: 3.5 + env: TOX_ENV=py35,coverage + - python: 3.6 + env: TOX_ENV=py36 +install: + - pip install -r test_requirements.txt +before_script: + # stop the build if there are Python syntax errors or undefined names + - flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics +script: + # Check python install package + - pip3 install -e . + - tox -e $TOX_ENV + # Check python uninstall package + - pip3 uninstall -y algorithms +notifications: + on_success: change + on_failure: change # `always` will be the setting once code changes slow down diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..380bd3d64 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,64 @@ +# Contributing + +We love pull requests from everyone. By contributing to this repository, you +agree to abide by the [Code of Conduct](CODE_OF_CONDUCT.md). + +## Get Started + +* First [fork][fork] the repository and then clone it using: + + git clone git@github.com:your-username/algorithms.git + +* After that create a branch for your changes. For example: + * add_XXX if you will add new algorithms or data structures. + * fix_XXX if you will fixe a bug on a certain algorithm or data structure. + * test_XXX if you wrote a test/s. + +You may contribute by: +- implementing new algorithms in the repo. Be sure to keep it under +right section (e.g. [array](array), [dp](dp), etc). Make a new section for it if +it doesn't fall under any section. Make sure that your implementation works. +- optimizing or improving the existing algorithms. +- adding a different solution for the problem. +- finding and fixing bugs. +- adding examples to explain the algorithms better. +- adding test cases. + +## Pull Requests +Push to your fork and [submit a pull request][pr]. + +We will review and may suggest some changes or improvements or alternatives. +Some things that will increase the chance that your pull request is accepted: + +* All algorithms should be written in **Python 3**. +(There are a few algorithms still in _Python 2_ syntax. You can start by converting +[those][issue120] to _Python 3_.) +* Write clean and understandable code. +* Properly comment the code and briefly explain what the algorithm is doing in the [docstrings][docstr]. +* You may also explain the output using a minimal example. +* Try to also include a couple of test cases for the algorithm. +* Write a [good commit message][commit]. + + +## Issues +Submit a [new issue][newissue] if there is an algorithm to add, or if a bug was found in an existing algorithm. Before submitting a new issue please review the [existing issues][issues] to avoid creating duplicates. Also, consider resolving current issues or contributing to the discussion on an issue. + +## Collaborators +You can ask for any help or clarifications from the collaborators. +[Keon Kim](https://github.com/keon) + +[Rahul Goswami](https://github.com/goswami-rahul) + +[Ankit Agarwal](https://github.com/ankit167) + +[Hai Hoang Dang](https://github.com/danghai) + +[Saad](https://github.com/SaadBenn) + +[fork]: https://help.github.com/articles/fork-a-repo/ +[docstr]: https://www.python.org/dev/peps/pep-0257/#multi-line-docstrings +[commit]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html +[pr]: https://github.com/keon/algorithms/compare/ +[newissue]: https://github.com/keon/algorithms/issues/new +[issue120]: https://github.com/keon/algorithms/issues/120 +[issues]: https://github.com/keon/algorithms/issues/ diff --git a/CONTRIBUTING_JP.md b/CONTRIBUTING_JP.md new file mode 100644 index 000000000..a8e46c238 --- /dev/null +++ b/CONTRIBUTING_JP.md @@ -0,0 +1,61 @@ +# 貢献 + +私たちは、誰からもプルリクエストを歓迎します。このレポジトリに貢献をするためには[Code of Conduct](CODE_OF_CONDUCT.md)を従うことを同意しなければなりません。 + +## 始める + +* まずリポジトリを[フォーク][fork]し,次を使用してクローンします. + + git clone git@github.com:your-username/algorithms.git + +* その後,変更のためのブランチを作成します. 例えば: + * add_XXX : 新しいアルゴリズムやデータ構造を追加する場合 + * fix_XXX : 特定のアルゴリズムやデータ構造のbugを修正する場合 + * test_XXX : テストを作成する場合 + +以下の方法で貢献できます: +- レポジトリの新しいアルゴリズムを開発すること。 正しいセクションに保存してください(例: [array](array), [dp](dp), 等)。 どのセクションにも該当しない場合は、新しいセクションを作成します。 また、コードが正常に作動するかどうか確認してください。 +- 既存のアルゴリズムの最適化または改善。 +- 問題の他のソリューションを追加。 +- バグの検索と修正。 +- アルゴリズムをよりよく説明するための例を追加。 +- テストケースの追加。 + +## プルリクエスト +フォークにプッシュして[プルリクエストを送信します][pr]。 + +私たちは、検討した後、変更、改善、代替案を提案することもできます。 +あなたのプルリクエストが受け入れられる可能性が高くなる方法: + +* すべてのアルゴリズムは**Python 3**で開発されなければなりません。 +(まだPython 2で作成されたアルゴリズムがいくつかあります。これらをPython 3に転換する作業でスタートすることもできます。) +* きれいで理解しやすいコードを作成する。 +* コードに適切なコメントを残して[docstrings][docstr]にアルゴリズムが何をしているのか簡単に説明する。 +* 小さな例を通じて出力を説明する。 +* アルゴリズムのテストケースをいくつか含ませる。 +* [good commit message][commit]を書く。 + + +## イシュー +追加するアルゴリズムがあったり、既存のアルゴリズムにバグが発見された場合の[new issue][newissue]を提出してください。 新たなイシューを提出する前に重複を避けるために、[existing issues][issues]を確認してください。 また、現在のイシューを解決したり論議中のイシューに貢献することも考慮してください。 + +## コラボレータ +コラボレータには,どのようなヘルプや説明も求めることができます. + +[Keon Kim](https://github.com/keon) + +[Rahul Goswami](https://github.com/goswami-rahul) + +[Ankit Agarwal](https://github.com/ankit167) + +[Hai Hoang Dang](https://github.com/danghai) + +[Saad](https://github.com/SaadBenn) + +[fork]: https://help.github.com/articles/fork-a-repo/ +[docstr]: https://www.python.org/dev/peps/pep-0257/#multi-line-docstrings +[commit]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html +[pr]: https://github.com/keon/algorithms/compare/ +[newissue]: https://github.com/keon/algorithms/issues/new +[issue120]: https://github.com/keon/algorithms/issues/120 +[issues]: https://github.com/keon/algorithms/issues/ diff --git a/CONTRIBUTING_KR.md b/CONTRIBUTING_KR.md new file mode 100644 index 000000000..522f0e5b9 --- /dev/null +++ b/CONTRIBUTING_KR.md @@ -0,0 +1,66 @@ +# 기여 활동 + +모든 pull request는 환영입니다. 이 저장소에 기여 함으로써, 당신은 [code of conduct](CODE_OF_CONDUCT.md) +를 준수하는 것에 동의 한 것입니다. + + +## 시작하기 + +* 우선 이 저장소를 [fork][fork] 하시고, 이를 사용하기 위해서 다음과 같이 clone 주세요: + + git clone git@github.com:your-username/algorithms.git + +* 그리고 새로운 내용을 더할 branch를 만들어주세요. 예를 들어: + * add_XXX 만약 당신이 새로운 알고리즘이나 자료 구조를 추가 했을 경우. + * fix_XXX 만약 당신이 어떤 알고리즘이나 자료 구조에서 고쳐야할 bug를 발견했을 경우. + * test_XXX 만약 당신이 test/s를 작성한 경우. + +당신은 다음과 같이 기여할 수 있습니다: +- 새로운 알고리즘을 구현해주세요. 그리고, 그것을 정확히 분류해주세요(e.g. [array](array), [dp](dp), etc). +만약 당신의 알고리즘이 어떤 섹션에도 포함이 되지 않는다면, 새로운 섹션을 만들어 주세요. 단, 당신의 알고리즘이 제대로 작동하는지 +확인해주세요. +- 알고리즘들을 최적화하거나 향상시켜주세요. +- 문제들에 대해서 다른 해결 법을 추가해주세요. +- 버그들을 찾거나 고쳐주세요. +- 알고리즘들을 더 잘 설명하기 위한 새로운 예시들을 추가해주세요. +- test cases를 추가해주세요. + +## Pull Requests +당신의 fork에 push 하고 pull request를 제출하세요 [submit a pull request][pr]. + +우리는 이를 검토할 것이며, 변화, 개량, 혹은 대안을 제시할 수 도 있습니다. +여기에 당신의 pull request가 허용될 가능성을 높여주는 몇몇 요소들이 있습니다: + +* 모든 알고리즘들은 **Python 3**로 작성되어야 합니다. +(몇몇 알고리즘들은 여전히 _python 2_ 로 작성되어져 있습니다. 당신은 이를 Python 3으로 번역함으로써 저희에게 기여 해주실 수도 있습니다. +[those][issue120] to _Python 3_.) +* 깔끔하고 이해할 수 있는 코드를 작성해주세요. +* 코드에 대해 올바르게 주석 처리 해 주시고, 알고리즘이 수행하는 작업에 대해서 [docstrings][docstr]에서 설명해 주세요. +* 당신은 간단한 예시를 제시함으로써 출력 값에 대하여 설명하실 수도 있습니다. +* 또한 가능하다면 알고리즘에 대하여, 두 가지의 test cases를 포함 시켜주세요. +* [good commit message][commit]를 작성해주세요. + + +## Issues +만약 추가해야 할 알고리즘이 있거나, 현재 저희 프로젝트의 어떤 알고리즘에서 버그가 발견된다면 [new issue][newissue]에 이를 추가해주세요. 새로운 issue를 제안하기 전에 중복된 issue을 발생을 피하기 위해서 [existing issues][issues]를 확인해주세요. 또한, 현재 존재하는 issue를 해결하는 것을 고려해주시거나 issue에 대한 토의에 기여해주세요. + +## Collaborators +저희 협업자 들에게 어떤 도움이나 확인이 필요하다면, 위 주소로 물어봐주세요. + +[Keon Kim](https://github.com/keon) + +[Rahul Goswami](https://github.com/goswami-rahul) + +[Ankit Agarwal](https://github.com/ankit167) + +[Hai Hoang Dang](https://github.com/danghai) + +[Saad](https://github.com/SaadBenn) + +[fork]: https://help.github.com/articles/fork-a-repo/ +[docstr]: https://www.python.org/dev/peps/pep-0257/#multi-line-docstrings +[commit]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html +[pr]: https://github.com/keon/algorithms/compare/ +[newissue]: https://github.com/keon/algorithms/issues/new +[issue120]: https://github.com/keon/algorithms/issues/120 +[issues]: https://github.com/keon/algorithms/issues/ diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 000000000..50af9b9c9 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,3 @@ +include README.md +include LICENSE +include algorithms/* diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..3de1ba41a --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,13 @@ +(Put an `X` inside the `[ ]` to denote check mark `[X]`.) + + +- [ ] **If creating a new file :** + - [ ] added links to it in the README files ? + - [ ] included tests with it ? + - [ ] added description (overview of algorithm, time and space compleixty, and possible edge case) in docstrings ? + +- [ ] **if done some changes :** + - [ ] wrote short description in the PR explaining what the changes do ? + - [ ] Fixes #[issue number] if related to any issue + +- [ ] **other** diff --git a/README.md b/README.md index 1b4a87966..7bb26892d 100644 --- a/README.md +++ b/README.md @@ -1,211 +1,371 @@ +

+ +English | [简体中文](README_CN.md) | [Deutsch](README_GE.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Português](README_PTBR.md) + +[![PyPI version](https://badge.fury.io/py/algorithms.svg)](https://badge.fury.io/py/algorithms) +[![Open Source Helpers](https://www.codetriage.com/keon/algorithms/badges/users.svg)](https://www.codetriage.com/keon/algorithms) +[![Build Status](https://travis-ci.org/keon/algorithms.svg?branch=master)](https://travis-ci.org/keon/algorithms) +[![Coverage Status](https://coveralls.io/repos/github/keon/algorithms/badge.svg?branch=master)](https://coveralls.io/github/keon/algorithms?branch=master) + Pythonic Data Structures and Algorithms ========================================= -Minimal and clean example implementations of data structures and algorithms in Python. +Minimal and clean example implementations of data structures and algorithms in Python 3. + +## Contributing +Thanks for your interest in contributing! There are many ways to contribute to this project. [Get started here](CONTRIBUTING.md) + +## Tests + +### Use unittest +For running all tests write down: + + $ python3 -m unittest discover tests + +For running some specific tests you can do this as following (Ex: sort): + + $ python3 -m unittest tests.test_sort + +### Use pytest +For running all tests write down: + $ python3 -m pytest tests + +## Install +If you want to use the API algorithms in your code, it is as simple as: + + $ pip3 install algorithms + +You can test by creating a python file: (Ex: use `merge_sort` in `sort`) + +```python3 +from algorithms.sort import merge_sort + +if __name__ == "__main__": + my_list = [1, 8, 3, 5, 6] + my_list = merge_sort(my_list) + print(my_list) +``` + +## Uninstall +If you want to uninstall algorithms, it is as simple as: + + $ pip3 uninstall -y algorithms ## List of Implementations -- [array](array) - - [circular_counter](array/circular_counter.py) - - [flatten](array/flatten.py) - - [garage](array/garage.py) - - [longest_non_repeat](array/longest_non_repeat.py/) - - [merge_intervals](array/merge_intervals.py) - - [missing_ranges](array/missing_ranges.py) - - [plus_one](array/plus_one.py) - - [rotate_array](array/rotate_array.py) - - [summary_ranges](array/summary_ranges.py) - - [three_sum](array/three_sum.py) - - [two_sum](array/two_sum.py) -- [backtrack](backtrack) - - [general_solution.md](backtrack/) - - [anagram](backtrack/anagram.py) - - [array_sum_combinations](backtrack/array_sum_combination.py) - - [combination_sum](backtrack/combination_sum.py) - - [expression_add_operators](backtrack/expression_add_operators.py) - - [factor_combinations](backtrack/factor_combinations.py) - - [generate_abbreviations](backtrack/generate_abbreviations.py) - - [generate_parenthesis](backtrack/generate_parenthesis.py) - - [letter_combination](backtrack/letter_combination.py) - - [palindrome_partitioning](backtrack/palindrome_partitioning.py) - - [pattern_match](backtrack/pattern_match.py) - - [permute](backtrack/permute.py) - - [permute_unique](backtrack/permute_unique.py) - - [subsets](backtrack/subsets.py) - - [subsets_unique](backtrack/subsets_unique.py) -- [bfs](bfs) - - [shortest_distance_from_all_buildings](bfs/shortest_distance_from_all_buildings.py) - - [word_ladder](bfs/word_ladder.py) -- [bit](bit) - - [bytes_int_conversion](bit/bytes_int_conversion.py) - - [count_ones](bit/count_ones.py) - - [find_missing_number](bit/find_missing_number.py) - - [power_of_two](bit/power_of_two.py) - - [reverse_bits](bit/reverse_bits.py) - - [single_number2](bit/single_number2.py) - - [single_number](bit/single_number.py) - - [subsets](bit/subsets.py) - - [add_without_operator](bit/add_without_operator.py) -- [calculator](calculator) - - [math_parser](calculator/math_parser.py) -- [dfs](dfs) - - [all_factors](dfs/all_factors.py) - - [count_islands](dfs/count_islands.py) - - [pacific_atlantic](dfs/pacific_atlantic.py) - - [sudoku_solver](dfs/sudoku_solver.py) - - [walls_and_gates](dfs/walls_and_gates.py) -- [dp](dp) - - [buy_sell_stock](dp/buy_sell_stock.py) - - [climbing_stairs](dp/climbing_stairs.py) - - [combination_sum](dp/combination_sum.py) - - [house_robber](dp/house_robber.py) - - [knapsack](dp/knapsack.py) - - [longest_increasing](dp/longest_increasing.py) - - [max_product_subarray](dp/max_product_subarray.py) - - [max_subarray](dp/max_subarray.py) - - [num_decodings](dp/num_decodings.py) - - [regex_matching](dp/regex_matching.py) - - [word_break](dp/word_break.py) -- [graph](graph) - - [2-sat](graph/satisfiability.py) - - [clone_graph](graph/clone_graph.py) - - [cycle_detection](graph/cycle_detection.py) - - [find_path](graph/find_path.py) - - [graph](graph/graph.py) - - [traversal](graph/traversal.py) - - [markov_chain](graph/markov_chain.py) -- [heap](heap) - - [merge_sorted_k_lists](heap/merge_sorted_k_lists.py) - - [skyline](heap/skyline.py) - - [sliding_window_max](heap/sliding_window_max.py) -- [linkedlist](linkedlist) - - [add_two_numbers](linkedlist/add_two_numbers.py) - - [copy_random_pointer](linkedlist/copy_random_pointer.py) - - [delete_node](linkedlist/delete_node.py) - - [first_cyclic_node](linkedlist/first_cyclic_node.py) - - [is_cyclic](linkedlist/is_cyclic.py) - - [is_palindrome](linkedlist/is_palindrome.py) - - [kth_to_last](linkedlist/kth_to_last.py) - - [linkedlist](linkedlist/linkedlist.py) - - [remove_duplicates](linkedlist/remove_duplicates.py) - - [reverse](linkedlist/reverse.py) - - [rotate_list](linkedlist/rotate_list.py) - - [swap_in_pairs](linkedlist/swap_in_pairs.py) -- [map](map) - - [hashtable](map/hashtable.py) - - [longest_common_subsequence](map/longest_common_subsequence.py) - - [randomized_set](map/randomized_set.py) - - [valid_sudoku](map/valid_sudoku.py) -- [math](math) - - [extended_gcd](math/extended_gcd.py) - - [gcd/lcm](math/gcd.py) - - [prime_test](math/prime_test.py) - - [primes_sieve_of_eratosthenes](math/primes_sieve_of_eratosthenes.py) - - [generate_strobogrammtic](math/generate_strobogrammtic.py) - - [is_strobogrammatic](math/is_strobogrammatic.py) - - [nth_digit](math/nth_digit.py) - - [rabin_miller](math/rabin_miller.py) - - [rsa](math/rsa.py) - - [sqrt_precision_factor](math/sqrt_precision_factor.py) - - [pythagoras](math/pythagoras.py) -- [matrix](matrix) - - [matrix_rotation.txt](matrix/matrix_rotation.txt) - - [copy_transform](matrix/copy_transform.py) - - [bomb_enemy](matrix/bomb_enemy.py) - - [rotate_image](matrix/rotate_image.py) - - [sparse_dot_vector](matrix/sparse_dot_vector.py) - - [sparse_mul](matrix/sparse_mul.py) - - [spiral_traversal](matrix/spiral_traversal.py) - - [count_paths](matrix/count_paths.py) -- [queue](queue) - - [max_sliding_window](queue/max_sliding_window.py) - - [moving_average](queue/moving_average.py) - - [queue](queue/queue.py) - - [reconstruct_queue](queue/reconstruct_queue.py) - - [zigzagiterator](queue/zigzagiterator.py) -- [search](search) - - [binary_search](search/binary_search.py) - - [count_elem](search/count_elem.py) - - [first_occurance](search/first_occurance.py) - - [last_occurance](search/last_occurance.py) -- [set](set) - - [randomized_set](set/randomized_set.py) -- [sort](sort) - - [bubble_sort](sort/bubble_sort.py) - - [comb_sort](sort/comb_sort.py) - - [counting_sort](sort/counting_sort.py) - - [heap_sort](sort/heap_sort.py) - - [insertion_sort](sort/insertion_sort.py) - - [meeting_rooms](sort/meeting_rooms.py) - - [merge_sort](sort/merge_sort.py) - - [quick_sort](sort/quick_sort.py) - - [selection_sort](sort/selection_sort.py) - - [sort_colors](sort/sort_colors.py) - - [topsort](sort/topsort.py) - - [wiggle_sort](sort/wiggle_sort.py) -- [stack](stack) - - [longest_abs_path](stack/longest_abs_path.py) - - [simplify_path](stack/simplify_path.py) - - [stack](stack/stack.py) - - [valid_parenthesis](stack/valid_parenthesis.py) -- [string](string) - - [add_binary](string/add_binary.py) - - [breaking_bad](string/breaking_bad.py) - - [decode_string](string/decode_string.py) - - [encode_decode](string/encode_decode.py) - - [group_anagrams](string/group_anagrams.py) - - [int_to_roman](string/int_to_roman.py) - - [is_palindrome](string/is_palindrome.py) - - [license_number](string/license_number.py) - - [make_sentence](string/make_sentence.py) - - [multiply_strings](string/multiply_strings.py) - - [one_edit_distance](string/one_edit_distance.py) - - [rabin_karp](string/rabin_karp.py) - - [reverse_string](string/reverse_string.py) - - [reverse_vowel](string/reverse_vowel.py) - - [reverse_words](string/reverse_words.py) - - [roman_to_int](string/roman_to_int.py) - - [word_squares](string/word_squares.py) -- [tree](tree) - - [segment-tree](tree/Segment_Tree) - - [segment_tree](tree/Segment_Tree/segment_tree.py) - - [binary_tree_paths](tree/binary_tree_paths.py) - - [bintree2list](tree/bintree2list.py) - - [bst](tree/tree/bst) - - [array2bst](tree/bst/array2bst.py) - - [bst_closest_value](tree/bst/bst_closest_value.py) - - [BSTIterator](tree/bst/BSTIterator.py) - - [delete_node](tree/bst/delete_node.py) - - [is_bst](tree/bst/is_bst.py) - - [kth_smallest](tree/bst/kth_smallest.py) - - [lowest_common_ancestor](tree/bst/lowest_common_ancestor.py) - - [predecessor](tree/bst/predecessor.py) - - [serialize_deserialize](tree/bst/serialize_deserialize.py) - - [successor](tree/bst/successor.py) - - [unique_bst](tree/bst/unique_bst.py) - - [deepest_left](tree/deepest_left.py) - - [invert_tree](tree/invert_tree.py) - - [is_balanced](tree/is_balanced.py) - - [is_subtree](tree/is_subtree.py) - - [is_symmetric](tree/is_symmetric.py) - - [longest_consecutive](tree/longest_consecutive.py) - - [lowest_common_ancestor](tree/lowest_common_ancestor.py) - - [max_height](tree/max_height.py) - - [max_path_sum](tree/max_path_sum.py) - - [min_height](tree/min_height.py) - - [path_sum2](tree/path_sum2.py) - - [path_sum](tree/path_sum.py) - - [pretty_print](tree/pretty_print.py) - - [same_tree](tree/same_tree.py) - - [traversal](tree/traversal) - - [inorder](tree/traversal/inorder.py) - - [level_order](tree/traversal/level_order.py) - - [zigzag](tree/traversal/zigzag.py) - - [tree](tree/tree.py) - - [trie](tree/trie) - - [add_and_search](tree/trie/add_and_search.py) - - [trie](tree/trie/trie.py) -- [union-find](union-find) - - [count_islands](union-find/count_islands.py) +- [arrays](algorithms/arrays) + - [delete_nth](algorithms/arrays/delete_nth.py) + - [flatten](algorithms/arrays/flatten.py) + - [garage](algorithms/arrays/garage.py) + - [josephus_problem](algorithms/arrays/josephus.py) + - [limit](algorithms/arrays/limit.py) + - [longest_non_repeat](algorithms/arrays/longest_non_repeat.py/) + - [max_ones_index](algorithms/arrays/max_ones_index.py) + - [merge_intervals](algorithms/arrays/merge_intervals.py) + - [missing_ranges](algorithms/arrays/missing_ranges.py) + - [plus_one](algorithms/arrays/plus_one.py) + - [rotate](algorithms/arrays/rotate.py) + - [summarize_ranges](algorithms/arrays/summarize_ranges.py) + - [three_sum](algorithms/arrays/three_sum.py) + - [trimmean](algorithms/arrays/trimmean.py) + - [top_1](algorithms/arrays/top_1.py) + - [two_sum](algorithms/arrays/two_sum.py) + - [move_zeros](algorithms/arrays/move_zeros.py) + - [n_sum](algorithms/arrays/n_sum.py) +- [backtrack](algorithms/backtrack) + - [general_solution.md](algorithms/backtrack/) + - [anagram](algorithms/backtrack/anagram.py) + - [array_sum_combinations](algorithms/backtrack/array_sum_combinations.py) + - [combination_sum](algorithms/backtrack/combination_sum.py) + - [expression_add_operators](algorithms/backtrack/expression_add_operators.py) + - [factor_combinations](algorithms/backtrack/factor_combinations.py) + - [generate_abbreviations](algorithms/backtrack/generate_abbreviations.py) + - [generate_parenthesis](algorithms/backtrack/generate_parenthesis.py) + - [letter_combination](algorithms/backtrack/letter_combination.py) + - [palindrome_partitioning](algorithms/backtrack/palindrome_partitioning.py) + - [pattern_match](algorithms/backtrack/pattern_match.py) + - [permute](algorithms/backtrack/permute.py) + - [permute_unique](algorithms/backtrack/permute_unique.py) + - [subsets](algorithms/backtrack/subsets.py) + - [subsets_unique](algorithms/backtrack/subsets_unique.py) +- [bfs](algorithms/bfs) + - [maze_search](algorithms/bfs/maze_search.py) + - [shortest_distance_from_all_buildings](algorithms/bfs/shortest_distance_from_all_buildings.py) + - [word_ladder](algorithms/bfs/word_ladder.py) +- [bit](algorithms/bit) + - [add_bitwise_operator](algorithms/bit/add_bitwise_operator.py) + - [bit_operation](algorithms/bit/bit_operation.py) + - [bytes_int_conversion](algorithms/bit/bytes_int_conversion.py) + - [count_flips_to_convert](algorithms/bit/count_flips_to_convert.py) + - [count_ones](algorithms/bit/count_ones.py) + - [find_difference](algorithms/bit/find_difference.py) + - [find_missing_number](algorithms/bit/find_missing_number.py) + - [flip_bit_longest_sequence](algorithms/bit/flip_bit_longest_sequence.py) + - [power_of_two](algorithms/bit/power_of_two.py) + - [reverse_bits](algorithms/bit/reverse_bits.py) + - [single_number](algorithms/bit/single_number.py) + - [single_number2](algorithms/bit/single_number2.py) + - [single_number3](algorithms/bit/single_number3.py) + - [subsets](algorithms/bit/subsets.py) + - [swap_pair](algorithms/bit/swap_pair.py) + - [has_alternative_bit](algorithms/bit/has_alternative_bit.py) + - [insert_bit](algorithms/bit/insert_bit.py) + - [remove_bit](algorithms/bit/remove_bit.py) + - [binary_gap](algorithms/bit/binary_gap.py) +- [calculator](algorithms/calculator) + - [math_parser](algorithms/calculator/math_parser.py) +- [dfs](algorithms/dfs) + - [all_factors](algorithms/dfs/all_factors.py) + - [count_islands](algorithms/dfs/count_islands.py) + - [pacific_atlantic](algorithms/dfs/pacific_atlantic.py) + - [sudoku_solver](algorithms/dfs/sudoku_solver.py) + - [walls_and_gates](algorithms/dfs/walls_and_gates.py) +- [dp](algorithms/dp) + - [buy_sell_stock](algorithms/dp/buy_sell_stock.py) + - [climbing_stairs](algorithms/dp/climbing_stairs.py) + - [coin_change](algorithms/dp/coin_change.py) + - [combination_sum](algorithms/dp/combination_sum.py) + - [egg_drop](algorithms/dp/egg_drop.py) + - [house_robber](algorithms/dp/house_robber.py) + - [job_scheduling](algorithms/dp/job_scheduling.py) + - [knapsack](algorithms/dp/knapsack.py) + - [longest_increasing](algorithms/dp/longest_increasing.py) + - [matrix_chain_order](algorithms/dp/matrix_chain_order.py) + - [max_product_subarray](algorithms/dp/max_product_subarray.py) + - [max_subarray](algorithms/dp/max_subarray.py) + - [min_cost_path](algorithms/dp/min_cost_path.py) + - [num_decodings](algorithms/dp/num_decodings.py) + - [regex_matching](algorithms/dp/regex_matching.py) + - [rod_cut](algorithms/dp/rod_cut.py) + - [word_break](algorithms/dp/word_break.py) + - [fibonacci](algorithms/dp/fib.py) +- [graph](algorithms/graph) + - [check_bipartite](algorithms/graph/check_bipartite.py) + - [strongly_connected](algorithms/graph/checkDiGraphStronglyConnected.py) + - [clone_graph](algorithms/graph/clone_graph.py) + - [cycle_detection](algorithms/graph/cycle_detection.py) + - [find_all_cliques](algorithms/graph/find_all_cliques.py) + - [find_path](algorithms/graph/find_path.py) + - [graph](algorithms/graph/graph.py) + - [markov_chain](algorithms/graph/markov_chain.py) + - [minimum_spanning_tree](algorithms/graph/minimum_spanning_tree.py) + - [satisfiability](algorithms/graph/satisfiability.py) + - [tarjan](algorithms/graph/tarjan.py) + - [traversal](algorithms/graph/traversal.py) +- [heap](algorithms/heap) + - [merge_sorted_k_lists](algorithms/heap/merge_sorted_k_lists.py) + - [skyline](algorithms/heap/skyline.py) + - [sliding_window_max](algorithms/heap/sliding_window_max.py) + - [binary_heap](algorithms/heap/binary_heap.py) +- [linkedlist](algorithms/linkedlist) + - [add_two_numbers](algorithms/linkedlist/add_two_numbers.py) + - [copy_random_pointer](algorithms/linkedlist/copy_random_pointer.py) + - [delete_node](algorithms/linkedlist/delete_node.py) + - [first_cyclic_node](algorithms/linkedlist/first_cyclic_node.py) + - [is_cyclic](algorithms/linkedlist/is_cyclic.py) + - [is_palindrome](algorithms/linkedlist/is_palindrome.py) + - [kth_to_last](algorithms/linkedlist/kth_to_last.py) + - [linkedlist](algorithms/linkedlist/linkedlist.py) + - [remove_duplicates](algorithms/linkedlist/remove_duplicates.py) + - [reverse](algorithms/linkedlist/reverse.py) + - [rotate_list](algorithms/linkedlist/rotate_list.py) + - [swap_in_pairs](algorithms/linkedlist/swap_in_pairs.py) + - [is_sorted](algorithms/linkedlist/is_sorted.py) + - [remove_range](algorithms/linkedlist/remove_range.py) +- [map](algorithms/map) + - [hashtable](algorithms/map/hashtable.py) + - [separate_chaining_hashtable](algorithms/map/separate_chaining_hashtable.py) + - [longest_common_subsequence](algorithms/map/longest_common_subsequence.py) + - [randomized_set](algorithms/map/randomized_set.py) + - [valid_sudoku](algorithms/map/valid_sudoku.py) +- [maths](algorithms/maths) + - [base_conversion](algorithms/maths/base_conversion.py) + - [combination](algorithms/maths/combination.py) + - [decimal_to_binary_ip](algorithms/maths/decimal_to_binary_ip.py) + - [extended_gcd](algorithms/maths/extended_gcd.py) + - [factorial](algorithms/maths/factorial.py) + - [gcd/lcm](algorithms/maths/gcd.py) + - [generate_strobogrammtic](algorithms/maths/generate_strobogrammtic.py) + - [is_strobogrammatic](algorithms/maths/is_strobogrammatic.py) + - [modular_exponential](algorithms/maths/modular_exponential.py) + - [next_bigger](algorithms/maths/next_bigger.py) + - [next_perfect_square](algorithms/maths/next_perfect_square.py) + - [nth_digit](algorithms/maths/nth_digit.py) + - [prime_check](algorithms/maths/prime_check.py) + - [primes_sieve_of_eratosthenes](algorithms/maths/primes_sieve_of_eratosthenes.py) + - [pythagoras](algorithms/maths/pythagoras.py) + - [rabin_miller](algorithms/maths/rabin_miller.py) + - [rsa](algorithms/maths/rsa.py) + - [sqrt_precision_factor](algorithms/maths/sqrt_precision_factor.py) + - [summing_digits](algorithms/maths/summing_digits.py) +- [matrix](algorithms/matrix) + - [sudoku_validator](algorithms/matrix/sudoku_validator.py) + - [bomb_enemy](algorithms/matrix/bomb_enemy.py) + - [copy_transform](algorithms/matrix/copy_transform.py) + - [count_paths](algorithms/matrix/count_paths.py) + - [matrix_rotation.txt](algorithms/matrix/matrix_rotation.txt) + - [rotate_image](algorithms/matrix/rotate_image.py) + - [search_in_sorted_matrix](algorithms/matrix/search_in_sorted_matrix.py) + - [sparse_dot_vector](algorithms/matrix/sparse_dot_vector.py) + - [sparse_mul](algorithms/matrix/sparse_mul.py) + - [spiral_traversal](algorithms/matrix/spiral_traversal.py) +- [queues](algorithms/queues) + - [max_sliding_window](algorithms/queues/max_sliding_window.py) + - [moving_average](algorithms/queues/moving_average.py) + - [queue](algorithms/queues/queue.py) + - [reconstruct_queue](algorithms/queues/reconstruct_queue.py) + - [zigzagiterator](algorithms/queues/zigzagiterator.py) +- [search](algorithms/search) + - [binary_search](algorithms/search/binary_search.py) + - [first_occurance](algorithms/search/first_occurance.py) + - [last_occurance](algorithms/search/last_occurance.py) + - [linear_search](algorithms/search/linear_search.py) + - [search_insert](algorithms/search/search_insert.py) + - [two_sum](algorithms/search/two_sum.py) + - [search_range](algorithms/search/search_range.py) + - [find_min_rotate](algorithms/search/find_min_rotate.py) + - [search_rotate](algorithms/search/search_rotate.py) + - [jump_search](algorithms/search/jump_search.py) + - [next_greatest_letter](algorithms/search/next_greatest_letter.py) +- [set](algorithms/set) + - [randomized_set](algorithms/set/randomized_set.py) + - [set_covering](algorithms/set/set_covering.py) +- [sort](algorithms/sort) + - [bitonic_sort](algorithms/sort/bitonic_sort.py) + - [bogo_sort](algorithms/sort/bogo_sort.py) + - [bubble_sort](algorithms/sort/bubble_sort.py) + - [bucket_sort](algorithms/sort/bucket_sort.py) + - [cocktail_shaker_sort](algorithms/sort/cocktail_shaker_sort.py) + - [comb_sort](algorithms/sort/comb_sort.py) + - [counting_sort](algorithms/sort/counting_sort.py) + - [cycle_sort](algorithms/sort/cycle_sort.py) + - [gnome_sort](algorithms/sort/gnome_sort.py) + - [heap_sort](algorithms/sort/heap_sort.py) + - [insertion_sort](algorithms/sort/insertion_sort.py) + - [meeting_rooms](algorithms/sort/meeting_rooms.py) + - [merge_sort](algorithms/sort/merge_sort.py) + - [pancake_sort](algorithms/sort/pancake_sort.py) + - [quick_sort](algorithms/sort/quick_sort.py) + - [radix_sort](algorithms/sort/radix_sort.py) + - [selection_sort](algorithms/sort/selection_sort.py) + - [shell_sort](algorithms/sort/shell_sort.py) + - [sort_colors](algorithms/sort/sort_colors.py) + - [top_sort](algorithms/sort/top_sort.py) + - [wiggle_sort](algorithms/sort/wiggle_sort.py) +- [stack](algorithms/stack) + - [longest_abs_path](algorithms/stack/longest_abs_path.py) + - [simplify_path](algorithms/stack/simplify_path.py) + - [stack](algorithms/stack/stack.py) + - [valid_parenthesis](algorithms/stack/valid_parenthesis.py) + - [stutter](algorithms/stack/stutter.py) + - [switch_pairs](algorithms/stack/switch_pairs.py) + - [is_consecutive](algorithms/stack/is_consecutive.py) + - [remove_min](algorithms/stack/remove_min.py) + - [is_sorted](algorithms/stack/is_sorted.py) +- [strings](algorithms/strings) + - [fizzbuzz](algorithms/strings/fizzbuzz.py) + - [delete_reoccurring_characters](algorithms/strings/delete_reoccurring_characters.py) + - [strip_url_params](algorithms/strings/strip_url_params.py) + - [validate_coordinates](algorithms/strings/validate_coordinates.py) + - [domain_extractor](algorithms/strings/domain_extractor.py) + - [merge_string_checker](algorithms/strings/merge_string_checker.py) + - [add_binary](algorithms/strings/add_binary.py) + - [breaking_bad](algorithms/strings/breaking_bad.py) + - [decode_string](algorithms/strings/decode_string.py) + - [encode_decode](algorithms/strings/encode_decode.py) + - [group_anagrams](algorithms/strings/group_anagrams.py) + - [int_to_roman](algorithms/strings/int_to_roman.py) + - [is_palindrome](algorithms/strings/is_palindrome.py) + - [license_number](algorithms/strings/license_number.py) + - [make_sentence](algorithms/strings/make_sentence.py) + - [multiply_strings](algorithms/strings/multiply_strings.py) + - [one_edit_distance](algorithms/strings/one_edit_distance.py) + - [rabin_karp](algorithms/strings/rabin_karp.py) + - [reverse_string](algorithms/strings/reverse_string.py) + - [reverse_vowel](algorithms/strings/reverse_vowel.py) + - [reverse_words](algorithms/strings/reverse_words.py) + - [roman_to_int](algorithms/strings/roman_to_int.py) + - [word_squares](algorithms/strings/word_squares.py) + - [unique_morse](algorithms/strings/unique_morse.py) + - [judge_circle](algorithms/strings/judge_circle.py) + - [strong_password](algorithms/strings/strong_password.py) + - [caesar_cipher](algorithms/strings/caesar_cipher.py) + - [contain_string](algorithms/strings/contain_string.py) + - [count_binary_substring](algorithms/strings/count_binary_substring.py) + - [repeat_string](algorithms/strings/repeat_string.py) + - [min_distance](algorithms/strings/min_distance.py) + - [longest_common_prefix](algorithms/strings/longest_common_prefix.py) + - [rotate](algorithms/strings/rotate.py) + - [first_unique_char](algorithms/strings/first_unique_char.py) + - [repeat_substring](algorithms/strings/repeat_substring.py) +- [tree](algorithms/tree) + - [bst](algorithms/tree/tree/bst) + - [array2bst](algorithms/tree/bst/array2bst.py) + - [bst_closest_value](algorithms/tree/bst/bst_closest_value.py) + - [BSTIterator](algorithms/tree/bst/BSTIterator.py) + - [delete_node](algorithms/tree/bst/delete_node.py) + - [is_bst](algorithms/tree/bst/is_bst.py) + - [kth_smallest](algorithms/tree/bst/kth_smallest.py) + - [lowest_common_ancestor](algorithms/tree/bst/lowest_common_ancestor.py) + - [predecessor](algorithms/tree/bst/predecessor.py) + - [serialize_deserialize](algorithms/tree/bst/serialize_deserialize.py) + - [successor](algorithms/tree/bst/successor.py) + - [unique_bst](algorithms/tree/bst/unique_bst.py) + - [depth_sum](algorithms/tree/bst/depth_sum.py) + - [count_left_node](algorithms/tree/bst/count_left_node.py) + - [num_empty](algorithms/tree/bst/num_empty.py) + - [height](algorithms/tree/bst/height.py) + - [red_black_tree](algorithms/tree/red_black_tree) + - [red_black_tree](algorithms/tree/red_black_tree/red_black_tree.py) + - [segment_tree](algorithms/tree/segment_tree) + - [segment_tree](algorithms/tree/segment_tree/segment_tree.py) + - [traversal](algorithms/tree/traversal) + - [inorder](algorithms/tree/traversal/inorder.py) + - [level_order](algorithms/tree/traversal/level_order.py) + - [postorder](algorithms/tree/traversal/postorder.py) + - [preorder](algorithms/tree/traversal/preorder.py) + - [zigzag](algorithms/tree/traversal/zigzag.py) + - [trie](algorithms/tree/trie) + - [add_and_search](algorithms/tree/trie/add_and_search.py) + - [trie](algorithms/tree/trie/trie.py) + - [binary_tree_paths](algorithms/tree/binary_tree_paths.py) + - [bintree2list](algorithms/tree/bintree2list.py) + - [deepest_left](algorithms/tree/deepest_left.py) + - [invert_tree](algorithms/tree/invert_tree.py) + - [is_balanced](algorithms/tree/is_balanced.py) + - [is_subtree](algorithms/tree/is_subtree.py) + - [is_symmetric](algorithms/tree/is_symmetric.py) + - [longest_consecutive](algorithms/tree/longest_consecutive.py) + - [lowest_common_ancestor](algorithms/tree/lowest_common_ancestor.py) + - [max_height](algorithms/tree/max_height.py) + - [max_path_sum](algorithms/tree/max_path_sum.py) + - [min_height](algorithms/tree/min_height.py) + - [path_sum](algorithms/tree/path_sum.py) + - [path_sum2](algorithms/tree/path_sum2.py) + - [pretty_print](algorithms/tree/pretty_print.py) + - [same_tree](algorithms/tree/same_tree.py) + - [tree](algorithms/tree/tree.py) +- [unix](algorithms/unix) + - [path](algorithms/unix/path/) + - [join_with_slash](algorithms/unix/path/join_with_slash.py) + - [full_path](algorithms/unix/path/full_path.py) + - [split](algorithms/unix/path/split.py) + - [simplify_path](algorithms/unix/path/simplify_path.py) +- [union-find](algorithms/union-find) + - [count_islands](algorithms/union-find/count_islands.py) +- [machine-learning](algorithms/machine-learning) + - [nearest neighbor classification](algorithms/machine-learning/nearest_neighbor.py) + +## Contributors +The repo is maintained by + +* [Keon Kim](https://github.com/keon) +* [Rahul Goswami](https://github.com/goswami-rahul) +* [Christian Bender](https://github.com/christianbender) +* [Ankit Agarwal](https://github.com/ankit167) +* [Hai Hoang Dang](https://github.com/danghai) +* [Saad](https://github.com/SaadBenn) +And thanks to [all the contributors](https://github.com/keon/algorithms/graphs/contributors) +who helped in building the repo. diff --git a/README_CN.md b/README_CN.md new file mode 100644 index 000000000..4dc5cfcc7 --- /dev/null +++ b/README_CN.md @@ -0,0 +1,317 @@ +

+ +[English](README.md) | 简体中文 | [Deutsch](README_GE.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Português](README_PTBR.md) + +[![PyPI version](https://badge.fury.io/py/algorithms.svg)](https://badge.fury.io/py/algorithms) +[![Open Source Helpers](https://www.codetriage.com/keon/algorithms/badges/users.svg)](https://www.codetriage.com/keon/algorithms) +[![Build Status](https://travis-ci.org/keon/algorithms.svg?branch=master)](https://travis-ci.org/keon/algorithms) +[![Coverage Status](https://coveralls.io/repos/github/keon/algorithms/badge.svg?branch=master)](https://coveralls.io/github/keon/algorithms?branch=master) + +Python版数据结构和算法 +========================================= + +python版数据结构和算法实现的简约版小示例 + +谢谢关注。有多种方法可以贡献你的代码。[从这里开始吧](https://github.com/keon/algorithms/blob/master/CONTRIBUTING.md) + +[或者可以用不同语言来完成上述算法,期待加入](https://github.com/yunshuipiao/sw-algorithms):https://github.com/yunshuipiao/sw-algorithms + +## 测试 + +### 单元测试 +如下代码可以运行全部测试: +``` + +python3 -m unittest discover tests + +``` + +针对特定模块(比如:sort)的测试, 可以使用如下代码: +``` + +python3 -m unittest tests.test_sort + +``` + +### 使用pytest +如下代码运行所有测试代码: +``` + +python3 -m pytest tests + +``` + +## 安装 +如果想在代码中使用算法API, 可按如下步骤进行: +``` + +pip3 install git+https://github.com/keon/algorithms + +``` + +通过创建python文件(比如:在sort模块使用merge_sort)进行测试: +``` + +from algorithms.sort import merge_sort + +if __name__ == "__main__": + my_list = [1, 8, 3, 5, 6] + my_list = merge_sort(my_list) + print(my_list) + +``` + +## 卸载 +如下代码可卸载该API: + +``` + +pip3 uninstall -y algorithms + +``` + +## 实现列表 + +- [array:数组](arrays) + - [delete_nth: 删除第n项](algorithms/arrays/delete_nth.py) + - [flatten:数组降维](algorithms/arrays/flatten.py) + - [garage:停车场](algorithms/arrays/garage.py) + - [josephus_problem: 约瑟夫问题](algorithms/arrays/josephus.py) + - [max_ones_index](algorithms/arrays/max_ones_index.py) + - [limit](algorithms/arrays/limit.py) + - [longest_non_repeat:最长不重复子串](algorithms/arrays/longest_non_repeat.py/) + - [merge_intervals:合并重叠间隔](algorithms/arrays/merge_intervals.py) + - [missing_ranges:遗失的范围](algorithms/arrays/missing_ranges.py) + - [plus_one:加一运算](algorithms/arrays/plus_one.py) + - [rotate:反转数组](algorithms/arrays/rotate.py) + - [summarize_ranges:数组范围](algorithms/arrays/summarize_ranges.py) + - [three_sum:三数和为零](algorithms/arrays/three_sum.py) + - [trimmean](algorithms/arrays/trimmean.py) + - [top_1](algorithms/arrays/top_1.py) + - [two_sum:两数和](algorithms/arrays/two_sum.py) + - [move_zeros: 0后置问题](algorithms/arrays/move_zeros.py) +- [backtrack:回溯](algorithms/backtrack) + - [general_solution.md:一般方法](algorithms/backtrack/) + - [anagram:同字母异序词](algorithms/backtrack/anagram.py) + - [array_sum_combinations:数组和](algorithms/backtrack/array_sum_combinations.py) + - [combination_sum:和的合并](algorithms/backtrack/combination_sum.py) + - [expression_add_operators:给表达式添加运算符](algorithms/backtrack/expression_add_operators.py) + - [factor_combinations:因素组合](algorithms/backtrack/factor_combinations.py) + - [generate_abbreviations:缩写生成](algorithms/backtrack/generate_abbreviations.py) + - [generate_parenthesis:括号生成](algorithms/backtrack/generate_parenthesis.py) + - [letter_combination:字母组合](algorithms/backtrack/letter_combination.py) + - [palindrome_partitioning:字符串的所有回文子串](algorithms/backtrack/palindrome_partitioning.py) + - [pattern_match:模式匹配](algorithms/backtrack/pattern_match.py) + - [permute:排列](algorithms/backtrack/permute.py) + - [permute_unique:唯一排列](algorithms/backtrack/permute_unique.py) + - [subsets:子集](algorithms/backtrack/subsets.py) + - [subsets_unique:唯一子集](algorithms/backtrack/subsets_unique.py) +- [bfs:广度优先搜索](algorithms/bfs) + - [maze_search](algorithms/bfs/maze_search.py) + - [shortest_distance_from_all_buildings:所有建筑物的最短路径:](algorithms/bfs/shortest_distance_from_all_buildings.py) + - [word_ladder:词语阶梯](algorithms/bfs/word_ladder.py) +- [bit:位操作](algorithms/bit) + - [bytes_int_conversion:字节整数转换](algorithms/bit/bytes_int_conversion.py) + - [count_ones:统计1出现的次数](algorithms/bit/count_ones.py) + - [count_flips_to_convert](algorithms/bit/count_flips_to_convert.py) + - [find_missing_number:寻找缺失数](algorithms/bit/find_missing_number.py) + - [flip_bit_longest_sequence](algorithms/bit/flip_bit_longest_sequence.py) + - [power_of_two:2的n次方数判断](algorithms/bit/power_of_two.py) + - [reverse_bits:反转位](algorithms/bit/reverse_bits.py) + - [single_number2:寻找出现1次的数(2)](algorithms/bit/single_number2.py) + - [single_number:寻找出现1次的数(1)](algorithms/bit/single_number.py) + - [subsets: 求所有子集](algorithms/bit/subsets.py) + - [add_bitwise_operator:无操作符的加法](algorithms/bit/add_bitwise_operator.py) +- [calculator:计算](algorithms/calculator) + - [math_parser: 数字解析](algorithms/calculator/math_parser.py) +- [dfs:深度优先搜索](algorithms/dfs) + - [all_factors:因素分解](algorithms/dfs/all_factors.py) + - [count_islands:岛计数](algorithms/dfs/count_islands.py) + - [pacific_atlantic:太平洋大西洋](algorithms/dfs/pacific_atlantic.py) + - [sudoku_solver:数独解法](algorithms/dfs/sudoku_solver.py) + - [walls_and_gates:墙和门](algorithms/dfs/walls_and_gates.py) +- [dp:动态规划](algorithms/dp) + - [buy_sell_stock:股票买卖](algorithms/dp/buy_sell_stock.py) + - [climbing_stairs:爬梯子问题](algorithms/dp/climbing_stairs.py) + - [combination_sum:和组合问题](algorithms/dp/combination_sum.py) + - [house_robber:打家劫舍](algorithms/dp/house_robber.py) + - [knapsack:背包问题](algorithms/dp/knapsack.py) + - [longest_increasing:最长递增子序列](algorithms/dp/longest_increasing.py) + - [max_product_subarray:最大子数组乘积](algorithms/dp/max_product_subarray.py) + - [max_subarray:最大子数组](algorithms/dp/max_subarray.py) + - [num_decodings:数字解码](algorithms/dp/num_decodings.py) + - [regex_matching:正则匹配](algorithms/dp/regex_matching.py) + - [word_break:单词分割](algorithms/dp/word_break.py) +- [graph:图](graph) + - [check_bipartite](algorithms/graph/check_bipartite.py) + - [2-sat:2-sat](algorithms/graph/satisfiability.py) + - [clone_graph:克隆图](algorithms/graph/clone_graph.py) + - [cycle_detection:判断圈算法](algorithms/graph/cycle_detection.py) + - [find_path:发现路径](algorithms/graph/find_path.py) + - [graph:图](algorithms/graph/graph.py) + - [traversal:遍历](algorithms/graph/traversal.py) + - [markov_chain:马尔可夫链](algorithms/graph/markov_chain.py) +- [heap:堆](algorithms/heap) + - [merge_sorted_k_lists:合并k个有序链](algorithms/heap/merge_sorted_k_lists.py) + - [skyline:天际线](algorithms/heap/skyline.py) + - [sliding_window_max:滑动窗口最大值](algorithms/heap/sliding_window_max.py) +- [linkedlist:链表](algorithms/linkedlist) + - [add_two_numbers:链表数相加](algorithms/linkedlist/add_two_numbers.py) + - [copy_random_pointer:复制带有随机指针的链表](algorithms/linkedlist/copy_random_pointer.py) + - [delete_node:删除节点](algorithms/linkedlist/delete_node.py) + - [first_cyclic_node:环链表的第一个节点](algorithms/linkedlist/first_cyclic_node.py) + - [is_cyclic:判断环链表](algorithms/linkedlist/is_cyclic.py) + - [is_palindrome:回文链表](algorithms/linkedlist/is_palindrome.py) + - [kth_to_last:倒数第k个节点](algorithms/linkedlist/kth_to_last.py) + - [linkedlist: 链表](algorithms/linkedlist/linkedlist.py) + - [remove_duplicates:删除重复元素](algorithms/linkedlist/remove_duplicates.py) + - [reverse:反转链表](algorithms/linkedlist/reverse.py) + - [rotate_list:旋转链表](algorithms/linkedlist/rotate_list.py) + - [swap_in_pairs:链表节点交换](algorithms/linkedlist/swap_in_pairs.py) +- [map:映射](algorithms/map) + - [hashtable:哈希表](algorithms/map/hashtable.py) + - [separate_chaining_hashtable:拉链法哈希表](algorithms/map/separate_chaining_hashtable.py) + - [longest_common_subsequence:最长公共子序列](algorithms/map/longest_common_subsequence.py) + - [randomized_set:随机集](algorithms/map/randomized_set.py) + - [valid_sudoku:有效数独](algorithms/map/valid_sudoku.py) +- [math:数学问题](algorithms/maths) + - [extended_gcd:扩展欧几里得算法](algorithms/maths/extended_gcd.py) + - [combination](algorithms/maths/combination.py) + - [factorial](algorithms/maths/factorial.py) + - [gcd/lcm:最大公约数和最小公倍数](algorithms/maths/gcd.py) + - [prime_test:主要测试](algorithms/maths/prime_test.py) + - [primes_sieve_of_eratosthenes:埃拉托色尼的质数筛](algorithms/maths/primes_sieve_of_eratosthenes.py) + - [generate_strobogrammtic:生成对称数](algorithms/maths/generate_strobogrammtic.py) + - [is_strobogrammatic:判断对称数](algorithms/maths/is_strobogrammatic.py) + - [modular_exponential](algorithms/maths/modular_exponential.py) + - [nth_digit:第n位](algorithms/maths/nth_digit.py) + - [rabin_miller:米勒-拉宾素性检验](algorithms/maths/rabin_miller.py) + - [rsa:rsa加密](algorithms/maths/rsa.py) + - [sqrt_precision_factor:开发精度因素](algorithms/maths/sqrt_precision_factor.py) + - [pythagoras:毕达哥拉斯](algorithms/maths/pythagoras.py) +- [matrix:矩阵](algorithms/matrix) + - [matrix_rotation:矩阵旋转](algorithms/matrix/matrix_rotation.txt) + - [copy_transform:复制变换](algorithms/matrix/copy_transform.py) + - [bomb_enemy:炸弹人](algorithms/matrix/bomb_enemy.py) + - [rotate_image:旋转图像](algorithms/matrix/rotate_image.py) + - [sparse_dot_vector:解析点向量](algorithms/matrix/sparse_dot_vector.py) + - [sparse_mul:稀疏矩阵](algorithms/matrix/sparse_mul.py) + - [spiral_traversal:循环遍历](algorithms/matrix/spiral_traversal.py) + - [count_paths:计算路径](algorithms/matrix/count_paths.py) +- [queue:队列](algorithms/queues) + - [max_sliding_window:最大移动窗口](algorithms/queues/max_sliding_window.py) + - [moving_average:移动平均](algorithms/queues/moving_average.py) + - [queue:队列](algorithms/queues/queue.py) + - [reconstruct_queue:重建队列](algorithms/queues/reconstruct_queue.py) + - [zigzagiterator:锯齿形迭代](algorithms/queues/zigzagiterator.py) +- [search:查找](algorithms/search) + - [binary_search:二分查找](algorithms/search/binary_search.py) + - [count_elem:元素计数](algorithms/search/count_elem.py) + - [first_occurance:首次出现](algorithms/search/first_occurance.py) + - [last_occurance:最后一次出现](algorithms/search/last_occurance.py) + - [linear_search](algorithms/search/linear_search.py) + - [jump_search](algorithms/search/jump_search.py) +- [set:集合](algorithms/set) + - [randomized_set:随机集合](algorithms/set/randomized_set.py) + - [set_covering:集合覆盖](algorithms/set/set_covering.py) +- [sort:排序](algorithms/sort) + - [bitonic_sort](algorithms/sort/bitonic_sort.py) + - [bogo_sort](algorithms/sort/bogo_sort.py) + - [bubble_sort:冒泡排序](algorithms/sort/bubble_sort.py) + - [bucket_sort](algorithms/sort/bucket_sort.py) + - [cocktail_shaker_sort](algorithms/sort/cocktail_shaker_sort.py) + - [comb_sort:梳排序](algorithms/sort/comb_sort.py) + - [counting_sort:计数排序](algorithms/sort/counting_sort.py) + - [cycle_sort](algorithms/sort/cycle_sort.py) + - [gnome_sort](algorithms/sort/gnome_sort.py) + - [heap_sort:堆排序](algorithms/sort/heap_sort.py) + - [insertion_sort:插入排序](algorithms/sort/insertion_sort.py) + - [meeting_rooms:会议室](algorithms/sort/meeting_rooms.py) + - [merge_sort:归并排序](algorithms/sort/merge_sort.py) + - [pancake_sort](algorithms/sort/pancake_sort.py) + - [quick_sort:快速排序](algorithms/sort/quick_sort.py) + - [radix_sort](algorithms/sort/radix_sort.py) + - [selection_sort:选择排序](algorithms/sort/selection_sort.py) + - [shell_sort](algorithms/sort/shell_sort.py) + - [sort_colors:颜色排序](algorithms/sort/sort_colors.py) + - [top_sort:top排序](algorithms/sort/top_sort.py) + - [wiggle_sort:摇摆排序](algorithms/sort/wiggle_sort.py) +- [stack:栈](algorithms/stack) + - [longest_abs_path:最长相对路径](algorithms/stack/longest_abs_path.py) + - [simplify_path:简化路径](algorithms/stack/simplify_path.py) + - [stack:栈](algorithms/stack/stack.py) + - [valid_parenthesis:验证括号](algorithms/stack/valid_parenthesis.py) +- [string:字符串](algorithms/strings) + - [add_binary:二进制数相加](algorithms/strings/add_binary.py) + - [breaking_bad:打破坏](algorithms/strings/breaking_bad.py) + - [decode_string:字符串编码](algorithms/strings/decode_string.py) + - [encode_decode:编解码](algorithms/strings/encode_decode.py) + - [group_anagrams:群组错位词](algorithms/strings/group_anagrams.py) + - [int_to_roman:整数转换罗马数字](algorithms/strings/int_to_roman.py) + - [is_palindrome:回文字符串](algorithms/strings/is_palindrome.py) + - [license_number:拍照号码](algorithms/strings/license_number.py) + - [make_sentence:造句](algorithms/strings/make_sentence.py) + - [multiply_strings:字符串相乘](algorithms/strings/multiply_strings.py) + - [one_edit_distance:一个编辑距离](algorithms/strings/one_edit_distance.py) + - [rabin_karp:Rabin-Karp 算法](algorithms/strings/rabin_karp.py) + - [reverse_string:反转字符串](algorithms/strings/reverse_string.py) + - [reverse_vowel:反转元音](algorithms/strings/reverse_vowel.py) + - [reverse_words:反转单词](algorithms/strings/reverse_words.py) + - [roman_to_int:罗马数转换整数](algorithms/strings/roman_to_int.py) + - [word_squares:单词平方](algorithms/strings/word_squares.py) +- [tree:树](algorithms/tree) + - [segment-tree:线段树](algorithms/tree/segment_tree) + - [segment_tree:线段树](algorithms/tree/segment_tree/segment_tree.py) + - [binary_tree_paths:二叉树路径](algorithms/tree/binary_tree_paths.py) + - [bintree2list:二叉树转换链表](algorithms/tree/bintree2list.py) + - [bst:二叉搜索树](algorithms/tree/tree/bst) + - [array2bst:数组转换](algorithms/tree/bst/array2bst.py) + - [bst_closest_value:最近二叉搜索树值](algorithms/tree/bst/bst_closest_value.py) + - [BSTIterator:二叉搜索树迭代](algorithms/tree/bst/BSTIterator.py) + - [delete_node:删除节点](algorithms/tree/bst/delete_node.py) + - [is_bst:判断二叉搜索树](algorithms/tree/bst/is_bst.py) + - [kth_smallest:二叉搜索树的第k小节点](algorithms/tree/bst/kth_smallest.py) + - [lowest_common_ancestor:最近公共祖先](algorithms/tree/bst/lowest_common_ancestor.py) + - [predecessor:前任](algorithms/tree/bst/predecessor.py) + - [serialize_deserialize:序列化反序列化](algorithms/tree/bst/serialize_deserialize.py) + - [successor:继承者](algorithms/tree/bst/successor.py) + - [unique_bst:唯一BST](algorithms/tree/bst/unique_bst.py) + - [deepest_left:最深叶子节点](algorithms/tree/deepest_left.py) + - [invert_tree:反转树](algorithms/tree/invert_tree.py) + - [is_balanced:判断平衡树](algorithms/tree/is_balanced.py) + - [is_subtree:判断子树](algorithms/tree/is_subtree.py) + - [is_symmetric:判断对称树](algorithms/tree/is_symmetric.py) + - [longest_consecutive:最长连续节点](algorithms/tree/longest_consecutive.py) + - [lowest_common_ancestor:最近公共祖先](algorithms/tree/lowest_common_ancestor.py) + - [max_height:最大高度](algorithms/tree/max_height.py) + - [max_path_sum:最长路径和](algorithms/tree/max_path_sum.py) + - [min_height:最小高度](algorithms/tree/min_height.py) + - [path_sum2:路径和2](algorithms/tree/path_sum2.py) + - [path_sum:路径和](algorithms/tree/path_sum.py) + - [pretty_print:完美打印](algorithms/tree/pretty_print.py) + - [same_tree:相同树](algorithms/tree/same_tree.py) + - [traversal:遍历](algorithms/tree/traversal) + - [inorder:中序遍历](algorithms/tree/traversal/inorder.py) + - [level_order:层次遍历](algorithms/tree/traversal/level_order.py) + - [postorder](algorithms/tree/traversal/postorder.py) + - [preorder](algorithms/tree/traversal/preorder.py) + - [zigzag:锯齿形遍历](algorithms/tree/traversal/zigzag.py) + - [tree:树](algorithms/tree/tree.py) + - [trie:字典树](algorithms/tree/trie) + - [add_and_search:添加和查找](algorithms/tree/trie/add_and_search.py) + - [trie:字典](algorithms/tree/trie/trie.py) +- [union-find:并查集](algorithms/union-find) + - [count_islands:岛计数](algorithms/union-find/count_islands.py) + + +## 贡献 +谢谢主要维护人员: + +* [Keon Kim](https://github.com/keon) +* [Rahul Goswami](https://github.com/goswami-rahul) +* [Christian Bender](https://github.com/christianbender) +* [Ankit Agarwal](https://github.com/ankit167) +* [Hai Hoang Dang](https://github.com/danghai) +* [Saad](https://github.com/SaadBenn) + +以及[所有贡献者](https://github.com/keon/algorithms/graphs/contributors) diff --git a/README_GE.md b/README_GE.md new file mode 100644 index 000000000..be908bc28 --- /dev/null +++ b/README_GE.md @@ -0,0 +1,356 @@ +

+ +[English](README.md) | [简体中文](README_CN.md) | Deutsch | [日本語](README_JP.md) | [한국어](README_KR.md) | [Português](README_PTBR.md) + +[![PyPI version](https://badge.fury.io/py/algorithms.svg)](https://badge.fury.io/py/algorithms) +[![Open Source Helpers](https://www.codetriage.com/keon/algorithms/badges/users.svg)](https://www.codetriage.com/keon/algorithms) +[![Build Status](https://travis-ci.org/keon/algorithms.svg?branch=master)](https://travis-ci.org/keon/algorithms) +[![Coverage Status](https://coveralls.io/repos/github/keon/algorithms/badge.svg?branch=master)](https://coveralls.io/github/keon/algorithms?branch=master) + +Pythonische Datenstrukturen und Algorithmen +========================================= + +In diesem Repository finden Sie eine große Auswahl an Algorithmen und Datenstrukturen implementiert in Python 3. + +## Beteiligen + +Sie können sich gerne auch an diesem Projekt beteiligen. Zum Beispiel selbst Algorithmen und Datenstrukturen beisteuern, oder bereits bestehende Implementierungen verbessern, oder auch dokumentieren. Fühlen Sie sich frei und machen Sie einen Pull-Request. Alternativ können Sie auch den Issue-Tracker benutzen um auf Probleme (Bugs) in bereits bestehenden Implementierungen hinzuweisen. + +In diesem Projekt halten wir uns an die [PEP8](https://www.python.org/dev/peps/pep-0008/) Codestyle Konventionen. + +## Tests + +### Benutzen der Unittests + +Um alle Tests laufen zu lassen, tippen Sie die unten stehende Befehlzeile in die Kommandozeile: + + $ python3 -m unittest discover tests + +Um einen besonderen Test laufen zu lassen, tippen Sie folgendes: + + $ python3 -m unittest tests.test_sort + +### Benutzen von pytest + +Zum ausführen aller Tests: + + $ python3 -m pytest tests + +## Install + +Wenn Sie das Projekt installieren wollen, um es als Module in Ihren Projekten nutzen zu können. Dann tippen Sie unten stehende Befehlzeile in die Kommandozeile: + + $ pip3 install git+https://github.com/keon/algorithms + +Sie können die Installation testen in dem Sie unten stehenden Code in eine Datei packen und ausführen. + +```python3 +from algorithms.sort import merge_sort + +if __name__ == "__main__": + my_list = [1, 8, 3, 5, 6] + my_list = merge_sort(my_list) + print(my_list) +``` + +## Uninstall + +Um das Projekt zu deinstallieren tippen Sie folgendes: + + $ pip3 uninstall -y algorithms + + +## Liste von Implementierungen + +- [arrays](algorithms/arrays) + - [delete_nth](algorithms/arrays/delete_nth.py) + - [flatten](algorithms/arrays/flatten.py) + - [garage](algorithms/arrays/garage.py) + - [josephus_problem](algorithms/arrays/josephus.py) + - [limit](algorithms/arrays/limit.py) + - [longest_non_repeat](algorithms/arrays/longest_non_repeat.py/) + - [max_ones_index](algorithms/arrays/max_ones_index.py) + - [merge_intervals](algorithms/arrays/merge_intervals.py) + - [missing_ranges](algorithms/arrays/missing_ranges.py) + - [plus_one](algorithms/arrays/plus_one.py) + - [rotate](algorithms/arrays/rotate.py) + - [summarize_ranges](algorithms/arrays/summarize_ranges.py) + - [three_sum](algorithms/arrays/three_sum.py) + - [trimmean](algorithms/arrays/trimmean.py) + - [top_1](algorithms/arrays/top_1.py) + - [two_sum](algorithms/arrays/two_sum.py) + - [move_zeros](algorithms/arrays/move_zeros.py) +- [backtrack](algorithms/backtrack) + - [general_solution.md](algorithms/backtrack/) + - [anagram](algorithms/backtrack/anagram.py) + - [array_sum_combinations](algorithms/backtrack/array_sum_combinations.py) + - [combination_sum](algorithms/backtrack/combination_sum.py) + - [expression_add_operators](algorithms/backtrack/expression_add_operators.py) + - [factor_combinations](algorithms/backtrack/factor_combinations.py) + - [generate_abbreviations](algorithms/backtrack/generate_abbreviations.py) + - [generate_parenthesis](algorithms/backtrack/generate_parenthesis.py) + - [letter_combination](algorithms/backtrack/letter_combination.py) + - [palindrome_partitioning](algorithms/backtrack/palindrome_partitioning.py) + - [pattern_match](algorithms/backtrack/pattern_match.py) + - [permute](algorithms/backtrack/permute.py) + - [permute_unique](algorithms/backtrack/permute_unique.py) + - [subsets](algorithms/backtrack/subsets.py) + - [subsets_unique](algorithms/backtrack/subsets_unique.py) +- [bfs](algorithms/bfs) + - [maze_search](algorithms/bfs/maze_search.py) + - [shortest_distance_from_all_buildings](algorithms/bfs/shortest_distance_from_all_buildings.py) + - [word_ladder](algorithms/bfs/word_ladder.py) +- [bit](algorithms/bit) + - [bytes_int_conversion](algorithms/bit/bytes_int_conversion.py) + - [count_ones](algorithms/bit/count_ones.py) + - [count_flips_to_convert](algorithms/bit/count_flips_to_convert.py) + - [find_missing_number](algorithms/bit/find_missing_number.py) + - [flip_bit_longest_sequence](algorithms/bit/flip_bit_longest_sequence.py) + - [power_of_two](algorithms/bit/power_of_two.py) + - [reverse_bits](algorithms/bit/reverse_bits.py) + - [single_number](algorithms/bit/single_number.py) + - [single_number2](algorithms/bit/single_number2.py) + - [single_number3](algorithms/bit/single_number3.py) + - [subsets](algorithms/bit/subsets.py) + - [add_bitwise_operator](algorithms/bit/add_bitwise_operator.py) + - [bit_operation](algorithms/bit/bit_operation.py) + - [swap_pair](algorithms/bit/swap_pair.py) + - [find_difference](algorithms/bit/find_difference.py) + - [has_alternative_bit](algorithms/bit/has_alternative_bit.py) + - [insert_bit](algorithms/bit/insert_bit.py) + - [remove_bit](algorithms/bit/remove_bit.py) +- [calculator](algorithms/calculator) + - [math_parser](algorithms/calculator/math_parser.py) +- [dfs](algorithms/dfs) + - [all_factors](algorithms/dfs/all_factors.py) + - [count_islands](algorithms/dfs/count_islands.py) + - [pacific_atlantic](algorithms/dfs/pacific_atlantic.py) + - [sudoku_solver](algorithms/dfs/sudoku_solver.py) + - [walls_and_gates](algorithms/dfs/walls_and_gates.py) +- [dp](algorithms/dp) + - [buy_sell_stock](algorithms/dp/buy_sell_stock.py) + - [climbing_stairs](algorithms/dp/climbing_stairs.py) + - [coin_change](algorithms/dp/coin_change.py) + - [combination_sum](algorithms/dp/combination_sum.py) + - [egg_drop](algorithms/dp/egg_drop.py) + - [house_robber](algorithms/dp/house_robber.py) + - [job_scheduling](algorithms/dp/job_scheduling.py) + - [knapsack](algorithms/dp/knapsack.py) + - [longest_increasing](algorithms/dp/longest_increasing.py) + - [matrix_chain_order](algorithms/dp/matrix_chain_order.py) + - [max_product_subarray](algorithms/dp/max_product_subarray.py) + - [max_subarray](algorithms/dp/max_subarray.py) + - [min_cost_path](algorithms/dp/min_cost_path.py) + - [num_decodings](algorithms/dp/num_decodings.py) + - [regex_matching](algorithms/dp/regex_matching.py) + - [rod_cut](algorithms/dp/rod_cut.py) + - [word_break](algorithms/dp/word_break.py) + - [fibonacci](algorithms/dp/fib.py) +- [graph](algorithms/graph) + - [check_bipartite](algorithms/graph/check_bipartite.py) + - [strongly_connected](algorithms/graph/checkDiGraphStronglyConnected.py) + - [clone_graph](algorithms/graph/clone_graph.py) + - [cycle_detection](algorithms/graph/cycle_detection.py) + - [find_all_cliques](algorithms/graph/find_all_cliques.py) + - [find_path](algorithms/graph/find_path.py) + - [graph](algorithms/graph/graph.py) + - [markov_chain](algorithms/graph/markov_chain.py) + - [minimum_spanning_tree](algorithms/graph/minimum_spanning_tree.py) + - [satisfiability](algorithms/graph/satisfiability.py) + - [tarjan](algorithms/graph/tarjan.py) + - [traversal](algorithms/graph/traversal.py) +- [heap](algorithms/heap) + - [merge_sorted_k_lists](algorithms/heap/merge_sorted_k_lists.py) + - [skyline](algorithms/heap/skyline.py) + - [sliding_window_max](algorithms/heap/sliding_window_max.py) + - [binary_heap](algorithms/heap/binary_heap.py) +- [linkedlist](algorithms/linkedlist) + - [add_two_numbers](algorithms/linkedlist/add_two_numbers.py) + - [copy_random_pointer](algorithms/linkedlist/copy_random_pointer.py) + - [delete_node](algorithms/linkedlist/delete_node.py) + - [first_cyclic_node](algorithms/linkedlist/first_cyclic_node.py) + - [is_cyclic](algorithms/linkedlist/is_cyclic.py) + - [is_palindrome](algorithms/linkedlist/is_palindrome.py) + - [kth_to_last](algorithms/linkedlist/kth_to_last.py) + - [linkedlist](algorithms/linkedlist/linkedlist.py) + - [remove_duplicates](algorithms/linkedlist/remove_duplicates.py) + - [reverse](algorithms/linkedlist/reverse.py) + - [rotate_list](algorithms/linkedlist/rotate_list.py) + - [swap_in_pairs](algorithms/linkedlist/swap_in_pairs.py) + - [is_sorted](algorithms/linkedlist/is_sorted.py) + - [remove_range](algorithms/linkedlist/remove_range.py) +- [map](algorithms/map) + - [hashtable](algorithms/map/hashtable.py) + - [separate_chaining_hashtable](algorithms/map/separate_chaining_hashtable.py) + - [longest_common_subsequence](algorithms/map/longest_common_subsequence.py) + - [randomized_set](algorithms/map/randomized_set.py) + - [valid_sudoku](algorithms/map/valid_sudoku.py) +- [maths](algorithms/maths) + - [base_conversion](algorithms/maths/base_conversion.py) + - [combination](algorithms/maths/combination.py) + - [extended_gcd](algorithms/maths/extended_gcd.py) + - [factorial](algorithms/maths/factorial.py) + - [gcd/lcm](algorithms/maths/gcd.py) + - [generate_strobogrammtic](algorithms/maths/generate_strobogrammtic.py) + - [is_strobogrammatic](algorithms/maths/is_strobogrammatic.py) + - [modular_exponential](algorithms/maths/modular_exponential.py) + - [next_bigger](algorithms/maths/next_bigger.py) + - [next_perfect_square](algorithms/maths/next_perfect_square.py) + - [nth_digit](algorithms/maths/nth_digit.py) + - [prime_check](algorithms/maths/prime_check.py) + - [primes_sieve_of_eratosthenes](algorithms/maths/primes_sieve_of_eratosthenes.py) + - [pythagoras](algorithms/maths/pythagoras.py) + - [rabin_miller](algorithms/maths/rabin_miller.py) + - [rsa](algorithms/maths/rsa.py) + - [sqrt_precision_factor](algorithms/maths/sqrt_precision_factor.py) + - [summing_digits](algorithms/maths/summing_digits.py) +- [matrix](algorithms/matrix) + - [sudoku_validator](algorithms/matrix/sudoku_validator.py) + - [bomb_enemy](algorithms/matrix/bomb_enemy.py) + - [copy_transform](algorithms/matrix/copy_transform.py) + - [count_paths](algorithms/matrix/count_paths.py) + - [matrix_rotation.txt](algorithms/matrix/matrix_rotation.txt) + - [rotate_image](algorithms/matrix/rotate_image.py) + - [search_in_sorted_matrix](algorithms/matrix/search_in_sorted_matrix.py) + - [sparse_dot_vector](algorithms/matrix/sparse_dot_vector.py) + - [sparse_mul](algorithms/matrix/sparse_mul.py) + - [spiral_traversal](algorithms/matrix/spiral_traversal.py) +- [queues](algorithms/queues) + - [max_sliding_window](algorithms/queues/max_sliding_window.py) + - [moving_average](algorithms/queues/moving_average.py) + - [queue](algorithms/queues/queue.py) + - [reconstruct_queue](algorithms/queues/reconstruct_queue.py) + - [zigzagiterator](algorithms/queues/zigzagiterator.py) +- [search](algorithms/search) + - [binary_search](algorithms/search/binary_search.py) + - [first_occurance](algorithms/search/first_occurance.py) + - [last_occurance](algorithms/search/last_occurance.py) + - [linear_search](algorithms/search/linear_search.py) + - [search_insert](algorithms/search/search_insert.py) + - [two_sum](algorithms/search/two_sum.py) + - [search_range](algorithms/search/search_range.py) + - [find_min_rotate](algorithms/search/find_min_rotate.py) + - [search_rotate](algorithms/search/search_rotate.py) + - [jump_search](algorithms/search/jump_search.py) +- [set](algorithms/set) + - [randomized_set](algorithms/set/randomized_set.py) + - [set_covering](algorithms/set/set_covering.py) +- [sort](algorithms/sort) + - [bitonic_sort](algorithms/sort/bitonic_sort.py) + - [bogo_sort](algorithms/sort/bogo_sort.py) + - [bubble_sort](algorithms/sort/bubble_sort.py) + - [bucket_sort](algorithms/sort/bucket_sort.py) + - [cocktail_shaker_sort](algorithms/sort/cocktail_shaker_sort.py) + - [comb_sort](algorithms/sort/comb_sort.py) + - [counting_sort](algorithms/sort/counting_sort.py) + - [cycle_sort](algorithms/sort/cycle_sort.py) + - [gnome_sort](algorithms/sort/gnome_sort.py) + - [heap_sort](algorithms/sort/heap_sort.py) + - [insertion_sort](algorithms/sort/insertion_sort.py) + - [meeting_rooms](algorithms/sort/meeting_rooms.py) + - [merge_sort](algorithms/sort/merge_sort.py) + - [pancake_sort](algorithms/sort/pancake_sort.py) + - [quick_sort](algorithms/sort/quick_sort.py) + - [radix_sort](algorithms/sort/radix_sort.py) + - [selection_sort](algorithms/sort/selection_sort.py) + - [shell_sort](algorithms/sort/shell_sort.py) + - [sort_colors](algorithms/sort/sort_colors.py) + - [top_sort](algorithms/sort/top_sort.py) + - [wiggle_sort](algorithms/sort/wiggle_sort.py) +- [stack](algorithms/stack) + - [longest_abs_path](algorithms/stack/longest_abs_path.py) + - [simplify_path](algorithms/stack/simplify_path.py) + - [stack](algorithms/stack/stack.py) + - [valid_parenthesis](algorithms/stack/valid_parenthesis.py) + - [stutter](algorithms/stack/stutter.py) + - [switch_pairs](algorithms/stack/switch_pairs.py) + - [is_consecutive](algorithms/stack/is_consecutive.py) + - [remove_min](algorithms/stack/remove_min.py) + - [is_sorted](algorithms/stack/is_sorted.py) +- [strings](algorithms/strings) + - [fizzbuzz](algorithms/strings/fizzbuzz.py) + - [delete_reoccurring_characters](algorithms/strings/delete_reoccurring_characters.py) + - [strip_url_params](algorithms/strings/strip_url_params.py) + - [validate_coordinates](algorithms/strings/validate_coordinates.py) + - [domain_extractor](algorithms/strings/domain_extractor.py) + - [merge_string_checker](algorithms/strings/merge_string_checker.py) + - [add_binary](algorithms/strings/add_binary.py) + - [breaking_bad](algorithms/strings/breaking_bad.py) + - [decode_string](algorithms/strings/decode_string.py) + - [encode_decode](algorithms/strings/encode_decode.py) + - [group_anagrams](algorithms/strings/group_anagrams.py) + - [int_to_roman](algorithms/strings/int_to_roman.py) + - [is_palindrome](algorithms/strings/is_palindrome.py) + - [license_number](algorithms/strings/license_number.py) + - [make_sentence](algorithms/strings/make_sentence.py) + - [multiply_strings](algorithms/strings/multiply_strings.py) + - [one_edit_distance](algorithms/strings/one_edit_distance.py) + - [rabin_karp](algorithms/strings/rabin_karp.py) + - [reverse_string](algorithms/strings/reverse_string.py) + - [reverse_vowel](algorithms/strings/reverse_vowel.py) + - [reverse_words](algorithms/strings/reverse_words.py) + - [roman_to_int](algorithms/strings/roman_to_int.py) + - [word_squares](algorithms/strings/word_squares.py) +- [tree](algorithms/tree) + - [bst](algorithms/tree/tree/bst) + - [array2bst](algorithms/tree/bst/array2bst.py) + - [bst_closest_value](algorithms/tree/bst/bst_closest_value.py) + - [BSTIterator](algorithms/tree/bst/BSTIterator.py) + - [delete_node](algorithms/tree/bst/delete_node.py) + - [is_bst](algorithms/tree/bst/is_bst.py) + - [kth_smallest](algorithms/tree/bst/kth_smallest.py) + - [lowest_common_ancestor](algorithms/tree/bst/lowest_common_ancestor.py) + - [predecessor](algorithms/tree/bst/predecessor.py) + - [serialize_deserialize](algorithms/tree/bst/serialize_deserialize.py) + - [successor](algorithms/tree/bst/successor.py) + - [unique_bst](algorithms/tree/bst/unique_bst.py) + - [depth_sum](algorithms/tree/bst/depth_sum.py) + - [count_left_node](algorithms/tree/bst/count_left_node.py) + - [num_empty](algorithms/tree/bst/num_empty.py) + - [height](algorithms/tree/bst/height.py) + - [red_black_tree](algorithms/tree/red_black_tree) + - [red_black_tree](algorithms/tree/red_black_tree/red_black_tree.py) + - [segment_tree](algorithms/tree/segment_tree) + - [segment_tree](algorithms/tree/segment_tree/segment_tree.py) + - [traversal](algorithms/tree/traversal) + - [inorder](algorithms/tree/traversal/inorder.py) + - [level_order](algorithms/tree/traversal/level_order.py) + - [postorder](algorithms/tree/traversal/postorder.py) + - [preorder](algorithms/tree/traversal/preorder.py) + - [zigzag](algorithms/tree/traversal/zigzag.py) + - [trie](algorithms/tree/trie) + - [add_and_search](algorithms/tree/trie/add_and_search.py) + - [trie](algorithms/tree/trie/trie.py) + - [binary_tree_paths](algorithms/tree/binary_tree_paths.py) + - [bintree2list](algorithms/tree/bintree2list.py) + - [deepest_left](algorithms/tree/deepest_left.py) + - [invert_tree](algorithms/tree/invert_tree.py) + - [is_balanced](algorithms/tree/is_balanced.py) + - [is_subtree](algorithms/tree/is_subtree.py) + - [is_symmetric](algorithms/tree/is_symmetric.py) + - [longest_consecutive](algorithms/tree/longest_consecutive.py) + - [lowest_common_ancestor](algorithms/tree/lowest_common_ancestor.py) + - [max_height](algorithms/tree/max_height.py) + - [max_path_sum](algorithms/tree/max_path_sum.py) + - [min_height](algorithms/tree/min_height.py) + - [path_sum](algorithms/tree/path_sum.py) + - [path_sum2](algorithms/tree/path_sum2.py) + - [pretty_print](algorithms/tree/pretty_print.py) + - [same_tree](algorithms/tree/same_tree.py) + - [tree](algorithms/tree/tree.py) +- [union-find](algorithms/union-find) + - [count_islands](algorithms/union-find/count_islands.py) + +## Mitwirkende + +Das Projekt wird von folgenden Personen betreut. + +* [Keon Kim](https://github.com/keon) +* [Rahul Goswami](https://github.com/goswami-rahul) +* [Christian Bender](https://github.com/christianbender) +* [Ankit Agarwal](https://github.com/ankit167) +* [Hai Hoang Dang](https://github.com/danghai) +* [Saad](https://github.com/SaadBenn) + +Und danke an alle [Contributors](https://github.com/keon/algorithms/graphs/contributors) +die geholfen haben das Projekt aufzubauen! diff --git a/README_JP.md b/README_JP.md new file mode 100644 index 000000000..c0a56c2bf --- /dev/null +++ b/README_JP.md @@ -0,0 +1,348 @@ +

+ +[English](README.md) | [简体中文](README_CN.md) | [Deutsch](README_GE.md) | 日本語 | [한국어](README_KR.md) | [Português](README_PTBR.md) + +[![PyPI version](https://badge.fury.io/py/algorithms.svg)](https://badge.fury.io/py/algorithms) +[![Open Source Helpers](https://www.codetriage.com/keon/algorithms/badges/users.svg)](https://www.codetriage.com/keon/algorithms) +[![Build Status](https://travis-ci.org/keon/algorithms.svg?branch=master)](https://travis-ci.org/keon/algorithms) +[![Coverage Status](https://coveralls.io/repos/github/keon/algorithms/badge.svg?branch=master)](https://coveralls.io/github/keon/algorithms?branch=master) + +Pythonのデータ構造とアルゴリズム +========================================= + +Python 3で開発された簡単で明確なデータ構造とアルゴリズムの例を紹介します。 + +## 貢献 +貢献に興味を持っていただきありがとうございます。 このプロジェクトに貢献する方法はたくさんあります。 + +[簡単にコミュニティへ貢献するには](CONTRIBUTING_JP.md) + +## テスト + +### unittestを使用 +すべてのテストを実行するには: + + $ python3 -m unittest discover tests + +特定のテストを実行するためには、(例: ソート): + + $ python3 -m unittest tests.test_sort + +### pytestを使用 +すべてのテストを実行するには: + + $ python3 -m pytest tests + +## インストール +自分のコードでAPIアルゴリズムを活用したい場合は、以下のコードで簡単に実行することができます。 + + $ pip3 install git+https://github.com/keon/algorithms + +Pythonファイルを作成してテストを実行することができます:(例:「sort」の「merge_sort」を使用) + +```python3 +from sort import merge_sort + +if __name__ == "__main__": + my_list = [1, 8, 3, 5, 6] + my_list = merge_sort.merge_sort(my_list) + print(my_list) +``` + +## 削除 +アルゴリズムを削除する場合は、次のコードで簡単に実行することができます: + + $ pip3 uninstall -y algorithms + +## アルゴリズムのリスト + +- [arrays : 配列](algorithms/arrays) + - [delete_nth](algorithms/arrays/delete_nth.py) + - [flatten](algorithms/arrays/flatten.py) + - [garage](algorithms/arrays/garage.py) + - [josephus_problem](algorithms/arrays/josephus.py) + - [limit](algorithms/arrays/limit.py) + - [longest_non_repeat](algorithms/arrays/longest_non_repeat.py/) + - [max_ones_index](algorithms/arrays/max_ones_index.py) + - [merge_intervals](algorithms/arrays/merge_intervals.py) + - [missing_ranges](algorithms/arrays/missing_ranges.py) + - [plus_one](algorithms/arrays/plus_one.py) + - [rotate](algorithms/arrays/rotate.py) + - [summarize_ranges](algorithms/arrays/summarize_ranges.py) + - [three_sum](algorithms/arrays/three_sum.py) + - [trimmean](algorithms/arrays/trimmean.py) + - [top_1](algorithms/arrays/top_1.py) + - [two_sum](algorithms/arrays/two_sum.py) + - [move_zeros](algorithms/arrays/move_zeros.py) +- [backtrack : バックトラッキング](algorithms/backtrack) + - [general_solution.md](algorithms/backtrack/) + - [anagram](algorithms/backtrack/anagram.py) + - [array_sum_combinations](algorithms/backtrack/array_sum_combinations.py) + - [combination_sum](algorithms/backtrack/combination_sum.py) + - [expression_add_operators](algorithms/backtrack/expression_add_operators.py) + - [factor_combinations](algorithms/backtrack/factor_combinations.py) + - [generate_abbreviations](algorithms/backtrack/generate_abbreviations.py) + - [generate_parenthesis](algorithms/backtrack/generate_parenthesis.py) + - [letter_combination](algorithms/backtrack/letter_combination.py) + - [palindrome_partitioning](algorithms/backtrack/palindrome_partitioning.py) + - [pattern_match](algorithms/backtrack/pattern_match.py) + - [permute](algorithms/backtrack/permute.py) + - [permute_unique](algorithms/backtrack/permute_unique.py) + - [subsets](algorithms/backtrack/subsets.py) + - [subsets_unique](algorithms/backtrack/subsets_unique.py) +- [bfs : 幅優先探索](algorithms/bfs) + - [maze_search](algorithms/bfs/maze_search.py) + - [shortest_distance_from_all_buildings](algorithms/bfs/shortest_distance_from_all_buildings.py) + - [word_ladder](algorithms/bfs/word_ladder.py) +- [bit : ビット](algorithms/bit) + - [bytes_int_conversion](algorithms/bit/bytes_int_conversion.py) + - [count_ones](algorithms/bit/count_ones.py) + - [count_flips_to_convert](algorithms/bit/count_flips_to_convert.py) + - [find_missing_number](algorithms/bit/find_missing_number.py) + - [flip_bit_longest_sequence](algorithms/bit/flip_bit_longest_sequence.py) + - [power_of_two](algorithms/bit/power_of_two.py) + - [reverse_bits](algorithms/bit/reverse_bits.py) + - [single_number](algorithms/bit/single_number.py) + - [single_number2](algorithms/bit/single_number2.py) + - [single_number3](algorithms/bit/single_number3.py) + - [subsets](algorithms/bit/subsets.py) + - [add_bitwise_operator](algorithms/bit/add_bitwise_operator.py) + - [bit_operation](algorithms/bit/bit_operation.py) + - [swap_pair](algorithms/bit/swap_pair.py) + - [find_difference](algorithms/bit/find_difference.py) + - [has_alternative_bit](algorithms/bit/has_alternative_bit.py) + - [insert_bit](algorithms/bit/insert_bit.py) + - [remove_bit](algorithms/bit/remove_bit.py) +- [calculator : 計算機](algorithms/calculator) + - [math_parser](algorithms/calculator/math_parser.py) +- [dfs : 深さ優先探索](algorithms/dfs) + - [all_factors](algorithms/dfs/all_factors.py) + - [count_islands](algorithms/dfs/count_islands.py) + - [pacific_atlantic](algorithms/dfs/pacific_atlantic.py) + - [sudoku_solver](algorithms/dfs/sudoku_solver.py) + - [walls_and_gates](algorithms/dfs/walls_and_gates.py) +- [dp : 動的計画法](algorithms/dp) + - [buy_sell_stock](algorithms/dp/buy_sell_stock.py) + - [climbing_stairs](algorithms/dp/climbing_stairs.py) + - [coin_change](algorithms/dp/coin_change.py) + - [combination_sum](algorithms/dp/combination_sum.py) + - [egg_drop](algorithms/dp/egg_drop.py) + - [house_robber](algorithms/dp/house_robber.py) + - [job_scheduling](algorithms/dp/job_scheduling.py) + - [knapsack](algorithms/dp/knapsack.py) + - [longest_increasing](algorithms/dp/longest_increasing.py) + - [matrix_chain_order](algorithms/dp/matrix_chain_order.py) + - [max_product_subarray](algorithms/dp/max_product_subarray.py) + - [max_subarray](algorithms/dp/max_subarray.py) + - [min_cost_path](algorithms/dp/min_cost_path.py) + - [num_decodings](algorithms/dp/num_decodings.py) + - [regex_matching](algorithms/dp/regex_matching.py) + - [rod_cut](algorithms/dp/rod_cut.py) + - [word_break](algorithms/dp/word_break.py) + - [fibonacci](algorithms/dp/fib.py) +- [graph : グラフ](algorithms/graph) + - [check_bipartite](algorithms/graph/check_bipartite.py) + - [strongly_connected](algorithms/graph/checkDiGraphStronglyConnected.py) + - [clone_graph](algorithms/graph/clone_graph.py) + - [cycle_detection](algorithms/graph/cycle_detection.py) + - [find_all_cliques](algorithms/graph/find_all_cliques.py) + - [find_path](algorithms/graph/find_path.py) + - [graph](algorithms/graph/graph.py) + - [markov_chain](algorithms/graph/markov_chain.py) + - [minimum_spanning_tree](algorithms/graph/minimum_spanning_tree.py) + - [satisfiability](algorithms/graph/satisfiability.py) + - [tarjan](algorithms/graph/tarjan.py) + - [traversal](algorithms/graph/traversal.py) +- [heap : ヒープ](algorithms/heap) + - [merge_sorted_k_lists](algorithms/heap/merge_sorted_k_lists.py) + - [skyline](algorithms/heap/skyline.py) + - [sliding_window_max](algorithms/heap/sliding_window_max.py) + - [binary_heap](algorithms/heap/binary_heap.py) +- [linkedlist : 連結リスト](algorithms/linkedlist) + - [add_two_numbers](algorithms/linkedlist/add_two_numbers.py) + - [copy_random_pointer](algorithms/linkedlist/copy_random_pointer.py) + - [delete_node](algorithms/linkedlist/delete_node.py) + - [first_cyclic_node](algorithms/linkedlist/first_cyclic_node.py) + - [is_cyclic](algorithms/linkedlist/is_cyclic.py) + - [is_palindrome](algorithms/linkedlist/is_palindrome.py) + - [kth_to_last](algorithms/linkedlist/kth_to_last.py) + - [linkedlist](algorithms/linkedlist/linkedlist.py) + - [remove_duplicates](algorithms/linkedlist/remove_duplicates.py) + - [reverse](algorithms/linkedlist/reverse.py) + - [rotate_list](algorithms/linkedlist/rotate_list.py) + - [swap_in_pairs](algorithms/linkedlist/swap_in_pairs.py) + - [is_sorted](algorithms/linkedlist/is_sorted.py) + - [remove_range](algorithms/linkedlist/remove_range.py) +- [map : マップ](algorithms/map) + - [hashtable](algorithms/map/hashtable.py) + - [separate_chaining_hashtable](algorithms/map/separate_chaining_hashtable.py) + - [longest_common_subsequence](algorithms/map/longest_common_subsequence.py) + - [randomized_set](algorithms/map/randomized_set.py) + - [valid_sudoku](algorithms/map/valid_sudoku.py) +- [maths : 数学](algorithms/maths) + - [base_conversion](algorithms/maths/base_conversion.py) + - [combination](algorithms/maths/combination.py) + - [extended_gcd](algorithms/maths/extended_gcd.py) + - [factorial](algorithms/maths/factorial.py) + - [gcd/lcm](algorithms/maths/gcd.py) + - [generate_strobogrammtic](algorithms/maths/generate_strobogrammtic.py) + - [is_strobogrammatic](algorithms/maths/is_strobogrammatic.py) + - [modular_exponential](algorithms/maths/modular_exponential.py) + - [next_bigger](algorithms/maths/next_bigger.py) + - [next_perfect_square](algorithms/maths/next_perfect_square.py) + - [nth_digit](algorithms/maths/nth_digit.py) + - [prime_check](algorithms/maths/prime_check.py) + - [primes_sieve_of_eratosthenes](algorithms/maths/primes_sieve_of_eratosthenes.py) + - [pythagoras](algorithms/maths/pythagoras.py) + - [rabin_miller](algorithms/maths/rabin_miller.py) + - [rsa](algorithms/maths/rsa.py) + - [sqrt_precision_factor](algorithms/maths/sqrt_precision_factor.py) + - [summing_digits](algorithms/maths/summing_digits.py) +- [matrix : 行列](algorithms/matrix) + - [sudoku_validator](algorithms/matrix/sudoku_validator.py) + - [bomb_enemy](algorithms/matrix/bomb_enemy.py) + - [copy_transform](algorithms/matrix/copy_transform.py) + - [count_paths](algorithms/matrix/count_paths.py) + - [matrix_rotation.txt](algorithms/matrix/matrix_rotation.txt) + - [rotate_image](algorithms/matrix/rotate_image.py) + - [search_in_sorted_matrix](algorithms/matrix/search_in_sorted_matrix.py) + - [sparse_dot_vector](algorithms/matrix/sparse_dot_vector.py) + - [sparse_mul](algorithms/matrix/sparse_mul.py) + - [spiral_traversal](algorithms/matrix/spiral_traversal.py) +- [queues : キュー](algorithms/queues) + - [max_sliding_window](algorithms/queues/max_sliding_window.py) + - [moving_average](algorithms/queues/moving_average.py) + - [queue](algorithms/queues/queue.py) + - [reconstruct_queue](algorithms/queues/reconstruct_queue.py) + - [zigzagiterator](algorithms/queues/zigzagiterator.py) +- [search : サーチ](algorithms/search) + - [binary_search](algorithms/search/binary_search.py) + - [first_occurance](algorithms/search/first_occurance.py) + - [last_occurance](algorithms/search/last_occurance.py) + - [linear_search](algorithms/search/linear_search.py) + - [search_insert](algorithms/search/search_insert.py) + - [two_sum](algorithms/search/two_sum.py) + - [search_range](algorithms/search/search_range.py) + - [find_min_rotate](algorithms/search/find_min_rotate.py) + - [search_rotate](algorithms/search/search_rotate.py) + - [jump_search](algorithms/search/jump_search.py) +- [set : セット](algorithms/set) + - [randomized_set](algorithms/set/randomized_set.py) + - [set_covering](algorithms/set/set_covering.py) +- [sort : ソート](algorithms/sort) + - [bitonic_sort](algorithms/sort/bitonic_sort.py) + - [bogo_sort](algorithms/sort/bogo_sort.py) + - [bubble_sort](algorithms/sort/bubble_sort.py) + - [bucket_sort](algorithms/sort/bucket_sort.py) + - [cocktail_shaker_sort](algorithms/sort/cocktail_shaker_sort.py) + - [comb_sort](algorithms/sort/comb_sort.py) + - [counting_sort](algorithms/sort/counting_sort.py) + - [cycle_sort](algorithms/sort/cycle_sort.py) + - [gnome_sort](algorithms/sort/gnome_sort.py) + - [heap_sort](algorithms/sort/heap_sort.py) + - [insertion_sort](algorithms/sort/insertion_sort.py) + - [meeting_rooms](algorithms/sort/meeting_rooms.py) + - [merge_sort](algorithms/sort/merge_sort.py) + - [pancake_sort](algorithms/sort/pancake_sort.py) + - [quick_sort](algorithms/sort/quick_sort.py) + - [radix_sort](algorithms/sort/radix_sort.py) + - [selection_sort](algorithms/sort/selection_sort.py) + - [shell_sort](algorithms/sort/shell_sort.py) + - [sort_colors](algorithms/sort/sort_colors.py) + - [top_sort](algorithms/sort/top_sort.py) + - [wiggle_sort](algorithms/sort/wiggle_sort.py) +- [stack : スタック](algorithms/stack) + - [longest_abs_path](algorithms/stack/longest_abs_path.py) + - [simplify_path](algorithms/stack/simplify_path.py) + - [stack](algorithms/stack/stack.py) + - [valid_parenthesis](algorithms/stack/valid_parenthesis.py) + - [stutter](algorithms/stack/stutter.py) + - [switch_pairs](algorithms/stack/switch_pairs.py) + - [is_consecutive](algorithms/stack/is_consecutive.py) + - [remove_min](algorithms/stack/remove_min.py) + - [is_sorted](algorithms/stack/is_sorted.py) +- [strings : 文字列](algorithms/strings) + - [fizzbuzz](algorithms/strings/fizzbuzz.py) + - [delete_reoccurring_characters](algorithms/strings/delete_reoccurring_characters.py) + - [strip_url_params](algorithms/strings/strip_url_params.py) + - [validate_coordinates](algorithms/strings/validate_coordinates.py) + - [domain_extractor](algorithms/strings/domain_extractor.py) + - [merge_string_checker](algorithms/strings/merge_string_checker.py) + - [add_binary](algorithms/strings/add_binary.py) + - [breaking_bad](algorithms/strings/breaking_bad.py) + - [decode_string](algorithms/strings/decode_string.py) + - [encode_decode](algorithms/strings/encode_decode.py) + - [group_anagrams](algorithms/strings/group_anagrams.py) + - [int_to_roman](algorithms/strings/int_to_roman.py) + - [is_palindrome](algorithms/strings/is_palindrome.py) + - [license_number](algorithms/strings/license_number.py) + - [make_sentence](algorithms/strings/make_sentence.py) + - [multiply_strings](algorithms/strings/multiply_strings.py) + - [one_edit_distance](algorithms/strings/one_edit_distance.py) + - [rabin_karp](algorithms/strings/rabin_karp.py) + - [reverse_string](algorithms/strings/reverse_string.py) + - [reverse_vowel](algorithms/strings/reverse_vowel.py) + - [reverse_words](algorithms/strings/reverse_words.py) + - [roman_to_int](algorithms/strings/roman_to_int.py) + - [word_squares](algorithms/strings/word_squares.py) +- [tree : 木構造](algorithms/tree) + - [bst](algorithms/tree/tree/bst) + - [array2bst](algorithms/tree/bst/array2bst.py) + - [bst_closest_value](algorithms/tree/bst/bst_closest_value.py) + - [BSTIterator](algorithms/tree/bst/BSTIterator.py) + - [delete_node](algorithms/tree/bst/delete_node.py) + - [is_bst](algorithms/tree/bst/is_bst.py) + - [kth_smallest](algorithms/tree/bst/kth_smallest.py) + - [lowest_common_ancestor](algorithms/tree/bst/lowest_common_ancestor.py) + - [predecessor](algorithms/tree/bst/predecessor.py) + - [serialize_deserialize](algorithms/tree/bst/serialize_deserialize.py) + - [successor](algorithms/tree/bst/successor.py) + - [unique_bst](algorithms/tree/bst/unique_bst.py) + - [depth_sum](algorithms/tree/bst/depth_sum.py) + - [count_left_node](algorithms/tree/bst/count_left_node.py) + - [num_empty](algorithms/tree/bst/num_empty.py) + - [height](algorithms/tree/bst/height.py) + - [red_black_tree](algorithms/tree/red_black_tree) + - [red_black_tree](algorithms/tree/red_black_tree/red_black_tree.py) + - [segment_tree](algorithms/tree/segment_tree) + - [segment_tree](algorithms/tree/segment_tree/segment_tree.py) + - [traversal](algorithms/tree/traversal) + - [inorder](algorithms/tree/traversal/inorder.py) + - [level_order](algorithms/tree/traversal/level_order.py) + - [postorder](algorithms/tree/traversal/postorder.py) + - [preorder](algorithms/tree/traversal/preorder.py) + - [zigzag](algorithms/tree/traversal/zigzag.py) + - [trie](algorithms/tree/trie) + - [add_and_search](algorithms/tree/trie/add_and_search.py) + - [trie](algorithms/tree/trie/trie.py) + - [binary_tree_paths](algorithms/tree/binary_tree_paths.py) + - [bintree2list](algorithms/tree/bintree2list.py) + - [deepest_left](algorithms/tree/deepest_left.py) + - [invert_tree](algorithms/tree/invert_tree.py) + - [is_balanced](algorithms/tree/is_balanced.py) + - [is_subtree](algorithms/tree/is_subtree.py) + - [is_symmetric](algorithms/tree/is_symmetric.py) + - [longest_consecutive](algorithms/tree/longest_consecutive.py) + - [lowest_common_ancestor](algorithms/tree/lowest_common_ancestor.py) + - [max_height](algorithms/tree/max_height.py) + - [max_path_sum](algorithms/tree/max_path_sum.py) + - [min_height](algorithms/tree/min_height.py) + - [path_sum](algorithms/tree/path_sum.py) + - [path_sum2](algorithms/tree/path_sum2.py) + - [pretty_print](algorithms/tree/pretty_print.py) + - [same_tree](algorithms/tree/same_tree.py) + - [tree](algorithms/tree/tree.py) +- [union-find : 素集合データ構造](algorithms/union-find) + - [count_islands](algorithms/union-find/count_islands.py) + +## 貢献者 +本リポジトリは次の方によって維持されています + +* [Keon Kim](https://github.com/keon) +* [Rahul Goswami](https://github.com/goswami-rahul) +* [Christian Bender](https://github.com/christianbender) +* [Ankit Agarwal](https://github.com/ankit167) +* [Hai Hoang Dang](https://github.com/danghai) +* [Saad](https://github.com/SaadBenn) + +また、[全ての貢献者](https://github.com/keon/algorithms/graphs/contributors)に感謝を伝えます。 diff --git a/README_KR.md b/README_KR.md new file mode 100644 index 000000000..efd98483e --- /dev/null +++ b/README_KR.md @@ -0,0 +1,343 @@ +

+ +[English](README.md) | [简体中文](README_CN.md) | [Deutsch](README_GE.md) | [日本語](README_JP.md) | 한국어 | [Português](README_PTBR.md) + +[![PyPI version](https://badge.fury.io/py/algorithms.svg)](https://badge.fury.io/py/algorithms) +[![Open Source Helpers](https://www.codetriage.com/keon/algorithms/badges/users.svg)](https://www.codetriage.com/keon/algorithms) +[![Build Status](https://travis-ci.org/keon/algorithms.svg?branch=master)](https://travis-ci.org/keon/algorithms) +[![Coverage Status](https://coveralls.io/repos/github/keon/algorithms/badge.svg?branch=master)](https://coveralls.io/github/keon/algorithms?branch=master) + +Python 버전 자료구조 및 알고리즘 +========================================= + +Python 3로 구현한 간단하고 명확한 자료구조와 알고리즘들의 예제 입니다. + +## 기여 활동 +프로젝트 활동 참여에 관심을 가져주셔서 감사합니다! 여러가지 방법으로 이 프로젝트에 기여해주세요. [기여 방법 소개](CONTRIBUTING.md) + +## 테스트 종류들 + +### unittest 사용 +아래 명시된 모든 테스트 실행하기: + + $ python3 -m unittest discover tests + +특정 테스트 실행하기 위해선 아래 코드로 실행할 수 있습니다 (예시: sort): + + $ python3 -m unittest tests.test_sort + +### pytest 사용 +아래 명시된 모든 테스트 실행하기: + + $ python3 -m pytest tests + +## 알고리즘 설치 +만약 API 알고리즘들을 당신의 코드에 사용하기를 원한다면, 아래 코드로 간단하게 실행할 수 있습니다: + + $ pip3 install git+https://github.com/keon/algorithms + +그리고 python 파일을 만듦으로 테스트할 수 있습니다: (예시: 'sort'안에서 'merge_sort'사용) + +```python3 +from sort import merge_sort + +if __name__ == "__main__": + my_list = [1, 8, 3, 5, 6] + my_list = merge_sort.merge_sort(my_list) + print(my_list) +``` + +## 알고리즘 삭제 +만약 당신이 알고리즘들을 삭제하기 원한다면, 아래 코드로 간단하게 실행할 수 있습니다: + + $ pip3 uninstall -y algorithms + +## 구현 알고리즘 목록 + +- [arrays : 배열](algorithms/arrays) + - [delete_nth](algorithms/arrays/delete_nth.py) + - [flatten](algorithms/arrays/flatten.py) + - [garage](algorithms/arrays/garage.py) + - [josephus_problem](algorithms/arrays/josephus.py) + - [limit](algorithms/arrays/limit.py) + - [longest_non_repeat](algorithms/arrays/longest_non_repeat.py/) + - [max_ones_index](algorithms/arrays/max_ones_index.py) + - [merge_intervals](algorithms/arrays/merge_intervals.py) + - [missing_ranges](algorithms/arrays/missing_ranges.py) + - [plus_one](algorithms/arrays/plus_one.py) + - [rotate](algorithms/arrays/rotate.py) + - [summarize_ranges](algorithms/arrays/summarize_ranges.py) + - [three_sum](algorithms/arrays/three_sum.py) + - [trimmean](algorithms/arrays/trimmean.py) + - [top_1](algorithms/arrays/top_1.py) + - [two_sum](algorithms/arrays/two_sum.py) + - [move_zeros](algorithms/arrays/move_zeros.py) +- [backtrack : 백트래킹](algorithms/backtrack) + - [general_solution.md](algorithms/backtrack/) + - [anagram](algorithms/backtrack/anagram.py) + - [array_sum_combinations](algorithms/backtrack/array_sum_combinations.py) + - [combination_sum](algorithms/backtrack/combination_sum.py) + - [expression_add_operators](algorithms/backtrack/expression_add_operators.py) + - [factor_combinations](algorithms/backtrack/factor_combinations.py) + - [generate_abbreviations](algorithms/backtrack/generate_abbreviations.py) + - [generate_parenthesis](algorithms/backtrack/generate_parenthesis.py) + - [letter_combination](algorithms/backtrack/letter_combination.py) + - [palindrome_partitioning](algorithms/backtrack/palindrome_partitioning.py) + - [pattern_match](algorithms/backtrack/pattern_match.py) + - [permute](algorithms/backtrack/permute.py) + - [permute_unique](algorithms/backtrack/permute_unique.py) + - [subsets](algorithms/backtrack/subsets.py) + - [subsets_unique](algorithms/backtrack/subsets_unique.py) +- [bfs : 너비 우선 탐색](algorithms/bfs) + - [maze_search](algorithms/bfs/maze_search.py) + - [shortest_distance_from_all_buildings](algorithms/bfs/shortest_distance_from_all_buildings.py) + - [word_ladder](algorithms/bfs/word_ladder.py) +- [bit : 비트](algorithms/bit) + - [bytes_int_conversion](algorithms/bit/bytes_int_conversion.py) + - [count_ones](algorithms/bit/count_ones.py) + - [find_missing_number](algorithms/bit/find_missing_number.py) + - [power_of_two](algorithms/bit/power_of_two.py) + - [reverse_bits](algorithms/bit/reverse_bits.py) + - [single_number](algorithms/bit/single_number.py) + - [single_number2](algorithms/bit/single_number2.py) + - [single_number3](algorithms/bit/single_number3.py) + - [subsets](algorithms/bit/subsets.py) + - [add_bitwise_operator](algorithms/bit/add_bitwise_operator.py) + - [bit_operation](algorithms/bit/bit_operation.py) + - [swap_pair](algorithms/bit/swap_pair.py) + - [find_difference](algorithms/bit/find_difference.py) + - [has_alternative_bit](algorithms/bit/has_alternative_bit.py) + - [insert_bit](algorithms/bit/insert_bit.py) + - [remove_bit](algorithms/bit/remove_bit.py) +- [calculator : 계산기](algorithms/calculator) + - [math_parser](algorithms/calculator/math_parser.py) +- [dfs : 깊이 우선 탐색](algorithms/dfs) + - [all_factors](algorithms/dfs/all_factors.py) + - [count_islands](algorithms/dfs/count_islands.py) + - [pacific_atlantic](algorithms/dfs/pacific_atlantic.py) + - [sudoku_solver](algorithms/dfs/sudoku_solver.py) + - [walls_and_gates](algorithms/dfs/walls_and_gates.py) +- [dp : 동적 계획법](algorithms/dp) + - [buy_sell_stock](algorithms/dp/buy_sell_stock.py) + - [climbing_stairs](algorithms/dp/climbing_stairs.py) + - [coin_change](algorithms/dp/coin_change.py) + - [combination_sum](algorithms/dp/combination_sum.py) + - [egg_drop](algorithms/dp/egg_drop.py) + - [house_robber](algorithms/dp/house_robber.py) + - [job_scheduling](algorithms/dp/job_scheduling.py) + - [knapsack](algorithms/dp/knapsack.py) + - [longest_increasing](algorithms/dp/longest_increasing.py) + - [matrix_chain_order](algorithms/dp/matrix_chain_order.py) + - [max_product_subarray](algorithms/dp/max_product_subarray.py) + - [max_subarray](algorithms/dp/max_subarray.py) + - [min_cost_path](algorithms/dp/min_cost_path.py) + - [num_decodings](algorithms/dp/num_decodings.py) + - [regex_matching](algorithms/dp/regex_matching.py) + - [rod_cut](algorithms/dp/rod_cut.py) + - [word_break](algorithms/dp/word_break.py) + - [fibonacci](algorithms/dp/fib.py) +- [graph : 그래프](algorithms/graph) + - [check_bipartite](algorithms/graph/check_bipartite.py) + - [strongly_connected](algorithms/graph/checkDiGraphStronglyConnected.py) + - [clone_graph](algorithms/graph/clone_graph.py) + - [cycle_detection](algorithms/graph/cycle_detection.py) + - [find_all_cliques](algorithms/graph/find_all_cliques.py) + - [find_path](algorithms/graph/find_path.py) + - [graph](algorithms/graph/graph.py) + - [markov_chain](algorithms/graph/markov_chain.py) + - [minimum_spanning_tree](algorithms/graph/minimum_spanning_tree.py) + - [satisfiability](algorithms/graph/satisfiability.py) + - [tarjan](algorithms/graph/tarjan.py) + - [traversal](algorithms/graph/traversal.py) +- [heap : 힙](algorithms/heap) + - [merge_sorted_k_lists](algorithms/heap/merge_sorted_k_lists.py) + - [skyline](algorithms/heap/skyline.py) + - [sliding_window_max](algorithms/heap/sliding_window_max.py) + - [binary_heap](algorithms/heap/binary_heap.py) +- [linkedlist : 연결 리스트](algorithms/linkedlist) + - [add_two_numbers](algorithms/linkedlist/add_two_numbers.py) + - [copy_random_pointer](algorithms/linkedlist/copy_random_pointer.py) + - [delete_node](algorithms/linkedlist/delete_node.py) + - [first_cyclic_node](algorithms/linkedlist/first_cyclic_node.py) + - [is_cyclic](algorithms/linkedlist/is_cyclic.py) + - [is_palindrome](algorithms/linkedlist/is_palindrome.py) + - [kth_to_last](algorithms/linkedlist/kth_to_last.py) + - [linkedlist](algorithms/linkedlist/linkedlist.py) + - [remove_duplicates](algorithms/linkedlist/remove_duplicates.py) + - [reverse](algorithms/linkedlist/reverse.py) + - [rotate_list](algorithms/linkedlist/rotate_list.py) + - [swap_in_pairs](algorithms/linkedlist/swap_in_pairs.py) + - [is_sorted](algorithms/linkedlist/is_sorted.py) + - [remove_range](algorithms/linkedlist/remove_range.py) +- [map : 맵](algorithms/map) + - [hashtable](algorithms/map/hashtable.py) + - [separate_chaining_hashtable](algorithms/map/separate_chaining_hashtable.py) + - [longest_common_subsequence](algorithms/map/longest_common_subsequence.py) + - [randomized_set](algorithms/map/randomized_set.py) + - [valid_sudoku](algorithms/map/valid_sudoku.py) +- [maths : 수학 계산](algorithms/maths) + - [base_conversion](algorithms/maths/base_conversion.py) + - [combination](algorithms/maths/combination.py) + - [extended_gcd](algorithms/maths/extended_gcd.py) + - [factorial](algorithms/maths/factorial.py) + - [gcd/lcm](algorithms/maths/gcd.py) + - [generate_strobogrammtic](algorithms/maths/generate_strobogrammtic.py) + - [is_strobogrammatic](algorithms/maths/is_strobogrammatic.py) + - [modular_exponential](algorithms/maths/modular_exponential.py) + - [next_bigger](algorithms/maths/next_bigger.py) + - [next_perfect_square](algorithms/maths/next_perfect_square.py) + - [nth_digit](algorithms/maths/nth_digit.py) + - [prime_check](algorithms/maths/prime_check.py) + - [primes_sieve_of_eratosthenes](algorithms/maths/primes_sieve_of_eratosthenes.py) + - [pythagoras](algorithms/maths/pythagoras.py) + - [rabin_miller](algorithms/maths/rabin_miller.py) + - [rsa](algorithms/maths/rsa.py) + - [sqrt_precision_factor](algorithms/maths/sqrt_precision_factor.py) + - [summing_digits](algorithms/maths/summing_digits.py) +- [matrix : 행렬](algorithms/matrix) + - [sudoku_validator](algorithms/matrix/sudoku_validator.py) + - [bomb_enemy](algorithms/matrix/bomb_enemy.py) + - [copy_transform](algorithms/matrix/copy_transform.py) + - [count_paths](algorithms/matrix/count_paths.py) + - [matrix_rotation.txt](algorithms/matrix/matrix_rotation.txt) + - [rotate_image](algorithms/matrix/rotate_image.py) + - [search_in_sorted_matrix](algorithms/matrix/search_in_sorted_matrix.py) + - [sparse_dot_vector](algorithms/matrix/sparse_dot_vector.py) + - [sparse_mul](algorithms/matrix/sparse_mul.py) + - [spiral_traversal](algorithms/matrix/spiral_traversal.py) +- [queues : 큐](algorithms/queues) + - [max_sliding_window](algorithms/queues/max_sliding_window.py) + - [moving_average](algorithms/queues/moving_average.py) + - [queue](algorithms/queues/queue.py) + - [reconstruct_queue](algorithms/queues/reconstruct_queue.py) + - [zigzagiterator](algorithms/queues/zigzagiterator.py) +- [search : 탐색 알고리즘](algorithms/search) + - [binary_search](algorithms/search/binary_search.py) + - [first_occurance](algorithms/search/first_occurance.py) + - [last_occurance](algorithms/search/last_occurance.py) + - [linear_search](algorithms/search/linear_search.py) + - [search_insert](algorithms/search/search_insert.py) + - [two_sum](algorithms/search/two_sum.py) + - [search_range](algorithms/search/search_range.py) + - [find_min_rotate](algorithms/search/find_min_rotate.py) + - [search_rotate](algorithms/search/search_rotate.py) + - [jump_search](algorithms/search/jump_search.py) +- [set : 집합](algorithms/set) + - [randomized_set](algorithms/set/randomized_set.py) + - [set_covering](algorithms/set/set_covering.py) +- [sort : 정렬 알고리즘](algorithms/sort) + - [bitonic_sort](algorithms/sort/bitonic_sort.py) + - [bogo_sort](algorithms/sort/bogo_sort.py) + - [bubble_sort](algorithms/sort/bubble_sort.py) + - [cocktail_shaker_sort](algorithms/sort/cocktail_shaker_sort.py) + - [comb_sort](algorithms/sort/comb_sort.py) + - [counting_sort](algorithms/sort/counting_sort.py) + - [cycle_sort](algorithms/sort/cycle_sort.py) + - [gnome_sort](algorithms/sort/gnome_sort.py) + - [heap_sort](algorithms/sort/heap_sort.py) + - [insertion_sort](algorithms/sort/insertion_sort.py) + - [meeting_rooms](algorithms/sort/meeting_rooms.py) + - [merge_sort](algorithms/sort/merge_sort.py) + - [pancake_sort](algorithms/sort/pancake_sort.py) + - [quick_sort](algorithms/sort/quick_sort.py) + - [radix_sort](algorithms/sort/radix_sort.py) + - [selection_sort](algorithms/sort/selection_sort.py) + - [sort_colors](algorithms/sort/sort_colors.py) + - [top_sort](algorithms/sort/top_sort.py) + - [wiggle_sort](algorithms/sort/wiggle_sort.py) +- [stack : 스택](algorithms/stack) + - [longest_abs_path](algorithms/stack/longest_abs_path.py) + - [simplify_path](algorithms/stack/simplify_path.py) + - [stack](algorithms/stack/stack.py) + - [valid_parenthesis](algorithms/stack/valid_parenthesis.py) + - [stutter](algorithms/stack/stutter.py) + - [switch_pairs](algorithms/stack/switch_pairs.py) + - [is_consecutive](algorithms/stack/is_consecutive.py) + - [remove_min](algorithms/stack/remove_min.py) + - [is_sorted](algorithms/stack/is_sorted.py) +- [strings : 문자열](algorithms/strings) + - [fizzbuzz](algorithms/strings/fizzbuzz.py) + - [delete_reoccurring_characters](algorithms/strings/delete_reoccurring_characters.py) + - [strip_url_params](algorithms/strings/strip_url_params.py) + - [validate_coordinates](algorithms/strings/validate_coordinates.py) + - [domain_extractor](algorithms/strings/domain_extractor.py) + - [merge_string_checker](algorithms/strings/merge_string_checker.py) + - [add_binary](algorithms/strings/add_binary.py) + - [breaking_bad](algorithms/strings/breaking_bad.py) + - [decode_string](algorithms/strings/decode_string.py) + - [encode_decode](algorithms/strings/encode_decode.py) + - [group_anagrams](algorithms/strings/group_anagrams.py) + - [int_to_roman](algorithms/strings/int_to_roman.py) + - [is_palindrome](algorithms/strings/is_palindrome.py) + - [license_number](algorithms/strings/license_number.py) + - [make_sentence](algorithms/strings/make_sentence.py) + - [multiply_strings](algorithms/strings/multiply_strings.py) + - [one_edit_distance](algorithms/strings/one_edit_distance.py) + - [rabin_karp](algorithms/strings/rabin_karp.py) + - [reverse_string](algorithms/strings/reverse_string.py) + - [reverse_vowel](algorithms/strings/reverse_vowel.py) + - [reverse_words](algorithms/strings/reverse_words.py) + - [roman_to_int](algorithms/strings/roman_to_int.py) + - [word_squares](algorithms/strings/word_squares.py) +- [tree : 트리](algorithms/tree) + - [bst : 이진 탐색 트리](algorithms/tree/tree/bst) + - [array2bst](algorithms/tree/bst/array2bst.py) + - [bst_closest_value](algorithms/tree/bst/bst_closest_value.py) + - [BSTIterator](algorithms/tree/bst/BSTIterator.py) + - [delete_node](algorithms/tree/bst/delete_node.py) + - [is_bst](algorithms/tree/bst/is_bst.py) + - [kth_smallest](algorithms/tree/bst/kth_smallest.py) + - [lowest_common_ancestor](algorithms/tree/bst/lowest_common_ancestor.py) + - [predecessor](algorithms/tree/bst/predecessor.py) + - [serialize_deserialize](algorithms/tree/bst/serialize_deserialize.py) + - [successor](algorithms/tree/bst/successor.py) + - [unique_bst](algorithms/tree/bst/unique_bst.py) + - [depth_sum](algorithms/tree/bst/depth_sum.py) + - [count_left_node](algorithms/tree/bst/count_left_node.py) + - [num_empty](algorithms/tree/bst/num_empty.py) + - [height](algorithms/tree/bst/height.py) + - [red_black_tree : 레드 블랙 트리](algorithms/tree/red_black_tree) + - [red_black_tree](algorithms/tree/red_black_tree/red_black_tree.py) + - [segment_tree : 세그먼트 트리](algorithms/tree/segment_tree) + - [segment_tree](algorithms/tree/segment_tree/segment_tree.py) + - [traversal : 트리 순회](algorithms/tree/traversal) + - [inorder](algorithms/tree/traversal/inorder.py) + - [level_order](algorithms/tree/traversal/level_order.py) + - [postorder](algorithms/tree/traversal/postorder.py) + - [preorder](algorithms/tree/traversal/preorder.py) + - [zigzag](algorithms/tree/traversal/zigzag.py) + - [trie : 트라이](algorithms/tree/trie) + - [add_and_search](algorithms/tree/trie/add_and_search.py) + - [trie](algorithms/tree/trie/trie.py) + - [binary_tree_paths](algorithms/tree/binary_tree_paths.py) + - [bintree2list](algorithms/tree/bintree2list.py) + - [deepest_left](algorithms/tree/deepest_left.py) + - [invert_tree](algorithms/tree/invert_tree.py) + - [is_balanced](algorithms/tree/is_balanced.py) + - [is_subtree](algorithms/tree/is_subtree.py) + - [is_symmetric](algorithms/tree/is_symmetric.py) + - [longest_consecutive](algorithms/tree/longest_consecutive.py) + - [lowest_common_ancestor](algorithms/tree/lowest_common_ancestor.py) + - [max_height](algorithms/tree/max_height.py) + - [max_path_sum](algorithms/tree/max_path_sum.py) + - [min_height](algorithms/tree/min_height.py) + - [path_sum](algorithms/tree/path_sum.py) + - [path_sum2](algorithms/tree/path_sum2.py) + - [pretty_print](algorithms/tree/pretty_print.py) + - [same_tree](algorithms/tree/same_tree.py) + - [tree](algorithms/tree/tree.py) +- [union-find : 합집합 찾기](algorithms/union-find) + - [count_islands](algorithms/union-find/count_islands.py) + +## 기여자들 +이 저장소는 아래 사람들에 의해 유지되고 있습니다. + +* [Keon Kim](https://github.com/keon) +* [Rahul Goswami](https://github.com/goswami-rahul) +* [Christian Bender](https://github.com/christianbender) +* [Ankit Agarwal](https://github.com/ankit167) +* [Hai Hoang Dang](https://github.com/danghai) +* [Saad](https://github.com/SaadBenn) + +그리고 이 저장소를 만드는데 도움을 준 [모든 기여자](https://github.com/keon/algorithms/graphs/contributors) +분 들에게 감사를 표합니다. diff --git a/README_PTBR.md b/README_PTBR.md new file mode 100644 index 000000000..2870a1546 --- /dev/null +++ b/README_PTBR.md @@ -0,0 +1,358 @@ +

+ +[English](README.md) | [简体中文](README_CN.md) | [Deutsch](README_GE.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | Português + +[![PyPI version](https://badge.fury.io/py/algorithms.svg)](https://badge.fury.io/py/algorithms) +[![Open Source Helpers](https://www.codetriage.com/keon/algorithms/badges/users.svg)](https://www.codetriage.com/keon/algorithms) +[![Build Status](https://travis-ci.org/keon/algorithms.svg?branch=master)](https://travis-ci.org/keon/algorithms) +[![Coverage Status](https://coveralls.io/repos/github/keon/algorithms/badge.svg?branch=master)](https://coveralls.io/github/keon/algorithms?branch=master) + +Estruturas de Dados e Algoritmos Pythonicos +========================================= + +Exemplos de implementações mínimas e limpas de estruturas de dados e algoritmos em Python 3. + +## Contribuir +Obrigado pelo seu interesse em contribuir! Há muitas maneiras de contribuir para este projeto. [Comece aqui](CONTRIBUTING.md) + +## Testes + +### Usando unittest +Para executar todos os testes, digite: + + $ python3 -m unittest discover tests + +Para executar algum teste específico, você pode fazer isso da seguinte maneira (Ex.: sort): + + $ python3 -m unittest tests.test_sort + +### Usando pytest +Para executar todos os testes, digite: + + $ python3 -m pytest tests + +## Instalar +Se você quiser usar os algoritmos da API em seu código, é tão simples quanto: + + $ pip3 install algorithms + +Você pode testar criando um arquivo python: (Ex.: usando `merge_sort` em `sort`) + +```python3 +from algorithms.sort import merge_sort + +if __name__ == "__main__": + my_list = [1, 8, 3, 5, 6] + my_list = merge_sort(my_list) + print(my_list) +``` + +## Desinstalar +Se você deseja desinstalar os algoritmos, é tão simples quanto: + + $ pip3 uninstall -y algorithms + +## Lista de Implementações + +- [arrays](algorithms/arrays) + - [delete_nth](algorithms/arrays/delete_nth.py) + - [flatten](algorithms/arrays/flatten.py) + - [garage](algorithms/arrays/garage.py) + - [josephus_problem](algorithms/arrays/josephus.py) + - [limit](algorithms/arrays/limit.py) + - [longest_non_repeat](algorithms/arrays/longest_non_repeat.py/) + - [max_ones_index](algorithms/arrays/max_ones_index.py) + - [merge_intervals](algorithms/arrays/merge_intervals.py) + - [missing_ranges](algorithms/arrays/missing_ranges.py) + - [plus_one](algorithms/arrays/plus_one.py) + - [rotate](algorithms/arrays/rotate.py) + - [summarize_ranges](algorithms/arrays/summarize_ranges.py) + - [three_sum](algorithms/arrays/three_sum.py) + - [trimmean](algorithms/arrays/trimmean.py) + - [top_1](algorithms/arrays/top_1.py) + - [two_sum](algorithms/arrays/two_sum.py) + - [move_zeros](algorithms/arrays/move_zeros.py) +- [backtrack](algorithms/backtrack) + - [general_solution.md](algorithms/backtrack/) + - [anagram](algorithms/backtrack/anagram.py) + - [array_sum_combinations](algorithms/backtrack/array_sum_combinations.py) + - [combination_sum](algorithms/backtrack/combination_sum.py) + - [expression_add_operators](algorithms/backtrack/expression_add_operators.py) + - [factor_combinations](algorithms/backtrack/factor_combinations.py) + - [generate_abbreviations](algorithms/backtrack/generate_abbreviations.py) + - [generate_parenthesis](algorithms/backtrack/generate_parenthesis.py) + - [letter_combination](algorithms/backtrack/letter_combination.py) + - [palindrome_partitioning](algorithms/backtrack/palindrome_partitioning.py) + - [pattern_match](algorithms/backtrack/pattern_match.py) + - [permute](algorithms/backtrack/permute.py) + - [permute_unique](algorithms/backtrack/permute_unique.py) + - [subsets](algorithms/backtrack/subsets.py) + - [subsets_unique](algorithms/backtrack/subsets_unique.py) +- [bfs](algorithms/bfs) + - [maze_search](algorithms/bfs/maze_search.py) + - [shortest_distance_from_all_buildings](algorithms/bfs/shortest_distance_from_all_buildings.py) + - [word_ladder](algorithms/bfs/word_ladder.py) +- [bit](algorithms/bit) + - [add_bitwise_operator](algorithms/bit/add_bitwise_operator.py) + - [bit_operation](algorithms/bit/bit_operation.py) + - [bytes_int_conversion](algorithms/bit/bytes_int_conversion.py) + - [count_flips_to_convert](algorithms/bit/count_flips_to_convert.py) + - [count_ones](algorithms/bit/count_ones.py) + - [find_difference](algorithms/bit/find_difference.py) + - [find_missing_number](algorithms/bit/find_missing_number.py) + - [flip_bit_longest_sequence](algorithms/bit/flip_bit_longest_sequence.py) + - [power_of_two](algorithms/bit/power_of_two.py) + - [reverse_bits](algorithms/bit/reverse_bits.py) + - [single_number](algorithms/bit/single_number.py) + - [single_number2](algorithms/bit/single_number2.py) + - [single_number3](algorithms/bit/single_number3.py) + - [subsets](algorithms/bit/subsets.py) + - [swap_pair](algorithms/bit/swap_pair.py) + - [has_alternative_bit](algorithms/bit/has_alternative_bit.py) + - [insert_bit](algorithms/bit/insert_bit.py) + - [remove_bit](algorithms/bit/remove_bit.py) +- [calculator](algorithms/calculator) + - [math_parser](algorithms/calculator/math_parser.py) +- [dfs](algorithms/dfs) + - [all_factors](algorithms/dfs/all_factors.py) + - [count_islands](algorithms/dfs/count_islands.py) + - [pacific_atlantic](algorithms/dfs/pacific_atlantic.py) + - [sudoku_solver](algorithms/dfs/sudoku_solver.py) + - [walls_and_gates](algorithms/dfs/walls_and_gates.py) +- [dp](algorithms/dp) + - [buy_sell_stock](algorithms/dp/buy_sell_stock.py) + - [climbing_stairs](algorithms/dp/climbing_stairs.py) + - [coin_change](algorithms/dp/coin_change.py) + - [combination_sum](algorithms/dp/combination_sum.py) + - [egg_drop](algorithms/dp/egg_drop.py) + - [house_robber](algorithms/dp/house_robber.py) + - [job_scheduling](algorithms/dp/job_scheduling.py) + - [knapsack](algorithms/dp/knapsack.py) + - [longest_increasing](algorithms/dp/longest_increasing.py) + - [matrix_chain_order](algorithms/dp/matrix_chain_order.py) + - [max_product_subarray](algorithms/dp/max_product_subarray.py) + - [max_subarray](algorithms/dp/max_subarray.py) + - [min_cost_path](algorithms/dp/min_cost_path.py) + - [num_decodings](algorithms/dp/num_decodings.py) + - [regex_matching](algorithms/dp/regex_matching.py) + - [rod_cut](algorithms/dp/rod_cut.py) + - [word_break](algorithms/dp/word_break.py) + - [fibonacci](algorithms/dp/fib.py) +- [graph](algorithms/graph) + - [check_bipartite](algorithms/graph/check_bipartite.py) + - [strongly_connected](algorithms/graph/checkDiGraphStronglyConnected.py) + - [clone_graph](algorithms/graph/clone_graph.py) + - [cycle_detection](algorithms/graph/cycle_detection.py) + - [find_all_cliques](algorithms/graph/find_all_cliques.py) + - [find_path](algorithms/graph/find_path.py) + - [graph](algorithms/graph/graph.py) + - [markov_chain](algorithms/graph/markov_chain.py) + - [minimum_spanning_tree](algorithms/graph/minimum_spanning_tree.py) + - [satisfiability](algorithms/graph/satisfiability.py) + - [tarjan](algorithms/graph/tarjan.py) + - [traversal](algorithms/graph/traversal.py) +- [heap](algorithms/heap) + - [merge_sorted_k_lists](algorithms/heap/merge_sorted_k_lists.py) + - [skyline](algorithms/heap/skyline.py) + - [sliding_window_max](algorithms/heap/sliding_window_max.py) + - [binary_heap](algorithms/heap/binary_heap.py) +- [linkedlist](algorithms/linkedlist) + - [add_two_numbers](algorithms/linkedlist/add_two_numbers.py) + - [copy_random_pointer](algorithms/linkedlist/copy_random_pointer.py) + - [delete_node](algorithms/linkedlist/delete_node.py) + - [first_cyclic_node](algorithms/linkedlist/first_cyclic_node.py) + - [is_cyclic](algorithms/linkedlist/is_cyclic.py) + - [is_palindrome](algorithms/linkedlist/is_palindrome.py) + - [kth_to_last](algorithms/linkedlist/kth_to_last.py) + - [linkedlist](algorithms/linkedlist/linkedlist.py) + - [remove_duplicates](algorithms/linkedlist/remove_duplicates.py) + - [reverse](algorithms/linkedlist/reverse.py) + - [rotate_list](algorithms/linkedlist/rotate_list.py) + - [swap_in_pairs](algorithms/linkedlist/swap_in_pairs.py) + - [is_sorted](algorithms/linkedlist/is_sorted.py) + - [remove_range](algorithms/linkedlist/remove_range.py) +- [map](algorithms/map) + - [hashtable](algorithms/map/hashtable.py) + - [separate_chaining_hashtable](algorithms/map/separate_chaining_hashtable.py) + - [longest_common_subsequence](algorithms/map/longest_common_subsequence.py) + - [randomized_set](algorithms/map/randomized_set.py) + - [valid_sudoku](algorithms/map/valid_sudoku.py) +- [maths](algorithms/maths) + - [base_conversion](algorithms/maths/base_conversion.py) + - [combination](algorithms/maths/combination.py) + - [decimal_to_binary_ip](algorithms/maths/decimal_to_binary_ip.py) + - [extended_gcd](algorithms/maths/extended_gcd.py) + - [factorial](algorithms/maths/factorial.py) + - [gcd/lcm](algorithms/maths/gcd.py) + - [generate_strobogrammtic](algorithms/maths/generate_strobogrammtic.py) + - [is_strobogrammatic](algorithms/maths/is_strobogrammatic.py) + - [next_bigger](algorithms/maths/next_bigger.py) + - [next_perfect_square](algorithms/maths/next_perfect_square.py) + - [nth_digit](algorithms/maths/nth_digit.py) + - [prime_check](algorithms/maths/prime_check.py) + - [primes_sieve_of_eratosthenes](algorithms/maths/primes_sieve_of_eratosthenes.py) + - [pythagoras](algorithms/maths/pythagoras.py) + - [rabin_miller](algorithms/maths/rabin_miller.py) + - [rsa](algorithms/maths/rsa.py) + - [sqrt_precision_factor](algorithms/maths/sqrt_precision_factor.py) + - [summing_digits](algorithms/maths/summing_digits.py) +- [matrix](algorithms/matrix) + - [sudoku_validator](algorithms/matrix/sudoku_validator.py) + - [bomb_enemy](algorithms/matrix/bomb_enemy.py) + - [copy_transform](algorithms/matrix/copy_transform.py) + - [count_paths](algorithms/matrix/count_paths.py) + - [matrix_rotation.txt](algorithms/matrix/matrix_rotation.txt) + - [rotate_image](algorithms/matrix/rotate_image.py) + - [search_in_sorted_matrix](algorithms/matrix/search_in_sorted_matrix.py) + - [sparse_dot_vector](algorithms/matrix/sparse_dot_vector.py) + - [sparse_mul](algorithms/matrix/sparse_mul.py) + - [spiral_traversal](algorithms/matrix/spiral_traversal.py) +- [queues](algorithms/queues) + - [max_sliding_window](algorithms/queues/max_sliding_window.py) + - [moving_average](algorithms/queues/moving_average.py) + - [queue](algorithms/queues/queue.py) + - [reconstruct_queue](algorithms/queues/reconstruct_queue.py) + - [zigzagiterator](algorithms/queues/zigzagiterator.py) +- [search](algorithms/search) + - [binary_search](algorithms/search/binary_search.py) + - [first_occurance](algorithms/search/first_occurance.py) + - [last_occurance](algorithms/search/last_occurance.py) + - [linear_search](algorithms/search/linear_search.py) + - [search_insert](algorithms/search/search_insert.py) + - [two_sum](algorithms/search/two_sum.py) + - [search_range](algorithms/search/search_range.py) + - [find_min_rotate](algorithms/search/find_min_rotate.py) + - [search_rotate](algorithms/search/search_rotate.py) + - [jump_search](algorithms/search/jump_search.py) +- [set](algorithms/set) + - [randomized_set](algorithms/set/randomized_set.py) + - [set_covering](algorithms/set/set_covering.py) +- [sort](algorithms/sort) + - [bitonic_sort](algorithms/sort/bitonic_sort.py) + - [bogo_sort](algorithms/sort/bogo_sort.py) + - [bubble_sort](algorithms/sort/bubble_sort.py) + - [bucket_sort](algorithms/sort/bucket_sort.py) + - [cocktail_shaker_sort](algorithms/sort/cocktail_shaker_sort.py) + - [comb_sort](algorithms/sort/comb_sort.py) + - [counting_sort](algorithms/sort/counting_sort.py) + - [cycle_sort](algorithms/sort/cycle_sort.py) + - [gnome_sort](algorithms/sort/gnome_sort.py) + - [heap_sort](algorithms/sort/heap_sort.py) + - [insertion_sort](algorithms/sort/insertion_sort.py) + - [meeting_rooms](algorithms/sort/meeting_rooms.py) + - [merge_sort](algorithms/sort/merge_sort.py) + - [pancake_sort](algorithms/sort/pancake_sort.py) + - [quick_sort](algorithms/sort/quick_sort.py) + - [radix_sort](algorithms/sort/radix_sort.py) + - [selection_sort](algorithms/sort/selection_sort.py) + - [shell_sort](algorithms/sort/shell_sort.py) + - [sort_colors](algorithms/sort/sort_colors.py) + - [top_sort](algorithms/sort/top_sort.py) + - [wiggle_sort](algorithms/sort/wiggle_sort.py) +- [stack](algorithms/stack) + - [longest_abs_path](algorithms/stack/longest_abs_path.py) + - [simplify_path](algorithms/stack/simplify_path.py) + - [stack](algorithms/stack/stack.py) + - [valid_parenthesis](algorithms/stack/valid_parenthesis.py) + - [stutter](algorithms/stack/stutter.py) + - [switch_pairs](algorithms/stack/switch_pairs.py) + - [is_consecutive](algorithms/stack/is_consecutive.py) + - [remove_min](algorithms/stack/remove_min.py) + - [is_sorted](algorithms/stack/is_sorted.py) +- [strings](algorithms/strings) + - [fizzbuzz](algorithms/strings/fizzbuzz.py) + - [delete_reoccurring_characters](algorithms/strings/delete_reoccurring_characters.py) + - [strip_url_params](algorithms/strings/strip_url_params.py) + - [validate_coordinates](algorithms/strings/validate_coordinates.py) + - [domain_extractor](algorithms/strings/domain_extractor.py) + - [merge_string_checker](algorithms/strings/merge_string_checker.py) + - [add_binary](algorithms/strings/add_binary.py) + - [breaking_bad](algorithms/strings/breaking_bad.py) + - [decode_string](algorithms/strings/decode_string.py) + - [encode_decode](algorithms/strings/encode_decode.py) + - [group_anagrams](algorithms/strings/group_anagrams.py) + - [int_to_roman](algorithms/strings/int_to_roman.py) + - [is_palindrome](algorithms/strings/is_palindrome.py) + - [license_number](algorithms/strings/license_number.py) + - [make_sentence](algorithms/strings/make_sentence.py) + - [multiply_strings](algorithms/strings/multiply_strings.py) + - [one_edit_distance](algorithms/strings/one_edit_distance.py) + - [rabin_karp](algorithms/strings/rabin_karp.py) + - [reverse_string](algorithms/strings/reverse_string.py) + - [reverse_vowel](algorithms/strings/reverse_vowel.py) + - [reverse_words](algorithms/strings/reverse_words.py) + - [roman_to_int](algorithms/strings/roman_to_int.py) + - [word_squares](algorithms/strings/word_squares.py) + - [unique_morse](algorithms/strings/unique_morse.py) + - [judge_circle](algorithms/strings/judge_circle.py) + - [strong_password](algorithms/strings/strong_password.py) + - [caesar_cipher](algorithms/strings/caesar_cipher.py) + - [contain_string](algorithms/strings/contain_string.py) + - [count_binary_substring](algorithms/strings/count_binary_substring.py) +- [tree](algorithms/tree) + - [bst](algorithms/tree/tree/bst) + - [array2bst](algorithms/tree/bst/array2bst.py) + - [bst_closest_value](algorithms/tree/bst/bst_closest_value.py) + - [BSTIterator](algorithms/tree/bst/BSTIterator.py) + - [delete_node](algorithms/tree/bst/delete_node.py) + - [is_bst](algorithms/tree/bst/is_bst.py) + - [kth_smallest](algorithms/tree/bst/kth_smallest.py) + - [lowest_common_ancestor](algorithms/tree/bst/lowest_common_ancestor.py) + - [predecessor](algorithms/tree/bst/predecessor.py) + - [serialize_deserialize](algorithms/tree/bst/serialize_deserialize.py) + - [successor](algorithms/tree/bst/successor.py) + - [unique_bst](algorithms/tree/bst/unique_bst.py) + - [depth_sum](algorithms/tree/bst/depth_sum.py) + - [count_left_node](algorithms/tree/bst/count_left_node.py) + - [num_empty](algorithms/tree/bst/num_empty.py) + - [height](algorithms/tree/bst/height.py) + - [red_black_tree](algorithms/tree/red_black_tree) + - [red_black_tree](algorithms/tree/red_black_tree/red_black_tree.py) + - [segment_tree](algorithms/tree/segment_tree) + - [segment_tree](algorithms/tree/segment_tree/segment_tree.py) + - [traversal](algorithms/tree/traversal) + - [inorder](algorithms/tree/traversal/inorder.py) + - [level_order](algorithms/tree/traversal/level_order.py) + - [postorder](algorithms/tree/traversal/postorder.py) + - [preorder](algorithms/tree/traversal/preorder.py) + - [zigzag](algorithms/tree/traversal/zigzag.py) + - [trie](algorithms/tree/trie) + - [add_and_search](algorithms/tree/trie/add_and_search.py) + - [trie](algorithms/tree/trie/trie.py) + - [binary_tree_paths](algorithms/tree/binary_tree_paths.py) + - [bintree2list](algorithms/tree/bintree2list.py) + - [deepest_left](algorithms/tree/deepest_left.py) + - [invert_tree](algorithms/tree/invert_tree.py) + - [is_balanced](algorithms/tree/is_balanced.py) + - [is_subtree](algorithms/tree/is_subtree.py) + - [is_symmetric](algorithms/tree/is_symmetric.py) + - [longest_consecutive](algorithms/tree/longest_consecutive.py) + - [lowest_common_ancestor](algorithms/tree/lowest_common_ancestor.py) + - [max_height](algorithms/tree/max_height.py) + - [max_path_sum](algorithms/tree/max_path_sum.py) + - [min_height](algorithms/tree/min_height.py) + - [path_sum](algorithms/tree/path_sum.py) + - [path_sum2](algorithms/tree/path_sum2.py) + - [pretty_print](algorithms/tree/pretty_print.py) + - [same_tree](algorithms/tree/same_tree.py) + - [tree](algorithms/tree/tree.py) +- [unix](algorithms/unix) + - [path](algorithms/unix/path/) + - [join_with_slash](algorithms/unix/path/join_with_slash.py) + - [full_path](algorithms/unix/path/full_path.py) + - [split](algorithms/unix/path/split.py) +- [union-find](algorithms/union-find) + - [count_islands](algorithms/union-find/count_islands.py) + +## Contribuidores +O repositório é mantido por + +* [Keon Kim](https://github.com/keon) +* [Rahul Goswami](https://github.com/goswami-rahul) +* [Christian Bender](https://github.com/christianbender) +* [Ankit Agarwal](https://github.com/ankit167) +* [Hai Hoang Dang](https://github.com/danghai) +* [Saad](https://github.com/SaadBenn) + +Obrigado a [todos os contribuidores](https://github.com/keon/algorithms/graphs/contributors) +que ajudaram na construção do repositório. diff --git a/algorithms/__init__.py b/algorithms/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/algorithms/arrays/__init__.py b/algorithms/arrays/__init__.py new file mode 100644 index 000000000..9670db750 --- /dev/null +++ b/algorithms/arrays/__init__.py @@ -0,0 +1,18 @@ +from .delete_nth import * +from .flatten import * +from .garage import * +from .josephus import * +from .longest_non_repeat import * +from .max_ones_index import * +from .merge_intervals import * +from .missing_ranges import * +from .move_zeros import * +from .plus_one import * +from .rotate import * +from .summarize_ranges import * +from .three_sum import * +from .trimmean import * +from .top_1 import * +from .two_sum import * +from .limit import * +from .n_sum import * diff --git a/algorithms/arrays/delete_nth.py b/algorithms/arrays/delete_nth.py new file mode 100644 index 000000000..8d768a4ed --- /dev/null +++ b/algorithms/arrays/delete_nth.py @@ -0,0 +1,32 @@ +""" +Given a list lst and a number N, create a new list +that contains each number of the list at most N times without reordering. + +For example if N = 2, and the input is [1,2,3,1,2,1,2,3], you take [1,2,3,1,2], +drop the next [1,2] since this would lead to 1 and 2 being in the result 3 times, and then take 3, +which leads to [1,2,3,1,2,3] +""" +import collections + + +# Time complexity O(n^2) +def delete_nth_naive(array, n): + ans = [] + for num in array: + if ans.count(num) < n: + ans.append(num) + return ans + + +# Time Complexity O(n), using hash tables. +def delete_nth(array, n): + result = [] + counts = collections.defaultdict(int) # keep track of occurrences + + for i in array: + + if counts[i] < n: + result.append(i) + counts[i] += 1 + + return result diff --git a/algorithms/arrays/flatten.py b/algorithms/arrays/flatten.py new file mode 100644 index 000000000..f2eba48fe --- /dev/null +++ b/algorithms/arrays/flatten.py @@ -0,0 +1,31 @@ +""" +Implement Flatten Arrays. +Given an array that may contain nested arrays, +produce a single resultant array. +""" +from collections import Iterable + + +# return list +def flatten(input_arr, output_arr=None): + if output_arr is None: + output_arr = [] + for ele in input_arr: + if isinstance(ele, Iterable): + flatten(ele, output_arr) + else: + output_arr.append(ele) + return output_arr + + +# returns iterator +def flatten_iter(iterable): + """ + Takes as input multi dimensional iterable and + returns generator which produces one dimensional output. + """ + for element in iterable: + if isinstance(element, Iterable): + yield from flatten_iter(element) + else: + yield element diff --git a/algorithms/arrays/garage.py b/algorithms/arrays/garage.py new file mode 100644 index 000000000..d590343b7 --- /dev/null +++ b/algorithms/arrays/garage.py @@ -0,0 +1,54 @@ +""" +There is a parking lot with only one empty spot. Given the initial state +of the parking lot and the final state. Each step we are only allowed to +move a car +out of its place and move it into the empty spot. +The goal is to find out the least movement needed to rearrange +the parking lot from the initial state to the final state. + +Say the initial state is an array: + +[1, 2, 3, 0, 4], +where 1, 2, 3, 4 are different cars, and 0 is the empty spot. + +And the final state is + +[0, 3, 2, 1, 4]. +We can swap 1 with 0 in the initial array to get [0, 2, 3, 1, 4] and so on. +Each step swap with 0 only. + +Edit: +Now also prints the sequence of changes in states. +Output of this example :- + +initial: [1, 2, 3, 0, 4] +final: [0, 3, 2, 1, 4] +Steps = 4 +Sequence : +0 2 3 1 4 +2 0 3 1 4 +2 3 0 1 4 +0 3 2 1 4 +""" + + +def garage(initial, final): + + initial = initial[::] # create a copy to prevent changes in original 'initial'. + steps = 0 + seq = [] # list of each step in sequence + while initial != final: + zero = initial.index(0) + if zero != final.index(0): + car_to_move = final[zero] + pos = initial.index(car_to_move) + initial[zero], initial[pos] = initial[pos], initial[zero] + else: + for i in range(len(initial)): + if initial[i] != final[i]: + initial[zero], initial[i] = initial[i], initial[zero] + break + seq.append(initial[::]) + steps += 1 + + return steps, seq diff --git a/algorithms/arrays/josephus.py b/algorithms/arrays/josephus.py new file mode 100644 index 000000000..f77c48a5c --- /dev/null +++ b/algorithms/arrays/josephus.py @@ -0,0 +1,20 @@ +""" +There are people sitting in a circular fashion, +print every third member while removing them, +the next counter starts immediately after the member is removed. +Print till all the members are exhausted. + +For example: +Input: consider 123456789 members sitting in a circular fashion, +Output: 369485271 +""" + + +def josephus(int_list, skip): + skip = skip - 1 # list starts with 0 index + idx = 0 + len_list = (len(int_list)) + while len_list > 0: + idx = (skip+idx) % len_list # hash index to every 3rd + yield int_list.pop(idx) + len_list -= 1 diff --git a/algorithms/arrays/limit.py b/algorithms/arrays/limit.py new file mode 100644 index 000000000..adcc57797 --- /dev/null +++ b/algorithms/arrays/limit.py @@ -0,0 +1,30 @@ +""" +Sometimes you need to limit array result to use. Such as you only need the value over +10 or, you need value under than 100. By use this algorithms, you can limit your array +to specific value + +If array, Min, Max value was given, it returns array that contains values of given array +which was larger than Min, and lower than Max. You need to give 'unlimit' to use only Min +or Max. + +ex) limit([1,2,3,4,5], None, 3) = [1,2,3] + +Complexity = O(n) +""" + +def limit(arr, min_lim = None, max_lim = None): + result = [] + if min_lim == None: + for i in arr: + if i<= max_lim: + result.append(i) + elif max_lim == None: + for i in arr: + if i >= min_lim: + result.append(i) + else: + for i in arr: + if i >= min_lim and i <= max_lim: + result.append(i) + + return result diff --git a/algorithms/arrays/longest_non_repeat.py b/algorithms/arrays/longest_non_repeat.py new file mode 100644 index 000000000..e97efcca0 --- /dev/null +++ b/algorithms/arrays/longest_non_repeat.py @@ -0,0 +1,46 @@ +""" +Given a string, find the length of the longest substring +without repeating characters. +Examples: +Given "abcabcbb", the answer is "abc", which the length is 3. +Given "bbbbb", the answer is "b", with the length of 1. +Given "pwwkew", the answer is "wke", with the length of 3. +Note that the answer must be a substring, +"pwke" is a subsequence and not a substring. +""" + + +def longest_non_repeat_v1(string): + """ + Finds the length of the longest substring + without repeating characters. + """ + if string is None: + return 0 + temp = [] + max_len = 0 + for i in string: + if i in temp: + temp = [] + temp.append(i) + max_len = max(max_len, len(temp)) + return max_len + + +def longest_non_repeat_v2(string): + """ + Finds the length of the longest substring + without repeating characters. + Uses alternative algorithm. + """ + if string is None: + return 0 + start, max_len = 0, 0 + used_char = {} + for index, char in enumerate(string): + if char in used_char and start <= used_char[char]: + start = used_char[char] + 1 + else: + max_len = max(max_len, index - start + 1) + used_char[char] = index + return max_len diff --git a/algorithms/arrays/max_ones_index.py b/algorithms/arrays/max_ones_index.py new file mode 100644 index 000000000..40abdf505 --- /dev/null +++ b/algorithms/arrays/max_ones_index.py @@ -0,0 +1,43 @@ +""" +Find the index of 0 to be replaced with 1 to get +longest continuous sequence +of 1s in a binary array. +Returns index of 0 to be +replaced with 1 to get longest +continuous sequence of 1s. +If there is no 0 in array, then +it returns -1. + +e.g. +let input array = [1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1] +If we replace 0 at index 3 with 1, we get the longest continuous +sequence of 1s in the array. +So the function return => 3 +""" + + +def max_ones_index(arr): + + n = len(arr) + max_count = 0 + max_index = 0 + prev_zero = -1 + prev_prev_zero = -1 + + for curr in range(n): + + # If current element is 0, + # then calculate the difference + # between curr and prev_prev_zero + if arr[curr] == 0: + if curr - prev_prev_zero > max_count: + max_count = curr - prev_prev_zero + max_index = prev_zero + + prev_prev_zero = prev_zero + prev_zero = curr + + if n - prev_prev_zero > max_count: + max_index = prev_zero + + return max_index diff --git a/algorithms/arrays/merge_intervals.py b/algorithms/arrays/merge_intervals.py new file mode 100644 index 000000000..ee9ce6053 --- /dev/null +++ b/algorithms/arrays/merge_intervals.py @@ -0,0 +1,78 @@ +""" +Given a collection of intervals, merge all overlapping intervals. +""" + + +class Interval: + """ + In mathematics, a (real) interval is a set of real + numbers with the property that any number that lies + between two numbers in the set is also included in the set. + """ + + def __init__(self, start=0, end=0): + self.start = start + self.end = end + + def __repr__(self): + return "Interval ({}, {})".format(self.start, self.end) + + def __iter__(self): + return iter(range(self.start, self.end)) + + def __getitem__(self, index): + if index < 0: + return self.end + index + return self.start + index + + def __len__(self): + return self.end - self.start + + def __contains__(self, item): + if self.start >= item >= self.end: + return True + return False + + def __eq__(self, other): + if self.start == other.start and self.end == other.end: + return True + return False + + def as_list(self): + """ Return interval as list. """ + return list(self) + + @staticmethod + def merge(intervals): + """ Merges two intervals into one. """ + out = [] + for i in sorted(intervals, key=lambda i: i.start): + if out and i.start <= out[-1].end: + out[-1].end = max(out[-1].end, i.end) + else: + out += i, + return out + + @staticmethod + def print_intervals(intervals): + """ + Prints out the intervals. + """ + res = [] + for i in intervals: + res.append(repr(i)) + print("".join(res)) + + +def merge_intervals(intervals): + """ Merges intervals in the form of list. """ + if intervals is None: + return None + intervals.sort(key=lambda i: i[0]) + out = [intervals.pop(0)] + for i in intervals: + if out[-1][-1] >= i[0]: + out[-1][-1] = max(out[-1][-1], i[-1]) + else: + out.append(i) + return out diff --git a/algorithms/arrays/missing_ranges.py b/algorithms/arrays/missing_ranges.py new file mode 100644 index 000000000..68fcc4391 --- /dev/null +++ b/algorithms/arrays/missing_ranges.py @@ -0,0 +1,23 @@ +""" +Find missing ranges between low and high in the given array. +Ex) [3, 5] lo=1 hi=10 => answer: [(1, 2), (4, 4), (6, 10)] +""" + + +def missing_ranges(arr, lo, hi): + + res = [] + start = lo + + for n in arr: + + if n == start: + start += 1 + elif n > start: + res.append((start, n-1)) + start = n + 1 + + if start <= hi: + res.append((start, hi)) + + return res diff --git a/algorithms/arrays/move_zeros.py b/algorithms/arrays/move_zeros.py new file mode 100644 index 000000000..b2acd759d --- /dev/null +++ b/algorithms/arrays/move_zeros.py @@ -0,0 +1,22 @@ +""" +Write an algorithm that takes an array and moves all of the zeros to the end, +preserving the order of the other elements. + move_zeros([false, 1, 0, 1, 2, 0, 1, 3, "a"]) + returns => [false, 1, 1, 2, 1, 3, "a", 0, 0] + +The time complexity of the below algorithm is O(n). +""" + + +def move_zeros(array): + result = [] + zeros = 0 + + for i in array: + if i is 0: # not using `not i` to avoid `False`, `[]`, etc. + zeros += 1 + else: + result.append(i) + + result.extend([0] * zeros) + return result diff --git a/algorithms/arrays/n_sum.py b/algorithms/arrays/n_sum.py new file mode 100644 index 000000000..a01348e57 --- /dev/null +++ b/algorithms/arrays/n_sum.py @@ -0,0 +1,129 @@ +""" +Given an array of n integers, are there elements a, b, .. , n in nums +such that a + b + .. + n = target? + +Find all unique triplets in the array which gives the sum of target. + +Example: + basic: + Given: + n = 4, nums = [1, 0, -1, 0, -2, 2], target = 0, + return [[-2, -1, 1, 2], [-2, 0, 0, 2], [-1, 0, 0, 1]] + + advanced: + Given: + n = 2 + nums = [[-3, 0], [-2, 1], [2, 2], [3, 3], [8, 4], [-9, 5]] + taget = -5 + def sum(a, b): + return [a[0] + b[1], a[1] + b[0]] + def compare(num, taget): + if num[0] < taget: + return -1 + elif if num[0] > taget: + return 1 + else: + return 0 + return [[-9, 5], [8, 4]] + because -9 + 4 = -5 +""" + + +def n_sum(n, nums, target, **kv): + """ + n: int + nums: list[object] + target: object + sum_closure: function, optional + Given two elements of nums, return sum of both. + compare_closure: function, optional + Given one object of nums and target, return one of -1, 1, or 0. + same_closure: function, optional + Given two object of nums, return bool. + return: list[list[object]] + + Note: + 1. type of sum_closure's return should be same as type of compare_closure's first param + """ + + def sum_closure_default(a, b): + return a + b + + def compare_closure_default(num, taget): + if num < taget: + return -1 + elif num > taget: + return 1 + else: + return 0 + + def same_closure_default(a, b): + return a == b + + def n_sum(n, nums, target): + if n == 2: + results = two_sum(nums, target) + else: + results = [] + prev_num = None + for index, num in enumerate(nums): + if prev_num is not None and \ + same_closure(prev_num, num): + continue + prev_num = num + n_minus1_results = n_sum(n - 1, + nums[index + 1:], + target - num) + n_minus1_results = append_elem_to_each_list(num, + n_minus1_results) + results += n_minus1_results + return union(results) + + def two_sum(nums, target): + nums.sort() + lt = 0 + rt = len(nums) - 1 + results = [] + while lt < rt: + sum_ = sum_closure(nums[lt], nums[rt]) + flag = compare_closure(sum_, target) + if flag == -1: + lt += 1 + elif flag == 1: + rt -= 1 + else: + results.append(sorted([nums[lt], nums[rt]])) + lt += 1 + rt -= 1 + while (lt < len(nums) and + same_closure(nums[lt - 1], nums[lt])): + lt += 1 + while (0 <= rt and + same_closure(nums[rt], nums[rt + 1])): + rt -= 1 + return results + + def append_elem_to_each_list(elem, container): + results = [] + for elems in container: + elems.append(elem) + results.append(sorted(elems)) + return results + + def union(duplicate_results): + results = [] + + if len(duplicate_results) != 0: + duplicate_results.sort() + results.append(duplicate_results[0]) + for result in duplicate_results[1:]: + if results[-1] != result: + results.append(result) + + return results + + sum_closure = kv.get('sum_closure', sum_closure_default) + same_closure = kv.get('same_closure', same_closure_default) + compare_closure = kv.get('compare_closure', compare_closure_default) + nums.sort() + return n_sum(n, nums, target) diff --git a/array/plus_one.py b/algorithms/arrays/plus_one.py similarity index 62% rename from array/plus_one.py rename to algorithms/arrays/plus_one.py index 65c93e34d..d5bf347f6 100644 --- a/array/plus_one.py +++ b/algorithms/arrays/plus_one.py @@ -1,11 +1,13 @@ -# Given a non-negative number represented as an array of digits, -# plus one to the number. +""" +Given a non-negative number represented as an array of digits, +plus one to the number. -# The digits are stored such that the most significant -# digit is at the head of the list. +The digits are stored such that the most significant +digit is at the head of the list. +""" -def plusOne(digits): +def plus_one_v1(digits): """ :type digits: List[int] :rtype: List[int] @@ -15,18 +17,18 @@ def plusOne(digits): ten = 0 i = len(digits)-1 while i >= 0 or ten == 1: - sum = 0 + summ = 0 if i >= 0: - sum += digits[i] + summ += digits[i] if ten: - sum += 1 - res.append(sum % 10) - ten = sum / 10 + summ += 1 + res.append(summ % 10) + ten = summ // 10 i -= 1 return res[::-1] -def plus_one(digits): +def plus_one_v2(digits): n = len(digits) for i in range(n-1, -1, -1): if digits[i] < 9: @@ -37,7 +39,8 @@ def plus_one(digits): return digits -def plus_1(num_arr): +def plus_one_v3(num_arr): + for idx, digit in reversed(list(enumerate(num_arr))): num_arr[idx] = (num_arr[idx] + 1) % 10 if num_arr[idx]: diff --git a/algorithms/arrays/rotate.py b/algorithms/arrays/rotate.py new file mode 100644 index 000000000..d90035b58 --- /dev/null +++ b/algorithms/arrays/rotate.py @@ -0,0 +1,61 @@ +""" +Rotate an array of n elements to the right by k steps. + +For example, with n = 7 and k = 3, +the array [1,2,3,4,5,6,7] is rotated to [5,6,7,1,2,3,4]. + +Note: +Try to come up as many solutions as you can, +there are at least 3 different ways to solve this problem. +""" + + +def rotate_v1(array, k): + """ + Rotate the entire array 'k' times + T(n)- O(nk) + + :type array: List[int] + :type k: int + :rtype: void Do not return anything, modify array in-place instead. + """ + array = array[:] + n = len(array) + for i in range(k): + temp = array[n - 1] + for j in range(n-1, 0, -1): + array[j] = array[j - 1] + array[0] = temp + return array + + +def rotate_v2(array, k): + """ + Reverse segments of the array, followed by the entire array + T(n)- O(n) + :type array: List[int] + :type k: int + :rtype: void Do not return anything, modify nums in-place instead. + """ + array = array[:] + + def reverse(arr, a, b): + while a < b: + arr[a], arr[b] = arr[b], arr[a] + a += 1 + b -= 1 + + n = len(array) + k = k % n + reverse(array, 0, n - k - 1) + reverse(array, n - k, n - 1) + reverse(array, 0, n - 1) + return array + + +def rotate_v3(array, k): + if array is None: + return None + length = len(array) + k = k % length + return array[length - k:] + array[:length - k] diff --git a/algorithms/arrays/summarize_ranges.py b/algorithms/arrays/summarize_ranges.py new file mode 100644 index 000000000..8cfba68e1 --- /dev/null +++ b/algorithms/arrays/summarize_ranges.py @@ -0,0 +1,27 @@ +""" +Given a sorted integer array without duplicates, +return the summary of its ranges. + +For example, given [0, 1, 2, 4, 5, 7], return [(0, 2), (4, 5), (7, 7)]. +""" + + +def summarize_ranges(array): + """ + :type array: List[int] + :rtype: List[] + """ + res = [] + if len(array) == 1: + return [str(array[0])] + i = 0 + while i < len(array): + num = array[i] + while i + 1 < len(array) and array[i + 1] - array[i] == 1: + i += 1 + if array[i] != num: + res.append((num, array[i])) + else: + res.append((num, num)) + i += 1 + return res diff --git a/array/three_sum.py b/algorithms/arrays/three_sum.py similarity index 53% rename from array/three_sum.py rename to algorithms/arrays/three_sum.py index 4b5753347..524b83edd 100644 --- a/array/three_sum.py +++ b/algorithms/arrays/three_sum.py @@ -8,39 +8,41 @@ For example, given array S = [-1, 0, 1, 2, -1, -4], A solution set is: -[ - [-1, 0, 1], - [-1, -1, 2] -] +{ + (-1, 0, 1), + (-1, -1, 2) +} """ -def three_sum(nums:"List[int]")->"List[int]": - res = [] - nums.sort() - for i in range(len(nums)-2): - if i > 0 and nums[i] == nums[i-1]: +def three_sum(array): + """ + :param array: List[int] + :return: Set[ Tuple[int, int, int] ] + """ + res = set() + array.sort() + for i in range(len(array) - 2): + if i > 0 and array[i] == array[i - 1]: continue - l, r = i+1, len(nums)-1 + l, r = i + 1, len(array) - 1 while l < r: - s = nums[i] + nums[l] + nums[r] + s = array[i] + array[l] + array[r] if s > 0: r -= 1 elif s < 0: l += 1 else: # found three sum - res.append((nums[i], nums[l], nums[r])) + res.add((array[i], array[l], array[r])) + # remove duplicates - while l < r and nums[l] == nums[l+1]: - l+=1 - while l < r and nums[r] == nums[r-1]: + while l < r and array[l] == array[l + 1]: + l += 1 + + while l < r and array[r] == array[r - 1]: r -= 1 + l += 1 r -= 1 return res - - -if __name__ == "__main__": - x = [-1,0,1,2,-1,-4] - print(three_sum(x)) diff --git a/algorithms/arrays/top_1.py b/algorithms/arrays/top_1.py new file mode 100644 index 000000000..dea9aa2f9 --- /dev/null +++ b/algorithms/arrays/top_1.py @@ -0,0 +1,32 @@ +""" +this algorithms receive array and check most_frequent_value(a.k.a mode). Also, sometimes it can be have numerous most_frequent_value, +so this funtion returns list. This result can be used as finding representative value on array. + +This algorithms get array, and make dictionary of it, find most frequent count, and make result list. + +For example) top_1([1, 1, 2, 2, 3, 4]) will return [1, 2] + +Complexity: O(n) +""" +def top_1(arr): + values = {} + #reserve each value which first appears on keys + #reserve how many time each value appears by index number on values + result = [] + f_val = 0 + + for i in arr: + if i in values: + values[i] += 1 + else: + values[i] = 1 + + f_val = max(values.values()) + + for i in values.keys(): + if values[i] == f_val: + result.append(i) + else: + continue + + return result diff --git a/algorithms/arrays/trimmean.py b/algorithms/arrays/trimmean.py new file mode 100644 index 000000000..4cea0db74 --- /dev/null +++ b/algorithms/arrays/trimmean.py @@ -0,0 +1,22 @@ +""" +When make reliable means, we need to neglect best and worst value. For example, when making average score on athletes we need this option. +So, this algorithms, fix some percentage to neglect when making mean. For example, if you suggest 20%, it will neglect best 10% value, and +worst 10% value. + +This algorithm gets array and percentage to neglect. After sorted, if index of array is larger or smaller or wanted ratio, we don't +compute it. + +Compleity: O(n) +""" +def trimmean(arr, per): + ratio = per/200 + # /100 for easy calculation by *, and /2 for easy adaption to best and worst parts. + cal_sum = 0 + # sum value to be calculated to trimmean. + arr.sort() + neg_val = int(len(arr)*ratio) + arr = arr[neg_val:len(arr)-neg_val] + for i in arr: + cal_sum += i + #print(cal_sum, len(arr)) + return cal_sum/len(arr) diff --git a/array/two_sum.py b/algorithms/arrays/two_sum.py similarity index 61% rename from array/two_sum.py rename to algorithms/arrays/two_sum.py index 29afd7919..2b6f1ed0b 100644 --- a/array/two_sum.py +++ b/algorithms/arrays/two_sum.py @@ -9,21 +9,15 @@ Given nums = [2, 7, 11, 15], target = 9, Because nums[0] + nums[1] = 2 + 7 = 9, - return [0, 1]. + return (0, 1) """ -def two_sum(nums:"List[int]", target:"int")->"List[int]": +def two_sum(array, target): dic = {} - for i, num in enumerate(nums): + for i, num in enumerate(array): if num in dic: - return [dic[num], i] + return dic[num], i else: dic[target - num] = i - - -if __name__ == "__main__": - arr = [3,2,4] - target = 6 - res = two_sum(arr, target) - print(res) + return None diff --git a/algorithms/backtrack/__init__.py b/algorithms/backtrack/__init__.py new file mode 100644 index 000000000..f8cdab753 --- /dev/null +++ b/algorithms/backtrack/__init__.py @@ -0,0 +1,15 @@ +from .add_operators import * +from .anagram import * +from .array_sum_combinations import * +from .combination_sum import * +from .factor_combinations import * +from .find_words import * +from .generate_abbreviations import * +from .generate_parenthesis import * +from .letter_combination import * +from .palindrome_partitioning import * +from .pattern_match import * +from .permute_unique import * +from .permute import * +from .subsets_unique import * +from .subsets import * diff --git a/algorithms/backtrack/add_operators.py b/algorithms/backtrack/add_operators.py new file mode 100644 index 000000000..7a847396c --- /dev/null +++ b/algorithms/backtrack/add_operators.py @@ -0,0 +1,45 @@ +""" +Given a string that contains only digits 0-9 and a target value, +return all possibilities to add binary operators (not unary) +, -, or * +between the digits so they prevuate to the target value. + +Examples: +"123", 6 -> ["1+2+3", "1*2*3"] +"232", 8 -> ["2*3+2", "2+3*2"] +"105", 5 -> ["1*0+5","10-5"] +"00", 0 -> ["0+0", "0-0", "0*0"] +"3456237490", 9191 -> [] +""" + + +def add_operators(num, target): + """ + :type num: str + :type target: int + :rtype: List[str] + """ + + def dfs(res, path, num, target, pos, prev, multed): + if pos == len(num): + if target == prev: + res.append(path) + return + for i in range(pos, len(num)): + if i != pos and num[pos] == '0': # all digits have to be used + break + cur = int(num[pos:i+1]) + if pos == 0: + dfs(res, path + str(cur), num, target, i+1, cur, cur) + else: + dfs(res, path + "+" + str(cur), num, target, + i+1, prev + cur, cur) + dfs(res, path + "-" + str(cur), num, target, + i+1, prev - cur, -cur) + dfs(res, path + "*" + str(cur), num, target, + i+1, prev - multed + multed * cur, multed * cur) + + res = [] + if not num: + return res + dfs(res, "", num, target, 0, 0, 0) + return res diff --git a/algorithms/backtrack/anagram.py b/algorithms/backtrack/anagram.py new file mode 100644 index 000000000..7c807cdbd --- /dev/null +++ b/algorithms/backtrack/anagram.py @@ -0,0 +1,13 @@ +def anagram(s1, s2): + c1 = [0] * 26 + c2 = [0] * 26 + + for c in s1: + pos = ord(c)-ord('a') + c1[pos] = c1[pos] + 1 + + for c in s2: + pos = ord(c)-ord('a') + c2[pos] = c2[pos] + 1 + + return c1 == c2 diff --git a/algorithms/backtrack/array_sum_combinations.py b/algorithms/backtrack/array_sum_combinations.py new file mode 100644 index 000000000..b152e4d11 --- /dev/null +++ b/algorithms/backtrack/array_sum_combinations.py @@ -0,0 +1,84 @@ +""" +WAP to take one element from each of the array add it to the target sum. +Print all those three-element combinations. + +/* +A = [1, 2, 3, 3] +B = [2, 3, 3, 4] +C = [2, 3, 3, 4] +target = 7 +*/ + +Result: +[[1, 2, 4], [1, 3, 3], [1, 3, 3], [1, 3, 3], [1, 3, 3], [1, 4, 2], + [2, 2, 3], [2, 2, 3], [2, 3, 2], [2, 3, 2], [3, 2, 2], [3, 2, 2]] +""" +import itertools +from functools import partial + + +def array_sum_combinations(A, B, C, target): + + def over(constructed_sofar): + sum = 0 + to_stop, reached_target = False, False + for elem in constructed_sofar: + sum += elem + if sum >= target or len(constructed_sofar) >= 3: + to_stop = True + if sum == target and 3 == len(constructed_sofar): + reached_target = True + return to_stop, reached_target + + def construct_candidates(constructed_sofar): + array = A + if 1 == len(constructed_sofar): + array = B + elif 2 == len(constructed_sofar): + array = C + return array + + def backtrack(constructed_sofar=[], res=[]): + to_stop, reached_target = over(constructed_sofar) + if to_stop: + if reached_target: + res.append(constructed_sofar) + return + candidates = construct_candidates(constructed_sofar) + + for candidate in candidates: + constructed_sofar.append(candidate) + backtrack(constructed_sofar[:], res) + constructed_sofar.pop() + + res = [] + backtrack([], res) + return res + + +def unique_array_sum_combinations(A, B, C, target): + """ + 1. Sort all the arrays - a,b,c. - This improves average time complexity. + 2. If c[i] < Sum, then look for Sum - c[i] in array a and b. + When pair found, insert c[i], a[j] & b[k] into the result list. + This can be done in O(n). + 3. Keep on doing the above procedure while going through complete c array. + + Complexity: O(n(m+p)) + """ + def check_sum(n, *nums): + if sum(x for x in nums) == n: + return (True, nums) + else: + return (False, nums) + + pro = itertools.product(A, B, C) + func = partial(check_sum, target) + sums = list(itertools.starmap(func, pro)) + + res = set() + for s in sums: + if s[0] is True and s[1] not in res: + res.add(s[1]) + + return list(res) diff --git a/backtrack/combination_sum.py b/algorithms/backtrack/combination_sum.py similarity index 58% rename from backtrack/combination_sum.py rename to algorithms/backtrack/combination_sum.py index 3ac87b6e9..4d4dfb176 100644 --- a/backtrack/combination_sum.py +++ b/algorithms/backtrack/combination_sum.py @@ -15,17 +15,19 @@ ] """ -def combinationSum(self, candidates, target): + +def combination_sum(candidates, target): + + def dfs(nums, target, index, path, res): + if target < 0: + return # backtracking + if target == 0: + res.append(path) + return + for i in range(index, len(nums)): + dfs(nums, target-nums[i], i, path+[nums[i]], res) + res = [] candidates.sort() - self.dfs(candidates, target, 0, [], res) + dfs(candidates, target, 0, [], res) return res - -def dfs(self, nums, target, index, path, res): - if target < 0: - return # backtracking - if target == 0: - res.append(path) - return - for i in range(index, len(nums)): - self.dfs(nums, target-nums[i], i, path+[nums[i]], res) diff --git a/backtrack/factor_combinations.py b/algorithms/backtrack/factor_combinations.py similarity index 69% rename from backtrack/factor_combinations.py rename to algorithms/backtrack/factor_combinations.py index 787c1cf25..3240b05a8 100644 --- a/backtrack/factor_combinations.py +++ b/algorithms/backtrack/factor_combinations.py @@ -3,7 +3,8 @@ 8 = 2 x 2 x 2; = 2 x 4. -Write a function that takes an integer n and return all possible combinations of its factors. +Write a function that takes an integer n +and return all possible combinations of its factors. Note: You may assume that n is always positive. @@ -34,27 +35,29 @@ ] """ -# Iterative: -def getFactors(self, n): +# Iterative: +def get_factors(n): todo, combis = [(n, 2, [])], [] while todo: n, i, combi = todo.pop() while i * i <= n: if n % i == 0: - combis += combi + [i, n/i], - todo += (n/i, i, combi+[i]), + combis.append(combi + [i, n//i]) + todo.append((n//i, i, combi+[i])) i += 1 return combis + # Recursive: +def recursive_get_factors(n): -def getFactors(self, n): def factor(n, i, combi, combis): while i * i <= n: if n % i == 0: - combis += combi + [i, n/i], - factor(n/i, i, combi+[i], combis) + combis.append(combi + [i, n//i]), + factor(n//i, i, combi+[i], combis) i += 1 return combis + return factor(n, 2, [], []) diff --git a/algorithms/backtrack/find_words.py b/algorithms/backtrack/find_words.py new file mode 100644 index 000000000..da6b6347a --- /dev/null +++ b/algorithms/backtrack/find_words.py @@ -0,0 +1,73 @@ +''' +Given a matrix of words and a list of words to search, +return a list of words that exists in the board +This is Word Search II on LeetCode + +board = [ + ['o','a','a','n'], + ['e','t','a','e'], + ['i','h','k','r'], + ['i','f','l','v'] + ] + +words = ["oath","pea","eat","rain"] +''' + + +def find_words(board, words): + + def backtrack(board, i, j, trie, pre, used, result): + ''' + backtrack tries to build each words from + the board and return all words found + + @param: board, the passed in board of characters + @param: i, the row index + @param: j, the column index + @param: trie, a trie of the passed in words + @param: pre, a buffer of currently build string that differs + by recursion stack + @param: used, a replica of the board except in booleans + to state whether a character has been used + @param: result, the resulting set that contains all words found + + @return: list of words found + ''' + + if '#' in trie: + result.add(pre) + + if i < 0 or i >= len(board) or j < 0 or j >= len(board[0]): + return + + if not used[i][j] and board[i][j] in trie: + used[i][j] = True + backtrack(board, i+1, j, trie[board[i][j]], + pre+board[i][j], used, result) + backtrack(board, i, j+1, trie[board[i][j]], + pre+board[i][j], used, result) + backtrack(board, i-1, j, trie[board[i][j]], + pre+board[i][j], used, result) + backtrack(board, i, j-1, trie[board[i][j]], + pre+board[i][j], used, result) + used[i][j] = False + + # make a trie structure that is essentially dictionaries of dictionaries + # that map each character to a potential next character + trie = {} + for word in words: + curr_trie = trie + for char in word: + if char not in curr_trie: + curr_trie[char] = {} + curr_trie = curr_trie[char] + curr_trie['#'] = '#' + + # result is a set of found words since we do not want repeats + result = set() + used = [[False]*len(board[0]) for _ in range(len(board))] + + for i in range(len(board)): + for j in range(len(board[0])): + backtrack(board, i, j, trie, '', used, result) + return list(result) diff --git a/algorithms/backtrack/generate_abbreviations.py b/algorithms/backtrack/generate_abbreviations.py new file mode 100644 index 000000000..132965687 --- /dev/null +++ b/algorithms/backtrack/generate_abbreviations.py @@ -0,0 +1,26 @@ +""" +given input word, return the list of abbreviations. +ex) +word => [1ord, w1rd, wo1d, w2d, 3d, w3 ... etc] +""" + + +def generate_abbreviations(word): + + def backtrack(result, word, pos, count, cur): + if pos == len(word): + if count > 0: + cur += str(count) + result.append(cur) + return + + if count > 0: # add the current word + backtrack(result, word, pos+1, 0, cur+str(count)+word[pos]) + else: + backtrack(result, word, pos+1, 0, cur+word[pos]) + # skip the current word + backtrack(result, word, pos+1, count+1, cur) + + result = [] + backtrack(result, word, 0, 0, "") + return result diff --git a/algorithms/backtrack/generate_parenthesis.py b/algorithms/backtrack/generate_parenthesis.py new file mode 100644 index 000000000..073cec96a --- /dev/null +++ b/algorithms/backtrack/generate_parenthesis.py @@ -0,0 +1,43 @@ +""" +Given n pairs of parentheses, write a function to generate +all combinations of well-formed parentheses. + +For example, given n = 3, a solution set is: + +[ + "((()))", + "(()())", + "(())()", + "()(())", + "()()()" +] +""" + + +def generate_parenthesis_v1(n): + def add_pair(res, s, left, right): + if left == 0 and right == 0: + res.append(s) + return + if right > 0: + add_pair(res, s + ")", left, right - 1) + if left > 0: + add_pair(res, s + "(", left - 1, right + 1) + + res = [] + add_pair(res, "", n, 0) + return res + + +def generate_parenthesis_v2(n): + def add_pair(res, s, left, right): + if left == 0 and right == 0: + res.append(s) + if left > 0: + add_pair(res, s + "(", left - 1, right) + if right > 0 and left < right: + add_pair(res, s + ")", left, right - 1) + + res = [] + add_pair(res, "", n, n) + return res diff --git a/backtrack/letter_combination.py b/algorithms/backtrack/letter_combination.py similarity index 79% rename from backtrack/letter_combination.py rename to algorithms/backtrack/letter_combination.py index f4a4c4e0b..8a2f8d151 100644 --- a/backtrack/letter_combination.py +++ b/algorithms/backtrack/letter_combination.py @@ -7,7 +7,7 @@ """ -def letter_combinations(digits:"str")->"List[str]": +def letter_combinations(digits): if digits == "": return [] kmaps = { @@ -28,8 +28,3 @@ def letter_combinations(digits:"str")->"List[str]": tmp.append(an + char) ans = tmp return ans - - -if __name__ == "__main__": - digit_string = "23" - print(letter_combinations(digit_string)) diff --git a/algorithms/backtrack/palindrome_partitioning.py b/algorithms/backtrack/palindrome_partitioning.py new file mode 100644 index 000000000..052339dcc --- /dev/null +++ b/algorithms/backtrack/palindrome_partitioning.py @@ -0,0 +1,42 @@ +""" It looks like you need to be looking not for all palindromic substrings, +but rather for all the ways you can divide the input string +up into palindromic substrings. +(There's always at least one way, +since one-character substrings are always palindromes.) +""" + + +def palindromic_substrings(s): + if not s: + return [[]] + results = [] + for i in range(len(s), 0, -1): + sub = s[:i] + if sub == sub[::-1]: + for rest in palindromic_substrings(s[i:]): + results.append([sub] + rest) + return results + + +""" +There's two loops. +The outer loop checks each length of initial substring +(in descending length order) to see if it is a palindrome. +If so, it recurses on the rest of the string and loops over the returned +values, adding the initial substring to +each item before adding it to the results. +""" + + +def palindromic_substrings_iter(s): + """ + A slightly more Pythonic approach with a recursive generator + """ + if not s: + yield [] + return + for i in range(len(s), 0, -1): + sub = s[:i] + if sub == sub[::-1]: + for rest in palindromic_substrings_iter(s[i:]): + yield [sub] + rest diff --git a/algorithms/backtrack/pattern_match.py b/algorithms/backtrack/pattern_match.py new file mode 100644 index 000000000..03a23a4e9 --- /dev/null +++ b/algorithms/backtrack/pattern_match.py @@ -0,0 +1,42 @@ +""" +Given a pattern and a string str, +find if str follows the same pattern. + +Here follow means a full match, such that there is a bijection between +a letter in pattern and a non-empty substring in str. + +Examples: +pattern = "abab", str = "redblueredblue" should return true. +pattern = "aaaa", str = "asdasdasdasd" should return true. +pattern = "aabb", str = "xyzabcxzyabc" should return false. +Notes: +You may assume both pattern and str contains only lowercase letters. +""" + + +def pattern_match(pattern, string): + """ + :type pattern: str + :type string: str + :rtype: bool + """ + def backtrack(pattern, string, dic): + + if len(pattern) == 0 and len(string) > 0: + return False + + if len(pattern) == len(string) == 0: + return True + + for end in range(1, len(string)-len(pattern)+2): + if pattern[0] not in dic and string[:end] not in dic.values(): + dic[pattern[0]] = string[:end] + if backtrack(pattern[1:], string[end:], dic): + return True + del dic[pattern[0]] + elif pattern[0] in dic and dic[pattern[0]] == string[:end]: + if backtrack(pattern[1:], string[end:], dic): + return True + return False + + return backtrack(pattern, string, {}) diff --git a/algorithms/backtrack/permute.py b/algorithms/backtrack/permute.py new file mode 100644 index 000000000..4ce484369 --- /dev/null +++ b/algorithms/backtrack/permute.py @@ -0,0 +1,54 @@ +""" +Given a collection of distinct numbers, return all possible permutations. + +For example, +[1,2,3] have the following permutations: +[ + [1,2,3], + [1,3,2], + [2,1,3], + [2,3,1], + [3,1,2], + [3,2,1] +] +""" + + +def permute(elements): + """ + returns a list with the permuations. + """ + if len(elements) <= 1: + return elements + else: + tmp = [] + for perm in permute(elements[1:]): + for i in range(len(elements)): + tmp.append(perm[:i] + elements[0:1] + perm[i:]) + return tmp + + +def permute_iter(elements): + """ + iterator: returns a perumation by each call. + """ + if len(elements) <= 1: + yield elements + else: + for perm in permute_iter(elements[1:]): + for i in range(len(elements)): + yield perm[:i] + elements[0:1] + perm[i:] + + +# DFS Version +def permute_recursive(nums): + def dfs(res, nums, path): + if not nums: + res.append(path) + for i in range(len(nums)): + print(nums[:i]+nums[i+1:]) + dfs(res, nums[:i]+nums[i+1:], path+[nums[i]]) + + res = [] + dfs(res, nums, []) + return res diff --git a/algorithms/backtrack/permute_unique.py b/algorithms/backtrack/permute_unique.py new file mode 100644 index 000000000..3b82e2c46 --- /dev/null +++ b/algorithms/backtrack/permute_unique.py @@ -0,0 +1,25 @@ +""" +Given a collection of numbers that might contain duplicates, +return all possible unique permutations. + +For example, +[1,1,2] have the following unique permutations: +[ + [1,1,2], + [1,2,1], + [2,1,1] +] +""" + + +def permute_unique(nums): + perms = [[]] + for n in nums: + new_perms = [] + for l in perms: + for i in range(len(l)+1): + new_perms.append(l[:i]+[n]+l[i:]) + if i < len(l) and l[i] == n: + break # handles duplication + perms = new_perms + return perms diff --git a/algorithms/backtrack/subsets.py b/algorithms/backtrack/subsets.py new file mode 100644 index 000000000..1c443024f --- /dev/null +++ b/algorithms/backtrack/subsets.py @@ -0,0 +1,59 @@ +""" +Given a set of distinct integers, nums, return all possible subsets. + +Note: The solution set must not contain duplicate subsets. + +For example, +If nums = [1,2,3], a solution is: + +[ + [3], + [1], + [2], + [1,2,3], + [1,3], + [2,3], + [1,2], + [] +] +""" + + +def subsets(nums): + """ + O(2**n) + """ + def backtrack(res, nums, stack, pos): + if pos == len(nums): + res.append(list(stack)) + else: + # take nums[pos] + stack.append(nums[pos]) + backtrack(res, nums, stack, pos+1) + stack.pop() + # dont take nums[pos] + backtrack(res, nums, stack, pos+1) + + res = [] + backtrack(res, nums, [], 0) + return res + + +""" +simplified backtrack + +def backtrack(res, nums, cur, pos): + if pos >= len(nums): + res.append(cur) + else: + backtrack(res, nums, cur+[nums[pos]], pos+1) + backtrack(res, nums, cur, pos+1) +""" + + +# Iteratively +def subsets_v2(self, nums): + res = [[]] + for num in sorted(nums): + res += [item+[num] for item in res] + return res diff --git a/algorithms/backtrack/subsets_unique.py b/algorithms/backtrack/subsets_unique.py new file mode 100644 index 000000000..da73a25ca --- /dev/null +++ b/algorithms/backtrack/subsets_unique.py @@ -0,0 +1,37 @@ +""" +Given a collection of integers that might contain duplicates, nums, +return all possible subsets. + +Note: The solution set must not contain duplicate subsets. + +For example, +If nums = [1,2,2], a solution is: + +[ + [2], + [1], + [1,2,2], + [2,2], + [1,2], + [] +] +""" + + +def subsets_unique(nums): + + def backtrack(res, nums, stack, pos): + if pos == len(nums): + res.add(tuple(stack)) + else: + # take + stack.append(nums[pos]) + backtrack(res, nums, stack, pos+1) + stack.pop() + + # don't take + backtrack(res, nums, stack, pos+1) + + res = set() + backtrack(res, nums, [], 0) + return list(res) diff --git a/algorithms/bfs/__init__.py b/algorithms/bfs/__init__.py new file mode 100644 index 000000000..b3d3b0c6a --- /dev/null +++ b/algorithms/bfs/__init__.py @@ -0,0 +1,3 @@ +from .maze_search import * +from .shortest_distance_from_all_buildings import * +from .word_ladder import * diff --git a/algorithms/bfs/maze_search.py b/algorithms/bfs/maze_search.py new file mode 100644 index 000000000..efa65c357 --- /dev/null +++ b/algorithms/bfs/maze_search.py @@ -0,0 +1,33 @@ +''' +BFS time complexity : O(|E|) +BFS space complexity : O(|V|) + +do BFS from (0,0) of the grid and get the minimum number of steps needed to get to the lower right column + +only step on the columns whose value is 1 + +if there is no path, it returns -1 +''' + +def maze_search(grid): + dx = [0,0,-1,1] + dy = [-1,1,0,0] + n = len(grid) + m = len(grid[0]) + q = [(0,0,0)] + visit = [[0]*m for _ in range(n)] + if grid[0][0] == 0: + return -1 + visit[0][0] = 1 + while q: + i, j, step = q.pop(0) + if i == n-1 and j == m-1: + return step + for k in range(4): + x = i + dx[k] + y = j + dy[k] + if x>=0 and x=0 and y "hot" -> "dot" -> "dog" -> "cog", +return its length 5. +. +Note: +Return -1 if there is no such transformation sequence. +All words have the same length. +All words contain only lowercase alphabetic characters. +""" +import unittest + + +def ladder_length(begin_word, end_word, word_list): + """ + Bidirectional BFS!!! + :type begin_word: str + :type end_word: str + :type word_list: Set[str] + :rtype: int + """ + if len(begin_word) != len(end_word): + return -1 # not possible + + if begin_word == end_word: + return 0 + + # when only differ by 1 character + if sum(c1 != c2 for c1, c2 in zip(begin_word, end_word)) == 1: + return 1 + + begin_set = set() + end_set = set() + begin_set.add(begin_word) + end_set.add(end_word) + result = 2 + while begin_set and end_set: + + if len(begin_set) > len(end_set): + begin_set, end_set = end_set, begin_set + + next_begin_set = set() + for word in begin_set: + for ladder_word in word_range(word): + if ladder_word in end_set: + return result + if ladder_word in word_list: + next_begin_set.add(ladder_word) + word_list.remove(ladder_word) + begin_set = next_begin_set + result += 1 + # print(begin_set) + # print(result) + return -1 + + +def word_range(word): + for ind in range(len(word)): + temp = word[ind] + for c in [chr(x) for x in range(ord('a'), ord('z') + 1)]: + if c != temp: + yield word[:ind] + c + word[ind + 1:] + + +class TestSuite(unittest.TestCase): + + def test_ladder_length(self): + + # hit -> hot -> dot -> dog -> cog + self.assertEqual(5, ladder_length('hit', 'cog', ["hot", "dot", "dog", "lot", "log"])) + + # pick -> sick -> sink -> sank -> tank == 5 + self.assertEqual(5, ladder_length('pick', 'tank', + ['tock', 'tick', 'sank', 'sink', 'sick'])) + + # live -> life == 1, no matter what is the word_list. + self.assertEqual(1, ladder_length('live', 'life', ['hoho', 'luck'])) + + # 0 length from ate -> ate + self.assertEqual(0, ladder_length('ate', 'ate', [])) + + # not possible to reach ! + self.assertEqual(-1, ladder_length('rahul', 'coder', ['blahh', 'blhah'])) + + +if __name__ == '__main__': + + unittest.main() diff --git a/algorithms/bit/__init__.py b/algorithms/bit/__init__.py new file mode 100644 index 000000000..617f5006f --- /dev/null +++ b/algorithms/bit/__init__.py @@ -0,0 +1,18 @@ +from .add_bitwise_operator import * +from .count_ones import * +from .find_missing_number import * +from .power_of_two import * +from .reverse_bits import * +from .single_number import * +from .single_number2 import * +from .single_number3 import * +from .subsets import * +from .bit_operation import * +from .swap_pair import * +from .find_difference import * +from .has_alternative_bit import * +from .insert_bit import * +from .remove_bit import * +from .count_flips_to_convert import * +from .flip_bit_longest_sequence import * +from .binary_gap import * diff --git a/bit/add_without_operator.py b/algorithms/bit/add_bitwise_operator.py similarity index 55% rename from bit/add_without_operator.py rename to algorithms/bit/add_bitwise_operator.py index a87b0f0d8..d9704decb 100644 --- a/bit/add_without_operator.py +++ b/algorithms/bit/add_bitwise_operator.py @@ -5,19 +5,10 @@ Input: 2 3 Output: 5 """ +def add_bitwise_operator(x, y): - -def addWithoutOperator(x, y): - while y != 0: + while y: carry = x & y x = x ^ y y = carry << 1 - print x - - -def main(): - x, y = map(int, raw_input().split()) - addWithoutOperator(x, y) - -if __name__ == '__main__': - main() + return x diff --git a/algorithms/bit/binary_gap.py b/algorithms/bit/binary_gap.py new file mode 100644 index 000000000..75a1233b5 --- /dev/null +++ b/algorithms/bit/binary_gap.py @@ -0,0 +1,27 @@ +""" +Given a positive integer N, find and return the longest distance between two +consecutive 1' in the binary representation of N. +If there are not two consecutive 1's, return 0 + +For example: +Input: 22 +Output: 2 +Explanation: +22 in binary is 10110 +In the binary representation of 22, there are three ones, and two consecutive pairs of 1's. +The first consecutive pair of 1's have distance 2. +The second consecutive pair of 1's have distance 1. +The answer is the largest of these two distances, which is 2 +""" +def binary_gap(N): + last = None + ans = 0 + index = 0 + while N != 0: + if N & 1: + if last is not None: + ans = max(ans, index - last) + last = index + index = index + 1 + N = N >> 1 + return ans diff --git a/algorithms/bit/bit_operation.py b/algorithms/bit/bit_operation.py new file mode 100644 index 000000000..58f7d7559 --- /dev/null +++ b/algorithms/bit/bit_operation.py @@ -0,0 +1,37 @@ +""" +Fundamental bit operation: + get_bit(num, i): get an exact bit at specific index + set_bit(num, i): set a bit at specific index + clear_bit(num, i): clear a bit at specific index + update_bit(num, i, bit): update a bit at specific index +""" + +""" +This function shifts 1 over by i bits, creating a value being like 0001000. By +performing an AND with num, we clear all bits other than the bit at bit i. +Finally we compare that to 0 +""" +def get_bit(num, i): + return (num & (1 << i)) != 0 + +""" +This function shifts 1 over by i bits, creating a value being like 0001000. By +performing an OR with num, only value at bit i will change. +""" +def set_bit(num, i): + return num | (1 << i) + +""" +This method operates in almost the reverse of set_bit +""" +def clear_bit(num, i): + mask = ~(1 << i) + return num & mask + +""" +To set the ith bit to value, we first clear the bit at position i by using a +mask. Then, we shift the intended value. Finally we OR these two numbers +""" +def update_bit(num, i, bit): + mask = ~(1 << i) + return (num & mask) | (bit << i) diff --git a/bit/bytes_int_conversion.py b/algorithms/bit/bytes_int_conversion.py similarity index 100% rename from bit/bytes_int_conversion.py rename to algorithms/bit/bytes_int_conversion.py diff --git a/algorithms/bit/count_flips_to_convert.py b/algorithms/bit/count_flips_to_convert.py new file mode 100644 index 000000000..6ba1d116d --- /dev/null +++ b/algorithms/bit/count_flips_to_convert.py @@ -0,0 +1,19 @@ +""" +Write a function to determine the number of bits you would need to +flip to convert integer A to integer B. +For example: +Input: 29 (or: 11101), 15 (or: 01111) +Output: 2 +""" + + +def count_flips_to_convert(a, b): + + diff = a ^ b + + # count number of ones in diff + count = 0 + while diff: + diff &= (diff - 1) + count += 1 + return count diff --git a/algorithms/bit/count_ones.py b/algorithms/bit/count_ones.py new file mode 100644 index 000000000..700efe9e2 --- /dev/null +++ b/algorithms/bit/count_ones.py @@ -0,0 +1,32 @@ +""" +Write a function that takes an unsigned integer and +returns the number of ’1' bits it has +(also known as the Hamming weight). + +For example, the 32-bit integer ’11' has binary +representation 00000000000000000000000000001011, +so the function should return 3. + +T(n)- O(k) : k is the number of 1s present in binary representation. +NOTE: this complexity is better than O(log n). +e.g. for n = 00010100000000000000000000000000 +only 2 iterations are required. + +Number of loops is +equal to the number of 1s in the binary representation.""" +def count_ones_recur(n): + """Using Brian Kernighan’s Algorithm. (Recursive Approach)""" + + if not n: + return 0 + return 1 + count_ones_recur(n & (n-1)) + + +def count_ones_iter(n): + """Using Brian Kernighan’s Algorithm. (Iterative Approach)""" + + count = 0 + while n: + n &= (n-1) + count += 1 + return count diff --git a/algorithms/bit/find_difference.py b/algorithms/bit/find_difference.py new file mode 100644 index 000000000..67f2f0608 --- /dev/null +++ b/algorithms/bit/find_difference.py @@ -0,0 +1,28 @@ +""" +Given two strings s and t which consist of only lowercase letters. +String t is generated by random shuffling string s and then add one more letter +at a random position. Find the letter that was added in t. + +For example: +Input: +s = "abcd" +t = "abecd" +Output: 'e' + +Explanation: +'e' is the letter that was added. +""" + +""" +We use the characteristic equation of XOR. +A xor B xor C = A xor C xor B +If A == C, then A xor C = 0 +and then, B xor 0 = B +""" +def find_difference(s, t): + ret = 0 + for ch in s + t: + # ord(ch) return an integer representing the Unicode code point of that character + ret = ret ^ ord(ch) + # chr(i) Return the string representing a character whose Unicode code point is the integer i + return chr(ret) diff --git a/bit/find_missing_number.py b/algorithms/bit/find_missing_number.py similarity index 60% rename from bit/find_missing_number.py rename to algorithms/bit/find_missing_number.py index 115ce4855..9aedc508b 100644 --- a/bit/find_missing_number.py +++ b/algorithms/bit/find_missing_number.py @@ -1,14 +1,10 @@ -def find_missing_number(nums): - """Returns the missing number from a sequence of unique integers +""" + Returns the missing number from a sequence of unique integers in range [0..n] in O(n) time and space. The difference between consecutive integers cannot be more than 1. If the sequence is already complete, the next integer in the sequence will be returned. - - >>> find_missing_number(i for i in range(0, 10000) if i != 1234) - 1234 - >>> find_missing_number([4, 1, 3, 0, 6, 5, 2]) - 7 - """ +""" +def find_missing_number(nums): missing = 0 for i, num in enumerate(nums): @@ -16,3 +12,12 @@ def find_missing_number(nums): missing ^= i + 1 return missing + + +def find_missing_number2(nums): + + num_sum = sum(nums) + n = len(nums) + total_sum = n*(n+1) // 2 + missing = total_sum - num_sum + return missing diff --git a/algorithms/bit/flip_bit_longest_sequence.py b/algorithms/bit/flip_bit_longest_sequence.py new file mode 100644 index 000000000..a952edb3e --- /dev/null +++ b/algorithms/bit/flip_bit_longest_sequence.py @@ -0,0 +1,30 @@ +""" +You have an integer and you can flip exactly one bit from a 0 to 1. +Write code to find the length of the longest sequence of 1s you could create. +For example: +Input: 1775 ( or: 11011101111) +Output: 8 +""" + + +def flip_bit_longest_seq(num): + + curr_len = 0 + prev_len = 0 + max_len = 0 + + while num: + if num & 1 == 1: # last digit is 1 + curr_len += 1 + + elif num & 1 == 0: # last digit is 0 + if num & 2 == 0: # second last digit is 0 + prev_len = 0 + else: + prev_len = curr_len + curr_len = 0 + + max_len = max(max_len, prev_len + curr_len) + num = num >> 1 # right shift num + + return max_len + 1 diff --git a/algorithms/bit/has_alternative_bit.py b/algorithms/bit/has_alternative_bit.py new file mode 100644 index 000000000..ade2f56a6 --- /dev/null +++ b/algorithms/bit/has_alternative_bit.py @@ -0,0 +1,38 @@ +""" +Given a positive integer, check whether it has alternating bits: namely, +if two adjacent bits will always have different values. + +For example: +Input: 5 +Output: True because the binary representation of 5 is: 101. + +Input: 7 +Output: False because the binary representation of 7 is: 111. + +Input: 11 +Output: False because the binary representation of 11 is: 1011. + +Input: 10 +Output: True because The binary representation of 10 is: 1010. +""" + +# Time Complexity - O(number of bits in n) +def has_alternative_bit(n): + first_bit = 0 + second_bit = 0 + while n: + first_bit = n & 1 + if n >> 1: + second_bit = (n >> 1) & 1 + if not first_bit ^ second_bit: + return False + else: + return True + n = n >> 1 + return True + +# Time Complexity - O(1) +def has_alternative_bit_fast(n): + mask1 = int('aaaaaaaa', 16) # for bits ending with zero (...1010) + mask2 = int('55555555', 16) # for bits ending with one (...0101) + return mask1 == (n + (n ^ mask1)) or mask2 == (n + (n ^ mask2)) diff --git a/algorithms/bit/insert_bit.py b/algorithms/bit/insert_bit.py new file mode 100644 index 000000000..f799489c8 --- /dev/null +++ b/algorithms/bit/insert_bit.py @@ -0,0 +1,44 @@ +""" +Insertion: + +insert_one_bit(num, bit, i): insert exact one bit at specific position +For example: + +Input: num = 10101 (21) +insert_one_bit(num, 1, 2): 101101 (45) +insert_one_bit(num, 0 ,2): 101001 (41) +insert_one_bit(num, 1, 5): 110101 (53) +insert_one_bit(num, 1, 0): 101011 (43) + +insert_mult_bits(num, bits, len, i): insert multiple bits with len at specific position +For example: + +Input: num = 101 (5) +insert_mult_bits(num, 7, 3, 1): 101111 (47) +insert_mult_bits(num, 7, 3, 0): 101111 (47) +insert_mult_bits(num, 7, 3, 3): 111101 (61) +""" + +""" +Insert exact one bit at specific position + +Algorithm: +1. Create a mask having bit from i to the most significant bit, and append the new bit at 0 position +2. Keep the bit from 0 position to i position ( like 000...001111) +3) Merge mask and num +""" +def insert_one_bit(num, bit, i): + # Create mask + mask = num >> i + mask = (mask << 1) | bit + mask = mask << i + # Keep the bit from 0 position to i position + right = ((1 << i) - 1) & num + return right | mask + +def insert_mult_bits(num, bits, len, i): + mask = num >> i + mask = (mask << len) | bits + mask = mask << i + right = ((1 << i) - 1) & num + return right | mask diff --git a/bit/power_of_two.py b/algorithms/bit/power_of_two.py similarity index 98% rename from bit/power_of_two.py rename to algorithms/bit/power_of_two.py index bdb629471..01587788a 100644 --- a/bit/power_of_two.py +++ b/algorithms/bit/power_of_two.py @@ -1,8 +1,6 @@ """ given an integer, write a function to determine if it is a power of two """ - - def is_power_of_two(n): """ :type n: int diff --git a/algorithms/bit/remove_bit.py b/algorithms/bit/remove_bit.py new file mode 100644 index 000000000..b302cf986 --- /dev/null +++ b/algorithms/bit/remove_bit.py @@ -0,0 +1,15 @@ +""" +Remove_bit(num, i): remove a bit at specific position. +For example: + +Input: num = 10101 (21) +remove_bit(num, 2): output = 1001 (9) +remove_bit(num, 4): output = 101 (5) +remove_bit(num, 0): output = 1010 (10) +""" + +def remove_bit(num, i): + mask = num >> (i + 1) + mask = mask << i + right = ((1 << i) - 1) & num + return mask | right diff --git a/bit/reverse_bits.py b/algorithms/bit/reverse_bits.py similarity index 99% rename from bit/reverse_bits.py rename to algorithms/bit/reverse_bits.py index 06beebf39..410c63d57 100644 --- a/bit/reverse_bits.py +++ b/algorithms/bit/reverse_bits.py @@ -6,8 +6,6 @@ return 964176192 (represented in binary as 00111001011110000010100101000000). """ - - def reverse_bits(n): m = 0 i = 0 diff --git a/bit/single_number.py b/algorithms/bit/single_number.py similarity index 61% rename from bit/single_number.py rename to algorithms/bit/single_number.py index 756c3d79e..6e0b6f705 100644 --- a/bit/single_number.py +++ b/algorithms/bit/single_number.py @@ -2,14 +2,18 @@ Given an array of integers, every element appears twice except for one. Find that single one. +NOTE: This also works for finding a number occurring odd + number of times, where all the other numbers appear + even number of times. + Note: Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory? """ - - def single_number(nums): """ + Returns single number, if found. + Else if all numbers appear twice, returns 0. :type nums: List[int] :rtype: int """ diff --git a/bit/single_number2.py b/algorithms/bit/single_number2.py similarity index 70% rename from bit/single_number2.py rename to algorithms/bit/single_number2.py index 8646b2149..2f0e36c59 100644 --- a/bit/single_number2.py +++ b/algorithms/bit/single_number2.py @@ -6,10 +6,9 @@ Note: Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory? -""" -""" +Solution: 32 bits for each integer. Consider 1 bit in it, the sum of each integer's corresponding bit (except for the single number) @@ -19,24 +18,6 @@ In this way, you get the 32 bits of the single number. """ - -def single_number(nums): - """ - :type nums: List[int] - :rtype: int - """ - res = 0 - for i in range(0, 32): - count = 0 - for num in nums: - if ((num >> i) & 1): - count += 1 - res |= ((count % 3) << i) - if res >= 2**31: - res -= 2**32 - return res - - # Another awesome answer def single_number2(nums): ones, twos = 0, 0 diff --git a/algorithms/bit/single_number3.py b/algorithms/bit/single_number3.py new file mode 100644 index 000000000..5182bd271 --- /dev/null +++ b/algorithms/bit/single_number3.py @@ -0,0 +1,49 @@ +""" +Given an array of numbers nums, +in which exactly two elements appear only once +and all the other elements appear exactly twice. +Find the two elements that appear only once. +Limitation: Time Complexity: O(N) and Space Complexity O(1) + +For example: + +Given nums = [1, 2, 1, 3, 2, 5], return [3, 5]. + +Note: +The order of the result is not important. +So in the above example, [5, 3] is also correct. + + +Solution: +1. Use XOR to cancel out the pairs and isolate A^B +2. It is guaranteed that at least 1 bit exists in A^B since + A and B are different numbers. ex) 010 ^ 111 = 101 +3. Single out one bit R (right most bit in this solution) to use it as a pivot +4. Divide all numbers into two groups. + One group with a bit in the position R + One group without a bit in the position R +5. Use the same strategy we used in step 1 to isolate A and B from each group. +""" + + +def single_number3(nums): + """ + :type nums: List[int] + :rtype: List[int] + """ + # isolate a^b from pairs using XOR + ab = 0 + for n in nums: + ab ^= n + + # isolate right most bit from a^b + right_most = ab & (-ab) + + # isolate a and b from a^b + a, b = 0, 0 + for n in nums: + if n & right_most: + a ^= n + else: + b ^= n + return [a, b] diff --git a/bit/subsets.py b/algorithms/bit/subsets.py similarity index 76% rename from bit/subsets.py rename to algorithms/bit/subsets.py index 8eafd79f4..6d5a0a284 100644 --- a/bit/subsets.py +++ b/algorithms/bit/subsets.py @@ -7,45 +7,31 @@ For example, If nums = [1,2,3], a solution is: -[ - [3], - [1], - [2], - [1,2,3], - [1,3], - [2,3], - [1,2], - [] -] +{ + (1, 2), + (1, 3), + (1,), + (2,), + (3,), + (1, 2, 3), + (), + (2, 3) +} """ - - -def subnets(nums): - nums.sort() - total = 2 ** len(nums) # or 1 << len(nums) - res = [] * total +def subsets(nums): + """ + :param nums: List[int] + :return: Set[tuple] + """ + n = len(nums) + total = 1 << n + res = set() for i in range(total): - res.append([]) - - for i in range(len(nums)): - for j in range(total): - if ((j >> i) & 1) > 0: # i & 1 << j - res[j].append(nums[i]) - return res - + subset = tuple(num for j, num in enumerate(nums) if i & 1 << j) + res.add(subset) -def subsets2(self, nums): - res = [] - nums.sort() - for i in range(1 << len(nums)): - tmp = [] - for j in range(len(nums)): - if i & 1 << j: # if i >> j & 1: - tmp.append(nums[j]) - res.append(tmp) return res - """ this explanation is from leet_nik @ leetcode This is an amazing solution. Learnt a lot. diff --git a/algorithms/bit/swap_pair.py b/algorithms/bit/swap_pair.py new file mode 100644 index 000000000..c1bf75811 --- /dev/null +++ b/algorithms/bit/swap_pair.py @@ -0,0 +1,21 @@ +""" +Swap_pair: A function swap odd and even bits in an integer with as few instructions +as possible (Ex bit and bit 1 are swapped, bit 2 and bit 3 are swapped) + +For example: +22: 010110 --> 41: 101001 +10: 1010 --> 5 : 0101 +""" + +""" +We can approach this as operating on the odds bit first, and then the even bits. +We can mask all odd bits with 10101010 in binary ('AA') then shift them right by 1 +Similarly, we mask all even bit with 01010101 in binary ('55') then shift them left +by 1. Finally, we merge these two values by OR operation. +""" +def swap_pair(num): + # odd bit arithmetic right shift 1 bit + odd = (num & int('AAAAAAAA', 16)) >> 1 + # even bit left shift 1 bit + even = (num & int('55555555', 16)) << 1 + return odd | even diff --git a/calculator/math_parser.py b/algorithms/calculator/math_parser.py similarity index 76% rename from calculator/math_parser.py rename to algorithms/calculator/math_parser.py index 5c6dedd08..a1d0e0ba5 100644 --- a/calculator/math_parser.py +++ b/algorithms/calculator/math_parser.py @@ -14,6 +14,8 @@ | Parsed expression: ['2452', '*', '(', '3', '*', '6', '+', '1', ')', '*', '6', '/', '235'] | Evaluation result: 1189.4808510638297 ------------------------------------------------------------------------------------------------- + +Now added '^' operator for exponents. (by @goswami-rahul) """ from collections import deque @@ -21,13 +23,14 @@ numeric_value = re.compile('\d+(\.\d+)?') -__operators__ = "+-/*" +__operators__ = "+-/*^" __parenthesis__ = "()" __priority__ = { '+': 0, '-': 0, '*': 1, '/': 1, + '^': 2 } def is_operator(token): @@ -59,6 +62,7 @@ def calc(n2, n1, operator): elif operator == '+': return n1 + n2 elif operator == '*': return n1 * n2 elif operator == '/': return n1 / n2 + elif operator == '^': return n1 ** n2 return 0 def apply_operation(op_stack, out_stack): @@ -85,8 +89,11 @@ def parse(expression): if len(current) > 0: result.append(current) current = "" - if i != ' ': + if i in __operators__ or i in __parenthesis__: result.append(i) + else: + raise Exception("invalid syntax " + i) + if len(current) > 0: result.append(current) return result @@ -100,7 +107,8 @@ def evaluate(expression): """ op_stack = deque() # operator stack out_stack = deque() # output stack (values) - for token in parse(expression): + tokens = parse(expression) # calls the function only once! + for token in tokens: if numeric_value.match(token): out_stack.append(float(token)) elif token == '(': @@ -117,4 +125,23 @@ def evaluate(expression): while len(op_stack) > 0: apply_operation(op_stack, out_stack) - return out_stack[-1] \ No newline at end of file + return out_stack[-1] + + +def main(): + """ + simple user-interface + """ + print("\t\tCalculator\n\n") + user_input = input("expression or exit: ") + while user_input != "exit": + try: + print("The result is {0}".format(evaluate(user_input))) + except Exception: + print("invalid syntax!") + user_input = input("expression or exit: ") + print("program end") + + +if __name__ == "__main__": + main() diff --git a/algorithms/dfs/all_factors.py b/algorithms/dfs/all_factors.py new file mode 100644 index 000000000..6c692f30b --- /dev/null +++ b/algorithms/dfs/all_factors.py @@ -0,0 +1,127 @@ +""" +Numbers can be regarded as product of its factors. For example, +8 = 2 x 2 x 2; + = 2 x 4. + + +Write a function that takes an integer n and return all possible combinations +of its factors.Numbers can be regarded as product of its factors. For example, +8 = 2 x 2 x 2; + = 2 x 4. + +Examples: +input: 1 +output: +[] + + +input: 37 +output: +[] + +input: 32 +output: +[ + [2, 16], + [2, 2, 8], + [2, 2, 2, 4], + [2, 2, 2, 2, 2], +""" +import unittest + +def get_factors(n): + """[summary] + + Arguments: + n {[int]} -- [to analysed number] + + Returns: + [list of lists] -- [all factors of the number n] + """ + + def factor(n, i, combi, res): + """[summary] + helper function + + Arguments: + n {[int]} -- [number] + i {[int]} -- [to tested divisor] + combi {[list]} -- [catch divisors] + res {[list]} -- [all factors of the number n] + + Returns: + [list] -- [res] + """ + + while i * i <= n: + if n % i == 0: + res += combi + [i, int(n/i)], + factor(n/i, i, combi+[i], res) + i += 1 + return res + return factor(n, 2, [], []) + + +def get_factors_iterative1(n): + """[summary] + Computes all factors of n. + Translated the function get_factors(...) in + a call-stack modell. + + Arguments: + n {[int]} -- [to analysed number] + + Returns: + [list of lists] -- [all factors] + """ + + todo, res = [(n, 2, [])], [] + while todo: + n, i, combi = todo.pop() + while i * i <= n: + if n % i == 0: + res += combi + [i, n//i], + todo.append((n//i, i, combi+[i])), + i += 1 + return res + + +def get_factors_iterative2(n): + """[summary] + analog as above + + Arguments: + n {[int]} -- [description] + + Returns: + [list of lists] -- [all factors of n] + """ + + ans, stack, x = [], [], 2 + while True: + if x > n // x: + if not stack: + return ans + ans.append(stack + [n]) + x = stack.pop() + n *= x + x += 1 + elif n % x == 0: + stack.append(x) + n //= x + else: + x += 1 + +class TestAllFactors(unittest.TestCase): + def test_get_factors(self): + self.assertEqual([[2, 16], [2, 2, 8], [2, 2, 2, 4], [2, 2, 2, 2, 2], [2, 4, 4], [4, 8]], + get_factors(32)) + def test_get_factors_iterative1(self): + self.assertEqual([[2, 16], [4, 8], [2, 2, 8], [2, 4, 4], [2, 2, 2, 4], [2, 2, 2, 2, 2]], + get_factors_iterative1(32)) + def test_get_factors_iterative2(self): + self.assertEqual([[2, 2, 2, 2, 2], [2, 2, 2, 4], [2, 2, 8], [2, 4, 4], [2, 16], [4, 8]], + get_factors_iterative2(32)) + +if __name__ == "__main__": + unittest.main() diff --git a/dfs/count_islands.py b/algorithms/dfs/count_islands.py similarity index 82% rename from dfs/count_islands.py rename to algorithms/dfs/count_islands.py index 5c4711526..60f6802d2 100644 --- a/dfs/count_islands.py +++ b/algorithms/dfs/count_islands.py @@ -27,18 +27,18 @@ def num_islands(grid): for i, row in enumerate(grid): for j, col in enumerate(grid[i]): if col == '1': - DFS(grid, i, j) + dfs(grid, i, j) count += 1 return count -def DFS(grid, i, j): +def dfs(grid, i, j): if (i < 0 or i >= len(grid)) or (j < 0 or len(grid[0])): return if grid[i][j] != '1': return grid[i][j] = '0' - DFS(grid, i+1, j) - DFS(grid, i-1, j) - DFS(grid, i, j+1) - DFS(grid, i, j-1) + dfs(grid, i+1, j) + dfs(grid, i-1, j) + dfs(grid, i, j+1) + dfs(grid, i, j-1) diff --git a/dfs/pacific_atlantic.py b/algorithms/dfs/pacific_atlantic.py similarity index 79% rename from dfs/pacific_atlantic.py rename to algorithms/dfs/pacific_atlantic.py index 38f2c5cbe..984aa26ba 100644 --- a/dfs/pacific_atlantic.py +++ b/algorithms/dfs/pacific_atlantic.py @@ -42,24 +42,24 @@ def pacific_atlantic(matrix): atlantic = [[False for _ in range (n)] for _ in range(m)] pacific = [[False for _ in range (n)] for _ in range(m)] for i in range(n): - DFS(pacific, matrix, float("-inf"), i, 0) - DFS(atlantic, matrix, float("-inf"), i, m-1) + dfs(pacific, matrix, float("-inf"), i, 0) + dfs(atlantic, matrix, float("-inf"), i, m-1) for i in range(m): - DFS(pacific, matrix, float("-inf"), 0, i) - DFS(atlantic, matrix, float("-inf"), n-1, i) + dfs(pacific, matrix, float("-inf"), 0, i) + dfs(atlantic, matrix, float("-inf"), n-1, i) for i in range(n): for j in range(m): if pacific[i][j] and atlantic[i][j]: res.append([i, j]) return res -def DFS(grid, matrix, height, i, j): +def dfs(grid, matrix, height, i, j): if i < 0 or i >= len(matrix) or j < 0 or j >= len(matrix[0]): return if grid[i][j] or matrix[i][j] < height: return grid[i][j] = True - DFS(grid, matrix, matrix[i][j], i-1, j) - DFS(grid, matrix, matrix[i][j], i+1, j) - DFS(grid, matrix, matrix[i][j], i, j-1) - DFS(grid, matrix, matrix[i][j], i, j+1) + dfs(grid, matrix, matrix[i][j], i-1, j) + dfs(grid, matrix, matrix[i][j], i+1, j) + dfs(grid, matrix, matrix[i][j], i, j-1) + dfs(grid, matrix, matrix[i][j], i, j+1) diff --git a/algorithms/dfs/sudoku_solver.py b/algorithms/dfs/sudoku_solver.py new file mode 100644 index 000000000..387cdcaea --- /dev/null +++ b/algorithms/dfs/sudoku_solver.py @@ -0,0 +1,110 @@ +""" +It's similar to how human solve Sudoku. + +create a hash table (dictionary) val to store possible values in every location. +Each time, start from the location with fewest possible values, choose one value +from it and then update the board and possible values at other locations. +If this update is valid, keep solving (DFS). If this update is invalid (leaving +zero possible values at some locations) or this value doesn't lead to the +solution, undo the updates and then choose the next value. +Since we calculated val at the beginning and start filling the board from the +location with fewest possible values, the amount of calculation and thus the +runtime can be significantly reduced: + + +The run time is 48-68 ms on LeetCode OJ, which seems to be among the fastest +python solutions here. + + +The PossibleVals function may be further simplified/optimized, but it works just +fine for now. (it would look less lengthy if we are allowed to use numpy array +for the board lol). +""" + +import unittest + +class Sudoku: + def __init__ (self, board, row, col): + self.board = board + self.row = row + self.col = col + self.val = self.possible_values() + + def possible_values(self): + a = "123456789" + d, val = {}, {} + for i in range(self.row): + for j in range(self.col): + ele = self.board[i][j] + if ele != ".": + d[("r", i)] = d.get(("r", i), []) + [ele] + d[("c", j)] = d.get(("c", j), []) + [ele] + d[(i//3, j//3)] = d.get((i//3, j//3), []) + [ele] + else: + val[(i,j)] = [] + for (i,j) in val.keys(): + inval = d.get(("r",i),[])+d.get(("c",j),[])+d.get((i/3,j/3),[]) + val[(i,j)] = [n for n in a if n not in inval ] + return val + + def solve(self): + if len(self.val)==0: + return True + kee = min(self.val.keys(), key=lambda x: len(self.val[x])) + nums = self.val[kee] + for n in nums: + update = {kee:self.val[kee]} + if self.valid_one(n, kee, update): # valid choice + if self.solve(): # keep solving + return True + self.undo(kee, update) # invalid choice or didn't solve it => undo + return False + + def valid_one(self, n, kee, update): + self.board[kee[0]][kee[1]] = n + del self.val[kee] + i, j = kee + for ind in self.val.keys(): + if n in self.val[ind]: + if ind[0]==i or ind[1]==j or (ind[0]/3,ind[1]/3)==(i/3,j/3): + update[ind] = n + self.val[ind].remove(n) + if len(self.val[ind])==0: + return False + return True + + def undo(self, kee, update): + self.board[kee[0]][kee[1]]="." + for k in update: + if k not in self.val: + self.val[k]= update[k] + else: + self.val[k].append(update[k]) + return None + + def __str__(self): + """[summary] + Generates a board representation as string. + + Returns: + [str] -- [board representation] + """ + + resp = "" + for i in range(self.row): + for j in range(self.col): + resp += " {0} ".format(self.board[i][j]) + resp += "\n" + return resp + + +class TestSudoku(unittest.TestCase): + def test_sudoku_solver(self): + board = [["5","3","."], ["6",".", "."],[".","9","8"]] + test_obj = Sudoku(board, 3, 3) + test_obj.solve() + self.assertEqual([['5', '3', '1'], ['6', '1', '2'], ['1', '9', '8']],test_obj.board) + + +if __name__ == "__main__": + unittest.main() diff --git a/dfs/walls_and_gates.py b/algorithms/dfs/walls_and_gates.py similarity index 65% rename from dfs/walls_and_gates.py rename to algorithms/dfs/walls_and_gates.py index 252cb7a1e..7d10b4d6e 100644 --- a/dfs/walls_and_gates.py +++ b/algorithms/dfs/walls_and_gates.py @@ -7,16 +7,16 @@ def walls_and_gates(rooms): for i in range(len(rooms)): for j in range(len(rooms[0])): if rooms[i][j] == 0: - DFS(rooms, i, j, 0) + dfs(rooms, i, j, 0) -def DFS(rooms, i, j, depth): +def dfs(rooms, i, j, depth): if (i < 0 or i >= len(rooms)) or (j < 0 or j >= len(rooms[0])): return # out of bounds if rooms[i][j] < depth: return # crossed rooms[i][j] = depth - DFS(rooms, i+1, j, depth+1) - DFS(rooms, i-1, j, depth+1) - DFS(rooms, i, j+1, depth+1) - DFS(rooms, i, j-1, depth+1) + dfs(rooms, i+1, j, depth+1) + dfs(rooms, i-1, j, depth+1) + dfs(rooms, i, j+1, depth+1) + dfs(rooms, i, j-1, depth+1) diff --git a/dp/buy_sell_stock.py b/algorithms/dp/buy_sell_stock.py similarity index 100% rename from dp/buy_sell_stock.py rename to algorithms/dp/buy_sell_stock.py diff --git a/dp/climbing_stairs.py b/algorithms/dp/climbing_stairs.py similarity index 100% rename from dp/climbing_stairs.py rename to algorithms/dp/climbing_stairs.py diff --git a/algorithms/dp/coin_change.py b/algorithms/dp/coin_change.py new file mode 100644 index 000000000..dce9bb57a --- /dev/null +++ b/algorithms/dp/coin_change.py @@ -0,0 +1,48 @@ +""" +Problem +Given a value N, if we want to make change for N cents, and we have infinite supply of each of +S = { S1, S2, .. , Sm} valued //coins, how many ways can we make the change? +The order of coins doesn’t matter. +For example, for N = 4 and S = [1, 2, 3], there are four solutions: +[1, 1, 1, 1], [1, 1, 2], [2, 2], [1, 3]. +So output should be 4. + +For N = 10 and S = [2, 5, 3, 6], there are five solutions: +[2, 2, 2, 2, 2], [2, 2, 3, 3], [2, 2, 6], [2, 3, 5] and [5, 5]. +So the output should be 5. +""" + +def count(s, n): + # We need n+1 rows as the table is consturcted in bottom up + # manner using the base case 0 value case (n = 0) + m = len(s) + table = [[0 for x in range(m)] for x in range(n+1)] + + # Fill the enteries for 0 value case (n = 0) + for i in range(m): + table[0][i] = 1 + + # Fill rest of the table enteries in bottom up manner + for i in range(1, n+1): + for j in range(m): + # Count of solutions including S[j] + x = table[i - s[j]][j] if i-s[j] >= 0 else 0 + + # Count of solutions excluding S[j] + y = table[i][j-1] if j >= 1 else 0 + + # total count + table[i][j] = x + y + + return table[n][m-1] + + +if __name__ == '__main__': + + coins = [1, 2, 3] + n = 4 + assert count(coins, n) == 4 + + coins = [2, 5, 3, 6] + n = 10 + assert count(coins, n) == 5 diff --git a/dp/combination_sum.py b/algorithms/dp/combination_sum.py similarity index 95% rename from dp/combination_sum.py rename to algorithms/dp/combination_sum.py index 469b02885..67bc6aff7 100644 --- a/dp/combination_sum.py +++ b/algorithms/dp/combination_sum.py @@ -61,6 +61,6 @@ def combination_sum_bottom_up(nums, target): combination_sum_topdown([1, 2, 3], 4) -print dp[4] +print(dp[4]) -print combination_sum_bottom_up([1, 2, 3], 4) +print(combination_sum_bottom_up([1, 2, 3], 4)) diff --git a/algorithms/dp/egg_drop.py b/algorithms/dp/egg_drop.py new file mode 100644 index 000000000..c087982c5 --- /dev/null +++ b/algorithms/dp/egg_drop.py @@ -0,0 +1,31 @@ +# A Dynamic Programming based Python Program for the Egg Dropping Puzzle +INT_MAX = 32767 + +# Function to get minimum number of trials needed in worst +# case with n eggs and k floors +def egg_drop(n, k): + # A 2D table where entery eggFloor[i][j] will represent minimum + # number of trials needed for i eggs and j floors. + egg_floor = [[0 for x in range(k+1)] for x in range(n+1)] + + # We need one trial for one floor and0 trials for 0 floors + for i in range(1, n+1): + egg_floor[i][1] = 1 + egg_floor[i][0] = 0 + + # We always need j trials for one egg and j floors. + for j in range(1, k+1): + egg_floor[1][j] = j + + # Fill rest of the entries in table using optimal substructure + # property + for i in range(2, n+1): + for j in range(2, k+1): + egg_floor[i][j] = INT_MAX + for x in range(1, j+1): + res = 1 + max(egg_floor[i-1][x-1], egg_floor[i][j-x]) + if res < egg_floor[i][j]: + egg_floor[i][j] = res + + # eggFloor[n][k] holds the result + return egg_floor[n][k] diff --git a/algorithms/dp/fib.py b/algorithms/dp/fib.py new file mode 100644 index 000000000..765ee2f34 --- /dev/null +++ b/algorithms/dp/fib.py @@ -0,0 +1,73 @@ +def fib_recursive(n): + """[summary] + Computes the n-th fibonacci number recursive. + Problem: This implementation is very slow. + approximate O(2^n) + + Arguments: + n {[int]} -- [description] + + Returns: + [int] -- [description] + """ + + # precondition + assert n >= 0, 'n must be a positive integer' + + if n <= 1: + return n + else: + return fib_recursive(n-1) + fib_recursive(n-2) + +# print(fib_recursive(35)) # => 9227465 (slow) + +def fib_list(n): + """[summary] + This algorithm computes the n-th fibbonacci number + very quick. approximate O(n) + The algorithm use dynamic programming. + + Arguments: + n {[int]} -- [description] + + Returns: + [int] -- [description] + """ + + # precondition + assert n >= 0, 'n must be a positive integer' + + list_results = [0, 1] + for i in range(2, n+1): + list_results.append(list_results[i-1] + list_results[i-2]) + return list_results[n] + +# print(fib_list(100)) # => 354224848179261915075 + +def fib_iter(n): + """[summary] + Works iterative approximate O(n) + + Arguments: + n {[int]} -- [description] + + Returns: + [int] -- [description] + """ + + # precondition + assert n >= 0, 'n must be positive integer' + + fib_1 = 0 + fib_2 = 1 + sum = 0 + if n <= 1: + return n + for i in range(n-1): + sum = fib_1 + fib_2 + fib_1 = fib_2 + fib_2 = sum + return sum + +# => 354224848179261915075 +# print(fib_iter(100)) diff --git a/dp/house_robber.py b/algorithms/dp/house_robber.py similarity index 100% rename from dp/house_robber.py rename to algorithms/dp/house_robber.py diff --git a/algorithms/dp/job_scheduling.py b/algorithms/dp/job_scheduling.py new file mode 100644 index 000000000..7c7236820 --- /dev/null +++ b/algorithms/dp/job_scheduling.py @@ -0,0 +1,62 @@ +# Python program for weighted job scheduling using Dynamic +# Programming and Binary Search + +# Class to represent a job +class Job: + def __init__(self, start, finish, profit): + self.start = start + self.finish = finish + self.profit = profit + + +# A Binary Search based function to find the latest job +# (before current job) that doesn't conflict with current +# job. "index" is index of the current job. This function +# returns -1 if all jobs before index conflict with it. +# The array jobs[] is sorted in increasing order of finish +# time. +def binary_search(job, start_index): + + # Initialize 'lo' and 'hi' for Binary Search + lo = 0 + hi = start_index - 1 + + # Perform binary Search iteratively + while lo <= hi: + mid = (lo + hi) // 2 + if job[mid].finish <= job[start_index].start: + if job[mid + 1].finish <= job[start_index].start: + lo = mid + 1 + else: + return mid + else: + hi = mid - 1 + return -1 + +# The main function that returns the maximum possible +# profit from given array of jobs +def schedule(job): + + # Sort jobs according to finish time + job = sorted(job, key = lambda j: j.finish) + + # Create an array to store solutions of subproblems. table[i] + # stores the profit for jobs till arr[i] (including arr[i]) + n = len(job) + table = [0 for _ in range(n)] + + table[0] = job[0].profit; + + # Fill entries in table[] using recursive property + for i in range(1, n): + + # Find profit including the current job + incl_prof = job[i].profit + l = binary_search(job, i) + if (l != -1): + incl_prof += table[l]; + + # Store maximum of including and excluding + table[i] = max(incl_prof, table[i - 1]) + + return table[n-1] diff --git a/dp/knapsack.py b/algorithms/dp/knapsack.py similarity index 100% rename from dp/knapsack.py rename to algorithms/dp/knapsack.py diff --git a/dp/longest_increasing.py b/algorithms/dp/longest_increasing.py similarity index 100% rename from dp/longest_increasing.py rename to algorithms/dp/longest_increasing.py diff --git a/algorithms/dp/matrix_chain_order.py b/algorithms/dp/matrix_chain_order.py new file mode 100644 index 000000000..8c1ecae43 --- /dev/null +++ b/algorithms/dp/matrix_chain_order.py @@ -0,0 +1,45 @@ +''' +Dynamic Programming +Implementation of matrix Chain Multiplication +Time Complexity: O(n^3) +Space Complexity: O(n^2) +''' +INF = float("inf") + +def matrix_chain_order(array): + n=len(array) + matrix = [[0 for x in range(n)] for x in range(n)] + sol = [[0 for x in range(n)] for x in range(n)] + for chain_length in range(2,n): + for a in range(1,n-chain_length+1): + b = a+chain_length-1 + + matrix[a][b] = INF + for c in range(a, b): + cost = matrix[a][c] + matrix[c+1][b] + array[a-1]*array[c]*array[b] + if cost < matrix[a][b]: + matrix[a][b] = cost + sol[a][b] = c + return matrix , sol +#Print order of matrix with Ai as matrix + +def print_optimal_solution(optimal_solution,i,j): + if i==j: + print("A" + str(i),end = " ") + else: + print("(",end = " ") + print_optimal_solution(optimal_solution,i,optimal_solution[i][j]) + print_optimal_solution(optimal_solution,optimal_solution[i][j]+1,j) + print(")",end = " ") + +def main(): + array=[30,35,15,5,10,20,25] + n=len(array) + #Size of matrix created from above array will be + # 30*35 35*15 15*5 5*10 10*20 20*25 + matrix , optimal_solution = matrix_chain_order(array) + + print("No. of Operation required: "+str((matrix[1][n-1]))) + print_optimal_solution(optimal_solution,1,n-1) +if __name__ == '__main__': + main() diff --git a/dp/max_product_subarray.py b/algorithms/dp/max_product_subarray.py similarity index 83% rename from dp/max_product_subarray.py rename to algorithms/dp/max_product_subarray.py index 69b0dacf3..592e98fb1 100644 --- a/dp/max_product_subarray.py +++ b/algorithms/dp/max_product_subarray.py @@ -5,6 +5,7 @@ For example, given the array [2,3,-2,4], the contiguous subarray [2,3] has the largest product = 6. """ +from functools import reduce def max_product(nums): @@ -35,6 +36,7 @@ def max_product(nums): #=> max_product_so_far: 1, [1] ''' + def subarray_with_max_product(arr): ''' arr is list of positive/negative numbers ''' l = len(arr) @@ -45,7 +47,8 @@ def subarray_with_max_product(arr): for i in range(l): max_product_end *= arr[i] - if arr[i] > 0: all_negative_flag = False + if arr[i] > 0: + all_negative_flag = False if max_product_end <= 0: max_product_end = arr[i] @@ -55,10 +58,10 @@ def subarray_with_max_product(arr): product_so_far = max_product_end so_far_end_i = i so_far_start_i = max_start_i - + if all_negative_flag: - print "max_product_so_far: %s, %s" % \ - (reduce(lambda x, y: x * y, arr), arr) + print("max_product_so_far: %s, %s" % + (reduce(lambda x, y: x * y, arr), arr)) else: - print "max_product_so_far: %s, %s" % (product_so_far,\ - arr[so_far_start_i:so_far_end_i + 1]) + print("max_product_so_far: %s, %s" % + (product_so_far, arr[so_far_start_i:so_far_end_i + 1])) diff --git a/dp/max_subarray.py b/algorithms/dp/max_subarray.py similarity index 100% rename from dp/max_subarray.py rename to algorithms/dp/max_subarray.py diff --git a/algorithms/dp/min_cost_path.py b/algorithms/dp/min_cost_path.py new file mode 100644 index 000000000..8af947075 --- /dev/null +++ b/algorithms/dp/min_cost_path.py @@ -0,0 +1,53 @@ +""" +author @goswami-rahul + +To find minimum cost path +from station 0 to station N-1, +where cost of moving from ith station to jth station is given as: + +Matrix of size (N x N) +where Matrix[i][j] denotes the cost of moving from +station i --> station j for i < j + +NOTE that values where Matrix[i][j] and i > j does not +mean anything, and hence represented by -1 or INF + +For the input below (cost matrix), +Minimum cost is obtained as from { 0 --> 1 --> 3} + = cost[0][1] + cost[1][3] = 65 +the Output will be: + +The Minimum cost to reach station 4 is 65 + +Time Complexity: O(n^2) +Space Complexity: O(n) +""" + +INF = float("inf") + +def min_cost(cost): + + n = len(cost) + # dist[i] stores minimum cost from 0 --> i. + dist = [INF] * n + + dist[0] = 0 # cost from 0 --> 0 is zero. + + for i in range(n): + for j in range(i+1,n): + dist[j] = min(dist[j], dist[i] + cost[i][j]) + + return dist[n-1] + +if __name__ == '__main__': + + cost = [ [ 0, 15, 80, 90], # cost[i][j] is the cost of + [-1, 0, 40, 50], # going from i --> j + [-1, -1, 0, 70], + [-1, -1, -1, 0] ] # cost[i][j] = -1 for i > j + total_len = len(cost) + + mcost = min_cost(cost) + assert mcost == 65 + + print("The Minimum cost to reach station %d is %d" % (total_len, mcost)) diff --git a/dp/num_decodings.py b/algorithms/dp/num_decodings.py similarity index 100% rename from dp/num_decodings.py rename to algorithms/dp/num_decodings.py diff --git a/dp/regex_matching.py b/algorithms/dp/regex_matching.py similarity index 98% rename from dp/regex_matching.py rename to algorithms/dp/regex_matching.py index 19ce37a2c..78b13c65c 100644 --- a/dp/regex_matching.py +++ b/algorithms/dp/regex_matching.py @@ -18,9 +18,10 @@ isMatch("ab", ".*") → true isMatch("aab", "c*a*b") → true """ +import unittest class Solution(object): - def isMatch(self, s, p): + def is_match(self, s, p): m, n = len(s) + 1, len(p) + 1 matches = [[False] * n for _ in range(m)] diff --git a/algorithms/dp/rod_cut.py b/algorithms/dp/rod_cut.py new file mode 100644 index 000000000..b4a82245e --- /dev/null +++ b/algorithms/dp/rod_cut.py @@ -0,0 +1,24 @@ +# A Dynamic Programming solution for Rod cutting problem +INT_MIN = -32767 + +# Returns the best obtainable price for a rod of length n and +# price[] as prices of different pieces +def cut_rod(price): + n = len(price) + val = [0]*(n+1) + + # Build the table val[] in bottom up manner and return + # the last entry from the table + for i in range(1, n+1): + max_val = INT_MIN + for j in range(i): + max_val = max(max_val, price[j] + val[i-j-1]) + val[i] = max_val + + return val[n] + +# Driver program to test above functions +arr = [1, 5, 8, 9, 10, 17, 17, 20] +print("Maximum Obtainable Value is " + str(cut_rod(arr))) + +# This code is contributed by Bhavya Jain diff --git a/dp/word_break.py b/algorithms/dp/word_break.py similarity index 100% rename from dp/word_break.py rename to algorithms/dp/word_break.py diff --git a/algorithms/graph/Transitive_Closure_DFS.py b/algorithms/graph/Transitive_Closure_DFS.py new file mode 100644 index 000000000..8e8e74da4 --- /dev/null +++ b/algorithms/graph/Transitive_Closure_DFS.py @@ -0,0 +1,52 @@ +# This class represents a directed graph using adjacency +class Graph: + def __init__(self, vertices): + # No. of vertices + self.V = vertices + + # default dictionary to store graph + self.graph = {} + + # To store transitive closure + self.tc = [[0 for j in range(self.V)] for i in range(self.V)] + + # function to add an edge to graph + def add_edge(self, u, v): + if u in self.graph: + self.graph[u].append(v) + else: + self.graph[u] = [v] + + # A recursive DFS traversal function that finds + # all reachable vertices for s + def dfs_util(self, s, v): + + # Mark reachability from s to v as true. + self.tc[s][v] = 1 + + # Find all the vertices reachable through v + for i in self.graph[v]: + if self.tc[s][i] == 0: + self.dfs_util(s, i) + + # The function to find transitive closure. It uses + # recursive dfs_util() + def transitive_closure(self): + + # Call the recursive helper function to print DFS + # traversal starting from all vertices one by one + for i in range(self.V): + self.dfs_util(i, i) + print(self.tc) + + +g = Graph(4) +g.add_edge(0, 1) +g.add_edge(0, 2) +g.add_edge(1, 2) +g.add_edge(2, 0) +g.add_edge(2, 3) +g.add_edge(3, 3) + +print("Transitive closure matrix is") +g.transitive_closure() diff --git a/algorithms/graph/__init__.py b/algorithms/graph/__init__.py new file mode 100644 index 000000000..f94f12c5c --- /dev/null +++ b/algorithms/graph/__init__.py @@ -0,0 +1,2 @@ +from .tarjan import * +from .check_bipartite import * diff --git a/algorithms/graph/check_bipartite.py b/algorithms/graph/check_bipartite.py new file mode 100644 index 000000000..dacc003b3 --- /dev/null +++ b/algorithms/graph/check_bipartite.py @@ -0,0 +1,39 @@ +""" + +Bipartite graph is a graph whose vertices can be divided into two disjoint and independent sets. +(https://en.wikipedia.org/wiki/Bipartite_graph) + +Time complexity is O(|E|) +Space complexity is O(|V|) + +""" + +def check_bipartite(adj_list): + + V = len(adj_list) + + # Divide vertexes in the graph into set_type 1 and 2 + # Initialize all set_types as -1 + set_type = [-1 for v in range(V)] + set_type[0] = 0 + + q = [0] + + while q: + v = q.pop(0) + + # If there is a self-loop, it cannot be bipartite + if adj_list[v][v]: + return False + + for u in range(V): + if adj_list[v][u]: + if set_type[u] == set_type[v]: + return False + elif set_type[u] == -1: + # set type of u opposite of v + set_type[u] = 1 - set_type[v] + q.append(u) + + return True + diff --git a/algorithms/graph/check_digraph_strongly_connected.py b/algorithms/graph/check_digraph_strongly_connected.py new file mode 100644 index 000000000..25ae80cd2 --- /dev/null +++ b/algorithms/graph/check_digraph_strongly_connected.py @@ -0,0 +1,53 @@ +from collections import defaultdict + +class Graph: + def __init__(self,v): + self.v = v + self.graph = defaultdict(list) + + def add_edge(self,u,v): + self.graph[u].append(v) + + def dfs(self): + visited = [False] * self.v + self.dfs_util(0,visited) + if visited == [True]*self.v: + return True + return False + + def dfs_util(self,i,visited): + visited[i] = True + for u in self.graph[i]: + if not(visited[u]): + self.dfs_util(u,visited) + + def reverse_graph(self): + g = Graph(self.v) + for i in range(len(self.graph)): + for j in self.graph[i]: + g.add_edge(j,i) + return g + + + def is_sc(self): + if self.dfs(): + gr = self.reverse_graph() + if gr.dfs(): + return True + return False + + +g1 = Graph(5) +g1.add_edge(0, 1) +g1.add_edge(1, 2) +g1.add_edge(2, 3) +g1.add_edge(3, 0) +g1.add_edge(2, 4) +g1.add_edge(4, 2) +print ("Yes") if g1.is_sc() else print("No") + +g2 = Graph(4) +g2.add_edge(0, 1) +g2.add_edge(1, 2) +g2.add_edge(2, 3) +print ("Yes") if g2.is_sc() else print("No") diff --git a/graph/clone_graph.py b/algorithms/graph/clone_graph.py similarity index 58% rename from graph/clone_graph.py rename to algorithms/graph/clone_graph.py index d2af1def6..0fbae1d43 100644 --- a/graph/clone_graph.py +++ b/algorithms/graph/clone_graph.py @@ -26,68 +26,72 @@ / \ \_/ """ +import collections # Definition for a undirected graph node -# class UndirectedGraphNode: -# def __init__(self, x): -# self.label = x -# self.neighbors = [] +class UndirectedGraphNode: + def __init__(self, x): + self.label = x + self.neighbors = [] # BFS -def cloneGraph1(self, node): +def clone_graph1(node): if not node: return - nodeCopy = UndirectedGraphNode(node.label) - dic = {node: nodeCopy} + node_copy = UndirectedGraphNode(node.label) + dic = {node: node_copy} queue = collections.deque([node]) while queue: node = queue.popleft() for neighbor in node.neighbors: - if neighbor not in dic: # neighbor is not visited - neighborCopy = UndirectedGraphNode(neighbor.label) - dic[neighbor] = neighborCopy - dic[node].neighbors.append(neighborCopy) + if neighbor not in dic: # neighbor is not visited + neighbor_copy = UndirectedGraphNode(neighbor.label) + dic[neighbor] = neighbor_copy + dic[node].neighbors.append(neighbor_copy) queue.append(neighbor) else: dic[node].neighbors.append(dic[neighbor]) - return nodeCopy + return node_copy + # DFS iteratively -def cloneGraph2(self, node): +def clone_graph2(node): if not node: return - nodeCopy = UndirectedGraphNode(node.label) - dic = {node: nodeCopy} + node_copy = UndirectedGraphNode(node.label) + dic = {node: node_copy} stack = [node] while stack: node = stack.pop() for neighbor in node.neighbors: if neighbor not in dic: - neighborCopy = UndirectedGraphNode(neighbor.label) - dic[neighbor] = neighborCopy - dic[node].neighbors.append(neighborCopy) + neighbor_copy = UndirectedGraphNode(neighbor.label) + dic[neighbor] = neighbor_copy + dic[node].neighbors.append(neighbor_copy) stack.append(neighbor) else: dic[node].neighbors.append(dic[neighbor]) - return nodeCopy + return node_copy + # DFS recursively -def cloneGraph(self, node): +def clone_graph(node): if not node: return - nodeCopy = UndirectedGraphNode(node.label) - dic = {node: nodeCopy} - self.dfs(node, dic) - return nodeCopy + node_copy = UndirectedGraphNode(node.label) + dic = {node: node_copy} + dfs(node, dic) + return node_copy + -def dfs(self, node, dic): +def dfs(node, dic): for neighbor in node.neighbors: if neighbor not in dic: - neighborCopy = UndirectedGraphNode(neighbor.label) - dic[neighbor] = neighborCopy - dic[node].neighbors.append(neighborCopy) - self.dfs(neighbor, dic) + neighbor_copy = UndirectedGraphNode(neighbor.label) + dic[neighbor] = neighbor_copy + dic[node].neighbors.append(neighbor_copy) + dfs(neighbor, dic) else: dic[node].neighbors.append(dic[neighbor]) diff --git a/graph/cycle_detection.py b/algorithms/graph/cycle_detection.py similarity index 100% rename from graph/cycle_detection.py rename to algorithms/graph/cycle_detection.py diff --git a/algorithms/graph/dijkstra.py b/algorithms/graph/dijkstra.py new file mode 100644 index 000000000..fe5772ec5 --- /dev/null +++ b/algorithms/graph/dijkstra.py @@ -0,0 +1,36 @@ +#Dijkstra's single source shortest path algorithm + +class Graph(): + + def __init__(self, vertices): + self.vertices = vertices + self.graph = [[0 for column in range(vertices)] for row in range(vertices)] + + def min_distance(self, dist, min_dist_set): + min_dist = float("inf") + for v in range(self.vertices): + if dist[v] < min_dist and min_dist_set[v] == False: + min_dist = dist[v] + min_index = v + return min_index + + def dijkstra(self, src): + + dist = [float("inf")] * self.vertices + dist[src] = 0 + min_dist_set = [False] * self.vertices + + for count in range(self.vertices): + + #minimum distance vertex that is not processed + u = self.min_distance(dist, min_dist_set) + + #put minimum distance vertex in shortest tree + min_dist_set[u] = True + + #Update dist value of the adjacent vertices + for v in range(self.vertices): + if self.graph[u][v] > 0 and min_dist_set[v] == False and dist[v] > dist[u] + self.graph[u][v]: + dist[v] = dist[u] + self.graph[u][v] + + return dist diff --git a/algorithms/graph/find_all_cliques.py b/algorithms/graph/find_all_cliques.py new file mode 100644 index 000000000..10f7a24eb --- /dev/null +++ b/algorithms/graph/find_all_cliques.py @@ -0,0 +1,35 @@ +# takes dict of sets +# each key is a vertex +# value is set of all edges connected to vertex +# returns list of lists (each sub list is a maximal clique) +# implementation of the basic algorithm described in: +# Bron, Coen; Kerbosch, Joep (1973), "Algorithm 457: finding all cliques of an undirected graph", + + +def find_all_cliques(edges): + def expand_clique(candidates, nays): + nonlocal compsub + if not candidates and not nays: + nonlocal solutions + solutions.append(compsub.copy()) + else: + for selected in candidates.copy(): + candidates.remove(selected) + candidates_temp = get_connected(selected, candidates) + nays_temp = get_connected(selected, nays) + compsub.append(selected) + expand_clique(candidates_temp, nays_temp) + nays.add(compsub.pop()) + + def get_connected(vertex, old_set): + new_set = set() + for neighbor in edges[str(vertex)]: + if neighbor in old_set: + new_set.add(neighbor) + return new_set + + compsub = [] + solutions = [] + possibles = set(edges.keys()) + expand_clique(possibles, set()) + return solutions diff --git a/graph/find_path.py b/algorithms/graph/find_path.py similarity index 100% rename from graph/find_path.py rename to algorithms/graph/find_path.py diff --git a/algorithms/graph/graph.py b/algorithms/graph/graph.py new file mode 100644 index 000000000..31e564599 --- /dev/null +++ b/algorithms/graph/graph.py @@ -0,0 +1,113 @@ +""" +These are classes to represent a Graph and its elements. +It can be shared across graph algorithms. +""" + +class Node(object): + def __init__(self, name): + self.name = name + + @staticmethod + def get_name(obj): + if isinstance(obj, Node): + return obj.name + elif isinstance(obj, str): + return obj + return'' + + def __eq__(self, obj): + return self.name == self.get_name(obj) + + def __repr__(self): + return self.name + + def __hash__(self): + return hash(self.name) + + def __ne__(self, obj): + return self.name != self.get_name(obj) + + def __lt__(self, obj): + return self.name < self.get_name(obj) + + def __le__(self, obj): + return self.name <= self.get_name(obj) + + def __gt__(self, obj): + return self.name > self.get_name(obj) + + def __ge__(self, obj): + return self.name >= self.get_name(obj) + + def __bool__(self): + return self.name + +class DirectedEdge(object): + def __init__(self, node_from, node_to): + self.nf = node_from + self.nt = node_to + + def __eq__(self, obj): + if isinstance(obj, DirectedEdge): + return obj.nf == self.nf and obj.nt == self.nt + return False + + def __repr__(self): + return '({0} -> {1})'.format(self.nf, self.nt) + +class DirectedGraph(object): + def __init__(self, load_dict={}): + self.nodes = [] + self.edges = [] + self.adjmt = {} + + if load_dict and type(load_dict) == dict: + for v in load_dict: + node_from = self.add_node(v) + self.adjmt[node_from] = [] + for w in load_dict[v]: + node_to = self.add_node(w) + self.adjmt[node_from].append(node_to) + self.add_edge(v, w) + + def add_node(self, node_name): + try: + return self.nodes[self.nodes.index(node_name)] + except ValueError: + node = Node(node_name) + self.nodes.append(node) + return node + + def add_edge(self, node_name_from, node_name_to): + try: + node_from = self.nodes[self.nodes.index(node_name_from)] + node_to = self.nodes[self.nodes.index(node_name_to)] + self.edges.append(DirectedEdge(node_from, node_to)) + except ValueError: + pass + +class Graph: + def __init__(self, vertices): + # No. of vertices + self.V = vertices + + # default dictionary to store graph + self.graph = {} + + # To store transitive closure + self.tc = [[0 for j in range(self.V)] for i in range(self.V)] + + # function to add an edge to graph + def add_edge(self, u, v): + if u in self.graph: + self.graph[u].append(v) + else: + self.graph[u] = [v] + +#g = Graph(4) +#g.add_edge(0, 1) +#g.add_edge(0, 2) +#g.add_edge(1, 2) +#g.add_edge(2, 0) +#g.add_edge(2, 3) +#g.add_edge(3, 3) diff --git a/graph/markov_chain.py b/algorithms/graph/markov_chain.py similarity index 100% rename from graph/markov_chain.py rename to algorithms/graph/markov_chain.py diff --git a/graph/minimum_spanning_tree.py b/algorithms/graph/minimum_spanning_tree.py similarity index 96% rename from graph/minimum_spanning_tree.py rename to algorithms/graph/minimum_spanning_tree.py index 2f47e39bc..2a877f985 100644 --- a/graph/minimum_spanning_tree.py +++ b/algorithms/graph/minimum_spanning_tree.py @@ -22,7 +22,7 @@ def __init__(self, n): for i in range(n): self.parent[i] = i # Make all nodes his own parent, creating n sets. - def mergeSet(self, a, b): + def merge_set(self, a, b): # Args: # a, b (int): Indexes of nodes whose sets will be merged. @@ -39,12 +39,12 @@ def mergeSet(self, a, b): self.parent[b] = a # Merge set(b) and set(a) self.size[a] += self.size[b] # Add size of old set(b) to set(a) - def findSet(self, a): + def find_set(self, a): if self.parent[a] != a: # Very important, memoize result of the # recursion in the list to optimize next # calls and make this operation practically constant, O(1) - self.parent[a] = self.findSet(self.parent[a]) + self.parent[a] = self.find_set(self.parent[a]) # node it's the set root, so we can return that index return self.parent[a] @@ -76,7 +76,7 @@ def kruskal(n, edges, ds): set_u = ds.findSet(edge.u) # Set of the node set_v = ds.findSet(edge.v) # Set of the node if set_u != set_v: - ds.mergeSet(set_u, set_v) + ds.merge_set(set_u, set_v) mst.append(edge) if len(mst) == n-1: # If we have selected n-1 edges, all the other diff --git a/algorithms/graph/path_between_two_vertices_in_digraph.py b/algorithms/graph/path_between_two_vertices_in_digraph.py new file mode 100644 index 000000000..ee6ac418f --- /dev/null +++ b/algorithms/graph/path_between_two_vertices_in_digraph.py @@ -0,0 +1,51 @@ +from collections import defaultdict + +class Graph: + def __init__(self,v): + self.v = v + self.graph = defaultdict(list) + self.has_path = False + + def add_edge(self,u,v): + self.graph[u].append(v) + + def dfs(self,x,y): + visited = [False] * self.v + self.dfsutil(visited,x,y,) + + def dfsutil(self,visited,x,y): + visited[x] = True + for i in self.graph[x]: + if y in self.graph[x]: + self.has_path = True + return + if(not(visited[i])): + self.dfsutil(visited,x,i) + + def is_reachable(self,x,y): + self.has_path = False + self.dfs(x,y) + return self.has_path + + +# Create a graph given in the above diagram +g = Graph(4) +g.add_edge(0, 1) +g.add_edge(0, 2) +g.add_edge(1, 2) +g.add_edge(2, 0) +g.add_edge(2, 3) +g.add_edge(3, 3) + +u =1; v = 3 + +if g.is_reachable(u, v): + print("There is a path from %d to %d" % (u,v)) +else : + print("There is no path from %d to %d" % (u,v)) + +u = 3; v = 1 +if g.is_reachable(u, v) : + print("There is a path from %d to %d" % (u,v)) +else : + print("There is no path from %d to %d" % (u,v)) diff --git a/graph/satisfiability.py b/algorithms/graph/satisfiability.py similarity index 98% rename from graph/satisfiability.py rename to algorithms/graph/satisfiability.py index ad166c964..bc1d1892e 100644 --- a/graph/satisfiability.py +++ b/algorithms/graph/satisfiability.py @@ -118,8 +118,9 @@ def solve_sat(formula): return value + if __name__ == '__main__': result = solve_sat(formula) for (variable, assign) in result.iteritems(): - print variable, ":", assign + print("{}:{}".format(variable, assign)) diff --git a/algorithms/graph/tarjan.py b/algorithms/graph/tarjan.py new file mode 100644 index 000000000..a2f44e58d --- /dev/null +++ b/algorithms/graph/tarjan.py @@ -0,0 +1,57 @@ +""" +Implements Tarjan's algorithm for finding strongly connected components +in a graph. +https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm +""" +from algorithms.graph.graph import DirectedGraph + +class Tarjan(object): + def __init__(self, dict_graph): + self.graph = DirectedGraph(dict_graph) + self.index = 0 + self.stack = [] + + # Runs Tarjan + # Set all node index to None + for v in self.graph.nodes: + v.index = None + + self.sccs = [] + for v in self.graph.nodes: + if v.index == None: + self.strongconnect(v, self.sccs) + + def strongconnect(self, v, sccs): + # Set the depth index for v to the smallest unused index + v.index = self.index + v.lowlink = self.index + self.index += 1 + self.stack.append(v) + v.on_stack = True + + # Consider successors of v + for w in self.graph.adjmt[v]: + if w.index == None: + # Successor w has not yet been visited; recurse on it + self.strongconnect(w, sccs) + v.lowlink = min(v.lowlink, w.lowlink) + elif w.on_stack: + # Successor w is in stack S and hence in the current SCC + # If w is not on stack, then (v, w) is a cross-edge in the DFS tree and must be ignored + # Note: The next line may look odd - but is correct. + # It says w.index not w.lowlink; that is deliberate and from the original paper + v.lowlink = min(v.lowlink, w.index) + + # If v is a root node, pop the stack and generate an SCC + if v.lowlink == v.index: + # start a new strongly connected component + scc = [] + while True: + w = self.stack.pop() + w.on_stack = False + scc.append(w) + if w == v: + break + scc.sort() + sccs.append(scc) + diff --git a/graph/traversal.py b/algorithms/graph/traversal.py similarity index 100% rename from graph/traversal.py rename to algorithms/graph/traversal.py diff --git a/algorithms/heap/__init__.py b/algorithms/heap/__init__.py new file mode 100644 index 000000000..3731b0ee9 --- /dev/null +++ b/algorithms/heap/__init__.py @@ -0,0 +1,3 @@ +from .binary_heap import * +from .skyline import * +from .sliding_window_max import * diff --git a/algorithms/heap/binary_heap.py b/algorithms/heap/binary_heap.py new file mode 100644 index 000000000..5119a0309 --- /dev/null +++ b/algorithms/heap/binary_heap.py @@ -0,0 +1,115 @@ +""" +Binary Heap. A min heap is a complete binary tree where each node is smaller +its childen. The root, therefore, is the minimum element in the tree. The min +heap use array to represent the data and operation. For example a min heap: + + 4 + / \ + 50 7 + / \ / +55 90 87 + +Heap [0, 4, 50, 7, 55, 90, 87] + +Method in class: insert, remove_min +For example insert(2) in a min heap: + + 4 4 2 + / \ / \ / \ + 50 7 --> 50 2 --> 50 4 + / \ / \ / \ / \ / \ / \ +55 90 87 2 55 90 87 7 55 90 87 7 + +For example remove_min() in a min heap: + + 4 87 7 + / \ / \ / \ + 50 7 --> 50 7 --> 50 87 + / \ / / \ / \ +55 90 87 55 90 55 90 + +""" +from abc import ABCMeta, abstractmethod + +class AbstractHeap(metaclass=ABCMeta): + """Abstract Class for Binary Heap.""" + def __init__(self): + pass + @abstractmethod + def perc_up(self, i): + pass + @abstractmethod + def insert(self, val): + pass + @abstractmethod + def perc_down(self,i): + pass + @abstractmethod + def min_child(self,i): + pass + @abstractmethod + def remove_min(self,i): + pass +class BinaryHeap(AbstractHeap): + def __init__(self): + self.currentSize = 0 + self.heap = [(0)] + + def perc_up(self, i): + while i // 2 > 0: + if self.heap[i] < self.heap[i // 2]: + # Swap value of child with value of its parent + tmp = self.heap[i] + self.heap[i] = self.heap[i // 2] + self.heap[i // 2] = tmp + i = i // 2 + + """ + Method insert always start by inserting the element at the bottom. + it inserts rightmost spot so as to maintain the complete tree property + Then, it fix the tree by swapping the new element with its parent, + until it finds an appropriate spot for the element. It essentially + perc_up the minimum element + Complexity: O(logN) + """ + def insert(self, val): + self.heap.append(val) + self.currentSize = self.currentSize + 1 + self.perc_up(self.currentSize) + + """ + Method min_child returns index of smaller 2 childs of its parent + """ + def min_child(self, i): + if 2 * i + 1 > self.currentSize: # No right child + return 2 * i + else: + # left child > right child + if self.heap[2 * i] > self.heap[2 * i +1]: + return 2 * i + 1 + else: + return 2 * i + + def perc_down(self, i): + while 2 * i < self.currentSize: + min_child = self.min_child(i) + if self.heap[min_child] < self.heap[i]: + # Swap min child with parent + tmp = self.heap[min_child] + self.heap[min_child] = self.heap[i] + self.heap[i] = tmp + i = min_child + """ + Remove Min method removes the minimum element and swap it with the last + element in the heap( the bottommost, rightmost element). Then, it + perc_down this element, swapping it with one of its children until the + min heap property is restored + Complexity: O(logN) + """ + def remove_min(self): + ret = self.heap[1] # the smallest value at beginning + self.heap[1] = self.heap[self.currentSize] # Repalce it by the last value + self.currentSize = self.currentSize - 1 + self.heap.pop() + self.perc_down(1) + return ret diff --git a/heap/merge_sorted_k_lists.py b/algorithms/heap/merge_sorted_k_lists.py similarity index 61% rename from heap/merge_sorted_k_lists.py rename to algorithms/heap/merge_sorted_k_lists.py index 266b12321..2fbfe1df2 100644 --- a/heap/merge_sorted_k_lists.py +++ b/algorithms/heap/merge_sorted_k_lists.py @@ -2,22 +2,26 @@ Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. """ + +from heapq import heappop, heapreplace, heapify +from queue import PriorityQueue + + # Definition for singly-linked list. -# class ListNode(object): -# def __init__(self, x): -# self.val = x -# self.next = None +class ListNode(object): + def __init__(self, x): + self.val = x + self.next = None -from heapq import heappush, heappop, heapreplace, heapify -def mergeKLists(lists): +def merge_k_lists(lists): dummy = node = ListNode(0) h = [(n.val, n) for n in lists if n] heapify(h) while h: v, n = h[0] if n.next is None: - heappop(h) #only change heap size when necessary + heappop(h) # only change heap size when necessary else: heapreplace(h, (n.next.val, n.next)) node.next = n @@ -25,18 +29,19 @@ def mergeKLists(lists): return dummy.next -from Queue import PriorityQueue def merge_k_lists(lists): dummy = ListNode(None) curr = dummy q = PriorityQueue() for node in lists: - if node: q.put((node.val,node)) - while q.qsize()>0: - curr.next = q.get()[1] - curr=curr.next - if curr.next: q.put((curr.next.val, curr.next)) + if node: + q.put((node.val, node)) + while not q.empty(): + curr.next = q.get()[1] # These two lines seem to + curr = curr.next # be equivalent to :- curr = q.get()[1] + if curr.next: + q.put((curr.next.val, curr.next)) return dummy.next @@ -44,13 +49,13 @@ def merge_k_lists(lists): I think my code's complexity is also O(nlogk) and not using heap or priority queue, n means the total elements and k means the size of list. -The mergeTwoLists functiony in my code comes from the problem Merge Two Sorted Lists +The mergeTwoLists function in my code comes from the problem Merge Two Sorted Lists whose complexity obviously is O(n), n is the sum of length of l1 and l2. To put it simpler, assume the k is 2^x, So the progress of combination is like a full binary tree, from bottom to top. So on every level of tree, the combination complexity is n, -beacause every level have all n numbers without repetition. -The level of tree is x, ie logk. So the complexity is O(nlogk). +because every level have all n numbers without repetition. +The level of tree is x, ie log k. So the complexity is O(n log k). for example, 8 ListNode, and the length of every ListNode is x1, x2, x3, x4, x5, x6, x7, x8, total is n. diff --git a/heap/skyline.py b/algorithms/heap/skyline.py similarity index 85% rename from heap/skyline.py rename to algorithms/heap/skyline.py index b3ab5cc70..c666703f3 100644 --- a/heap/skyline.py +++ b/algorithms/heap/skyline.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ A city's skyline is the outer contour of the silhouette formed by all the buildings in that city when viewed from a distance. @@ -33,22 +34,21 @@ into one in the final output as such: [...[2 3], [4 5], [12 7], ...] """ - import heapq -def get_skyline(LRH): +def get_skyline(lrh): """ Wortst Time Complexity: O(NlogN) :type buildings: List[List[int]] :rtype: List[List[int]] """ skyline, live = [], [] - i, n = 0, len(LRH) + i, n = 0, len(lrh) while i < n or live: - if not live or i < n and LRH[i][0] <= -live[0][1]: - x = LRH[i][0] - while i < n and LRH[i][0] == x: - heapq.heappush(live, (-LRH[i][2], -LRH[i][1])) + if not live or i < n and lrh[i][0] <= -live[0][1]: + x = lrh[i][0] + while i < n and lrh[i][0] == x: + heapq.heappush(live, (-lrh[i][2], -lrh[i][1])) i += 1 else: x = -live[0][1] @@ -58,9 +58,3 @@ def get_skyline(LRH): if not skyline or height != skyline[-1][1]: skyline += [x, height], return skyline - -buildings = [ [2, 9, 10], [3, 7, 15], [5, 12, 12], [15, 20, 10], [19, 24, 8] ] -# [ [2 10], [3 15], [7 12], [12 0], [15 10], [20 8], [24, 0] ] -print(get_skyline(buildings)) - - diff --git a/heap/sliding_window_max.py b/algorithms/heap/sliding_window_max.py similarity index 100% rename from heap/sliding_window_max.py rename to algorithms/heap/sliding_window_max.py diff --git a/algorithms/linkedlist/__init__.py b/algorithms/linkedlist/__init__.py new file mode 100644 index 000000000..21263bfe9 --- /dev/null +++ b/algorithms/linkedlist/__init__.py @@ -0,0 +1,8 @@ +from .reverse import * +from .is_sorted import * +from .remove_range import * +from .swap_in_pairs import * +from .rotate_list import * +from .is_cyclic import * +from .merge_two_list import * +from .is_palindrome import * diff --git a/algorithms/linkedlist/add_two_numbers.py b/algorithms/linkedlist/add_two_numbers.py new file mode 100644 index 000000000..02f7eff6b --- /dev/null +++ b/algorithms/linkedlist/add_two_numbers.py @@ -0,0 +1,132 @@ +""" +You are given two non-empty linked lists representing +two non-negative integers. The digits are stored in reverse order +and each of their nodes contain a single digit. +Add the two numbers and return it as a linked list. + +You may assume the two numbers do not contain any leading zero, +except the number 0 itself. + +Input: (2 -> 4 -> 3) + (5 -> 6 -> 4) +Output: 7 -> 0 -> 8 +""" + +import unittest + + +class Node: + def __init__(self, x): + self.val = x + self.next = None + + +def add_two_numbers(left: Node, right: Node) -> Node: + head = Node(0) + current = head + sum = 0 + while left or right: + print("adding: ", left.val, right.val) + sum //= 10 + if left: + sum += left.val + left = left.next + if right: + sum += right.val + right = right.next + current.next = Node(sum % 10) + current = current.next + if sum // 10 == 1: + current.next = Node(1) + return head.next + + +def convert_to_list(number: int) -> Node: + """ + converts a positive integer into a (reversed) linked list. + for example: give 112 + result 2 -> 1 -> 1 + """ + if number >= 0: + head = Node(0) + current = head + remainder = number % 10 + quotient = number // 10 + + while quotient != 0: + current.next = Node(remainder) + current = current.next + remainder = quotient % 10 + quotient //= 10 + current.next = Node(remainder) + return head.next + else: + print("number must be positive!") + + +def convert_to_str(l: Node) -> str: + """ + converts the non-negative number list into a string. + """ + result = "" + while l: + result += str(l.val) + l = l.next + return result + + +class TestSuite(unittest.TestCase): + """ + testsuite for the linked list structure and + the adding function, above. + """ + + def test_convert_to_str(self): + number1 = Node(2) + number1.next = Node(4) + number1.next.next = Node(3) + self.assertEqual("243", convert_to_str(number1)) + + def test_add_two_numbers(self): + # 1. test case + number1 = Node(2) + number1.next = Node(4) + number1.next.next = Node(3) + number2 = Node(5) + number2.next = Node(6) + number2.next.next = Node(4) + result = convert_to_str(add_two_numbers(number1, number2)) + self.assertEqual("708", result) + + # 2. test case + number3 = Node(1) + number3.next = Node(1) + number3.next.next = Node(9) + number4 = Node(1) + number4.next = Node(0) + number4.next.next = Node(1) + result = convert_to_str(add_two_numbers(number3, number4)) + self.assertEqual("2101", result) + + # 3. test case + number5 = Node(1) + number6 = Node(0) + result = convert_to_str(add_two_numbers(number5, number6)) + self.assertEqual("1", result) + + # 4. test case + number7 = Node(9) + number7.next = Node(1) + number7.next.next = Node(1) + number8 = Node(1) + number8.next = Node(0) + number8.next.next = Node(1) + result = convert_to_str(add_two_numbers(number7, number8)) + self.assertEqual("022", result) + + def test_convert_to_list(self): + result = convert_to_str(convert_to_list(112)) + self.assertEqual("211", result) + + +if __name__ == "__main__": + unittest.main() diff --git a/algorithms/linkedlist/copy_random_pointer.py b/algorithms/linkedlist/copy_random_pointer.py new file mode 100644 index 000000000..7653d4902 --- /dev/null +++ b/algorithms/linkedlist/copy_random_pointer.py @@ -0,0 +1,38 @@ +""" +A linked list is given such that each node contains an additional random +pointer which could point to any node in the list or null. + +Return a deep copy of the list. +""" +# from collections import defaultdict + +# TODO: This requires to be rewritten -- commenting it out for now +# class Solution0: + # @param head, a RandomListNode + # @return a RandomListNode + # def copyRandomList(self, head): + # dic = dict() + # m = n = head + # while m: + # dic[m] = RandomListNode(m.label) + # m = m.next + # while n: + # dic[n].next = dic.get(n.next) + # dic[n].random = dic.get(n.random) + # n = n.next + # return dic.get(head) +# +# +# class Solution1: # O(n) +# @param head, a RandomListNode +# @return a RandomListNode + # def copyRandomList(self, head): + # copy = defaultdict(lambda: RandomListNode(0)) + # copy[None] = None + # node = head + # while node: + # copy[node].label = node.label + # copy[node].next = copy[node.next] + # copy[node].random = copy[node.random] + # node = node.next + # return copy[head] diff --git a/algorithms/linkedlist/delete_node.py b/algorithms/linkedlist/delete_node.py new file mode 100644 index 000000000..302b52918 --- /dev/null +++ b/algorithms/linkedlist/delete_node.py @@ -0,0 +1,63 @@ +""" +Write a function to delete a node (except the tail) +in a singly linked list, given only access to that node. + +Supposed the linked list is 1 -> 2 -> 3 -> 4 and +you are given the third node with value 3, +the linked list should become 1 -> 2 -> 4 after calling your function. +""" +import unittest + + +class Node: + def __init__(self, x): + self.val = x + self.next = None + + +def delete_node(node): + if node is None or node.next is None: + raise ValueError + node.val = node.next.val + node.next = node.next.next + + +class TestSuite(unittest.TestCase): + + def test_delete_node(self): + + # make linkedlist 1 -> 2 -> 3 -> 4 + head = Node(1) + curr = head + for i in range(2, 6): + curr.next = Node(i) + curr = curr.next + + # node3 = 3 + node3 = head.next.next + + # after delete_node => 1 -> 2 -> 4 + delete_node(node3) + + curr = head + self.assertEqual(1, curr.val) + + curr = curr.next + self.assertEqual(2, curr.val) + + curr = curr.next + self.assertEqual(4, curr.val) + + curr = curr.next + self.assertEqual(5, curr.val) + + tail = curr + self.assertIsNone(tail.next) + + self.assertRaises(ValueError, delete_node, tail) + self.assertRaises(ValueError, delete_node, tail.next) + + +if __name__ == '__main__': + + unittest.main() diff --git a/algorithms/linkedlist/first_cyclic_node.py b/algorithms/linkedlist/first_cyclic_node.py new file mode 100644 index 000000000..9ab7f88f7 --- /dev/null +++ b/algorithms/linkedlist/first_cyclic_node.py @@ -0,0 +1,64 @@ +""" + Given a linked list, find the first node of a cycle in it. + 1 -> 2 -> 3 -> 4 -> 5 -> 1 => 1 + A -> B -> C -> D -> E -> C => C + + Note: The solution is a direct implementation + Floyd's cycle-finding algorithm (Floyd's Tortoise and Hare). +""" +import unittest + + +class Node: + + def __init__(self, x): + self.val = x + self.next = None + + +def first_cyclic_node(head): + """ + :type head: Node + :rtype: Node + """ + runner = walker = head + while runner and runner.next: + runner = runner.next.next + walker = walker.next + if runner is walker: + break + + if runner is None or runner.next is None: + return None + + walker = head + while runner is not walker: + runner, walker = runner.next, walker.next + return runner + + +class TestSuite(unittest.TestCase): + + def test_first_cyclic_node(self): + + # create linked list => A -> B -> C -> D -> E -> C + head = Node('A') + head.next = Node('B') + curr = head.next + + cyclic_node = Node('C') + curr.next = cyclic_node + + curr = curr.next + curr.next = Node('D') + curr = curr.next + curr.next = Node('E') + curr = curr.next + curr.next = cyclic_node + + self.assertEqual('C', first_cyclic_node(head).val) + + +if __name__ == '__main__': + + unittest.main() diff --git a/algorithms/linkedlist/intersection.py b/algorithms/linkedlist/intersection.py new file mode 100644 index 000000000..71c4c1c61 --- /dev/null +++ b/algorithms/linkedlist/intersection.py @@ -0,0 +1,101 @@ +""" + This function takes two lists and returns the node they have in common, if any. + In this example: + 1 -> 3 -> 5 + \ + 7 -> 9 -> 11 + / + 2 -> 4 -> 6 + ...we would return 7. + Note that the node itself is the unique identifier, not the value of the node. + """ +import unittest + + +class Node(object): + def __init__(self, val=None): + self.val = val + self.next = None + + +def intersection(h1, h2): + + count = 0 + flag = None + h1_orig = h1 + h2_orig = h2 + + while h1 or h2: + count += 1 + + if not flag and (h1.next is None or h2.next is None): + # We hit the end of one of the lists, set a flag for this + flag = (count, h1.next, h2.next) + + if h1: + h1 = h1.next + if h2: + h2 = h2.next + + long_len = count # Mark the length of the longer of the two lists + short_len = flag[0] + + if flag[1] is None: + shorter = h1_orig + longer = h2_orig + elif flag[2] is None: + shorter = h2_orig + longer = h1_orig + + while longer and shorter: + + while long_len > short_len: + # force the longer of the two lists to "catch up" + longer = longer.next + long_len -= 1 + + if longer == shorter: + # The nodes match, return the node + return longer + else: + longer = longer.next + shorter = shorter.next + + return None + + +class TestSuite(unittest.TestCase): + + def test_intersection(self): + + # create linked list as: + # 1 -> 3 -> 5 + # \ + # 7 -> 9 -> 11 + # / + # 2 -> 4 -> 6 + a1 = Node(1) + b1 = Node(3) + c1 = Node(5) + d = Node(7) + a2 = Node(2) + b2 = Node(4) + c2 = Node(6) + e = Node(9) + f = Node(11) + + a1.next = b1 + b1.next = c1 + c1.next = d + a2.next = b2 + b2.next = c2 + c2.next = d + d.next = e + e.next = f + + self.assertEqual(7, intersection(a1, a2).val) + + +if __name__ == '__main__': + + unittest.main() diff --git a/linkedlist/is_cyclic.py b/algorithms/linkedlist/is_cyclic.py similarity index 84% rename from linkedlist/is_cyclic.py rename to algorithms/linkedlist/is_cyclic.py index e9be312c6..70d2c6616 100644 --- a/linkedlist/is_cyclic.py +++ b/algorithms/linkedlist/is_cyclic.py @@ -4,7 +4,11 @@ Follow up: Can you solve it without using extra space? """ +class Node: + def __init__(self, x): + self.val = x + self.next = None def is_cyclic(head): """ diff --git a/algorithms/linkedlist/is_palindrome.py b/algorithms/linkedlist/is_palindrome.py new file mode 100644 index 000000000..c9f2bc18c --- /dev/null +++ b/algorithms/linkedlist/is_palindrome.py @@ -0,0 +1,89 @@ +def is_palindrome(head): + if not head: + return True + # split the list to two parts + fast, slow = head.next, head + while fast and fast.next: + fast = fast.next.next + slow = slow.next + second = slow.next + slow.next = None # Don't forget here! But forget still works! + # reverse the second part + node = None + while second: + nxt = second.next + second.next = node + node = second + second = nxt + # compare two parts + # second part has the same or one less node + while node: + if node.val != head.val: + return False + node = node.next + head = head.next + return True + + +def is_palindrome_stack(head): + if not head or not head.next: + return True + + # 1. Get the midpoint (slow) + slow = fast = cur = head + while fast and fast.next: + fast, slow = fast.next.next, slow.next + + # 2. Push the second half into the stack + stack = [slow.val] + while slow.next: + slow = slow.next + stack.append(slow.val) + + # 3. Comparison + while stack: + if stack.pop() != cur.val: + return False + cur = cur.next + + return True + + +def is_palindrome_dict(head): + """ + This function builds up a dictionary where the keys are the values of the list, + and the values are the positions at which these values occur in the list. + We then iterate over the dict and if there is more than one key with an odd + number of occurrences, bail out and return False. + Otherwise, we want to ensure that the positions of occurrence sum to the + value of the length of the list - 1, working from the outside of the list inward. + For example: + Input: 1 -> 1 -> 2 -> 3 -> 2 -> 1 -> 1 + d = {1: [0,1,5,6], 2: [2,4], 3: [3]} + '3' is the middle outlier, 2+4=6, 0+6=6 and 5+1=6 so we have a palindrome. + """ + if not head or not head.next: + return True + d = {} + pos = 0 + while head: + if head.val in d.keys(): + d[head.val].append(pos) + else: + d[head.val] = [pos] + head = head.next + pos += 1 + checksum = pos - 1 + middle = 0 + for v in d.values(): + if len(v) % 2 != 0: + middle += 1 + else: + step = 0 + for i in range(0, len(v)): + if v[i] + v[len(v) - 1 - step] != checksum: + return False + step += 1 + if middle > 1: + return False + return True diff --git a/algorithms/linkedlist/is_sorted.py b/algorithms/linkedlist/is_sorted.py new file mode 100644 index 000000000..978c5a69b --- /dev/null +++ b/algorithms/linkedlist/is_sorted.py @@ -0,0 +1,19 @@ +""" +Given a linked list, is_sort function returns true if the list is in sorted +(increasing) order and return false otherwise. An empty list is considered +to be sorted. + +For example: +Null :List is sorted +1 2 3 4 :List is sorted +1 2 -1 3 :List is not sorted +""" +def is_sorted(head): + if not head: + return True + current = head + while current.next: + if current.val > current.next.val: + return False + current = current.next + return True diff --git a/algorithms/linkedlist/kth_to_last.py b/algorithms/linkedlist/kth_to_last.py new file mode 100644 index 000000000..fb70a5c16 --- /dev/null +++ b/algorithms/linkedlist/kth_to_last.py @@ -0,0 +1,120 @@ +class Node(): + def __init__(self, val=None): + self.val = val + self.next = None + + +def kth_to_last_eval(head, k): + """ + This is a suboptimal, hacky method using eval(), which is not + safe for user input. We guard against danger by ensuring k in an int + """ + if not isinstance(k, int) or not head.val: + return False + + nexts = '.'.join(['next' for n in range(1, k+1)]) + seeker = str('.'.join(['head', nexts])) + + while head: + if eval(seeker) is None: + return head + else: + head = head.next + + return False + + +def kth_to_last_dict(head, k): + """ + This is a brute force method where we keep a dict the size of the list + Then we check it for the value we need. If the key is not in the dict, + our and statement will short circuit and return False + """ + if not (head and k > -1): + return False + d = dict() + count = 0 + while head: + d[count] = head + head = head.next + count += 1 + return len(d)-k in d and d[len(d)-k] + + +def kth_to_last(head, k): + """ + This is an optimal method using iteration. + We move p1 k steps ahead into the list. + Then we move p1 and p2 together until p1 hits the end. + """ + if not (head or k > -1): + return False + p1 = head + p2 = head + for i in range(1, k+1): + if p1 is None: + # Went too far, k is not valid + raise IndexError + p1 = p1.next + while p1: + p1 = p1.next + p2 = p2.next + return p2 + + +def print_linked_list(head): + string = "" + while head.next: + string += head.val + " -> " + head = head.next + string += head.val + print(string) + + +def test(): + # def make_test_li + # A A B C D C F G + a1 = Node("A") + a2 = Node("A") + b = Node("B") + c1 = Node("C") + d = Node("D") + c2 = Node("C") + f = Node("F") + g = Node("G") + a1.next = a2 + a2.next = b + b.next = c1 + c1.next = d + d.next = c2 + c2.next = f + f.next = g + print_linked_list(a1) + + # test kth_to_last_eval + kth = kth_to_last_eval(a1, 4) + try: + assert kth.val == "D" + except AssertionError as e: + e.args += ("Expecting D, got %s" % kth.val,) + raise + + # test kth_to_last_dict + kth = kth_to_last_dict(a1, 4) + try: + assert kth.val == "D" + except AssertionError as e: + e.args += ("Expecting D, got %s" % kth.val,) + raise + + # test kth_to_last + kth = kth_to_last(a1, 4) + try: + assert kth.val == "D" + except AssertionError as e: + e.args += ("Expecting D, got %s" % kth.val,) + raise + print("all passed.") + +if __name__ == '__main__': + test() diff --git a/linkedlist/linkedlist.py b/algorithms/linkedlist/linkedlist.py similarity index 100% rename from linkedlist/linkedlist.py rename to algorithms/linkedlist/linkedlist.py diff --git a/algorithms/linkedlist/merge_two_list.py b/algorithms/linkedlist/merge_two_list.py new file mode 100644 index 000000000..6be44f33e --- /dev/null +++ b/algorithms/linkedlist/merge_two_list.py @@ -0,0 +1,37 @@ +""" +Merge two sorted linked lists and return it as a new list. The new list should +be made by splicing together the nodes of the first two lists. + +For example: +Input: 1->2->4, 1->3->4 +Output: 1->1->2->3->4->4 +""" +class Node: + + def __init__(self, x): + self.val = x + self.next = None + +def merge_two_list(l1, l2): + ret = cur = Node(0) + while l1 and l2: + if l1.val < l2.val: + cur.next = l1 + l1 = l1.next + else: + cur.next = l2 + l2 = l2.next + cur = cur.next + cur.next = l1 or l2 + return ret.next + +# recursively +def merge_two_list_recur(l1, l2): + if not l1 or not l2: + return l1 or l2 + if l1.val < l2.val: + l1.next = merge_two_list_recur(l1.next, l2) + return l1 + else: + l2.next = merge_two_list_recur(l1, l2.next) + return l2 diff --git a/algorithms/linkedlist/partition.py b/algorithms/linkedlist/partition.py new file mode 100644 index 000000000..8078f3321 --- /dev/null +++ b/algorithms/linkedlist/partition.py @@ -0,0 +1,76 @@ +""" +Write code to partition a linked list around a value x, such that all nodes less +than x come before all nodes greater than or equal to x. If x is contained +within the list, the values of x only need to be after the elements less than x. +The partition element x can appear anywhere in the "right partition"; +it does not need to appear between the left and right partitions. + +3 -> 5 -> 8 -> 5 -> 10 -> 2 -> 1 [partition=5] +3 -> 1 -> 2 -> 10 -> 5 -> 5 -> 8 + +We assume the values of all linked list nodes are int and that x in an int. +""" + + +class Node(): + def __init__(self, val=None): + self.val = int(val) + self.next = None + + +def print_linked_list(head): + string = "" + while head.next: + string += str(head.val) + " -> " + head = head.next + string += str(head.val) + print(string) + + +def partition(head, x): + left = None + right = None + prev = None + current = head + while current: + if int(current.val) >= x: + if not right: + right = current + else: + if not left: + left = current + else: + prev.next = current.next + left.next = current + left = current + left.next = right + if prev and prev.next is None: + break + # cache previous value in case it needs to be pointed elsewhere + prev = current + current = current.next + + +def test(): + a = Node("3") + b = Node("5") + c = Node("8") + d = Node("5") + e = Node("10") + f = Node("2") + g = Node("1") + + a.next = b + b.next = c + c.next = d + d.next = e + e.next = f + f.next = g + + print_linked_list(a) + partition(a, 5) + print_linked_list(a) + + +if __name__ == '__main__': + test() diff --git a/linkedlist/remove_duplicates.py b/algorithms/linkedlist/remove_duplicates.py similarity index 86% rename from linkedlist/remove_duplicates.py rename to algorithms/linkedlist/remove_duplicates.py index 683fd4f4d..ba18aecf2 100644 --- a/linkedlist/remove_duplicates.py +++ b/algorithms/linkedlist/remove_duplicates.py @@ -3,7 +3,7 @@ def __init__(self, val = None): self.val = val self.next = None -def removeDups(head): +def remove_dups(head): """ Time Complexity: O(N) Space Complexity: O(N) @@ -18,7 +18,7 @@ def removeDups(head): prev = head head = head.next -def removeDupsWithoutSet(head): +def remove_dups_wothout_set(head): """ Time Complexity: O(N^2) Space Complexity: O(1) @@ -33,7 +33,7 @@ def removeDupsWithoutSet(head): runner = runner.next current = current.next -def printLinkedList(head): +def print_linked_list(head): string = "" while head.next: string += head.val + " -> " @@ -60,8 +60,7 @@ def printLinkedList(head): c2.next = f f.next = g -removeDups(a1) -printLinkedList(a1) -removeDupsWithoutSet(a1) -printLinkedList(a1) - +remove_dups(a1) +print_linked_list(a1) +remove_dups_wothout_set(a1) +print_linked_list(a1) diff --git a/algorithms/linkedlist/remove_range.py b/algorithms/linkedlist/remove_range.py new file mode 100644 index 000000000..ba7c1443e --- /dev/null +++ b/algorithms/linkedlist/remove_range.py @@ -0,0 +1,28 @@ +""" +Given a linked list, remove_range function accepts a starting and ending index +as parameters and removes the elements at those indexes (inclusive) from the list + +For example: +List: [8, 13, 17, 4, 9, 12, 98, 41, 7, 23, 0, 92] +remove_range(list, 3, 8); +List becomes: [8, 13, 17, 23, 0, 92] + +legal range of the list (0 < start index < end index < size of list). +""" +def remove_range(head, start, end): + assert(start <= end) + # Case: remove node at head + if start == 0: + for i in range(0, end+1): + if head != None: + head = head.next + else: + current = head + # Move pointer to start position + for i in range(0,start-1): + current = current.next + # Remove data until the end + for i in range(0, end-start + 1): + if current != None and current.next != None: + current.next = current.next.next + return head diff --git a/linkedlist/reverse.py b/algorithms/linkedlist/reverse.py similarity index 81% rename from linkedlist/reverse.py rename to algorithms/linkedlist/reverse.py index dcc77cb0c..c41b81a8b 100644 --- a/linkedlist/reverse.py +++ b/algorithms/linkedlist/reverse.py @@ -1,8 +1,10 @@ """ -Reverse a singly linked list. -""" - +Reverse a singly linked list. For example: +1 --> 2 --> 3 --> 4 +After reverse: +4 --> 3 --> 2 --> 1 +""" # # Iterative solution # T(n)- O(n) @@ -36,6 +38,6 @@ def reverse_list_recursive(head): return head p = head.next head.next = None - revrest = self.reverse(p) + revrest = reverse_list_recursive(p) p.next = head return revrest diff --git a/linkedlist/rotate_list.py b/algorithms/linkedlist/rotate_list.py similarity index 100% rename from linkedlist/rotate_list.py rename to algorithms/linkedlist/rotate_list.py diff --git a/algorithms/linkedlist/swap_in_pairs.py b/algorithms/linkedlist/swap_in_pairs.py new file mode 100644 index 000000000..7f82f5b1e --- /dev/null +++ b/algorithms/linkedlist/swap_in_pairs.py @@ -0,0 +1,30 @@ +""" +Given a linked list, swap every two adjacent nodes +and return its head. + +For example, +Given 1->2->3->4, you should return the list as 2->1->4->3. + +Your algorithm should use only constant space. +You may not modify the values in the list, +only nodes itself can be changed. +""" +class Node(object): + def __init__(self, x): + self.val = x + self.next = None + +def swap_pairs(head): + if not head: + return head + start = Node(0) + start.next = head + current = start + while current.next and current.next.next: + first = current.next + second = current.next.next + first.next = second.next + current.next = second + current.next.next = first + current = current.next.next + return start.next diff --git a/algorithms/map/__init__.py b/algorithms/map/__init__.py new file mode 100644 index 000000000..a663c8a30 --- /dev/null +++ b/algorithms/map/__init__.py @@ -0,0 +1,2 @@ +from .hashtable import * +from .separate_chaining_hashtable import * diff --git a/map/hashtable.py b/algorithms/map/hashtable.py similarity index 63% rename from map/hashtable.py rename to algorithms/map/hashtable.py index 1de554737..f579c0b0d 100644 --- a/map/hashtable.py +++ b/algorithms/map/hashtable.py @@ -1,6 +1,3 @@ -from unittest import TestCase - - class HashTable(object): """ HashMap Data Type @@ -120,81 +117,3 @@ def __resize(self): for key, value in zip(keys, values): if key is not self._empty and key is not self._deleted: self.put(key, value) - - -class TestHashTable(TestCase): - def test_one_entry(self): - m = HashTable(10) - m.put(1, '1') - self.assertEqual('1', m.get(1)) - - def test_add_entry_bigger_than_table_size(self): - m = HashTable(10) - m.put(11, '1') - self.assertEqual('1', m.get(11)) - - def test_get_none_if_key_missing_and_hash_collision(self): - m = HashTable(10) - m.put(1, '1') - self.assertEqual(None, m.get(11)) - - def test_two_entries_with_same_hash(self): - m = HashTable(10) - m.put(1, '1') - m.put(11, '11') - self.assertEqual('1', m.get(1)) - self.assertEqual('11', m.get(11)) - - def test_get_on_full_table_does_halts(self): - # and does not search forever - m = HashTable(10) - for i in range(10, 20): - m.put(i, i) - self.assertEqual(None, m.get(1)) - - def test_delete_key(self): - m = HashTable(10) - m.put(1, 1) - m.del_(1) - self.assertEqual(None, m.get(1)) - - def test_delete_key_and_reassign(self): - m = HashTable(10) - m.put(1, 1) - del m[1] - m.put(1, 2) - self.assertEqual(2, m.get(1)) - - def test_assigning_to_full_table_throws_error(self): - m = HashTable(3) - m.put(1, 1) - m.put(2, 2) - m.put(3, 3) - with self.assertRaises(ValueError): - m.put(4, 4) - - def test_len_trivial(self): - m = HashTable(10) - self.assertEqual(0, len(m)) - for i in range(10): - m.put(i, i) - self.assertEqual(i + 1, len(m)) - - def test_len_after_deletions(self): - m = HashTable(10) - m.put(1, 1) - self.assertEqual(1, len(m)) - m.del_(1) - self.assertEqual(0, len(m)) - m.put(11, 42) - self.assertEqual(1, len(m)) - - def test_resizable_hash_table(self): - m = ResizableHashTable() - self.assertEqual(ResizableHashTable.MIN_SIZE, m.size) - for i in range(ResizableHashTable.MIN_SIZE): - m.put(i, 'foo') - self.assertEqual(ResizableHashTable.MIN_SIZE * 2, m.size) - self.assertEqual('foo', m.get(1)) - self.assertEqual('foo', m.get(3)) - self.assertEqual('foo', m.get(ResizableHashTable.MIN_SIZE - 1)) diff --git a/map/longest_common_subsequence.py b/algorithms/map/longest_common_subsequence.py similarity index 81% rename from map/longest_common_subsequence.py rename to algorithms/map/longest_common_subsequence.py index 247b238e1..631b19d0e 100644 --- a/map/longest_common_subsequence.py +++ b/algorithms/map/longest_common_subsequence.py @@ -1,14 +1,14 @@ """ Given string a and b, with b containing all distinct characters, -find the longest common subsequence's +find the longest common sub sequence's length. -length. Expected complexity O(nlogn). +Expected complexity O(n logn). """ def max_common_sub_string(s1, s2): # Assuming s2 has all unique chars - s2dic = {s2[i]: i for i in xrange(len(s2))} + s2dic = {s2[i]: i for i in range(len(s2))} maxr = 0 subs = '' i = 0 diff --git a/map/randomized_set.py b/algorithms/map/randomized_set.py similarity index 100% rename from map/randomized_set.py rename to algorithms/map/randomized_set.py diff --git a/algorithms/map/separate_chaining_hashtable.py b/algorithms/map/separate_chaining_hashtable.py new file mode 100644 index 000000000..fecb251fa --- /dev/null +++ b/algorithms/map/separate_chaining_hashtable.py @@ -0,0 +1,84 @@ +import unittest + + +class Node(object): + def __init__(self, key=None, value=None, next=None): + self.key = key + self.value = value + self.next = next + + +class SeparateChainingHashTable(object): + """ + HashTable Data Type: + By having each bucket contain a linked list of elements that are hashed to that bucket. + + Usage: + >>> table = SeparateChainingHashTable() # Create a new, empty map. + >>> table.put('hello', 'world') # Add a new key-value pair. + >>> len(table) # Return the number of key-value pairs stored in the map. + 1 + >>> table.get('hello') # Get value by key. + 'world' + >>> del table['hello'] # Equivalent to `table.del_('hello')`, deleting key-value pair. + >>> table.get('hello') is None # Return `None` if a key doesn't exist. + True + """ + _empty = None + + def __init__(self, size=11): + self.size = size + self._len = 0 + self._table = [self._empty] * size + + def put(self, key, value): + hash_ = self.hash(key) + node_ = self._table[hash_] + if node_ is self._empty: + self._table[hash_] = Node(key, value) + else: + while node_.next is not None: + if node_.key == key: + node_.value = value + return + node_ = node_.next + node_.next = Node(key, value) + self._len += 1 + + def get(self, key): + hash_ = self.hash(key) + node_ = self._table[hash_] + while node_ is not self._empty: + if node_.key == key: + return node_.value + node_ = node_.next + return None + + def del_(self, key): + hash_ = self.hash(key) + node_ = self._table[hash_] + pre_node = None + while node_ is not None: + if node_.key == key: + if pre_node is None: + self._table[hash_] = node_.next + else: + pre_node.next = node_.next + self._len -= 1 + pre_node = node_ + node_ = node_.next + + def hash(self, key): + return hash(key) % self.size + + def __len__(self): + return self._len + + def __getitem__(self, key): + return self.get(key) + + def __delitem__(self, key): + return self.del_(key) + + def __setitem__(self, key, value): + self.put(key, value) diff --git a/map/valid_sudoku.py b/algorithms/map/valid_sudoku.py similarity index 100% rename from map/valid_sudoku.py rename to algorithms/map/valid_sudoku.py diff --git a/algorithms/maths/__init__.py b/algorithms/maths/__init__.py new file mode 100644 index 000000000..177fed31d --- /dev/null +++ b/algorithms/maths/__init__.py @@ -0,0 +1,15 @@ +from .base_conversion import * +from .decimal_to_binary_ip import * +from .extended_gcd import * +from .factorial import * +from .gcd import * +from .generate_strobogrammtic import * +from .is_strobogrammatic import * +from .modular_exponential import * +from .next_perfect_square import * +from .prime_check import * +from .primes_sieve_of_eratosthenes import * +from .pythagoras import * +from .rabin_miller import * +from .rsa import * +from .combination import * diff --git a/algorithms/maths/base_conversion.py b/algorithms/maths/base_conversion.py new file mode 100644 index 000000000..cc3b62cbb --- /dev/null +++ b/algorithms/maths/base_conversion.py @@ -0,0 +1,50 @@ +""" +Integer base conversion algorithm + +int2base(5, 2) return '101'. +base2int('F', 16) return 15. + +""" + +import string + +def int_to_base(n, base): + """ + :type n: int + :type base: int + :rtype: str + """ + is_negative = False + if n == 0: + return '0' + elif n < 0: + is_negative = True + n *= -1 + digit = string.digits + string.ascii_uppercase + res = '' + while n > 0: + res += digit[n % base] + n //= base + if is_negative: + return '-' + res[::-1] + else: + return res[::-1] + + +def base_to_int(s, base): + """ + Note : You can use int() built-in function instread of this. + :type s: str + :type base: int + :rtype: int + """ + + digit = {} + for i,c in enumerate(string.digits + string.ascii_uppercase): + digit[c] = i + multiplier = 1 + res = 0 + for c in s[::-1]: + res += digit[c] * multiplier + multiplier *= base + return res diff --git a/algorithms/maths/combination.py b/algorithms/maths/combination.py new file mode 100644 index 000000000..308b0bcc4 --- /dev/null +++ b/algorithms/maths/combination.py @@ -0,0 +1,17 @@ +def combination(n, r): + """This function calculates nCr.""" + if n == r or r == 0: + return 1 + else: + return combination(n-1, r-1) + combination(n-1, r) + +def combination_memo(n, r): + """This function calculates nCr using memoization method.""" + memo = {} + def recur(n, r): + if n == r or r == 0: + return 1 + if (n, r) not in memo: + memo[(n, r)] = recur(n - 1, r - 1) + recur(n - 1, r) + return memo[(n, r)] + return recur(n, r) diff --git a/algorithms/maths/decimal_to_binary_ip.py b/algorithms/maths/decimal_to_binary_ip.py new file mode 100644 index 000000000..579e3402a --- /dev/null +++ b/algorithms/maths/decimal_to_binary_ip.py @@ -0,0 +1,27 @@ +""" +Given an ip address in dotted-decimal representation, determine the +binary representation. For example, +decimal_to_binary(255.0.0.5) returns 11111111.00000000.00000000.00000101 +accepts string +returns string +""" + +def decimal_to_binary_util(val): + bits = [128, 64, 32, 16, 8, 4, 2, 1] + val = int(val) + binary_rep = '' + for bit in bits: + if val >= bit: + binary_rep += str(1) + val -= bit + else: + binary_rep += str(0) + + return binary_rep + +def decimal_to_binary_ip(ip): + values = ip.split('.') + binary_list = [] + for val in values: + binary_list.append(decimal_to_binary_util(val)) + return '.'.join(binary_list) diff --git a/math/extended_gcd.py b/algorithms/maths/extended_gcd.py similarity index 58% rename from math/extended_gcd.py rename to algorithms/maths/extended_gcd.py index 69ecfa301..83b657807 100644 --- a/math/extended_gcd.py +++ b/algorithms/maths/extended_gcd.py @@ -1,11 +1,10 @@ -""" -extended GCD algorithm -return s,t,g -such that a s + b t = GCD(a, b) -and s and t are coprime -""" +def extended_gcd(a, b): + """Extended GCD algorithm. + Return s, t, g + such that a * s + b * t = GCD(a, b) + and s and t are co-prime. + """ -def extended_gcd(a,b): old_s, s = 1, 0 old_t, t = 0, 1 old_r, r = a, b @@ -17,4 +16,4 @@ def extended_gcd(a,b): old_s, s = s, old_s - quotient * s old_t, t = t, old_t - quotient * t - return old_s, old_t, old_r \ No newline at end of file + return old_s, old_t, old_r diff --git a/algorithms/maths/factorial.py b/algorithms/maths/factorial.py new file mode 100644 index 000000000..23e1f5354 --- /dev/null +++ b/algorithms/maths/factorial.py @@ -0,0 +1,32 @@ +def factorial(n, mod=None): + """Calculates factorial iteratively. + If mod is not None, then return (n! % mod) + Time Complexity - O(n)""" + if not (isinstance(n, int) and n >= 0): + raise ValueError("'n' must be a non-negative integer.") + if mod is not None and not (isinstance(mod, int) and mod > 0): + raise ValueError("'mod' must be a positive integer") + result = 1 + if n == 0: + return 1 + for i in range(2, n+1): + result *= i + if mod: + result %= mod + return result + + +def factorial_recur(n, mod=None): + """Calculates factorial recursively. + If mod is not None, then return (n! % mod) + Time Complexity - O(n)""" + if not (isinstance(n, int) and n >= 0): + raise ValueError("'n' must be a non-negative integer.") + if mod is not None and not (isinstance(mod, int) and mod > 0): + raise ValueError("'mod' must be a positive integer") + if n == 0: + return 1 + result = n * factorial(n - 1, mod) + if mod: + result %= mod + return result diff --git a/math/gcd.py b/algorithms/maths/gcd.py similarity index 81% rename from math/gcd.py rename to algorithms/maths/gcd.py index 9792d8faa..1f80fc981 100644 --- a/math/gcd.py +++ b/algorithms/maths/gcd.py @@ -2,10 +2,9 @@ def gcd(a, b): """Computes the greatest common divisor of integers a and b using Euclid's Algorithm. """ - while True: - if b == 0: - return a + while b != 0: a, b = b, a % b + return a def lcm(a, b): diff --git a/math/generate_strobogrammtic.py b/algorithms/maths/generate_strobogrammtic.py similarity index 81% rename from math/generate_strobogrammtic.py rename to algorithms/maths/generate_strobogrammtic.py index f5589ac7e..dd0c400c4 100644 --- a/math/generate_strobogrammtic.py +++ b/algorithms/maths/generate_strobogrammtic.py @@ -14,8 +14,7 @@ def gen_strobogrammatic(n): :type n: int :rtype: List[str] """ - result = helper(n, n) - return result + return helper(n, n) def helper(n, length): @@ -35,7 +34,7 @@ def helper(n, length): return result -def strobogrammaticInRange(low, high): +def strobogrammatic_in_range(low, high): """ :type low: str :type high: str @@ -43,19 +42,21 @@ def strobogrammaticInRange(low, high): """ res = [] count = 0 - for i in range(len(low), len(high)+1): + low_len = len(low) + high_len = len(high) + for i in range(low_len, high_len + 1): res.extend(helper2(i, i)) for perm in res: - if len(perm) == len(low) and int(perm) < int(low): + if len(perm) == low_len and int(perm) < int(low): continue - elif len(perm) == len(high) and int(perm) > int(high): + elif len(perm) == high_len and int(perm) > int(high): continue else: count += 1 return count -def helper2(self, n, length): +def helper2(n, length): if n == 0: return [""] if n == 1: diff --git a/math/is_strobogrammatic.py b/algorithms/maths/is_strobogrammatic.py similarity index 79% rename from math/is_strobogrammatic.py rename to algorithms/maths/is_strobogrammatic.py index e42e87980..018e6955d 100644 --- a/math/is_strobogrammatic.py +++ b/algorithms/maths/is_strobogrammatic.py @@ -24,3 +24,8 @@ def is_strobogrammatic(num): i += 1 j -= 1 return True + + +def is_strobogrammatic2(num: str): + """Another implementation.""" + return num == num[::-1].replace('6', '#').replace('9', '6').replace('#', '9') diff --git a/algorithms/maths/modular_exponential.py b/algorithms/maths/modular_exponential.py new file mode 100644 index 000000000..f0e58de8f --- /dev/null +++ b/algorithms/maths/modular_exponential.py @@ -0,0 +1,18 @@ +def modular_exponential(base, exponent, mod): + """Computes (base ^ exponent) % mod. + Time complexity - O(log n) + Use similar to Python in-built function pow.""" + if exponent < 0: + raise ValueError("Exponent must be positive.") + base %= mod + result = 1 + + while exponent > 0: + # If the last bit is 1, add 2^k. + if exponent & 1: + result = (result * base) % mod + exponent = exponent >> 1 + # Utilize modular multiplication properties to combine the computed mod C values. + base = (base * base) % mod + + return result diff --git a/algorithms/maths/next_bigger.py b/algorithms/maths/next_bigger.py new file mode 100644 index 000000000..e14e22cb8 --- /dev/null +++ b/algorithms/maths/next_bigger.py @@ -0,0 +1,64 @@ +""" +I just bombed an interview and made pretty much zero +progress on my interview question. + +Given a number, find the next higher number which has the +exact same set of digits as the original number. +For example: given 38276 return 38627. + given 99999 return -1. (no such number exists) + +Condensed mathematical description: + +Find largest index i such that array[i − 1] < array[i]. +(If no such i exists, then this is already the last permutation.) + +Find largest index j such that j ≥ i and array[j] > array[i − 1]. + +Swap array[j] and array[i − 1]. + +Reverse the suffix starting at array[i]. + +""" +import unittest + + +def next_bigger(num): + + digits = [int(i) for i in str(num)] + idx = len(digits) - 1 + + while idx >= 1 and digits[idx-1] >= digits[idx]: + idx -= 1 + + if idx == 0: + return -1 # no such number exists + + pivot = digits[idx-1] + swap_idx = len(digits) - 1 + + while pivot >= digits[swap_idx]: + swap_idx -= 1 + + digits[swap_idx], digits[idx-1] = digits[idx-1], digits[swap_idx] + digits[idx:] = digits[:idx-1:-1] # prefer slicing instead of reversed(digits[idx:]) + + return int(''.join(str(x) for x in digits)) + + +class TestSuite(unittest.TestCase): + + def test_next_bigger(self): + + self.assertEqual(next_bigger(38276), 38627) + self.assertEqual(next_bigger(12345), 12354) + self.assertEqual(next_bigger(1528452), 1528524) + self.assertEqual(next_bigger(138654), 143568) + + self.assertEqual(next_bigger(54321), -1) + self.assertEqual(next_bigger(999), -1) + self.assertEqual(next_bigger(5), -1) + + +if __name__ == '__main__': + + unittest.main() diff --git a/algorithms/maths/next_perfect_square.py b/algorithms/maths/next_perfect_square.py new file mode 100644 index 000000000..7e6e6b918 --- /dev/null +++ b/algorithms/maths/next_perfect_square.py @@ -0,0 +1,19 @@ +""" +This program will look for the next perfect square. +Check the argument to see if it is a perfect square itself, if it is not then return -1 otherwise look for the next perfect square +for instance if you pass 121 then the script should return the next perfect square which is 144. +""" + +def find_next_square(sq): + root = sq ** 0.5 + if root.is_integer(): + return (root + 1)**2 + return -1 + + +# Another way: + +def find_next_square2(sq): + x = sq**0.5 + return -1 if x % 1 else (x+1)**2 + diff --git a/algorithms/maths/nth_digit.py b/algorithms/maths/nth_digit.py new file mode 100644 index 000000000..381926f3e --- /dev/null +++ b/algorithms/maths/nth_digit.py @@ -0,0 +1,17 @@ +def find_nth_digit(n): + """find the nth digit of given number. + 1. find the length of the number where the nth digit is from. + 2. find the actual number where the nth digit is from + 3. find the nth digit and return + """ + length = 1 + count = 9 + start = 1 + while n > length * count: + n -= length * count + length += 1 + count *= 10 + start *= 10 + start += (n-1) / length + s = str(start) + return int(s[(n-1) % length]) \ No newline at end of file diff --git a/algorithms/maths/prime_check.py b/algorithms/maths/prime_check.py new file mode 100644 index 000000000..60e4427ab --- /dev/null +++ b/algorithms/maths/prime_check.py @@ -0,0 +1,17 @@ +def prime_check(n): + """Return True if n is a prime number + Else return False. + """ + + if n <= 1: + return False + if n == 2 or n == 3: + return True + if n % 2 == 0 or n % 3 == 0: + return False + j = 5 + while j * j <= n: + if n % j == 0 or n % (j + 2) == 0: + return False + j += 6 + return True diff --git a/math/primes_sieve_of_eratosthenes.py b/algorithms/maths/primes_sieve_of_eratosthenes.py similarity index 59% rename from math/primes_sieve_of_eratosthenes.py rename to algorithms/maths/primes_sieve_of_eratosthenes.py index 34289da64..7c21c6d82 100644 --- a/math/primes_sieve_of_eratosthenes.py +++ b/algorithms/maths/primes_sieve_of_eratosthenes.py @@ -1,11 +1,12 @@ -''' -Using sieve of Eratosthenes, primes(x) returns list of all primes less than x +""" +Return list of all primes less than n, +Using sieve of Eratosthenes. Modification: We don't need to check all even numbers, we can make the sieve excluding even numbers and adding 2 to the primes list by default. -We are going to make an array of: x / 2 - 1 if number is even, else x / 2 +We are going to make an array of: x / 2 - 1 if number is even, else x / 2 (The -1 with even number it's to exclude the number itself) Because we just need numbers [from 3..x if x is odd] @@ -21,20 +22,25 @@ With this, we have reduced the array size to a half, and complexity it's also a half now. -''' +""" -def primes(x): - assert(x >= 0) + +def get_primes(n): + """Return list of all primes less than n, + Using sieve of Eratosthenes. + """ + if n <= 0: + raise ValueError("'n' must be a positive integer.") # If x is even, exclude x from list (-1): - sieve_size = (x//2 - 1) if x % 2 == 0 else (x//2) - sieve = [1 for v in range(sieve_size)] # Sieve - primes = [] # List of Primes - if x >= 2: - primes.append(2) # Add 2 by default - for i in range(0, sieve_size): - if sieve[i] == 1: + sieve_size = (n // 2 - 1) if n % 2 == 0 else (n // 2) + sieve = [True for _ in range(sieve_size)] # Sieve + primes = [] # List of Primes + if n >= 2: + primes.append(2) # 2 is prime by default + for i in range(sieve_size): + if sieve[i]: value_at_i = i*2 + 3 primes.append(value_at_i) for j in range(i, sieve_size, value_at_i): - sieve[j] = 0 + sieve[j] = False return primes diff --git a/math/pythagoras.py b/algorithms/maths/pythagoras.py similarity index 88% rename from math/pythagoras.py rename to algorithms/maths/pythagoras.py index 8effe1349..d89626039 100644 --- a/math/pythagoras.py +++ b/algorithms/maths/pythagoras.py @@ -13,4 +13,4 @@ def pythagoras(opposite,adjacent,hypotenuse): else: return "You already know the answer!" except: - print ("Error, check your input. You must know 2 of the 3 variables.") + raise ValueError("invalid argument were given.") diff --git a/algorithms/maths/rabin_miller.py b/algorithms/maths/rabin_miller.py new file mode 100644 index 000000000..a3aad8ef2 --- /dev/null +++ b/algorithms/maths/rabin_miller.py @@ -0,0 +1,51 @@ +""" +Rabin-Miller primality test +returning False implies that n is guaranteed composite +returning True means that n is probably prime +with a 4 ** -k chance of being wrong +""" +import random + + +def is_prime(n, k): + + def pow2_factor(num): + """factor n into a power of 2 times an odd number""" + power = 0 + while num % 2 == 0: + num /= 2 + power += 1 + return power, num + + def valid_witness(a): + """ + returns true if a is a valid 'witness' for n + a valid witness increases chances of n being prime + an invalid witness guarantees n is composite + """ + x = pow(int(a), int(d), int(n)) + + if x == 1 or x == n - 1: + return False + + for _ in range(r - 1): + x = pow(int(x), int(2), int(n)) + + if x == 1: + return True + if x == n - 1: + return False + + return True + + # precondition n >= 5 + if n < 5: + return n == 2 or n == 3 # True for prime + + r, d = pow2_factor(n - 1) + + for _ in range(k): + if valid_witness(random.randrange(2, n - 2)): + return False + + return True diff --git a/algorithms/maths/rsa.py b/algorithms/maths/rsa.py new file mode 100644 index 000000000..70b7bc5cc --- /dev/null +++ b/algorithms/maths/rsa.py @@ -0,0 +1,96 @@ +""" +RSA encryption algorithm +a method for encrypting a number that uses seperate encryption and decryption keys +this file only implements the key generation algorithm + +there are three important numbers in RSA called n, e, and d +e is called the encryption exponent +d is called the decryption exponent +n is called the modulus + +these three numbers satisfy +((x ** e) ** d) % n == x % n + +to use this system for encryption, n and e are made publicly available, and d is kept secret +a number x can be encrypted by computing (x ** e) % n +the original number can then be recovered by computing (E ** d) % n, where E is +the encrypted number + +fortunately, python provides a three argument version of pow() that can compute powers modulo +a number very quickly: +(a ** b) % c == pow(a,b,c) +""" + +import random + + +def generate_key(k, seed=None): + """ + the RSA key generating algorithm + k is the number of bits in n + """ + + def modinv(a, m): + """calculate the inverse of a mod m + that is, find b such that (a * b) % m == 1""" + b = 1 + while not (a * b) % m == 1: + b += 1 + return b + + def gen_prime(k, seed=None): + """generate a prime with k bits""" + + def is_prime(num): + if num == 2: + return True + for i in range(2, int(num ** 0.5) + 1): + if num % i == 0: + return False + return True + + random.seed(seed) + while True: + key = random.randrange(int(2 ** (k - 1)), int(2 ** k)) + if is_prime(key): + return key + + # size in bits of p and q need to add up to the size of n + p_size = k / 2 + q_size = k - p_size + + e = gen_prime(k, seed) # in many cases, e is also chosen to be a small constant + + while True: + p = gen_prime(p_size, seed) + if p % e != 1: + break + + while True: + q = gen_prime(q_size, seed) + if q % e != 1: + break + + n = p * q + l = (p - 1) * (q - 1) # calculate totient function + d = modinv(e, l) + + return int(n), int(e), int(d) + + +def encrypt(data, e, n): + return pow(int(data), int(e), int(n)) + + +def decrypt(data, d, n): + return pow(int(data), int(d), int(n)) + + + +# sample usage: +# n,e,d = generate_key(16) +# data = 20 +# encrypted = pow(data,e,n) +# decrypted = pow(encrypted,d,n) +# assert decrypted == data + diff --git a/algorithms/maths/sqrt_precision_factor.py b/algorithms/maths/sqrt_precision_factor.py new file mode 100644 index 000000000..bb47c0945 --- /dev/null +++ b/algorithms/maths/sqrt_precision_factor.py @@ -0,0 +1,19 @@ +""" +Given a positive integer N and a precision factor P, +it produces an output +with a maximum error P from the actual square root of N. + +Example: +Given N = 5 and P = 0.001, can produce output x such that +2.235 < x < 2.237. Actual square root of 5 being 2.236. +""" + + +def square_root(n, epsilon=0.001): + """Return square root of n, with maximum absolute error epsilon""" + guess = n / 2 + + while abs(guess * guess - n) > epsilon: + guess = (guess + (n / guess)) / 2 + + return guess diff --git a/algorithms/maths/summing_digits.py b/algorithms/maths/summing_digits.py new file mode 100644 index 000000000..f181a92eb --- /dev/null +++ b/algorithms/maths/summing_digits.py @@ -0,0 +1,33 @@ +""" +Recently, I encountered an interview question whose description was as below: + +The number 89 is the first integer with more than one digit whose digits when raised up to consecutive powers give the same +number. For example, 89 = 8**1 + 9**2 gives the number 89. + +The next number after 89 with this property is 135 = 1**1 + 3**2 + 5**3 = 135. + +Write a function that returns a list of numbers with the above property. The function will receive range as parameter. +""" + +def sum_dig_pow(a, b): + result = [] + + for number in range(a, b + 1): + exponent = 1 # set to 1 + summation = 0 # set to 1 + number_as_string = str(number) + + tokens = list(map(int, number_as_string)) # parse the string into individual digits + + for k in tokens: + summation = summation + (k ** exponent) + exponent += 1 + + if summation == number: + result.append(number) + return result + + +# Some test cases: +assert sum_dig_pow(1, 10) == [1, 2, 3, 4, 5, 6, 7, 8, 9] +assert sum_dig_pow(1, 100) == [1, 2, 3, 4, 5, 6, 7, 8, 9, 89] diff --git a/algorithms/matrix/bomb_enemy.py b/algorithms/matrix/bomb_enemy.py new file mode 100644 index 000000000..debcf5d81 --- /dev/null +++ b/algorithms/matrix/bomb_enemy.py @@ -0,0 +1,93 @@ +""" +Given a 2D grid, each cell is either a wall 'W', +an enemy 'E' or empty '0' (the number zero), +return the maximum enemies you can kill using one bomb. +The bomb kills all the enemies in the same row and column from +the planted point until it hits the wall since the wall is too strong +to be destroyed. +Note that you can only put the bomb at an empty cell. + +Example: +For the given grid + +0 E 0 0 +E 0 W E +0 E 0 0 + +return 3. (Placing a bomb at (1,1) kills 3 enemies) +""" + +def max_killed_enemies(grid): + if not grid: return 0 + m, n = len(grid), len(grid[0]) + max_killed = 0 + row_e, col_e = 0, [0] * n + # iterates over all cells in the grid + for i in range(m): + for j in range(n): + # makes sure we are next to a wall. + if j == 0 or grid[i][j-1] == 'W': + row_e = row_kills(grid, i, j) + # makes sure we are next to a wall. + if i == 0 or grid[i-1][j] == 'W': + col_e[j] = col_kills(grid, i, j) + # makes sure the cell contains a 0 + if grid[i][j] == '0': + # updates the variable + max_killed = max(max_killed, row_e + col_e[j]) + + return max_killed + +# calculate killed enemies for row i from column j +def row_kills(grid, i, j): + num = 0 + len_row = len(grid[0]) + while j < len_row and grid[i][j] != 'W': + if grid[i][j] == 'E': + num += 1 + j += 1 + return num + +# calculate killed enemies for column j from row i +def col_kills(grid, i, j): + num = 0 + len_col = len(grid) + while i < len_col and grid[i][j] != 'W': + if grid[i][j] == 'E': + num += 1 + i += 1 + return num + + + +# ----------------- TESTS ------------------------- + +""" + Testsuite for the project +""" + +import unittest + +class TestBombEnemy(unittest.TestCase): + def test_3x4(self): + grid1 = [["0","E","0","0"], + ["E","0","W","E"], + ["0","E","0","0"]] + self.assertEqual(3,max_killed_enemies(grid1)) + def test_4x4(self): + grid1 = [ + ["0", "E", "0", "E"], + ["E", "E", "E", "0"], + ["E", "0", "W", "E"], + ["0", "E", "0", "0"]] + grid2 = [ + ["0", "0", "0", "E"], + ["E", "0", "0", "0"], + ["E", "0", "W", "E"], + ["0", "E", "0", "0"]] + self.assertEqual(5,max_killed_enemies(grid1)) + self.assertEqual(3,max_killed_enemies(grid2)) + +if __name__ == "__main__": + unittest.main() + diff --git a/matrix/copy_transform.py b/algorithms/matrix/copy_transform.py similarity index 100% rename from matrix/copy_transform.py rename to algorithms/matrix/copy_transform.py diff --git a/matrix/count_paths.py b/algorithms/matrix/count_paths.py similarity index 85% rename from matrix/count_paths.py rename to algorithms/matrix/count_paths.py index f1241e779..7f57c9ef4 100644 --- a/matrix/count_paths.py +++ b/algorithms/matrix/count_paths.py @@ -1,5 +1,3 @@ - - # # Count the number of unique paths from a[0][0] to a[m-1][n-1] # We are allowed to move either right or down from a cell in the matrix. @@ -10,6 +8,8 @@ # matrix. Return count[m-1][n-1] # T(n)- O(mn), S(n)- O(mn) # + + def count_paths(m, n): if m < 1 or n < 1: return -1 @@ -25,14 +25,15 @@ def count_paths(m, n): for j in range(1, n): # Number of ways to reach a[i][j] = number of ways to reach # a[i-1][j] + a[i][j-1] - count[i][j] = count[i-1][j] + count[i][j-1] + count[i][j] = count[i - 1][j] + count[i][j - 1] - print count[m-1][n-1] + print(count[m - 1][n - 1]) def main(): - m, n = map(int, raw_input().split()) + m, n = map(int, input('Enter two positive integers: ').split()) count_paths(m, n) + if __name__ == '__main__': - main() \ No newline at end of file + main() diff --git a/matrix/matrix_rotation.txt b/algorithms/matrix/matrix_rotation.txt similarity index 99% rename from matrix/matrix_rotation.txt rename to algorithms/matrix/matrix_rotation.txt index c25eb201b..9ce876e6a 100644 --- a/matrix/matrix_rotation.txt +++ b/algorithms/matrix/matrix_rotation.txt @@ -260,7 +260,7 @@ We’ll call this our ‘layer loop’. # 5x5 matrix matrix = [ [0,1,2,3,4], - [5,6,6,8,9], + [5,6,7,8,9], [10,11,12,13,14], [15,16,17,18,19], [20,21,22,23,24] diff --git a/matrix/rotate_image.py b/algorithms/matrix/rotate_image.py similarity index 100% rename from matrix/rotate_image.py rename to algorithms/matrix/rotate_image.py diff --git a/matrix/search_in_sorted_matrix.py b/algorithms/matrix/search_in_sorted_matrix.py similarity index 100% rename from matrix/search_in_sorted_matrix.py rename to algorithms/matrix/search_in_sorted_matrix.py diff --git a/algorithms/matrix/sparse_dot_vector.py b/algorithms/matrix/sparse_dot_vector.py new file mode 100644 index 000000000..19053ec58 --- /dev/null +++ b/algorithms/matrix/sparse_dot_vector.py @@ -0,0 +1,71 @@ +#! /usr/bin/env python3 + +""" +Suppose we have very large sparse vectors, which contains a lot of zeros and double . + +find a data structure to store them +get the dot product of them +""" + + +def vector_to_index_value_list(vector): + return [(i, v) for i, v in enumerate(vector) if v != 0.0] + + +def dot_product(iv_list1, iv_list2): + + product = 0 + p1 = len(iv_list1) - 1 + p2 = len(iv_list2) - 1 + + while p1 >= 0 and p2 >= 0: + i1, v1 = iv_list1[p1] + i2, v2 = iv_list2[p2] + + if i1 < i2: + p1 -= 1 + elif i2 < i1: + p2 -= 1 + else: + product += v1 * v2 + p1 -= 1 + p2 -= 1 + + return product + + +def __test_simple(): + print(dot_product(vector_to_index_value_list([1., 2., 3.]), + vector_to_index_value_list([0., 2., 2.]))) + # 10 + + +def __test_time(): + vector_length = 1024 + vector_count = 1024 + nozero_counut = 10 + + def random_vector(): + import random + vector = [0 for _ in range(vector_length)] + for i in random.sample(range(vector_length), nozero_counut): + vector[i] = random.random() + return vector + + vectors = [random_vector() for _ in range(vector_count)] + iv_lists = [vector_to_index_value_list(vector) for vector in vectors] + + import time + + time_start = time.time() + for i in range(vector_count): + for j in range(i): + dot_product(iv_lists[i], iv_lists[j]) + time_end = time.time() + + print(time_end - time_start, 'seconds') + + +if __name__ == '__main__': + __test_simple() + __test_time() diff --git a/algorithms/matrix/sparse_mul.py b/algorithms/matrix/sparse_mul.py new file mode 100644 index 000000000..c3b7b61e4 --- /dev/null +++ b/algorithms/matrix/sparse_mul.py @@ -0,0 +1,99 @@ +""" +Given two sparse matrices A and B, return the result of AB. + +You may assume that A's column number is equal to B's row number. + +Example: + +A = [ + [ 1, 0, 0], + [-1, 0, 3] +] + +B = [ + [ 7, 0, 0 ], + [ 0, 0, 0 ], + [ 0, 0, 1 ] +] + + + | 1 0 0 | | 7 0 0 | | 7 0 0 | +AB = | -1 0 3 | x | 0 0 0 | = | -7 0 3 | + | 0 0 1 | +""" + + +# Python solution without table (~156ms): +def multiply(self, a, b): + """ + :type A: List[List[int]] + :type B: List[List[int]] + :rtype: List[List[int]] + """ + if a is None or b is None: return None + m, n, l = len(a), len(b[0]), len(b[0]) + if len(b) != n: + raise Exception("A's column number must be equal to B's row number.") + c = [[0 for _ in range(l)] for _ in range(m)] + for i, row in enumerate(a): + for k, eleA in enumerate(row): + if eleA: + for j, eleB in enumerate(b[k]): + if eleB: c[i][j] += eleA * eleB + return c + + +# Python solution with only one table for B (~196ms): +def multiply(self, a, b): + """ + :type A: List[List[int]] + :type B: List[List[int]] + :rtype: List[List[int]] + """ + if a is None or b is None: return None + m, n, l = len(a), len(a[0]), len(b[0]) + if len(b) != n: + raise Exception("A's column number must be equal to B's row number.") + c = [[0 for _ in range(l)] for _ in range(m)] + table_b = {} + for k, row in enumerate(b): + table_b[k] = {} + for j, eleB in enumerate(row): + if eleB: table_b[k][j] = eleB + for i, row in enumerate(a): + for k, eleA in enumerate(row): + if eleA: + for j, eleB in table_b[k].iteritems(): + c[i][j] += eleA * eleB + return c + +# Python solution with two tables (~196ms): +def multiply(self, a, b): + """ + :type A: List[List[int]] + :type B: List[List[int]] + :rtype: List[List[int]] + """ + if a is None or b is None: return None + m, n = len(a), len(b[0]) + if len(b) != n: + raise Exception("A's column number must be equal to B's row number.") + l = len(b[0]) + table_a, table_b = {}, {} + for i, row in enumerate(a): + for j, ele in enumerate(row): + if ele: + if i not in table_a: table_a[i] = {} + table_a[i][j] = ele + for i, row in enumerate(b): + for j, ele in enumerate(row): + if ele: + if i not in table_b: table_b[i] = {} + table_b[i][j] = ele + c = [[0 for j in range(l)] for i in range(m)] + for i in table_a: + for k in table_a[i]: + if k not in table_b: continue + for j in table_b[k]: + c[i][j] += table_a[i][k] * table_b[k][j] + return c diff --git a/matrix/spiral_traversal.py b/algorithms/matrix/spiral_traversal.py similarity index 100% rename from matrix/spiral_traversal.py rename to algorithms/matrix/spiral_traversal.py diff --git a/algorithms/matrix/sudoku_validator.py b/algorithms/matrix/sudoku_validator.py new file mode 100644 index 000000000..ad2faebeb --- /dev/null +++ b/algorithms/matrix/sudoku_validator.py @@ -0,0 +1,113 @@ +""" +Write a function validSolution/ValidateSolution/valid_solution() that accepts a 2D array representing a Sudoku board, and returns true if it is a valid solution, or false otherwise. The cells of the sudoku board may also contain 0's, which will represent empty cells. Boards containing one or more zeroes are considered to be invalid solutions. +The board is always 9 cells by 9 cells, and every cell only contains integers from 0 to 9. + +(More info at: http://en.wikipedia.org/wiki/Sudoku) +""" + +# Using dict/hash-table +from collections import defaultdict +def valid_solution_hashtable(board): + for i in range(len(board)): + dict_row = defaultdict(int) + dict_col = defaultdict(int) + for j in range(len(board[0])): + value_row = board[i][j] + value_col = board[j][i] + if not value_row or value_col == 0: + return False + if value_row in dict_row: + return False + else: + dict_row[value_row] += 1 + + if value_col in dict_col: + return False + else: + dict_col[value_col] += 1 + + for i in range(3): + for j in range(3): + grid_add = 0 + for k in range(3): + for l in range(3): + grid_add += board[i*3+k][j*3+l] + if grid_add != 45: + return False + return True + + +# Without hash-table/dict +def valid_solution(board): + correct = [1, 2, 3, 4, 5, 6, 7, 8, 9] + # check rows + for row in board: + if sorted(row) != correct: + return False + + # check columns + for column in zip(*board): + if sorted(column) != correct: + return False + + # check regions + for i in range(3): + for j in range(3): + region = [] + for line in board[i*3:(i+1)*3]: + region += line[j*3:(j+1)*3] + + if sorted(region) != correct: + return False + + # if everything correct + return True + + +# Using set +def valid_solution_set (board): + valid = set(range(1, 10)) + + for row in board: + if set(row) != valid: + return False + + for col in [[row[i] for row in board] for i in range(9)]: + if set(col) != valid: + return False + + for x in range(3): + for y in range(3): + if set(sum([row[x*3:(x+1)*3] for row in board[y*3:(y+1)*3]], [])) != valid: + return False + + return True + +# test cases +# To avoid congestion I'll leave testing all the functions to the reader. Just change the name of the function in the below test cases. +import unittest +class TestSuite(unittest.TestCase): + def test_valid(self): + self.assertTrue(valid_solution([[5, 3, 4, 6, 7, 8, 9, 1, 2], + [6, 7, 2, 1, 9, 5, 3, 4, 8], + [1, 9, 8, 3, 4, 2, 5, 6, 7], + [8, 5, 9, 7, 6, 1, 4, 2, 3], + [4, 2, 6, 8, 5, 3, 7, 9, 1], + [7, 1, 3, 9, 2, 4, 8, 5, 6], + [9, 6, 1, 5, 3, 7, 2, 8, 4], + [2, 8, 7, 4, 1, 9, 6, 3, 5], + [3, 4, 5, 2, 8, 6, 1, 7, 9]])) + + def test_invalid(self): + self.assertFalse(valid_solution([[5, 3, 4, 6, 7, 8, 9, 1, 2], + [6, 7, 2, 1, 9, 0, 3, 4, 9], + [1, 0, 0, 3, 4, 2, 5, 6, 0], + [8, 5, 9, 7, 6, 1, 0, 2, 0], + [4, 2, 6, 8, 5, 3, 7, 9, 1], + [7, 1, 3, 9, 2, 4, 8, 5, 6], + [9, 0, 1, 5, 3, 7, 2, 1, 4], + [2, 8, 7, 4, 1, 9, 6, 3, 5], + [3, 0, 0, 4, 8, 1, 1, 7, 9]])) + +if __name__ == "__main__": + unittest.main() diff --git a/algorithms/ml/nearest_neighbor.py b/algorithms/ml/nearest_neighbor.py new file mode 100644 index 000000000..d0fabab15 --- /dev/null +++ b/algorithms/ml/nearest_neighbor.py @@ -0,0 +1,41 @@ +import math + +def distance(x,y): + """[summary] + HELPER-FUNCTION + calculates the (eulidean) distance between vector x and y. + + Arguments: + x {[tuple]} -- [vector] + y {[tuple]} -- [vector] + """ + assert len(x) == len(y), "The vector must have same length" + result = () + sum = 0 + for i in range(len(x)): + result += (x[i] -y[i],) + for component in result: + sum += component**2 + return math.sqrt(sum) + + +def nearest_neighbor(x, tSet): + """[summary] + Implements the nearest neighbor algorithm + + Arguments: + x {[tupel]} -- [vector] + tSet {[dict]} -- [training set] + + Returns: + [type] -- [result of the AND-function] + """ + assert isinstance(x, tuple) and isinstance(tSet, dict) + current_key = () + min_d = float('inf') + for key in tSet: + d = distance(x, key) + if d < min_d: + min_d = d + current_key = key + return tSet[current_key] diff --git a/algorithms/queues/__init__.py b/algorithms/queues/__init__.py new file mode 100644 index 000000000..3c4f22ca0 --- /dev/null +++ b/algorithms/queues/__init__.py @@ -0,0 +1,4 @@ +from .queue import * +from .max_sliding_window import * +from .reconstruct_queue import * +from .priority_queue import * diff --git a/algorithms/queues/max_sliding_window.py b/algorithms/queues/max_sliding_window.py new file mode 100644 index 000000000..7d1a45f11 --- /dev/null +++ b/algorithms/queues/max_sliding_window.py @@ -0,0 +1,32 @@ +""" +Given an array and a number k +Find the max elements of each of its sub-arrays of length k. + +Keep indexes of good candidates in deque d. +The indexes in d are from the current window, they're increasing, +and their corresponding nums are decreasing. +Then the first deque element is the index of the largest window value. + +For each index i: + +1. Pop (from the end) indexes of smaller elements (they'll be useless). +2. Append the current index. +3. Pop (from the front) the index i - k, if it's still in the deque + (it falls out of the window). +4. If our window has reached size k, + append the current window maximum to the output. +""" + +import collections +def max_sliding_window(arr, k): + qi = collections.deque() # queue storing indexes of elements + result = [] + for i, n in enumerate(arr): + while qi and arr[qi[-1]] < n: + qi.pop() + qi.append(i) + if qi[0] == i - k: + qi.popleft() + if i >= k - 1: + result.append(arr[qi[0]]) + return result diff --git a/queue/moving_average.py b/algorithms/queues/moving_average.py similarity index 100% rename from queue/moving_average.py rename to algorithms/queues/moving_average.py diff --git a/algorithms/queues/priority_queue.py b/algorithms/queues/priority_queue.py new file mode 100644 index 000000000..76b08156e --- /dev/null +++ b/algorithms/queues/priority_queue.py @@ -0,0 +1,56 @@ +""" +Implementation of priority queue using linear array. +Insertion - O(n) +Extract min/max Node - O(1) +""" +import itertools + + +class PriorityQueueNode: + def __init__(self, data, priority): + self.data = data + self.priority = priority + + def __repr__(self): + return "{}: {}".format(self.data, self.priority) + + +class PriorityQueue: + def __init__(self, items=None, priorities=None): + """Create a priority queue with items (list or iterable). + If items is not passed, create empty priority queue.""" + self.priority_queue_list = [] + if items is None: + return + if priorities is None: + priorities = itertools.repeat(None) + for item, priority in zip(items, priorities): + self.push(item, priority=priority) + + def __repr__(self): + return "PriorityQueue({!r})".format(self.priority_queue_list) + + def size(self): + """Return size of the priority queue. + """ + return len(self.priority_queue_list) + + def push(self, item, priority=None): + """Push the item in the priority queue. + if priority is not given, priority is set to the value of item. + """ + priority = item if priority is None else priority + node = PriorityQueueNode(item, priority) + for index, current in enumerate(self.priority_queue_list): + if current.priority < node.priority: + self.priority_queue_list.insert(index, node) + return + # when traversed complete queue + self.priority_queue_list.append(node) + + def pop(self): + """Remove and return the item with the lowest priority. + """ + # remove and return the first node from the queue + return self.priority_queue_list.pop().data + diff --git a/algorithms/queues/queue.py b/algorithms/queues/queue.py new file mode 100644 index 000000000..feab56b62 --- /dev/null +++ b/algorithms/queues/queue.py @@ -0,0 +1,140 @@ +""" +Queue Abstract Data Type (ADT) +* Queue() creates a new queue that is empty. + It needs no parameters and returns an empty queue. +* enqueue(item) adds a new item to the rear of the queue. + It needs the item and returns nothing. +* dequeue() removes the front item from the queue. + It needs no parameters and returns the item. The queue is modified. +* isEmpty() tests to see whether the queue is empty. + It needs no parameters and returns a boolean value. +* size() returns the number of items in the queue. + It needs no parameters and returns an integer. +* peek() returns the front element of the queue. +""" +from abc import ABCMeta, abstractmethod +class AbstractQueue(metaclass=ABCMeta): + + def __init__(self): + self._size = 0 + + def __len__(self): + return self._size + + def is_empty(self): + return self._size == 0 + + @abstractmethod + def enqueue(self, value): + pass + + @abstractmethod + def dequeue(self): + pass + + @abstractmethod + def peek(self): + pass + + @abstractmethod + def __iter__(self): + pass + + +class ArrayQueue(AbstractQueue): + + def __init__(self, capacity=10): + """ + Initialize python List with capacity of 10 or user given input. + Python List type is a dynamic array, so we have to restrict its + dynamic nature to make it work like a static array. + """ + super().__init__() + self._array = [None] * capacity + self._front = 0 + self._rear = 0 + + def __iter__(self): + probe = self._front + while True: + if probe == self._rear: + return + yield self._array[probe] + probe += 1 + + def enqueue(self, value): + if self._rear == len(self._array): + self._expand() + self._array[self._rear] = value + self._rear += 1 + self._size += 1 + + def dequeue(self): + if self.is_empty(): + raise IndexError("Queue is empty") + value = self._array[self._front] + self._array[self._front] = None + self._front += 1 + self._size -= 1 + return value + + def peek(self): + """returns the front element of queue.""" + if self.is_empty(): + raise IndexError("Queue is empty") + return self._array[self._front] + + def _expand(self): + """expands size of the array. + Time Complexity: O(n) + """ + self._array += [None] * len(self._array) + + +class QueueNode: + def __init__(self, value): + self.value = value + self.next = None + +class LinkedListQueue(AbstractQueue): + + def __init__(self): + super().__init__() + self._front = None + self._rear = None + + def __iter__(self): + probe = self._front + while True: + if probe is None: + return + yield probe.value + probe = probe.next + + def enqueue(self, value): + node = QueueNode(value) + if self._front is None: + self._front = node + self._rear = node + else: + self._rear.next = node + self._rear = node + self._size += 1 + + def dequeue(self): + if self.is_empty(): + raise IndexError("Queue is empty") + value = self._front.value + if self._front is self._rear: + self._front = None + self._rear = None + else: + self._front = self._front.next + self._size -= 1 + return value + + def peek(self): + """returns the front element of queue.""" + if self.is_empty(): + raise IndexError("Queue is empty") + return self._front.value diff --git a/queue/reconstruct_queue.py b/algorithms/queues/reconstruct_queue.py similarity index 95% rename from queue/reconstruct_queue.py rename to algorithms/queues/reconstruct_queue.py index 56069bc35..b86a67cd0 100644 --- a/queue/reconstruct_queue.py +++ b/algorithms/queues/reconstruct_queue.py @@ -23,7 +23,5 @@ def reconstruct_queue(people): queue = [] people.sort(key=lambda x: (-x[0], x[1])) for h, k in people: - queue.insert(k, (h, k)) + queue.insert(k, [h, k]) return queue - - diff --git a/queue/zigzagiterator.py b/algorithms/queues/zigzagiterator.py similarity index 100% rename from queue/zigzagiterator.py rename to algorithms/queues/zigzagiterator.py diff --git a/algorithms/search/__init__.py b/algorithms/search/__init__.py new file mode 100644 index 000000000..ac5220b38 --- /dev/null +++ b/algorithms/search/__init__.py @@ -0,0 +1,11 @@ +from .binary_search import * +from .first_occurrence import * +from .last_occurrence import * +from .linear_search import * +from .search_insert import * +from .two_sum import * +from .search_range import * +from .find_min_rotate import * +from .search_rotate import * +from .jump_search import * +from .next_greatest_letter import * diff --git a/algorithms/search/binary_search.py b/algorithms/search/binary_search.py new file mode 100644 index 000000000..644f98f8e --- /dev/null +++ b/algorithms/search/binary_search.py @@ -0,0 +1,29 @@ +# +# Binary search works for a sorted array. +# Note: The code logic is written for an array sorted in +# increasing order. +# T(n): O(log n) +# +def binary_search(array, query): + lo, hi = 0, len(array) - 1 + while lo <= hi: + mid = (hi + lo) // 2 + val = array[mid] + if val == query: + return mid + elif val < query: + lo = mid + 1 + else: + hi = mid - 1 + return None + +def binary_search_recur(array, low, high, val): + if low > high: # error case + return -1 + mid = (low + high) // 2 + if val < array[mid]: + return binary_search_recur(array, low, mid - 1, val) + elif val > array[mid]: + return binary_search_recur(array, mid + 1, high, val) + else: + return mid diff --git a/algorithms/search/find_min_rotate.py b/algorithms/search/find_min_rotate.py new file mode 100644 index 000000000..1afc4eef2 --- /dev/null +++ b/algorithms/search/find_min_rotate.py @@ -0,0 +1,28 @@ +""" +Suppose an array sorted in ascending order is rotated at some pivot unknown +to you beforehand. (i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2). + +Find the minimum element. The complexity must be O(logN) + +You may assume no duplicate exists in the array. +""" +def find_min_rotate(array): + low = 0 + high = len(array) - 1 + while low < high: + mid = (low + high) // 2 + if array[mid] > array[high]: + low = mid + 1 + else: + high = mid + + return array[low] + +def find_min_rotate_recur(array, low, high): + mid = (low + high) // 2 + if mid == low: + return array[low] + elif array[mid] > array[high]: + return find_min_rotate_recur(array, mid + 1, high) + else: + return find_min_rotate_recur(array, low, mid) diff --git a/algorithms/search/first_occurrence.py b/algorithms/search/first_occurrence.py new file mode 100644 index 000000000..86dc89ced --- /dev/null +++ b/algorithms/search/first_occurrence.py @@ -0,0 +1,18 @@ +# +# Find first occurance of a number in a sorted array (increasing order) +# Approach- Binary Search +# T(n)- O(log n) +# +def first_occurrence(array, query): + lo, hi = 0, len(array) - 1 + while lo <= hi: + mid = (lo + hi) // 2 + #print("lo: ", lo, " hi: ", hi, " mid: ", mid) + if lo == hi: + break + if array[mid] < query: + lo = mid + 1 + else: + hi = mid + if array[lo] == query: + return lo diff --git a/algorithms/search/jump_search.py b/algorithms/search/jump_search.py new file mode 100644 index 000000000..66f726a50 --- /dev/null +++ b/algorithms/search/jump_search.py @@ -0,0 +1,40 @@ +import math + +def jump_search(arr,target): + """Jump Search + Worst-case Complexity: O(√n) (root(n)) + All items in list must be sorted like binary search + + Find block that contains target value and search it linearly in that block + It returns a first target value in array + + reference: https://en.wikipedia.org/wiki/Jump_search + + """ + n = len(arr) + block_size = int(math.sqrt(n)) + block_prev = 0 + block= block_size + + # return -1 means that array doesn't contain taget value + # find block that contains target value + + if arr[n - 1] < target: + return -1 + while block <= n and arr[block - 1] < target: + block_prev = block + block += block_size + + # find target value in block + + while arr[block_prev] < target : + block_prev += 1 + if block_prev == min(block, n) : + return -1 + + # if there is target value in array, return it + + if arr[block_prev] == target : + return block_prev + else : + return -1 diff --git a/algorithms/search/last_occurrence.py b/algorithms/search/last_occurrence.py new file mode 100644 index 000000000..345b42395 --- /dev/null +++ b/algorithms/search/last_occurrence.py @@ -0,0 +1,16 @@ +# +# Find last occurance of a number in a sorted array (increasing order) +# Approach- Binary Search +# T(n)- O(log n) +# +def last_occurrence(array, query): + lo, hi = 0, len(array) - 1 + while lo <= hi: + mid = (hi + lo) // 2 + if (array[mid] == query and mid == len(array)-1) or \ + (array[mid] == query and array[mid+1] > query): + return mid + elif (array[mid] <= query): + lo = mid + 1 + else: + hi = mid - 1 diff --git a/algorithms/search/linear_search.py b/algorithms/search/linear_search.py new file mode 100644 index 000000000..da4d9b93c --- /dev/null +++ b/algorithms/search/linear_search.py @@ -0,0 +1,13 @@ +# +# Linear search works in any array. +# +# T(n): O(n) +# + +def linear_search(array, query): + length = len(array) + for i in range(length): + if array[i] == query: + return i + + return -1 diff --git a/algorithms/search/next_greatest_letter.py b/algorithms/search/next_greatest_letter.py new file mode 100644 index 000000000..5abe001cd --- /dev/null +++ b/algorithms/search/next_greatest_letter.py @@ -0,0 +1,60 @@ +''' +Given a list of sorted characters letters containing only lowercase letters, +and given a target letter target, find the smallest element in the list that +is larger than the given target. + +Letters also wrap around. For example, if the target is target = 'z' and +letters = ['a', 'b'], the answer is 'a'. + +Input: +letters = ["c", "f", "j"] +target = "a" +Output: "c" + +Input: +letters = ["c", "f", "j"] +target = "c" +Output: "f" + +Input: +letters = ["c", "f", "j"] +target = "d" +Output: "f" + +Reference: https://leetcode.com/problems/find-smallest-letter-greater-than-target/description/ +''' + +import bisect + +""" +Using bisect libarary +""" +def next_greatest_letter(letters, target): + index = bisect.bisect(letters, target) + return letters[index % len(letters)] + +""" +Using binary search: complexity O(logN) +""" +def next_greatest_letter_v1(letters, target): + if letters[0] > target: + return letters[0] + if letters[len(letters) - 1] <= target: + return letters[0] + left, right = 0, len(letters) - 1 + while left <= right: + mid = left + (right - left) // 2 + if letters[mid] > target: + right = mid - 1 + else: + left = mid + 1 + return letters[left] + +""" +Brute force: complexity O(N) +""" +def next_greatest_letter_v2(letters, target): + for index in letters: + if index > target: + return index + return letters[0] diff --git a/algorithms/search/search_insert.py b/algorithms/search/search_insert.py new file mode 100644 index 000000000..b10eb7d5f --- /dev/null +++ b/algorithms/search/search_insert.py @@ -0,0 +1,20 @@ +""" +Given a sorted array and a target value, return the index if the target is +found. If not, return the index where it would be if it were inserted in order. + +For example: +[1,3,5,6], 5 -> 2 +[1,3,5,6], 2 -> 1 +[1,3,5,6], 7 -> 4 +[1,3,5,6], 0 -> 0 +""" +def search_insert(array, val): + low = 0 + high = len(array) - 1 + while low <= high: + mid = low + (high - low) // 2 + if val > array[mid]: + low = mid + 1 + else: + high = mid - 1 + return low diff --git a/algorithms/search/search_range.py b/algorithms/search/search_range.py new file mode 100644 index 000000000..116d8f541 --- /dev/null +++ b/algorithms/search/search_range.py @@ -0,0 +1,33 @@ +""" +Given an array of integers nums sorted in ascending order, find the starting +and ending position of a given target value. If the target is not found in the +array, return [-1, -1]. + +For example: +Input: nums = [5,7,7,8,8,8,10], target = 8 +Output: [3,5] +Input: nums = [5,7,7,8,8,8,10], target = 11 +Output: [-1,-1] +""" +def search_range(nums, target): + """ + :type nums: List[int] + :type target: int + :rtype: List[int] + """ + low = 0 + high = len(nums) - 1 + while low <= high: + mid = low + (high - low) // 2 + if target < nums[mid]: + high = mid - 1 + elif target > nums[mid]: + low = mid + 1 + else: + break + + for j in range(len(nums) - 1, -1, -1): + if nums[j] == target: + return [mid, j] + + return [-1, -1] diff --git a/algorithms/search/search_rotate.py b/algorithms/search/search_rotate.py new file mode 100644 index 000000000..ade027082 --- /dev/null +++ b/algorithms/search/search_rotate.py @@ -0,0 +1,78 @@ +""" +Search in Rotated Sorted Array +Suppose an array sorted in ascending order is rotated at some pivot unknown +to you beforehand. (i.e., [0,1,2,4,5,6,7] might become [4,5,6,7,0,1,2]). + +You are given a target value to search. If found in the array return its index, +otherwise return -1. + +Your algorithm's runtime complexity must be in the order of O(log n). +--------------------------------------------------------------------------------- +Explanation algorithm: + +In classic binary search, we compare val with the midpoint to figure out if +val belongs on the low or the high side. The complication here is that the +array is rotated and may have an inflection point. Consider, for example: + +Array1: [10, 15, 20, 0, 5] +Array2: [50, 5, 20, 30, 40] + +Note that both arrays have a midpoint of 20, but 5 appears on the left side of +one and on the right side of the other. Therefore, comparing val with the +midpoint is insufficient. + +However, if we look a bit deeper, we can see that one half of the array must be +ordered normally(increasing order). We can therefore look at the normally ordered +half to determine whether we should search the low or hight side. + +For example, if we are searching for 5 in Array1, we can look at the left element (10) +and middle element (20). Since 10 < 20, the left half must be ordered normally. And, since 5 +is not between those, we know that we must search the right half + +In array2, we can see that since 50 > 20, the right half must be ordered normally. We turn to +the middle 20, and right 40 element to check if 5 would fall between them. The value 5 would not +Therefore, we search the left half. + +There are 2 possible solution: iterative and recursion. +Recursion helps you understand better the above algorithm explanation +""" +def search_rotate(array, val): + if not array: + return -1 + + low, high = 0, len(array) - 1 + while low <= high: + mid = (low + high) // 2 + if val == array[mid]: + return mid + + if array[low] <= array[mid]: + if array[low] <= val <= array[mid]: + high = mid - 1 + else: + low = mid + 1 + else: + if array[mid] <= val <= array[high]: + low = mid + 1 + else: + high = mid - 1 + + return -1 + +# Recursion technique +def search_rotate_recur(array, low, high, val): + if low >= high: + return -1 + mid = (low + high) // 2 + if val == array[mid]: # found element + return mid + if array[low] <= array[mid]: + if array[low] <= val <= array[mid]: + return search_rotate_recur(array, low, mid - 1, val) # Search left + else: + return search_rotate_recur(array, mid + 1, high, val) # Search right + else: + if array[mid] <= val <= array[high]: + return search_rotate_recur(array, mid + 1, high, val) # Search right + else: + return search_rotate_recur(array, low, mid - 1, val) # Search left diff --git a/algorithms/search/two_sum.py b/algorithms/search/two_sum.py new file mode 100644 index 000000000..4251e5378 --- /dev/null +++ b/algorithms/search/two_sum.py @@ -0,0 +1,51 @@ +""" +Given an array of integers that is already sorted in ascending order, find two +numbers such that they add up to a specific target number. The function two_sum +should return indices of the two numbers such that they add up to the target, +where index1 must be less than index2. Please note that your returned answers +(both index1 and index2) are not zero-based. +You may assume that each input would have exactly one solution and you +may not use the same element twice. + +Input: numbers = [2, 7, 11, 15], target=9 +Output: index1 = 1, index2 = 2 + +Solution: +two_sum: using binary search +two_sum1: using dictionary as a hash table +two_sum2: using two pointers +""" +# Using binary search technique +def two_sum(numbers, target): + for i in range(len(numbers)): + second_val = target - numbers[i] + low, high = i+1, len(numbers)-1 + while low <= high: + mid = low + (high - low) // 2 + if second_val == numbers[mid]: + return [i + 1, mid + 1] + elif second_val > numbers[mid]: + low = mid + 1 + else: + high = mid - 1 + +# Using dictionary as a hash table +def two_sum1(numbers, target): + dic = {} + for i, num in enumerate(numbers): + if target - num in dic: + return [dic[target - num] + 1, i + 1] + dic[num] = i + +# Using two pointers +def two_sum2(numbers, target): + p1 = 0 # pointer 1 holds from left of array numbers + p2 = len(numbers) - 1 # pointer 2 holds from right of array numbers + while p1 < p2: + s = numbers[p1] + numbers[p2] + if s == target: + return [p1 + 1, p2 + 1] + elif s > target: + p2 = p2 - 1 + else: + p1 = p1 + 1 diff --git a/algorithms/set/randomized_set.py b/algorithms/set/randomized_set.py new file mode 100644 index 000000000..53f2b5af4 --- /dev/null +++ b/algorithms/set/randomized_set.py @@ -0,0 +1,70 @@ +#! /usr/bin/env python3 + +""" +Design a data structure that supports all following operations +in average O(1) time. + +insert(val): Inserts an item val to the set if not already present. +remove(val): Removes an item val from the set if present. +random_element: Returns a random element from current set of elements. + Each element must have the same probability of being returned. +""" + +import random + + +class RandomizedSet(): + """ + idea: shoot + """ + + def __init__(self): + self.elements = [] + self.index_map = {} # element -> index + + def insert(self, new_one): + if new_one in self.index_map: + return + self.index_map[new_one] = len(self.elements) + self.elements.append(new_one) + + def remove(self, old_one): + if not old_one in self.index_map: + return + index = self.index_map[old_one] + last = self.elements.pop() + self.index_map.pop(old_one) + if index == len(self.elements): + return + self.elements[index] = last + self.index_map[last] = index + + def random_element(self): + return random.choice(self.elements) + + +def __test(): + rset = RandomizedSet() + ground_truth = set() + n = 64 + + for i in range(n): + rset.insert(i) + ground_truth.add(i) + + # Remove a half + for i in random.sample(range(n), n // 2): + rset.remove(i) + ground_truth.remove(i) + + print(len(ground_truth), len(rset.elements), len(rset.index_map)) + for i in ground_truth: + assert(i == rset.elements[rset.index_map[i]]) + + for i in range(n): + print(rset.random_element(), end=' ') + print() + + +if __name__ == "__main__": + __test() diff --git a/algorithms/set/set_covering.py b/algorithms/set/set_covering.py new file mode 100644 index 000000000..ba51f62d0 --- /dev/null +++ b/algorithms/set/set_covering.py @@ -0,0 +1,115 @@ +from itertools import chain, combinations + +""" +Universe *U* of n elements +Collection of subsets of U: + S = S1,S2...,Sm + Where every substet Si has an associated cost. + +Find a minimum cost subcollection of S that covers all elements of U + +Example: + U = {1,2,3,4,5} + S = {S1,S2,S3} + + S1 = {4,1,3}, Cost(S1) = 5 + S2 = {2,5}, Cost(S2) = 10 + S3 = {1,4,3,2}, Cost(S3) = 3 + + Output: + Set cover = {S2, S3} + Min Cost = 13 +""" + + +def powerset(iterable): + """Calculate the powerset of any iterable. + + For a range of integers up to the length of the given list, + make all possible combinations and chain them together as one object. + From https://docs.python.org/3/library/itertools.html#itertools-recipes + """ + "list(powerset([1,2,3])) --> [(), (1,), (2,), (3,), (1,2), (1,3), (2,3), (1,2,3)]" + s = list(iterable) + return chain.from_iterable(combinations(s, r) for r in range(len(s) + 1)) + + +def optimal_set_cover(universe, subsets, costs): + """ Optimal algorithm - DONT USE ON BIG INPUTS - O(2^n) complexity! + Finds the minimum cost subcollection os S that covers all elements of U + + Args: + universe (list): Universe of elements + subsets (dict): Subsets of U {S1:elements,S2:elements} + costs (dict): Costs of each subset in S - {S1:cost, S2:cost...} + """ + pset = powerset(subsets.keys()) + best_set = None + best_cost = float("inf") + for subset in pset: + covered = set() + cost = 0 + for s in subset: + covered.update(subsets[s]) + cost += costs[s] + if len(covered) == len(universe) and cost < best_cost: + best_set = subset + best_cost = cost + return best_set + + +def greedy_set_cover(universe, subsets, costs): + """Approximate greedy algorithm for set-covering. Can be used on large + inputs - though not an optimal solution. + + Args: + universe (list): Universe of elements + subsets (dict): Subsets of U {S1:elements,S2:elements} + costs (dict): Costs of each subset in S - {S1:cost, S2:cost...} + """ + elements = set(e for s in subsets.keys() for e in subsets[s]) + # elements don't cover universe -> invalid input for set cover + if elements != universe: + return None + + # track elements of universe covered + covered = set() + cover_sets = [] + + while covered != universe: + min_cost_elem_ratio = float("inf") + min_set = None + # find set with minimum cost:elements_added ratio + for s, elements in subsets.items(): + new_elements = len(elements - covered) + # set may have same elements as already covered -> new_elements = 0 + # check to avoid division by 0 error + if new_elements != 0: + cost_elem_ratio = costs[s] / new_elements + if cost_elem_ratio < min_cost_elem_ratio: + min_cost_elem_ratio = cost_elem_ratio + min_set = s + cover_sets.append(min_set) + # union + covered |= subsets[min_set] + return cover_sets + + +if __name__ == '__main__': + universe = {1, 2, 3, 4, 5} + subsets = {'S1': {4, 1, 3}, 'S2': {2, 5}, 'S3': {1, 4, 3, 2}} + costs = {'S1': 5, 'S2': 10, 'S3': 3} + + optimal_cover = optimal_set_cover(universe, subsets, costs) + optimal_cost = sum(costs[s] for s in optimal_cover) + + greedy_cover = greedy_set_cover(universe, subsets, costs) + greedy_cost = sum(costs[s] for s in greedy_cover) + + print('Optimal Set Cover:') + print(optimal_cover) + print('Cost = %s' % optimal_cost) + + print('Greedy Set Cover:') + print(greedy_cover) + print('Cost = %s' % greedy_cost) diff --git a/algorithms/sort/__init__.py b/algorithms/sort/__init__.py new file mode 100644 index 000000000..fceb3bd22 --- /dev/null +++ b/algorithms/sort/__init__.py @@ -0,0 +1,18 @@ +from .bitonic_sort import * +from .bogo_sort import * +from .bubble_sort import * +from .comb_sort import * +from .counting_sort import * +from .cycle_sort import * +from .heap_sort import * +from .insertion_sort import * +from .merge_sort import * +from .pancake_sort import * +from .quick_sort import * +from .selection_sort import * +from .top_sort import * +from .bucket_sort import * +from .shell_sort import * +from .radix_sort import * +from .gnome_sort import * +from .cocktail_shaker_sort import * diff --git a/algorithms/sort/bitonic_sort.py b/algorithms/sort/bitonic_sort.py new file mode 100644 index 000000000..f8cad90e1 --- /dev/null +++ b/algorithms/sort/bitonic_sort.py @@ -0,0 +1,43 @@ +def bitonic_sort(arr, reverse=False): + """ + bitonic sort is sorting algorithm to use multiple process, but this code not containing parallel process + It can sort only array that sizes power of 2 + It can sort array in both increasing order and decreasing order by giving argument true(increasing) and false(decreasing) + + Worst-case in parallel: O(log(n)^2) + Worst-case in non-parallel: O(nlog(n)^2) + + reference: https://en.wikipedia.org/wiki/Bitonic_sorter + """ + def compare(arr, reverse): + n = len(arr)//2 + for i in range(n): + if reverse != (arr[i] > arr[i+n]): + arr[i], arr[i+n] = arr[i+n], arr[i] + return arr + + def bitonic_merge(arr, reverse): + n = len(arr) + + if n <= 1: + return arr + + arr = compare(arr, reverse) + left = bitonic_merge(arr[:n // 2], reverse) + right = bitonic_merge(arr[n // 2:], reverse) + return left + right + + #end of function(compare and bitionic_merge) definition + n = len(arr) + if n <= 1: + return arr + # checks if n is power of two + if not (n and (not(n & (n - 1))) ): + raise ValueError("the size of input should be power of two") + + left = bitonic_sort(arr[:n // 2], True) + right = bitonic_sort(arr[n // 2:], False) + + arr = bitonic_merge(left + right, reverse) + + return arr diff --git a/algorithms/sort/bogo_sort.py b/algorithms/sort/bogo_sort.py new file mode 100644 index 000000000..60e1be8f0 --- /dev/null +++ b/algorithms/sort/bogo_sort.py @@ -0,0 +1,32 @@ +import random + +def bogo_sort(arr, simulation=False): + """Bogo Sort + Best Case Complexity: O(n) + Worst Case Complexity: O(∞) + Average Case Complexity: O(n(n-1)!) + """ + + iteration = 0 + if simulation: + print("iteration",iteration,":",*arr) + + def is_sorted(arr): + #check the array is inorder + i = 0 + arr_len = len(arr) + while i+1 < arr_len: + if arr[i] > arr[i+1]: + return False + i += 1 + + + return True + while not is_sorted(arr): + random.shuffle(arr) + + if simulation: + iteration = iteration + 1 + print("iteration",iteration,":",*arr) + + return arr diff --git a/algorithms/sort/bubble_sort.py b/algorithms/sort/bubble_sort.py new file mode 100644 index 000000000..76c9816fa --- /dev/null +++ b/algorithms/sort/bubble_sort.py @@ -0,0 +1,34 @@ +""" + +https://en.wikipedia.org/wiki/Bubble_sort + +Worst-case performance: O(N^2) + +If you call bubble_sort(arr,True), you can see the process of the sort +Default is simulation = False + +""" + + +def bubble_sort(arr, simulation=False): + def swap(i, j): + arr[i], arr[j] = arr[j], arr[i] + + n = len(arr) + swapped = True + + iteration = 0 + if simulation: + print("iteration",iteration,":",*arr) + + while swapped: + swapped = False + for i in range(1, n): + if arr[i - 1] > arr[i]: + swap(i - 1, i) + swapped = True + if simulation: + iteration = iteration + 1 + print("iteration",iteration,":",*arr) + + return arr diff --git a/algorithms/sort/bucket_sort.py b/algorithms/sort/bucket_sort.py new file mode 100644 index 000000000..d89232ccf --- /dev/null +++ b/algorithms/sort/bucket_sort.py @@ -0,0 +1,28 @@ +def bucket_sort(arr): + ''' Bucket Sort + Complexity: O(n^2) + The complexity is dominated by nextSort + ''' + # The number of buckets and make buckets + num_buckets = len(arr) + buckets = [[] for bucket in range(num_buckets)] + # Assign values into bucket_sort + for value in arr: + index = value * num_buckets // (max(arr) + 1) + buckets[index].append(value) + # Sort + sorted_list = [] + for i in range(num_buckets): + sorted_list.extend(next_sort(buckets[i])) + return sorted_list + +def next_sort(arr): + # We will use insertion sort here. + for i in range(1, len(arr)): + j = i - 1 + key = arr[i] + while arr[j] > key and j >= 0: + arr[j+1] = arr[j] + j = j - 1 + arr[j + 1] = key + return arr diff --git a/algorithms/sort/cocktail_shaker_sort.py b/algorithms/sort/cocktail_shaker_sort.py new file mode 100644 index 000000000..973b824b6 --- /dev/null +++ b/algorithms/sort/cocktail_shaker_sort.py @@ -0,0 +1,30 @@ +def cocktail_shaker_sort(arr): + """ + Cocktail_shaker_sort + Sorting a given array + mutation of bubble sort + + reference: https://en.wikipedia.org/wiki/Cocktail_shaker_sort + + Worst-case performance: O(N^2) + """ + + def swap(i, j): + arr[i], arr[j] = arr[j], arr[i] + + n = len(arr) + swapped = True + while swapped: + swapped = False + for i in range(1, n): + if arr[i - 1] > arr[i]: + swap(i - 1, i) + swapped = True + if swapped == False: + return arr + swapped = False + for i in range(n-1,0,-1): + if arr[i - 1] > arr[i]: + swap(i - 1, i) + swapped = True + return arr diff --git a/sort/comb_sort.py b/algorithms/sort/comb_sort.py similarity index 70% rename from sort/comb_sort.py rename to algorithms/sort/comb_sort.py index b9dc1f67a..efa58fc5e 100644 --- a/sort/comb_sort.py +++ b/algorithms/sort/comb_sort.py @@ -6,11 +6,8 @@ """ -from math import floor - def comb_sort(arr): - def swap(i, j): arr[i], arr[j] = arr[j], arr[i] @@ -19,7 +16,7 @@ def swap(i, j): shrink = 1.3 sorted = False while not sorted: - gap = int(floor(gap/shrink)) + gap = int(gap / shrink) if gap > 1: sorted = False else: @@ -32,10 +29,4 @@ def swap(i, j): swap(i, i + gap) sorted = False i = i + 1 - - -array = [1, 5, 65, 23, 57, 1232, -1, -5, -2, 242, 100, - 4, 423, 2, 564, 9, 0, 10, 43, 64, 32, 1, 999] -print(array) -comb_sort(array) -print(array) + return arr diff --git a/algorithms/sort/counting_sort.py b/algorithms/sort/counting_sort.py new file mode 100644 index 000000000..46688e218 --- /dev/null +++ b/algorithms/sort/counting_sort.py @@ -0,0 +1,36 @@ +def counting_sort(arr): + """ + Counting_sort + Sorting a array which has no element greater than k + Creating a new temp_arr,where temp_arr[i] contain the number of + element less than or equal to i in the arr + Then placing the number i into a correct position in the result_arr + return the result_arr + Complexity: 0(n) + """ + + m = min(arr) + # in case there are negative elements, change the array to all positive element + different = 0 + if m < 0: + # save the change, so that we can convert the array back to all positive number + different = -m + for i in range(len(arr)): + arr[i] += -m + k = max(arr) + temp_arr = [0] * (k + 1) + for i in range(0, len(arr)): + temp_arr[arr[i]] = temp_arr[arr[i]] + 1 + # temp_array[i] contain the times the number i appear in arr + + for i in range(1, k + 1): + temp_arr[i] = temp_arr[i] + temp_arr[i - 1] + # temp_array[i] contain the number of element less than or equal i in arr + + result_arr = arr.copy() + # creating a result_arr an put the element in a correct positon + for i in range(len(arr) - 1, -1, -1): + result_arr[temp_arr[arr[i]] - 1] = arr[i] - different + temp_arr[arr[i]] = temp_arr[arr[i]] - 1 + + return result_arr diff --git a/algorithms/sort/cycle_sort.py b/algorithms/sort/cycle_sort.py new file mode 100644 index 000000000..6e512c924 --- /dev/null +++ b/algorithms/sort/cycle_sort.py @@ -0,0 +1,46 @@ +def cycle_sort(arr): + """ + cycle_sort + This is based on the idea that the permutations to be sorted + can be decomposed into cycles, + and the results can be individually sorted by cycling. + + reference: https://en.wikipedia.org/wiki/Cycle_sort + + Average time complexity : O(N^2) + Worst case time complexity : O(N^2) + """ + len_arr = len(arr) + # Finding cycle to rotate. + for cur in range(len_arr - 1): + item = arr[cur] + + # Finding an indx to put items in. + index = cur + for i in range(cur + 1, len_arr): + if arr[i] < item: + index += 1 + + # Case of there is not a cycle + if index == cur: + continue + + # Putting the item immediately right after the duplicate item or on the right. + while item == arr[index]: + index += 1 + arr[index], item = item, arr[index] + + # Rotating the remaining cycle. + while index != cur: + + # Finding where to put the item. + index = cur + for i in range(cur + 1, len_arr): + if arr[i] < item: + index += 1 + + # After item is duplicated, put it in place or put it there. + while item == arr[index]: + index += 1 + arr[index], item = item, arr[index] + return arr diff --git a/algorithms/sort/gnome_sort.py b/algorithms/sort/gnome_sort.py new file mode 100644 index 000000000..aaf38a278 --- /dev/null +++ b/algorithms/sort/gnome_sort.py @@ -0,0 +1,19 @@ +""" + +Gnome Sort +Best case performance is O(n) +Worst case performance is O(n^2) + +""" + + +def gnome_sort(arr): + n = len(arr) + index = 0 + while index < n: + if index == 0 or arr[index] >= arr[index-1]: + index = index + 1 + else: + arr[index], arr[index-1] = arr[index-1], arr[index] + index = index - 1 + return arr diff --git a/algorithms/sort/heap_sort.py b/algorithms/sort/heap_sort.py new file mode 100644 index 000000000..ec4c7fa2b --- /dev/null +++ b/algorithms/sort/heap_sort.py @@ -0,0 +1,92 @@ +def max_heap_sort(arr, simulation=False): + """ Heap Sort that uses a max heap to sort an array in ascending order + Complexity: O(n log(n)) + """ + iteration = 0 + if simulation: + print("iteration",iteration,":",*arr) + + for i in range(len(arr) - 1, 0, -1): + iteration = max_heapify(arr, i, simulation, iteration) + + if simulation: + iteration = iteration + 1 + print("iteration",iteration,":",*arr) + return arr + + +def max_heapify(arr, end, simulation, iteration): + """ Max heapify helper for max_heap_sort + """ + last_parent = (end - 1) // 2 + + # Iterate from last parent to first + for parent in range(last_parent, -1, -1): + current_parent = parent + + # Iterate from current_parent to last_parent + while current_parent <= last_parent: + # Find greatest child of current_parent + child = 2 * current_parent + 1 + if child + 1 <= end and arr[child] < arr[child + 1]: + child = child + 1 + + # Swap if child is greater than parent + if arr[child] > arr[current_parent]: + arr[current_parent], arr[child] = arr[child], arr[current_parent] + current_parent = child + if simulation: + iteration = iteration + 1 + print("iteration",iteration,":",*arr) + # If no swap occured, no need to keep iterating + else: + break + arr[0], arr[end] = arr[end], arr[0] + return iteration + +def min_heap_sort(arr, simulation=False): + """ Heap Sort that uses a min heap to sort an array in ascending order + Complexity: O(n log(n)) + """ + iteration = 0 + if simulation: + print("iteration",iteration,":",*arr) + + for i in range(0, len(arr) - 1): + iteration = min_heapify(arr, i, simulation, iteration) + + return arr + + +def min_heapify(arr, start, simulation, iteration): + """ Min heapify helper for min_heap_sort + """ + # Offset last_parent by the start (last_parent calculated as if start index was 0) + # All array accesses need to be offset by start + end = len(arr) - 1 + last_parent = (end - start - 1) // 2 + + # Iterate from last parent to first + for parent in range(last_parent, -1, -1): + current_parent = parent + + # Iterate from current_parent to last_parent + while current_parent <= last_parent: + # Find lesser child of current_parent + child = 2 * current_parent + 1 + if child + 1 <= end - start and arr[child + start] > arr[ + child + 1 + start]: + child = child + 1 + + # Swap if child is less than parent + if arr[child + start] < arr[current_parent + start]: + arr[current_parent + start], arr[child + start] = \ + arr[child + start], arr[current_parent + start] + current_parent = child + if simulation: + iteration = iteration + 1 + print("iteration",iteration,":",*arr) + # If no swap occured, no need to keep iterating + else: + break + return iteration diff --git a/algorithms/sort/insertion_sort.py b/algorithms/sort/insertion_sort.py new file mode 100644 index 000000000..06c86228d --- /dev/null +++ b/algorithms/sort/insertion_sort.py @@ -0,0 +1,25 @@ +def insertion_sort(arr, simulation=False): + """ Insertion Sort + Complexity: O(n^2) + """ + + iteration = 0 + if simulation: + print("iteration",iteration,":",*arr) + + for i in range(len(arr)): + cursor = arr[i] + pos = i + + while pos > 0 and arr[pos - 1] > cursor: + # Swap the number down the list + arr[pos] = arr[pos - 1] + pos = pos - 1 + # Break and do the final swap + arr[pos] = cursor + + if simulation: + iteration = iteration + 1 + print("iteration",iteration,":",*arr) + + return arr diff --git a/sort/meeting_rooms.py b/algorithms/sort/meeting_rooms.py similarity index 89% rename from sort/meeting_rooms.py rename to algorithms/sort/meeting_rooms.py index 72ebb7d51..2cb60c69a 100644 --- a/sort/meeting_rooms.py +++ b/algorithms/sort/meeting_rooms.py @@ -16,6 +16,6 @@ def can_attend_meetings(intervals): """ intervals = sorted(intervals, key=lambda x: x.start) for i in range(1, len(intervals)): - if intervals[i].start < intervals[i-1].end: + if intervals[i].start < intervals[i - 1].end: return False return True diff --git a/algorithms/sort/merge_sort.py b/algorithms/sort/merge_sort.py new file mode 100644 index 000000000..351a4c5e8 --- /dev/null +++ b/algorithms/sort/merge_sort.py @@ -0,0 +1,38 @@ +def merge_sort(arr): + """ Merge Sort + Complexity: O(n log(n)) + """ + # Our recursive base case + if len(arr) <= 1: + return arr + mid = len(arr) // 2 + # Perform merge_sort recursively on both halves + left, right = merge_sort(arr[:mid]), merge_sort(arr[mid:]) + + # Merge each side together + return merge(left, right, arr.copy()) + + +def merge(left, right, merged): + """ Merge helper + Complexity: O(n) + """ + + left_cursor, right_cursor = 0, 0 + while left_cursor < len(left) and right_cursor < len(right): + # Sort each one and place into the result + if left[left_cursor] <= right[right_cursor]: + merged[left_cursor+right_cursor]=left[left_cursor] + left_cursor += 1 + else: + merged[left_cursor + right_cursor] = right[right_cursor] + right_cursor += 1 + # Add the left overs if there's any left to the result + for left_cursor in range(left_cursor, len(left)): + merged[left_cursor + right_cursor] = left[left_cursor] + # Add the left overs if there's any left to the result + for right_cursor in range(right_cursor, len(right)): + merged[left_cursor + right_cursor] = right[right_cursor] + + # Return result + return merged diff --git a/algorithms/sort/pancake_sort.py b/algorithms/sort/pancake_sort.py new file mode 100644 index 000000000..19725c043 --- /dev/null +++ b/algorithms/sort/pancake_sort.py @@ -0,0 +1,25 @@ +def pancake_sort(arr): + """ + Pancake_sort + Sorting a given array + mutation of selection sort + + reference: https://www.geeksforgeeks.org/pancake-sorting/ + + Overall time complexity : O(N^2) + """ + + len_arr = len(arr) + if len_arr <= 1: + return arr + for cur in range(len(arr), 1, -1): + #Finding index of maximum number in arr + index_max = arr.index(max(arr[0:cur])) + if index_max+1 != cur: + #Needs moving + if index_max != 0: + #reverse from 0 to index_max + arr[:index_max+1] = reversed(arr[:index_max+1]) + # Reverse list + arr[:cur] = reversed(arr[:cur]) + return arr diff --git a/algorithms/sort/quick_sort.py b/algorithms/sort/quick_sort.py new file mode 100644 index 000000000..10794e7a5 --- /dev/null +++ b/algorithms/sort/quick_sort.py @@ -0,0 +1,32 @@ +def quick_sort(arr, simulation=False): + """ Quick sort + Complexity: best O(n log(n)) avg O(n log(n)), worst O(N^2) + """ + + iteration = 0 + if simulation: + print("iteration",iteration,":",*arr) + arr, _ = quick_sort_recur(arr, 0, len(arr) - 1, iteration, simulation) + return arr + +def quick_sort_recur(arr, first, last, iteration, simulation): + if first < last: + pos = partition(arr, first, last) + # Start our two recursive calls + if simulation: + iteration = iteration + 1 + print("iteration",iteration,":",*arr) + + _, iteration = quick_sort_recur(arr, first, pos - 1, iteration, simulation) + _, iteration = quick_sort_recur(arr, pos + 1, last, iteration, simulation) + + return arr, iteration + +def partition(arr, first, last): + wall = first + for pos in range(first, last): + if arr[pos] < arr[last]: # last is the pivot + arr[pos], arr[wall] = arr[wall], arr[pos] + wall += 1 + arr[wall], arr[last] = arr[last], arr[wall] + return wall diff --git a/algorithms/sort/radix_sort.py b/algorithms/sort/radix_sort.py new file mode 100644 index 000000000..dd4113d05 --- /dev/null +++ b/algorithms/sort/radix_sort.py @@ -0,0 +1,34 @@ +""" +radix sort +complexity: O(nk) . n is the size of input list and k is the digit length of the number +""" +def radix_sort(arr, simulation=False): + is_done = False + position = 1 + + iteration = 0 + if simulation: + print("iteration",iteration,":",*arr) + + while not is_done: + queue_list = [list() for _ in range(10)] + is_done = True + + for num in arr: + digit_number = num // position % 10 + queue_list[digit_number].append(num) + if is_done and digit_number > 0: + is_done = False + + index = 0 + for numbers in queue_list: + for num in numbers: + arr[index] = num + index += 1 + + if simulation: + iteration = iteration + 1 + print("iteration",iteration,":",*arr) + + position *= 10 + return arr diff --git a/algorithms/sort/selection_sort.py b/algorithms/sort/selection_sort.py new file mode 100644 index 000000000..f6d535225 --- /dev/null +++ b/algorithms/sort/selection_sort.py @@ -0,0 +1,23 @@ +def selection_sort(arr, simulation=False): + """ Selection Sort + Complexity: O(n^2) + """ + iteration = 0 + if simulation: + print("iteration",iteration,":",*arr) + + for i in range(len(arr)): + minimum = i + + for j in range(i + 1, len(arr)): + # "Select" the correct value + if arr[j] < arr[minimum]: + minimum = j + + arr[minimum], arr[i] = arr[i], arr[minimum] + + if simulation: + iteration = iteration + 1 + print("iteration",iteration,":",*arr) + + return arr diff --git a/algorithms/sort/shell_sort.py b/algorithms/sort/shell_sort.py new file mode 100644 index 000000000..61f5f612e --- /dev/null +++ b/algorithms/sort/shell_sort.py @@ -0,0 +1,21 @@ +def shell_sort(arr): + ''' Shell Sort + Complexity: O(n^2) + ''' + n = len(arr) + # Initialize size of the gap + gap = n//2 + + while gap > 0: + y_index = gap + while y_index < len(arr): + y = arr[y_index] + x_index = y_index - gap + while x_index >= 0 and y < arr[x_index]: + arr[x_index + gap] = arr[x_index] + x_index = x_index - gap + arr[x_index + gap] = y + y_index = y_index + 1 + gap = gap//2 + + return arr diff --git a/sort/sort_colors.py b/algorithms/sort/sort_colors.py similarity index 89% rename from sort/sort_colors.py rename to algorithms/sort/sort_colors.py index e85879781..aa44e221e 100644 --- a/sort/sort_colors.py +++ b/algorithms/sort/sort_colors.py @@ -25,6 +25,6 @@ def sort_colors(nums): if __name__ == "__main__": - nums = [0,1,1,1,2,2,2,1,1,1,0,0,0,0,1,1,1,0,0,2,2] + nums = [0, 1, 1, 1, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 2, 2] sort_colors(nums) print(nums) diff --git a/algorithms/sort/top_sort.py b/algorithms/sort/top_sort.py new file mode 100644 index 000000000..b9188bb28 --- /dev/null +++ b/algorithms/sort/top_sort.py @@ -0,0 +1,66 @@ +GRAY, BLACK = 0, 1 + +def top_sort_recursive(graph): + """ Time complexity is the same as DFS, which is O(V + E) + Space complexity: O(V) + """ + order, enter, state = [], set(graph), {} + + def dfs(node): + state[node] = GRAY + #print(node) + for k in graph.get(node, ()): + sk = state.get(k, None) + if sk == GRAY: + raise ValueError("cycle") + if sk == BLACK: + continue + enter.discard(k) + dfs(k) + order.append(node) + state[node] = BLACK + + while enter: dfs(enter.pop()) + return order + +def top_sort(graph): + """ Time complexity is the same as DFS, which is O(V + E) + Space complexity: O(V) + """ + order, enter, state = [], set(graph), {} + + def is_ready(node): + lst = graph.get(node, ()) + if len(lst) == 0: + return True + for k in lst: + sk = state.get(k, None) + if sk == GRAY: + raise ValueError("cycle") + if sk != BLACK: + return False + return True + + while enter: + node = enter.pop() + stack = [] + while True: + state[node] = GRAY + stack.append(node) + for k in graph.get(node, ()): + sk = state.get(k, None) + if sk == GRAY: + raise ValueError("cycle") + if sk == BLACK: + continue + enter.discard(k) + stack.append(k) + while stack and is_ready(stack[-1]): + node = stack.pop() + order.append(node) + state[node] = BLACK + if len(stack) == 0: + break + node = stack.pop() + + return order diff --git a/sort/wiggle_sort.py b/algorithms/sort/wiggle_sort.py similarity index 73% rename from sort/wiggle_sort.py rename to algorithms/sort/wiggle_sort.py index 2e09584c3..15dc76d4b 100644 --- a/sort/wiggle_sort.py +++ b/algorithms/sort/wiggle_sort.py @@ -1,4 +1,6 @@ - +""" +Given an unsorted array nums, reorder it such that nums[0] < nums[1] > nums[2] < nums[3].... +""" def wiggle_sort(nums): for i in range(len(nums)): if (i % 2 == 1) == (nums[i-1] > nums[i]): diff --git a/algorithms/stack/__init__.py b/algorithms/stack/__init__.py new file mode 100644 index 000000000..4e8d0603f --- /dev/null +++ b/algorithms/stack/__init__.py @@ -0,0 +1,10 @@ +from .stack import * +from .is_consecutive import * +from .is_sorted import * +from .remove_min import * +from .stutter import * +from .switch_pairs import * +from .valid_parenthesis import * +from .simplify_path import * +from .stack import * +from .ordered_stack import * diff --git a/algorithms/stack/is_consecutive.py b/algorithms/stack/is_consecutive.py new file mode 100644 index 000000000..f73ddffc7 --- /dev/null +++ b/algorithms/stack/is_consecutive.py @@ -0,0 +1,58 @@ +""" +Given a stack, a function is_consecutive takes a stack as a parameter and that +returns whether or not the stack contains a sequence of consecutive integers +starting from the bottom of the stack (returning true if it does, returning +false if it does not). + +For example: +bottom [3, 4, 5, 6, 7] top +Then the call of is_consecutive(s) should return true. +bottom [3, 4, 6, 7] top +Then the call of is_consecutive(s) should return false. +bottom [3, 2, 1] top +The function should return false due to reverse order. + +Note: There are 2 solutions: +first_is_consecutive: it uses a single stack as auxiliary storage +second_is_consecutive: it uses a single queue as auxiliary storage +""" +import collections + +def first_is_consecutive(stack): + storage_stack = [] + for i in range(len(stack)): + first_value = stack.pop() + if len(stack) == 0: # Case odd number of values in stack + return True + second_value = stack.pop() + if first_value - second_value != 1: # Not consecutive + return False + stack.append(second_value) # Backup second value + storage_stack.append(first_value) + + # Back up stack from storage stack + for i in range(len(storage_stack)): + stack.append(storage_stack.pop()) + return True + +def second_is_consecutive(stack): + q = collections.deque() + for i in range(len(stack)): + first_value = stack.pop() + if len(stack) == 0: # Case odd number of values in stack + return True + second_value = stack.pop() + if first_value - second_value != 1: # Not consecutive + return False + stack.append(second_value) # Backup second value + q.append(first_value) + + # Back up stack from queue + for i in range(len(q)): + stack.append(q.pop()) + for i in range(len(stack)): + q.append(stack.pop()) + for i in range(len(q)): + stack.append(q.pop()) + + return True diff --git a/algorithms/stack/is_sorted.py b/algorithms/stack/is_sorted.py new file mode 100644 index 000000000..8824dfa95 --- /dev/null +++ b/algorithms/stack/is_sorted.py @@ -0,0 +1,30 @@ +""" +Given a stack, a function is_sorted accepts a stack as a parameter and returns +true if the elements in the stack occur in ascending increasing order from +bottom, and false otherwise. That is, the smallest element should be at bottom + +For example: +bottom [6, 3, 5, 1, 2, 4] top +The function should return false +bottom [1, 2, 3, 4, 5, 6] top +The function should return true +""" +def is_sorted(stack): + storage_stack = [] + for i in range(len(stack)): + if len(stack) == 0: + return True + first_val = stack.pop() + if len(stack) == 0: + return True + second_val = stack.pop() + if first_val < second_val: + return False + storage_stack.append(first_val) + storage_stack.append(second_val) + + # Backup stack + for i in range(len(storage_stack)): + stack.append(storage_stack.pop()) + + return True diff --git a/stack/longest_abs_path.py b/algorithms/stack/longest_abs_path.py similarity index 83% rename from stack/longest_abs_path.py rename to algorithms/stack/longest_abs_path.py index f7627f5aa..67aad5a6a 100644 --- a/stack/longest_abs_path.py +++ b/algorithms/stack/longest_abs_path.py @@ -38,7 +38,7 @@ def length_longest_path(input): :type input: str :rtype: int """ - currlen, maxlen = 0, 0 # running length and max length + curr_len, max_len = 0, 0 # running length and max length stack = [] # keep track of the name length for s in input.split('\n'): print("---------") @@ -46,20 +46,20 @@ def length_longest_path(input): depth = s.count('\t') # the depth of current dir or file print("depth: ", depth) print("stack: ", stack) - print("curlen: ", currlen) + print("curlen: ", curr_len) while len(stack) > depth: # go back to the correct depth - currlen -= stack.pop() + curr_len -= stack.pop() stack.append(len(s.strip('\t'))+1) # 1 is the length of '/' - currlen += stack[-1] # increase current length + curr_len += stack[-1] # increase current length print("stack: ", stack) - print("curlen: ", currlen) + print("curlen: ", curr_len) if '.' in s: # update maxlen only when it is a file - maxlen = max(maxlen, currlen-1) # -1 is to minus one '/' - return maxlen + max_len = max(max_len, curr_len-1) # -1 is to minus one '/' + return max_len st= "dir\n\tsubdir1\n\t\tfile1.ext\n\t\tsubsubdirectory1\n\tsubdir2\n\t\tsubsubdir2\n\t\t\tfile2.ext" st2 = "a\n\tb1\n\t\tf1.txt\n\taaaaa\n\t\tf2.txt" print("path:", st2) -print("answer:", lengthLongestPath(st2)) +print("answer:", length_longest_path(st2)) diff --git a/algorithms/stack/ordered_stack.py b/algorithms/stack/ordered_stack.py new file mode 100644 index 000000000..7a30d143e --- /dev/null +++ b/algorithms/stack/ordered_stack.py @@ -0,0 +1,33 @@ +#The stack remains always ordered such that the highest value is at the top and the lowest at the bottom + +class OrderedStack: + def __init__(self): + self.items = [] + + def is_empty(self): + return self.items == [] + + def push_t(self, item): + self.items.append(item) + + def push(self, item): #push method to maintain order when pushing new elements + temp_stack = OrderedStack() + if self.is_empty() or item > self.peek(): + self.push_t(item) + else: + while item < self.peek() and not self.is_empty(): + temp_stack.push_t(self.pop()) + self.push_t(item) + while not temp_stack.is_empty(): + self.push_t(temp_stack.pop()) + + def pop(self): + if self.is_empty(): + raise IndexError("Stack is empty") + return self.items.pop() + + def peek(self): + return self.items[len(self.items) - 1] + + def size(self): + return len(self.items) diff --git a/algorithms/stack/remove_min.py b/algorithms/stack/remove_min.py new file mode 100644 index 000000000..1cd8fc6ae --- /dev/null +++ b/algorithms/stack/remove_min.py @@ -0,0 +1,28 @@ +""" +Given a stack, a function remove_min accepts a stack as a parameter +and removes the smallest value from the stack. + +For example: +bottom [2, 8, 3, -6, 7, 3] top +After remove_min(stack): +bottom [2, 8, 3, 7, 3] top + +""" +def remove_min(stack): + storage_stack = [] + if len(stack) == 0: # Stack is empty + return stack + # Find the smallest value + min = stack.pop() + stack.append(min) + for i in range(len(stack)): + val = stack.pop() + if val <= min: + min = val + storage_stack.append(val) + # Back up stack and remove min value + for i in range(len(storage_stack)): + val = storage_stack.pop() + if val != min: + stack.append(val) + return stack diff --git a/stack/simplify_path.py b/algorithms/stack/simplify_path.py similarity index 84% rename from stack/simplify_path.py rename to algorithms/stack/simplify_path.py index c6b7c7a5a..fed4385a6 100644 --- a/stack/simplify_path.py +++ b/algorithms/stack/simplify_path.py @@ -10,13 +10,12 @@ * Another corner case is the path might contain multiple slashes '/' together, such as "/home//foo/". In this case, you should ignore redundant slashes and return "/home/foo". """ - def simplify_path(path): """ :type path: str :rtype: str """ - skip = set(['..','.','']) + skip = {'..', '.', ''} stack = [] paths = path.split('/') for tok in paths: @@ -25,8 +24,4 @@ def simplify_path(path): stack.pop() elif tok not in skip: stack.append(tok) - return '/' +'/'.join(stack) - -p = '/my/name/is/..//keon' -print(p) -print(simplify_path(p)) + return '/' + '/'.join(stack) diff --git a/algorithms/stack/stack.py b/algorithms/stack/stack.py new file mode 100644 index 000000000..216c26492 --- /dev/null +++ b/algorithms/stack/stack.py @@ -0,0 +1,132 @@ +""" +Stack Abstract Data Type (ADT) +Stack() creates a new stack that is empty. + It needs no parameters and returns an empty stack. +push(item) adds a new item to the top of the stack. + It needs the item and returns nothing. +pop() removes the top item from the stack. + It needs no parameters and returns the item. The stack is modified. +peek() returns the top item from the stack but does not remove it. + It needs no parameters. The stack is not modified. +isEmpty() tests to see whether the stack is empty. + It needs no parameters and returns a boolean value. +size() returns the number of items on the stack. + It needs no parameters and returns an integer. +""" +from abc import ABCMeta, abstractmethod +class AbstractStack(metaclass=ABCMeta): + """Abstract Class for Stacks.""" + def __init__(self): + self._top = -1 + + def __len__(self): + return self._top + 1 + + def __str__(self): + result = " ".join(map(str, self)) + return 'Top-> ' + result + + def is_empty(self): + return self._top == -1 + + @abstractmethod + def __iter__(self): + pass + + @abstractmethod + def push(self, value): + pass + + @abstractmethod + def pop(self): + pass + + @abstractmethod + def peek(self): + pass + + +class ArrayStack(AbstractStack): + def __init__(self, size=10): + """ + Initialize python List with size of 10 or user given input. + Python List type is a dynamic array, so we have to restrict its + dynamic nature to make it work like a static array. + """ + super().__init__() + self._array = [None] * size + + def __iter__(self): + probe = self._top + while True: + if probe == -1: + return + yield self._array[probe] + probe -= 1 + + def push(self, value): + self._top += 1 + if self._top == len(self._array): + self._expand() + self._array[self._top] = value + + def pop(self): + if self.is_empty(): + raise IndexError("stack is empty") + value = self._array[self._top] + self._top -= 1 + return value + + def peek(self): + """returns the current top element of the stack.""" + if self.is_empty(): + raise IndexError("stack is empty") + return self._array[self._top] + + def _expand(self): + """ + expands size of the array. + Time Complexity: O(n) + """ + self._array += [None] * len(self._array) # double the size of the array + + +class StackNode: + """Represents a single stack node.""" + def __init__(self, value): + self.value = value + self.next = None + + +class LinkedListStack(AbstractStack): + + def __init__(self): + super().__init__() + self.head = None + + def __iter__(self): + probe = self.head + while True: + if probe is None: + return + yield probe.value + probe = probe.next + + def push(self, value): + node = StackNode(value) + node.next = self.head + self.head = node + self._top += 1 + + def pop(self): + if self.is_empty(): + raise IndexError("Stack is empty") + value = self.head.value + self.head = self.head.next + self._top -= 1 + return value + + def peek(self): + if self.is_empty(): + raise IndexError("Stack is empty") + return self.head.value diff --git a/algorithms/stack/stutter.py b/algorithms/stack/stutter.py new file mode 100644 index 000000000..30314992c --- /dev/null +++ b/algorithms/stack/stutter.py @@ -0,0 +1,44 @@ +""" +Given a stack, stutter takes a stack as a parameter and replaces every value +in the stack with two occurrences of that value. + +For example, suppose the stack stores these values: +bottom [3, 7, 1, 14, 9] top +Then the stack should store these values after the method terminates: +bottom [3, 3, 7, 7, 1, 1, 14, 14, 9, 9] top + +Note: There are 2 solutions: +first_stutter: it uses a single stack as auxiliary storage +second_stutter: it uses a single queue as auxiliary storage +""" +import collections + +def first_stutter(stack): + storage_stack = [] + for i in range(len(stack)): + storage_stack.append(stack.pop()) + for i in range(len(storage_stack)): + val = storage_stack.pop() + stack.append(val) + stack.append(val) + + return stack + +def second_stutter(stack): + q = collections.deque() + # Put all values into queue from stack + for i in range(len(stack)): + q.append(stack.pop()) + # Put values back into stack from queue + for i in range(len(q)): + stack.append(q.pop()) + # Now, stack is reverse, put all values into queue from stack + for i in range(len(stack)): + q.append(stack.pop()) + # Put 2 times value into stack from queue + for i in range(len(q)): + val = q.pop() + stack.append(val) + stack.append(val) + + return stack diff --git a/algorithms/stack/switch_pairs.py b/algorithms/stack/switch_pairs.py new file mode 100644 index 000000000..f5db2280d --- /dev/null +++ b/algorithms/stack/switch_pairs.py @@ -0,0 +1,62 @@ +""" +Given a stack, switch_pairs function takes a stack as a parameter and that +switches successive pairs of numbers starting at the bottom of the stack. + +For example, if the stack initially stores these values: +bottom [3, 8, 17, 9, 1, 10] top +Your function should switch the first pair (3, 8), the second pair (17, 9), ...: +bottom [8, 3, 9, 17, 10, 1] top + +if there are an odd number of values in the stack, the value at the top of the +stack is not moved: For example: +bottom [3, 8, 17, 9, 1] top +It would again switch pairs of values, but the value at the top of the stack (1) +would not be moved +bottom [8, 3, 9, 17, 1] top + +Note: There are 2 solutions: +first_switch_pairs: it uses a single stack as auxiliary storage +second_switch_pairs: it uses a single queue as auxiliary storage +""" +import collections + +def first_switch_pairs(stack): + storage_stack = [] + for i in range(len(stack)): + storage_stack.append(stack.pop()) + for i in range(len(storage_stack)): + if len(storage_stack) == 0: + break + first = storage_stack.pop() + if len(storage_stack) == 0: # case: odd number of values in stack + stack.append(first) + break + second = storage_stack.pop() + stack.append(second) + stack.append(first) + return stack + +def second_switch_pairs(stack): + q = collections.deque() + # Put all values into queue from stack + for i in range(len(stack)): + q.append(stack.pop()) + # Put values back into stack from queue + for i in range(len(q)): + stack.append(q.pop()) + # Now, stack is reverse, put all values into queue from stack + for i in range(len(stack)): + q.append(stack.pop()) + # Swap pairs by appending the 2nd value before appending 1st value + for i in range(len(q)): + if len(q) == 0: + break + first = q.pop() + if len(q) == 0: # case: odd number of values in stack + stack.append(first) + break + second = q.pop() + stack.append(second) + stack.append(first) + + return stack diff --git a/algorithms/stack/valid_parenthesis.py b/algorithms/stack/valid_parenthesis.py new file mode 100644 index 000000000..b62ac02c4 --- /dev/null +++ b/algorithms/stack/valid_parenthesis.py @@ -0,0 +1,20 @@ +""" +Given a string containing just the characters +'(', ')', '{', '}', '[' and ']', +determine if the input string is valid. + +The brackets must close in the correct order, +"()" and "()[]{}" are all valid but "(]" and "([)]" are not. +""" +def is_valid(s: str) -> bool: + stack = [] + dic = {")": "(", + "}": "{", + "]": "["} + for char in s: + if char in dic.values(): + stack.append(char) + elif char in dic: + if not stack or dic[char] != stack.pop(): + return False + return not stack diff --git a/algorithms/strings/__init__.py b/algorithms/strings/__init__.py new file mode 100644 index 000000000..98306f61d --- /dev/null +++ b/algorithms/strings/__init__.py @@ -0,0 +1,36 @@ +from .add_binary import * +from .breaking_bad import * +from .decode_string import * +from .delete_reoccurring import * +from .domain_extractor import * +from .encode_decode import * +from .group_anagrams import * +from .int_to_roman import * +from .is_palindrome import * +from .is_rotated import * +from .license_number import * +from .make_sentence import * +from .merge_string_checker import * +from .multiply_strings import * +from .one_edit_distance import * +from .rabin_karp import * +from .reverse_string import * +from .reverse_vowel import * +from .reverse_words import * +from .roman_to_int import * +from .strip_url_params import * +from .validate_coordinates import * +from .word_squares import * +from .unique_morse import * +from .judge_circle import * +from .strong_password import * +from .caesar_cipher import * +from .contain_string import * +from .count_binary_substring import * +from .repeat_string import * +from .text_justification import * +from .min_distance import * +from .longest_common_prefix import * +from .rotate import * +from .first_unique_char import * +from .repeat_substring import * diff --git a/string/add_binary.py b/algorithms/strings/add_binary.py similarity index 90% rename from string/add_binary.py rename to algorithms/strings/add_binary.py index 74e94794c..0158a80c8 100644 --- a/string/add_binary.py +++ b/algorithms/strings/add_binary.py @@ -21,6 +21,6 @@ def add_binary(a, b): c += ord(b[j]) - zero j -= 1 s = chr(c % 2 + zero) + s - # c //= 2 for python3 - c /= 2 + c //= 2 + return s diff --git a/algorithms/strings/breaking_bad.py b/algorithms/strings/breaking_bad.py new file mode 100644 index 000000000..98baf7cdb --- /dev/null +++ b/algorithms/strings/breaking_bad.py @@ -0,0 +1,95 @@ +""" +Given an api which returns an array of words and an array of symbols, display +the word with their matched symbol surrounded by square brackets. + +If the word string matches more than one symbol, then choose the one with +longest length. (ex. 'Microsoft' matches 'i' and 'cro'): + +Example: +Words array: ['Amazon', 'Microsoft', 'Google'] +Symbols: ['i', 'Am', 'cro', 'Na', 'le', 'abc'] + +Output: +[Am]azon, Mi[cro]soft, Goog[le] + +My solution(Wrong): +(I sorted the symbols array in descending order of length and ran loop over +words array to find a symbol match(using indexOf in javascript) which +worked. But I didn't make it through the interview, I am guessing my solution +was O(n^2) and they expected an efficient algorithm. + +output: +['[Am]azon', 'Mi[cro]soft', 'Goog[le]', 'Amaz[o]n', 'Micr[o]s[o]ft', 'G[o][o]gle'] +""" + +from functools import reduce + + +def match_symbol(words, symbols): + import re + combined = [] + for s in symbols: + for c in words: + r = re.search(s, c) + if r: + combined.append(re.sub(s, "[{}]".format(s), c)) + return combined + +def match_symbol_1(words, symbols): + res = [] + # reversely sort the symbols according to their lengths. + symbols = sorted(symbols, key=lambda _: len(_), reverse=True) + for word in words: + for symbol in symbols: + word_replaced = '' + # once match, append the `word_replaced` to res, process next word + if word.find(symbol) != -1: + word_replaced = word.replace(symbol, '[' + symbol + ']') + res.append(word_replaced) + break + # if this word matches no symbol, append it. + if word_replaced == '': + res.append(word) + return res + +""" +Another approach is to use a Tree for the dictionary (the symbols), and then +match brute force. The complexity will depend on the dictionary; +if all are suffixes of the other, it will be n*m +(where m is the size of the dictionary). For example, in Python: +""" + + +class TreeNode: + def __init__(self): + self.c = dict() + self.sym = None + + +def bracket(words, symbols): + root = TreeNode() + for s in symbols: + t = root + for char in s: + if char not in t.c: + t.c[char] = TreeNode() + t = t.c[char] + t.sym = s + result = dict() + for word in words: + i = 0 + symlist = list() + while i < len(word): + j, t = i, root + while j < len(word) and word[j] in t.c: + t = t.c[word[j]] + if t.sym is not None: + symlist.append((j + 1 - len(t.sym), j + 1, t.sym)) + j += 1 + i += 1 + if len(symlist) > 0: + sym = reduce(lambda x, y: x if x[1] - x[0] >= y[1] - y[0] else y, + symlist) + result[word] = "{}[{}]{}".format(word[:sym[0]], sym[2], + word[sym[1]:]) + return tuple(word if word not in result else result[word] for word in words) diff --git a/algorithms/strings/caesar_cipher.py b/algorithms/strings/caesar_cipher.py new file mode 100644 index 000000000..379f63e0d --- /dev/null +++ b/algorithms/strings/caesar_cipher.py @@ -0,0 +1,19 @@ + +""" +Julius Caesar protected his confidential information by encrypting it using a cipher. +Caesar's cipher shifts each letter by a number of letters. If the shift takes you +past the end of the alphabet, just rotate back to the front of the alphabet. +In the case of a rotation by 3, w, x, y and z would map to z, a, b and c. +Original alphabet: abcdefghijklmnopqrstuvwxyz +Alphabet rotated +3: defghijklmnopqrstuvwxyzabc +""" +def caesar_cipher(s, k): + result = "" + for char in s: + n = ord(char) + if 64 < n < 91: + n = ((n - 65 + k) % 26) + 65 + if 96 < n < 123: + n = ((n - 97 + k) % 26) + 97 + result = result + chr(n) + return result diff --git a/algorithms/strings/contain_string.py b/algorithms/strings/contain_string.py new file mode 100644 index 000000000..67056fed6 --- /dev/null +++ b/algorithms/strings/contain_string.py @@ -0,0 +1,25 @@ +""" +Implement strStr(). + +Return the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack. + +Example 1: +Input: haystack = "hello", needle = "ll" +Output: 2 + +Example 2: +Input: haystack = "aaaaa", needle = "bba" +Output: -1 +Reference: https://leetcode.com/problems/implement-strstr/description/ +""" +def contain_string(haystack, needle): + if len(needle) == 0: + return 0 + if len(needle) > len(haystack): + return -1 + for i in range(len(haystack)): + if len(haystack) - i < len(needle): + return -1 + if haystack[i:i+len(needle)] == needle: + return i + return -1 diff --git a/algorithms/strings/count_binary_substring.py b/algorithms/strings/count_binary_substring.py new file mode 100644 index 000000000..bd775ee53 --- /dev/null +++ b/algorithms/strings/count_binary_substring.py @@ -0,0 +1,33 @@ +""" +Give a string s, count the number of non-empty (contiguous) substrings that have + 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. + +Substrings that occur multiple times are counted the number of times they occur. +Example 1: +Input: "00110011" +Output: 6 +Explanation: There are 6 substrings that have equal number of consecutive 1's and 0's: "0011", "01", "1100", "10", "0011", and "01". + +Notice that some of these substrings repeat and are counted the number of times they occur. + +Also, "00110011" is not a valid substring because all the 0's (and 1's) are not grouped together. + +Example 2: +Input: "10101" +Output: 4 +Explanation: There are 4 substrings: "10", "01", "10", "01" that have equal number of consecutive 1's and 0's. +Reference: https://leetcode.com/problems/count-binary-substrings/description/ +""" +def count_binary_substring(s): + cur = 1 + pre = 0 + count = 0 + for i in range(1, len(s)): + if s[i] != s[i - 1]: + count = count + min(pre, cur) + pre = cur + cur = 1 + else: + cur = cur + 1 + count = count + min(pre, cur) + return count diff --git a/string/decode_string.py b/algorithms/strings/decode_string.py similarity index 91% rename from string/decode_string.py rename to algorithms/strings/decode_string.py index d6a0a7aac..6a2350f6f 100644 --- a/string/decode_string.py +++ b/algorithms/strings/decode_string.py @@ -36,9 +36,3 @@ def decode_string(s): else: cur_string += c return cur_string - -a = "3[a]2[bc]" #"aaabcbc". -b = "3[a2[c]]" #"accaccacc". - -print(decode_string(a)) -print(decode_string(b)) diff --git a/algorithms/strings/delete_reoccurring.py b/algorithms/strings/delete_reoccurring.py new file mode 100644 index 000000000..131a953b9 --- /dev/null +++ b/algorithms/strings/delete_reoccurring.py @@ -0,0 +1,19 @@ +""" +QUESTION: Given a string as your input, delete any reoccurring +character, and return the new string. + +This is a Google warmup interview question that was asked duirng phone screening +at my university. +""" + +# time complexity O(n) +def delete_reoccurring_characters(string): + seen_characters = set() + output_string = '' + for char in string: + if char not in seen_characters: + seen_characters.add(char) + output_string += char + return output_string + + \ No newline at end of file diff --git a/algorithms/strings/domain_extractor.py b/algorithms/strings/domain_extractor.py new file mode 100644 index 000000000..1b9ff2b78 --- /dev/null +++ b/algorithms/strings/domain_extractor.py @@ -0,0 +1,29 @@ +""" +Write a function that when given a URL as a string, parses out just the domain name and returns it as a string. + +Examples: +domain_name("http://github.com/SaadBenn") == "github" +domain_name("http://www.zombie-bites.com") == "zombie-bites" +domain_name("https://www.cnet.com") == "cnet" + +Note: The idea is not to use any built-in libraries such as re (regular expression) or urlparse except .split() built-in function +""" + +# Non pythonic way +def domain_name_1(url): + #grab only the non http(s) part + full_domain_name = url.split('//')[-1] + #grab the actual one depending on the len of the list + actual_domain = full_domain_name.split('.') + + # case when www is in the url + if (len(actual_domain) > 2): + return actual_domain[1] + # case when www is not in the url + return actual_domain[0] + + +# pythonic one liner +def domain_name_2(url): + return url.split("//")[-1].split("www.")[-1].split(".")[0] + diff --git a/algorithms/strings/encode_decode.py b/algorithms/strings/encode_decode.py new file mode 100644 index 000000000..4fbdf8943 --- /dev/null +++ b/algorithms/strings/encode_decode.py @@ -0,0 +1,30 @@ +""" Design an algorithm to encode a list of strings to a string. + The encoded mystring is then sent over the network and is decoded + back to the original list of strings. +""" + +# Implement the encode and decode methods. + +def encode(strs): + """Encodes a list of strings to a single string. + :type strs: List[str] + :rtype: str + """ + res = '' + for string in strs.split(): + res += str(len(string)) + ":" + string + return res + +def decode(s): + """Decodes a single string to a list of strings. + :type s: str + :rtype: List[str] + """ + strs = [] + i = 0 + while i < len(s): + index = s.find(":", i) + size = int(s[i:index]) + strs.append(s[index+1: index+1+size]) + i = index+1+size + return strs \ No newline at end of file diff --git a/algorithms/strings/first_unique_char.py b/algorithms/strings/first_unique_char.py new file mode 100644 index 000000000..44ba05e49 --- /dev/null +++ b/algorithms/strings/first_unique_char.py @@ -0,0 +1,27 @@ +""" +Given a string, find the first non-repeating character in it and return it's +index. If it doesn't exist, return -1. + +For example: +s = "leetcode" +return 0. + +s = "loveleetcode", +return 2. + +Reference: https://leetcode.com/problems/first-unique-character-in-a-string/description/ +""" +def first_unique_char(s): + """ + :type s: str + :rtype: int + """ + if (len(s) == 1): + return 0 + ban = [] + for i in range(len(s)): + if all(s[i] != s[k] for k in range(i + 1, len(s))) == True and s[i] not in ban: + return i + else: + ban.append(s[i]) + return -1 diff --git a/algorithms/strings/fizzbuzz.py b/algorithms/strings/fizzbuzz.py new file mode 100644 index 000000000..09078563b --- /dev/null +++ b/algorithms/strings/fizzbuzz.py @@ -0,0 +1,55 @@ +""" +Wtite a function that returns an array containing the numbers from 1 to N, +where N is the parametered value. N will never be less than 1. + +Replace certain values however if any of the following conditions are met: + +If the value is a multiple of 3: use the value 'Fizz' instead +If the value is a multiple of 5: use the value 'Buzz' instead +If the value is a multiple of 3 & 5: use the value 'FizzBuzz' instead +""" + +""" +There is no fancy algorithm to solve fizz buzz. + +Iterate from 1 through n +Use the mod operator to determine if the current iteration is divisible by: +3 and 5 -> 'FizzBuzz' +3 -> 'Fizz' +5 -> 'Buzz' +else -> string of current iteration +return the results +Complexity: + +Time: O(n) +Space: O(n) +""" + +def fizzbuzz(n): + + # Validate the input + if n < 1: + raise ValueError('n cannot be less than one') + if n is None: + raise TypeError('n cannot be None') + + result = [] + + for i in range(1, n+1): + if i%3 == 0 and i%5 == 0: + result.append('FizzBuzz') + elif i%3 == 0: + result.append('Fizz') + elif i%5 == 0: + result.append('Buzz') + else: + result.append(i) + return result + +# Alternative solution +def fizzbuzz_with_helper_func(n): + return [fb(m) for m in range(1,n+1)] + +def fb(m): + r = (m % 3 == 0) * "Fizz" + (m % 5 == 0) * "Buzz" + return r if r != "" else m diff --git a/algorithms/strings/group_anagrams.py b/algorithms/strings/group_anagrams.py new file mode 100644 index 000000000..d3b9047c2 --- /dev/null +++ b/algorithms/strings/group_anagrams.py @@ -0,0 +1,28 @@ +""" +Given an array of strings, group anagrams together. + +For example, given: ["eat", "tea", "tan", "ate", "nat", "bat"], +Return: + +[ + ["ate", "eat","tea"], + ["nat","tan"], + ["bat"] +] +""" + + +def group_anagrams(strs): + d = {} + ans = [] + k = 0 + for str in strs: + sstr = ''.join(sorted(str)) + if sstr not in d: + d[sstr] = k + k += 1 + ans.append([]) + ans[-1].append(str) + else: + ans[d[sstr]].append(str) + return ans diff --git a/algorithms/strings/int_to_roman.py b/algorithms/strings/int_to_roman.py new file mode 100644 index 000000000..88866eae7 --- /dev/null +++ b/algorithms/strings/int_to_roman.py @@ -0,0 +1,15 @@ +""" +Given an integer, convert it to a roman numeral. +Input is guaranteed to be within the range from 1 to 3999. +""" + +def int_to_roman(num): + """ + :type num: int + :rtype: str + """ + m = ["", "M", "MM", "MMM"]; + c = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"]; + x = ["", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"]; + i = ["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"]; + return m[num//1000] + c[(num%1000)//100] + x[(num%100)//10] + i[num%10]; diff --git a/algorithms/strings/is_palindrome.py b/algorithms/strings/is_palindrome.py new file mode 100644 index 000000000..0318bf5f4 --- /dev/null +++ b/algorithms/strings/is_palindrome.py @@ -0,0 +1,87 @@ +""" +Given a string, determine if it is a palindrome, +considering only alphanumeric characters and ignoring cases. +For example, +"A man, a plan, a canal: Panama" is a palindrome. +"race a car" is not a palindrome. +Note: +Have you consider that the string might be empty? +This is a good question to ask during an interview. +For the purpose of this problem, +we define empty string as valid palindrome. +""" +from string import ascii_letters + + +def is_palindrome(s): + """ + :type s: str + :rtype: bool + """ + i = 0 + j = len(s)-1 + while i < j: + while i < j and not s[i].isalnum(): + i += 1 + while i < j and not s[j].isalnum(): + j -= 1 + if s[i].lower() != s[j].lower(): + return False + i, j = i+1, j-1 + return True + +""" +Here is a bunch of other variations of is_palindrome function. + +Variation 1: +Find the reverse of the string and compare it with the original string + +Variation 2: +Loop from the start to length/2 and check the first character and last character +and so on... for instance s[0] compared with s[n-1], s[1] == s[n-2]... + +Variation 3: +Using stack idea. + +Note: We are assuming that we are just checking a one word string. To check if a complete sentence +""" +def remove_punctuation(s): + """ + Remove punctuation, case sensitivity and spaces + """ + return "".join(i.lower() for i in s if i in ascii_letters) + +# Variation 1 +def string_reverse(s): + return s[::-1] + +def is_palindrome_reverse(s): + s = remove_punctuation(s) + + # can also get rid of the string_reverse function and just do this return s == s[::-1] in one line. + if (s == string_reverse(s)): + return True + return False + + +# Variation 2 +def is_palindrome_two_pointer(s): + s = remove_punctuation(s) + + for i in range(0, len(s)//2): + if (s[i] != s[len(s) - i - 1]): + return False + return True + + +# Variation 3 +def is_palindrome_stack(s): + stack = [] + s = remove_punctuation(s) + + for i in range(len(s)//2, len(s)): + stack.append(s[i]) + for i in range(0, len(s)//2): + if s[i] != stack.pop(): + return False + return True diff --git a/algorithms/strings/is_rotated.py b/algorithms/strings/is_rotated.py new file mode 100644 index 000000000..a827fb497 --- /dev/null +++ b/algorithms/strings/is_rotated.py @@ -0,0 +1,31 @@ +""" +Given two strings s1 and s2, determine if s2 is a rotated version of s1. +For example, +is_rotated("hello", "llohe") returns True +is_rotated("hello", "helol") returns False + +accepts two strings +returns bool +Reference: https://leetcode.com/problems/rotate-string/description/ +""" + +def is_rotated(s1, s2): + if len(s1) == len(s2): + return s2 in s1 + s1 + else: + return False + +""" +Another solution: brutal force +Complexity: O(N^2) +""" +def is_rotated_v1(s1, s2): + if len(s1) != len(s2): + return False + if len(s1) == 0: + return True + + for c in range(len(s1)): + if all(s1[(c + i) % len(s1)] == s2[i] for i in range(len(s1))): + return True + return False diff --git a/algorithms/strings/judge_circle.py b/algorithms/strings/judge_circle.py new file mode 100644 index 000000000..46468a1ad --- /dev/null +++ b/algorithms/strings/judge_circle.py @@ -0,0 +1,25 @@ +""" +Initially, there is a Robot at position (0, 0). Given a sequence of its moves, +judge if this robot makes a circle, which means it moves back to the original place. + +The move sequence is represented by a string. And each move is represent by a +character. The valid robot moves are R (Right), L (Left), U (Up) and D (down). +The output should be true or false representing whether the robot makes a circle. + +Example 1: +Input: "UD" +Output: true +Example 2: +Input: "LL" +Output: false +""" +def judge_circle(moves): + dict_moves = { + 'U' : 0, + 'D' : 0, + 'R' : 0, + 'L' : 0 + } + for char in moves: + dict_moves[char] = dict_moves[char] + 1 + return dict_moves['L'] == dict_moves['R'] and dict_moves['U'] == dict_moves['D'] diff --git a/algorithms/strings/license_number.py b/algorithms/strings/license_number.py new file mode 100644 index 000000000..6f6d9a42c --- /dev/null +++ b/algorithms/strings/license_number.py @@ -0,0 +1,11 @@ + +def license_number(key, k): + res, alnum = [], [] + for char in key: + if char != "-": + alnum.append(char) + for i, char in enumerate(reversed(alnum)): + res.append(char) + if (i+1) % k == 0 and i != len(alnum)-1: + res.append("-") + return "".join(res[::-1]) diff --git a/algorithms/strings/longest_common_prefix.py b/algorithms/strings/longest_common_prefix.py new file mode 100644 index 000000000..80facc934 --- /dev/null +++ b/algorithms/strings/longest_common_prefix.py @@ -0,0 +1,66 @@ +""" +Write a function to find the longest common prefix string amongst an array of strings. + +If there is no common prefix, return an empty string "". + +Example 1: +Input: ["flower","flow","flight"] +Output: "fl" + +Example 2: +Input: ["dog","racecar","car"] +Output: "" +Explanation: There is no common prefix among the input strings. + +Reference: https://leetcode.com/problems/longest-common-prefix/description/ +""" + +""" +First solution: Horizontal scanning +""" +def common_prefix(s1, s2): + "Return prefix common of 2 strings" + if not s1 or not s2: + return "" + k = 0 + while s1[k] == s2[k]: + k = k + 1 + if k >= len(s1) or k >= len(s2): + return s1[0:k] + return s1[0:k] + +def longest_common_prefix_v1(strs): + if not strs: + return "" + result = strs[0] + for i in range(len(strs)): + result = common_prefix(result, strs[i]) + return result + +""" +Second solution: Vertical scanning +""" +def longest_common_prefix_v2(strs): + if not strs: + return "" + for i in range(len(strs[0])): + for string in strs[1:]: + if i == len(string) or string[i] != strs[0][i]: + return strs[0][0:i] + return strs[0] + +""" +Third solution: Divide and Conquer +""" +def longest_common_prefix_v3(strs): + if not strs: + return "" + return longest_common(strs, 0, len(strs) -1) + +def longest_common(strs, left, right): + if left == right: + return strs[left] + mid = (left + right) // 2 + lcp_left = longest_common(strs, left, mid) + lcp_right = longest_common(strs, mid + 1, right) + return common_prefix(lcp_left, lcp_right) diff --git a/string/make_sentence.py b/algorithms/strings/make_sentence.py similarity index 57% rename from string/make_sentence.py rename to algorithms/strings/make_sentence.py index 0cc2263c3..2b7bcf79f 100644 --- a/string/make_sentence.py +++ b/algorithms/strings/make_sentence.py @@ -14,21 +14,14 @@ count = 0 -def make_sentence(str_piece, dictionarys): + +def make_sentence(str_piece, dictionaries): global count if len(str_piece) == 0: return True for i in range(0, len(str_piece)): prefix, suffix = str_piece[0:i], str_piece[i:] - if ((prefix in dictionarys and suffix in dictionarys) - or (prefix in dictionarys - and make_sentence(suffix, dictionarys))): - count += 1 + if prefix in dictionaries: + if suffix in dictionaries or make_sentence(suffix, dictionaries): + count += 1 return True - - -if __name__ == "__main__": - dictionarys = ["", "app", "let", "t", "apple", "applet"] - word = "applet" - make_sentence(word, dictionarys) - print count \ No newline at end of file diff --git a/algorithms/strings/merge_string_checker.py b/algorithms/strings/merge_string_checker.py new file mode 100644 index 000000000..004226e06 --- /dev/null +++ b/algorithms/strings/merge_string_checker.py @@ -0,0 +1,42 @@ +""" +At a job interview, you are challenged to write an algorithm to check if a +given string, s, can be formed from two other strings, part1 and part2. +The restriction is that the characters in part1 and part2 are in the same +order as in s. The interviewer gives you the following example and tells +you to figure out the rest from the given test cases. +'codewars' is a merge from 'cdw' and 'oears': +s: c o d e w a r s = codewars +part1: c d w = cdw +part2: o e a r s = oears +""" + + +# Recursive Solution +def is_merge_recursive(s, part1, part2): + if not part1: + return s == part2 + if not part2: + return s == part1 + if not s: + return part1 + part2 == '' + if s[0] == part1[0] and is_merge_recursive(s[1:], part1[1:], part2): + return True + if s[0] == part2[0] and is_merge_recursive(s[1:], part1, part2[1:]): + return True + return False + + +# An iterative approach +def is_merge_iterative(s, part1, part2): + tuple_list = [(s, part1, part2)] + while tuple_list: + string, p1, p2 = tuple_list.pop() + if string: + if p1 and string[0] == p1[0]: + tuple_list.append((string[1:], p1[1:], p2)) + if p2 and string[0] == p2[0]: + tuple_list.append((string[1:], p1, p2[1:])) + else: + if not p1 and not p2: + return True + return False diff --git a/algorithms/strings/min_distance.py b/algorithms/strings/min_distance.py new file mode 100644 index 000000000..4e80f471f --- /dev/null +++ b/algorithms/strings/min_distance.py @@ -0,0 +1,28 @@ +""" +Given two words word1 and word2, find the minimum number of steps required to +make word1 and word2 the same, where in each step you can delete one character +in either string. + +For example: +Input: "sea", "eat" +Output: 2 +Explanation: You need one step to make "sea" to "ea" and another step to make "eat" to "ea". + +Reference: https://leetcode.com/problems/delete-operation-for-two-strings/description/ +""" + +def min_distance(word1, word2): + return len(word1) + len(word2) - 2 * lcs(word1, word2, len(word1), len(word2)) + +def lcs(s1, s2, i, j): + """ + The length of longest common subsequence among the two given strings s1 and s2 + """ + if i == 0 or j == 0: + return 0 + elif s1[i - 1] == s2[j - 1]: + return 1 + lcs(s1, s2, i - 1, j - 1) + else: + return max(lcs(s1, s2, i - 1, j), lcs(s1, s2, i, j - 1)) + +# TODO: Using dynamic programming diff --git a/string/multiply_strings.py b/algorithms/strings/multiply_strings.py similarity index 100% rename from string/multiply_strings.py rename to algorithms/strings/multiply_strings.py diff --git a/string/one_edit_distance.py b/algorithms/strings/one_edit_distance.py similarity index 100% rename from string/one_edit_distance.py rename to algorithms/strings/one_edit_distance.py diff --git a/string/rabin_karp.py b/algorithms/strings/rabin_karp.py similarity index 87% rename from string/rabin_karp.py rename to algorithms/strings/rabin_karp.py index ca6a18aad..d900a7380 100644 --- a/string/rabin_karp.py +++ b/algorithms/strings/rabin_karp.py @@ -2,25 +2,25 @@ # Rabin Karp Algorithm class RollingHash: - def __init__(self, text, sizeWord): + def __init__(self, text, size_word): self.text = text self.hash = 0 - self.sizeWord = sizeWord + self.size_word = size_word - for i in range(0, sizeWord): + for i in range(0, size_word): #ord maps the character to a number #subtract out the ASCII value of "a" to start the indexing at zero - self.hash += (ord(self.text[i]) - ord("a")+1)*(26**(sizeWord - i -1)) + self.hash += (ord(self.text[i]) - ord("a")+1)*(26**(size_word - i -1)) #start index of current window self.window_start = 0 #end of index window - self.window_end = sizeWord + self.window_end = size_word def move_window(self): if self.window_end <= len(self.text) - 1: #remove left letter from hash value - self.hash -= (ord(self.text[self.window_start]) - ord("a")+1)*26**(self.sizeWord-1) + self.hash -= (ord(self.text[self.window_start]) - ord("a")+1)*26**(self.size_word-1) self.hash *= 26 self.hash += ord(self.text[self.window_end])- ord("a")+1 self.window_start += 1 diff --git a/algorithms/strings/repeat_string.py b/algorithms/strings/repeat_string.py new file mode 100644 index 000000000..cc871f64c --- /dev/null +++ b/algorithms/strings/repeat_string.py @@ -0,0 +1,24 @@ +""" +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. + +For example, with A = "abcd" and B = "cdabcdab". + +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"). + +Note: +The length of A and B will be between 1 and 10000. + +Reference: https://leetcode.com/problems/repeated-string-match/description/ +""" +def repeat_string(A, B): + count = 1 + tmp = A + max_count = (len(B) / len(A)) + 1 + while not(B in tmp): + tmp = tmp + A + if (count > max_count): + count = -1 + break + count = count + 1 + + return count diff --git a/algorithms/strings/repeat_substring.py b/algorithms/strings/repeat_substring.py new file mode 100644 index 000000000..0f26c2819 --- /dev/null +++ b/algorithms/strings/repeat_substring.py @@ -0,0 +1,25 @@ +""" +Given a non-empty string check if it can be constructed by taking +a substring of it and appending multiple copies of the substring together. + +For example: +Input: "abab" +Output: True +Explanation: It's the substring "ab" twice. + +Input: "aba" +Output: False + +Input: "abcabcabcabc" +Output: True +Explanation: It's the substring "abc" four times. + +Reference: https://leetcode.com/problems/repeated-substring-pattern/description/ +""" +def repeat_substring(s): + """ + :type s: str + :rtype: bool + """ + str = (s + s)[1:-1] + return s in str diff --git a/string/reverse_string.py b/algorithms/strings/reverse_string.py similarity index 81% rename from string/reverse_string.py rename to algorithms/strings/reverse_string.py index 963caef28..f472135c7 100644 --- a/string/reverse_string.py +++ b/algorithms/strings/reverse_string.py @@ -4,9 +4,6 @@ def recursive(s): return s return recursive(s[l//2:]) + recursive(s[:l//2]) -s = "hello there" -print(recursive(s)) - def iterative(s): r = list(s) i, j = 0, len(s) - 1 @@ -16,10 +13,9 @@ def iterative(s): j -= 1 return "".join(r) -print(iterative(s)) - def pythonic(s): r = list(reversed(s)) return "".join(r) -print(pythonic(s)) +def ultra_pythonic(s): + return s[::-1] diff --git a/string/reverse_vowel.py b/algorithms/strings/reverse_vowel.py similarity index 85% rename from string/reverse_vowel.py rename to algorithms/strings/reverse_vowel.py index 70111ff34..b9fc6305e 100644 --- a/string/reverse_vowel.py +++ b/algorithms/strings/reverse_vowel.py @@ -11,7 +11,3 @@ def reverse_vowel(s): s[i], s[j] = s[j], s[i] i, j = i + 1, j - 1 return "".join(s) - -test = "hello" -print(test) -print(reverse_vowel(test)) diff --git a/algorithms/strings/reverse_words.py b/algorithms/strings/reverse_words.py new file mode 100644 index 000000000..62467ce8a --- /dev/null +++ b/algorithms/strings/reverse_words.py @@ -0,0 +1,20 @@ + +def reverse(array, i, j): + while i < j: + array[i], array[j] = array[j], array[i] + i += 1 + j -= 1 + + +def reverse_words(string): + arr = string.strip().split() # arr is list of words + n = len(arr) + reverse(arr, 0, n-1) + + return " ".join(arr) + + +if __name__ == "__main__": + test = "I am keon kim and I like pizza" + print(test) + print(reverse_words(test)) diff --git a/string/roman_to_int.py b/algorithms/strings/roman_to_int.py similarity index 100% rename from string/roman_to_int.py rename to algorithms/strings/roman_to_int.py diff --git a/algorithms/strings/rotate.py b/algorithms/strings/rotate.py new file mode 100644 index 000000000..7f713baf5 --- /dev/null +++ b/algorithms/strings/rotate.py @@ -0,0 +1,18 @@ +""" +Given a strings s and int k, return a string that rotates k times + +For example, +rotate("hello", 2) return "llohe" +rotate("hello", 5) return "hello" +rotate("hello", 6) return "elloh" +rotate("hello", 7) return "llohe" + +accepts two strings +returns bool +""" +def rotate(s, k): + double_s = s + s + if k <= len(s): + return double_s[k:k + len(s)] + else: + return double_s[k-len(s):k] diff --git a/algorithms/strings/strip_url_params.py b/algorithms/strings/strip_url_params.py new file mode 100644 index 000000000..364f78091 --- /dev/null +++ b/algorithms/strings/strip_url_params.py @@ -0,0 +1,95 @@ +""" +Write a function that does the following: +Removes any duplicate query string parameters from the url +Removes any query string parameters specified within the 2nd argument (optional array) + +An example: +www.saadbenn.com?a=1&b=2&a=2') // returns 'www.saadbenn.com?a=1&b=2' +""" +from collections import defaultdict +import urllib +import urllib.parse + +# Here is a very non-pythonic grotesque solution +def strip_url_params1(url, params_to_strip=None): + + if not params_to_strip: + params_to_strip = [] + if url: + result = '' # final result to be returned + tokens = url.split('?') + domain = tokens[0] + query_string = tokens[-1] + result += domain + # add the '?' to our result if it is in the url + if len(tokens) > 1: + result += '?' + if not query_string: + return url + else: + # logic for removing duplicate query strings + # build up the list by splitting the query_string using digits + key_value_string = [] + string = '' + for char in query_string: + if char.isdigit(): + key_value_string.append(string + char) + string = '' + else: + string += char + dict = defaultdict(int) + # logic for checking whether we should add the string to our result + for i in key_value_string: + _token = i.split('=') + if _token[0]: + length = len(_token[0]) + if length == 1: + if _token and (not(_token[0] in dict)): + if params_to_strip: + if _token[0] != params_to_strip[0]: + dict[_token[0]] = _token[1] + result = result + _token[0] + '=' + _token[1] + else: + if not _token[0] in dict: + dict[_token[0]] = _token[1] + result = result + _token[0] + '=' + _token[1] + else: + check = _token[0] + letter = check[1] + if _token and (not(letter in dict)): + if params_to_strip: + if letter != params_to_strip[0]: + dict[letter] = _token[1] + result = result + _token[0] + '=' + _token[1] + else: + if not letter in dict: + dict[letter] = _token[1] + result = result + _token[0] + '=' + _token[1] + return result + +# A very friendly pythonic solution (easy to follow) +def strip_url_params2(url, param_to_strip=[]): + if '?' not in url: + return url + + queries = (url.split('?')[1]).split('&') + queries_obj = [query[0] for query in queries] + for i in range(len(queries_obj) - 1, 0, -1): + if queries_obj[i] in param_to_strip or queries_obj[i] in queries_obj[0:i]: + queries.pop(i) + + return url.split('?')[0] + '?' + '&'.join(queries) + + +# Here is my friend's solution using python's builtin libraries +def strip_url_params3(url, strip=None): + if not strip: strip = [] + + parse = urllib.parse.urlparse(url) + query = urllib.parse.parse_qs(parse.query) + + query = {k: v[0] for k, v in query.items() if k not in strip} + query = urllib.parse.urlencode(query) + new = parse._replace(query=query) + + return new.geturl() \ No newline at end of file diff --git a/algorithms/strings/strong_password.py b/algorithms/strings/strong_password.py new file mode 100644 index 000000000..85acd0223 --- /dev/null +++ b/algorithms/strings/strong_password.py @@ -0,0 +1,43 @@ +""" +The signup page required her to input a name and a password. However, the password +must be strong. The website considers a password to be strong if it satisfies the following criteria: + +1) Its length is at least 6. +2) It contains at least one digit. +3) It contains at least one lowercase English character. +4) It contains at least one uppercase English character. +5) It contains at least one special character. The special characters are: !@#$%^&*()-+ +She typed a random string of length in the password field but wasn't sure if it was strong. +Given the string she typed, can you find the minimum number of characters she must add to make her password strong? + +Note: Here's the set of types of characters in a form you can paste in your solution: +numbers = "0123456789" +lower_case = "abcdefghijklmnopqrstuvwxyz" +upper_case = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +special_characters = "!@#$%^&*()-+" + +Input Format +The first line contains an integer denoting the length of the string. +The second line contains a string consisting of characters, the password +typed by Louise. Each character is either a lowercase/uppercase English alphabet, a digit, or a special character. + +Sample Input 1: strong_password(3,"Ab1") +Output: 3 (Because She can make the password strong by adding characters,for example, $hk, turning the password into Ab1$hk which is strong. +2 characters aren't enough since the length must be at least 6.) + +Sample Output 2: strong_password(11,"#Algorithms") +Output: 1 (Because the password isn't strong, but she can make it strong by adding a single digit.) + +""" +def strong_password(n, password): + count_error = 0 + # Return the minimum number of characters to make the password strong + if any(i.isdigit() for i in password) == False: + count_error = count_error + 1 + if any(i.islower() for i in password) == False: + count_error = count_error + 1 + if any(i.isupper() for i in password) == False: + count_error = count_error + 1 + if any(i in '!@#$%^&*()-+' for i in password) == False: + count_error = count_error + 1 + return max(count_error, 6 - n) diff --git a/algorithms/strings/text_justification.py b/algorithms/strings/text_justification.py new file mode 100644 index 000000000..a14a8ba39 --- /dev/null +++ b/algorithms/strings/text_justification.py @@ -0,0 +1,89 @@ +""" +Given an array of words and a width maxWidth, format the text such that each line +has exactly maxWidth characters and is fully (left and right) justified. + +You should pack your words in a greedy approach; that is, pack as many words as +you can in each line. Pad extra spaces ' ' when necessary so that each line has +exactly maxWidth characters. + +Extra spaces between words should be distributed as evenly as possible. If the +number of spaces on a line do not divide evenly between words, the empty slots +on the left will be assigned more spaces than the slots on the right. + +For the last line of text, it should be left justified and no extra space is +inserted between words. + +Note: +A word is defined as a character sequence consisting of non-space characters only. +Each word's length is guaranteed to be greater than 0 and not exceed maxWidth. +The input array words contains at least one word. + +Example: +Input: +words = ["What","must","be","acknowledgment","shall","be"] +maxWidth = 16 +Output: +[ + "What must be", + "acknowledgment ", + "shall be " +] +""" + + +def text_justification(words, max_width): + ''' + :type words: list + :type max_width: int + :rtype: list + ''' + ret = [] # return value + row_len = 0 # current length of strs in a row + row_words = [] # current words in a row + index = 0 # the index of current word in words + is_first_word = True # is current word the first in a row + while index < len(words): + while row_len <= max_width and index < len(words): + if len(words[index]) > max_width: + raise ValueError("there exists word whose length is larger than max_width") + tmp = row_len + row_words.append(words[index]) + tmp += len(words[index]) + if not is_first_word: + tmp += 1 # except for the first word, each word should have at least a ' ' before it. + if tmp > max_width: + row_words.pop() + break + row_len = tmp + index += 1 + is_first_word = False + # here we have already got a row of str , then we should supplement enough ' ' to make sure the length is max_width. + row = "" + # if the row is the last + if index == len(words): + for word in row_words: + row += (word + ' ') + row = row[:-1] + row += ' ' * (max_width - len(row)) + # not the last row and more than one word + elif len(row_words) != 1: + space_num = max_width - row_len + space_num_of_each_interval = space_num // (len(row_words) - 1) + space_num_rest = space_num - space_num_of_each_interval * (len(row_words) - 1) + for j in range(len(row_words)): + row += row_words[j] + if j != len(row_words) - 1: + row += ' ' * (1 + space_num_of_each_interval) + if space_num_rest > 0: + row += ' ' + space_num_rest -= 1 + # row with only one word + else: + row += row_words[0] + row += ' ' * (max_width - len(row)) + ret.append(row) + # after a row , reset those value + row_len = 0 + row_words = [] + is_first_word = True + return ret diff --git a/algorithms/strings/unique_morse.py b/algorithms/strings/unique_morse.py new file mode 100644 index 000000000..6d8618644 --- /dev/null +++ b/algorithms/strings/unique_morse.py @@ -0,0 +1,94 @@ +""" +International Morse Code defines a standard encoding where each letter is mapped to +a series of dots and dashes, as follows: "a" maps to ".-", "b" maps to "-...", "c" +maps to "-.-.", and so on. + +For convenience, the full table for the 26 letters of the English alphabet is given below: + 'a':".-", + 'b':"-...", + 'c':"-.-.", + 'd': "-..", + 'e':".", + 'f':"..-.", + 'g':"--.", + 'h':"....", + 'i':"..", + 'j':".---", + 'k':"-.-", + 'l':".-..", + 'm':"--", + 'n':"-.", + 'o':"---", + 'p':".--.", + 'q':"--.-", + 'r':".-.", + 's':"...", + 't':"-", + 'u':"..-", + 'v':"...-", + 'w':".--", + 'x':"-..-", + 'y':"-.--", + 'z':"--.." + +Now, given a list of words, each word can be written as a concatenation of the +Morse code of each letter. For example, "cab" can be written as "-.-.-....-", +(which is the concatenation "-.-." + "-..." + ".-"). We'll call such a +concatenation, the transformation of a word. + +Return the number of different transformations among all words we have. +Example: +Input: words = ["gin", "zen", "gig", "msg"] +Output: 2 +Explanation: +The transformation of each word is: +"gin" -> "--...-." +"zen" -> "--...-." +"gig" -> "--...--." +"msg" -> "--...--." + +There are 2 different transformations, "--...-." and "--...--.". +""" + +morse_code = { + 'a':".-", + 'b':"-...", + 'c':"-.-.", + 'd': "-..", + 'e':".", + 'f':"..-.", + 'g':"--.", + 'h':"....", + 'i':"..", + 'j':".---", + 'k':"-.-", + 'l':".-..", + 'm':"--", + 'n':"-.", + 'o':"---", + 'p':".--.", + 'q':"--.-", + 'r':".-.", + 's':"...", + 't':"-", + 'u':"..-", + 'v':"...-", + 'w':".--", + 'x':"-..-", + 'y':"-.--", + 'z':"--.." +} +def convert_morse_word(word): + morse_word = "" + word = word.lower() + for char in word: + morse_word = morse_word + morse_code[char] + return morse_word + +def unique_morse(words): + unique_morse_word = [] + for word in words: + morse_word = convert_morse_word(word) + if morse_word not in unique_morse_word: + unique_morse_word.append(morse_word) + return len(unique_morse_word) diff --git a/algorithms/strings/validate_coordinates.py b/algorithms/strings/validate_coordinates.py new file mode 100644 index 000000000..371214be0 --- /dev/null +++ b/algorithms/strings/validate_coordinates.py @@ -0,0 +1,49 @@ +"""" +Create a function that will validate if given parameters are valid geographical coordinates. +Valid coordinates look like the following: "23.32353342, -32.543534534". The return value should be either true or false. +Latitude (which is first float) can be between 0 and 90, positive or negative. Longitude (which is second float) can be between 0 and 180, positive or negative. +Coordinates can only contain digits, or one of the following symbols (including space after comma) -, . +There should be no space between the minus "-" sign and the digit after it. + +Here are some valid coordinates: +-23, 25 +43.91343345, 143 +4, -3 + +And some invalid ones: +23.234, - 23.4234 +N23.43345, E32.6457 +6.325624, 43.34345.345 +0, 1,2 + +""" +# I'll be adding my attempt as well as my friend's solution (took us ~ 1 hour) + +# my attempt +import re +def is_valid_coordinates_0(coordinates): + for char in coordinates: + if not (char.isdigit() or char in ['-', '.', ',', ' ']): + return False + l = coordinates.split(", ") + if len(l) != 2: + return False + try: + latitude = float(l[0]) + longitude = float(l[1]) + except: + return False + return -90 <= latitude <= 90 and -180 <= longitude <= 180 + +# friends solutions +def is_valid_coordinates_1(coordinates): + try: + lat, lng = [abs(float(c)) for c in coordinates.split(',') if 'e' not in c] + except ValueError: + return False + + return lat <= 90 and lng <= 180 + +# using regular expression +def is_valid_coordinates_regular_expression(coordinates): + return bool(re.match("-?(\d|[1-8]\d|90)\.?\d*, -?(\d|[1-9]\d|1[0-7]\d|180)\.?\d*$", coordinates)) diff --git a/string/word_squares.py b/algorithms/strings/word_squares.py similarity index 95% rename from string/word_squares.py rename to algorithms/strings/word_squares.py index 7d05bb982..6614d212d 100644 --- a/string/word_squares.py +++ b/algorithms/strings/word_squares.py @@ -65,6 +65,3 @@ def build(square): build([word]) return squares -dic =["area","lead","wall","lady","ball"] -print(word_squares(dic)) - diff --git a/algorithms/tree/avl/__init__.py b/algorithms/tree/avl/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/algorithms/tree/avl/avl.py b/algorithms/tree/avl/avl.py new file mode 100644 index 000000000..102fec82d --- /dev/null +++ b/algorithms/tree/avl/avl.py @@ -0,0 +1,124 @@ +from tree.tree import TreeNode + + +class AvlTree(object): + """ + An avl tree. + """ + + def __init__(self): + # Root node of the tree. + self.node = None + self.height = -1 + self.balance = 0 + + def insert(self, key): + """ + Insert new key into node + """ + # Create new node + n = TreeNode(key) + if not self.node: + self.node = n + self.node.left = AvlTree() + self.node.right = AvlTree() + elif key < self.node.val: + self.node.left.insert(key) + elif key > self.node.val: + self.node.right.insert(key) + self.re_balance() + + def re_balance(self): + """ + Re balance tree. After inserting or deleting a node, + """ + self.update_heights(recursive=False) + self.update_balances(False) + + while self.balance < -1 or self.balance > 1: + if self.balance > 1: + if self.node.left.balance < 0: + self.node.left.rotate_left() + self.update_heights() + self.update_balances() + self.rotate_right() + self.update_heights() + self.update_balances() + + if self.balance < -1: + if self.node.right.balance > 0: + self.node.right.rotate_right() + self.update_heights() + self.update_balances() + self.rotate_left() + self.update_heights() + self.update_balances() + + def update_heights(self, recursive=True): + """ + Update tree height + """ + if self.node: + if recursive: + if self.node.left: + self.node.left.update_heights() + if self.node.right: + self.node.right.update_heights() + + self.height = 1 + max(self.node.left.height, self.node.right.height) + else: + self.height = -1 + + def update_balances(self, recursive=True): + """ + Calculate tree balance factor + + """ + if self.node: + if recursive: + if self.node.left: + self.node.left.update_balances() + if self.node.right: + self.node.right.update_balances() + + self.balance = self.node.left.height - self.node.right.height + else: + self.balance = 0 + + def rotate_right(self): + """ + Right rotation + """ + new_root = self.node.left.node + new_left_sub = new_root.right.node + old_root = self.node + + self.node = new_root + old_root.left.node = new_left_sub + new_root.right.node = old_root + + def rotate_left(self): + """ + Left rotation + """ + new_root = self.node.right.node + new_left_sub = new_root.left.node + old_root = self.node + + self.node = new_root + old_root.right.node = new_left_sub + new_root.left.node = old_root + + def in_order_traverse(self): + """ + In-order traversal of the tree + """ + result = [] + + if not self.node: + return result + + result.extend(self.node.left.in_order_traverse()) + result.append(self.node.key) + result.extend(self.node.right.in_order_traverse()) + return result diff --git a/tree/bintree2list.py b/algorithms/tree/bin_tree_to_list.py similarity index 78% rename from tree/bintree2list.py rename to algorithms/tree/bin_tree_to_list.py index 091295444..d23834b12 100644 --- a/tree/bintree2list.py +++ b/algorithms/tree/bin_tree_to_list.py @@ -4,28 +4,28 @@ def __init__(self, val = 0): self.left = None self.right = None -def bintree2list(root): +def bin_tree_to_list(root): """ type root: root class """ if not root: return root - root = bintree2list_util(root) + root = bin_tree_to_list_util(root) while root.left: root = root.left return root -def bintree2list_util(root): +def bin_tree_to_list_util(root): if not root: return root if root.left: - left = bintree2list_util(root.left) + left = bin_tree_to_list_util(root.left) while left.right: left = left.right left.right = root root.left = left if root.right: - right = bintree2list_util(root.right) + right = bin_tree_to_list_util(root.right) while right.left: right = right.left right.left = root @@ -45,5 +45,5 @@ def print_tree(root): tree.left.right = Node(30) tree.right.left = Node(36) -head = bintree2list(tree) +head = bin_tree_to_list(tree) print_tree(head) diff --git a/algorithms/tree/binary_tree_paths.py b/algorithms/tree/binary_tree_paths.py new file mode 100644 index 000000000..3de72f710 --- /dev/null +++ b/algorithms/tree/binary_tree_paths.py @@ -0,0 +1,14 @@ +def binary_tree_paths(root): + res = [] + if not root: + return res + dfs(res, root, str(root.val)) + return res + +def dfs(res, root, cur): + if not root.left and not root.right: + res.append(cur) + if root.left: + dfs(res, root.left, cur+'->'+str(root.left.val)) + if root.right: + dfs(res, root.right, cur+'->'+str(root.right.val)) diff --git a/tree/bst/BSTIterator.py b/algorithms/tree/bst/BSTIterator.py similarity index 93% rename from tree/bst/BSTIterator.py rename to algorithms/tree/bst/BSTIterator.py index a19aecd8a..af8a68d77 100644 --- a/tree/bst/BSTIterator.py +++ b/algorithms/tree/bst/BSTIterator.py @@ -10,7 +10,7 @@ def has_next(self): return bool(self.stack) def next(self): - node = stack.pop() + node = self.stack.pop() tmp = node if tmp.right: tmp = tmp.right diff --git a/algorithms/tree/bst/array_to_bst.py b/algorithms/tree/bst/array_to_bst.py new file mode 100644 index 000000000..7ca5cf943 --- /dev/null +++ b/algorithms/tree/bst/array_to_bst.py @@ -0,0 +1,21 @@ +""" +Given an array where elements are sorted in ascending order, +convert it to a height balanced BST. +""" + + +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +def array_to_bst(nums): + if not nums: + return None + mid = len(nums)//2 + node = TreeNode(nums[mid]) + node.left = array_to_bst(nums[:mid]) + node.right = array_to_bst(nums[mid+1:]) + return node diff --git a/algorithms/tree/bst/bst.py b/algorithms/tree/bst/bst.py new file mode 100644 index 000000000..de1bddd8c --- /dev/null +++ b/algorithms/tree/bst/bst.py @@ -0,0 +1,139 @@ +""" +Implement Binary Search Tree. It has method: + 1. Insert + 2. Search + 3. Size + 4. Traversal (Preorder, Inorder, Postorder) +""" + +import unittest + +class Node(object): + def __init__(self, data): + self.data = data + self.left = None + self.right = None + +class BST(object): + def __init__(self): + self.root = None + + def get_root(self): + return self.root + + """ + Get the number of elements + Using recursion. Complexity O(logN) + """ + def size(self): + return self.recur_size(self.root) + + def recur_size(self, root): + if root is None: + return 0 + else: + return 1 + self.recur_size(root.left) + self.recur_size(root.right) + + """ + Search data in bst + Using recursion. Complexity O(logN) + """ + def search(self, data): + return self.recur_search(self.root, data) + + def recur_search(self, root, data): + if root is None: + return False + if root.data == data: + return True + elif data > root.data: # Go to right root + return self.recur_search(root.right, data) + else: # Go to left root + return self.recur_search(root.left, data) + + """ + Insert data in bst + Using recursion. Complexity O(logN) + """ + def insert(self, data): + if self.root: + return self.recur_insert(self.root, data) + else: + self.root = Node(data) + return True + + def recur_insert(self, root, data): + if root.data == data: # The data is already there + return False + elif data < root.data: # Go to left root + if root.left: # If left root is a node + return self.recur_insert(root.left, data) + else: # left root is a None + root.left = Node(data) + return True + else: # Go to right root + if root.right: # If right root is a node + return self.recur_insert(root.right, data) + else: + root.right = Node(data) + return True + + """ + Preorder, Postorder, Inorder traversal bst + """ + def preorder(self, root): + if root: + print(str(root.data), end = ' ') + self.preorder(root.left) + self.preorder(root.right) + + def inorder(self, root): + if root: + self.inorder(root.left) + print(str(root.data), end = ' ') + self.inorder(root.right) + + def postorder(self, root): + if root: + self.postorder(root.left) + self.postorder(root.right) + print(str(root.data), end = ' ') + +""" + The tree is created for testing: + + 10 + / \ + 6 15 + / \ / \ + 4 9 12 24 + / / \ + 7 20 30 + / + 18 +""" + +class TestSuite(unittest.TestCase): + def setUp(self): + self.tree = BST() + self.tree.insert(10) + self.tree.insert(15) + self.tree.insert(6) + self.tree.insert(4) + self.tree.insert(9) + self.tree.insert(12) + self.tree.insert(24) + self.tree.insert(7) + self.tree.insert(20) + self.tree.insert(30) + self.tree.insert(18) + + def test_search(self): + self.assertTrue(self.tree.search(24)) + self.assertFalse(self.tree.search(50)) + + def test_size(self): + self.assertEqual(11, self.tree.size()) + +if __name__ == '__main__': + unittest.main() diff --git a/tree/bst/bst_closest_value.py b/algorithms/tree/bst/bst_closest_value.py similarity index 100% rename from tree/bst/bst_closest_value.py rename to algorithms/tree/bst/bst_closest_value.py diff --git a/algorithms/tree/bst/count_left_node.py b/algorithms/tree/bst/count_left_node.py new file mode 100644 index 000000000..aa4155566 --- /dev/null +++ b/algorithms/tree/bst/count_left_node.py @@ -0,0 +1,61 @@ +""" +Write a function count_left_node returns the number of left children in the +tree. For example: the following tree has four left children (the nodes +storing the values 6, 3, 7, and 10): + + 9 + / \ + 6 12 + / \ / \ + 3 8 10 15 + / \ + 7 18 + + count_left_node = 4 + +""" +import unittest +from bst import Node +from bst import bst + +def count_left_node(root): + if root is None: + return 0 + elif root.left is None: + return count_left_node(root.right) + else: + return 1 + count_left_node(root.left) + count_left_node(root.right) + +""" + The tree is created for testing: + + 9 + / \ + 6 12 + / \ / \ + 3 8 10 15 + / \ + 7 18 + + count_left_node = 4 + +""" + +class TestSuite(unittest.TestCase): + def setUp(self): + self.tree = bst() + self.tree.insert(9) + self.tree.insert(6) + self.tree.insert(12) + self.tree.insert(3) + self.tree.insert(8) + self.tree.insert(10) + self.tree.insert(15) + self.tree.insert(7) + self.tree.insert(18) + + def test_count_left_node(self): + self.assertEqual(4, count_left_node(self.tree.root)) + +if __name__ == '__main__': + unittest.main() diff --git a/tree/bst/delete_node.py b/algorithms/tree/bst/delete_node.py similarity index 97% rename from tree/bst/delete_node.py rename to algorithms/tree/bst/delete_node.py index 7ee827daa..f86ecbc3d 100644 --- a/tree/bst/delete_node.py +++ b/algorithms/tree/bst/delete_node.py @@ -38,7 +38,7 @@ """ class Solution(object): - def deleteNode(self, root, key): + def delete_node(self, root, key): """ :type root: TreeNode :type key: int diff --git a/algorithms/tree/bst/depth_sum.py b/algorithms/tree/bst/depth_sum.py new file mode 100644 index 000000000..9674e65dd --- /dev/null +++ b/algorithms/tree/bst/depth_sum.py @@ -0,0 +1,66 @@ +""" +Write a function depthSum returns the sum of the values stored +in a binary search tree of integers weighted by the depth of each value. + +For example: + + 9 + / \ + 6 12 + / \ / \ + 3 8 10 15 + / \ + 7 18 + + depth_sum = 1*9 + 2*(6+12) + 3*(3+8+10+15) + 4*(7+18) + +""" +import unittest +from bst import Node +from bst import bst + +def depth_sum(root, n): + if root: + return recur_depth_sum(root, 1) + +def recur_depth_sum(root, n): + if root is None: + return 0 + elif root.left is None and root.right is None: + return root.data * n + else: + return n * root.data + recur_depth_sum(root.left, n+1) + recur_depth_sum(root.right, n+1) + +""" + The tree is created for testing: + + 9 + / \ + 6 12 + / \ / \ + 3 8 10 15 + / \ + 7 18 + + depth_sum = 1*9 + 2*(6+12) + 3*(3+8+10+15) + 4*(7+18) + +""" + +class TestSuite(unittest.TestCase): + def setUp(self): + self.tree = bst() + self.tree.insert(9) + self.tree.insert(6) + self.tree.insert(12) + self.tree.insert(3) + self.tree.insert(8) + self.tree.insert(10) + self.tree.insert(15) + self.tree.insert(7) + self.tree.insert(18) + + def test_depth_sum(self): + self.assertEqual(253, depth_sum(self.tree.root, 4)) + +if __name__ == '__main__': + unittest.main() diff --git a/algorithms/tree/bst/height.py b/algorithms/tree/bst/height.py new file mode 100644 index 000000000..c84b0c013 --- /dev/null +++ b/algorithms/tree/bst/height.py @@ -0,0 +1,60 @@ +""" +Write a function height returns the height of a tree. The height is defined to +be the number of levels. The empty tree has height 0, a tree of one node has +height 1, a root node with one or two leaves as children has height 2, and so on +For example: height of tree is 4 + + 9 + / \ + 6 12 + / \ / \ + 3 8 10 15 + / \ + 7 18 + + height = 4 + +""" +import unittest +from bst import Node +from bst import bst + +def height(root): + if root is None: + return 0 + else: + return 1 + max(height(root.left), height(root.right)) + +""" + The tree is created for testing: + + 9 + / \ + 6 12 + / \ / \ + 3 8 10 15 + / \ + 7 18 + + count_left_node = 4 + +""" + +class TestSuite(unittest.TestCase): + def setUp(self): + self.tree = bst() + self.tree.insert(9) + self.tree.insert(6) + self.tree.insert(12) + self.tree.insert(3) + self.tree.insert(8) + self.tree.insert(10) + self.tree.insert(15) + self.tree.insert(7) + self.tree.insert(18) + + def test_height(self): + self.assertEqual(4, height(self.tree.root)) + +if __name__ == '__main__': + unittest.main() diff --git a/tree/bst/is_bst.py b/algorithms/tree/bst/is_bst.py similarity index 97% rename from tree/bst/is_bst.py rename to algorithms/tree/bst/is_bst.py index 74ef0a579..a132a5af3 100644 --- a/tree/bst/is_bst.py +++ b/algorithms/tree/bst/is_bst.py @@ -21,7 +21,7 @@ """ -def is_BST(root): +def is_bst(root): """ :type root: TreeNode :rtype: bool diff --git a/tree/bst/kth_smallest.py b/algorithms/tree/bst/kth_smallest.py similarity index 93% rename from tree/bst/kth_smallest.py rename to algorithms/tree/bst/kth_smallest.py index c2e511a60..4996791f2 100644 --- a/tree/bst/kth_smallest.py +++ b/algorithms/tree/bst/kth_smallest.py @@ -21,7 +21,7 @@ def kth_smallest(root, k): class Solution(object): - def kthSmallest(self, root, k): + def kth_smallest(self, root, k): """ :type root: TreeNode :type k: int @@ -51,4 +51,4 @@ def helper(self, node, count): n2.left, n2.right = n4, n5 n3.left, n3.right = n6, n7 print(kth_smallest(n1, 2)) - print(Solution().kthSmallest(n1, 2)) + print(Solution().kth_smallest(n1, 2)) diff --git a/tree/bst/lowest_common_ancestor.py b/algorithms/tree/bst/lowest_common_ancestor.py similarity index 100% rename from tree/bst/lowest_common_ancestor.py rename to algorithms/tree/bst/lowest_common_ancestor.py diff --git a/algorithms/tree/bst/num_empty.py b/algorithms/tree/bst/num_empty.py new file mode 100644 index 000000000..36600e905 --- /dev/null +++ b/algorithms/tree/bst/num_empty.py @@ -0,0 +1,67 @@ +""" +Write a function num_empty returns returns the number of empty branches in a +tree. Function should count the total number of empty branches among the nodes +of the tree. A leaf node has two empty branches. In the case, if root is None, +it considered as a 1 empty branch +For example: the following tree has 10 empty branch (* is empty branch) + + 9 __ + / \___ + 6 12 + / \ / \ + 3 8 10 15 + / \ / \ / \ / \ + * * 7 * * * * 18 + / \ / \ + * * * * + + empty_branch = 10 + +""" +import unittest +from bst import Node +from bst import bst + +def num_empty(root): + if root is None: + return 1 + elif root.left is None and root.right: + return 1 + num_empty(root.right) + elif root.right is None and root.left: + return 1 + num_empty(root.left) + else: + return num_empty(root.left) + num_empty(root.right) + +""" + The tree is created for testing: + + 9 + / \ + 6 12 + / \ / \ + 3 8 10 15 + / \ + 7 18 + + num_empty = 10 + +""" + +class TestSuite(unittest.TestCase): + def setUp(self): + self.tree = bst() + self.tree.insert(9) + self.tree.insert(6) + self.tree.insert(12) + self.tree.insert(3) + self.tree.insert(8) + self.tree.insert(10) + self.tree.insert(15) + self.tree.insert(7) + self.tree.insert(18) + + def test_num_empty(self): + self.assertEqual(10, num_empty(self.tree.root)) + +if __name__ == '__main__': + unittest.main() diff --git a/tree/bst/predecessor.py b/algorithms/tree/bst/predecessor.py similarity index 100% rename from tree/bst/predecessor.py rename to algorithms/tree/bst/predecessor.py diff --git a/tree/bst/serialize_deserialize.py b/algorithms/tree/bst/serialize_deserialize.py similarity index 82% rename from tree/bst/serialize_deserialize.py rename to algorithms/tree/bst/serialize_deserialize.py index 602e2222d..20f911220 100644 --- a/tree/bst/serialize_deserialize.py +++ b/algorithms/tree/bst/serialize_deserialize.py @@ -1,5 +1,12 @@ +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + def serialize(root): def build_string(node): if node: diff --git a/tree/bst/successor.py b/algorithms/tree/bst/successor.py similarity index 100% rename from tree/bst/successor.py rename to algorithms/tree/bst/successor.py diff --git a/tree/bst/unique_bst.py b/algorithms/tree/bst/unique_bst.py similarity index 100% rename from tree/bst/unique_bst.py rename to algorithms/tree/bst/unique_bst.py diff --git a/tree/deepest_left.py b/algorithms/tree/deepest_left.py similarity index 100% rename from tree/deepest_left.py rename to algorithms/tree/deepest_left.py diff --git a/tree/invert_tree.py b/algorithms/tree/invert_tree.py similarity index 100% rename from tree/invert_tree.py rename to algorithms/tree/invert_tree.py diff --git a/tree/is_balanced.py b/algorithms/tree/is_balanced.py similarity index 100% rename from tree/is_balanced.py rename to algorithms/tree/is_balanced.py diff --git a/algorithms/tree/is_subtree.py b/algorithms/tree/is_subtree.py new file mode 100644 index 000000000..844b0d5f7 --- /dev/null +++ b/algorithms/tree/is_subtree.py @@ -0,0 +1,73 @@ +""" +Given two binary trees s and t, check if t is a subtree of s. +A subtree of a tree t is a tree consisting of a node in t and +all of its descendants in t. + +Example 1: + +Given s: + + 3 + / \ + 4 5 + / \ + 1 2 + +Given t: + + 4 + / \ + 1 2 +Return true, because t is a subtree of s. + +Example 2: + +Given s: + + 3 + / \ + 4 5 + / \ + 1 2 + / + 0 + +Given t: + + 3 + / + 4 + / \ + 1 2 +Return false, because even though t is part of s, +it does not contain all descendants of t. + +Follow up: +What if one tree is significantly lager than the other? +""" +import collections + + +def is_subtree(big, small): + flag = False + queue = collections.deque() + queue.append(big) + while queue: + node = queue.popleft() + if node.val == small.val: + flag = comp(node, small) + break + else: + queue.append(node.left) + queue.append(node.right) + return flag + + +def comp(p, q): + if not p and not q: + return True + if p and q: + return p.val == q.val and comp(p.left,q.left) and comp(p.right, q.right) + return False + + diff --git a/tree/is_symmetric.py b/algorithms/tree/is_symmetric.py similarity index 100% rename from tree/is_symmetric.py rename to algorithms/tree/is_symmetric.py diff --git a/algorithms/tree/longest_consecutive.py b/algorithms/tree/longest_consecutive.py new file mode 100644 index 000000000..4fd809a1c --- /dev/null +++ b/algorithms/tree/longest_consecutive.py @@ -0,0 +1,49 @@ +""" +Given a binary tree, find the length of the longest consecutive sequence path. + +The path refers to any sequence of nodes from some starting node to any node +in the tree along the parent-child connections. +The longest consecutive path need to be from parent to child +(cannot be the reverse). + +For example, + 1 + \ + 3 + / \ + 2 4 + \ + 5 +Longest consecutive sequence path is 3-4-5, so return 3. + 2 + \ + 3 + / + 2 + / + 1 +""" + + +def longest_consecutive(root): + """ + :type root: TreeNode + :rtype: int + """ + if not root: + return 0 + max_len = 0 + dfs(root, 0, root.val, max_len) + return max_len + + +def dfs(root, cur, target, max_len): + if not root: + return + if root.val == target: + cur += 1 + else: + cur = 1 + max_len = max(cur, max_len) + dfs(root.left, cur, root.val+1, max_len) + dfs(root.right, cur, root.val+1, max_len) diff --git a/tree/lowest_common_ancestor.py b/algorithms/tree/lowest_common_ancestor.py similarity index 91% rename from tree/lowest_common_ancestor.py rename to algorithms/tree/lowest_common_ancestor.py index 1702949f5..5c4016aed 100644 --- a/tree/lowest_common_ancestor.py +++ b/algorithms/tree/lowest_common_ancestor.py @@ -21,7 +21,7 @@ """ -def LCA(root, p, q): +def lca(root, p, q): """ :type root: TreeNode :type p: TreeNode @@ -30,8 +30,8 @@ def LCA(root, p, q): """ if not root or root is p or root is q: return root - left = LCA(root.left, p, q) - right = LCA(root.right, p, q) + left = lca(root.left, p, q) + right = lca(root.right, p, q) if left and right: return root return left if left else right diff --git a/tree/max_height.py b/algorithms/tree/max_height.py similarity index 100% rename from tree/max_height.py rename to algorithms/tree/max_height.py diff --git a/algorithms/tree/max_path_sum.py b/algorithms/tree/max_path_sum.py new file mode 100644 index 000000000..10559b52a --- /dev/null +++ b/algorithms/tree/max_path_sum.py @@ -0,0 +1,15 @@ + + +def max_path_sum(root): + maximum = float("-inf") + helper(root, maximum) + return maximum + + +def helper(root, maximum): + if not root: + return 0 + left = helper(root.left, maximum) + right = helper(root.right, maximum) + maximum = max(maximum, left+right+root.val) + return root.val + maximum diff --git a/tree/min_height.py b/algorithms/tree/min_height.py similarity index 97% rename from tree/min_height.py rename to algorithms/tree/min_height.py index ebb8f75be..a3923d43e 100644 --- a/tree/min_height.py +++ b/algorithms/tree/min_height.py @@ -5,7 +5,7 @@ def __init__(self, val = 0): self.right = None -def minDepth(self, root): +def min_depth(self, root): """ :type root: TreeNode :rtype: int diff --git a/tree/path_sum.py b/algorithms/tree/path_sum.py similarity index 100% rename from tree/path_sum.py rename to algorithms/tree/path_sum.py diff --git a/tree/path_sum2.py b/algorithms/tree/path_sum2.py similarity index 89% rename from tree/path_sum2.py rename to algorithms/tree/path_sum2.py index 22c878038..c989f9c18 100644 --- a/tree/path_sum2.py +++ b/algorithms/tree/path_sum2.py @@ -23,17 +23,17 @@ def path_sum(root, sum): if not root: return [] res = [] - DFS(root, sum, [], res) + dfs(root, sum, [], res) return res -def DFS(root, sum, ls, res): +def dfs(root, sum, ls, res): if not root.left and not root.right and root.val == sum: ls.append(root.val) res.append(ls) if root.left: - DFS(root.left, sum-root.val, ls+[root.val], res) + dfs(root.left, sum-root.val, ls+[root.val], res) if root.right: - DFS(root.right, sum-root.val, ls+[root.val], res) + dfs(root.right, sum-root.val, ls+[root.val], res) # DFS with stack diff --git a/algorithms/tree/pretty_print.py b/algorithms/tree/pretty_print.py new file mode 100644 index 000000000..1e756470a --- /dev/null +++ b/algorithms/tree/pretty_print.py @@ -0,0 +1,22 @@ +# a -> Adam -> Book -> 4 +# b -> Bill -> Computer -> 5 +# -> TV -> 6 +# Jill -> Sports -> 1 +# c -> Bill -> Sports -> 3 +# d -> Adam -> Computer -> 3 +# Quin -> Computer -> 3 +# e -> Quin -> Book -> 5 +# -> TV -> 2 +# f -> Adam -> Computer -> 7 +from __future__ import print_function + + +def tree_print(tree): + for key in tree: + print(key, end=' ') # end=' ' prevents a newline character + tree_element = tree[key] # multiple lookups is expensive, even amortized O(1)! + for subElem in tree_element: + print(" -> ", subElem, end=' ') + if type(subElem) != str: # OP wants indenting after digits + print("\n ") # newline and a space to match indenting + print() # forces a newline diff --git a/algorithms/tree/red_black_tree/red_black_tree.py b/algorithms/tree/red_black_tree/red_black_tree.py new file mode 100644 index 000000000..33fb6af4a --- /dev/null +++ b/algorithms/tree/red_black_tree/red_black_tree.py @@ -0,0 +1,294 @@ +""" +Implementation of Red-Black tree. +""" + + +class RBNode: + def __init__(self, val, is_red, parent=None, left=None, right=None): + self.val = val + self.parent = parent + self.left = left + self.right = right + self.color = is_red + + +class RBTree: + def __init__(self): + self.root = None + + def left_rotate(self, node): + # set the node as the left child node of the current node's right node + right_node = node.right + if right_node is None: + return + else: + # right node's left node become the right node of current node + node.right = right_node.left + if right_node.left is not None: + right_node.left.parent = node + right_node.parent = node.parent + # check the parent case + if node.parent is None: + self.root = right_node + elif node is node.parent.left: + node.parent.left = right_node + else: + node.parent.right = right_node + right_node.left = node + node.parent = right_node + + def right_rotate(self, node): + # set the node as the right child node of the current node's left node + left_node = node.left + if left_node is None: + return + else: + # left node's right node become the left node of current node + node.left = left_node.right + if left_node.right is not None: + left_node.right.parent = node + left_node.parent = node.parent + # check the parent case + if node.parent is None: + self.root = left_node + elif node is node.parent.left: + node.parent.left = left_node + else: + node.parent.right = left_node + left_node.right = node + node.parent = left_node + + def insert(self, node): + # the inserted node's color is default is red + root = self.root + insert_node_parent = None + # find the position of inserted node + while root is not None: + insert_node_parent = root + if insert_node_parent.val < node.val: + root = root.right + else: + root = root.left + # set the n ode's parent node + node.parent = insert_node_parent + if insert_node_parent is None: + # case 1 inserted tree is null + self.root = node + elif insert_node_parent.val > node.val: + # case 2 not null and find left or right + insert_node_parent.left = node + else: + insert_node_parent.right = node + node.left = None + node.right = None + node.color = 1 + # fix the tree to + self.fix_insert(node) + + def fix_insert(self, node): + # case 1 the parent is null, then set the inserted node as root and color = 0 + if node.parent is None: + node.color = 0 + self.root = node + return + # case 2 the parent color is black, do nothing + # case 3 the parent color is red + while node.parent and node.parent.color is 1: + if node.parent is node.parent.parent.left: + uncle_node = node.parent.parent.right + if uncle_node and uncle_node.color is 1: + # case 3.1 the uncle node is red + # then set parent and uncle color is black and grandparent is red + # then node => node.parent + node.parent.color = 0 + node.parent.parent.right.color = 0 + node.parent.parent.color = 1 + node = node.parent.parent + continue + elif node is node.parent.right: + # case 3.2 the uncle node is black or null, and the node is right of parent + # then set his parent node is current node + # left rotate the node and continue the next + node = node.parent + self.left_rotate(node) + # case 3.3 the uncle node is black and parent node is left + # then parent node set black and grandparent set red + node.parent.color = 0 + node.parent.parent.color = 1 + self.right_rotate(node.parent.parent) + else: + uncle_node = node.parent.parent.left + if uncle_node and uncle_node.color is 1: + # case 3.1 the uncle node is red + # then set parent and uncle color is black and grandparent is red + # then node => node.parent + node.parent.color = 0 + node.parent.parent.left.color = 0 + node.parent.parent.color = 1 + node = node.parent.parent + continue + elif node is node.parent.left: + # case 3.2 the uncle node is black or null, and the node is right of parent + # then set his parent node is current node + # left rotate the node and continue the next + node = node.parent + self.right_rotate(node) + # case 3.3 the uncle node is black and parent node is left + # then parent node set black and grandparent set red + node.parent.color = 0 + node.parent.parent.color = 1 + self.left_rotate(node.parent.parent) + self.root.color = 0 + + def transplant(self, node_u, node_v): + """ + replace u with v + :param node_u: replaced node + :param node_v: + :return: None + """ + if node_u.parent is None: + self.root = node_v + elif node_u is node_u.parent.left: + node_u.parent.left = node_v + elif node_u is node_u.parent.right: + node_u.parent.right = node_v + # check is node_v is None + if node_v: + node_v.parent = node_u.parent + + def maximum(self, node): + """ + find the max node when node regard as a root node + :param node: + :return: max node + """ + temp_node = node + while temp_node.right is not None: + temp_node = temp_node.right + return temp_node + + def minimum(self, node): + """ + find the minimum node when node regard as a root node + :param node: + :return: minimum node + """ + temp_node = node + while temp_node.left: + temp_node = temp_node.left + return temp_node + + def delete(self, node): + # find the node position + node_color = node.color + if node.left is None: + temp_node = node.right + self.transplant(node, node.right) + elif node.right is None: + temp_node = node.left + self.transplant(node, node.left) + else: + # both child exits ,and find minimum child of right child + node_min = self.minimum(node.right) + node_color = node_min.color + temp_node = node_min.right + ## + if node_min.parent != node: + self.transplant(node_min, node_min.right) + node_min.right = node.right + node_min.right.parent = node_min + self.transplant(node, node_min) + node_min.left = node.left + node_min.left.parent = node_min + node_min.color = node.color + # when node is black, then need to fix it with 4 cases + if node_color == 0: + self.delete_fixup(temp_node) + + def delete_fixup(self, node): + # 4 cases + while node != self.root and node.color == 0: + # node is not root and color is black + if node == node.parent.left: + # node is left node + node_brother = node.parent.right + + # case 1: node's red, can not get black node + # set brother is black and parent is red + if node_brother.color == 1: + node_brother.color = 0 + node.parent.color = 1 + self.left_rotate(node.parent) + node_brother = node.parent.right + + # case 2: brother node is black, and its children node is both black + if (node_brother.left is None or node_brother.left.color == 0) and ( + node_brother.right is None or node_brother.right.color == 0): + node_brother.color = 1 + node = node.parent + else: + + # case 3: brother node is black , and its left child node is red and right is black + if node_brother.right is None or node_brother.right.color == 0: + node_brother.color = 1 + node_brother.left.color = 0 + self.right_rotate(node_brother) + node_brother = node.parent.right + + # case 4: brother node is black, and right is red, and left is any color + node_brother.color = node.parent.color + node.parent.color = 0 + node_brother.right.color = 0 + self.left_rotate(node.parent) + node = self.root + break + else: + node_brother = node.parent.left + if node_brother.color == 1: + node_brother.color = 0 + node.parent.color = 1 + self.left_rotate(node.parent) + node_brother = node.parent.right + if (node_brother.left is None or node_brother.left.color == 0) and ( + node_brother.right is None or node_brother.right.color == 0): + node_brother.color = 1 + node = node.parent + else: + if node_brother.left is None or node_brother.left.color == 0: + node_brother.color = 1 + node_brother.right.color = 0 + self.left_rotate(node_brother) + node_brother = node.parent.left + node_brother.color = node.parent.color + node.parent.color = 0 + node_brother.left.color = 0 + self.right_rotate(node.parent) + node = self.root + break + node.color = 0 + + def inorder(self): + res = [] + if not self.root: + return res + stack = [] + root = self.root + while root or stack: + while root: + stack.append(root) + root = root.left + root = stack.pop() + res.append({'val': root.val, 'color': root.color}) + root = root.right + return res + + +if __name__ == "__main__": + rb = RBTree() + children = [11, 2, 14, 1, 7, 15, 5, 8, 4] + for child in children: + node = RBNode(child, 1) + print(child) + rb.insert(node) + print(rb.inorder()) diff --git a/tree/same_tree.py b/algorithms/tree/same_tree.py similarity index 82% rename from tree/same_tree.py rename to algorithms/tree/same_tree.py index 9100c4f2e..a407f6fd3 100644 --- a/tree/same_tree.py +++ b/algorithms/tree/same_tree.py @@ -7,11 +7,11 @@ """ -def isSameTree(p, q): +def is_same_tree(p, q): if not p and not q: return True if p and q and p.val == q.val: - return isSameTree(p.left, q.left) and isSameTree(p.right, q.right) + return is_same_tree(p.left, q.left) and is_same_tree(p.right, q.right) return False # Time Complexity O(min(N,M)) diff --git a/tree/Segment_Tree/segment_tree.py b/algorithms/tree/segment_tree/segment_tree.py similarity index 83% rename from tree/Segment_Tree/segment_tree.py rename to algorithms/tree/segment_tree/segment_tree.py index d9f0a6909..2bf51966f 100644 --- a/tree/Segment_Tree/segment_tree.py +++ b/algorithms/tree/segment_tree/segment_tree.py @@ -3,19 +3,19 @@ allowing queries to be done later in log(N) time function takes 2 values and returns a same type value ''' -class segment_tree: +class SegmentTree: def __init__(self,arr,function): self.segment = [0 for x in range(3*len(arr)+3)] self.arr = arr self.fn = function self.maketree(0,0,len(arr)-1) - def maketree(self,i,l,r): + def make_tree(self,i,l,r): if l==r: self.segment[i] = self.arr[l] elif l path/to/dir/file +path/to/dir/ + file --> path/to/dir/file +http://algorithms.com/ + part --> http://algorithms.com/part +http://algorithms.com + part --> http://algorithms/part +""" +import os + +def join_with_slash(base, suffix): + # Remove / trailing + base = base.rstrip('/') + # Remove / leading + suffix = suffix.lstrip('/').rstrip() + full_path = "{}/{}".format(base, suffix) + return full_path diff --git a/algorithms/unix/path/simplify_path.py b/algorithms/unix/path/simplify_path.py new file mode 100644 index 000000000..8880fc0c6 --- /dev/null +++ b/algorithms/unix/path/simplify_path.py @@ -0,0 +1,29 @@ +""" +Given an absolute path for a file (Unix-style), simplify it. + +For example, +path = "/home/", => "/home" +path = "/a/./b/../../c/", => "/c" + +Corner Cases: + +Did you consider the case where path = "/../"? +In this case, you should return "/". +Another corner case is the path might contain multiple slashes '/' together, such as "/home//foo/". +In this case, you should ignore redundant slashes and return "/home/foo". + +Reference: https://leetcode.com/problems/simplify-path/description/ +""" + +import os +def simplify_path_v1(path): + return os.path.abspath(path) + +def simplify_path_v2(path): + stack, tokens = [], path.split("/") + for token in tokens: + if token == ".." and stack: + stack.pop() + elif token != ".." and token != "." and token: + stack.append(token) + return "/" + "/".join(stack) diff --git a/algorithms/unix/path/split.py b/algorithms/unix/path/split.py new file mode 100644 index 000000000..168b12057 --- /dev/null +++ b/algorithms/unix/path/split.py @@ -0,0 +1,23 @@ +""" +Splitting a path into 2 parts +Example: +Input: https://algorithms/unix/test.py (for url) +Output: + part[0]: https://algorithms/unix + part[1]: test.py + +Input: algorithms/unix/test.py (for file path) +Output: + part[0]: algorithms/unix + part[1]: test.py +""" +import os + +def split(path): + parts = [] + split_part = path.rpartition('/') + # Takt the origin path without the last part + parts.append(split_part[0]) + # Take the last element of list + parts.append(split_part[2]) + return parts diff --git a/array/circular_counter.py b/array/circular_counter.py deleted file mode 100644 index f0574f361..000000000 --- a/array/circular_counter.py +++ /dev/null @@ -1,46 +0,0 @@ -""" -There are people sitting in a circular fashion, -print every third member while removing them, -the next counter starts immediately after the member is removed. -Print till all the members are exhausted. - -For example: -Input: consider 123456789 members sitting in a circular fashion, -Output: 369485271 -""" - -a = ['1','2','3','4','5','6','7','8','9'] - -def josepheus(int_list, skip): - skip = skip - 1 #list starts with 0 index - idx = 0 - while len(int_list)>0: - idx = (skip+idx)%len(int_list) #hashing to keep changing the index to every 3rd - print(int_list.pop(idx)) - - -josepheus(a,3) - -""" -the reason for hashing is that we have to find the index of the item which needs to be removed. -So for e.g. if you iterate with the initial list of folks with every 3rd item eliminated: - -INPUT -int_list = 123456789 -skip = 3 - -While Iteration: - -int_list = 123456789 -len(int_list) = 9 -skip = 2 # as int_list starts from 0 -idx = (0 + 2) % 9 #here previous index was 0 -so 3rd element which is 3 in this case eliminated -int_list = 12456789 -len(int_list) = 8 -idx = (2 + 2) % 8 #here previous index was 2 -so 3rd element starting from 4th person which is 6 would be deleted. -and so on -The reason why we have to do this way is I am not putting the people who escape at the back of list so ideally in 2 while iteration the list should have been -45678912 and then no hashing needed to be done, which means you can directly remove the third element -""" diff --git a/array/flatten.py b/array/flatten.py deleted file mode 100644 index 8ca86b7cb..000000000 --- a/array/flatten.py +++ /dev/null @@ -1,56 +0,0 @@ -""" -Implement Flatten Arrays. -Given an array that may contain nested arrays, -give a single resultant array. - -function flatten(input){ -} - -Example: - -Input: var input = [2, 1, [3, [4, 5], 6], 7, [8]]; -flatten(input); -Output: [2, 1, 3, 4, 5, 6, 7, 8] -""" - - -def list_flatten(l, a=None): - a = list(a) if isinstance(a, (list, tuple)) else [] - for i in l: - if isinstance(i, (list, tuple)): - a = list_flatten(i, a) - else: - a.append(i) - return a - - -# stack version -# public static List flatten(List l) { -# List main = new ArrayList(); - # Stack> stack = new Stack>(); - # Stack indexes = new Stack(); - # stack.add(l); - # indexes.add(0); - # while (true) { - # if (stack.isEmpty()) - # break; - # int index1 = indexes.pop(); - # l = stack.pop(); - # for (int i = index1; i < l.size(); i++) { - # NestedList n = l.get(i); - # if (n.isInteger()) { - # main.add(n.value); - # } else { - # stack.add(l); - # indexes.add(i+1); - # l = n.list; - # stack.add(l); - # indexes.add(0); - # break; - - # } - # } - # } - - # return main; -# } diff --git a/array/garage.py b/array/garage.py deleted file mode 100644 index 6d99f8d7c..000000000 --- a/array/garage.py +++ /dev/null @@ -1,42 +0,0 @@ -# There is a parking lot with only one empty spot. Given the initial state -# of the parking lot and the final state. Each step we are only allowed to -# move a car -# out of its place and move it into the empty spot. -# The goal is to find out the least movement needed to rearrange -# the parking lot from the initial state to the final state. - -# Say the initial state is an array: - -# [1,2,3,0,4], -# where 1,2,3,4 are different cars, and 0 is the empty spot. - -# And the final state is - -# [0,3,2,1,4]. -# We can swap 1 with 0 in the initial array to get [0,2,3,1,4] and so on. -# Each step swap with 0 only. -# Edited by cyberking-saga - - -def garage(initial, final): - steps = 0 - while initial != final: - zero = initial.index(0) - if zero != final.index(0): - car_to_move = final[zero] - pos = initial.index(car_to_move) - initial[zero], initial[pos] = initial[pos], initial[zero] - else: - for i in range(len(initial)): - if initial[i] != final[i]: - initial[zero], initial[i] = initial[i], initial[zero] - break - steps += 1 - return steps - -if __name__ == "__main__": - initial = [1, 2, 3, 0, 4] - final = [0, 3, 2, 1, 4] - print("initial:", initial) - print("final:", final) - print(garage(initial, final)) diff --git a/array/longest_non_repeat.py b/array/longest_non_repeat.py deleted file mode 100644 index c5f6edb59..000000000 --- a/array/longest_non_repeat.py +++ /dev/null @@ -1,27 +0,0 @@ -# Given a string, find the length of the longest substring -# without repeating characters. - -# Examples: - -# Given "abcabcbb", the answer is "abc", which the length is 3. - -# Given "bbbbb", the answer is "b", with the length of 1. - -# Given "pwwkew", the answer is "wke", with the length of 3. -# Note that the answer must be a substring, -# "pwke" is a subsequence and not a substring. - -def longest_non_repeat(s): - start, maxlen = 0, 0 - used_char = {} - for i, char in enumerate(s): - if char in used_char and start <= used_char[char]: - start = used_char[char] + 1 - else: - maxlen = max(maxlen, i-start+1) - used_char[char] = i - return maxlen - -a = "abcabcdefbb" -print(a) -print(longest_non_repeat(a)) diff --git a/array/merge_intervals.py b/array/merge_intervals.py deleted file mode 100644 index 0d465e2d4..000000000 --- a/array/merge_intervals.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -Given a collection of intervals, merge all overlapping intervals. - -For example, -Given [1,3],[2,6],[8,10],[15,18], -return [1,6],[8,10],[15,18]. -""" - -# Definition for an interval. -class Interval(object): - def __init__(self, s=0, e=0): - self.start = s - self.end = e - -def merge(intervals): - """ - :type intervals: List[Interval] - :rtype: List[Interval] - """ - out = [] - for i in sorted(intervals, key=lambda i: i.start): - if out and i.start <= out[-1].end: - out[-1].end = max(out[-1].end, i.end) - else: - out += i, - return out - -def print_intervals(intervals): - res = [] - for i in intervals: - res.append('['+str(i.start)+','+str(i.end)+']') - print("".join(res)) - -if __name__ == "__main__": - given = [[1,3],[2,6],[8,10],[15,18]] - intervals = [] - for l, r in given: - intervals.append(Interval(l,r)) - print_intervals(intervals) - print_intervals(merge(intervals)) diff --git a/array/missing_ranges.py b/array/missing_ranges.py deleted file mode 100644 index 91fc31ccb..000000000 --- a/array/missing_ranges.py +++ /dev/null @@ -1,27 +0,0 @@ -## find missing ranges between low and high in the given array. -# ex) [3, 5] lo=1 hi=10 => answer: [1->2, 4, 6->10] - -def missing_ranges(nums, lo, hi): - res = [] - start = lo - for num in nums: - if num < start: - continue - if num == start: - start += 1 - continue - res.append(get_range(start, num-1)) - start = num + 1 - if start <= hi: - res.append(get_range(start, hi)) - return res - -def get_range(n1, n2): - if n1 == n2: - return str(n1) - else: - return str(n1) + "->" + str(n2) - -nums = [3, 5, 10, 11, 12, 15, 19] -print("original:", nums) -print("missing range: ", missing_ranges(nums,0,20)) diff --git a/array/rotate_array.py b/array/rotate_array.py deleted file mode 100644 index ecc3e900a..000000000 --- a/array/rotate_array.py +++ /dev/null @@ -1,52 +0,0 @@ -""" -Rotate an array of n elements to the right by k steps. - -For example, with n = 7 and k = 3, -the array [1,2,3,4,5,6,7] is rotated to [5,6,7,1,2,3,4]. - -Note: -Try to come up as many solutions as you can, -there are at least 3 different ways to solve this problem. -""" - - -# -# Rotate the entire array 'k' times -# T(n)- O(nk) -# -def rotate_one_by_one(nums, k): - """ - :type nums: List[int] - :type k: int - :rtype: void Do not return anything, modify nums in-place instead. - """ - n = len(nums) - for i in range(k): - temp = nums[n-1] - for j in range(n-1, 0, -1): - nums[j] = nums[j-1] - nums[0] = temp - - -# -# Reverse segments of the array, followed by the entire array -# T(n)- O(n) -# -def rotate(nums, k): - """ - :type nums: List[int] - :type k: int - :rtype: void Do not return anything, modify nums in-place instead. - """ - n = len(nums) - k = k % n - reverse(nums, 0, n - k - 1) - reverse(nums, n - k, n - 1) - reverse(nums, 0, n - 1) - - -def reverse(array, a, b): - while a < b: - array[a], array[b] = array[b], array[a] - a += 1 - b -= 1 diff --git a/array/summary_ranges.py b/array/summary_ranges.py deleted file mode 100644 index 3cbe5abf4..000000000 --- a/array/summary_ranges.py +++ /dev/null @@ -1,27 +0,0 @@ -""" -Given a sorted integer array without duplicates, -return the summary of its ranges. - -For example, given [0,1,2,4,5,7], return ["0->2","4->5","7"]. -""" - - -def summary_ranges(nums): - """ - :type nums: List[int] - :rtype: List[str] - """ - res = [] - if len(nums) == 1: - return [str(nums[0])] - i = 0 - while i < len(nums): - num = nums[i] - while i+1 < len(nums) and nums[i+1] - nums[i] == 1: - i += 1 - if nums[i] != num: - res.append(str(num) + "->" + str(nums[i])) - else: - res.append(str(num)) - i += 1 - return res diff --git a/backtrack/anagram.py b/backtrack/anagram.py deleted file mode 100644 index df3c9dc46..000000000 --- a/backtrack/anagram.py +++ /dev/null @@ -1,44 +0,0 @@ -def all_perms(elements): - if len(elements) <=1: - yield elements - else: - for perm in all_perms(elements[1:]): - for i in range(len(elements)): - yield perm[:i] + elements[0:1] + perm[i:] - -def all_perms(elements): - if len(elements) <=1: - return elements - else: - tmp = [] - for perm in all_perms(elements[1:]): - for i in range(len(elements)): - tmp.append(perm[:i] + elements[0:1] + perm[i:]) - return tmp - -word = "abc" -print(list(all_perms(word))) - -def anagram(s1,s2): - c1 = [0]*26 - c2 = [0]*26 - - for i in range(len(s1)): - pos = ord(s1[i])-ord('a') - c1[pos] = c1[pos] + 1 - - for i in range(len(s2)): - pos = ord(s2[i])-ord('a') - c2[pos] = c2[pos] + 1 - - j = 0 - stillOK = True - while j<26 and stillOK: - if c1[j]==c2[j]: - j = j + 1 - else: - stillOK = False - - return stillOK - -print(anagram('apple','pleap')) diff --git a/backtrack/array_sum_combinations.py b/backtrack/array_sum_combinations.py deleted file mode 100644 index b267083d7..000000000 --- a/backtrack/array_sum_combinations.py +++ /dev/null @@ -1,86 +0,0 @@ -""" -WAP to take one element from each of the array add it to the target sum. Print all those three-element combinations. - -/* -A = [1, 2, 3, 3] -B = [2, 3, 3, 4] -C = [1, 2, 2, 2] -target = 7 -*/ - -Result: -[[1, 2, 4], [1, 3, 3], [1, 3, 3], [1, 3, 3], [1, 3, 3], [1, 4, 2], [2, 2, 3], [2, 2, 3], [2, 3, 2], [2, 3, 2], [3, 2, 2], [3, 2, 2]] -""" - - -A = [1, 2, 3, 3] -B = [2, 3, 3, 4] -C = [1, 2, 2, 2] -target = 7 - -def construct_candidates(constructed_sofar): - global A,B,C - array = A - if 1 == len(constructed_sofar) : - array = B - elif 2 == len(constructed_sofar) : - array = C - return array - - -def over(constructed_sofar): - global target - sum = 0 - to_stop, reached_target = False, False - for elem in constructed_sofar: - sum += elem - if sum >= target or len(constructed_sofar) >= 3 : - to_stop = True - if sum == target and 3 == len(constructed_sofar): - reached_target = True - - return to_stop, reached_target - -def backtrack(constructed_sofar): - to_stop, reached_target = over(constructed_sofar) - if to_stop: - if reached_target : - print constructed_sofar - return - candidates = construct_candidates(constructed_sofar) - for candidate in candidates : - constructed_sofar.append(candidate) - backtrack(constructed_sofar[:]) - constructed_sofar.pop() -backtrack([]) - - -# Complexity: O(n(m+p)) - -# 1. Sort all the arrays - a,b,c. - This will improve average time complexity. -# 2. If c[i] < Sum, then look for Sum - c[i] in array a and b. When pair found, insert c[i], a[j] & b[k] into the result list. This can be done in O(n). -# 3. Keep on doing the above procedure while going through complete c array. - - -import itertools -from functools import partial -A = [1,2,3,3] -B = [2,3,3,4] -C = [1,2,2,2] -S = 7 - -def check_sum(N, *nums): - if sum(x for x in nums) == N: - return (True, nums) - else: - return (False, nums) - -pro = itertools.product(A,B,C) -func = partial(check_sum, S) -sums = list(itertools.starmap(func, pro)) - -res = set() -for s in sums: - if s[0] == True and s[1] not in res: - res.add(s[1]) -print res diff --git a/backtrack/expression_add_operators.py b/backtrack/expression_add_operators.py deleted file mode 100644 index 2e1f45067..000000000 --- a/backtrack/expression_add_operators.py +++ /dev/null @@ -1,53 +0,0 @@ -""" -Given a string that contains only digits 0-9 and a target value, -return all possibilities to add binary operators (not unary) +, -, or * -between the digits so they prevuate to the target value. - -Examples: -"123", 6 -> ["1+2+3", "1*2*3"] -"232", 8 -> ["2*3+2", "2+3*2"] -"105", 5 -> ["1*0+5","10-5"] -"00", 0 -> ["0+0", "0-0", "0*0"] -"3456237490", 9191 -> [] -""" - -def add_operator(num, target): - """ - :type num: str - :type target: int - :rtype: List[str] - """ - res = [] - if not num: return res - helper(res, "", num, target, 0, 0, 0) - return res - -def helper(res, path, num, target, pos, prev, multed): - if pos == len(num): - if (target == prev): - res.append(path) - return - for i in range(pos, len(num)): - if i != pos and num[pos] == '0': # all digits have to be used - break - cur = int(num[pos:i+1]) - if pos == 0: - helper(res, path + str(cur), num, target, i+1, cur, cur) - else: - helper(res, path + "+" + str(cur), num, target, i+1, prev + cur, cur) - helper(res, path + "-" + str(cur), num, target, i+1, prev - cur, -cur) - helper(res, path + "*" + str(cur), num, target, i+1, prev - multed + multed * cur, multed * cur) - - -# "123", 6 -> ["1+2+3", "1*2*3"] -s = "123" -target = 6 -print(add_operator(s, target)) -# "232", 8 -> ["2*3+2", "2+3*2"] -s = "232" -target = 8 -print(add_operator(s, target)) - -s = "123045" -target = 3 -print(add_operator(s, target)) diff --git a/backtrack/general_solution.md b/backtrack/general_solution.md deleted file mode 100644 index 0c0ee0c6f..000000000 --- a/backtrack/general_solution.md +++ /dev/null @@ -1,156 +0,0 @@ -This structure might apply to many other backtracking questions, but here I am just going to demonstrate Subsets, Permutations, and Combination Sum. - -# Subsets : https://leetcode.com/problems/subsets/ - -public List> subsets(int[] nums) { - List> list = new ArrayList<>(); - Arrays.sort(nums); - backtrack(list, new ArrayList<>(), nums, 0); - return list; -} - -private void backtrack(List> list , List tempList, int [] nums, int start){ - list.add(new ArrayList<>(tempList)); - for(int i = start; i < nums.length; i++){ - tempList.add(nums[i]); - backtrack(list, tempList, nums, i + 1); - tempList.remove(tempList.size() - 1); - } -} - -# Subsets II (contains duplicates) : https://leetcode.com/problems/subsets-ii/ - -public List> subsetsWithDup(int[] nums) { - List> list = new ArrayList<>(); - Arrays.sort(nums); - backtrack(list, new ArrayList<>(), nums, 0); - return list; -} - -private void backtrack(List> list, List tempList, int [] nums, int start){ - list.add(new ArrayList<>(tempList)); - for(int i = start; i < nums.length; i++){ - if(i > start && nums[i] == nums[i-1]) continue; // skip duplicates - tempList.add(nums[i]); - backtrack(list, tempList, nums, i + 1); - tempList.remove(tempList.size() - 1); - } -} - -# Permutations : https://leetcode.com/problems/permutations/ - -public List> permute(int[] nums) { - List> list = new ArrayList<>(); - // Arrays.sort(nums); // not necessary - backtrack(list, new ArrayList<>(), nums); - return list; -} - -private void backtrack(List> list, List tempList, int [] nums){ - if(tempList.size() == nums.length){ - list.add(new ArrayList<>(tempList)); - } else{ - for(int i = 0; i < nums.length; i++){ - if(tempList.contains(nums[i])) continue; // element already exists, skip - tempList.add(nums[i]); - backtrack(list, tempList, nums); - tempList.remove(tempList.size() - 1); - } - } -} - -# Permutations II (contains duplicates) : https://leetcode.com/problems/permutations-ii/ - -public List> permuteUnique(int[] nums) { - List> list = new ArrayList<>(); - Arrays.sort(nums); - backtrack(list, new ArrayList<>(), nums, new boolean[nums.length]); - return list; -} - -private void backtrack(List> list, List tempList, int [] nums, boolean [] used){ - if(tempList.size() == nums.length){ - list.add(new ArrayList<>(tempList)); - } else{ - for(int i = 0; i < nums.length; i++){ - if(used[i] || i > 0 && nums[i] == nums[i-1] && !used[i - 1]) continue; - used[i] = true; - tempList.add(nums[i]); - backtrack(list, tempList, nums, used); - used[i] = false; - tempList.remove(tempList.size() - 1); - } - } -} - -# Combination Sum : https://leetcode.com/problems/combination-sum/ - -public List> combinationSum(int[] nums, int target) { - List> list = new ArrayList<>(); - Arrays.sort(nums); - backtrack(list, new ArrayList<>(), nums, target, 0); - return list; -} - -private void backtrack(List> list, List tempList, int [] nums, int remain, int start){ - if(remain < 0) return; - else if(remain == 0) list.add(new ArrayList<>(tempList)); - else{ - for(int i = start; i < nums.length; i++){ - tempList.add(nums[i]); - backtrack(list, tempList, nums, remain - nums[i], i); // not i + 1 because we can reuse same elements - tempList.remove(tempList.size() - 1); - } - } -} -# Combination Sum II (can't reuse same element) : https://leetcode.com/problems/combination-sum-ii/ - -public List> combinationSum2(int[] nums, int target) { - List> list = new ArrayList<>(); - Arrays.sort(nums); - backtrack(list, new ArrayList<>(), nums, target, 0); - return list; - -} - -private void backtrack(List> list, List tempList, int [] nums, int remain, int start){ - if(remain < 0) return; - else if(remain == 0) list.add(new ArrayList<>(tempList)); - else{ - for(int i = start; i < nums.length; i++){ - if(i > start && nums[i] == nums[i-1]) continue; // skip duplicates - tempList.add(nums[i]); - backtrack(list, tempList, nums, remain - nums[i], i + 1); - tempList.remove(tempList.size() - 1); - } - } -} - - -# Palindrome Partitioning : https://leetcode.com/problems/palindrome-partitioning/ - -public List> partition(String s) { - List> list = new ArrayList<>(); - backtrack(list, new ArrayList<>(), s, 0); - return list; -} - -public void backtrack(List> list, List tempList, String s, int start){ - if(start == s.length()) - list.add(new ArrayList<>(tempList)); - else{ - for(int i = start; i < s.length(); i++){ - if(isPalindrome(s, start, i)){ - tempList.add(s.substring(start, i + 1)); - backtrack(list, tempList, s, i + 1); - tempList.remove(tempList.size() - 1); - } - } - } -} - -public boolean isPalindrome(String s, int low, int high){ - while(low < high) - if(s.charAt(low++) != s.charAt(high--)) return false; - return true; -} diff --git a/backtrack/generate_abbreviations.py b/backtrack/generate_abbreviations.py deleted file mode 100644 index 929c33994..000000000 --- a/backtrack/generate_abbreviations.py +++ /dev/null @@ -1,26 +0,0 @@ -""" -given input word, return the list of abbreviations. -ex) -word => [1ord, w1rd, wo1d, w2d, 3d, w3 ... etc] -""" - - -def generate_abbreviations(word): - result = [] - backtrack(result, word, 0, 0, "") - return result - - -def backtrack(result, word, pos, count, cur): - if pos == len(word): - if count > 0: - cur += str(count) - result.append(cur) - return - - if count > 0: # add the current word - backtrack(result, word, pos+1, 0, cur+str(count)+word[pos]) - else: - backtrack(result, word, pos+1, 0, cur+word[pos]) - # skip the current word - backtrack(result, word, pos+1, count+1, cur) diff --git a/backtrack/generate_parenthesis.py b/backtrack/generate_parenthesis.py deleted file mode 100644 index 6dac22c94..000000000 --- a/backtrack/generate_parenthesis.py +++ /dev/null @@ -1,32 +0,0 @@ -""" -Given n pairs of parentheses, write a function to generate -all combinations of well-formed parentheses. - -For example, given n = 3, a solution set is: - -[ - "((()))", - "(()())", - "(())()", - "()(())", - "()()()" -] -""" - -def gen_parenthesis(n:"int")->"List[str]": - res = [] - add_pair(res, "", n, 0) - return res - -def add_pair(res, s, left, right): - if left == 0 and right == 0: - res.append(s) - return - if right > 0: - add_pair(res, s+")", left, right-1) - if left > 0: - add_pair(res, s+"(", left-1, right+1) - - -if __name__=="__main__": - print(gen_parenthesis(3)) diff --git a/backtrack/palindrome_partitioning.py b/backtrack/palindrome_partitioning.py deleted file mode 100644 index 654079010..000000000 --- a/backtrack/palindrome_partitioning.py +++ /dev/null @@ -1,29 +0,0 @@ -# It looks like you need to be looking not for all palindromic substrings, but rather for all the ways you can divide the input string up into palindromic substrings. (There's always at least one way, since one-character substrings are always palindromes.) - -# Here's the way I've done it: - -def palindromic_substrings(s): - if not s: - return [[]] - results = [] - for i in range(len(s), 0, -1): - sub = s[:i] - if sub == sub[::-1]: - for rest in palindromic_substrings(s[i:]): - results.append([sub] + rest) - return results - -# There's two loops. -# The outer loop checks each length of initial substring (in descending length order) to see if it is a palindrome. If so, it recurses on the rest of the string and loops over the returned values, adding the initial substring to each item before adding it to the results. - -# A slightly more Pythonic approach would be to make a recursive generator: - -def palindromic_substrings(s): - if not s: - yield [] - return - for i in range(len(s), 0, -1): - sub = s[:i] - if sub == sub[::-1]: - for rest in palindromic_substrings(s[i:]): - yield [sub] + rest diff --git a/backtrack/pattern_match.py b/backtrack/pattern_match.py deleted file mode 100644 index 532dfb9c2..000000000 --- a/backtrack/pattern_match.py +++ /dev/null @@ -1,52 +0,0 @@ -""" -Given a pattern and a string str, -find if str follows the same pattern. - -Here follow means a full match, such that there is a bijection between -a letter in pattern and a non-empty substring in str. - -Examples: -pattern = "abab", str = "redblueredblue" should return true. -pattern = "aaaa", str = "asdasdasdasd" should return true. -pattern = "aabb", str = "xyzabcxzyabc" should return false. -Notes: -You may assume both pattern and str contains only lowercase letters. -""" - - -def pattern_match(pattern, string): - """ - :type pattern: str - :type string: str - :rtype: bool - """ - return backtrack(pattern, string, {}) - - -def backtrack(pattern, string, dic): - print(dic) - if len(pattern) == 0 and len(string) > 0: - return False - if len(pattern) == len(string) == 0: - return True - for end in range(1, len(string)-len(pattern)+2): - if pattern[0] not in dic and string[:end] not in dic.values(): - dic[pattern[0]] = string[:end] - if backtrack(pattern[1:], string[end:], dic): - return True - del dic[pattern[0]] - elif pattern[0] in dic and dic[pattern[0]] == string[:end]: - if backtrack(pattern[1:], string[end:], dic): - return True - return False - -if __name__ == "__main__": - pattern1 = "abab" - string1 = "redblueredblue" - pattern2 = "aaaa" - string2 = "asdasdasdasd" - pattern3 = "aabb" - string3 = "xyzabcxzyabc" - print(pattern_match(pattern1, string1)) - print(pattern_match(pattern2, string2)) - print(pattern_match(pattern3, string3)) diff --git a/backtrack/permute.py b/backtrack/permute.py deleted file mode 100644 index 397597230..000000000 --- a/backtrack/permute.py +++ /dev/null @@ -1,40 +0,0 @@ -# Given a collection of distinct numbers, return all possible permutations. - -# For example, -# [1,2,3] have the following permutations: -# [ - # [1,2,3], - # [1,3,2], - # [2,1,3], - # [2,3,1], - # [3,1,2], - # [3,2,1] -# ] - -def permute(nums): - perms = [[]] - for n in nums: - new_perms = [] - for perm in perms: - for i in range(len(perm)+1): - new_perms.append(perm[:i] + [n] + perm[i:]) ###insert n - print(i, perm[:i], [n], perm[i:], ">>>>", new_perms) - perms = new_perms - return perms - -# DFS Version -# def permute(nums): - # res = [] - # dfs(res, nums, []) - # return res - -# def dfs(res, nums, path): - # if not nums: - # res.append(path) - # for i in range(len(nums)): - # print(nums[:i]+nums[i+1:]) - # dfs(res, nums[:i]+nums[i+1:], path+[nums[i]]) - -test = [1,2,3] -print(test) -print(permute(test)) diff --git a/backtrack/permute_unique.py b/backtrack/permute_unique.py deleted file mode 100644 index 911d40c76..000000000 --- a/backtrack/permute_unique.py +++ /dev/null @@ -1,24 +0,0 @@ -# Given a collection of numbers that might contain duplicates, -# return all possible unique permutations. - -# For example, -# [1,1,2] have the following unique permutations: -# [ - # [1,1,2], - # [1,2,1], - # [2,1,1] -# ] - -def permute_unique(nums): - perms = [[]] - for n in nums: - new_perms = [] - for l in perms: - for i in range(len(l)+1): - new_perms.append(l[:i]+[n]+l[i:]) - if i= len(nums): - # res.append(cur) - # else: - # backtrack(res, nums, cur+[nums[pos]], pos+1) - # backtrack(res, nums, cur, pos+1) - - -# Iteratively -def subsets2(self, nums): - res = [[]] - for num in sorted(nums): - res += [item+[num] for item in res] - return res - -test = [1,2,3] -print(test) -print(subsets(test)) diff --git a/backtrack/subsets_unique.py b/backtrack/subsets_unique.py deleted file mode 100644 index fac0cb0c2..000000000 --- a/backtrack/subsets_unique.py +++ /dev/null @@ -1,38 +0,0 @@ -# Given a collection of integers that might contain duplicates, nums, -# return all possible subsets. - -# Note: The solution set must not contain duplicate subsets. - -# For example, -# If nums = [1,2,2], a solution is: - -# [ - # [2], - # [1], - # [1,2,2], - # [2,2], - # [1,2], - # [] -# ] - -def subsets_unique(nums): - res = set() - backtrack(res, nums, [], 0) - return list(res) - -def backtrack(res, nums, stack, pos): - if pos == len(nums): - res.add(tuple(stack)) - else: - # take - stack.append(nums[pos]) - backtrack(res, nums, stack, pos+1) - stack.pop() - - # don't take - backtrack(res, nums, stack, pos+1) - - -test = [1,2,2] -print(test) -print(subsets_unique(test)) diff --git a/backtrack/word_search.py b/backtrack/word_search.py deleted file mode 100644 index 989e5b29c..000000000 --- a/backtrack/word_search.py +++ /dev/null @@ -1,111 +0,0 @@ -import unittest -''' -Given a matrix of words and a list of words to search, return a list of words that exists in the board -This is Word Search II on LeetCode - -board = [ - ['o','a','a','n'], - ['e','t','a','e'], - ['i','h','k','r'], - ['i','f','l','v'] - ] - -words = ["oath","pea","eat","rain"] - -''' - -def find_words(board, words): - # make a trie structure that is essentially dictionaries of dictionaries that map each character to a potential next character - trie = {} - for word in words: - curr_trie = trie - for char in word: - if char not in curr_trie: - curr_trie[char] = {} - curr_trie = curr_trie[char] - curr_trie['#'] = '#' - - # result is a set of found words since we do not want repeats - result = set() - used = [[False]*len(board[0]) for _ in range(len(board))] - - for i in range(len(board)): - for j in range(len(board[0])): - backtrack(board, i, j, trie, '', used, result) - return list(result) - -''' -backtrack tries to build each words from the board and return all words found -@param: board, the passed in board of characters -@param: i, the row index -@param: j, the column index -@param: trie, a trie of the passed in words -@param: pre, a buffer of currently build string that differs by recursion stack -@param: used, a replica of the board except in booleans to state whether a character has been used -@param: result, the resulting set that contains all words found - -@return: list of words found -''' - -def backtrack(board, i, j, trie, pre, used, result): - if '#' in trie: - result.add(pre) - - if i < 0 or i >= len(board) or j < 0 or j >= len(board[0]): - return - - if not used[i][j] and board[i][j] in trie: - used[i][j]=True - backtrack(board,i+1,j,trie[board[i][j]],pre+board[i][j], used, result) - backtrack(board,i,j+1,trie[board[i][j]],pre+board[i][j], used, result) - backtrack(board,i-1,j,trie[board[i][j]],pre+board[i][j], used, result) - backtrack(board,i,j-1,trie[board[i][j]],pre+board[i][j], used, result) - used[i][j]=False - -class MyTests(unittest.TestCase): - def test_normal(self): - board = [ - ['o','a','a','n'], - ['e','t','a','e'], - ['i','h','k','r'], - ['i','f','l','v'] - ] - - words = ["oath","pea","eat","rain"] - self.assertEqual(find_words(board, words), ['oath', 'eat']) - - def test_none(self): - board = [ - ['o','a','a','n'], - ['e','t','a','e'], - ['i','h','k','r'], - ['i','f','l','v'] - ] - - words = ["chicken", "nugget", "hello", "world"] - self.assertEqual(find_words(board, words), []) - - def test_empty(self): - board = [] - words = [] - self.assertEqual(find_words(board, words), []) - - def test_uneven(self): - board = [ - ['o','a','a','n'], - ['e','t','a','e'] - ] - words = ["oath","pea","eat","rain"] - self.assertEqual(find_words(board, words), ['eat']) - - def test_repeat(self): - board = [ - ['a','a','a'], - ['a','a','a'], - ['a','a','a'] - ] - words = ["a", "aa", "aaa", "aaaa", "aaaaa"] - self.assertTrue(len(find_words(board, words))==5) - -if __name__=="__main__": - unittest.main() diff --git a/bfs/word_ladder.py b/bfs/word_ladder.py deleted file mode 100644 index baf5d8d4e..000000000 --- a/bfs/word_ladder.py +++ /dev/null @@ -1,62 +0,0 @@ -""" -Given two words (beginWord and endWord), and a dictionary's word list, -find the length of shortest transformation sequence -from beginWord to endWord, such that: - -Only one letter can be changed at a time -Each intermediate word must exist in the word list -For example, - -Given: -beginWord = "hit" -endWord = "cog" -wordList = ["hot","dot","dog","lot","log"] -As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog", -return its length 5. -. -Note: -Return 0 if there is no such transformation sequence. -All words have the same length. -All words contain only lowercase alphabetic characters. -""" -def ladderLength(beginWord, endWord, wordList): - """ - Bidirectional BFS!!! - :type beginWord: str - :type endWord: str - :type wordList: Set[str] - :rtype: int - """ - beginSet = set() - endSet = set() - beginSet.add(beginWord) - endSet.add(endWord) - result = 2 - while len(beginSet) != 0 and len(endSet) != 0: - if len(beginSet) > len(endSet): - beginSet, endSet = endSet, beginSet - nextBeginSet = set() - for word in beginSet: - for ladderWord in wordRange(word): - if ladderWord in endSet: - return result - if ladderWord in wordList: - nextBeginSet.add(ladderWord) - wordList.remove(ladderWord) - beginSet = nextBeginSet - result += 1 - print(beginSet) - print(result) - return 0 - -def wordRange(word): - for ind in range(len(word)): - tempC = word[ind] - for c in [chr(x) for x in range(ord('a'), ord('z')+1)]: - if c != tempC: - yield word[:ind] + c + word[ind+1:] - -beginWord = "hit" -endWord = "cog" -wordList = ["hot","dot","dog","lot","log"] -print(ladderLength(beginWord, endWord, wordList)) diff --git a/bit/count_ones.py b/bit/count_ones.py deleted file mode 100644 index 4eb8c59b4..000000000 --- a/bit/count_ones.py +++ /dev/null @@ -1,25 +0,0 @@ -""" -Write a function that takes an unsigned integer and -returns the number of ’1' bits it has -(also known as the Hamming weight). - -For example, the 32-bit integer ’11' has binary -representation 00000000000000000000000000001011, -so the function should return 3. - -T(n)- O(log n) -""" - - -def count_ones(n): - """ - :type n: int - :rtype: int - """ - if n < 0: - return - counter = 0 - while n: - counter += n & 1 - n >>= 1 - return counter diff --git a/dfs/all_factors.py b/dfs/all_factors.py deleted file mode 100644 index fbed6edb9..000000000 --- a/dfs/all_factors.py +++ /dev/null @@ -1,73 +0,0 @@ -""" -Numbers can be regarded as product of its factors. For example, -8 = 2 x 2 x 2; - = 2 x 4. - - -Write a function that takes an integer n and return all possible combinations -of its factors.Numbers can be regarded as product of its factors. For example, -8 = 2 x 2 x 2; - = 2 x 4. - -Examples: -input: 1 -output: -[] - - -input: 37 -output: -[] - -input: 32 -output: -[ - [2, 16], - [2, 2, 8], - [2, 2, 2, 4], - [2, 2, 2, 2, 2], -""" - - -def get_factors(n): - def factor(n, i, combi, res): - while i * i <= n: - if n % i == 0: - res += combi + [i, int(n/i)], - factor(n/i, i, combi+[i], res) - i += 1 - return res - return factor(n, 2, [], []) - - -def get_factors_iterative1(self, n): - todo, res = [(n, 2, [])], [] - while todo: - n, i, combi = todo.pop() - while i * i <= n: - if n % i == 0: - res += combi + [i, n/i], - todo += (n/i, i, combi+[i]), - i += 1 - return res - - -def get_factors_iterative2(n): - ans, stack, x = [], [], 2 - while True: - if x > n / x: - if not stack: - return ans - ans.append(stack + [n]) - x = stack.pop() - n *= x - x += 1 - elif n % x == 0: - stack.append(x) - n /= x - else: - x += 1 - - -if __name__ == "__main__": - print(get_factors(32)) diff --git a/dfs/sudoku_solver.py b/dfs/sudoku_solver.py deleted file mode 100644 index a6c1a3208..000000000 --- a/dfs/sudoku_solver.py +++ /dev/null @@ -1,79 +0,0 @@ -""" -It's similar to how human solve Sudoku. - -create a hash table (dictionary) val to store possible values in every location. -Each time, start from the location with fewest possible values, choose one value -from it and then update the board and possible values at other locations. -If this update is valid, keep solving (DFS). If this update is invalid (leaving -zero possible values at some locations) or this value doesn't lead to the -solution, undo the updates and then choose the next value. -Since we calculated val at the beginning and start filling the board from the -location with fewest possible values, the amount of calculation and thus the -runtime can be significantly reduced: - - -The run time is 48-68 ms on LeetCode OJ, which seems to be among the fastest -python solutions here. - - -The PossibleVals function may be further simplified/optimized, but it works just -fine for now. (it would look less lengthy if we are allowed to use numpy array -for the board lol). -""" - -def solveSudoku(self, board): - self.board = board - self.val = self.PossibleVals() - self.Solver() - -def PossibleVals(self): - a = "123456789" - d, val = {}, {} - for i in xrange(9): - for j in xrange(9): - ele = self.board[i][j] - if ele != ".": - d[("r", i)] = d.get(("r", i), []) + [ele] - d[("c", j)] = d.get(("c", j), []) + [ele] - d[(i//3, j//3)] = d.get((i//3, j//3), []) + [ele] - else: - val[(i,j)] = [] - for (i,j) in val.keys(): - inval = d.get(("r",i),[])+d.get(("c",j),[])+d.get((i/3,j/3),[]) - val[(i,j)] = [n for n in a if n not in inval ] - return val - -def Solver(self): - if len(self.val)==0: - return True - kee = min(self.val.keys(), key=lambda x: len(self.val[x])) - nums = self.val[kee] - for n in nums: - update = {kee:self.val[kee]} - if self.ValidOne(n, kee, update): # valid choice - if self.Solver(): # keep solving - return True - self.undo(kee, update) # invalid choice or didn't solve it => undo - return False - -def ValidOne(self, n, kee, update): - self.board[kee[0]][kee[1]] = n - del self.val[kee] - i, j = kee - for ind in self.val.keys(): - if n in self.val[ind]: - if ind[0]==i or ind[1]==j or (ind[0]/3,ind[1]/3)==(i/3,j/3): - update[ind] = n - self.val[ind].remove(n) - if len(self.val[ind])==0: - return False - return True - -def undo(self, kee, update): - self.board[kee[0]][kee[1]]="." - for k in update: - if k not in self.val: - self.val[k]= update[k] - else: - self.val[k].append(update[k]) - return None diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 000000000..06f6415a1 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +SPHINXPROJ = algorithms +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 000000000..a515cfe0d --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,36 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build +set SPHINXPROJ=algorithms + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% + +:end +popd diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 000000000..3f4e532f7 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,21 @@ +# Progress bars on iterators +tqdm +sphinx_rtd_theme + +# Downloading data and other files +requests + +# Required for tests only: + +# Style-checking for PEP8 +flake8 + +# Run unit tests +pytest + +# Lets pytest find our code by automatically modifying PYTHONPATH +pytest-pythonpath + +# Coverage statistics +pytest-cov +codecov diff --git a/docs/source/_static/algorithms_logo.png b/docs/source/_static/algorithms_logo.png new file mode 100644 index 000000000..2e8487d9e Binary files /dev/null and b/docs/source/_static/algorithms_logo.png differ diff --git a/docs/source/_static/logo/128pxblack.png b/docs/source/_static/logo/128pxblack.png new file mode 100644 index 000000000..d18f07fd4 Binary files /dev/null and b/docs/source/_static/logo/128pxblack.png differ diff --git a/docs/source/_static/logo/128pxblack.svg b/docs/source/_static/logo/128pxblack.svg new file mode 100644 index 000000000..bff184164 --- /dev/null +++ b/docs/source/_static/logo/128pxblack.svg @@ -0,0 +1,10 @@ + + + + + diff --git a/docs/source/_static/logo/128pxblue.png b/docs/source/_static/logo/128pxblue.png new file mode 100644 index 000000000..b94a8ca38 Binary files /dev/null and b/docs/source/_static/logo/128pxblue.png differ diff --git a/docs/source/_static/logo/128pxblue.svg b/docs/source/_static/logo/128pxblue.svg new file mode 100644 index 000000000..be4cd88f3 --- /dev/null +++ b/docs/source/_static/logo/128pxblue.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/docs/source/_static/logo/128pxorange.png b/docs/source/_static/logo/128pxorange.png new file mode 100644 index 000000000..683883494 Binary files /dev/null and b/docs/source/_static/logo/128pxorange.png differ diff --git a/docs/source/_static/logo/128pxorange.svg b/docs/source/_static/logo/128pxorange.svg new file mode 100644 index 000000000..4fb4476f7 --- /dev/null +++ b/docs/source/_static/logo/128pxorange.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/docs/source/_static/logo/256pxblack.png b/docs/source/_static/logo/256pxblack.png new file mode 100644 index 000000000..4c1f054d5 Binary files /dev/null and b/docs/source/_static/logo/256pxblack.png differ diff --git a/docs/source/_static/logo/256pxblack.svg b/docs/source/_static/logo/256pxblack.svg new file mode 100644 index 000000000..43d314751 --- /dev/null +++ b/docs/source/_static/logo/256pxblack.svg @@ -0,0 +1,10 @@ + + + + + diff --git a/docs/source/_static/logo/256pxblue.png b/docs/source/_static/logo/256pxblue.png new file mode 100644 index 000000000..464efa3f9 Binary files /dev/null and b/docs/source/_static/logo/256pxblue.png differ diff --git a/docs/source/_static/logo/256pxblue.svg b/docs/source/_static/logo/256pxblue.svg new file mode 100644 index 000000000..d85a6f781 --- /dev/null +++ b/docs/source/_static/logo/256pxblue.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/docs/source/_static/logo/256pxorange.png b/docs/source/_static/logo/256pxorange.png new file mode 100644 index 000000000..6eb98e422 Binary files /dev/null and b/docs/source/_static/logo/256pxorange.png differ diff --git a/docs/source/_static/logo/256pxorange.svg b/docs/source/_static/logo/256pxorange.svg new file mode 100644 index 000000000..99fc6d6be --- /dev/null +++ b/docs/source/_static/logo/256pxorange.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/docs/source/_static/logo/512pxblack.png b/docs/source/_static/logo/512pxblack.png new file mode 100644 index 000000000..7254ef341 Binary files /dev/null and b/docs/source/_static/logo/512pxblack.png differ diff --git a/docs/source/_static/logo/512pxblack.svg b/docs/source/_static/logo/512pxblack.svg new file mode 100644 index 000000000..90b4aabc1 --- /dev/null +++ b/docs/source/_static/logo/512pxblack.svg @@ -0,0 +1,10 @@ + + + + + diff --git a/docs/source/_static/logo/512pxblue.png b/docs/source/_static/logo/512pxblue.png new file mode 100644 index 000000000..f0b951af8 Binary files /dev/null and b/docs/source/_static/logo/512pxblue.png differ diff --git a/docs/source/_static/logo/512pxblue.svg b/docs/source/_static/logo/512pxblue.svg new file mode 100644 index 000000000..9cae68895 --- /dev/null +++ b/docs/source/_static/logo/512pxblue.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/docs/source/_static/logo/512pxorange.png b/docs/source/_static/logo/512pxorange.png new file mode 100644 index 000000000..0f00865bf Binary files /dev/null and b/docs/source/_static/logo/512pxorange.png differ diff --git a/docs/source/_static/logo/512pxorange.svg b/docs/source/_static/logo/512pxorange.svg new file mode 100644 index 000000000..185fafdeb --- /dev/null +++ b/docs/source/_static/logo/512pxorange.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/docs/source/_static/logo/add b/docs/source/_static/logo/add new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/docs/source/_static/logo/add @@ -0,0 +1 @@ + diff --git a/docs/source/_static/logo/logotype1black.png b/docs/source/_static/logo/logotype1black.png new file mode 100644 index 000000000..8ff176ca3 Binary files /dev/null and b/docs/source/_static/logo/logotype1black.png differ diff --git a/docs/source/_static/logo/logotype1black.svg b/docs/source/_static/logo/logotype1black.svg new file mode 100644 index 000000000..81f7b4b29 --- /dev/null +++ b/docs/source/_static/logo/logotype1black.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + diff --git a/docs/source/_static/logo/logotype1blue.png b/docs/source/_static/logo/logotype1blue.png new file mode 100644 index 000000000..5cc48851f Binary files /dev/null and b/docs/source/_static/logo/logotype1blue.png differ diff --git a/docs/source/_static/logo/logotype1blue.svg b/docs/source/_static/logo/logotype1blue.svg new file mode 100644 index 000000000..2cd790cd0 --- /dev/null +++ b/docs/source/_static/logo/logotype1blue.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + diff --git a/docs/source/_static/logo/logotype1orange.png b/docs/source/_static/logo/logotype1orange.png new file mode 100644 index 000000000..87a706058 Binary files /dev/null and b/docs/source/_static/logo/logotype1orange.png differ diff --git a/docs/source/_static/logo/logotype1orange.svg b/docs/source/_static/logo/logotype1orange.svg new file mode 100644 index 000000000..45e0e10cf --- /dev/null +++ b/docs/source/_static/logo/logotype1orange.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + diff --git a/docs/source/_static/logo/logotype2black.png b/docs/source/_static/logo/logotype2black.png new file mode 100644 index 000000000..b5929b89a Binary files /dev/null and b/docs/source/_static/logo/logotype2black.png differ diff --git a/docs/source/_static/logo/logotype2black.svg b/docs/source/_static/logo/logotype2black.svg new file mode 100644 index 000000000..8f2307fe1 --- /dev/null +++ b/docs/source/_static/logo/logotype2black.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + diff --git a/docs/source/_static/logo/logotype2blue.png b/docs/source/_static/logo/logotype2blue.png new file mode 100644 index 000000000..b73257ca7 Binary files /dev/null and b/docs/source/_static/logo/logotype2blue.png differ diff --git a/docs/source/_static/logo/logotype2blue.svg b/docs/source/_static/logo/logotype2blue.svg new file mode 100644 index 000000000..57f1aa5fd --- /dev/null +++ b/docs/source/_static/logo/logotype2blue.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + diff --git a/docs/source/_static/logo/logotype2orange.png b/docs/source/_static/logo/logotype2orange.png new file mode 100644 index 000000000..fc5ab676e Binary files /dev/null and b/docs/source/_static/logo/logotype2orange.png differ diff --git a/docs/source/_static/logo/logotype2orange.svg b/docs/source/_static/logo/logotype2orange.svg new file mode 100644 index 000000000..bdd4c2d41 --- /dev/null +++ b/docs/source/_static/logo/logotype2orange.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + diff --git a/docs/source/arrays.rst b/docs/source/arrays.rst new file mode 100644 index 000000000..5e5623f5c --- /dev/null +++ b/docs/source/arrays.rst @@ -0,0 +1,16 @@ +.. role:: hidden + :class: hidden-section + +algorithms.arrays +================= + +.. automodule:: algorithms.arrays +.. currentmodule:: algorithms.arrays + +longest_non_repeat +------------------ + +:hidden:`longest_non_repeat_v1` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: longest_non_repeat_v1 diff --git a/docs/source/backtrack.rst b/docs/source/backtrack.rst new file mode 100644 index 000000000..0c33a3316 --- /dev/null +++ b/docs/source/backtrack.rst @@ -0,0 +1,5 @@ +.. role:: hidden + :class: hidden-section + +algorithms.backtrack +==================== diff --git a/docs/source/bfs.rst b/docs/source/bfs.rst new file mode 100644 index 000000000..699b9e925 --- /dev/null +++ b/docs/source/bfs.rst @@ -0,0 +1,5 @@ +.. role:: hidden + :class: hidden-section + +algorithms.bfs +================= diff --git a/docs/source/bit.rst b/docs/source/bit.rst new file mode 100644 index 000000000..b7aa877c0 --- /dev/null +++ b/docs/source/bit.rst @@ -0,0 +1,5 @@ +.. role:: hidden + :class: hidden-section + +algorithms.bit +================= diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 000000000..b6c0c8320 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# algorithms documentation build configuration file, created by +# sphinx-quickstart on Wed Jun 6 01:17:26 2018. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) +from recommonmark.parser import CommonMarkParser + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = ['sphinx.ext.autodoc', + 'sphinx.ext.doctest', + 'sphinx.ext.todo', + 'sphinx.ext.coverage', + 'sphinx.ext.mathjax', + 'sphinx.ext.ifconfig', + 'sphinx.ext.viewcode', + 'sphinx.ext.githubpages'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +source_parsers = { + '.md': CommonMarkParser +} +source_suffix = ['.rst', '.md'] + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = 'algorithms' +copyright = '2018, Algorithms Team & Contributors' +author = 'Algorithms Team & Contributors' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0.1.0' +# The full version, including alpha/beta/rc tags. +release = '0.1.0' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = [] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'alabaster' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# This is required for the alabaster theme +# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars +html_sidebars = { + '**': [ + 'about.html', + 'searchbox.html', + 'navigation.html', + 'relations.html', # needs 'show_related': True theme option to display + ] +} + + +# -- Options for HTMLHelp output ------------------------------------------ + +# Output file base name for HTML help builder. +htmlhelp_basename = 'algorithmsdoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'algorithms.tex', 'algorithms Documentation', + 'Algorithms Team \\& Contributors', 'manual'), +] + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'algorithms', 'algorithms Documentation', + [author], 1) +] + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'algorithms', 'algorithms Documentation', + author, 'algorithms', 'One line description of project.', + 'Miscellaneous'), +] diff --git a/docs/source/dfs.rst b/docs/source/dfs.rst new file mode 100644 index 000000000..1d2c5b6de --- /dev/null +++ b/docs/source/dfs.rst @@ -0,0 +1,5 @@ +.. role:: hidden + :class: hidden-section + +algorithms.dfs +================= diff --git a/docs/source/dp.rst b/docs/source/dp.rst new file mode 100644 index 000000000..1cc92081e --- /dev/null +++ b/docs/source/dp.rst @@ -0,0 +1,5 @@ +.. role:: hidden + :class: hidden-section + +algorithms.dp +================= diff --git a/docs/source/examples.rst b/docs/source/examples.rst new file mode 100644 index 000000000..5d3cbdd76 --- /dev/null +++ b/docs/source/examples.rst @@ -0,0 +1,5 @@ +.. role:: hidden + :class: hidden-section + +Examples +================= diff --git a/docs/source/graph.rst b/docs/source/graph.rst new file mode 100644 index 000000000..925d10524 --- /dev/null +++ b/docs/source/graph.rst @@ -0,0 +1,5 @@ +.. role:: hidden + :class: hidden-section + +algorithms.graph +================= diff --git a/docs/source/heap.rst b/docs/source/heap.rst new file mode 100644 index 000000000..068578a3e --- /dev/null +++ b/docs/source/heap.rst @@ -0,0 +1,5 @@ +.. role:: hidden + :class: hidden-section + +algorithms.heap +================= diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 000000000..d751ceea4 --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,38 @@ +.. image:: /_static/algorithms_logo.png + :target: https://github.com/keon/algorithms + :scale: 50 % + +The :mod:`algorithms` package consists of +minimal and clean example implementations of data structures and algorithms. + +.. toctree:: + :maxdepth: 2 + :caption: Package Reference + + self + algorithms.arrays + algorithms.backtrack + algorithms.bfs + algorithms.bit + algorithms.dfs + algorithms.dp + algorithms.graph + algorithms.heap + algorithms.linkedlist + algorithms.map + algorithms.maths + algorithms.matrix + algorithms.queues + algorithms.search + algorithms.set + algorithms.sort + algorithms.stack + algorithms.strings + algorithms.tree + examples + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` diff --git a/docs/source/linkedlist.rst b/docs/source/linkedlist.rst new file mode 100644 index 000000000..4a37b37e5 --- /dev/null +++ b/docs/source/linkedlist.rst @@ -0,0 +1,5 @@ +.. role:: hidden + :class: hidden-section + +algorithms.linkedlist +===================== diff --git a/docs/source/map.rst b/docs/source/map.rst new file mode 100644 index 000000000..31d281f85 --- /dev/null +++ b/docs/source/map.rst @@ -0,0 +1,5 @@ +.. role:: hidden + :class: hidden-section + +algorithms.map +================= diff --git a/docs/source/maths.rst b/docs/source/maths.rst new file mode 100644 index 000000000..1a45b7957 --- /dev/null +++ b/docs/source/maths.rst @@ -0,0 +1,5 @@ +.. role:: hidden + :class: hidden-section + +algorithms.maths +================= diff --git a/docs/source/matrix.rst b/docs/source/matrix.rst new file mode 100644 index 000000000..4d06e70a4 --- /dev/null +++ b/docs/source/matrix.rst @@ -0,0 +1,5 @@ +.. role:: hidden + :class: hidden-section + +algorithms.matrix +================= diff --git a/docs/source/queues.rst b/docs/source/queues.rst new file mode 100644 index 000000000..3ee9e18a7 --- /dev/null +++ b/docs/source/queues.rst @@ -0,0 +1,5 @@ +.. role:: hidden + :class: hidden-section + +algorithms.queue +================= diff --git a/docs/source/search.rst b/docs/source/search.rst new file mode 100644 index 000000000..091f0bf05 --- /dev/null +++ b/docs/source/search.rst @@ -0,0 +1,5 @@ +.. role:: hidden + :class: hidden-section + +algorithms.search +================= diff --git a/docs/source/set.rst b/docs/source/set.rst new file mode 100644 index 000000000..80984858b --- /dev/null +++ b/docs/source/set.rst @@ -0,0 +1,5 @@ +.. role:: hidden + :class: hidden-section + +algorithms.set +================= diff --git a/docs/source/sort.rst b/docs/source/sort.rst new file mode 100644 index 000000000..0b106e37b --- /dev/null +++ b/docs/source/sort.rst @@ -0,0 +1,5 @@ +.. role:: hidden + :class: hidden-section + +algorithms.sort +================= diff --git a/docs/source/stack.rst b/docs/source/stack.rst new file mode 100644 index 000000000..ad1f76525 --- /dev/null +++ b/docs/source/stack.rst @@ -0,0 +1,5 @@ +.. role:: hidden + :class: hidden-section + +algorithms.stack +================= diff --git a/docs/source/strings.rst b/docs/source/strings.rst new file mode 100644 index 000000000..708df2a0b --- /dev/null +++ b/docs/source/strings.rst @@ -0,0 +1,5 @@ +.. role:: hidden + :class: hidden-section + +algorithms.string +================= diff --git a/docs/source/tree.rst b/docs/source/tree.rst new file mode 100644 index 000000000..1dea821b4 --- /dev/null +++ b/docs/source/tree.rst @@ -0,0 +1,5 @@ +.. role:: hidden + :class: hidden-section + +algorithms.tree +================= diff --git a/graph/graph.py b/graph/graph.py deleted file mode 100644 index 3340226cc..000000000 --- a/graph/graph.py +++ /dev/null @@ -1,3 +0,0 @@ -class Graph: - def __init__(self, node, edges, source): - pass diff --git a/linkedlist/add_two_numbers.py b/linkedlist/add_two_numbers.py deleted file mode 100644 index 2f6d67513..000000000 --- a/linkedlist/add_two_numbers.py +++ /dev/null @@ -1,53 +0,0 @@ -""" -You are given two non-empty linked lists representing -two non-negative integers. The digits are stored in reverse order -and each of their nodes contain a single digit. -Add the two numbers and return it as a linked list. - -You may assume the two numbers do not contain any leading zero, -except the number 0 itself. - -Input: (2 -> 4 -> 3) + (5 -> 6 -> 4) -Output: 7 -> 0 -> 8 -""" - - -class Node: - def __init__(self, x): - self.val = x - self.next = None - - -def add_two_numbers(left:"Node", right:"Node")->"Node": - head = Node(0) - current = head - sum = 0 - while left or right: - print("adding: ", left.val, right.val) - sum //= 10 - if left: - sum += left.val - left = left.next - if right: - sum += right.val - right = right.next - current.next = Node(sum % 10) - current = current.next - if sum // 10 == 1: - current.next = Node(1) - return head.next - - -if __name__ == "__main__": - left = Node(2) - left.next = Node(4) - left.next.next = Node(3) - - right = Node(5) - right.next = Node(6) - right.next.next = Node(4) - - res = add_two_numbers(left, right) - while res: - print(res.val) - res = res.next diff --git a/linkedlist/copy_random_pointer.py b/linkedlist/copy_random_pointer.py deleted file mode 100644 index 7e2718bf5..000000000 --- a/linkedlist/copy_random_pointer.py +++ /dev/null @@ -1,36 +0,0 @@ -""" -A linked list is given such that each node contains an additional random -pointer which could point to any node in the list or null. - -Return a deep copy of the list. -""" - -class Solution: -# @param head, a RandomListNode -# @return a RandomListNode -def copyRandomList(self, head): - dic = dict() - m = n = head - while m: - dic[m] = RandomListNode(m.label) - m = m.next - while n: - dic[n].next = dic.get(n.next) - dic[n].random = dic.get(n.random) - n = n.next - return dic.get(head) - -#O(n) -class Solution: -# @param head, a RandomListNode -# @return a RandomListNode -def copyRandomList(self, head): - copy = collections.defaultdict(lambda: RandomListNode(0)) - copy[None] = None - node = head - while node: - copy[node].label = node.label - copy[node].next = copy[node.next] - copy[node].random = copy[node.random] - node = node.next - return copy[head] diff --git a/linkedlist/delete_node.py b/linkedlist/delete_node.py deleted file mode 100644 index 93e761fef..000000000 --- a/linkedlist/delete_node.py +++ /dev/null @@ -1,13 +0,0 @@ -""" -Write a function to delete a node (except the tail) -in a singly linked list, given only access to that node. - -Supposed the linked list is 1 -> 2 -> 3 -> 4 and -you are given the third node with value 3, -the linked list should become 1 -> 2 -> 4 after calling your function. -""" - - -def delete_node(node): - node.val = node.next.val - node.next = node.next.next diff --git a/linkedlist/first_cyclic_node.py b/linkedlist/first_cyclic_node.py deleted file mode 100644 index 48e66bc9e..000000000 --- a/linkedlist/first_cyclic_node.py +++ /dev/null @@ -1,8 +0,0 @@ -# find the first node of a cycle in the linked list. - -# 1 -> 2 -> 3 -> 4 -> 5 -> 1 => 1 -# A -> B -> C -> D -> E -> C => C - -def firstCyclicNode(): - pass - diff --git a/linkedlist/is_palindrome.py b/linkedlist/is_palindrome.py deleted file mode 100644 index b42b20727..000000000 --- a/linkedlist/is_palindrome.py +++ /dev/null @@ -1,48 +0,0 @@ -def is_palindrome(head): - if not head: - return True - # split the list to two parts - fast, slow = head.next, head - while fast and fast.next: - fast = fast.next.next - slow = slow.next - second = slow.next - slow.next = None # Don't forget here! But forget still works! - # reverse the second part - node = None - while second: - nxt = second.next - second.next = node - node = second - second = nxt - # compare two parts - # second part has the same or one less node - while node: - if node.val != head.val: - return False - node = node.next - head = head.next - return True - -def is_palindrome_stack(head): - if not head or not head.next: - return True - - # 1. Get the midpoint (slow) - slow = fast = cur = head - while fast and fast.next: - fast, slow = fast.next.next, slow.next - - # 2. Push the second half into the stack - stack = [slow.val] - while slow.next: - slow = slow.next - stack.append(slow.val) - - # 3. Comparison - while stack: - if stack.pop() != cur.val: - return False - cur = cur.next - - return True diff --git a/linkedlist/kth_to_last.py b/linkedlist/kth_to_last.py deleted file mode 100644 index 873bdccac..000000000 --- a/linkedlist/kth_to_last.py +++ /dev/null @@ -1,44 +0,0 @@ -class Node(): - def __init__(self, val = None): - self.val = val - self.next = None - -def printKthToLast(head): - """ - Time Complexity: O() - Space Complexity: O() - """ - pass - -def printLinkedList(head): - string = "" - while head.next: - string += head.val + " -> " - head = head.next - string += head.val - print(string) - -# A A B C D C F G - -a1 = Node("A") -a2 = Node("A") -b = Node("B") -c1 = Node("C") -d = Node("D") -c2 = Node("C") -f = Node("F") -g = Node("G") - -a1.next = a2 -a2.next = b -b.next = c1 -c1.next = d -d.next = c2 -c2.next = f -f.next = g - -# removeDups(a1) -# printLinkedList(a1) -# removeDupsWithoutSet(a1) -# printLinkedList(a1) - diff --git a/linkedlist/swap_in_pairs.py b/linkedlist/swap_in_pairs.py deleted file mode 100644 index ef0ed2eda..000000000 --- a/linkedlist/swap_in_pairs.py +++ /dev/null @@ -1,45 +0,0 @@ -""" -Given a linked list, swap every two adjacent nodes -and return its head. - -For example, -Given 1->2->3->4, you should return the list as 2->1->4->3. - -Your algorithm should use only constant space. -You may not modify the values in the list, -only nodes itself can be changed. -""" - - -class Node: - def __init__(self, x=0): - self.val = x - self.next = None - - -def swap_pairs(head:"Node")->"Node": - if not head: - return head - start = Node() - pre = start - pre.next = head - while pre.next and pre.next.next: - a = pre.next - b = pre.next.next - pre.next, a.next, b.next = b, b.next, a - pre = a - return start.next - - -if __name__ == "__main__": - n = Node(1) - n.next = Node(2) - n.next.next = Node(3) - n.next.next.next = Node(4) - res = swap_pairs(n) - - while res: - print(res.val, end=" ") - res = res.next - print("should be 2 1 4 3 ") - diff --git a/math/nth_digit.py b/math/nth_digit.py deleted file mode 100644 index ea2d6819e..000000000 --- a/math/nth_digit.py +++ /dev/null @@ -1,20 +0,0 @@ -""" -find nth digit -1. find the length of the number where the nth digit is from. -2. find the actual number where the nth digit is from -3. find the nth digit and return -""" - - -def find_nth_digit(n): - len = 1 - count = 9 - start = 1 - while n > len * count: - n -= len * count - len += 1 - count *= 10 - start *= 10 - start += (n-1) / len - s = str(start) - return int(s[(n-1) % len]) \ No newline at end of file diff --git a/math/prime_test.py b/math/prime_test.py deleted file mode 100644 index c3b795e4b..000000000 --- a/math/prime_test.py +++ /dev/null @@ -1,18 +0,0 @@ -''' -prime_test(n) returns a True if n is a prime number else it returns False -''' - - -def prime_test(n): - if n <= 1: - return False - if n==2 or n==3: - return True - if n%2==0 or n%3==0: - return False - j = 5 - while(j*j <= n): - if n%(j)==0 or n%(j+2)==0: - return False - j += 6 - return True \ No newline at end of file diff --git a/math/rabin_miller.py b/math/rabin_miller.py deleted file mode 100644 index b06db5c68..000000000 --- a/math/rabin_miller.py +++ /dev/null @@ -1,45 +0,0 @@ -import random,sys - -# factor n into a power of 2 times an odd number -def pow2_factor(n): - power = 0 - while n % 2 == 0: - n /= 2 - power += 1 - return power, n - -""" -Rabin-Miller primality test -returning False implies that n is guarenteed composite -returning True means that n is probably prime -with a 4 ** -k chance of being wrong -""" -def is_prime(n, k): - r, d = pow2_factor(n - 1) - - """ - returns true if a is a valid 'witness' for n - a valid witness increases chances of n being prime - an invalid witness guarentees n is composite - """ - def valid_witness(a): - x = pow(a, d, n) - - if x == 1 or x == n - 1: - return False - - for _ in range(r - 1): - x = pow(x, 2, n) - - if x == 1: - return True - if x == n - 1: - return False - - return True - - for _ in range(k): - if valid_witness(random.randrange(2, n - 2)): - return False - - return True \ No newline at end of file diff --git a/math/rsa.py b/math/rsa.py deleted file mode 100644 index f3b0f6866..000000000 --- a/math/rsa.py +++ /dev/null @@ -1,80 +0,0 @@ -""" -RSA encryption algorithm -a method for encrypting a number that uses seperate encryption and decryption keys -this file only implements the key generation algorithm - -there are three important numbers in RSA called n, e, and d -e is called the encryption exponent -d is called the decryption exponent -n is called the modulus - -these three numbers satisfy -((x ** e) ** d) % n == x % n - -to use this system for encryption, n and e are made publicly available, and d is kept secret -a number x can be encrypted by computing (x ** e) % n -the original number can then be recovered by computing (E ** d) % n, where E is -the encrypted number - -fortunately, python provides a three argument version of pow() that can compute powers modulo -a number very quickly: -(a ** b) % c == pow(a,b,c) -""" - -import random - -from rabin_miller import * # is_prime -from extended_gcd import * # extended_gcd - -""" -generate a prime with k bits -""" -def genprime(k): - while True: - n = random.randrange(2 ** (k - 1),2 ** k) - if is_prime(n,128): - return n - -""" -calculate the inverse of a mod m -that is, find b such that (a * b) % m == 1 -""" -def modinv(a, m): - x, y, g = extended_gcd(a,m) - return x % m - -""" -the RSA key generating algorithm -k is the number of bits in n -""" -def generate_key(k): - # size in bits of p and q need to add up to the size of n - p_size = k / 2 - q_size = k - p_size - - e = genprime(k) # in many cases, e is also chosen to be a small constant - - while True: - p = genprime(k / 2) - if p % e != 1: - break - - while True: - q = genprime(k - k / 2) - if q % e != 1: - break - - n = p * q - l = (p - 1) * (q - 1) # calculate totient function - d = modinv(e,l) - - return n, e, d - -""" -sample usage: -n,e,d = generate_key(1024) -data = 1337 -encrypted = pow(data,e,n) -decrypted = pow(encrypted,d,n) -assert decrypted == data -""" diff --git a/math/sqrt_precision_factor.py b/math/sqrt_precision_factor.py deleted file mode 100644 index ce4f0cf94..000000000 --- a/math/sqrt_precision_factor.py +++ /dev/null @@ -1,17 +0,0 @@ -""" -Given a positive integer N and a precision factor P, -write a square root function that produce an output -with a maximum error P from the actual square root of N. - -Example: -Given N = 5 and P = 0.001, can produce output O such that -2.235 < O > 2.237. Actual square root of 5 being 2.236. -""" - -def square_root(n,p): - guess = float(n) / 2 - - while abs(guess * guess - n) > p: - guess = (guess + (n / guess)) / 2 - - return guess diff --git a/matrix/bomb_enemy.py b/matrix/bomb_enemy.py deleted file mode 100644 index 0310c1197..000000000 --- a/matrix/bomb_enemy.py +++ /dev/null @@ -1,62 +0,0 @@ -# Given a 2D grid, each cell is either a wall 'W', -# an enemy 'E' or empty '0' (the number zero), -# return the maximum enemies you can kill using one bomb. -# The bomb kills all the enemies in the same row and column from -# the planted point until it hits the wall since the wall is too strong -# to be destroyed. -# Note that you can only put the bomb at an empty cell. - -# Example: -# For the given grid - -# 0 E 0 0 -# E 0 W E -# 0 E 0 0 - -# return 3. (Placing a bomb at (1,1) kills 3 enemies) - -def max_killed_enemies(grid): - if not grid: return 0 - m, n = len(grid), len(grid[0]) - max_killed = 0 - row_e, col_e = 0, [0] * n - for i in range(m): - for j in range(n): - if j == 0 or grid[i][j-1] == 'W': - row_e = row_kills(grid, i, j) - - if i == 0 or grid[i-1][j] == 'W': - col_e[j] = col_kills(grid, i, j) - - if grid[i][j] == '0': - max_killed = max(max_killed, row_e + col_e[j]) - - return max_killed - -# calculate killed enemies for row i from column j -def row_kills(grid, i, j): - num = 0 - while j < len(grid[0]) and grid[i][j] != 'W': - if grid[i][j] == 'E': - num += 1 - j += 1 - return num - -# calculate killed enemies for column j from row i -def col_kills(grid, i, j): - num = 0 - while i < len(grid) and grid[i][j] != 'W': - if grid[i][j] == 'E': - num += 1 - i += 1 - return num - -grid = [ -["0", "E", "0", "E"], -["E", "E", "E", "0"], -["E", "0", "W", "E"], -["0", "E", "0", "0"]] -print(grid) - -print(max_killed_enemies(grid)) - diff --git a/matrix/sparse_dot_vector.py b/matrix/sparse_dot_vector.py deleted file mode 100644 index 0cfceda7b..000000000 --- a/matrix/sparse_dot_vector.py +++ /dev/null @@ -1,12 +0,0 @@ -""" -Suppose we have very large sparse vectors, which contains a lot of zeros and double . - -find a data structure to store them -get the dot product of them - - -In this case, we first have to store the sparse vector using hash map. -for example [3,0,0,5,6] -> (0,3) (3,5) (4,6) The key is each element's position and the value is the number. - -Then we have two hash tables, and we have to iterate through them to calculate the dot product -""" diff --git a/matrix/sparse_mul.py b/matrix/sparse_mul.py deleted file mode 100644 index f87bcd27d..000000000 --- a/matrix/sparse_mul.py +++ /dev/null @@ -1,99 +0,0 @@ -""" -Given two sparse matrices A and B, return the result of AB. - -You may assume that A's column number is equal to B's row number. - -Example: - -A = [ - [ 1, 0, 0], - [-1, 0, 3] -] - -B = [ - [ 7, 0, 0 ], - [ 0, 0, 0 ], - [ 0, 0, 1 ] -] - - - | 1 0 0 | | 7 0 0 | | 7 0 0 | -AB = | -1 0 3 | x | 0 0 0 | = | -7 0 3 | - | 0 0 1 | -""" - - -# Python solution without table (~156ms): -def multiply(self, A, B): - """ - :type A: List[List[int]] - :type B: List[List[int]] - :rtype: List[List[int]] - """ - if A is None or B is None: return None - m, n, l = len(A), len(A[0]), len(B[0]) - if len(B) != n: - raise Exception("A's column number must be equal to B's row number.") - C = [[0 for _ in range(l)] for _ in range(m)] - for i, row in enumerate(A): - for k, eleA in enumerate(row): - if eleA: - for j, eleB in enumerate(B[k]): - if eleB: C[i][j] += eleA * eleB - return C - - -# Python solution with only one table for B (~196ms): -def multiply(self, A, B): - """ - :type A: List[List[int]] - :type B: List[List[int]] - :rtype: List[List[int]] - """ - if A is None or B is None: return None - m, n, l = len(A), len(A[0]), len(B[0]) - if len(B) != n: - raise Exception("A's column number must be equal to B's row number.") - C = [[0 for _ in range(l)] for _ in range(m)] - tableB = {} - for k, row in enumerate(B): - tableB[k] = {} - for j, eleB in enumerate(row): - if eleB: tableB[k][j] = eleB - for i, row in enumerate(A): - for k, eleA in enumerate(row): - if eleA: - for j, eleB in tableB[k].iteritems(): - C[i][j] += eleA * eleB - return C - -# Python solution with two tables (~196ms): -def multiply(self, A, B): - """ - :type A: List[List[int]] - :type B: List[List[int]] - :rtype: List[List[int]] - """ - if A is None or B is None: return None - m, n = len(A), len(A[0]) - if len(B) != n: - raise Exception("A's column number must be equal to B's row number.") - l = len(B[0]) - table_A, table_B = {}, {} - for i, row in enumerate(A): - for j, ele in enumerate(row): - if ele: - if i not in table_A: table_A[i] = {} - table_A[i][j] = ele - for i, row in enumerate(B): - for j, ele in enumerate(row): - if ele: - if i not in table_B: table_B[i] = {} - table_B[i][j] = ele - C = [[0 for j in range(l)] for i in range(m)] - for i in table_A: - for k in table_A[i]: - if k not in table_B: continue - for j in table_B[k]: - C[i][j] += table_A[i][k] * table_B[k][j] - return C diff --git a/queue/__init__.py b/queue/__init__.py deleted file mode 100644 index 2b5cb8ad8..000000000 --- a/queue/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .queue import * diff --git a/queue/max_sliding_window.py b/queue/max_sliding_window.py deleted file mode 100644 index 36157e301..000000000 --- a/queue/max_sliding_window.py +++ /dev/null @@ -1,34 +0,0 @@ -import collections - -# Keep indexes of good candidates in deque d. -# The indexes in d are from the current window, they're increasing, -# and their corresponding nums are decreasing. -# Then the first deque element is the index of the largest window value. - -# For each index i: - -# 1. Pop (from the end) indexes of smaller elements (they'll be useless). -# 2. Append the current index. -# 3. Pop (from the front) the index i - k, if it's still in the deque -# (it falls out of the window). -# 4. If our window has reached size k, -# append the current window maximum to the output. - - -def max_sliding_window(nums, k): - d = collections.deque() - out = [] - for i, n in enumerate(nums): - while d and nums[d[-1]] < n: - d.pop() - d += i, - if d[0] == i - k: - d.popleft() - if i >= k - 1: - out += nums[d[0]], - return out - - -array = [1,3,-1,-3,5,3,6,7] - -print(max_sliding_window(array)) diff --git a/queue/queue.py b/queue/queue.py deleted file mode 100644 index 28fc3d865..000000000 --- a/queue/queue.py +++ /dev/null @@ -1,118 +0,0 @@ -# Queue Abstract Data Type (ADT) -# * Queue() creates a new queue that is empty. -# It needs no parameters and returns an empty queue. -# * enqueue(item) adds a new item to the rear of the queue. -# It needs the item and returns nothing. -# * dequeue() removes the front item from the queue. -# It needs no parameters and returns the item. The queue is modified. -# * isEmpty() tests to see whether the queue is empty. -# It needs no parameters and returns a boolean value. -# * size() returns the number of items in the queue. -# It needs no parameters and returns an integer. - -class AbstractQueue: - def __init__(self): - self.top = 0 - - def isEmpty(self): - return self.top == 0 - - def __len__(self): - return self.top - - def __str__(self): - result = '------\n' - for element in self: - result += str(element) + '\n' - return result[:-1] + '\n------' - -class ArrayQueue(AbstractQueue): - def __init__(self, size=10): - """ - Initialize python List with size of 10 or user given input. - Python List type is a dynamic array, so we have to restrict its - dynamic nature to make it work like a static array. - """ - AbstractQueue.__init__(self) - self.array = [None] * size - self.front = 0 - self.rear = 0 - - def enqueue(self, value): - if self.rear == len(self.array): - self.expand() - self.array[self.rear] = value - self.rear += 1 - self.top += 1 - - def dequeue(self): - if self.isEmpty(): - raise IndexError("Queue is empty") - value = self.array[self.front] - self.array[self.front] = None - self.front -= 1 - self.top -= 1 - return value - - def expand(self): - """ - expands size of the array. - Time Complexity: O(n) - """ - new_array = [None] * len(self.array) * 2 # double the size of the array - for i, element in enumerate(self.array): - new_array[i] = element - self.array = new_array - - def __iter__(self): - probe = self.rear - while True: - if probe < 0: - raise StopIteration - yield self.array[probe] - probe -= 1 - -class QueueNode(object): - def __init__(self, value): - self.value = value - self.next = None - -class LinkedListQueue(AbstractQueue): - def __init__(self): - AbstractQueue.__init__(self) - self.front = None - self.rear = None - - def enqueue(self, value): - node = QueueNode(value) - if not self.front: - self.front = node - self.rear = node - else: - self.rear.next = node - self.rear = node - self.top += 1 - - def dequeue(self): - if self.isEmpty(): - raise IndexError("Queue is empty") - value = self.front.value - if self.front is self.rear: - self.front = None - self.rear = None - else: - self.front = self.front.next - self.top -= 1 - return value - - def __iter__(self): - probe = self.rear - while True: - if probe is None: - raise StopIteration - yield probe.value - probe = probe.next - -class HeapPriorityQueue(AbstractQueue): - def __init__(self): - pass diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..e69de29bb diff --git a/search/binary_search.py b/search/binary_search.py deleted file mode 100644 index 71d24bab4..000000000 --- a/search/binary_search.py +++ /dev/null @@ -1,37 +0,0 @@ -# -# Binary search works for a sorted array. -# Note: The code logic is written for an array sorted in -# increasing order. -# T(n): O(log n) -# - - -def binary_search(array, query): - lo, hi = 0, len(array) - 1 - while lo <= hi: - mid = lo + (hi - lo) // 2 - val = array[mid] - if val == query: - return mid - elif val < query: - lo = mid + 1 - else: - hi = mid - 1 - return None - - -def main(): - array = [1, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 6] - print(array) - print("-----SEARCH-----") - print("found: ", 5, " in index:", binary_search(array, 5)) - print("-----SEARCH-----") - print("found: ", 6, " in index:", binary_search(array, 6)) - print("-----SEARCH-----") - print("found: ", 7, " in index:", binary_search(array, 7)) - print("-----SEARCH-----") - print("found: ", -1, " in index:", binary_search(array, -1)) - print("-----SEARCH-----") - -if __name__ == "__main__": - main() diff --git a/search/first_occurance.py b/search/first_occurance.py deleted file mode 100644 index 3ac6eb025..000000000 --- a/search/first_occurance.py +++ /dev/null @@ -1,45 +0,0 @@ -# -# Find first occurance of a number in a sorted array (increasing order) -# Approach- Binary Search -# T(n)- O(log n) -# - - -def firstOccurance(array, query): - lo, hi = 0, len(array) - 1 - while lo <= hi: - mid = lo + (hi-lo) // 2 - print("lo: ", lo, " hi: ", hi, " mid: ", mid) - if (mid == 0 and array[mid] == query) or \ - (array[mid] == query and array[mid-1] < query): - return mid - elif array[mid] <= query: - lo = mid + 1 - else: - hi = mid - 1 - - -def main(): - array = [1, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 6, 6, 6] - print(array) - print("-----SEARCH-----") - query = 3 - print("found first: ", query, " in index:", firstOccurance(array, query)) - print("-----SEARCH-----") - query = 5 - print("found first: ", query, " in index:", firstOccurance(array, query)) - print("-----SEARCH-----") - query = 7 - print("found first: ", query, " in index:", firstOccurance(array, query)) - print("-----SEARCH-----") - query = 1 - print("found first: ", query, " in index:", firstOccurance(array, query)) - print("-----SEARCH-----") - query = -1 - print("found first: ", query, " in index:", firstOccurance(array, query)) - print("-----SEARCH-----") - query = 9 - print("found first: ", query, " in index:", firstOccurance(array, query)) - -if __name__ == "__main__": - main() diff --git a/search/last_occurance.py b/search/last_occurance.py deleted file mode 100644 index 0c2c34095..000000000 --- a/search/last_occurance.py +++ /dev/null @@ -1,47 +0,0 @@ -# -# Find last occurance of a number in a sorted array (increasing order) -# Approach- Binary Search -# T(n)- O(log n) -# - - -def lastOccurance(array, query): - lo, hi = 0, len(array) - 1 - while lo <= hi: - mid = lo + (hi - lo) // 2 - if (array[mid] == query and mid == len(array)-1) or \ - (array[mid] == query and array[mid+1] > query): - return mid - elif (array[mid] <= query): - lo = mid + 1 - else: - hi = mid - 1 - - -def main(): - array = [1, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 6, 6, 6] - print(array) - print("-----SEARCH-----") - query = 3 - print("found last: ", query, " in index:", lastOccurance(array, query)) - print("-----SEARCH-----") - query = 5 - print("found last: ", query, " in index:", lastOccurance(array, query)) - print("-----SEARCH-----") - query = 7 - print("found last: ", query, " in index:", lastOccurance(array, query)) - print("-----SEARCH-----") - query = 1 - print("found last: ", query, " in index:", lastOccurance(array, query)) - print("-----SEARCH-----") - query = -1 - print("found last: ", query, " in index:", lastOccurance(array, query)) - print("-----SEARCH-----") - query = 9 - print("found last: ", query, " in index:", lastOccurance(array, query)) - print("-----SEARCH-----") - query = 6 - print("found last: ", query, " in index:", lastOccurance(array, query)) - -if __name__ == "__main__": - main() diff --git a/set/randomized_set.py b/set/randomized_set.py deleted file mode 100644 index 5f66c90b8..000000000 --- a/set/randomized_set.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -Design a data structure that supports all following operations -in average O(1) time. - -insert(val): Inserts an item val to the set if not already present. -remove(val): Removes an item val from the set if present. -get_random: Returns a random element from current set of elements. - Each element must have the same probability of being returned. -""" - - -class RandomizedSet(): - """ - idea: - shit - """ - def __init__(self): - pass - - def insert(self, val): - pass - - def remove(self, val): - pass - - def get_random(self): - pass - - -if __name__ == "__main__": - rset = RandomizedSet() - rset.insert(1) - rset.insert(2) - rset.insert(3) - - rset.remove(2) - - print(rset.get_random()) - print(rset.get_random()) - print(rset.get_random()) diff --git a/setup.py b/setup.py new file mode 100644 index 000000000..f08f24f14 --- /dev/null +++ b/setup.py @@ -0,0 +1,29 @@ +import os +import io +import re +from setuptools import find_packages, setup + + +def long_description(): + with io.open('README.md', 'r', encoding='utf-8') as f: + readme = f.read() + return readme + + +setup(name='algorithms', + version='0.1.0', + description='Pythonic Data Structures and Algorithms', + long_description=long_description(), + long_description_content_type="text/markdown", + url='https://github.com/keon/algorithms', + author='Algorithms Team & Contributors', + author_email="kwk236@gmail.com", + license='MIT', + packages=find_packages(), + classifiers=[ + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + ], + zip_safe=False) diff --git a/sort/bubble_sort.py b/sort/bubble_sort.py deleted file mode 100644 index 9b5e23ee9..000000000 --- a/sort/bubble_sort.py +++ /dev/null @@ -1,28 +0,0 @@ -""" - -https://en.wikipedia.org/wiki/Bubble_sort - -Worst-case performance: O(N^2) - -""" - - -def bubble_sort(arr): - - def swap(i, j): - arr[i], arr[j] = arr[j], arr[i] - - n = len(arr) - swapped = True - while swapped: - swapped = False - for i in range(1, n): - if arr[i - 1] > arr[i]: - swap(i - 1, i) - swapped = True - -array = [1, 5, 65, 23, 57, 1232, -1, -5, -2, 242, 100, - 4, 423, 2, 564, 9, 0, 10, 43, 64, 32, 1, 999] -print(array) -bubble_sort(array) -print(array) diff --git a/sort/counting_sort.py b/sort/counting_sort.py deleted file mode 100644 index d43bdffbe..000000000 --- a/sort/counting_sort.py +++ /dev/null @@ -1,46 +0,0 @@ -def counting_sort(arr): - """ - Counting_sort - Sorting a array which has no element greater than k - Creating a new temp_arr,where temp_arr[i] contain the number of - element less than or equal to i in the arr - Then placing the number i into a correct position in the result_arr - return the result_arr - Complexity: 0(n) - """ - - m = min(arr) - #in case there are negative elements, change the array to all positive element - different = 0 - if m < 0: - #save the change, so that we can convert the array back to all positive number - different = -m - for i in range (len(arr)): - arr[i]+= -m - k = max(arr) - temp_arr = [0]*(k+1) - for i in range(0,len(arr)): - temp_arr[arr[i]] = temp_arr[arr[i]]+1 - #temp_array[i] contain the times the number i appear in arr - - for i in range(1, k+1): - temp_arr[i] = temp_arr[i] + temp_arr[i-1] - #temp_array[i] contain the number of element less than or equal i in arr - - result_arr = [0]*len(arr) - #creating a result_arr an put the element in a correct positon - for i in range(len(arr)-1,-1,-1): - result_arr[temp_arr[arr[i]]-1] = arr[i]-different - temp_arr[arr[i]] = temp_arr[arr[i]]-1 - - return result_arr - -if __name__ == "__main__": - positive_array = [1,2,3,4,9,1,2,8,3,5,7,0,9,8,1,7,4,5] - negative_array = [-5,-6,-2,-3,-4,-5,0,-9,-2,-3,-8,-4] - x = counting_sort(positive_array) - y = counting_sort(negative_array) - print(x) - print(y) - - diff --git a/sort/heap_sort.py b/sort/heap_sort.py deleted file mode 100644 index 348964bc7..000000000 --- a/sort/heap_sort.py +++ /dev/null @@ -1,93 +0,0 @@ -def max_heap_sort(arr): - """ Heap Sort that uses a max heap to sort an array in ascending order - Complexity: O(n log(n)) - """ - for i in range(len(arr)-1,0,-1): - max_heapify(arr, i) - - temp = arr[0] - arr[0] = arr[i] - arr[i] = temp - - -def max_heapify(arr, end): - """ Max heapify helper for max_heap_sort - """ - last_parent = int((end-1)/2) - - # Iterate from last parent to first - for parent in range(last_parent,-1,-1): - current_parent = parent - - # Iterate from current_parent to last_parent - while current_parent <= last_parent: - # Find greatest child of current_parent - child = 2*current_parent + 1 - if child + 1 <= end and arr[child] < arr[child+1]: - child = child + 1 - - # Swap if child is greater than parent - if arr[child] > arr[current_parent]: - temp = arr[current_parent] - arr[current_parent] = arr[child] - arr[child] = temp - - current_parent = child - # If no swap occured, no need to keep iterating - else: - break - - -def min_heap_sort(arr): - """ Heap Sort that uses a min heap to sort an array in ascending order - Complexity: O(n log(n)) - """ - for i in range(0, len(arr)-1): - min_heapify(arr, i) - - -def min_heapify(arr, start): - """ Min heapify helper for min_heap_sort - """ - # Offset last_parent by the start (last_parent calculated as if start index was 0) - # All array accesses need to be offet by start - end = len(arr)-1 - last_parent = int((end-start-1)/2) - - # Iterate from last parent to first - for parent in range(last_parent,-1,-1): - current_parent = parent - - # Iterate from current_parent to last_parent - while current_parent <= last_parent: - # Find lesser child of current_parent - child = 2*current_parent + 1 - if child + 1 <= end-start and arr[child+start] > arr[child+1+start]: - child = child + 1 - - # Swap if child is less than parent - if arr[child+start] < arr[current_parent+start]: - temp = arr[current_parent+start] - arr[current_parent+start] = arr[child+start] - arr[child+start] = temp - - current_parent = child - # If no swap occured, no need to keep iterating - else: - break - -if __name__ == '__main__': - import timeit - - array = [1,5,65,23,57,1232,-1,-5,-2,242,100,4,423,2,564,9,0,10,43,64] - print("array:") - print(array) - print("Max Heapify:") - max_heap_sort(array) - print(array) - array = [1,5,65,23,57,1232,-1,-5,-2,242,100,4,423,2,564,9,0,10,43,64] - print("Min Heapify:") - min_heap_sort(array) - print(array) - print("Max Heapify Time:", timeit.timeit('max_heap_sort(array)', setup="from __main__ import max_heap_sort, array",number=10000)) - print("Min Heapify Time:", timeit.timeit('min_heap_sort(array)', setup="from __main__ import min_heap_sort, array",number=10000)) diff --git a/sort/insertion_sort.py b/sort/insertion_sort.py deleted file mode 100644 index 99bd2058c..000000000 --- a/sort/insertion_sort.py +++ /dev/null @@ -1,14 +0,0 @@ -def insertion_sort(arr): - """ Insertion Sort - Complexity: O(n^2) - """ - for i in xrange(len(arr)): - cursor = arr[i] - pos = i - while pos > 0 and arr[pos-1] > cursor: - # Swap the number down the list - arr[pos] = arr[pos-1] - pos = pos-1 - # Break and do the final swap - arr[pos] = cursor - return arr diff --git a/sort/merge_sort.py b/sort/merge_sort.py deleted file mode 100644 index e2f2d2a27..000000000 --- a/sort/merge_sort.py +++ /dev/null @@ -1,40 +0,0 @@ -def merge_sort(arr): - """ Merge Sort - Complexity: O(n log(n)) - """ - # Our recursive base case - if len(arr)<= 1: - return arr - mid = len(arr)/2 - # Perform merge_sort recursively on both halves - left, right = merge_sort(arr[mid:]), merge_sort(arr[:mid]) - - # Merge each side together - return merge(left, right) - -def merge(left, right): - """ Merge helper - Complexity: O(n) - """ - arr = [] - left_cursor, right_cursor = 0,0 - while left_cursor < len(left) and right_cursor < len(right): - # Sort each one and place into the result - if left[left_cursor] <= right[right_cursor]: - arr.append(left[left_cursor]) - left_cursor+=1 - else: - arr.append(right[right_cursor]) - right_cursor+=1 - # Add the left overs if there's any left to the result - for i in range(left_cursor,len(left)): - arr.append(left[i]) - for i in range(right_cursor,len(right)): - arr.append(right[i]) - - # Return result - return arr - -array = [1,5, 7,4,3,2,1,9,0,10,43,64] -print(array) -print(merge_sort(array)) diff --git a/sort/quick_sort.py b/sort/quick_sort.py deleted file mode 100644 index 2fd38e10d..000000000 --- a/sort/quick_sort.py +++ /dev/null @@ -1,25 +0,0 @@ -def quick_sort(arr, first, last): - """ Quicksort - Complexity: best O(n) avg O(n log(n)), worst O(N^2) - """ - if first < last: - pos = partition(arr, first, last) - print(arr[first:pos-1], arr[pos+1:last]) - # Start our two recursive calls - quick_sort(arr, first, pos-1) - quick_sort(arr, pos+1, last) - -def partition(arr, first, last): - wall = first - for pos in range(first, last): - if arr[pos] < arr[last]: # last is the pivot - arr[pos], arr[wall] = arr[wall], arr[pos] - wall += 1 - arr[wall], arr[last] = arr[last], arr[wall] - print(wall) - return wall - -array = [1,5,65,23,57,1232,-1,-5,-2,242,100,4,423,2,564,9,0,10,43,64] -print(array) -quick_sort(array, 0, len(array)-1) -print(array) diff --git a/sort/selection_sort.py b/sort/selection_sort.py deleted file mode 100644 index acb0e30da..000000000 --- a/sort/selection_sort.py +++ /dev/null @@ -1,16 +0,0 @@ - -def selection_sort(arr): - """ Selection Sort - Complexity: O(n^2) - """ - for i in xrange(len(arr)): - minimum = i - for j in xrange(i+1, len(arr)): - # "Select" the correct value - if arr[j] < arr[minimum]: - minimum = j - # Using a pythonic swap - arr[minimum], arr[i] = arr[i], arr[minimum] - return arr - - diff --git a/sort/topsort.py b/sort/topsort.py deleted file mode 100644 index 76cd43c11..000000000 --- a/sort/topsort.py +++ /dev/null @@ -1,58 +0,0 @@ -""" -Given a list of system packages, -some packages cannot be installed until the other packages are installed. -Provide a valid sequence to install all of the packages. - -e.g. -a relies on b -b relies on c - -then a valid sequence is [c, b, a] -""" - -depGraph = { - - "a" : [ "b" ], - "b" : [ "c" ], - "c" : [ 'e'], - 'e' : [ ], - "d" : [ ], - "f" : ["e" , "d"] -} - - -given = [ "b", "c", "a", "d", "e", "f" ] - -def retDeps(visited, start): - queue = [] - out = [] - queue.append(start) - while queue: - newNode = queue.pop(0) - if newNode not in visited: - visited.add(newNode) - for child in depGraph[newNode]: - queue.append(child) - out.append(child) - out.append(start) - return out - - -def retDepGraph(): - visited = set() - out = [] - # visited.add(given[0]) - for pac in given: - if pac in visited: - continue - visited.add(pac) - #out.append(pac) - if pac in depGraph: - # find all children - for child in depGraph[pac]: - if child in visited: - continue - out.extend(retDeps(visited, child)) - out.append(pac) - print(out) -retDepGraph() diff --git a/stack/__init__.py b/stack/__init__.py deleted file mode 100644 index 843a7582a..000000000 --- a/stack/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .stack import * diff --git a/stack/stack.py b/stack/stack.py deleted file mode 100644 index d79936379..000000000 --- a/stack/stack.py +++ /dev/null @@ -1,113 +0,0 @@ -# Stack Abstract Data Type (ADT) -# Stack() creates a new stack that is empty. -# It needs no parameters and returns an empty stack. -# push(item) adds a new item to the top of the stack. -# It needs the item and returns nothing. -# pop() removes the top item from the stack. -# It needs no parameters and returns the item. The stack is modified. -# peek() returns the top item from the stack but does not remove it. -# It needs no parameters. The stack is not modified. -# isEmpty() tests to see whether the stack is empty. -# It needs no parameters and returns a boolean value. -# size() returns the number of items on the stack. -# It needs no parameters and returns an integer. - -class AbstractStack: - def __init__(self): - self.top = 0 - - def isEmpty(self): - return self.top == 0 - - def __len__(self): - return self.top - - def __str__(self): - result = '------\n' - for element in self: - result += str(element) + '\n' - return result[:-1] + '\n------' - -class ArrayStack(AbstractStack): - def __init__(self, size=10): - """ - Initialize python List with size of 10 or user given input. - Python List type is a dynamic array, so we have to restrict its - dynamic nature to make it work like a static array. - """ - AbstractStack.__init__(self) - self.array = [None] * size - - def push(self, value): - if self.top == len(self.array): - self.expand() - self.array[self.top] = value - self.top += 1 - - def pop(self): - if self.isEmpty(): - raise IndexError("stack is empty") - value = self.array[self.top - 1] - self.array[self.top - 1] = None - self.top -= 1 - return value - - def peek(self): - if self.isEmpty(): - raise IndexError("stack is empty") - return self.array[self.top] - - def expand(self): - """ - expands size of the array. - Time Complexity: O(n) - """ - newArray = [None] * len(self.array) * 2 # double the size of the array - for i, element in enumerate(self.array): - newArray[i] = element - self.array = newArray - - def __iter__(self): - probe = self.top - 1 - while True: - if probe < 0: - raise StopIteration - yield self.array[probe] - probe -= 1 - -class StackNode(object): - def __init__(self, value): - self.value = value - self.next = None - -class LinkedListStack(AbstractStack): - def __init__(self): - AbstractStack.__init__(self) - self.head = None - - def push(self, value): - node = StackNode(value) - node.next = self.head - self.head = node - self.top += 1 - - def pop(self): - if self.isEmpty(): - raise IndexError("stack is empty") - value = self.head.value - self.head = self.head.next - self.top -= 1 - return value - - def peek(self): - if self.isEmpty(): - raise IndexError("stack is empty") - return self.head.value - - def __iter__(self): - probe = self.head - while True: - if probe is None: - raise StopIteration - yield probe.value - probe = probe.next diff --git a/stack/valid_parenthesis.py b/stack/valid_parenthesis.py deleted file mode 100644 index 7b89734c3..000000000 --- a/stack/valid_parenthesis.py +++ /dev/null @@ -1,38 +0,0 @@ -""" -Given a string containing just the characters -'(', ')', '{', '}', '[' and ']', -determine if the input string is valid. - -The brackets must close in the correct order, -"()" and "()[]{}" are all valid but "(]" and "([)]" are not. -""" - - -def is_valid(s:"str")->"bool": - stack = [] - dic = { ")":"(", - "}":"{", - "]":"["} - for char in s: - if char in dic.values(): - stack.append(char) - elif char in dic.keys(): - if stack == []: - return False - s = stack.pop() - if dic[char] != s: - return False - return stack == [] - - -if __name__ == "__main__": - paren = "[]" - print(paren, is_valid(paren)) - paren = "[]()[]" - print(paren, is_valid(paren)) - paren = "[[[]]" - print(paren, is_valid(paren)) - paren = "{([])}" - print(paren, is_valid(paren)) - paren = "(}" - print(paren, is_valid(paren)) diff --git a/string/breaking_bad.py b/string/breaking_bad.py deleted file mode 100644 index 3248d9108..000000000 --- a/string/breaking_bad.py +++ /dev/null @@ -1,82 +0,0 @@ -""" -Given an api which returns an array of chemical names and an array of chemical -symbols, display the chemical names with their symbol surrounded by square -brackets: - -Ex: -Chemicals array: ['Amazon', 'Microsoft', 'Google'] -Symbols: ['I', 'Am', 'cro', 'Na', 'le', 'abc'] - -Output: -[Am]azon, Mi[cro]soft, Goog[le] - -If the chemical string matches more than one symbol, then choose the one with -longest length. (ex. 'Microsoft' matches 'i' and 'cro') - -My solution: -(I sorted the symbols array in descending order of length and ran loop over -chemicals array to find a symbol match(using indexOf in javascript) which -worked. But I din't make it through the interview, I am guessing my solution -was O(n2) and they expected an efficient algorithm. -""" - -chemicals = ['Amazon', 'Microsoft', 'Google'] -symbols = ['I', 'Am', 'cro', 'le', 'abc'] - -def match_symbol(chemicals, symbols): - import re - combined = [] - - for s in symbols: - for c in chemicals: - r = re.search(s, c) - if r: - combined.append(re.sub(s, "[{}]".format(s), c)) - - return combined - - -print match_symbol(chemicals, symbols) - -""" -One approach is to use a Trie for the dictionary (the symbols), and then match -brute force. The complexity will depend on the dictionary; -if all are suffixes of the other, it will be n*m -(where m is the size of the dictionary). For example, in Python: -""" - -from functools import reduce - -class TrieNode: - def __init__(self): - self.c = dict() - self.sym = None - -def bracket(words, symbols): - root = TrieNode() - for s in symbols: - t = root - for char in s: - if char not in t.c: - t.c[char] = TrieNode() - t = t.c[char] - t.sym = s - result = dict() - for word in words: - i = 0 - symlist = list() - while i < len(word): - j, t = i, root - while j < len(word) and word[j] in t.c: - t = t.c[word[j]] - if t.sym is not None: - symlist.append((j+1-len(t.sym), j+1, t.sym)) - j += 1 - i += 1 - if len(symlist) > 0: - sym = reduce(lambda x, y: x if x[1]-x[0] >= y[1]-y[0] else y, symlist) - result[word] = "{}[{}]{}".format(word[:sym[0]], sym[2], word[sym[1]:]) - return tuple(word if word not in result else result[word] for word in words) - -bracket(['amazon', 'microsoft', 'google'], ['i', 'am', 'cro', 'na', 'le', 'abc']) ->>> ('[am]azon', 'mi[cro]soft', 'goog[le]') diff --git a/string/encode_decode.py b/string/encode_decode.py deleted file mode 100644 index 8fecf3642..000000000 --- a/string/encode_decode.py +++ /dev/null @@ -1,55 +0,0 @@ -# Design an algorithm to encode a list of strings to a string. -# The encoded string is then sent over the network and is decoded -# back to the original list of strings. - -# Machine 1 (sender) has the function: - -# string encode(vector strs) { - # // ... your code - # return encoded_string; -# } -# Machine 2 (receiver) has the function: -# vector decode(string s) { - # //... your code - # return strs; -# } -# So Machine 1 does: - -# string encoded_string = encode(strs); -# and Machine 2 does: - -# vector strs2 = decode(encoded_string); -# strs2 in Machine 2 should be the same as strs in Machine 1. - -# Implement the encode and decode methods. - -def encode(strs): - """Encodes a list of strings to a single string. - :type strs: List[str] - :rtype: str - """ - res = '' - for string in strs.split(): - res += str(len(string)) + ":" + string - return res - -def decode(s): - """Decodes a single string to a list of strings. - :type s: str - :rtype: List[str] - """ - strs = [] - i = 0 - while i < len(s): - index = s.find(":", i) - size = int(s[i:index]) - strs.append(s[index+1: index+1+size]) - i = index+1+size - return strs - -strs = "keon is awesome" -print(strs) -enc = encode(strs) -print(enc) -dec = decode(enc) -print(dec) diff --git a/string/group_anagrams.py b/string/group_anagrams.py deleted file mode 100644 index 1bf3c0a45..000000000 --- a/string/group_anagrams.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -Given an array of strings, group anagrams together. - -For example, given: ["eat", "tea", "tan", "ate", "nat", "bat"], -Return: - -[ - ["ate", "eat","tea"], - ["nat","tan"], - ["bat"] -] -""" - - -class Solution(object): - def groupAnagrams(self, strs): - d = {} - ans = [] - k = 0 - for str in strs: - sstr = ''.join(sorted(str)) - if sstr not in d: - d[sstr] = k - k = k+1 - ans.append([]) - ans[-1].append(str) - else: - ans[d[sstr]].append(str) - return ans diff --git a/string/int_to_roman.py b/string/int_to_roman.py deleted file mode 100644 index dd2563783..000000000 --- a/string/int_to_roman.py +++ /dev/null @@ -1,17 +0,0 @@ -""" -Given an integer, convert it to a roman numeral. -Input is guaranteed to be within the range from 1 to 3999. -""" - -def int_to_roman(num): - """ - :type num: int - :rtype: str - """ - M = ["", "M", "MM", "MMM"]; - C = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"]; - X = ["", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"]; - I = ["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"]; - return M[num//1000] + C[(num%1000)//100] + X[(num%100)//10] + I[num%10]; - -print(int_to_roman(644)) diff --git a/string/is_palindrome.py b/string/is_palindrome.py deleted file mode 100644 index d9b23b653..000000000 --- a/string/is_palindrome.py +++ /dev/null @@ -1,33 +0,0 @@ -""" -Given a string, determine if it is a palindrome, -considering only alphanumeric characters and ignoring cases. - -For example, -"A man, a plan, a canal: Panama" is a palindrome. -"race a car" is not a palindrome. - -Note: -Have you consider that the string might be empty? -This is a good question to ask during an interview. - -For the purpose of this problem, -we define empty string as valid palindrome. -""" - - -def is_palindrome(s): - """ - :type s: str - :rtype: bool - """ - i = 0 - j = len(s)-1 - while i < j: - while i < j and not s[i].isalnum(): - i += 1 - while i < j and not s[j].isalnum(): - j -= 1 - if s[i].lower() != s[j].lower(): - return False - i, j = i+1, j-1 - return True diff --git a/string/license_number.py b/string/license_number.py deleted file mode 100644 index cbcb2d966..000000000 --- a/string/license_number.py +++ /dev/null @@ -1,19 +0,0 @@ - -def license_number(key, K): - res, alnum = [], [] - for char in key: - if char != "-": - alnum.append(char) - for i, char in enumerate(reversed(alnum)): - res.append(char) - if (i+1) % K == 0 and i != len(alnum)-1: - res.append("-") - return "".join(res[::-1]) - - -print(license_number("a-bc-dfd-df", 1), 1) -print(license_number("a-bc-dfd-df", 2), 2) -print(license_number("a-bc-dfd-df", 3), 3) -print(license_number("a-bc-dfd-df", 4), 4) -print(license_number("a-bc-dfd-df", 5), 5) - diff --git a/string/reverse_words.py b/string/reverse_words.py deleted file mode 100644 index c1a48ace2..000000000 --- a/string/reverse_words.py +++ /dev/null @@ -1,31 +0,0 @@ - -def reverse(array, i, j): - while i < j: - array[i], array[j] = array[j], array[i] - i += 1 - j -= 1 - -def reverse_words(string): - arr = list(string) - n = len(arr) - reverse(arr, 0, n-1) - - start = None - for i in range(n): - if arr[i] == " ": - if start is not None: - reverse(arr, start, i-1) - start = None - elif i == n-1: - if start is not None: - reverse(arr, start, i) - else: - if start is None: - start = i - return "".join(arr) - - -if __name__ == "__main__": - test = "I am keon kim and I like pizza" - print(test) - print(reverse_words(test)) diff --git a/test_requirements.txt b/test_requirements.txt new file mode 100644 index 000000000..7a3e47c8a --- /dev/null +++ b/test_requirements.txt @@ -0,0 +1,6 @@ +flake8 +python-coveralls +coverage +nose +pytest +tox diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/test_array.py b/tests/test_array.py new file mode 100644 index 000000000..47e24347a --- /dev/null +++ b/tests/test_array.py @@ -0,0 +1,362 @@ +from algorithms.arrays import ( + delete_nth, delete_nth_naive, + flatten_iter, flatten, + garage, + josephus, + longest_non_repeat_v1, longest_non_repeat_v2, + Interval, merge_intervals, + missing_ranges, + move_zeros, + plus_one_v1, plus_one_v2, plus_one_v3, + rotate_v1, rotate_v2, rotate_v3, + summarize_ranges, + three_sum, + two_sum, + max_ones_index, + trimmean, + top_1, + limit, + n_sum +) + +import unittest + + +class TestJosephus(unittest.TestCase): + + def test_josephus(self): + + a = ['1', '2', '3', '4', '5', '6', '7', '8', '9'] + josephus_generator = josephus(a, 3) + self.assertEqual(next(josephus_generator), '3') + self.assertEqual(next(josephus_generator), '6') + self.assertEqual(next(josephus_generator), '9') + self.assertEqual(next(josephus_generator), '4') + self.assertEqual(next(josephus_generator), '8') + self.assertEqual(next(josephus_generator), '5') + self.assertEqual(next(josephus_generator), '2') + self.assertEqual(next(josephus_generator), '7') + self.assertEqual(next(josephus_generator), '1') + self.assertRaises(StopIteration, next, josephus_generator) + + +class TestDeleteNth(unittest.TestCase): + + def test_delete_nth_naive(self): + + self.assertListEqual(delete_nth_naive( + [20, 37, 20, 21, 37, 21, 21], n=1), + [20, 37, 21]) + self.assertListEqual(delete_nth_naive( + [1, 1, 3, 3, 7, 2, 2, 2, 2], n=3), + [1, 1, 3, 3, 7, 2, 2, 2]) + self.assertListEqual(delete_nth_naive( + [1, 2, 3, 1, 1, 2, 1, 2, 3, 3, 2, 4, 5, 3, 1], n=3), + [1, 2, 3, 1, 1, 2, 2, 3, 3, 4, 5]) + self.assertListEqual(delete_nth_naive([], n=5), + []) + self.assertListEqual(delete_nth_naive( + [1, 2, 3, 1, 1, 2, 1, 2, 3, 3, 2, 4, 5, 3, 1], n=0), + []) + + def test_delete_nth(self): + + self.assertListEqual(delete_nth([20, 37, 20, 21, 37, 21, 21], n=1), + [20, 37, 21]) + self.assertListEqual(delete_nth([1, 1, 3, 3, 7, 2, 2, 2, 2], n=3), + [1, 1, 3, 3, 7, 2, 2, 2]) + self.assertListEqual(delete_nth([1, 2, 3, 1, 1, 2, 1, 2, 3, 3, 2, 4, 5, 3, 1], n=3), + [1, 2, 3, 1, 1, 2, 2, 3, 3, 4, 5]) + self.assertListEqual(delete_nth([], n=5), + []) + self.assertListEqual(delete_nth([1, 2, 3, 1, 1, 2, 1, 2, 3, 3, 2, 4, 5, 3, 1], n=0), + []) + + +class TestFlatten(unittest.TestCase): + + def test_flatten(self): + + nested_list = [2, 1, [3, [4, 5], 6], 7, [8]] + flattened = flatten(nested_list) + self.assertEqual(flattened, [2, 1, 3, 4, 5, 6, 7, 8]) + + nested_list = [[3, [4, 5], 6], 7, [8]] + flattened = flatten(nested_list) + self.assertEqual(flattened, [3, 4, 5, 6, 7, 8]) + + nested_list = [[], [8]] + flattened = flatten(nested_list) + self.assertEqual(flattened, [8]) + + def test_flatten_iter(self): + + nested_list = [2, 1, [3, [4, 5], 6], 7, [8]] + flattened = flatten_iter(nested_list) + self.assertEqual(next(flattened), 2) + self.assertEqual(next(flattened), 1) + self.assertEqual(next(flattened), 3) + self.assertEqual(next(flattened), 4) + self.assertEqual(next(flattened), 5) + self.assertEqual(next(flattened), 6) + self.assertEqual(next(flattened), 7) + self.assertEqual(next(flattened), 8) + self.assertRaises(StopIteration, next, flattened) + + nested_list = [[3, [4, 5], 6], 7, [8]] + flattened = flatten_iter(nested_list) + self.assertEqual(next(flattened), 3) + self.assertEqual(next(flattened), 4) + self.assertEqual(next(flattened), 5) + self.assertEqual(next(flattened), 6) + self.assertEqual(next(flattened), 7) + self.assertEqual(next(flattened), 8) + self.assertRaises(StopIteration, next, flattened) + + nested_list = [[], [8]] + flattened = flatten_iter(nested_list) + self.assertEqual(next(flattened), 8) + self.assertRaises(StopIteration, next, flattened) + + +class TestGarage(unittest.TestCase): + + def test_garage(self): + + initial = [1, 2, 3, 0, 4] + final = [0, 3, 2, 1, 4] + steps, seq = garage(initial, final) + + self.assertEqual(steps, 4) + self.assertListEqual(seq, [[0, 2, 3, 1, 4], + [2, 0, 3, 1, 4], + [2, 3, 0, 1, 4], + [0, 3, 2, 1, 4]]) + + +class TestLongestNonRepeat(unittest.TestCase): + + def test_longest_non_repeat_v1(self): + + string = "abcabcbb" + self.assertEqual(longest_non_repeat_v1(string), 3) + + string = "bbbbb" + self.assertEqual(longest_non_repeat_v1(string), 1) + + string = "pwwkew" + self.assertEqual(longest_non_repeat_v1(string), 3) + + def test_longest_non_repeat_v2(self): + + string = "abcabcbb" + self.assertEqual(longest_non_repeat_v2(string), 3) + + string = "bbbbb" + self.assertEqual(longest_non_repeat_v2(string), 1) + + string = "pwwkew" + self.assertEqual(longest_non_repeat_v2(string), 3) + + +class TestMaxOnesIndex(unittest.TestCase): + + def test_max_ones_index(self): + + self.assertEqual(9, max_ones_index([1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1])) + self.assertEqual(3, max_ones_index([1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1])) + self.assertEqual(-1, max_ones_index([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])) + + +class TestMergeInterval(unittest.TestCase): + + def test_merge(self): + interval_list = [[1, 3], [2, 6], [8, 10], [15, 18]] + intervals = [Interval(i[0], i[1]) for i in interval_list] + merged_intervals = Interval.merge(intervals) + self.assertEqual( + merged_intervals, + [Interval(1, 6), Interval(8, 10), Interval(15, 18)] + ) + + def test_merge_intervals(self): + interval_list = [[1, 3], [2, 6], [8, 10], [15, 18]] + merged_intervals = merge_intervals(interval_list) + self.assertEqual( + merged_intervals, + [[1, 6], [8, 10], [15, 18]] + ) + + +class TestMissingRanges(unittest.TestCase): + + def test_missing_ranges(self): + + arr = [3, 5, 10, 11, 12, 15, 19] + + self.assertListEqual(missing_ranges(arr, 0, 20), + [(0, 2), (4, 4), (6, 9), + (13, 14), (16, 18), (20, 20)]) + + self.assertListEqual(missing_ranges(arr, 6, 100), + [(6, 9), (13, 14), (16, 18), (20, 100)]) + + +class TestMoveZeros(unittest.TestCase): + + def test_move_zeros(self): + + self.assertListEqual(move_zeros([False, 1, 0, 1, 2, 0, 1, 3, "a"]), + [False, 1, 1, 2, 1, 3, "a", 0, 0]) + + self.assertListEqual(move_zeros([0, 34, 'rahul', [], None, 0, True, 0]), + [34, 'rahul', [], None, True, 0, 0, 0]) + + +class TestPlusOne(unittest.TestCase): + + def test_plus_one_v1(self): + + self.assertListEqual(plus_one_v1([0]), [1]) + self.assertListEqual(plus_one_v1([9]), [1, 0]) + self.assertListEqual(plus_one_v1([1, 0, 9]), [1, 1, 0]) + self.assertListEqual(plus_one_v1([9, 9, 8, 0, 0, 9]), + [9, 9, 8, 0, 1, 0]) + self.assertListEqual(plus_one_v1([9, 9, 9, 9]), + [1, 0, 0, 0, 0]) + + def test_plus_one_v2(self): + + self.assertListEqual(plus_one_v2([0]), [1]) + self.assertListEqual(plus_one_v2([9]), [1, 0]) + self.assertListEqual(plus_one_v2([1, 0, 9]), [1, 1, 0]) + self.assertListEqual(plus_one_v2([9, 9, 8, 0, 0, 9]), + [9, 9, 8, 0, 1, 0]) + self.assertListEqual(plus_one_v2([9, 9, 9, 9]), + [1, 0, 0, 0, 0]) + + def test_plus_one_v3(self): + + self.assertListEqual(plus_one_v3([0]), [1]) + self.assertListEqual(plus_one_v3([9]), [1, 0]) + self.assertListEqual(plus_one_v3([1, 0, 9]), [1, 1, 0]) + self.assertListEqual(plus_one_v3([9, 9, 8, 0, 0, 9]), + [9, 9, 8, 0, 1, 0]) + self.assertListEqual(plus_one_v3([9, 9, 9, 9]), + [1, 0, 0, 0, 0]) + + +class TestRotateArray(unittest.TestCase): + + def test_rotate_v1(self): + + self.assertListEqual(rotate_v1([1, 2, 3, 4, 5, 6, 7], k=3), + [5, 6, 7, 1, 2, 3, 4]) + self.assertListEqual(rotate_v1([1, 2, 3, 4, 5, 6, 7], k=1), + [7, 1, 2, 3, 4, 5, 6]) + self.assertListEqual(rotate_v1([1, 2, 3, 4, 5, 6, 7], k=7), + [1, 2, 3, 4, 5, 6, 7]) + self.assertListEqual(rotate_v1([1, 2], k=111), [2, 1]) + + def test_rotate_v2(self): + + self.assertListEqual(rotate_v2([1, 2, 3, 4, 5, 6, 7], k=3), + [5, 6, 7, 1, 2, 3, 4]) + self.assertListEqual(rotate_v2([1, 2, 3, 4, 5, 6, 7], k=1), + [7, 1, 2, 3, 4, 5, 6]) + self.assertListEqual(rotate_v2([1, 2, 3, 4, 5, 6, 7], k=7), + [1, 2, 3, 4, 5, 6, 7]) + self.assertListEqual(rotate_v2([1, 2], k=111), [2, 1]) + + def test_rotate_v3(self): + + self.assertListEqual(rotate_v3([1, 2, 3, 4, 5, 6, 7], k=3), + [5, 6, 7, 1, 2, 3, 4]) + self.assertListEqual(rotate_v3([1, 2, 3, 4, 5, 6, 7], k=1), + [7, 1, 2, 3, 4, 5, 6]) + self.assertListEqual(rotate_v3([1, 2, 3, 4, 5, 6, 7], k=7), + [1, 2, 3, 4, 5, 6, 7]) + self.assertListEqual(rotate_v3([1, 2], k=111), [2, 1]) + + +class TestSummaryRanges(unittest.TestCase): + + def test_summarize_ranges(self): + + self.assertListEqual(summarize_ranges([0, 1, 2, 4, 5, 7]), + [(0, 2), (4, 5), (7, 7)]) + self.assertListEqual(summarize_ranges([-5, -4, -3, 1, 2, 4, 5, 6]), + [(-5, -3), (1, 2), (4, 6)]) + self.assertListEqual(summarize_ranges([-2, -1, 0, 1, 2]), + [(-2, 2)]) + + +class TestThreeSum(unittest.TestCase): + + def test_three_sum(self): + + self.assertSetEqual(three_sum([-1, 0, 1, 2, -1, -4]), + {(-1, 0, 1), (-1, -1, 2)}) + + self.assertSetEqual(three_sum([-1, 3, 1, 2, -1, -4, -2]), + {(-4, 1, 3), (-2, -1, 3), (-1, -1, 2)}) + + +class TestSuite(unittest.TestCase): + + def test_two_sum(self): + + self.assertTupleEqual((0, 2), two_sum([2, 11, 7, 9], target=9)) + self.assertTupleEqual((0, 3), two_sum([-3, 5, 2, 3, 8, -9], target=0)) + + self.assertIsNone(two_sum([-3, 5, 2, 3, 8, -9], target=6)) + + +class TestTrimmean(unittest.TestCase): + + def test_trimmean(self): + + self.assertEqual(trimmean([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 20), 5.5) + self.assertEqual(trimmean([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 20), 6.0) + + +class TestTop1(unittest.TestCase): + + def test_top_1(self): + self.assertListEqual(top_1([1 , 1, 2, 2, 3]), [1, 2]) + self.assertListEqual(top_1([1, 2, 3, 324, 234, 23, 23, 1, 23, 23]), [23]) + + +class TestLimit(unittest.TestCase): + + def test_limit(self): + self.assertListEqual(limit([1, 2, 3, 4, 5], 2, 4), [2, 3, 4]) + self.assertListEqual(limit([1, 2, 3, 4, 5], 2), [2, 3, 4, 5]) + self.assertListEqual(limit([1, 2, 3, 4, 5], None, 4), [1, 2, 3, 4]) + + +class TestNSum(unittest.TestCase): + + def test_n_sum(self): + self.assertEqual(n_sum(2, [-3, 5, 2, 3, 8, -9], 6), []) # noqa: E501 + self.assertEqual(n_sum(3, [-5, -4, -3, -2, -1, 0, 1, 2, 3], 0), sorted([[-5,2,3],[-2,0,2],[-4,1,3],[-3,1,2],[-1,0,1],[-2,-1,3],[-3,0,3]])) # noqa: E501 + self.assertEqual(n_sum(3, [-1,0,1,2,-1,-4], 0), sorted([[-1,-1,2],[-1,0,1]])) # noqa: E501 + self.assertEqual(n_sum(4, [1, 0, -1, 0, -2, 2], 0), sorted([[-2, -1, 1, 2], [-2, 0, 0, 2], [-1, 0, 0, 1]])) # noqa: E501 + self.assertEqual(n_sum(4, [7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 6, 4, -3, -2], 10), sorted([[-6, 2, 7, 7], [-6, 3, 6, 7], [-6, 4, 5, 7], [-6, 4, 6, 6], [-5, 1, 7, 7], [-5, 2, 6, 7], [-5, 3, 5, 7], [-5, 3, 6, 6], [-5, 4, 4, 7], [-5, 4, 5, 6], [-4, 0, 7, 7], [-4, 1, 6, 7], [-4, 2, 5, 7], [-4, 2, 6, 6], [-4, 3, 4, 7], [-4, 3, 5, 6], [-4, 4, 4, 6], [-3, -1, 7, 7], [-3, 0, 6, 7], [-3, 1, 5, 7], [-3, 1, 6, 6], [-3, 2, 4, 7], [-3, 2, 5, 6], [-3, 3, 4, 6], [-3, 4, 4, 5], [-2, -2, 7, 7], [-2, -1, 6, 7], [-2, 0, 5, 7], [-2, 0, 6, 6], [-2, 1, 4, 7], [-2, 1, 5, 6], [-2, 2, 3, 7], [-2, 2, 4, 6], [-2, 3, 4, 5], [-1, 0, 4, 7], [-1, 0, 5, 6], [-1, 1, 3, 7], [-1, 1, 4, 6], [-1, 2, 3, 6], [-1, 2, 4, 5], [-1, 3, 4, 4], [0, 1, 2, 7], [0, 1, 3, 6], [0, 1, 4, 5], [0, 2, 3, 5], [0, 2, 4, 4], [1, 2, 3, 4]])) # noqa: E501 + + self.assertEqual(n_sum(2, [[-3, 0], [-2, 1], [2, 2], [3, 3], [8, 4], [-9, 5]], 0, # noqa: E501 + sum_closure=lambda a, b: a[0] + b[0]), # noqa: E501 + [[[-3, 0], [3, 3]], [[-2, 1], [2, 2]]]) # noqa: E501 + self.assertEqual(n_sum(2, [[-3, 0], [-2, 1], [2, 2], [3, 3], [8, 4], [-9, 5]], [0, 3], # noqa: E501 + sum_closure=lambda a, b: [a[0] + b[0], a[1] + b[1]], # noqa: E501 + same_closure=lambda a, b: a[0] == b[0] and a[1] == b[1]), # noqa: E501 + [[[-3, 0], [3, 3]], [[-2, 1], [2, 2]]]) # noqa: E501 + self.assertEqual(n_sum(2, [[-3, 0], [-2, 1], [2, 2], [3, 3], [8, 4], [-9, 5]], -5, # noqa: E501 + sum_closure=lambda a, b: [a[0] + b[1], a[1] + b[0]], # noqa: E501 + compare_closure=lambda a, b: -1 if a[0] < b else 1 if a[0] > b else 0), # noqa: E501 + [[[-9, 5], [8, 4]]]) # noqa: E501 + + +if __name__ == '__main__': + + unittest.main() diff --git a/tests/test_backtrack.py b/tests/test_backtrack.py new file mode 100644 index 000000000..c53f9f1e1 --- /dev/null +++ b/tests/test_backtrack.py @@ -0,0 +1,186 @@ +from algorithms.backtrack import ( + add_operators, + permute, + permute_iter, + anagram, + array_sum_combinations, + unique_array_sum_combinations, + combination_sum, + find_words, + pattern_match, +) + +import unittest +from algorithms.backtrack.generate_parenthesis import * + + +class TestAddOperator(unittest.TestCase): + def test_add_operators(self): + # "123", 6 -> ["1+2+3", "1*2*3"] + s = "123" + target = 6 + self.assertEqual(add_operators(s, target), ["1+2+3", "1*2*3"]) + # "232", 8 -> ["2*3+2", "2+3*2"] + s = "232" + target = 8 + self.assertEqual(add_operators(s, target), ["2+3*2", "2*3+2"]) + + s = "123045" + target = 3 + answer = ['1+2+3*0*4*5', + '1+2+3*0*45', + '1+2-3*0*4*5', + '1+2-3*0*45', + '1-2+3+0-4+5', + '1-2+3-0-4+5', + '1*2+3*0-4+5', + '1*2-3*0-4+5', + '1*23+0-4*5', + '1*23-0-4*5', + '12+3*0-4-5', + '12-3*0-4-5'] + self.assertEqual(add_operators(s, target), answer) + + +class TestPermuteAndAnagram(unittest.TestCase): + + def test_permute(self): + perms = ['abc', 'bac', 'bca', 'acb', 'cab', 'cba'] + self.assertEqual(perms, permute("abc")) + + def test_permute_iter(self): + it = permute_iter("abc") + perms = ['abc', 'bac', 'bca', 'acb', 'cab', 'cba'] + for i in range(len(perms)): + self.assertEqual(perms[i], next(it)) + + def test_angram(self): + self.assertTrue(anagram('apple', 'pleap')) + self.assertFalse(anagram("apple", "cherry")) + + +class TestArrayCombinationSum(unittest.TestCase): + + def test_array_sum_combinations(self): + A = [1, 2, 3, 3] + B = [2, 3, 3, 4] + C = [2, 3, 3, 4] + target = 7 + answer = [[1, 2, 4], [1, 3, 3], [1, 3, 3], [1, 3, 3], + [1, 3, 3], [1, 4, 2], [2, 2, 3], [2, 2, 3], + [2, 3, 2], [2, 3, 2], [3, 2, 2], [3, 2, 2]] + answer.sort() + self.assertListEqual(sorted(array_sum_combinations(A, B, C, target)), answer) + + def test_unique_array_sum_combinations(self): + A = [1, 2, 3, 3] + B = [2, 3, 3, 4] + C = [2, 3, 3, 4] + target = 7 + answer = [(2, 3, 2), (3, 2, 2), (1, 2, 4), + (1, 4, 2), (2, 2, 3), (1, 3, 3)] + answer.sort() + self.assertListEqual(sorted(unique_array_sum_combinations(A, B, C, target)), answer) + + +class TestCombinationSum(unittest.TestCase): + + def check_sum(self, nums, target): + if sum(nums) == target: + return (True, nums) + else: + return (False, nums) + + def test_combination_sum(self): + candidates1 = [2, 3, 6, 7] + target1 = 7 + answer1 = [ + [2, 2, 3], + [7] + ] + self.assertEqual(combination_sum(candidates1, target1), answer1) + + candidates2 = [2, 3, 5] + target2 = 8 + answer2 = [ + [2, 2, 2, 2], + [2, 3, 3], + [3, 5] + ] + self.assertEqual(combination_sum(candidates2, target2), answer2) + + +class TestFindWords(unittest.TestCase): + + def test_normal(self): + board = [ + ['o', 'a', 'a', 'n'], + ['e', 't', 'a', 'e'], + ['i', 'h', 'k', 'r'], + ['i', 'f', 'l', 'v'] + ] + + words = ["oath", "pea", "eat", "rain"] + self.assertEqual(find_words(board, words).sort(), + ['oath', 'eat'].sort()) + + def test_none(self): + board = [ + ['o', 'a', 'a', 'n'], + ['e', 't', 'a', 'e'], + ['i', 'h', 'k', 'r'], + ['i', 'f', 'l', 'v'] + ] + + words = ["chicken", "nugget", "hello", "world"] + self.assertEqual(find_words(board, words), []) + + def test_empty(self): + board = [] + words = [] + self.assertEqual(find_words(board, words), []) + + def test_uneven(self): + board = [ + ['o', 'a', 'a', 'n'], + ['e', 't', 'a', 'e'] + ] + words = ["oath", "pea", "eat", "rain"] + self.assertEqual(find_words(board, words), ['eat']) + + def test_repeat(self): + board = [ + ['a', 'a', 'a'], + ['a', 'a', 'a'], + ['a', 'a', 'a'] + ] + words = ["a", "aa", "aaa", "aaaa", "aaaaa"] + self.assertTrue(len(find_words(board, words)) == 5) + + +class TestPatternMatch(unittest.TestCase): + + def test_pattern_match(self): + pattern1 = "abab" + string1 = "redblueredblue" + pattern2 = "aaaa" + string2 = "asdasdasdasd" + pattern3 = "aabb" + string3 = "xyzabcxzyabc" + + self.assertTrue(pattern_match(pattern1, string1)) + self.assertTrue(pattern_match(pattern2, string2)) + self.assertFalse(pattern_match(pattern3, string3)) + +class TestGenerateParenthesis(unittest.TestCase): + + def test_generate_parenthesis(self): + self.assertEqual(generate_parenthesis_v1(2), ['()()', '(())']) + self.assertEqual(generate_parenthesis_v1(3), ['()()()', '()(())', '(())()', '(()())', '((()))']) + self.assertEqual(generate_parenthesis_v2(2), ['(())', '()()']) + self.assertEqual(generate_parenthesis_v2(3), ['((()))', '(()())', '(())()', '()(())', '()()()']) + +if __name__ == '__main__': + + unittest.main() + diff --git a/tests/test_bfs.py b/tests/test_bfs.py new file mode 100644 index 000000000..129d6d067 --- /dev/null +++ b/tests/test_bfs.py @@ -0,0 +1,18 @@ +from algorithms.bfs import ( + maze_search, + shortest_distance_from_all_buildings, + word_ladder +) + +import unittest + +class TestMazeSearch(unittest.TestCase): + + def test_maze_search(self): + grid_1 = [[1,0,1,1,1,1],[1,0,1,0,1,0],[1,0,1,0,1,1],[1,1,1,0,1,1]] + self.assertEqual(14, maze_search(grid_1)) + grid_2 = [[1,0,0],[0,1,1],[0,1,1]] + self.assertEqual(-1, maze_search(grid_2)) + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_bit.py b/tests/test_bit.py new file mode 100644 index 000000000..acfb4b035 --- /dev/null +++ b/tests/test_bit.py @@ -0,0 +1,254 @@ +from algorithms.bit import ( + add_bitwise_operator, + count_ones_iter, count_ones_recur, + count_flips_to_convert, + find_missing_number, find_missing_number2, + flip_bit_longest_seq, + is_power_of_two, + reverse_bits, + single_number, + single_number2, + single_number3, + subsets, + get_bit, set_bit, clear_bit, update_bit, + swap_pair, + find_difference, + has_alternative_bit, has_alternative_bit_fast, + insert_one_bit, insert_mult_bits, + remove_bit, + binary_gap +) + +import unittest +import random + + +class TestSuite(unittest.TestCase): + + def setUp(self): + """Initialize seed.""" + random.seed("test") + + def test_add_bitwise_operator(self): + self.assertEqual(5432 + 97823, add_bitwise_operator(5432, 97823)) + self.assertEqual(0, add_bitwise_operator(0, 0)) + self.assertEqual(10, add_bitwise_operator(10, 0)) + self.assertEqual(10, add_bitwise_operator(0, 10)) + + def test_count_ones_recur(self): + + # 8 -> 1000 + self.assertEqual(1, count_ones_recur(8)) + + # 109 -> 1101101 + self.assertEqual(5, count_ones_recur(109)) + + # 63 -> 111111 + self.assertEqual(6, count_ones_recur(63)) + + # 0 -> 0 + self.assertEqual(0, count_ones_recur(0)) + + def test_count_ones_iter(self): + + # 8 -> 1000 + self.assertEqual(1, count_ones_iter(8)) + + # 109 -> 1101101 + self.assertEqual(5, count_ones_iter(109)) + + # 63 -> 111111 + self.assertEqual(6, count_ones_iter(63)) + + # 0 -> 0 + self.assertEqual(0, count_ones_iter(0)) + + def test_count_flips_to_convert(self): + # 29: 11101 and 15: 01111 + self.assertEqual(2, count_flips_to_convert(29, 15)) + # 45: 0000101101 and 987: 1111011011 + self.assertEqual(8, count_flips_to_convert(45, 987)) + # 34: 100010 + self.assertEqual(0, count_flips_to_convert(34, 34)) + # 34: 100010 and 53: 110101 + self.assertEqual(4, count_flips_to_convert(34, 53)) + + def test_find_missing_number(self): + + self.assertEqual(7, find_missing_number([4, 1, 3, 0, 6, 5, 2])) + self.assertEqual(0, find_missing_number([1])) + self.assertEqual(1, find_missing_number([0])) + + nums = [i for i in range(100000) if i != 12345] + random.shuffle(nums) + self.assertEqual(12345, find_missing_number(nums)) + + def test_find_missing_number2(self): + + self.assertEqual(7, find_missing_number2([4, 1, 3, 0, 6, 5, 2])) + self.assertEqual(0, find_missing_number2([1])) + self.assertEqual(1, find_missing_number2([0])) + + nums = [i for i in range(100000) if i != 12345] + random.shuffle(nums) + self.assertEqual(12345, find_missing_number2(nums)) + + def test_flip_bit_longest_seq(self): + # 1775: 11011101111 + self.assertEqual(8, flip_bit_longest_seq(1775)) + # 5: 101 + self.assertEqual(3, flip_bit_longest_seq(5)) + # 71: 1000111 + self.assertEqual(4, flip_bit_longest_seq(71)) + # 0: 0 + self.assertEqual(1, flip_bit_longest_seq(0)) + + def test_is_power_of_two(self): + + self.assertTrue(is_power_of_two(64)) + self.assertFalse(is_power_of_two(91)) + self.assertTrue(is_power_of_two(2**1001)) + self.assertTrue(is_power_of_two(1)) + self.assertFalse(is_power_of_two(0)) + + def test_reverse_bits(self): + + self.assertEqual(43261596, reverse_bits(964176192)) + self.assertEqual(964176192, reverse_bits(43261596)) + self.assertEqual(1, reverse_bits(2147483648)) + + # bin(0) => 00000000000000000000000000000000 + self.assertEqual(0, reverse_bits(0)) + + # bin(2**32 - 1) => 11111111111111111111111111111111 + self.assertEqual(2**32 - 1, reverse_bits(2**32 - 1)) + + def test_single_number(self): + + random.seed('test') + + self.assertEqual(0, single_number([1, 0, 2, 1, 2, 3, 3])) + self.assertEqual(101, single_number([101])) + + single = random.randint(1, 100000) + nums = [random.randint(1, 100000) for _ in range(1000)] + nums *= 2 # nums contains pairs of random integers + nums.append(single) + random.shuffle(nums) + + self.assertEqual(single, single_number(nums)) + + def test_single_number2(self): + + self.assertEqual(3, single_number2([4, 2, 3, 2, 1, 1, 4, 2, 4, 1])) + single = random.randint(1, 100000) + nums = [random.randint(1, 100000) for _ in range(1000)] + nums *= 3 # nums contains triplets of random integers + nums.append(single) + random.shuffle(nums) + self.assertEqual(single, single_number2(nums)) + + def test_single_number3(self): + self.assertEqual(sorted([2,5]), + sorted(single_number3([2, 1, 5, 6, 6, 1]))) + self.assertEqual(sorted([4,3]), + sorted(single_number3([9, 9, 4, 3]))) + + def test_subsets(self): + + self.assertSetEqual(subsets([1, 2, 3]), + {(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)}) + + self.assertSetEqual(subsets([10, 20, 30, 40]), + {(10, 40), (10, 20, 40), (10, 30), (10, 20, 30, 40), (40,), + (10, 30, 40), (30,), (20, 30), (30, 40), (10,), (), + (10, 20), (20, 40), (20, 30, 40), (10, 20, 30), (20,)}) + + def test_get_bit(self): + # 22 = 10110 + self.assertEqual(1, get_bit(22, 2)) + self.assertEqual(0, get_bit(22, 3)) + + def test_set_bit(self): + # 22 = 10110 --> after set bit at 3th position: 30 = 11110 + self.assertEqual(30, set_bit(22, 3)) + + def test_clear_bit(self): + # 22 = 10110 --> after clear bit at 2nd position: 20 = 10010 + self.assertEqual(18, clear_bit(22, 2)) + + def test_update_bit(self): + # 22 = 10110 --> after update bit at 3th position with value 1: 30 = 11110 + self.assertEqual(30, update_bit(22, 3, 1)) + # 22 = 10110 --> after update bit at 2nd position with value 0: 20 = 10010 + self.assertEqual(18, update_bit(22, 2, 0)) + + def test_swap_pair(self): + # 22: 10110 --> 41: 101001 + self.assertEqual(41, swap_pair(22)) + # 10: 1010 --> 5 : 0101 + self.assertEqual(5, swap_pair(10)) + + def test_find_difference(self): + self.assertEqual('e', find_difference("abcd", "abecd")) + + def test_has_alternative_bit(self): + self.assertTrue(has_alternative_bit(5)) + self.assertFalse(has_alternative_bit(7)) + self.assertFalse(has_alternative_bit(11)) + self.assertTrue(has_alternative_bit(10)) + + def test_has_alternative_bit_fast(self): + self.assertTrue(has_alternative_bit_fast(5)) + self.assertFalse(has_alternative_bit_fast(7)) + self.assertFalse(has_alternative_bit_fast(11)) + self.assertTrue(has_alternative_bit_fast(10)) + + def test_insert_one_bit(self): + """ + Input: num = 10101 (21) + insert_one_bit(num, 1, 2): 101101 (45) + insert_one_bit(num, 0 ,2): 101001 (41) + insert_one_bit(num, 1, 5): 110101 (53) + insert_one_bit(num, 1, 0): 101010 (42) + """ + self.assertEqual(45, insert_one_bit(21, 1, 2)) + self.assertEqual(41, insert_one_bit(21, 0, 2)) + self.assertEqual(53, insert_one_bit(21, 1, 5)) + self.assertEqual(43, insert_one_bit(21, 1, 0)) + + def test_insert_mult_bits(self): + """ + Input: num = 101 (5) + insert_mult_bits(num, 7, 3, 1): 101111 (47) + insert_mult_bits(num, 7, 3, 0): 101111 (47) + insert_mult_bits(num, 7, 3, 3): 111101 (61) + """ + self.assertEqual(47, insert_mult_bits(5, 7, 3, 1)) + self.assertEqual(47, insert_mult_bits(5, 7, 3, 0)) + self.assertEqual(61, insert_mult_bits(5, 7, 3, 3)) + + def test_remove_bit(self): + """ + Input: num = 10101 (21) + remove_bit(num, 2): output = 1001 (9) + remove_bit(num, 4): output = 101 (5) + remove_bit(num, 0): output = 1010 (10) + """ + self.assertEqual(9, remove_bit(21, 2)) + self.assertEqual(5, remove_bit(21, 4)) + self.assertEqual(10, remove_bit(21, 0)) + + def test_binary_gap(self): + # 22 = 10110 + self.assertEqual(2, binary_gap(22)) + # 6 = 110 + self.assertEqual(1, binary_gap(6)) + # 8 = 1000 + self.assertEqual(0, binary_gap(8)) + # 145 = 10010001 + self.assertEqual(4, binary_gap(145)) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_graph.py b/tests/test_graph.py new file mode 100644 index 000000000..d402b86fa --- /dev/null +++ b/tests/test_graph.py @@ -0,0 +1,59 @@ +from algorithms.graph import Tarjan +from algorithms.graph import check_bipartite + +import unittest + + +class TestTarjan(unittest.TestCase): + """ + Test for the file tarjan.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_tarjan_example_1(self): + # Graph from https://en.wikipedia.org/wiki/File:Scc.png + example = { + 'A': ['B'], + 'B': ['C', 'E', 'F'], + 'C': ['D', 'G'], + 'D': ['C', 'H'], + 'E': ['A', 'F'], + 'F': ['G'], + 'G': ['F'], + 'H': ['D', 'G'] + } + + g = Tarjan(example) + self.assertEqual(g.sccs, [['F', 'G'], ['C', 'D', 'H'], ['A', 'B', 'E']]) + + def test_tarjan_example_2(self): + # Graph from https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm#/media/File:Tarjan%27s_Algorithm_Animation.gif + example = { + 'A': ['E'], + 'B': ['A'], + 'C': ['B', 'D'], + 'D': ['C'], + 'E': ['B'], + 'F': ['B', 'E', 'G'], + 'G': ['F', 'C'], + 'H': ['G', 'H', 'D'] + } + + g = Tarjan(example) + self.assertEqual(g.sccs, [['A', 'B', 'E'], ['C', 'D'], ['F', 'G'], ['H']]) + + +class TestCheckBipartite(unittest.TestCase): + + def test_check_bipartite(self): + + adj_list_1 = [[0, 0, 1], [0, 0, 1], [1, 1, 0]] + self.assertEqual(True, check_bipartite(adj_list_1)) + + adj_list_2 = [[0, 1, 0, 1], [1, 0, 1, 0], [0, 1, 0, 1], [1, 0, 1, 0]] + self.assertEqual(True, check_bipartite(adj_list_2)) + + adj_list_3 = [[0, 1, 0, 0], [1, 0, 1, 1], [0, 1, 0, 1], [0, 1, 1, 0]] + self.assertEqual(False, check_bipartite(adj_list_3)) diff --git a/tests/test_heap.py b/tests/test_heap.py new file mode 100644 index 000000000..0925972dd --- /dev/null +++ b/tests/test_heap.py @@ -0,0 +1,54 @@ +from algorithms.heap import ( + BinaryHeap, + get_skyline, + max_sliding_window +) + +import unittest + +class TestBinaryHeap(unittest.TestCase): + """ + Test suite for the binary_heap data structures + """ + def setUp(self): + self.min_heap = BinaryHeap() + self.min_heap.insert(4) + self.min_heap.insert(50) + self.min_heap.insert(7) + self.min_heap.insert(55) + self.min_heap.insert(90) + self.min_heap.insert(87) + + def test_insert(self): + # Before insert 2: [0, 4, 50, 7, 55, 90, 87] + # After insert: [0, 2, 50, 4, 55, 90, 87, 7] + self.min_heap.insert(2) + self.assertEqual([0, 2, 50, 4, 55, 90, 87, 7], + self.min_heap.heap) + self.assertEqual(7, self.min_heap.currentSize) + + def test_remove_min(self): + ret = self.min_heap.remove_min() + # Before remove_min : [0, 4, 50, 7, 55, 90, 87] + # After remove_min: [7, 50, 87, 55, 90] + # Test return value + self.assertEqual(4,ret) + self.assertEqual([0, 7, 50, 87, 55, 90], + self.min_heap.heap) + self.assertEqual(5, self.min_heap.currentSize) + +class TestSuite(unittest.TestCase): + def test_get_skyline(self): + buildings = [ [2, 9, 10], [3, 7, 15], [5, 12, 12], \ + [15, 20, 10], [19, 24, 8] ] + # Expect output + output = [ [2, 10], [3, 15], [7, 12], [12, 0], [15, 10], \ + [20, 8], [24, 0] ] + self.assertEqual(output, get_skyline(buildings)) + + def test_max_sliding_window(self): + nums = [1, 3, -1, -3, 5, 3, 6, 7] + self.assertEqual([3, 3, 5, 5, 6, 7], max_sliding_window(nums, 3)) + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_linkedlist.py b/tests/test_linkedlist.py new file mode 100644 index 000000000..435ac7261 --- /dev/null +++ b/tests/test_linkedlist.py @@ -0,0 +1,174 @@ +from algorithms.linkedlist import ( + reverse_list, reverse_list_recursive, + is_sorted, + remove_range, + swap_pairs, + rotate_right, + is_cyclic, + merge_two_list, merge_two_list_recur, + is_palindrome, is_palindrome_stack, is_palindrome_dict +) + +import unittest + +class Node(object): + def __init__(self, x): + self.val = x + self.next = None + +# Convert from linked list Node to list for testing +def convert(head): + ret = [] + if head: + current = head + while current: + ret.append(current.val) + current = current.next + return ret + +class TestSuite(unittest.TestCase): + def setUp(self): + # list test for palindrome + self.l = Node('A') + self.l.next = Node('B') + self.l.next.next = Node('C') + self.l.next.next.next = Node('B') + self.l.next.next.next.next = Node('A') + + self.l1 = Node('A') + self.l1.next = Node('B') + self.l1.next.next = Node('C') + self.l1.next.next.next = Node('B') + + def test_reverse_list(self): + head = Node(1) + head.next = Node(2) + head.next.next = Node(3) + head.next.next.next = Node(4) + self.assertEqual([4, 3, 2, 1], convert(reverse_list(head))) + head = Node(1) + head.next = Node(2) + head.next.next = Node(3) + head.next.next.next = Node(4) + self.assertEqual([4, 3, 2, 1], convert(reverse_list_recursive(head))) + + def test_is_sorted(self): + head = Node(-2) + head.next = Node(2) + head.next.next = Node(2) + head.next.next.next = Node(4) + head.next.next.next.next = Node(9) + # head -> -2 -> 2 -> 2 -> 4 -> 9 + self.assertTrue(is_sorted(head)) + head = Node(1) + head.next = Node(2) + head.next.next = Node(8) + head.next.next.next = Node(4) + head.next.next.next.next = Node(6) + # head -> 1 -> 2 -> 8 -> 4 -> 6 + self.assertFalse(is_sorted(head)) + + def test_remove_range(self): + + # Test case: middle case. + head = Node(0) + head.next = Node(1) + head.next.next = Node(2) + head.next.next.next = Node(3) + head.next.next.next.next = Node(4) + # Expect output: 0 4 + self.assertEqual([0, 4], convert(remove_range(head, 1, 3))) + + # Test case: taking out the front node + head = Node(0) + head.next = Node(1) + head.next.next = Node(2) + head.next.next.next = Node(3) + head.next.next.next.next = Node(4) + # Expect output: 2 3 4 + self.assertEqual([2, 3, 4], convert(remove_range(head, 0, 1))) + + # Test case: removing all the nodes + head = Node(0) + head.next = Node(1) + head.next.next = Node(2) + head.next.next.next = Node(3) + head.next.next.next.next = Node(4) + self.assertEqual([], convert(remove_range(head, 0, 7))) + + def test_swap_in_pairs(self): + head = Node(1) + head.next = Node(2) + head.next.next = Node(3) + head.next.next.next = Node(4) + # Expect output : 2 --> 1 --> 4 --> 3 + self.assertEqual([2, 1, 4, 3], convert(swap_pairs(head))) + + def test_rotate_right(self): + # Given 1->2->3->4->5->NULL + head = Node(1) + head.next = Node(2) + head.next.next = Node(3) + head.next.next.next = Node(4) + head.next.next.next.next = Node(5) + # K = 2. Expect output: 4->5->1->2->3->NULL. + self.assertEqual([4, 5, 1, 2, 3], convert(rotate_right(head, 2))) + + def test_is_cyclic(self): + # create linked list => A -> B -> C -> D -> E -> C + head = Node('A') + head.next = Node('B') + curr = head.next + cyclic_node = Node('C') + curr.next = cyclic_node + curr = curr.next + curr.next = Node('D') + curr = curr.next + curr.next = Node('E') + curr = curr.next + curr.next = cyclic_node + self.assertTrue(is_cyclic(head)) + + # create linked list 1 -> 2 -> 3 -> 4 + head = Node(1) + curr = head + for i in range(2, 6): + curr.next = Node(i) + curr = curr.next + self.assertFalse(is_cyclic(head)) + + def test_merge_two_list(self): + """ + Input: head1:1->2->4, head2: 1->3->4 + Output: 1->1->2->3->4->4 + """ + head1 = Node(1) + head1.next = Node(2) + head1.next.next = Node(4) + head2 = Node(1) + head2.next = Node(3) + head2.next.next = Node(4) + self.assertEqual([1, 1, 2, 3, 4, 4], + convert(merge_two_list(head1, head2))) + # Test recursive + head1 = Node(1) + head1.next = Node(2) + head1.next.next = Node(4) + head2 = Node(1) + head2.next = Node(3) + head2.next.next = Node(4) + self.assertEqual([1, 1, 2, 3, 4, 4], + convert(merge_two_list_recur(head1, head2))) + + def test_is_palindrome(self): + self.assertTrue(is_palindrome(self.l)) + self.assertFalse(is_palindrome(self.l1)) + def test_is_palindrome_stack(self): + self.assertTrue(is_palindrome_stack(self.l)) + self.assertFalse(is_palindrome_stack(self.l1)) + def test_is_palindrome_dict(self): + self.assertTrue(is_palindrome_dict(self.l)) + self.assertFalse(is_palindrome_dict(self.l1)) + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_map.py b/tests/test_map.py new file mode 100644 index 000000000..5786803e9 --- /dev/null +++ b/tests/test_map.py @@ -0,0 +1,152 @@ +from algorithms.map import ( + HashTable, ResizableHashTable, + Node, SeparateChainingHashTable +) + +import unittest + +class TestHashTable(unittest.TestCase): + def test_one_entry(self): + m = HashTable(10) + m.put(1, '1') + self.assertEqual('1', m.get(1)) + + def test_add_entry_bigger_than_table_size(self): + m = HashTable(10) + m.put(11, '1') + self.assertEqual('1', m.get(11)) + + def test_get_none_if_key_missing_and_hash_collision(self): + m = HashTable(10) + m.put(1, '1') + self.assertEqual(None, m.get(11)) + + def test_two_entries_with_same_hash(self): + m = HashTable(10) + m.put(1, '1') + m.put(11, '11') + self.assertEqual('1', m.get(1)) + self.assertEqual('11', m.get(11)) + + def test_get_on_full_table_does_halts(self): + # and does not search forever + m = HashTable(10) + for i in range(10, 20): + m.put(i, i) + self.assertEqual(None, m.get(1)) + + def test_delete_key(self): + m = HashTable(10) + for i in range(5): + m.put(i, i**2) + m.del_(1) + self.assertEqual(None, m.get(1)) + self.assertEqual(4,m.get(2)) + + def test_delete_key_and_reassign(self): + m = HashTable(10) + m.put(1, 1) + del m[1] + m.put(1, 2) + self.assertEqual(2, m.get(1)) + + def test_assigning_to_full_table_throws_error(self): + m = HashTable(3) + m.put(1, 1) + m.put(2, 2) + m.put(3, 3) + with self.assertRaises(ValueError): + m.put(4, 4) + + def test_len_trivial(self): + m = HashTable(10) + self.assertEqual(0, len(m)) + for i in range(10): + m.put(i, i) + self.assertEqual(i + 1, len(m)) + + def test_len_after_deletions(self): + m = HashTable(10) + m.put(1, 1) + self.assertEqual(1, len(m)) + m.del_(1) + self.assertEqual(0, len(m)) + m.put(11, 42) + self.assertEqual(1, len(m)) + + def test_resizable_hash_table(self): + m = ResizableHashTable() + self.assertEqual(ResizableHashTable.MIN_SIZE, m.size) + for i in range(ResizableHashTable.MIN_SIZE): + m.put(i, 'foo') + self.assertEqual(ResizableHashTable.MIN_SIZE * 2, m.size) + self.assertEqual('foo', m.get(1)) + self.assertEqual('foo', m.get(3)) + self.assertEqual('foo', m.get(ResizableHashTable.MIN_SIZE - 1)) + + def test_fill_up_the_limit(self): + m = HashTable(10) + for i in range(10): + m.put(i,i**2) + for i in range(10): + self.assertEqual(i**2,m.get(i)) + + +class TestSeparateChainingHashTable(unittest.TestCase): + def test_one_entry(self): + m = SeparateChainingHashTable(10) + m.put(1, '1') + self.assertEqual('1', m.get(1)) + + def test_two_entries_with_same_hash(self): + m = SeparateChainingHashTable(10) + m.put(1, '1') + m.put(11, '11') + self.assertEqual('1', m.get(1)) + self.assertEqual('11', m.get(11)) + + def test_len_trivial(self): + m = SeparateChainingHashTable(10) + self.assertEqual(0, len(m)) + for i in range(10): + m.put(i, i) + self.assertEqual(i + 1, len(m)) + + def test_len_after_deletions(self): + m = SeparateChainingHashTable(10) + m.put(1, 1) + self.assertEqual(1, len(m)) + m.del_(1) + self.assertEqual(0, len(m)) + m.put(11, 42) + self.assertEqual(1, len(m)) + + def test_delete_key(self): + m = SeparateChainingHashTable(10) + for i in range(5): + m.put(i, i**2) + m.del_(1) + self.assertEqual(None, m.get(1)) + self.assertEqual(4, m.get(2)) + + def test_delete_key_and_reassign(self): + m = SeparateChainingHashTable(10) + m.put(1, 1) + del m[1] + m.put(1, 2) + self.assertEqual(2, m.get(1)) + + def test_add_entry_bigger_than_table_size(self): + m = SeparateChainingHashTable(10) + m.put(11, '1') + self.assertEqual('1', m.get(11)) + + def test_get_none_if_key_missing_and_hash_collision(self): + m = SeparateChainingHashTable(10) + m.put(1, '1') + self.assertEqual(None, m.get(11)) + + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_maths.py b/tests/test_maths.py new file mode 100644 index 000000000..2499a4f88 --- /dev/null +++ b/tests/test_maths.py @@ -0,0 +1,277 @@ +from algorithms.maths import ( + int_to_base, base_to_int, + decimal_to_binary_ip, + extended_gcd, + factorial, factorial_recur, + gcd, lcm, + gen_strobogrammatic, strobogrammatic_in_range, + is_strobogrammatic, is_strobogrammatic2, + modular_exponential, + find_next_square, find_next_square2, + prime_check, + get_primes, + pythagoras, + is_prime, + encrypt, decrypt, + combination, combination_memo +) + +import unittest + + +class TestBaseConversion(unittest.TestCase): + """ + Test for the file base_conversion.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_int_to_base(self): + self.assertEqual("101", int_to_base(5, 2)) + self.assertEqual("0", int_to_base(0, 2)) + self.assertEqual("FF", int_to_base(255, 16)) + + def test_base_to_int(self): + self.assertEqual(5, base_to_int("101", 2)) + self.assertEqual(0, base_to_int("0", 2)) + self.assertEqual(255, base_to_int("FF", 16)) + + +class TestDecimalToBinaryIP(unittest.TestCase): + """ + Test for the file decimal_to_binary_ip.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_decimal_to_binary_ip(self): + self.assertEqual("00000000.00000000.00000000.00000000", + decimal_to_binary_ip("0.0.0.0")) + self.assertEqual("11111111.11111111.11111111.11111111", + decimal_to_binary_ip("255.255.255.255")) + self.assertEqual("11000000.10101000.00000000.00000001", + decimal_to_binary_ip("192.168.0.1")) + + +class TestExtendedGcd(unittest.TestCase): + """[summary] + Test for the file extended_gcd.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_extended_gcd(self): + self.assertEqual((0, 1, 2), extended_gcd(8, 2)) + self.assertEqual((0, 1, 17), extended_gcd(13, 17)) + + +class TestGcd(unittest.TestCase): + """[summary] + Test for the file gcd.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_gcd(self): + self.assertEqual(4, gcd(8, 12)) + self.assertEqual(1, gcd(13, 17)) + + def test_lcm(self): + self.assertEqual(24, lcm(8, 12)) + + +class TestGenerateStroboGrammatic(unittest.TestCase): + """[summary] + Test for the file generate_strobogrammatic.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_gen_strobomatic(self): + self.assertEqual(['88', '11', '96', '69'], gen_strobogrammatic(2)) + + def test_strobogrammatic_in_range(self): + self.assertEqual(4, strobogrammatic_in_range("10", "100")) + + +class TestIsStrobogrammatic(unittest.TestCase): + """[summary] + Test for the file is_strobogrammatic.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_is_strobogrammatic(self): + self.assertTrue(is_strobogrammatic("69")) + self.assertFalse(is_strobogrammatic("14")) + + def test_is_strobogrammatic2(self): + self.assertTrue(is_strobogrammatic2("69")) + self.assertFalse(is_strobogrammatic2("14")) + + +class TestModularExponential(unittest.TestCase): + """[summary] + Test for the file modular_Exponential.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_modular_exponential(self): + self.assertEqual(1, modular_exponential(5, 117, 19)) + self.assertEqual(pow(1243, 65321, 10**9 + 7), + modular_exponential(1243, 65321, 10**9 + 7)) + self.assertEqual(1, modular_exponential(12, 0, 78)) + self.assertRaises(ValueError, modular_exponential, 12, -2, 455) + + +class TestNextPerfectSquare(unittest.TestCase): + """[summary] + Test for the file next_perfect_square.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_find_next_square(self): + self.assertEqual(36, find_next_square(25)) + self.assertEqual(1, find_next_square(0)) + + def test_find_next_square2(self): + self.assertEqual(36, find_next_square2(25)) + self.assertEqual(1, find_next_square2(0)) + + +class TestPrimesSieveOfEratosthenes(unittest.TestCase): + """[summary] + Test for the file primes_sieve_of_eratosthenes.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_primes(self): + self.assertListEqual([2, 3, 5, 7], get_primes(7)) + self.assertRaises(ValueError, get_primes, -42) + + +class TestPrimeTest(unittest.TestCase): + """[summary] + Test for the file prime_test.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_prime_test(self): + """ + checks all prime numbers between 2 up to 100. + Between 2 up to 100 exists 25 prime numbers! + """ + counter = 0 + for i in range(2, 101): + if prime_check(i): + counter += 1 + self.assertEqual(25, counter) + + +class TestPythagoras(unittest.TestCase): + """[summary] + Test for the file pythagoras.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_pythagoras(self): + self.assertEqual("Hypotenuse = 3.605551275463989", pythagoras(3, 2, "?")) + + +class TestRabinMiller(unittest.TestCase): + """[summary] + Test for the file rabin_miller.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_is_prime(self): + self.assertTrue(is_prime(7, 2)) + self.assertTrue(is_prime(13, 11)) + self.assertFalse(is_prime(6, 2)) + + +class TestRSA(unittest.TestCase): + """[summary] + Test for the file rsa.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_encrypt_decrypt(self): + + self.assertEqual(7, decrypt(encrypt(7, 23, 143), 47, 143)) + + # def test_key_generator(self): # this test takes a while! + # for i in range(100): + # print("step {0}".format(i)) + # n, e, d = generate_key(26) + # data = 2 + # en = encrypt(data, e, n) + # dec = decrypt(en, d, n) + # self.assertEqual(data,dec) + + +class TestCombination(unittest.TestCase): + """[summary] + Test for the file combination.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_combination(self): + self.assertEqual(10, combination(5, 2)) + self.assertEqual(252, combination(10, 5)) + + def test_combination_memo(self): + self.assertEqual(10272278170, combination_memo(50, 10)) + self.assertEqual(847660528, combination_memo(40, 10)) + + +class TestFactorial(unittest.TestCase): + """[summary] + Test for the file factorial.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_factorial(self): + self.assertEqual(1, factorial(0)) + self.assertEqual(120, factorial(5)) + self.assertEqual(3628800, factorial(10)) + self.assertEqual(637816310, factorial(34521, 10**9 + 7)) + self.assertRaises(ValueError, factorial, -42) + self.assertRaises(ValueError, factorial, 42, -1) + + def test_factorial_recur(self): + self.assertEqual(1, factorial_recur(0)) + self.assertEqual(120, factorial_recur(5)) + self.assertEqual(3628800, factorial_recur(10)) + self.assertEqual(637816310, factorial_recur(34521, 10**9 + 7)) + self.assertRaises(ValueError, factorial_recur, -42) + self.assertRaises(ValueError, factorial_recur, 42, -1) + + +if __name__ == "__main__": + unittest.main() + diff --git a/tests/test_ml.py b/tests/test_ml.py new file mode 100644 index 000000000..61852ec31 --- /dev/null +++ b/tests/test_ml.py @@ -0,0 +1,31 @@ +from algorithms.ml.nearest_neighbor import ( + distance, + nearest_neighbor +) + +import unittest + +class TestML(unittest.TestCase): + def setUp(self): + # train set for the AND-function + self.trainSetAND = {(0,0) : 0, (0,1) :0, (1,0) : 0, (1,1) : 1} + + # train set for light or dark colors + self.trainSetLight = {(11, 98, 237) : 'L', (3, 39, 96) : 'D', (242, 226, 12) : 'L', (99, 93, 4) : 'D', + (232, 62, 32) : 'L', (119, 28, 11) : 'D', (25, 214, 47) : 'L', (89, 136, 247) : 'L', + (21, 34, 63) : 'D', (237, 99, 120) : 'L', (73, 33, 39) : 'D'} + def test_nearest_neighbor(self): + # AND-function + self.assertEqual(nearest_neighbor((1,1), self.trainSetAND), 1) + self.assertEqual(nearest_neighbor((0,1), self.trainSetAND), 0) + + # dark/light color test + self.assertEqual(nearest_neighbor((31, 242, 164), self.trainSetLight), 'L') + self.assertEqual(nearest_neighbor((13, 94, 64), self.trainSetLight), 'D') + self.assertEqual(nearest_neighbor((230, 52, 239), self.trainSetLight), 'L') + def test_distance(self): + self.assertAlmostEqual(distance((1,2,3), (1,0,-1)), 4.47, 2) + + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/tests/test_queues.py b/tests/test_queues.py new file mode 100644 index 000000000..b5b49ef61 --- /dev/null +++ b/tests/test_queues.py @@ -0,0 +1,107 @@ +import unittest + +from algorithms.queues import ( + ArrayQueue, LinkedListQueue, + max_sliding_window, + reconstruct_queue, + PriorityQueue +) + + +class TestQueue(unittest.TestCase): + """ + Test suite for the Queue data structures. + """ + + def test_ArrayQueue(self): + queue = ArrayQueue() + queue.enqueue(1) + queue.enqueue(2) + queue.enqueue(3) + + # test __iter__() + it = iter(queue) + self.assertEqual(1, next(it)) + self.assertEqual(2, next(it)) + self.assertEqual(3, next(it)) + self.assertRaises(StopIteration, next, it) + + # test __len__() + self.assertEqual(3, len(queue)) + + # test is_empty() + self.assertFalse(queue.is_empty()) + + # test peek() + self.assertEqual(1, queue.peek()) + + # test dequeue() + self.assertEqual(1, queue.dequeue()) + self.assertEqual(2, queue.dequeue()) + self.assertEqual(3, queue.dequeue()) + + self.assertTrue(queue.is_empty()) + + def test_LinkedListQueue(self): + queue = LinkedListQueue() + queue.enqueue(1) + queue.enqueue(2) + queue.enqueue(3) + + # test __iter__() + it = iter(queue) + self.assertEqual(1, next(it)) + self.assertEqual(2, next(it)) + self.assertEqual(3, next(it)) + self.assertRaises(StopIteration, next, it) + + # test __len__() + self.assertEqual(3, len(queue)) + + # test is_empty() + self.assertFalse(queue.is_empty()) + + # test peek() + self.assertEqual(1, queue.peek()) + + # test dequeue() + self.assertEqual(1, queue.dequeue()) + self.assertEqual(2, queue.dequeue()) + self.assertEqual(3, queue.dequeue()) + + self.assertTrue(queue.is_empty()) + + +class TestSuite(unittest.TestCase): + def test_max_sliding_window(self): + array = [1, 3, -1, -3, 5, 3, 6, 7] + self.assertEqual(max_sliding_window(array, k=5), [5, 5, 6, 7]) + self.assertEqual(max_sliding_window(array, k=3), [3, 3, 5, 5, 6, 7]) + self.assertEqual(max_sliding_window(array, k=7), [6, 7]) + + array = [8, 5, 10, 7, 9, 4, 15, 12, 90, 13] + self.assertEqual(max_sliding_window(array, k=4), [10, 10, 10, 15, 15, 90, 90]) + self.assertEqual(max_sliding_window(array, k=7), [15, 15, 90, 90]) + self.assertEqual(max_sliding_window(array, k=2), [8, 10, 10, 9, 9, 15, 15, 90, 90]) + + def test_reconstruct_queue(self): + self.assertEqual([[5, 0], [7, 0], [5, 2], [6, 1], [4, 4], [7, 1]], + reconstruct_queue([[7, 0], [4, 4], [7, 1], [5, 0], [6, 1], [5, 2]])) + + +class TestPriorityQueue(unittest.TestCase): + """Test suite for the PriorityQueue data structures. + """ + + def test_PriorityQueue(self): + queue = PriorityQueue([3, 4, 1, 6]) + self.assertEqual(4, queue.size()) + self.assertEqual(1, queue.pop()) + self.assertEqual(3, queue.size()) + queue.push(2) + self.assertEqual(4, queue.size()) + self.assertEqual(2, queue.pop()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_search.py b/tests/test_search.py new file mode 100644 index 000000000..60d8919f7 --- /dev/null +++ b/tests/test_search.py @@ -0,0 +1,127 @@ +from algorithms.search import ( + binary_search, binary_search_recur, + first_occurrence, + last_occurrence, + linear_search, + search_insert, + two_sum, two_sum1, two_sum2, + search_range, + find_min_rotate, find_min_rotate_recur, + search_rotate, search_rotate_recur, + jump_search, + next_greatest_letter, next_greatest_letter_v1, next_greatest_letter_v2 +) + +import unittest + +class TestSuite(unittest.TestCase): + + def test_first_occurrence(self): + def helper(array, query): + idx = array.index(query) if query in array else None + return idx + array = [1, 1, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 6, 6, 6] + self.assertEqual(first_occurrence(array, 1), helper(array, 1)) + self.assertEqual(first_occurrence(array, 3), helper(array, 3)) + self.assertEqual(first_occurrence(array, 5), helper(array, 5)) + self.assertEqual(first_occurrence(array, 6), helper(array, 6)) + self.assertEqual(first_occurrence(array, 7), helper(array, 7)) + self.assertEqual(first_occurrence(array, -1), helper(array, -1)) + + def test_binary_search(self): + array = [1, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 6] + self.assertEqual(10, binary_search(array, 5)) + self.assertEqual(11, binary_search(array, 6)) + self.assertEqual(None, binary_search(array, 7)) + self.assertEqual(None, binary_search(array, -1)) + # Test binary_search_recur + self.assertEqual(10, binary_search_recur(array, 0, 11, 5)) + self.assertEqual(11, binary_search_recur(array, 0, 11, 6)) + self.assertEqual(-1, binary_search_recur(array, 0, 11, 7)) + self.assertEqual(-1, binary_search_recur(array, 0, 11, -1)) + + def test_last_occurrence(self): + array = [1, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 6, 6, 6] + self.assertEqual(5, last_occurrence(array, 3)) + self.assertEqual(10, last_occurrence(array, 5)) + self.assertEqual(None, last_occurrence(array, 7)) + self.assertEqual(0, last_occurrence(array, 1)) + self.assertEqual(13, last_occurrence(array, 6)) + + def test_linear_search(self): + array = [1, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 6, 6, 6] + self.assertEqual(6, linear_search(array, 4)) + self.assertEqual(10, linear_search(array, 5)) + self.assertEqual(-1, linear_search(array, 7)) + self.assertEqual(-1, linear_search(array, -1)) + + def test_search_insert(self): + array = [1,3,5,6] + self.assertEqual(2, search_insert(array, 5)) + self.assertEqual(1, search_insert(array, 2)) + self.assertEqual(4, search_insert(array, 7)) + self.assertEqual(0, search_insert(array, 0)) + + def test_two_sum(self): + array = [2, 7, 11, 15] + # test two_sum + self.assertEqual([1, 2], two_sum(array, 9)) + self.assertEqual([2, 4], two_sum(array, 22)) + # test two_sum1 + self.assertEqual([1, 2], two_sum1(array, 9)) + self.assertEqual([2, 4], two_sum1(array, 22)) + # test two_sum2 + self.assertEqual([1, 2], two_sum2(array, 9)) + self.assertEqual([2, 4], two_sum2(array, 22)) + + def test_search_range(self): + array = [5, 7, 7, 8, 8, 8, 10] + self.assertEqual([3, 5], search_range(array, 8)) + self.assertEqual([1, 2], search_range(array, 7)) + self.assertEqual([-1, -1], search_range(array, 11)) + + def test_find_min_rotate(self): + array = [4, 5, 6, 7, 0, 1, 2] + self.assertEqual(0, find_min_rotate(array)) + array = [10, 20, -1, 0, 1, 2, 3, 4, 5] + self.assertEqual(-1, find_min_rotate(array)) + # Test find min using recursion + array = [4, 5, 6, 7, 0, 1, 2] + self.assertEqual(0, find_min_rotate_recur(array, 0, 6)) + array = [10, 20, -1, 0, 1, 2, 3, 4, 5] + self.assertEqual(-1, find_min_rotate_recur(array, 0, 8)) + + def test_search_rotate(self): + array = [15, 16, 19, 20, 25, 1, 3, 4, 5, 7, 10, 14] + self.assertEqual(8, search_rotate(array, 5)) + self.assertEqual(-1, search_rotate(array, 9)) + self.assertEqual(8, search_rotate_recur(array, 0, 11, 5)) + self.assertEqual(-1, search_rotate_recur(array, 0, 11, 9)) + + def test_jump_search(self): + array = [1, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 6] + self.assertEqual(10, jump_search(array, 5)) + self.assertEqual(2, jump_search(array, 3)) + self.assertEqual(-1, jump_search(array, 7)) + self.assertEqual(-1, jump_search(array, -1)) + + def test_next_greatest_letter(self): + letters = ["c", "f", "j"] + target = "a" + self.assertEqual("c", next_greatest_letter(letters, target)) + self.assertEqual("c", next_greatest_letter_v1(letters, target)) + self.assertEqual("c", next_greatest_letter_v2(letters, target)) + letters = ["c", "f", "j"] + target = "d" + self.assertEqual("f", next_greatest_letter(letters, target)) + self.assertEqual("f", next_greatest_letter_v1(letters, target)) + self.assertEqual("f", next_greatest_letter_v2(letters, target)) + letters = ["c", "f", "j"] + target = "j" + self.assertEqual("c", next_greatest_letter(letters, target)) + self.assertEqual("c", next_greatest_letter_v1(letters, target)) + self.assertEqual("c", next_greatest_letter_v2(letters, target)) + +if __name__ == '__main__': + + unittest.main() diff --git a/tests/test_sort.py b/tests/test_sort.py new file mode 100644 index 000000000..650f33920 --- /dev/null +++ b/tests/test_sort.py @@ -0,0 +1,123 @@ +from algorithms.sort import ( + bitonic_sort, + bogo_sort, + bubble_sort, + comb_sort, + counting_sort, + cycle_sort, + max_heap_sort, min_heap_sort, + insertion_sort, + merge_sort, + pancake_sort, + quick_sort, + selection_sort, + bucket_sort, + shell_sort, + radix_sort, + gnome_sort, + cocktail_shaker_sort, + top_sort, top_sort_recursive +) + +import unittest + + +class TestSuite(unittest.TestCase): + def test_bogo_sort(self): + self.assertEqual([1, 5, 23], + bogo_sort([1, 23, 5])) + + def test_bitonic_sort(self): + self.assertEqual([1, 2, 3, 5, 23, 57, 65, 1232], + bitonic_sort([1, 3, 2, 5, 65, 23, 57, 1232])) + self.assertEqual([1, 2, 3, 5, 23, 57, 65, 1232], + bitonic_sort([1, 3, 2, 5, 65, 23, 57, 1232],False)) + self.assertEqual([1232, 65, 57, 23, 5, 3, 2, 1], + bitonic_sort([1, 2, 3, 5, 65, 23, 57, 1232],True)) + + def test_bubble_sort(self): + self.assertEqual([1, 5, 23, 57, 65, 1232], + bubble_sort([1, 5, 65, 23, 57, 1232])) + + def test_comb_sort(self): + self.assertEqual([1, 5, 23, 57, 65, 1232], + comb_sort([1, 5, 65, 23, 57, 1232])) + + def test_counting_sort(self): + self.assertEqual([1, 5, 23, 57, 65, 1232], + counting_sort([1, 5, 65, 23, 57, 1232])) + self.assertEqual([-1232, -65, -57, -23, -5, -1], + counting_sort([-1, -5, -65, -23, -57, -1232])) + + def test_cycle_sort(self): + self.assertEqual([1, 5, 23, 57, 65, 1232], + cycle_sort([1, 5, 65, 23, 57, 1232])) + + def test_heap_sort(self): + self.assertEqual([1, 5, 23, 57, 65, 1232], + max_heap_sort([1, 5, 65, 23, 57, 1232])) + self.assertEqual([1, 5, 23, 57, 65, 1232], + min_heap_sort([1, 5, 65, 23, 57, 1232])) + + def test_insertion_sort(self): + self.assertEqual([1, 5, 23, 57, 65, 1232], + insertion_sort([1, 5, 65, 23, 57, 1232])) + + def test_merge_sort(self): + self.assertEqual([1, 5, 23, 57, 65, 1232], + merge_sort([1, 5, 65, 23, 57, 1232])) + + def test_pancake_sort(self): + self.assertEqual([1, 5, 23, 57, 65, 1232], + pancake_sort([1, 5, 65, 23, 57, 1232])) + + def test_quick_sort(self): + self.assertEqual([1, 5, 23, 57, 65, 1232], + quick_sort([1, 5, 65, 23, 57, 1232])) + + def test_selection_sort(self): + self.assertEqual([1, 5, 23, 57, 65, 1232], + selection_sort([1, 5, 65, 23, 57, 1232])) + + def test_bucket_sort(self): + self.assertEqual([1, 5, 23, 57, 65, 1232], + bucket_sort([1, 5, 65, 23, 57, 1232])) + + def test_shell_sort(self): + self.assertEqual([1, 5, 23, 57, 65, 1232], + shell_sort([1, 5, 65, 23, 57, 1232])) + + def test_radix_sort(self): + self.assertEqual([1, 5, 23, 57, 65, 1232], + radix_sort([1, 5, 65, 23, 57, 1232])) + + def test_gnome_sort(self): + self.assertEqual([1, 5, 23, 57, 65, 1232], + gnome_sort([1, 5, 65, 23, 57, 1232])) + + def test_cocktail_shaker_sort(self): + self.assertEqual([1, 5, 23, 57, 65, 1232], + cocktail_shaker_sort([1, 5, 65, 23, 57, 1232])) + +class TestTopSort(unittest.TestCase): + def setUp(self): + self.depGraph = { + "a" : [ "b" ], + "b" : [ "c" ], + "c" : [ 'e'], + 'e' : [ 'g' ], + "d" : [ ], + "f" : ["e" , "d"], + "g" : [ ] + } + + def test_topsort(self): + res = top_sort_recursive(self.depGraph) + #print(res) + self.assertTrue(res.index('g') < res.index('e')) + res = top_sort(self.depGraph) + self.assertTrue(res.index('g') < res.index('e')) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_stack.py b/tests/test_stack.py new file mode 100644 index 000000000..d04b27f14 --- /dev/null +++ b/tests/test_stack.py @@ -0,0 +1,150 @@ +from algorithms.stack import ( + first_is_consecutive, second_is_consecutive, + is_sorted, + remove_min, + first_stutter, second_stutter, + first_switch_pairs, second_switch_pairs, + is_valid, + simplify_path, + ArrayStack, LinkedListStack, + OrderedStack +) + +import unittest +class TestSuite(unittest.TestCase): + def test_is_consecutive(self): + self.assertTrue(first_is_consecutive([3, 4, 5, 6, 7])) + self.assertFalse(first_is_consecutive([3, 4, 6, 7])) + self.assertFalse(first_is_consecutive([3, 2, 1])) + + self.assertTrue(second_is_consecutive([3, 4, 5, 6, 7])) + self.assertFalse(second_is_consecutive([3, 4, 6, 7])) + self.assertFalse(second_is_consecutive([3, 2, 1])) + + def test_is_sorted(self): + # Test case: bottom [6, 3, 5, 1, 2, 4] top + self.assertFalse(is_sorted([6, 3, 5, 1, 2, 4])) + self.assertTrue(is_sorted([1, 2, 3, 4, 5, 6])) + + def test_remove_min(self): + # Test case: bottom [2, 8, 3, -6, 7, 3] top + self.assertEqual([2, 8, 3, 7, 3], remove_min([2, 8, 3, -6, 7, 3])) + # Test case: 2 smallest value [2, 8, 3, 7, 3] + self.assertEqual([4, 8, 7], remove_min([4, 8, 3, 7, 3])) + + def test_stutter(self): + # Test case: bottom [3, 7, 1, 14, 9] top + self.assertEqual([3, 3, 7, 7, 1, 1, 14, 14, 9, 9], + first_stutter([3, 7, 1, 14, 9])) + self.assertEqual([3, 3, 7, 7, 1, 1, 14, 14, 9, 9], + second_stutter([3, 7, 1, 14, 9])) + + def test_switch_pairs(self): + # Test case: even number of values in stack + # bottom [3, 8, 17, 9, 1, 10] top + self.assertEqual([8, 3, 9, 17, 10, 1], + first_switch_pairs([3, 8, 17, 9, 1, 10])) + self.assertEqual([8, 3, 9, 17, 10, 1], + second_switch_pairs([3, 8, 17, 9, 1, 10])) + # Test case: odd number of values in stack + # bottom [3, 8, 17, 9, 1] top + self.assertEqual([8, 3, 9, 17, 1], + first_switch_pairs([3, 8, 17, 9, 1])) + self.assertEqual([8, 3, 9, 17, 1], + second_switch_pairs([3, 8, 17, 9, 1])) + + def test_is_valid_parenthesis(self): + + self.assertTrue(is_valid("[]")) + self.assertTrue(is_valid("[]()[]")) + self.assertFalse(is_valid("[[[]]")) + self.assertTrue(is_valid("{([])}")) + self.assertFalse(is_valid("(}")) + + def test_simplify_path(self): + p = '/my/name/is/..//keon' + self.assertEqual('/my/name/keon', simplify_path(p)) + + +class TestStack(unittest.TestCase): + def test_ArrayStack(self): + stack = ArrayStack() + stack.push(1) + stack.push(2) + stack.push(3) + + # test __iter__() + it = iter(stack) + self.assertEqual(3, next(it)) + self.assertEqual(2, next(it)) + self.assertEqual(1, next(it)) + self.assertRaises(StopIteration, next, it) + + # test __len__() + self.assertEqual(3, len(stack)) + + # test __str__() + self.assertEqual(str(stack), "Top-> 3 2 1") + + # test is_empty() + self.assertFalse(stack.is_empty()) + + # test peek() + self.assertEqual(3, stack.peek()) + + # test pop() + self.assertEqual(3, stack.pop()) + self.assertEqual(2, stack.pop()) + self.assertEqual(1, stack.pop()) + + self.assertTrue(stack.is_empty()) + + def test_LinkedListStack(self): + stack = LinkedListStack() + + stack.push(1) + stack.push(2) + stack.push(3) + + # test __iter__() + it = iter(stack) + self.assertEqual(3, next(it)) + self.assertEqual(2, next(it)) + self.assertEqual(1, next(it)) + self.assertRaises(StopIteration, next, it) + + # test __len__() + self.assertEqual(3, len(stack)) + + # test __str__() + self.assertEqual(str(stack), "Top-> 3 2 1") + + # test is_empty() + self.assertFalse(stack.is_empty()) + + # test peek() + self.assertEqual(3, stack.peek()) + + # test pop() + self.assertEqual(3, stack.pop()) + self.assertEqual(2, stack.pop()) + self.assertEqual(1, stack.pop()) + + self.assertTrue(stack.is_empty()) + +class TestOrderedStack(unittest.TestCase): + def test_OrderedStack(self): + stack = OrderedStack() + self.assertTrue(stack.is_empty()) + stack.push(1) + stack.push(4) + stack.push(3) + stack.push(6) + "bottom - > 1 3 4 6 " + self.assertEqual(6, stack.pop()) + self.assertEqual(4, stack.peek()) + self.assertEqual(3, stack.size()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_strings.py b/tests/test_strings.py new file mode 100644 index 000000000..4474b75b2 --- /dev/null +++ b/tests/test_strings.py @@ -0,0 +1,522 @@ +from algorithms.strings import ( + add_binary, + match_symbol, match_symbol_1, bracket, + decode_string, + delete_reoccurring_characters, + domain_name_1, domain_name_2, + encode, decode, + group_anagrams, + int_to_roman, + is_palindrome, is_palindrome_reverse, + is_palindrome_two_pointer, is_palindrome_stack, + is_rotated, is_rotated_v1, + license_number, + make_sentence, + is_merge_recursive, is_merge_iterative, + multiply, + is_one_edit, is_one_edit2, + rabin_karp, + ultra_pythonic, iterative, recursive, pythonic, + reverse_vowel, + reverse_words, + roman_to_int, + strip_url_params1, strip_url_params2, strip_url_params3, + is_valid_coordinates_0, is_valid_coordinates_1, + is_valid_coordinates_regular_expression, + word_squares, + convert_morse_word, unique_morse, + judge_circle, + strong_password, + caesar_cipher, + contain_string, + count_binary_substring, + repeat_string, + text_justification, + min_distance, + longest_common_prefix_v1, longest_common_prefix_v2, longest_common_prefix_v3, + rotate, + first_unique_char, + repeat_substring +) + +import unittest + + +class TestAddBinary(unittest.TestCase): + """[summary] + Test for the file add_binary.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_add_binary(self): + self.assertEqual("100", add_binary("11", "1")) + self.assertEqual("101", add_binary("100", "1")) + self.assertEqual("10", add_binary("1", "1")) + + +class TestBreakingBad(unittest.TestCase): + """[summary] + Test for the file breaking_bad.py + + Arguments: + unittest {[type]} -- [description] + """ + + def setUp(self): + self.words = ['Amazon', 'Microsoft', 'Google'] + self.symbols = ['i', 'Am', 'cro', 'le', 'abc'] + self.result = ['M[i]crosoft', '[Am]azon', 'Mi[cro]soft', 'Goog[le]'] + + def test_match_symbol(self): + self.assertEqual(self.result, match_symbol(self.words, self.symbols)) + + def test_match_symbol_1(self): + self.assertEqual(['[Am]azon', 'Mi[cro]soft', 'Goog[le]'], match_symbol_1(self.words, self.symbols)) + + def test_bracket(self): + self.assertEqual(('[Am]azon', 'Mi[cro]soft', 'Goog[le]'), bracket(self.words, self.symbols)) + + +class TestDecodeString(unittest.TestCase): + """[summary] + Test for the file decode_string.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_decode_string(self): + self.assertEqual("aaabcbc", decode_string("3[a]2[bc]")) + self.assertEqual("accaccacc", decode_string("3[a2[c]]")) + + +class TestDeleteReoccurring(unittest.TestCase): + """[summary] + Test for the file delete_reoccurring.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_delete_reoccurring_characters(self): + self.assertEqual("abc", delete_reoccurring_characters("aaabcccc")) + + +class TestDomainExtractor(unittest.TestCase): + """[summary] + Test for the file domain_extractor.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_valid(self): + self.assertEqual(domain_name_1("https://github.com/SaadBenn"), "github") + + def test_invalid(self): + self.assertEqual(domain_name_2("http://google.com"), "google") + + +class TestEncodeDecode(unittest.TestCase): + """[summary] + Test for the file encode_decode.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_encode(self): + self.assertEqual("4:keon2:is7:awesome", encode("keon is awesome")) + + def test_decode(self): + self.assertEqual(['keon', 'is', 'awesome'], decode("4:keon2:is7:awesome")) + + +class TestGroupAnagrams(unittest.TestCase): + """[summary] + Test for the file group_anagrams.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_group_anagrams(self): + self.assertEqual([['eat', 'tea', 'ate'], ['tan', 'nat'], ['bat']], \ + group_anagrams(["eat", "tea", "tan", "ate", "nat", "bat"])) + + +class TestIntToRoman(unittest.TestCase): + """[summary] + Test for the file int_to_roman.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_int_to_roman(self): + self.assertEqual("DCXLIV", int_to_roman(644)) + self.assertEqual("I", int_to_roman(1)) + self.assertEqual("MMMCMXCIX", int_to_roman(3999)) + + +class TestIsPalindrome(unittest.TestCase): + """[summary] + Test for the file is_palindrome.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_is_palindrome(self): + # 'Otto' is a old german name. + self.assertTrue(is_palindrome("Otto")) + self.assertFalse(is_palindrome("house")) + + def test_is_palindrome_reverse(self): + # 'Otto' is a old german name. + self.assertTrue(is_palindrome_reverse("Otto")) + self.assertFalse(is_palindrome_reverse("house")) + + def test_is_palindrome_two_pointer(self): + # 'Otto' is a old german name. + self.assertTrue(is_palindrome_two_pointer("Otto")) + self.assertFalse(is_palindrome_two_pointer("house")) + + def test_is_palindrome_stack(self): + # 'Otto' is a old german name. + self.assertTrue(is_palindrome_stack("Otto")) + self.assertFalse(is_palindrome_stack("house")) + + +class TestIsRotated(unittest.TestCase): + """[summary] + Test for the file is_rotated.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_is_rotated(self): + self.assertTrue(is_rotated("hello", "hello")) + self.assertTrue(is_rotated("hello", "llohe")) + self.assertFalse(is_rotated("hello", "helol")) + self.assertFalse(is_rotated("hello", "lloh")) + self.assertTrue(is_rotated("", "")) + + def test_is_rotated_v1(self): + self.assertTrue(is_rotated_v1("hello", "hello")) + self.assertTrue(is_rotated_v1("hello", "llohe")) + self.assertFalse(is_rotated_v1("hello", "helol")) + self.assertFalse(is_rotated_v1("hello", "lloh")) + self.assertTrue(is_rotated_v1("", "")) + + +class TestRotated(unittest.TestCase): + def test_rotate(self): + self.assertEqual("llohe", rotate("hello", 2)) + self.assertEqual("hello", rotate("hello", 5)) + self.assertEqual("elloh", rotate("hello", 6)) + self.assertEqual("llohe", rotate("hello", 7)) + + +class TestLicenseNumber(unittest.TestCase): + """[summary] + Test for the file license_number.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_license_number(self): + self.assertEqual("a-b-c-d-f-d-d-f", license_number("a-bc-dfd-df", 1)) + self.assertEqual("ab-cd-fd-df", license_number("a-bc-dfd-df", 2)) + self.assertEqual("ab-cdf-ddf", license_number("a-bc-dfd-df", 3)) + self.assertEqual("abcd-fddf", license_number("a-bc-dfd-df", 4)) + self.assertEqual("abc-dfddf", license_number("a-bc-dfd-df", 5)) + + +class TestMakeSentence(unittest.TestCase): + """[summary] + Test for the file make_sentence.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_make_sentence(self): + dictionarys = ["", "app", "let", "t", "apple", "applet"] + word = "applet" + self.assertTrue(make_sentence(word, dictionarys)) + + +class TestMergeStringChecker(unittest.TestCase): + """[summary] + Test for the file merge_string_checker.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_is_merge_recursive(self): + self.assertTrue(is_merge_recursive("codewars", "cdw", "oears")) + + def test_is_merge_iterative(self): + self.assertTrue(is_merge_iterative("codewars", "cdw", "oears")) + + +class TestMultiplyStrings(unittest.TestCase): + """[summary] + Test for the file multiply_strings.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_multiply(self): + self.assertEqual("23", multiply("1", "23")) + self.assertEqual("529", multiply("23", "23")) + self.assertEqual("0", multiply("0", "23")) + self.assertEqual("1000000", multiply("100", "10000")) + + +class TestOneEditDistance(unittest.TestCase): + """[summary] + Test for the file one_edit_distance.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_is_one_edit(self): + self.assertTrue(is_one_edit("abc", "abd")) + self.assertFalse(is_one_edit("abc", "aed")) + self.assertFalse(is_one_edit("abcd", "abcd")) + + def test_is_one_edit2(self): + self.assertTrue(is_one_edit2("abc", "abd")) + self.assertFalse(is_one_edit2("abc", "aed")) + self.assertFalse(is_one_edit2("abcd", "abcd")) + + +class TestRabinKarp(unittest.TestCase): + """[summary] + Test for the file rabin_karp.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_rabin_karp(self): + self.assertEqual(3, rabin_karp("abc", "zsnabckfkd")) + self.assertEqual(None, rabin_karp("abc", "zsnajkskfkd")) + + +class TestReverseString(unittest.TestCase): + """[summary] + Test for the file reverse_string.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_recursive(self): + self.assertEqual("ereht olleh", recursive("hello there")) + + def test_iterative(self): + self.assertEqual("ereht olleh", iterative("hello there")) + + def test_pythonic(self): + self.assertEqual("ereht olleh", pythonic("hello there")) + + def test_ultra_pythonic(self): + self.assertEqual("ereht olleh", ultra_pythonic("hello there")) + + +class TestReverseVowel(unittest.TestCase): + """[summary] + Test for the file reverse_vowel.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_reverse_vowel(self): + self.assertEqual("holle", reverse_vowel("hello")) + + +class TestReverseWords(unittest.TestCase): + """[summary] + Test for the file reverse_words.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_reverse_words(self): + self.assertEqual("pizza like I and kim keon am I", \ + reverse_words("I am keon kim and I like pizza")) + + +class TestRomanToInt(unittest.TestCase): + """[summary] + Test for the file roman_to_int.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_roman_to_int(self): + self.assertEqual(621, roman_to_int("DCXXI")) + self.assertEqual(1, roman_to_int("I")) + self.assertEqual(3999, roman_to_int("MMMCMXCIX")) + + +# class TestStripUrlParams(unittest.TestCase): +# """[summary] +# Test for the file strip_urls_params.py + +# Arguments: +# unittest {[type]} -- [description] +# """ + +# def test_strip_url_params1(self): +# self.assertEqual(strip_url_params1("www.saadbenn.com?a=1&b=2&a=2"), "www.saadbenn.com?a=1&b=2") +# self.assertEqual(strip_url_params1("www.saadbenn.com?a=1&b=2", ['b']), "www.saadbenn.com?a=1") +# def test_strip_url_params2(self): +# self.assertEqual(strip_url_params2("www.saadbenn.com?a=1&b=2&a=2"), "www.saadbenn.com?a=1&b=2") +# self.assertEqual(strip_url_params2("www.saadbenn.com?a=1&b=2", ['b']), "www.saadbenn.com?a=1") +# def test_strip_url_params3(self): +# self.assertEqual(strip_url_params3("www.saadbenn.com?a=1&b=2&a=2"), "www.saadbenn.com?a=1&b=2") +# self.assertEqual(strip_url_params3("www.saadbenn.com?a=1&b=2", ['b']), "www.saadbenn.com?a=1") + + +class TestValidateCoordinates(unittest.TestCase): + """[summary] + Test for the file validate_coordinates.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_valid(self): + valid_coordinates = ["-23, 25", "4, -3", "90, 180", "-90, -180"] + for coordinate in valid_coordinates: + self.assertTrue(is_valid_coordinates_0(coordinate)) + + def test_invalid(self): + invalid_coordinates = ["23.234, - 23.4234", "99.234, 12.324", "6.325624, 43.34345.345", "0, 1,2", "23.245, 1e1"] + for coordinate in invalid_coordinates: + self.assertFalse(is_valid_coordinates_0(coordinate)) + + +class TestWordSquares(unittest.TestCase): + """[summary] + Test for the file word_squares.py + + Arguments: + unittest {[type]} -- [description] + """ + + def test_word_squares(self): + self.assertEqual([['wall', 'area', 'lead', 'lady'], ['ball', 'area', 'lead', 'lady']], \ + word_squares(["area", "lead", "wall", "lady", "ball"])) + + +class TestUniqueMorse(unittest.TestCase): + def test_convert_morse_word(self): + self.assertEqual("--...-.", convert_morse_word("gin")) + self.assertEqual("--...--.", convert_morse_word("msg")) + + def test_unique_morse(self): + self.assertEqual(2, unique_morse(["gin", "zen", "gig", "msg"])) + + +class TestJudgeCircle(unittest.TestCase): + def test_judge_circle(self): + self.assertTrue(judge_circle("UDLRUD")) + self.assertFalse(judge_circle("LLRU")) + + +class TestStrongPassword(unittest.TestCase): + def test_strong_password(self): + self.assertEqual(3, strong_password(3, "Ab1")) + self.assertEqual(1, strong_password(11, "#Algorithms")) + + +class TestCaesarCipher(unittest.TestCase): + def test_caesar_cipher(self): + self.assertEqual("Lipps_Asvph!", caesar_cipher("Hello_World!", 4)) + self.assertEqual("okffng-Qwvb", caesar_cipher("middle-Outz", 2)) + + +class TestContainString(unittest.TestCase): + def test_contain_string(self): + self.assertEqual(-1, contain_string("mississippi", "issipi")) + self.assertEqual(0, contain_string("Hello World", "")) + self.assertEqual(2, contain_string("hello", "ll")) + + +class TestCountBinarySubstring(unittest.TestCase): + def test_count_binary_substring(self): + self.assertEqual(6, count_binary_substring("00110011")) + self.assertEqual(4, count_binary_substring("10101")) + self.assertEqual(3, count_binary_substring("00110")) + + +class TestCountBinarySubstring(unittest.TestCase): + def test_repeat_string(self): + self.assertEqual(3, repeat_string("abcd", "cdabcdab")) + self.assertEqual(4, repeat_string("bb", "bbbbbbb")) + + +class TestTextJustification(unittest.TestCase): + def test_text_justification(self): + self.assertEqual(["This is an", + "example of text", + "justification. "], + + text_justification(["This", "is", "an", "example", "of", "text", "justification."] + , 16) + ) + + self.assertEqual(["What must be", + "acknowledgment ", + "shall be "], + + text_justification(["What", "must", "be", "acknowledgment", "shall", "be"] + , 16) + ) + +class TestMinDistance(unittest.TestCase): + def test_min_distance(self): + self.assertEqual(2, min_distance("sea", "eat")) + self.assertEqual(6, min_distance("abAlgocrithmf", "Algorithmmd")) + +class TestLongestCommonPrefix(unittest.TestCase): + def test_longest_common_prefix(self): + # Test first solution + self.assertEqual("fl", longest_common_prefix_v1(["flower","flow","flight"])) + self.assertEqual("", longest_common_prefix_v1(["dog","racecar","car"])) + # Test second solution + self.assertEqual("fl", longest_common_prefix_v2(["flower","flow","flight"])) + self.assertEqual("", longest_common_prefix_v2(["dog","racecar","car"])) + # Test third solution + self.assertEqual("fl", longest_common_prefix_v3(["flower","flow","flight"])) + self.assertEqual("", longest_common_prefix_v3(["dog","racecar","car"])) + +class TestFirstUniqueChar(unittest.TestCase): + def test_first_unique_char(self): + self.assertEqual(0, first_unique_char("leetcode")) + self.assertEqual(2, first_unique_char("loveleetcode")) + +class TestRepeatSubstring(unittest.TestCase): + def test_repeat_substring(self): + self.assertTrue(repeat_substring("abab")) + self.assertFalse(repeat_substring("aba")) + self.assertTrue(repeat_substring("abcabcabcabc")) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_tree.py b/tests/test_tree.py new file mode 100644 index 000000000..a9d53ca6d --- /dev/null +++ b/tests/test_tree.py @@ -0,0 +1,46 @@ +from algorithms.tree.traversal import ( + preorder, + preorder_rec, + postorder, + postorder_rec +) + +import unittest + +class Node: + + def __init__(self, val, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class TestTraversal(unittest.TestCase): + + def test_preorder(self): + n1 = Node(100) + n2 = Node(50) + n3 = Node(150) + n4 = Node(25) + n5 = Node(75) + n6 = Node(125) + n7 = Node(175) + n1.left, n1.right = n2, n3 + n2.left, n2.right = n4, n5 + n3.left, n3.right = n6, n7 + self.assertEqual([100, 50, 25, 75, 150, 125, 175], preorder(n1)) + self.assertEqual([100, 50, 25, 75, 150, 125, 175], preorder_rec(n1)) + + def test_postorder(self): + n1 = Node(100) + n2 = Node(50) + n3 = Node(150) + n4 = Node(25) + n5 = Node(75) + n6 = Node(125) + n7 = Node(175) + n1.left, n1.right = n2, n3 + n2.left, n2.right = n4, n5 + n3.left, n3.right = n6, n7 + self.assertEqual([25, 75, 50, 125, 175, 150, 100], postorder(n1)) + self.assertEqual([25, 75, 50, 125, 175, 150, 100], postorder_rec(n1)) diff --git a/tests/test_unix.py b/tests/test_unix.py new file mode 100644 index 000000000..e7a6c6349 --- /dev/null +++ b/tests/test_unix.py @@ -0,0 +1,42 @@ +from algorithms.unix import ( + join_with_slash, + full_path, + split, + simplify_path_v1, simplify_path_v2 +) +import os +import unittest +class TestUnixPath(unittest.TestCase): + def test_join_with_slash(self): + self.assertEqual("path/to/dir/file", join_with_slash("path/to/dir/", "file")) + self.assertEqual("path/to/dir/file", join_with_slash("path/to/dir", "file")) + self.assertEqual("http://algorithms/part", join_with_slash("http://algorithms", "part")) + self.assertEqual("http://algorithms/part", join_with_slash("http://algorithms/", "part")) + + def test_full_path(self): + file_name = "file_name" + # Test full path relative + expect_path = "{}/{}".format(os.getcwd(), file_name) + self.assertEqual(expect_path, full_path(file_name)) + # Test full path with expanding user + # ~/file_name + expect_path = "{}/{}".format(os.path.expanduser('~'), file_name) + self.assertEqual(expect_path, full_path("~/{}".format(file_name))) + + def test_split(self): + # Test url path + path = "https://algorithms/unix/test.py" + expect_result = split(path) + self.assertEqual("https://algorithms/unix", expect_result[0]) + self.assertEqual("test.py", expect_result[1]) + # Test file path + path = "algorithms/unix/test.py" + expect_result = split(path) + self.assertEqual("algorithms/unix", expect_result[0]) + self.assertEqual("test.py", expect_result[1]) + + def test_simplify_path(self): + self.assertEqual("/", simplify_path_v1("/../")) + self.assertEqual("/home/foo", simplify_path_v1("/home//foo/")) + self.assertEqual("/", simplify_path_v2("/../")) + self.assertEqual("/home/foo", simplify_path_v2("/home//foo/")) diff --git a/tmp/temporary.md b/tmp/temporary.md deleted file mode 100644 index 7f1f3247b..000000000 --- a/tmp/temporary.md +++ /dev/null @@ -1,29 +0,0 @@ -Given input which is a vector of (user name, log-in time, log-out time), -output time series which will have number of users logged in at each given -time slot in the input, output should only contain time slots which are given -in input for example if the input is "September", 1.2, 4.5), -("June", 3.1, 6.7), ("August", 8.9, 10.3) output should contain only -1.2, 3.1, 4.5, 3.1, 6.7, 8.9, 10.3 -Example: /* [ ("September", 1.2, 4.5), ("June", 3.1, 6.7), ("August", 8.9, 10.3) ] => -[(1.2, 1), (3.1, 2), (4.5, 1), (6.7, 0), (8.9, 1), (10.3, 0)] */ - - -• Sweeping line method - § record the time instance and it type: log in, log out - § Sort the time instances. Keep a variable to record the number of logged in users, number - § For a time instance, - □ if it is log-in type, number++, print -else number--, print - - -1. Constant time random access hash implementation - -2. Efficient elevator API - -3. Ransom note - -4. Median of k unsorted arrays - -5. Design of a task scheduler - -6. Custom comparator diff --git a/tox.ini b/tox.ini new file mode 100644 index 000000000..b6f350603 --- /dev/null +++ b/tox.ini @@ -0,0 +1,64 @@ +[tox] +envlist = + py34 + py35 + py36 + coverage + +[testenv] +passenv = TRAVIS TRAVIS_* +basepython = + py34: python3.4 + py35: python3.5 + py36: python3.6 + py37: python3.7 +deps = + coverage + coveralls +commands = + coverage run --source=tests,algorithms -m unittest discover tests + coverage report -m + coveralls + +[testenv:py34] +passenv = CI TRAVIS TRAVIS_* +basepython = + python3.4 +deps = + pytest +commands = + python3 -m unittest discover tests + python3 -m pytest tests + +[testenv:py35] +passenv = CI TRAVIS TRAVIS_* +basepython = + python3.5 +deps = + pytest +commands = + python3 -m unittest discover tests + python3 -m pytest tests + +[testenv:py36] +passenv = CI TRAVIS TRAVIS_* +basepython = + python3.4 +deps = + pytest +commands = + python3 -m unittest discover tests + python3 -m pytest tests + +[testenv:coverage] +passenv = CI TRAVIS TRAVIS_* +skip_install = True +basepython = + python3.5 +commands = + coverage run --source=tests,algorithms -m unittest discover tests + coverage report -m + coveralls +deps = + coverage + coveralls diff --git a/tree.md b/tree.md index 37d6f316b..adaa28165 100644 --- a/tree.md +++ b/tree.md @@ -1,27 +1,32 @@ ``` -. -├── array -│   ├── circular_counter.py +algorithms +├── arrays +│   ├── delete_nth.py │   ├── flatten.py │   ├── garage.py +│   ├── __init__.py +│   ├── josephus.py │   ├── longest_non_repeat.py +│   ├── max_ones_index.py │   ├── merge_intervals.py │   ├── missing_ranges.py +│   ├── move_zeros.py │   ├── plus_one.py -│   ├── rotate_array.py -│   ├── summary_ranges.py +│   ├── rotate.py +│   ├── summarize_ranges.py │   ├── three_sum.py │   └── two_sum.py ├── backtrack +│   ├── add_operators.py │   ├── anagram.py │   ├── array_sum_combinations.py │   ├── combination_sum.py -│   ├── expression_add_operators.py │   ├── factor_combinations.py -│   ├── general_solution.md +│   ├── find_words.py │   ├── generate_abbreviations.py │   ├── generate_parenthesis.py +│   ├── __init__.py │   ├── letter_combination.py │   ├── palindrome_partitioning.py │   ├── pattern_match.py @@ -33,22 +38,27 @@ │   ├── shortest_distance_from_all_buildings.py │   └── word_ladder.py ├── bit +│   ├── add_bitwise_operator.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 +│   ├── __init__.py +│   ├── insert_bit.py │   ├── power_of_two.py +│   ├── remove_bit.py │   ├── reverse_bits.py │   ├── single_number2.py +│   ├── single_number3.py │   ├── single_number.py -│   └── subsets.py -├── design -│   ├── alarm_system.md -│   ├── all_o_one_ds.md -│   ├── calculator.md -│   ├── excel_table.md -│   ├── LRUcache.md -│   ├── nearby_drivers.md -│   ├── ride_sharing.md -│   ├── task_runner.md -│   └── twitter_feeds.md +│   ├── subsets.py +│   └── swap_pair.py +├── calculator +│   └── math_parser.py ├── dfs │   ├── all_factors.py │   ├── count_islands.py @@ -58,33 +68,61 @@ ├── dp │   ├── buy_sell_stock.py │   ├── climbing_stairs.py +│   ├── coin_change.py │   ├── combination_sum.py +│   ├── egg_drop.py +│   ├── fib.py │   ├── house_robber.py +│   ├── job_scheduling.py +│   ├── knapsack.py │   ├── longest_increasing.py +│   ├── matrix_chain_order.py │   ├── max_product_subarray.py │   ├── max_subarray.py +│   ├── min_cost_path.py │   ├── num_decodings.py │   ├── regex_matching.py +│   ├── rod_cut.py │   └── word_break.py ├── graph +│   ├── checkDiGraphStronglyConnected.py │   ├── clone_graph.py +│   ├── cycle_detection.py +│   ├── dijkstra.py +│   ├── find_all_cliques.py │   ├── find_path.py │   ├── graph.py +│   ├── __init__.py +│   ├── markov_chain.py +│   ├── minimum_spanning_tree.py +│   ├── pathBetweenTwoVerticesInDiGraph.py +│   ├── satisfiability.py +│   ├── tarjan.py +│   ├── Transitive_Closure_DFS.py │   └── traversal.py ├── heap +│   ├── binary_heap.py +│   ├── __init__.py │   ├── merge_sorted_k_lists.py │   ├── skyline.py │   └── sliding_window_max.py +├── __init__.py ├── linkedlist │   ├── add_two_numbers.py │   ├── copy_random_pointer.py │   ├── delete_node.py │   ├── first_cyclic_node.py +│   ├── __init__.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 @@ -92,64 +130,110 @@ │   ├── hashtable.py │   ├── longest_common_subsequence.py │   ├── randomized_set.py +│   ├── separate_chaining_hashtable.py │   └── valid_sudoku.py -├── math +├── maths +│   ├── base_conversion.py +│   ├── combination.py +│   ├── extended_gcd.py +│   ├── factorial.py +│   ├── gcd.py │   ├── generate_strobogrammtic.py +│   ├── __init__.py │   ├── is_strobogrammatic.py +│   ├── next_bigger.py +│   ├── next_perfect_square.py │   ├── nth_digit.py -│   └── sqrt_precision_factor.py +│   ├── prime_check.py +│   ├── primes_sieve_of_eratosthenes.py +│   ├── pythagoras.py +│   ├── rabin_miller.py +│   ├── rsa.py +│   ├── sqrt_precision_factor.py +│   └── summing_digits.py ├── matrix │   ├── bomb_enemy.py +│   ├── copy_transform.py +│   ├── count_paths.py │   ├── matrix_rotation.txt │   ├── rotate_image.py +│   ├── search_in_sorted_matrix.py │   ├── sparse_dot_vector.py │   ├── sparse_mul.py -│   └── spiral_traversal.py -├── queue +│   ├── spiral_traversal.py +│   └── sudoku_validator.py +├── queues │   ├── __init__.py │   ├── max_sliding_window.py │   ├── moving_average.py +│   ├── priority_queue.py │   ├── queue.py │   ├── reconstruct_queue.py │   └── zigzagiterator.py -├── README.md ├── search │   ├── binary_search.py -│   ├── count_elem.py +│   ├── find_min_rotate.py │   ├── first_occurance.py -│   └── last_occurance.py +│   ├── __init__.py +│   ├── jump_search.py +│   ├── last_occurance.py +│   ├── linear_search.py +│   ├── search_insert.py +│   ├── search_range.py +│   ├── search_rotate.py +│   └── two_sum.py ├── set -│   └── randomized_set.py +│   ├── randomized_set.py +│   └── set_covering.py ├── sort +│   ├── bitonic_sort.py +│   ├── bogo_sort.py +│   ├── bubble_sort.py +│   ├── bucket_sort.py +│   ├── cocktail_shaker_sort.py +│   ├── comb_sort.py +│   ├── counting_sort.py +│   ├── gnome_sort.py +│   ├── heap_sort.py +│   ├── __init__.py │   ├── insertion_sort.py │   ├── meeting_rooms.py │   ├── merge_sort.py │   ├── quick_sort.py +│   ├── radix_sort.py │   ├── selection_sort.py +│   ├── shell_sort.py │   ├── sort_colors.py -│   ├── topsort.py +│   ├── top_sort.py │   └── wiggle_sort.py ├── stack │   ├── __init__.py -│   ├── __init__.pyc +│   ├── is_consecutive.py +│   ├── is_sorted.py │   ├── longest_abs_path.py -│   ├── __pycache__ -│   │   ├── __init__.cpython-35.pyc -│   │   └── stack.cpython-35.pyc +│   ├── ordered_stack.py +│   ├── remove_min.py │   ├── simplify_path.py │   ├── stack.py -│   ├── stack.pyc +│   ├── stutter.py +│   ├── switch_pairs.py │   └── valid_parenthesis.py -├── string +├── strings │   ├── add_binary.py │   ├── breaking_bad.py │   ├── decode_string.py +│   ├── delete_reoccurring.py +│   ├── domain_extractor.py │   ├── encode_decode.py +│   ├── fizzbuzz.py │   ├── group_anagrams.py +│   ├── __init__.py │   ├── int_to_roman.py │   ├── is_palindrome.py +│   ├── is_rotated.py │   ├── license_number.py │   ├── make_sentence.py +│   ├── merge_string_checker.py │   ├── multiply_strings.py │   ├── one_edit_distance.py │   ├── rabin_karp.py @@ -157,20 +241,28 @@ │   ├── reverse_vowel.py │   ├── reverse_words.py │   ├── roman_to_int.py +│   ├── strip_url_params.py +│   ├── validate_coordinates.py │   └── word_squares.py -├── tmp -│   └── temporary.md ├── tree +│   ├── avl +│   │   ├── avl.py +│   │   └── __init__.py │   ├── binary_tree_paths.py │   ├── bintree2list.py │   ├── bst │   │   ├── array2bst.py │   │   ├── bst_closest_value.py │   │   ├── BSTIterator.py +│   │   ├── bst.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 @@ -188,7 +280,11 @@ │   ├── path_sum2.py │   ├── path_sum.py │   ├── pretty_print.py +│   ├── red_black_tree +│   │   └── red_black_tree.py │   ├── same_tree.py +│   ├── segment_tree +│   │   └── segment_tree.py │   ├── traversal │   │   ├── inorder.py │   │   ├── level_order.py @@ -197,10 +293,8 @@ │   └── trie │   ├── add_and_search.py │   └── trie.py -├── tree.md └── union-find └── count_islands.py -26 directories, 173 files - +27 directories, 267 files ``` diff --git a/tree/binary_tree_paths.py b/tree/binary_tree_paths.py deleted file mode 100644 index aad327696..000000000 --- a/tree/binary_tree_paths.py +++ /dev/null @@ -1,14 +0,0 @@ -def binaryTreePaths(root): - res = [] - if not root: - return res - DFS(res, root, str(root.val)) - return res - -def DFS(res, root, cur): - if not root.left and not root.right: - res.append(cur) - if root.left: - DFS(res, root.left, cur+'->'+str(root.left.val)) - if root.right: - DFS(res, root.right, cur+'->'+str(root.right.val)) diff --git a/tree/bst/array2bst.py b/tree/bst/array2bst.py deleted file mode 100644 index cc147eac2..000000000 --- a/tree/bst/array2bst.py +++ /dev/null @@ -1,19 +0,0 @@ -""" -Given an array where elements are sorted in ascending order, -convert it to a height balanced BST. -""" -# class TreeNode(object): -# def __init__(self, x): -# self.val = x -# self.left = None -# self.right = None - - -def array2bst(nums): - if not nums: - return None - mid = len(nums)//2 - node = Node(nums[mid]) - node.left = array2bst(nums[:mid]) - node.right = array2bst(nums[mid+1:]) - return node diff --git a/tree/is_subtree.py b/tree/is_subtree.py deleted file mode 100644 index 9a6c91f95..000000000 --- a/tree/is_subtree.py +++ /dev/null @@ -1,69 +0,0 @@ -# Given two binary trees s and t, check if t is a subtree of s. -# A subtree of a tree t is a tree consisting of a node in t and -# all of its descendants in t. - -# Example 1: - -# Given s: - - # 3 - # / \ - # 4 5 - # / \ - # 1 2 - -# Given t: - - # 4 - # / \ - # 1 2 -# Return true, because t is a subtree of s. - -# Example 2: - -# Given s: - - # 3 - # / \ - # 4 5 - # / \ - # 1 2 - # / - # 0 - -# Given t: - - # 3 - # / - # 4 - # / \ - # 1 2 -# Return false, because even though t is part of s, -# it does not contain all descendants of t. - -# Follow up: -# What if one tree is significantly lager than the other? - - -def is_subtree(big, small): - flag = False - queue = collections.deque() - queue.append(big) - while queue: - node = queue.popleft() - if node.val == small.val: - flag = comp(node, small) - break - else: - queue.append(node.left) - queue.append(node.right) - return flag - -def comp(p, q): - if not p and not q: - return True - if p and q: - return p.val == q.val and comp(p.left,q.left) and comp(p.right, q.right) - return False - - diff --git a/tree/longest_consecutive.py b/tree/longest_consecutive.py deleted file mode 100644 index 498aaa6ca..000000000 --- a/tree/longest_consecutive.py +++ /dev/null @@ -1,45 +0,0 @@ -# Given a binary tree, find the length of the longest consecutive sequence path. - -# The path refers to any sequence of nodes from some starting node to any node -# in the tree along the parent-child connections. -# The longest consecutive path need to be from parent to child -# (cannot be the reverse). - -# For example, - # 1 - # \ - # 3 - # / \ - # 2 4 - # \ - # 5 -# Longest consecutive sequence path is 3-4-5, so return 3. - # 2 - # \ - # 3 - # / - # 2 - # / - # 1 - - -maxlen = 0 -def longestConsecutive(root): - """ - :type root: TreeNode - :rtype: int - """ - if not root: - return 0 - DFS(root, 0, root.val) - return maxlen - -def DFS(root, cur, target): - if not root: return - if root.val == target: - cur += 1 - else: - cur = 1 - maxlen = max(cur, maxlen) - DFS(root.left, cur, root.val+1) - DFS(root.right, cur, root.val+1) diff --git a/tree/max_path_sum.py b/tree/max_path_sum.py deleted file mode 100644 index 48c3e3fbf..000000000 --- a/tree/max_path_sum.py +++ /dev/null @@ -1,14 +0,0 @@ - -maximum = float("-inf") - -def max_path_sum(root): - helper(root) - return maximum - -def helper(root): - if not root: - return 0 - left = helper(root.left) - right = helper(root.right) - maximum = max(maximum, left+right+root.val) - return root.val + max(left, right) diff --git a/tree/pretty_print.py b/tree/pretty_print.py deleted file mode 100644 index dc58ab3ec..000000000 --- a/tree/pretty_print.py +++ /dev/null @@ -1,20 +0,0 @@ -# a -> Adam -> Book -> 4 -# b -> Bill -> Computer -> 5 - # -> TV -> 6 - # Jill -> Sports -> 1 -# c -> Bill -> Sports -> 3 -# d -> Adam -> Computer -> 3 - # Quin -> Computer -> 3 -# e -> Quin -> Book -> 5 - # -> TV -> 2 -# f -> Adam -> Computer -> 7 - -def treePrint(tree): - for key in tree: - print key, # comma prevents a newline character - treeElem = tree[key] # multiple lookups is expensive, even amortized O(1)! - for subElem in treeElem: - print " -> ", subElem, - if type(subElem) != str: # OP wants indenting after digits - print "\n " # newline and a space to match indenting - print "" # forces a newline