diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 507caa8..0000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,3 +0,0 @@ -# These are supported funding model platforms - -custom: paypal.me/miasteinkirch diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 2eff776..0000000 --- a/.gitignore +++ /dev/null @@ -1,20 +0,0 @@ -*.py[cod] -*~ - -# C extensions -*.so - -# Installer logs -pip-log.txt - -# Unit test / coverage reports -.coverage -.tox -nosetests.xml - -# Translations -*.mo - - -# PyCharm -.idea \ No newline at end of file diff --git a/First_edition_2014/README.md b/First_edition_2014/README.md deleted file mode 100644 index eb1e807..0000000 --- a/First_edition_2014/README.md +++ /dev/null @@ -1,15 +0,0 @@ -## Python, Algorithms and Data Structures (2014) - -[Download PDF (first edition,published by Hanbit Media)](https://github.com/bt3gl/Python-and-Algorithms-and-Data-Structures/blob/master/First_edition_2014/ebook_pdf/book_second_edition.pdf). - - -## To run the snippet: - -Install dependencies in a [virtual environment](https://coderwall.com/p/8-aeka): - -``` -virtualenv venv -source venv/bin/activate -pip install -r requirements.txt -``` - diff --git a/First_edition_2014/ebook_src/trees/__init__.py b/First_edition_2014/ebook_src/trees/__init__.py deleted file mode 100755 index e69de29..0000000 diff --git a/First_edition_2014/requirements.txt b/First_edition_2014/requirements.txt deleted file mode 100644 index 69a3f35..0000000 --- a/First_edition_2014/requirements.txt +++ /dev/null @@ -1,13 +0,0 @@ -Flask==1.0 -SQLAlchemy==0.9.7 -bpython==0.13.1 -coverage==3.7.1 -curtsies==0.0.34 -graphviz==0.4.2 -ipython==0.13.2 -matplotlib==1.3.1 -nose==1.3.0 -numpy==1.8.2 -scapy==2.4.1 -scikit-learn==0.14.1 -scipy==0.12.1 \ No newline at end of file diff --git a/MY_BOOK/600_stars.png b/MY_BOOK/600_stars.png new file mode 100644 index 0000000..ca4a6bb Binary files /dev/null and b/MY_BOOK/600_stars.png differ diff --git a/First_edition_2014/ebook_pdf/book_second_edition.pdf b/MY_BOOK/ebook_pdf/book_second_edition.pdf similarity index 100% rename from First_edition_2014/ebook_pdf/book_second_edition.pdf rename to MY_BOOK/ebook_pdf/book_second_edition.pdf diff --git a/First_edition_2014/ebook_src/abstract_structures/HashTable.py b/MY_BOOK/ebook_src/abstract_structures/HashTableClass.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/HashTable.py rename to MY_BOOK/ebook_src/abstract_structures/HashTableClass.py diff --git a/First_edition_2014/ebook_src/abstract_structures/Queue.py b/MY_BOOK/ebook_src/abstract_structures/QueueClass.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/Queue.py rename to MY_BOOK/ebook_src/abstract_structures/QueueClass.py diff --git a/First_edition_2014/ebook_src/abstract_structures/Stack.py b/MY_BOOK/ebook_src/abstract_structures/Stack.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/Stack.py rename to MY_BOOK/ebook_src/abstract_structures/Stack.py diff --git a/First_edition_2014/ebook_src/abstract_structures/__init__.py b/MY_BOOK/ebook_src/abstract_structures/__init__.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/__init__.py rename to MY_BOOK/ebook_src/abstract_structures/__init__.py diff --git a/First_edition_2014/ebook_src/USEFUL/dynamic_programming/__init__.py b/MY_BOOK/ebook_src/abstract_structures/heap/__init__.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/dynamic_programming/__init__.py rename to MY_BOOK/ebook_src/abstract_structures/heap/__init__.py diff --git a/First_edition_2014/ebook_src/abstract_structures/heap/find_N_largest_smallest_items_seq.py b/MY_BOOK/ebook_src/abstract_structures/heap/find_N_largest_smallest_items_seq.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/heap/find_N_largest_smallest_items_seq.py rename to MY_BOOK/ebook_src/abstract_structures/heap/find_N_largest_smallest_items_seq.py diff --git a/First_edition_2014/ebook_src/abstract_structures/heap/heapify.py b/MY_BOOK/ebook_src/abstract_structures/heap/heapify.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/heap/heapify.py rename to MY_BOOK/ebook_src/abstract_structures/heap/heapify.py diff --git a/First_edition_2014/ebook_src/abstract_structures/heap/merge_sorted_seqs.py b/MY_BOOK/ebook_src/abstract_structures/heap/merge_sorted_seqs.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/heap/merge_sorted_seqs.py rename to MY_BOOK/ebook_src/abstract_structures/heap/merge_sorted_seqs.py diff --git a/First_edition_2014/ebook_src/abstract_structures/heap/priority_queue.py b/MY_BOOK/ebook_src/abstract_structures/heap/priority_queue.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/heap/priority_queue.py rename to MY_BOOK/ebook_src/abstract_structures/heap/priority_queue.py diff --git a/First_edition_2014/ebook_src/USEFUL/oop/__init__.py b/MY_BOOK/ebook_src/abstract_structures/linked_list/__init__.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/oop/__init__.py rename to MY_BOOK/ebook_src/abstract_structures/linked_list/__init__.py diff --git a/First_edition_2014/ebook_src/abstract_structures/linked_list/circular_ll.py b/MY_BOOK/ebook_src/abstract_structures/linked_list/circular_ll.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/linked_list/circular_ll.py rename to MY_BOOK/ebook_src/abstract_structures/linked_list/circular_ll.py diff --git a/First_edition_2014/ebook_src/abstract_structures/linked_list/double_linked_list_fifo.py b/MY_BOOK/ebook_src/abstract_structures/linked_list/double_linked_list_fifo.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/linked_list/double_linked_list_fifo.py rename to MY_BOOK/ebook_src/abstract_structures/linked_list/double_linked_list_fifo.py diff --git a/First_edition_2014/ebook_src/abstract_structures/linked_list/find_kth_from_the_end.py b/MY_BOOK/ebook_src/abstract_structures/linked_list/find_kth_from_the_end.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/linked_list/find_kth_from_the_end.py rename to MY_BOOK/ebook_src/abstract_structures/linked_list/find_kth_from_the_end.py diff --git a/First_edition_2014/ebook_src/abstract_structures/linked_list/linked_list_fifo.py b/MY_BOOK/ebook_src/abstract_structures/linked_list/linked_list_fifo.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/linked_list/linked_list_fifo.py rename to MY_BOOK/ebook_src/abstract_structures/linked_list/linked_list_fifo.py diff --git a/First_edition_2014/ebook_src/abstract_structures/linked_list/linked_list_lifo.py b/MY_BOOK/ebook_src/abstract_structures/linked_list/linked_list_lifo.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/linked_list/linked_list_lifo.py rename to MY_BOOK/ebook_src/abstract_structures/linked_list/linked_list_lifo.py diff --git a/First_edition_2014/ebook_src/abstract_structures/linked_list/node.py b/MY_BOOK/ebook_src/abstract_structures/linked_list/node.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/linked_list/node.py rename to MY_BOOK/ebook_src/abstract_structures/linked_list/node.py diff --git a/First_edition_2014/ebook_src/abstract_structures/linked_list/part_linked_list.py b/MY_BOOK/ebook_src/abstract_structures/linked_list/part_linked_list.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/linked_list/part_linked_list.py rename to MY_BOOK/ebook_src/abstract_structures/linked_list/part_linked_list.py diff --git a/First_edition_2014/ebook_src/abstract_structures/linked_list/sum_linked_list.py b/MY_BOOK/ebook_src/abstract_structures/linked_list/sum_linked_list.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/linked_list/sum_linked_list.py rename to MY_BOOK/ebook_src/abstract_structures/linked_list/sum_linked_list.py diff --git a/First_edition_2014/ebook_src/abstract_structures/heap/__init__.py b/MY_BOOK/ebook_src/abstract_structures/queues/__init__.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/heap/__init__.py rename to MY_BOOK/ebook_src/abstract_structures/queues/__init__.py diff --git a/First_edition_2014/ebook_src/abstract_structures/queues/animal_shelter.py b/MY_BOOK/ebook_src/abstract_structures/queues/animal_shelter.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/queues/animal_shelter.py rename to MY_BOOK/ebook_src/abstract_structures/queues/animal_shelter.py diff --git a/First_edition_2014/ebook_src/abstract_structures/queues/deque.py b/MY_BOOK/ebook_src/abstract_structures/queues/deque.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/queues/deque.py rename to MY_BOOK/ebook_src/abstract_structures/queues/deque.py diff --git a/First_edition_2014/ebook_src/abstract_structures/queues/linked_queue.py b/MY_BOOK/ebook_src/abstract_structures/queues/linked_queue.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/queues/linked_queue.py rename to MY_BOOK/ebook_src/abstract_structures/queues/linked_queue.py diff --git a/First_edition_2014/ebook_src/abstract_structures/queues/palindrome_checker_with_deque.py b/MY_BOOK/ebook_src/abstract_structures/queues/palindrome_checker_with_deque.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/queues/palindrome_checker_with_deque.py rename to MY_BOOK/ebook_src/abstract_structures/queues/palindrome_checker_with_deque.py diff --git a/First_edition_2014/ebook_src/abstract_structures/queues/queue.py b/MY_BOOK/ebook_src/abstract_structures/queues/queue.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/queues/queue.py rename to MY_BOOK/ebook_src/abstract_structures/queues/queue.py diff --git a/First_edition_2014/ebook_src/abstract_structures/linked_list/__init__.py b/MY_BOOK/ebook_src/abstract_structures/stacks/__init__.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/linked_list/__init__.py rename to MY_BOOK/ebook_src/abstract_structures/stacks/__init__.py diff --git a/First_edition_2014/ebook_src/abstract_structures/stacks/dec2bin_with_stack.py b/MY_BOOK/ebook_src/abstract_structures/stacks/dec2bin_with_stack.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/stacks/dec2bin_with_stack.py rename to MY_BOOK/ebook_src/abstract_structures/stacks/dec2bin_with_stack.py diff --git a/First_edition_2014/ebook_src/abstract_structures/stacks/linked_stack.py b/MY_BOOK/ebook_src/abstract_structures/stacks/linked_stack.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/stacks/linked_stack.py rename to MY_BOOK/ebook_src/abstract_structures/stacks/linked_stack.py diff --git a/First_edition_2014/ebook_src/abstract_structures/stacks/reverse_string_with_stack.py b/MY_BOOK/ebook_src/abstract_structures/stacks/reverse_string_with_stack.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/stacks/reverse_string_with_stack.py rename to MY_BOOK/ebook_src/abstract_structures/stacks/reverse_string_with_stack.py diff --git a/First_edition_2014/ebook_src/abstract_structures/stacks/set_of_stacks.py b/MY_BOOK/ebook_src/abstract_structures/stacks/set_of_stacks.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/stacks/set_of_stacks.py rename to MY_BOOK/ebook_src/abstract_structures/stacks/set_of_stacks.py diff --git a/First_edition_2014/ebook_src/abstract_structures/stacks/stack.py b/MY_BOOK/ebook_src/abstract_structures/stacks/stack.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/stacks/stack.py rename to MY_BOOK/ebook_src/abstract_structures/stacks/stack.py diff --git a/First_edition_2014/ebook_src/abstract_structures/stacks/stack_with_min.py b/MY_BOOK/ebook_src/abstract_structures/stacks/stack_with_min.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/stacks/stack_with_min.py rename to MY_BOOK/ebook_src/abstract_structures/stacks/stack_with_min.py diff --git a/First_edition_2014/ebook_src/abstract_structures/stacks/towers_of_hanoi.py b/MY_BOOK/ebook_src/abstract_structures/stacks/towers_of_hanoi.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/stacks/towers_of_hanoi.py rename to MY_BOOK/ebook_src/abstract_structures/stacks/towers_of_hanoi.py diff --git a/First_edition_2014/ebook_src/bitwise/bit_array.py b/MY_BOOK/ebook_src/bitwise_operations/bit_array.py similarity index 100% rename from First_edition_2014/ebook_src/bitwise/bit_array.py rename to MY_BOOK/ebook_src/bitwise_operations/bit_array.py diff --git a/First_edition_2014/ebook_src/bitwise/bitwise.txt b/MY_BOOK/ebook_src/bitwise_operations/bitwise.txt similarity index 100% rename from First_edition_2014/ebook_src/bitwise/bitwise.txt rename to MY_BOOK/ebook_src/bitwise_operations/bitwise.txt diff --git a/First_edition_2014/ebook_src/bitwise/clear_bits.py b/MY_BOOK/ebook_src/bitwise_operations/clear_bits.py similarity index 100% rename from First_edition_2014/ebook_src/bitwise/clear_bits.py rename to MY_BOOK/ebook_src/bitwise_operations/clear_bits.py diff --git a/First_edition_2014/ebook_src/bitwise/find_bit_len.py b/MY_BOOK/ebook_src/bitwise_operations/find_bit_len.py similarity index 100% rename from First_edition_2014/ebook_src/bitwise/find_bit_len.py rename to MY_BOOK/ebook_src/bitwise_operations/find_bit_len.py diff --git a/First_edition_2014/ebook_src/bitwise/find_how_many_1_binary.py b/MY_BOOK/ebook_src/bitwise_operations/find_how_many_1_binary.py similarity index 100% rename from First_edition_2014/ebook_src/bitwise/find_how_many_1_binary.py rename to MY_BOOK/ebook_src/bitwise_operations/find_how_many_1_binary.py diff --git a/First_edition_2014/ebook_src/bitwise/get_bit.py b/MY_BOOK/ebook_src/bitwise_operations/get_bit.py similarity index 100% rename from First_edition_2014/ebook_src/bitwise/get_bit.py rename to MY_BOOK/ebook_src/bitwise_operations/get_bit.py diff --git a/First_edition_2014/ebook_src/bitwise/get_float_rep_bin.py b/MY_BOOK/ebook_src/bitwise_operations/get_float_rep_bin.py similarity index 100% rename from First_edition_2014/ebook_src/bitwise/get_float_rep_bin.py rename to MY_BOOK/ebook_src/bitwise_operations/get_float_rep_bin.py diff --git a/First_edition_2014/ebook_src/bitwise/insert_small_bin_into_big_bin.py b/MY_BOOK/ebook_src/bitwise_operations/insert_small_bin_into_big_bin.py similarity index 100% rename from First_edition_2014/ebook_src/bitwise/insert_small_bin_into_big_bin.py rename to MY_BOOK/ebook_src/bitwise_operations/insert_small_bin_into_big_bin.py diff --git a/First_edition_2014/ebook_src/bitwise/next_with_same_num_1s.py b/MY_BOOK/ebook_src/bitwise_operations/next_with_same_num_1s.py similarity index 100% rename from First_edition_2014/ebook_src/bitwise/next_with_same_num_1s.py rename to MY_BOOK/ebook_src/bitwise_operations/next_with_same_num_1s.py diff --git a/First_edition_2014/ebook_src/bitwise/num_bits_to_convert_2_nums.py b/MY_BOOK/ebook_src/bitwise_operations/num_bits_to_convert_2_nums.py similarity index 100% rename from First_edition_2014/ebook_src/bitwise/num_bits_to_convert_2_nums.py rename to MY_BOOK/ebook_src/bitwise_operations/num_bits_to_convert_2_nums.py diff --git a/First_edition_2014/ebook_src/bitwise/set_bit.py b/MY_BOOK/ebook_src/bitwise_operations/set_bit.py similarity index 100% rename from First_edition_2014/ebook_src/bitwise/set_bit.py rename to MY_BOOK/ebook_src/bitwise_operations/set_bit.py diff --git a/First_edition_2014/ebook_src/bitwise/swap_in_place.py b/MY_BOOK/ebook_src/bitwise_operations/swap_in_place.py similarity index 100% rename from First_edition_2014/ebook_src/bitwise/swap_in_place.py rename to MY_BOOK/ebook_src/bitwise_operations/swap_in_place.py diff --git a/First_edition_2014/ebook_src/bitwise/swap_odd_even.py b/MY_BOOK/ebook_src/bitwise_operations/swap_odd_even.py similarity index 100% rename from First_edition_2014/ebook_src/bitwise/swap_odd_even.py rename to MY_BOOK/ebook_src/bitwise_operations/swap_odd_even.py diff --git a/First_edition_2014/ebook_src/bitwise/update_bit.py b/MY_BOOK/ebook_src/bitwise_operations/update_bit.py similarity index 100% rename from First_edition_2014/ebook_src/bitwise/update_bit.py rename to MY_BOOK/ebook_src/bitwise_operations/update_bit.py diff --git a/First_edition_2014/ebook_src/abstract_structures/queues/__init__.py b/MY_BOOK/ebook_src/builtin_structures/__init__.py old mode 100644 new mode 100755 similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/queues/__init__.py rename to MY_BOOK/ebook_src/builtin_structures/__init__.py diff --git a/First_edition_2014/ebook_src/builtin_structures/anagram.py b/MY_BOOK/ebook_src/builtin_structures/anagram.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/anagram.py rename to MY_BOOK/ebook_src/builtin_structures/anagram.py diff --git a/First_edition_2014/ebook_src/builtin_structures/balance.txt b/MY_BOOK/ebook_src/builtin_structures/balance.txt similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/balance.txt rename to MY_BOOK/ebook_src/builtin_structures/balance.txt diff --git a/First_edition_2014/ebook_src/builtin_structures/balance_symbols.py b/MY_BOOK/ebook_src/builtin_structures/balance_symbols.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/balance_symbols.py rename to MY_BOOK/ebook_src/builtin_structures/balance_symbols.py diff --git a/First_edition_2014/ebook_src/builtin_structures/check_if_2_numbers_sum_to_k.py b/MY_BOOK/ebook_src/builtin_structures/check_if_2_numbers_sum_to_k.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/check_if_2_numbers_sum_to_k.py rename to MY_BOOK/ebook_src/builtin_structures/check_if_2_numbers_sum_to_k.py diff --git a/First_edition_2014/ebook_src/builtin_structures/check_if_3_numbers_sum_to_zero.py b/MY_BOOK/ebook_src/builtin_structures/check_if_3_numbers_sum_to_zero.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/check_if_3_numbers_sum_to_zero.py rename to MY_BOOK/ebook_src/builtin_structures/check_if_3_numbers_sum_to_zero.py diff --git a/First_edition_2014/ebook_src/builtin_structures/check_non_overlapping_intervals.py b/MY_BOOK/ebook_src/builtin_structures/check_non_overlapping_intervals.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/check_non_overlapping_intervals.py rename to MY_BOOK/ebook_src/builtin_structures/check_non_overlapping_intervals.py diff --git a/First_edition_2014/ebook_src/builtin_structures/convert_numerical_bases.py b/MY_BOOK/ebook_src/builtin_structures/convert_numerical_bases.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/convert_numerical_bases.py rename to MY_BOOK/ebook_src/builtin_structures/convert_numerical_bases.py diff --git a/First_edition_2014/ebook_src/builtin_structures/convert_str_2_int.py b/MY_BOOK/ebook_src/builtin_structures/convert_str_2_int.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/convert_str_2_int.py rename to MY_BOOK/ebook_src/builtin_structures/convert_str_2_int.py diff --git a/First_edition_2014/ebook_src/builtin_structures/count_unique_words_On2.py b/MY_BOOK/ebook_src/builtin_structures/count_unique_words_On2.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/count_unique_words_On2.py rename to MY_BOOK/ebook_src/builtin_structures/count_unique_words_On2.py diff --git a/First_edition_2014/ebook_src/builtin_structures/delete_duplicate_char_str.py b/MY_BOOK/ebook_src/builtin_structures/delete_duplicate_char_str.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/delete_duplicate_char_str.py rename to MY_BOOK/ebook_src/builtin_structures/delete_duplicate_char_str.py diff --git a/First_edition_2014/ebook_src/builtin_structures/fibonacci.py b/MY_BOOK/ebook_src/builtin_structures/fibonacci.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/fibonacci.py rename to MY_BOOK/ebook_src/builtin_structures/fibonacci.py diff --git a/First_edition_2014/ebook_src/builtin_structures/find_0_MxN_replace_cols_rows.py b/MY_BOOK/ebook_src/builtin_structures/find_0_MxN_replace_cols_rows.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/find_0_MxN_replace_cols_rows.py rename to MY_BOOK/ebook_src/builtin_structures/find_0_MxN_replace_cols_rows.py diff --git a/First_edition_2014/ebook_src/builtin_structures/find_dice_probabilities.py b/MY_BOOK/ebook_src/builtin_structures/find_dice_probabilities.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/find_dice_probabilities.py rename to MY_BOOK/ebook_src/builtin_structures/find_dice_probabilities.py diff --git a/First_edition_2014/ebook_src/builtin_structures/find_edit_distance.py b/MY_BOOK/ebook_src/builtin_structures/find_edit_distance.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/find_edit_distance.py rename to MY_BOOK/ebook_src/builtin_structures/find_edit_distance.py diff --git a/First_edition_2014/ebook_src/builtin_structures/find_first_non_repetead_char.py b/MY_BOOK/ebook_src/builtin_structures/find_first_non_repetead_char.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/find_first_non_repetead_char.py rename to MY_BOOK/ebook_src/builtin_structures/find_first_non_repetead_char.py diff --git a/First_edition_2014/ebook_src/builtin_structures/find_gcd.py b/MY_BOOK/ebook_src/builtin_structures/find_gcd.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/find_gcd.py rename to MY_BOOK/ebook_src/builtin_structures/find_gcd.py diff --git a/First_edition_2014/ebook_src/builtin_structures/find_if_substr.py b/MY_BOOK/ebook_src/builtin_structures/find_if_substr.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/find_if_substr.py rename to MY_BOOK/ebook_src/builtin_structures/find_if_substr.py diff --git a/First_edition_2014/ebook_src/builtin_structures/find_if_unique_char.py b/MY_BOOK/ebook_src/builtin_structures/find_if_unique_char.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/find_if_unique_char.py rename to MY_BOOK/ebook_src/builtin_structures/find_if_unique_char.py diff --git a/First_edition_2014/ebook_src/builtin_structures/find_largest_sum.py b/MY_BOOK/ebook_src/builtin_structures/find_largest_sum.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/find_largest_sum.py rename to MY_BOOK/ebook_src/builtin_structures/find_largest_sum.py diff --git a/First_edition_2014/ebook_src/builtin_structures/find_longest_inc_subseq.py b/MY_BOOK/ebook_src/builtin_structures/find_longest_inc_subseq.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/find_longest_inc_subseq.py rename to MY_BOOK/ebook_src/builtin_structures/find_longest_inc_subseq.py diff --git a/First_edition_2014/ebook_src/builtin_structures/find_longest_str_unique_chars.py b/MY_BOOK/ebook_src/builtin_structures/find_longest_str_unique_chars.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/find_longest_str_unique_chars.py rename to MY_BOOK/ebook_src/builtin_structures/find_longest_str_unique_chars.py diff --git a/First_edition_2014/ebook_src/builtin_structures/find_non_repeating_number.py b/MY_BOOK/ebook_src/builtin_structures/find_non_repeating_number.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/find_non_repeating_number.py rename to MY_BOOK/ebook_src/builtin_structures/find_non_repeating_number.py diff --git a/First_edition_2014/ebook_src/builtin_structures/find_product_without_division.py b/MY_BOOK/ebook_src/builtin_structures/find_product_without_division.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/find_product_without_division.py rename to MY_BOOK/ebook_src/builtin_structures/find_product_without_division.py diff --git a/First_edition_2014/ebook_src/builtin_structures/find_top_N_recurring_words.py b/MY_BOOK/ebook_src/builtin_structures/find_top_N_recurring_words.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/find_top_N_recurring_words.py rename to MY_BOOK/ebook_src/builtin_structures/find_top_N_recurring_words.py diff --git a/First_edition_2014/ebook_src/builtin_structures/find_two_missing_numbers_in_sequence.py b/MY_BOOK/ebook_src/builtin_structures/find_two_missing_numbers_in_sequence.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/find_two_missing_numbers_in_sequence.py rename to MY_BOOK/ebook_src/builtin_structures/find_two_missing_numbers_in_sequence.py diff --git a/First_edition_2014/ebook_src/builtin_structures/get_float_rep_bin.py b/MY_BOOK/ebook_src/builtin_structures/get_float_rep_bin.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/get_float_rep_bin.py rename to MY_BOOK/ebook_src/builtin_structures/get_float_rep_bin.py diff --git a/First_edition_2014/ebook_src/builtin_structures/interserction_two_arrays.py b/MY_BOOK/ebook_src/builtin_structures/interserction_two_arrays.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/interserction_two_arrays.py rename to MY_BOOK/ebook_src/builtin_structures/interserction_two_arrays.py diff --git a/First_edition_2014/ebook_src/builtin_structures/max_subarray_stocks.py b/MY_BOOK/ebook_src/builtin_structures/max_subarray_stocks.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/max_subarray_stocks.py rename to MY_BOOK/ebook_src/builtin_structures/max_subarray_stocks.py diff --git a/First_edition_2014/ebook_src/builtin_structures/number_of_zeros_factorial.txt b/MY_BOOK/ebook_src/builtin_structures/number_of_zeros_factorial.txt similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/number_of_zeros_factorial.txt rename to MY_BOOK/ebook_src/builtin_structures/number_of_zeros_factorial.txt diff --git a/First_edition_2014/ebook_src/builtin_structures/palindrome.py b/MY_BOOK/ebook_src/builtin_structures/palindrome.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/palindrome.py rename to MY_BOOK/ebook_src/builtin_structures/palindrome.py diff --git a/First_edition_2014/ebook_src/builtin_structures/permutations.py b/MY_BOOK/ebook_src/builtin_structures/permutations.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/permutations.py rename to MY_BOOK/ebook_src/builtin_structures/permutations.py diff --git a/First_edition_2014/ebook_src/builtin_structures/permutations_alphanumeric.py b/MY_BOOK/ebook_src/builtin_structures/permutations_alphanumeric.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/permutations_alphanumeric.py rename to MY_BOOK/ebook_src/builtin_structures/permutations_alphanumeric.py diff --git a/First_edition_2014/ebook_src/builtin_structures/primes.py b/MY_BOOK/ebook_src/builtin_structures/primes.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/primes.py rename to MY_BOOK/ebook_src/builtin_structures/primes.py diff --git a/First_edition_2014/ebook_src/builtin_structures/prod_other_ints.py b/MY_BOOK/ebook_src/builtin_structures/prod_other_ints.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/prod_other_ints.py rename to MY_BOOK/ebook_src/builtin_structures/prod_other_ints.py diff --git a/First_edition_2014/ebook_src/builtin_structures/ransom_note.py b/MY_BOOK/ebook_src/builtin_structures/ransom_note.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/ransom_note.py rename to MY_BOOK/ebook_src/builtin_structures/ransom_note.py diff --git a/First_edition_2014/ebook_src/builtin_structures/reverse_string.py b/MY_BOOK/ebook_src/builtin_structures/reverse_string.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/reverse_string.py rename to MY_BOOK/ebook_src/builtin_structures/reverse_string.py diff --git a/First_edition_2014/ebook_src/builtin_structures/reverse_words.py b/MY_BOOK/ebook_src/builtin_structures/reverse_words.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/reverse_words.py rename to MY_BOOK/ebook_src/builtin_structures/reverse_words.py diff --git a/First_edition_2014/ebook_src/builtin_structures/rotate_NxN.py b/MY_BOOK/ebook_src/builtin_structures/rotate_NxN.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/rotate_NxN.py rename to MY_BOOK/ebook_src/builtin_structures/rotate_NxN.py diff --git a/First_edition_2014/ebook_src/builtin_structures/runtime_dicts_with_timeit_module.py b/MY_BOOK/ebook_src/builtin_structures/runtime_dicts_with_timeit_module.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/runtime_dicts_with_timeit_module.py rename to MY_BOOK/ebook_src/builtin_structures/runtime_dicts_with_timeit_module.py diff --git a/First_edition_2014/ebook_src/builtin_structures/runtime_lists_with_timeit_module.py b/MY_BOOK/ebook_src/builtin_structures/runtime_lists_with_timeit_module.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/runtime_lists_with_timeit_module.py rename to MY_BOOK/ebook_src/builtin_structures/runtime_lists_with_timeit_module.py diff --git a/First_edition_2014/ebook_src/builtin_structures/search_entry_matrix.py b/MY_BOOK/ebook_src/builtin_structures/search_entry_matrix.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/search_entry_matrix.py rename to MY_BOOK/ebook_src/builtin_structures/search_entry_matrix.py diff --git a/First_edition_2014/ebook_src/builtin_structures/simple_str_comprension.py b/MY_BOOK/ebook_src/builtin_structures/simple_str_comprension.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/simple_str_comprension.py rename to MY_BOOK/ebook_src/builtin_structures/simple_str_comprension.py diff --git a/First_edition_2014/ebook_src/builtin_structures/sum_two_numbers_as_strings.py b/MY_BOOK/ebook_src/builtin_structures/sum_two_numbers_as_strings.py similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/sum_two_numbers_as_strings.py rename to MY_BOOK/ebook_src/builtin_structures/sum_two_numbers_as_strings.py diff --git a/First_edition_2014/ebook_src/abstract_structures/stacks/__init__.py b/MY_BOOK/ebook_src/dynamic_programming/__init__.py similarity index 100% rename from First_edition_2014/ebook_src/abstract_structures/stacks/__init__.py rename to MY_BOOK/ebook_src/dynamic_programming/__init__.py diff --git a/First_edition_2014/ebook_src/USEFUL/dynamic_programming/memo.py b/MY_BOOK/ebook_src/dynamic_programming/memo.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/dynamic_programming/memo.py rename to MY_BOOK/ebook_src/dynamic_programming/memo.py diff --git a/First_edition_2014/ebook_src/USEFUL/dynamic_programming/memoized_longest_inc_subseq.py b/MY_BOOK/ebook_src/dynamic_programming/memoized_longest_inc_subseq.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/dynamic_programming/memoized_longest_inc_subseq.py rename to MY_BOOK/ebook_src/dynamic_programming/memoized_longest_inc_subseq.py diff --git a/First_edition_2014/ebook_src/USEFUL/useful_with_files/change_ext_file.py b/MY_BOOK/ebook_src/manipulating_files/change_ext_file.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/useful_with_files/change_ext_file.py rename to MY_BOOK/ebook_src/manipulating_files/change_ext_file.py diff --git a/First_edition_2014/ebook_src/USEFUL/useful_with_files/count_unique_words_files.py b/MY_BOOK/ebook_src/manipulating_files/count_unique_words_files.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/useful_with_files/count_unique_words_files.py rename to MY_BOOK/ebook_src/manipulating_files/count_unique_words_files.py diff --git a/First_edition_2014/ebook_src/USEFUL/useful_with_files/count_unique_words_frequency.py b/MY_BOOK/ebook_src/manipulating_files/count_unique_words_frequency.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/useful_with_files/count_unique_words_frequency.py rename to MY_BOOK/ebook_src/manipulating_files/count_unique_words_frequency.py diff --git a/First_edition_2014/ebook_src/USEFUL/useful_with_files/grep_word_from_files.py b/MY_BOOK/ebook_src/manipulating_files/grep_word_from_files.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/useful_with_files/grep_word_from_files.py rename to MY_BOOK/ebook_src/manipulating_files/grep_word_from_files.py diff --git a/First_edition_2014/ebook_src/USEFUL/useful_with_files/remove_blank_lines.py b/MY_BOOK/ebook_src/manipulating_files/remove_blank_lines.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/useful_with_files/remove_blank_lines.py rename to MY_BOOK/ebook_src/manipulating_files/remove_blank_lines.py diff --git a/First_edition_2014/ebook_src/USEFUL/basic_examples/example_OrderedDict.py b/MY_BOOK/ebook_src/python_examples/example_OrderedDict.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/basic_examples/example_OrderedDict.py rename to MY_BOOK/ebook_src/python_examples/example_OrderedDict.py diff --git a/First_edition_2014/ebook_src/USEFUL/basic_examples/example_args.py b/MY_BOOK/ebook_src/python_examples/example_args.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/basic_examples/example_args.py rename to MY_BOOK/ebook_src/python_examples/example_args.py diff --git a/First_edition_2014/ebook_src/USEFUL/basic_examples/example_benchmark_decorator.py b/MY_BOOK/ebook_src/python_examples/example_benchmark_decorator.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/basic_examples/example_benchmark_decorator.py rename to MY_BOOK/ebook_src/python_examples/example_benchmark_decorator.py diff --git a/First_edition_2014/ebook_src/USEFUL/oop/ShapeClass.py b/MY_BOOK/ebook_src/python_examples/example_class.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/oop/ShapeClass.py rename to MY_BOOK/ebook_src/python_examples/example_class.py diff --git a/First_edition_2014/ebook_src/USEFUL/basic_examples/example_comp_lists.py b/MY_BOOK/ebook_src/python_examples/example_comp_lists.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/basic_examples/example_comp_lists.py rename to MY_BOOK/ebook_src/python_examples/example_comp_lists.py diff --git a/First_edition_2014/ebook_src/USEFUL/basic_examples/example_counter.py b/MY_BOOK/ebook_src/python_examples/example_counter.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/basic_examples/example_counter.py rename to MY_BOOK/ebook_src/python_examples/example_counter.py diff --git a/First_edition_2014/ebook_src/learning/decorator.py b/MY_BOOK/ebook_src/python_examples/example_decorator.py similarity index 100% rename from First_edition_2014/ebook_src/learning/decorator.py rename to MY_BOOK/ebook_src/python_examples/example_decorator.py diff --git a/First_edition_2014/ebook_src/USEFUL/basic_examples/example_decorators.py b/MY_BOOK/ebook_src/python_examples/example_decorators.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/basic_examples/example_decorators.py rename to MY_BOOK/ebook_src/python_examples/example_decorators.py diff --git a/First_edition_2014/ebook_src/USEFUL/basic_examples/example_defaultdict.py b/MY_BOOK/ebook_src/python_examples/example_defaultdict.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/basic_examples/example_defaultdict.py rename to MY_BOOK/ebook_src/python_examples/example_defaultdict.py diff --git a/First_edition_2014/ebook_src/USEFUL/basic_examples/example_doctest.py b/MY_BOOK/ebook_src/python_examples/example_doctest.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/basic_examples/example_doctest.py rename to MY_BOOK/ebook_src/python_examples/example_doctest.py diff --git a/First_edition_2014/ebook_src/USEFUL/basic_examples/example_fractions.py b/MY_BOOK/ebook_src/python_examples/example_fractions.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/basic_examples/example_fractions.py rename to MY_BOOK/ebook_src/python_examples/example_fractions.py diff --git a/First_edition_2014/ebook_src/USEFUL/basic_examples/example_generator.py b/MY_BOOK/ebook_src/python_examples/example_generator.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/basic_examples/example_generator.py rename to MY_BOOK/ebook_src/python_examples/example_generator.py diff --git a/First_edition_2014/ebook_src/USEFUL/basic_examples/example_lambda.py b/MY_BOOK/ebook_src/python_examples/example_lambda.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/basic_examples/example_lambda.py rename to MY_BOOK/ebook_src/python_examples/example_lambda.py diff --git a/First_edition_2014/ebook_src/USEFUL/basic_examples/example_logging.py b/MY_BOOK/ebook_src/python_examples/example_logging.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/basic_examples/example_logging.py rename to MY_BOOK/ebook_src/python_examples/example_logging.py diff --git a/First_edition_2014/ebook_src/USEFUL/advanced/lru_cache.py b/MY_BOOK/ebook_src/python_examples/example_lru_cache.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/advanced/lru_cache.py rename to MY_BOOK/ebook_src/python_examples/example_lru_cache.py diff --git a/First_edition_2014/ebook_src/USEFUL/basic_examples/example_numpy.py b/MY_BOOK/ebook_src/python_examples/example_numpy.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/basic_examples/example_numpy.py rename to MY_BOOK/ebook_src/python_examples/example_numpy.py diff --git a/First_edition_2014/ebook_src/USEFUL/basic_examples/example_open_files.py b/MY_BOOK/ebook_src/python_examples/example_open_files.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/basic_examples/example_open_files.py rename to MY_BOOK/ebook_src/python_examples/example_open_files.py diff --git a/First_edition_2014/ebook_src/USEFUL/basic_examples/example_pickle.py b/MY_BOOK/ebook_src/python_examples/example_pickle.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/basic_examples/example_pickle.py rename to MY_BOOK/ebook_src/python_examples/example_pickle.py diff --git a/First_edition_2014/ebook_src/USEFUL/basic_examples/example_queue.py b/MY_BOOK/ebook_src/python_examples/example_queue.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/basic_examples/example_queue.py rename to MY_BOOK/ebook_src/python_examples/example_queue.py diff --git a/First_edition_2014/ebook_src/USEFUL/basic_examples/example_random.py b/MY_BOOK/ebook_src/python_examples/example_random.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/basic_examples/example_random.py rename to MY_BOOK/ebook_src/python_examples/example_random.py diff --git a/First_edition_2014/ebook_src/USEFUL/basic_examples/example_setdefault.py b/MY_BOOK/ebook_src/python_examples/example_setdefault.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/basic_examples/example_setdefault.py rename to MY_BOOK/ebook_src/python_examples/example_setdefault.py diff --git a/First_edition_2014/ebook_src/USEFUL/basic_examples/example_sets.py b/MY_BOOK/ebook_src/python_examples/example_sets.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/basic_examples/example_sets.py rename to MY_BOOK/ebook_src/python_examples/example_sets.py diff --git a/First_edition_2014/ebook_src/USEFUL/basic_examples/example_socket.py b/MY_BOOK/ebook_src/python_examples/example_socket.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/basic_examples/example_socket.py rename to MY_BOOK/ebook_src/python_examples/example_socket.py diff --git a/First_edition_2014/ebook_src/USEFUL/basic_examples/example_string_format.py b/MY_BOOK/ebook_src/python_examples/example_string_format.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/basic_examples/example_string_format.py rename to MY_BOOK/ebook_src/python_examples/example_string_format.py diff --git a/First_edition_2014/ebook_src/USEFUL/basic_examples/example_subprocess.py b/MY_BOOK/ebook_src/python_examples/example_subprocess.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/basic_examples/example_subprocess.py rename to MY_BOOK/ebook_src/python_examples/example_subprocess.py diff --git a/First_edition_2014/ebook_src/USEFUL/basic_examples/example_telnet.py b/MY_BOOK/ebook_src/python_examples/example_telnet.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/basic_examples/example_telnet.py rename to MY_BOOK/ebook_src/python_examples/example_telnet.py diff --git a/First_edition_2014/ebook_src/USEFUL/basic_examples/example_testing.py b/MY_BOOK/ebook_src/python_examples/example_testing.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/basic_examples/example_testing.py rename to MY_BOOK/ebook_src/python_examples/example_testing.py diff --git a/First_edition_2014/ebook_src/USEFUL/basic_examples/example_threads.py b/MY_BOOK/ebook_src/python_examples/example_threads.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/basic_examples/example_threads.py rename to MY_BOOK/ebook_src/python_examples/example_threads.py diff --git a/First_edition_2014/ebook_src/USEFUL/basic_examples/example_time.py b/MY_BOOK/ebook_src/python_examples/example_time.py similarity index 100% rename from First_edition_2014/ebook_src/USEFUL/basic_examples/example_time.py rename to MY_BOOK/ebook_src/python_examples/example_time.py diff --git a/First_edition_2014/ebook_src/real_interview_problems/2n_packets.py b/MY_BOOK/ebook_src/real_interview_problems/2n_packets.py similarity index 100% rename from First_edition_2014/ebook_src/real_interview_problems/2n_packets.py rename to MY_BOOK/ebook_src/real_interview_problems/2n_packets.py diff --git a/First_edition_2014/ebook_src/real_interview_problems/balanced.py b/MY_BOOK/ebook_src/real_interview_problems/balanced.py similarity index 100% rename from First_edition_2014/ebook_src/real_interview_problems/balanced.py rename to MY_BOOK/ebook_src/real_interview_problems/balanced.py diff --git a/First_edition_2014/ebook_src/real_interview_problems/binary_search.py b/MY_BOOK/ebook_src/real_interview_problems/binary_search.py similarity index 100% rename from First_edition_2014/ebook_src/real_interview_problems/binary_search.py rename to MY_BOOK/ebook_src/real_interview_problems/binary_search.py diff --git a/First_edition_2014/ebook_src/real_interview_problems/bst.py b/MY_BOOK/ebook_src/real_interview_problems/bst.py similarity index 100% rename from First_edition_2014/ebook_src/real_interview_problems/bst.py rename to MY_BOOK/ebook_src/real_interview_problems/bst.py diff --git a/First_edition_2014/ebook_src/real_interview_problems/check_anagram.py b/MY_BOOK/ebook_src/real_interview_problems/check_anagram.py similarity index 100% rename from First_edition_2014/ebook_src/real_interview_problems/check_anagram.py rename to MY_BOOK/ebook_src/real_interview_problems/check_anagram.py diff --git a/First_edition_2014/ebook_src/real_interview_problems/combination.py b/MY_BOOK/ebook_src/real_interview_problems/combination.py similarity index 100% rename from First_edition_2014/ebook_src/real_interview_problems/combination.py rename to MY_BOOK/ebook_src/real_interview_problems/combination.py diff --git a/First_edition_2014/ebook_src/real_interview_problems/hash_table.py b/MY_BOOK/ebook_src/real_interview_problems/hash_table.py similarity index 100% rename from First_edition_2014/ebook_src/real_interview_problems/hash_table.py rename to MY_BOOK/ebook_src/real_interview_problems/hash_table.py diff --git a/First_edition_2014/ebook_src/real_interview_problems/linked_list.py b/MY_BOOK/ebook_src/real_interview_problems/linked_list.py similarity index 100% rename from First_edition_2014/ebook_src/real_interview_problems/linked_list.py rename to MY_BOOK/ebook_src/real_interview_problems/linked_list.py diff --git a/First_edition_2014/ebook_src/real_interview_problems/longest_common_prefix.py b/MY_BOOK/ebook_src/real_interview_problems/longest_common_prefix.py similarity index 100% rename from First_edition_2014/ebook_src/real_interview_problems/longest_common_prefix.py rename to MY_BOOK/ebook_src/real_interview_problems/longest_common_prefix.py diff --git a/First_edition_2014/ebook_src/real_interview_problems/longest_increasing_subsequence.py b/MY_BOOK/ebook_src/real_interview_problems/longest_increasing_subsequence.py similarity index 100% rename from First_edition_2014/ebook_src/real_interview_problems/longest_increasing_subsequence.py rename to MY_BOOK/ebook_src/real_interview_problems/longest_increasing_subsequence.py diff --git a/First_edition_2014/ebook_src/real_interview_problems/merge_sort.py b/MY_BOOK/ebook_src/real_interview_problems/merge_sort.py similarity index 100% rename from First_edition_2014/ebook_src/real_interview_problems/merge_sort.py rename to MY_BOOK/ebook_src/real_interview_problems/merge_sort.py diff --git a/First_edition_2014/other_resources/PYTHON.md b/MY_BOOK/ebook_src/real_interview_problems/other_resources/PYTHON.md similarity index 100% rename from First_edition_2014/other_resources/PYTHON.md rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/PYTHON.md diff --git a/First_edition_2014/other_resources/Project-Euler/.gitignore b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/.gitignore similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/.gitignore rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/.gitignore diff --git a/First_edition_2014/other_resources/Project-Euler/001-multiples_of_3_and_5.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/001-multiples_of_3_and_5.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/001-multiples_of_3_and_5.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/001-multiples_of_3_and_5.py diff --git a/First_edition_2014/other_resources/Project-Euler/002-even_fib_num.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/002-even_fib_num.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/002-even_fib_num.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/002-even_fib_num.py diff --git a/First_edition_2014/other_resources/Project-Euler/003-largest_prime_factor.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/003-largest_prime_factor.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/003-largest_prime_factor.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/003-largest_prime_factor.py diff --git a/First_edition_2014/other_resources/Project-Euler/004-larg_palindrome.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/004-larg_palindrome.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/004-larg_palindrome.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/004-larg_palindrome.py diff --git a/First_edition_2014/other_resources/Project-Euler/005-smallest_multiple.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/005-smallest_multiple.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/005-smallest_multiple.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/005-smallest_multiple.py diff --git a/First_edition_2014/other_resources/Project-Euler/006-sum_square_diff.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/006-sum_square_diff.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/006-sum_square_diff.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/006-sum_square_diff.py diff --git a/First_edition_2014/other_resources/Project-Euler/007-findstprime.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/007-findstprime.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/007-findstprime.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/007-findstprime.py diff --git a/First_edition_2014/other_resources/Project-Euler/008-largest_product_seq.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/008-largest_product_seq.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/008-largest_product_seq.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/008-largest_product_seq.py diff --git a/First_edition_2014/other_resources/Project-Euler/009-special_pyt.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/009-special_pyt.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/009-special_pyt.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/009-special_pyt.py diff --git a/First_edition_2014/other_resources/Project-Euler/010-summation_primes.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/010-summation_primes.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/010-summation_primes.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/010-summation_primes.py diff --git a/First_edition_2014/other_resources/Project-Euler/011-larg_prod_grid.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/011-larg_prod_grid.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/011-larg_prod_grid.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/011-larg_prod_grid.py diff --git a/First_edition_2014/other_resources/Project-Euler/012-highly_divisible_trian_num.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/012-highly_divisible_trian_num.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/012-highly_divisible_trian_num.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/012-highly_divisible_trian_num.py diff --git a/First_edition_2014/other_resources/Project-Euler/013-large_sum.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/013-large_sum.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/013-large_sum.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/013-large_sum.py diff --git a/First_edition_2014/other_resources/Project-Euler/014-long_collatz_seq.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/014-long_collatz_seq.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/014-long_collatz_seq.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/014-long_collatz_seq.py diff --git a/First_edition_2014/other_resources/Project-Euler/015-lattice_paths.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/015-lattice_paths.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/015-lattice_paths.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/015-lattice_paths.py diff --git a/First_edition_2014/other_resources/Project-Euler/016-power_digit_sum.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/016-power_digit_sum.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/016-power_digit_sum.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/016-power_digit_sum.py diff --git a/First_edition_2014/other_resources/Project-Euler/017-number_letter_counts.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/017-number_letter_counts.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/017-number_letter_counts.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/017-number_letter_counts.py diff --git a/First_edition_2014/other_resources/Project-Euler/018-max_path_sum.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/018-max_path_sum.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/018-max_path_sum.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/018-max_path_sum.py diff --git a/First_edition_2014/other_resources/Project-Euler/019-counting_sundays.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/019-counting_sundays.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/019-counting_sundays.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/019-counting_sundays.py diff --git a/First_edition_2014/other_resources/Project-Euler/020-factorial.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/020-factorial.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/020-factorial.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/020-factorial.py diff --git a/First_edition_2014/other_resources/Project-Euler/021-amicable_numbers.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/021-amicable_numbers.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/021-amicable_numbers.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/021-amicable_numbers.py diff --git a/First_edition_2014/other_resources/Project-Euler/022-names_score.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/022-names_score.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/022-names_score.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/022-names_score.py diff --git a/First_edition_2014/other_resources/Project-Euler/023-non_abund_sums.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/023-non_abund_sums.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/023-non_abund_sums.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/023-non_abund_sums.py diff --git a/First_edition_2014/other_resources/Project-Euler/024-lexico_per.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/024-lexico_per.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/024-lexico_per.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/024-lexico_per.py diff --git a/First_edition_2014/other_resources/Project-Euler/025-100-digit-fib.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/025-100-digit-fib.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/025-100-digit-fib.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/025-100-digit-fib.py diff --git a/First_edition_2014/other_resources/Project-Euler/026-reciprocal-cycles.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/026-reciprocal-cycles.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/026-reciprocal-cycles.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/026-reciprocal-cycles.py diff --git a/First_edition_2014/other_resources/Project-Euler/027-quad_primes.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/027-quad_primes.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/027-quad_primes.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/027-quad_primes.py diff --git a/First_edition_2014/other_resources/Project-Euler/028-number_spiral.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/028-number_spiral.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/028-number_spiral.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/028-number_spiral.py diff --git a/First_edition_2014/other_resources/Project-Euler/029-dist_pow.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/029-dist_pow.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/029-dist_pow.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/029-dist_pow.py diff --git a/First_edition_2014/other_resources/Project-Euler/030-digit_fifth_pow.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/030-digit_fifth_pow.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/030-digit_fifth_pow.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/030-digit_fifth_pow.py diff --git a/First_edition_2014/other_resources/Project-Euler/031-coin_sums.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/031-coin_sums.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/031-coin_sums.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/031-coin_sums.py diff --git a/First_edition_2014/other_resources/Project-Euler/032-pandigital.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/032-pandigital.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/032-pandigital.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/032-pandigital.py diff --git a/First_edition_2014/other_resources/Project-Euler/035-circular_primes.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/035-circular_primes.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/035-circular_primes.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/035-circular_primes.py diff --git a/First_edition_2014/other_resources/Project-Euler/046-gold_other.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/046-gold_other.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/046-gold_other.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/046-gold_other.py diff --git a/First_edition_2014/other_resources/Project-Euler/048-self_powers.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/048-self_powers.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/048-self_powers.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/048-self_powers.py diff --git a/First_edition_2014/other_resources/Project-Euler/065-100th-e-numerator.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/065-100th-e-numerator.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/065-100th-e-numerator.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/065-100th-e-numerator.py diff --git a/First_edition_2014/other_resources/Project-Euler/089-roman_numbers.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/089-roman_numbers.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/089-roman_numbers.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/089-roman_numbers.py diff --git a/First_edition_2014/other_resources/Project-Euler/092-square_dig_chains.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/092-square_dig_chains.py similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/092-square_dig_chains.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/092-square_dig_chains.py diff --git a/First_edition_2014/other_resources/Project-Euler/LICENSE b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/LICENSE similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/LICENSE rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/LICENSE diff --git a/First_edition_2014/other_resources/Project-Euler/README.md b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/README.md similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/README.md rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/README.md diff --git a/First_edition_2014/other_resources/Project-Euler/larg_prod_grid.dat b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/larg_prod_grid.dat similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/larg_prod_grid.dat rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/larg_prod_grid.dat diff --git a/First_edition_2014/other_resources/Project-Euler/large_sum.dat b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/large_sum.dat similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/large_sum.dat rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/large_sum.dat diff --git a/First_edition_2014/other_resources/Project-Euler/max_path_sum.dat b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/max_path_sum.dat similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/max_path_sum.dat rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/max_path_sum.dat diff --git a/First_edition_2014/other_resources/Project-Euler/max_path_sum0.dat b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/max_path_sum0.dat similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/max_path_sum0.dat rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/max_path_sum0.dat diff --git a/First_edition_2014/other_resources/Project-Euler/names.txt b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/names.txt similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/names.txt rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/names.txt diff --git a/First_edition_2014/other_resources/Project-Euler/roman.txt b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/roman.txt similarity index 100% rename from First_edition_2014/other_resources/Project-Euler/roman.txt rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Project-Euler/roman.txt diff --git a/First_edition_2014/other_resources/Top-Coder/.gitignore b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Top-Coder/.gitignore similarity index 100% rename from First_edition_2014/other_resources/Top-Coder/.gitignore rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Top-Coder/.gitignore diff --git a/First_edition_2014/other_resources/Top-Coder/2013/tco2013_round3_3b_div1.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Top-Coder/2013/tco2013_round3_3b_div1.py similarity index 100% rename from First_edition_2014/other_resources/Top-Coder/2013/tco2013_round3_3b_div1.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Top-Coder/2013/tco2013_round3_3b_div1.py diff --git a/First_edition_2014/other_resources/Top-Coder/README.md b/MY_BOOK/ebook_src/real_interview_problems/other_resources/Top-Coder/README.md similarity index 100% rename from First_edition_2014/other_resources/Top-Coder/README.md rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/Top-Coder/README.md diff --git a/First_edition_2014/other_resources/eudyptula-challenge-solutions/.gitignore b/MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/.gitignore similarity index 100% rename from First_edition_2014/other_resources/eudyptula-challenge-solutions/.gitignore rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/.gitignore diff --git a/First_edition_2014/other_resources/eudyptula-challenge-solutions/LICENSE b/MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/LICENSE similarity index 100% rename from First_edition_2014/other_resources/eudyptula-challenge-solutions/LICENSE rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/LICENSE diff --git a/First_edition_2014/other_resources/eudyptula-challenge-solutions/bt3/.DS_Store b/MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/bt3/.DS_Store similarity index 100% rename from First_edition_2014/other_resources/eudyptula-challenge-solutions/bt3/.DS_Store rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/bt3/.DS_Store diff --git a/First_edition_2014/other_resources/eudyptula-challenge-solutions/bt3/task_01/Makefile b/MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/bt3/task_01/Makefile similarity index 100% rename from First_edition_2014/other_resources/eudyptula-challenge-solutions/bt3/task_01/Makefile rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/bt3/task_01/Makefile diff --git a/First_edition_2014/other_resources/eudyptula-challenge-solutions/bt3/task_01/eudyptula_1.c b/MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/bt3/task_01/eudyptula_1.c similarity index 100% rename from First_edition_2014/other_resources/eudyptula-challenge-solutions/bt3/task_01/eudyptula_1.c rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/bt3/task_01/eudyptula_1.c diff --git a/First_edition_2014/other_resources/eudyptula-challenge-solutions/bt3/task_01/useful_commands b/MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/bt3/task_01/useful_commands similarity index 100% rename from First_edition_2014/other_resources/eudyptula-challenge-solutions/bt3/task_01/useful_commands rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/bt3/task_01/useful_commands diff --git a/First_edition_2014/other_resources/eudyptula-challenge-solutions/bt3/task_02/Screen Shot 2017-04-24 at 11.13.33 AM.png b/MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/bt3/task_02/Screen Shot 2017-04-24 at 11.13.33 AM.png similarity index 100% rename from First_edition_2014/other_resources/eudyptula-challenge-solutions/bt3/task_02/Screen Shot 2017-04-24 at 11.13.33 AM.png rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/bt3/task_02/Screen Shot 2017-04-24 at 11.13.33 AM.png diff --git a/First_edition_2014/other_resources/eudyptula-challenge-solutions/bt3/task_02/config b/MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/bt3/task_02/config similarity index 100% rename from First_edition_2014/other_resources/eudyptula-challenge-solutions/bt3/task_02/config rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/bt3/task_02/config diff --git a/First_edition_2014/other_resources/eudyptula-challenge-solutions/bt3/task_02/instructions.md b/MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/bt3/task_02/instructions.md similarity index 100% rename from First_edition_2014/other_resources/eudyptula-challenge-solutions/bt3/task_02/instructions.md rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/bt3/task_02/instructions.md diff --git a/First_edition_2014/other_resources/eudyptula-challenge-solutions/bt3/task_02/readme.md b/MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/bt3/task_02/readme.md similarity index 100% rename from First_edition_2014/other_resources/eudyptula-challenge-solutions/bt3/task_02/readme.md rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/bt3/task_02/readme.md diff --git a/First_edition_2014/other_resources/eudyptula-challenge-solutions/kiwiz/task_01/Makefile b/MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/kiwiz/task_01/Makefile similarity index 100% rename from First_edition_2014/other_resources/eudyptula-challenge-solutions/kiwiz/task_01/Makefile rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/kiwiz/task_01/Makefile diff --git a/First_edition_2014/other_resources/eudyptula-challenge-solutions/kiwiz/task_01/task-01.c b/MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/kiwiz/task_01/task-01.c similarity index 100% rename from First_edition_2014/other_resources/eudyptula-challenge-solutions/kiwiz/task_01/task-01.c rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/kiwiz/task_01/task-01.c diff --git a/First_edition_2014/other_resources/eudyptula-challenge-solutions/kiwiz/task_02/.config b/MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/kiwiz/task_02/.config similarity index 100% rename from First_edition_2014/other_resources/eudyptula-challenge-solutions/kiwiz/task_02/.config rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/kiwiz/task_02/.config diff --git a/First_edition_2014/other_resources/eudyptula-challenge-solutions/kiwiz/task_02/README.md b/MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/kiwiz/task_02/README.md similarity index 100% rename from First_edition_2014/other_resources/eudyptula-challenge-solutions/kiwiz/task_02/README.md rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/kiwiz/task_02/README.md diff --git a/First_edition_2014/other_resources/eudyptula-challenge-solutions/kiwiz/task_03/Makefile b/MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/kiwiz/task_03/Makefile similarity index 100% rename from First_edition_2014/other_resources/eudyptula-challenge-solutions/kiwiz/task_03/Makefile rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/kiwiz/task_03/Makefile diff --git a/First_edition_2014/other_resources/eudyptula-challenge-solutions/kiwiz/task_03/README.md b/MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/kiwiz/task_03/README.md similarity index 100% rename from First_edition_2014/other_resources/eudyptula-challenge-solutions/kiwiz/task_03/README.md rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/kiwiz/task_03/README.md diff --git a/First_edition_2014/other_resources/eudyptula-challenge-solutions/useful_scripts/sending_mutt.sh b/MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/useful_scripts/sending_mutt.sh similarity index 100% rename from First_edition_2014/other_resources/eudyptula-challenge-solutions/useful_scripts/sending_mutt.sh rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/eudyptula-challenge-solutions/useful_scripts/sending_mutt.sh diff --git a/First_edition_2014/other_resources/interview_cake/bitwise_stuff/clean_bit.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/bitwise_stuff/clean_bit.py similarity index 100% rename from First_edition_2014/other_resources/interview_cake/bitwise_stuff/clean_bit.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/bitwise_stuff/clean_bit.py diff --git a/First_edition_2014/other_resources/interview_cake/bitwise_stuff/count_bits.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/bitwise_stuff/count_bits.py similarity index 100% rename from First_edition_2014/other_resources/interview_cake/bitwise_stuff/count_bits.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/bitwise_stuff/count_bits.py diff --git a/First_edition_2014/other_resources/interview_cake/bitwise_stuff/get_bit.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/bitwise_stuff/get_bit.py similarity index 100% rename from First_edition_2014/other_resources/interview_cake/bitwise_stuff/get_bit.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/bitwise_stuff/get_bit.py diff --git a/First_edition_2014/other_resources/interview_cake/bitwise_stuff/update_bit.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/bitwise_stuff/update_bit.py similarity index 100% rename from First_edition_2014/other_resources/interview_cake/bitwise_stuff/update_bit.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/bitwise_stuff/update_bit.py diff --git a/First_edition_2014/other_resources/interview_cake/data_structures/angry_bird.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/data_structures/angry_bird.py similarity index 100% rename from First_edition_2014/other_resources/interview_cake/data_structures/angry_bird.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/data_structures/angry_bird.py diff --git a/First_edition_2014/other_resources/interview_cake/data_structures/file_system_hashing.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/data_structures/file_system_hashing.py similarity index 100% rename from First_edition_2014/other_resources/interview_cake/data_structures/file_system_hashing.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/data_structures/file_system_hashing.py diff --git a/First_edition_2014/other_resources/interview_cake/data_structures/inflight_entrain.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/data_structures/inflight_entrain.py similarity index 100% rename from First_edition_2014/other_resources/interview_cake/data_structures/inflight_entrain.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/data_structures/inflight_entrain.py diff --git a/First_edition_2014/other_resources/interview_cake/math/apple_stocks.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/math/apple_stocks.py similarity index 100% rename from First_edition_2014/other_resources/interview_cake/math/apple_stocks.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/math/apple_stocks.py diff --git a/First_edition_2014/other_resources/interview_cake/math/fib.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/math/fib.py similarity index 100% rename from First_edition_2014/other_resources/interview_cake/math/fib.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/math/fib.py diff --git a/First_edition_2014/other_resources/interview_cake/math/find_dup.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/math/find_dup.py similarity index 100% rename from First_edition_2014/other_resources/interview_cake/math/find_dup.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/math/find_dup.py diff --git a/First_edition_2014/other_resources/interview_cake/math/float_bin_num.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/math/float_bin_num.py similarity index 100% rename from First_edition_2014/other_resources/interview_cake/math/float_bin_num.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/math/float_bin_num.py diff --git a/First_edition_2014/other_resources/interview_cake/math/highest_product_3_int.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/math/highest_product_3_int.py similarity index 100% rename from First_edition_2014/other_resources/interview_cake/math/highest_product_3_int.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/math/highest_product_3_int.py diff --git a/First_edition_2014/other_resources/interview_cake/math/in_place_shuffle.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/math/in_place_shuffle.py similarity index 100% rename from First_edition_2014/other_resources/interview_cake/math/in_place_shuffle.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/math/in_place_shuffle.py diff --git a/First_edition_2014/other_resources/interview_cake/math/product_every_int.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/math/product_every_int.py similarity index 100% rename from First_edition_2014/other_resources/interview_cake/math/product_every_int.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/math/product_every_int.py diff --git a/First_edition_2014/other_resources/interview_cake/math/recursive_per.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/math/recursive_per.py similarity index 100% rename from First_edition_2014/other_resources/interview_cake/math/recursive_per.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/math/recursive_per.py diff --git a/First_edition_2014/other_resources/interview_cake/sort_and_search/big_words.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/sort_and_search/big_words.py similarity index 100% rename from First_edition_2014/other_resources/interview_cake/sort_and_search/big_words.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/sort_and_search/big_words.py diff --git a/First_edition_2014/other_resources/interview_cake/sort_and_search/binary_search.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/sort_and_search/binary_search.py similarity index 100% rename from First_edition_2014/other_resources/interview_cake/sort_and_search/binary_search.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/sort_and_search/binary_search.py diff --git a/First_edition_2014/other_resources/interview_cake/sort_and_search/merge_sort.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/sort_and_search/merge_sort.py similarity index 100% rename from First_edition_2014/other_resources/interview_cake/sort_and_search/merge_sort.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/sort_and_search/merge_sort.py diff --git a/First_edition_2014/other_resources/interview_cake/sort_and_search/merge_sorted_list.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/sort_and_search/merge_sorted_list.py similarity index 100% rename from First_edition_2014/other_resources/interview_cake/sort_and_search/merge_sorted_list.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/sort_and_search/merge_sorted_list.py diff --git a/First_edition_2014/other_resources/interview_cake/strings/hical_str_manipulation.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/strings/hical_str_manipulation.py similarity index 100% rename from First_edition_2014/other_resources/interview_cake/strings/hical_str_manipulation.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/strings/hical_str_manipulation.py diff --git a/First_edition_2014/other_resources/interview_cake/strings/online_poker.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/strings/online_poker.py similarity index 100% rename from First_edition_2014/other_resources/interview_cake/strings/online_poker.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/strings/online_poker.py diff --git a/First_edition_2014/other_resources/interview_cake/strings/palindrome.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/strings/palindrome.py similarity index 100% rename from First_edition_2014/other_resources/interview_cake/strings/palindrome.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/strings/palindrome.py diff --git a/First_edition_2014/other_resources/interview_cake/strings/reverse_in_place.py b/MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/strings/reverse_in_place.py similarity index 100% rename from First_edition_2014/other_resources/interview_cake/strings/reverse_in_place.py rename to MY_BOOK/ebook_src/real_interview_problems/other_resources/interview_cake/strings/reverse_in_place.py diff --git a/First_edition_2014/ebook_src/real_interview_problems/palindome.py b/MY_BOOK/ebook_src/real_interview_problems/palindome.py similarity index 100% rename from First_edition_2014/ebook_src/real_interview_problems/palindome.py rename to MY_BOOK/ebook_src/real_interview_problems/palindome.py diff --git a/First_edition_2014/ebook_src/real_interview_problems/permutation.py b/MY_BOOK/ebook_src/real_interview_problems/permutation.py similarity index 100% rename from First_edition_2014/ebook_src/real_interview_problems/permutation.py rename to MY_BOOK/ebook_src/real_interview_problems/permutation.py diff --git a/First_edition_2014/ebook_src/real_interview_problems/queue.py b/MY_BOOK/ebook_src/real_interview_problems/queue.py similarity index 100% rename from First_edition_2014/ebook_src/real_interview_problems/queue.py rename to MY_BOOK/ebook_src/real_interview_problems/queue.py diff --git a/First_edition_2014/ebook_src/real_interview_problems/quick_sort.py b/MY_BOOK/ebook_src/real_interview_problems/quick_sort.py similarity index 100% rename from First_edition_2014/ebook_src/real_interview_problems/quick_sort.py rename to MY_BOOK/ebook_src/real_interview_problems/quick_sort.py diff --git a/First_edition_2014/ebook_src/real_interview_problems/reverse_str.py b/MY_BOOK/ebook_src/real_interview_problems/reverse_str.py similarity index 100% rename from First_edition_2014/ebook_src/real_interview_problems/reverse_str.py rename to MY_BOOK/ebook_src/real_interview_problems/reverse_str.py diff --git a/First_edition_2014/ebook_src/real_interview_problems/stack.py b/MY_BOOK/ebook_src/real_interview_problems/stack.py similarity index 100% rename from First_edition_2014/ebook_src/real_interview_problems/stack.py rename to MY_BOOK/ebook_src/real_interview_problems/stack.py diff --git a/requirements.txt b/MY_BOOK/ebook_src/requirements.txt similarity index 67% rename from requirements.txt rename to MY_BOOK/ebook_src/requirements.txt index 33aaeda..e79bb92 100644 --- a/requirements.txt +++ b/MY_BOOK/ebook_src/requirements.txt @@ -1,12 +1,12 @@ -SQLAlchemy==0.9.7 +SQLAlchemy==1.3.0 bpython==0.13.1 coverage==3.7.1 curtsies==0.0.34 graphviz==0.4.2 -ipython==0.13.2 +ipython==8.10.0 matplotlib==1.3.1 nose==1.3.0 -numpy==1.8.2 +numpy==1.22.0 scapy==2.4.1 scikit-learn==0.14.1 -scipy==0.12.1 \ No newline at end of file +scipy==1.10.0 \ No newline at end of file diff --git a/First_edition_2014/ebook_src/builtin_structures/__init__.py b/MY_BOOK/ebook_src/searching_and_sorting/searching/__init__.py old mode 100755 new mode 100644 similarity index 100% rename from First_edition_2014/ebook_src/builtin_structures/__init__.py rename to MY_BOOK/ebook_src/searching_and_sorting/searching/__init__.py diff --git a/First_edition_2014/ebook_src/searching_and_sorting/searching/binary_search.py b/MY_BOOK/ebook_src/searching_and_sorting/searching/binary_search.py similarity index 100% rename from First_edition_2014/ebook_src/searching_and_sorting/searching/binary_search.py rename to MY_BOOK/ebook_src/searching_and_sorting/searching/binary_search.py diff --git a/First_edition_2014/ebook_src/searching_and_sorting/searching/binary_search_matrix.py b/MY_BOOK/ebook_src/searching_and_sorting/searching/binary_search_matrix.py similarity index 100% rename from First_edition_2014/ebook_src/searching_and_sorting/searching/binary_search_matrix.py rename to MY_BOOK/ebook_src/searching_and_sorting/searching/binary_search_matrix.py diff --git a/First_edition_2014/ebook_src/searching_and_sorting/searching/find_item_rotated_sorted_array.py b/MY_BOOK/ebook_src/searching_and_sorting/searching/find_item_rotated_sorted_array.py similarity index 100% rename from First_edition_2014/ebook_src/searching_and_sorting/searching/find_item_rotated_sorted_array.py rename to MY_BOOK/ebook_src/searching_and_sorting/searching/find_item_rotated_sorted_array.py diff --git a/First_edition_2014/ebook_src/searching_and_sorting/searching/find_max_unimodal_array.py b/MY_BOOK/ebook_src/searching_and_sorting/searching/find_max_unimodal_array.py similarity index 100% rename from First_edition_2014/ebook_src/searching_and_sorting/searching/find_max_unimodal_array.py rename to MY_BOOK/ebook_src/searching_and_sorting/searching/find_max_unimodal_array.py diff --git a/First_edition_2014/ebook_src/searching_and_sorting/searching/find_sqrt_bin_search.py b/MY_BOOK/ebook_src/searching_and_sorting/searching/find_sqrt_bin_search.py similarity index 100% rename from First_edition_2014/ebook_src/searching_and_sorting/searching/find_sqrt_bin_search.py rename to MY_BOOK/ebook_src/searching_and_sorting/searching/find_sqrt_bin_search.py diff --git a/First_edition_2014/ebook_src/searching_and_sorting/searching/find_str_array_with_empty_str.py b/MY_BOOK/ebook_src/searching_and_sorting/searching/find_str_array_with_empty_str.py similarity index 100% rename from First_edition_2014/ebook_src/searching_and_sorting/searching/find_str_array_with_empty_str.py rename to MY_BOOK/ebook_src/searching_and_sorting/searching/find_str_array_with_empty_str.py diff --git a/First_edition_2014/ebook_src/searching_and_sorting/searching/find_time_occurence_list.py b/MY_BOOK/ebook_src/searching_and_sorting/searching/find_time_occurence_list.py similarity index 92% rename from First_edition_2014/ebook_src/searching_and_sorting/searching/find_time_occurence_list.py rename to MY_BOOK/ebook_src/searching_and_sorting/searching/find_time_occurence_list.py index af26ded..c7fa6e0 100644 --- a/First_edition_2014/ebook_src/searching_and_sorting/searching/find_time_occurence_list.py +++ b/MY_BOOK/ebook_src/searching_and_sorting/searching/find_time_occurence_list.py @@ -2,7 +2,7 @@ __author__ = "bt3" -def binary_serch_counting(lst1, k, lo=0, hi=None): +def binary_search_counting(lst1, k, lo=0, hi=None): if hi is None: hi = len(lst1) while lo < hi: mid = (lo+hi)//2 @@ -24,7 +24,7 @@ def find_time_occurrence_list(seq, k): the dict is fixed. Another way, since the array is sorted, it to use binary search, since this is only O(logn). """ - index_some_k = binary_serch_counting(seq, k) + index_some_k = binary_search_counting(seq, k) count = 1 sizet = len(seq) diff --git a/First_edition_2014/ebook_src/searching_and_sorting/searching/ordered_sequential_search.py b/MY_BOOK/ebook_src/searching_and_sorting/searching/ordered_sequential_search.py similarity index 100% rename from First_edition_2014/ebook_src/searching_and_sorting/searching/ordered_sequential_search.py rename to MY_BOOK/ebook_src/searching_and_sorting/searching/ordered_sequential_search.py diff --git a/First_edition_2014/ebook_src/searching_and_sorting/searching/quick_select.py b/MY_BOOK/ebook_src/searching_and_sorting/searching/quick_select.py similarity index 100% rename from First_edition_2014/ebook_src/searching_and_sorting/searching/quick_select.py rename to MY_BOOK/ebook_src/searching_and_sorting/searching/quick_select.py diff --git a/First_edition_2014/ebook_src/searching_and_sorting/searching/searching_in_a_matrix.py b/MY_BOOK/ebook_src/searching_and_sorting/searching/searching_in_a_matrix.py similarity index 100% rename from First_edition_2014/ebook_src/searching_and_sorting/searching/searching_in_a_matrix.py rename to MY_BOOK/ebook_src/searching_and_sorting/searching/searching_in_a_matrix.py diff --git a/First_edition_2014/ebook_src/searching_and_sorting/searching/sequential_search.py b/MY_BOOK/ebook_src/searching_and_sorting/searching/sequential_search.py similarity index 100% rename from First_edition_2014/ebook_src/searching_and_sorting/searching/sequential_search.py rename to MY_BOOK/ebook_src/searching_and_sorting/searching/sequential_search.py diff --git a/First_edition_2014/ebook_src/searching_and_sorting/sorting/1.dat b/MY_BOOK/ebook_src/searching_and_sorting/sorting/1.dat similarity index 100% rename from First_edition_2014/ebook_src/searching_and_sorting/sorting/1.dat rename to MY_BOOK/ebook_src/searching_and_sorting/sorting/1.dat diff --git a/First_edition_2014/ebook_src/searching_and_sorting/sorting/2.dat b/MY_BOOK/ebook_src/searching_and_sorting/sorting/2.dat similarity index 100% rename from First_edition_2014/ebook_src/searching_and_sorting/sorting/2.dat rename to MY_BOOK/ebook_src/searching_and_sorting/sorting/2.dat diff --git a/First_edition_2014/ebook_src/searching_and_sorting/sorting/3.dat b/MY_BOOK/ebook_src/searching_and_sorting/sorting/3.dat similarity index 100% rename from First_edition_2014/ebook_src/searching_and_sorting/sorting/3.dat rename to MY_BOOK/ebook_src/searching_and_sorting/sorting/3.dat diff --git a/First_edition_2014/ebook_src/searching_and_sorting/searching/__init__.py b/MY_BOOK/ebook_src/searching_and_sorting/sorting/__init__.py similarity index 100% rename from First_edition_2014/ebook_src/searching_and_sorting/searching/__init__.py rename to MY_BOOK/ebook_src/searching_and_sorting/sorting/__init__.py diff --git a/First_edition_2014/ebook_src/searching_and_sorting/sorting/bubble_sort.py b/MY_BOOK/ebook_src/searching_and_sorting/sorting/bubble_sort.py similarity index 100% rename from First_edition_2014/ebook_src/searching_and_sorting/sorting/bubble_sort.py rename to MY_BOOK/ebook_src/searching_and_sorting/sorting/bubble_sort.py diff --git a/First_edition_2014/ebook_src/searching_and_sorting/sorting/count_sort.py b/MY_BOOK/ebook_src/searching_and_sorting/sorting/count_sort.py similarity index 100% rename from First_edition_2014/ebook_src/searching_and_sorting/sorting/count_sort.py rename to MY_BOOK/ebook_src/searching_and_sorting/sorting/count_sort.py diff --git a/First_edition_2014/ebook_src/searching_and_sorting/sorting/gnome_sort.py b/MY_BOOK/ebook_src/searching_and_sorting/sorting/gnome_sort.py similarity index 100% rename from First_edition_2014/ebook_src/searching_and_sorting/sorting/gnome_sort.py rename to MY_BOOK/ebook_src/searching_and_sorting/sorting/gnome_sort.py diff --git a/First_edition_2014/ebook_src/searching_and_sorting/sorting/heap.py b/MY_BOOK/ebook_src/searching_and_sorting/sorting/heap.py similarity index 100% rename from First_edition_2014/ebook_src/searching_and_sorting/sorting/heap.py rename to MY_BOOK/ebook_src/searching_and_sorting/sorting/heap.py diff --git a/First_edition_2014/ebook_src/searching_and_sorting/sorting/heap_sort.py b/MY_BOOK/ebook_src/searching_and_sorting/sorting/heap_sort.py similarity index 100% rename from First_edition_2014/ebook_src/searching_and_sorting/sorting/heap_sort.py rename to MY_BOOK/ebook_src/searching_and_sorting/sorting/heap_sort.py diff --git a/First_edition_2014/ebook_src/searching_and_sorting/sorting/insertion_sort.py b/MY_BOOK/ebook_src/searching_and_sorting/sorting/insertion_sort.py similarity index 100% rename from First_edition_2014/ebook_src/searching_and_sorting/sorting/insertion_sort.py rename to MY_BOOK/ebook_src/searching_and_sorting/sorting/insertion_sort.py diff --git a/First_edition_2014/ebook_src/searching_and_sorting/sorting/merge_and_sort_two_arrays.py b/MY_BOOK/ebook_src/searching_and_sorting/sorting/merge_and_sort_two_arrays.py similarity index 100% rename from First_edition_2014/ebook_src/searching_and_sorting/sorting/merge_and_sort_two_arrays.py rename to MY_BOOK/ebook_src/searching_and_sorting/sorting/merge_and_sort_two_arrays.py diff --git a/First_edition_2014/ebook_src/searching_and_sorting/sorting/merge_sort.py b/MY_BOOK/ebook_src/searching_and_sorting/sorting/merge_sort.py similarity index 100% rename from First_edition_2014/ebook_src/searching_and_sorting/sorting/merge_sort.py rename to MY_BOOK/ebook_src/searching_and_sorting/sorting/merge_sort.py diff --git a/First_edition_2014/ebook_src/searching_and_sorting/sorting/quick_sort.py b/MY_BOOK/ebook_src/searching_and_sorting/sorting/quick_sort.py similarity index 100% rename from First_edition_2014/ebook_src/searching_and_sorting/sorting/quick_sort.py rename to MY_BOOK/ebook_src/searching_and_sorting/sorting/quick_sort.py diff --git a/First_edition_2014/ebook_src/searching_and_sorting/sorting/selection_sort.py b/MY_BOOK/ebook_src/searching_and_sorting/sorting/selection_sort.py similarity index 100% rename from First_edition_2014/ebook_src/searching_and_sorting/sorting/selection_sort.py rename to MY_BOOK/ebook_src/searching_and_sorting/sorting/selection_sort.py diff --git a/First_edition_2014/ebook_src/searching_and_sorting/sorting/sort_anagrams_together.py b/MY_BOOK/ebook_src/searching_and_sorting/sorting/sort_anagrams_together.py similarity index 100% rename from First_edition_2014/ebook_src/searching_and_sorting/sorting/sort_anagrams_together.py rename to MY_BOOK/ebook_src/searching_and_sorting/sorting/sort_anagrams_together.py diff --git a/First_edition_2014/ebook_src/searching_and_sorting/sorting/__init__.py b/MY_BOOK/ebook_src/trees/__init__.py old mode 100644 new mode 100755 similarity index 100% rename from First_edition_2014/ebook_src/searching_and_sorting/sorting/__init__.py rename to MY_BOOK/ebook_src/trees/__init__.py diff --git a/First_edition_2014/ebook_src/trees/binary_search_tree.py b/MY_BOOK/ebook_src/trees/binary_search_tree.py similarity index 98% rename from First_edition_2014/ebook_src/trees/binary_search_tree.py rename to MY_BOOK/ebook_src/trees/binary_search_tree.py index 9f82203..d0802d4 100755 --- a/First_edition_2014/ebook_src/trees/binary_search_tree.py +++ b/MY_BOOK/ebook_src/trees/binary_search_tree.py @@ -19,7 +19,7 @@ class Node(object): new_node = Node(value) if not self.item: - self.item = new_node + self.item = new_node.value else: if value > self.item: diff --git a/First_edition_2014/ebook_src/trees/binary_tree.py b/MY_BOOK/ebook_src/trees/binary_tree.py similarity index 100% rename from First_edition_2014/ebook_src/trees/binary_tree.py rename to MY_BOOK/ebook_src/trees/binary_tree.py diff --git a/First_edition_2014/ebook_src/trees/binary_tree_generators.py b/MY_BOOK/ebook_src/trees/binary_tree_generators.py similarity index 100% rename from First_edition_2014/ebook_src/trees/binary_tree_generators.py rename to MY_BOOK/ebook_src/trees/binary_tree_generators.py diff --git a/First_edition_2014/ebook_src/trees/bunchclass.py b/MY_BOOK/ebook_src/trees/bunchclass.py similarity index 100% rename from First_edition_2014/ebook_src/trees/bunchclass.py rename to MY_BOOK/ebook_src/trees/bunchclass.py diff --git a/First_edition_2014/ebook_src/trees/check_ancestor.py b/MY_BOOK/ebook_src/trees/check_ancestor.py similarity index 100% rename from First_edition_2014/ebook_src/trees/check_ancestor.py rename to MY_BOOK/ebook_src/trees/check_ancestor.py diff --git a/First_edition_2014/ebook_src/trees/check_if_balanced.py b/MY_BOOK/ebook_src/trees/check_if_balanced.py similarity index 100% rename from First_edition_2014/ebook_src/trees/check_if_balanced.py rename to MY_BOOK/ebook_src/trees/check_if_balanced.py diff --git a/First_edition_2014/ebook_src/trees/check_if_bst.py b/MY_BOOK/ebook_src/trees/check_if_bst.py similarity index 97% rename from First_edition_2014/ebook_src/trees/check_if_bst.py rename to MY_BOOK/ebook_src/trees/check_if_bst.py index 792013f..eb8e8da 100755 --- a/First_edition_2014/ebook_src/trees/check_if_bst.py +++ b/MY_BOOK/ebook_src/trees/check_if_bst.py @@ -37,7 +37,7 @@ def isBST_other_method(node, max_node=None, min_node=None): if node.right: if node.right.item < node.item or node.right.item < min_node: - rihjt = False + right = False else: min_node = node.item right = isBST(node.right, max_node, min_node) diff --git a/First_edition_2014/ebook_src/trees/check_largest_item.py b/MY_BOOK/ebook_src/trees/check_largest_item.py similarity index 100% rename from First_edition_2014/ebook_src/trees/check_largest_item.py rename to MY_BOOK/ebook_src/trees/check_largest_item.py diff --git a/First_edition_2014/ebook_src/trees/simple_tree.py b/MY_BOOK/ebook_src/trees/simple_tree.py similarity index 100% rename from First_edition_2014/ebook_src/trees/simple_tree.py rename to MY_BOOK/ebook_src/trees/simple_tree.py diff --git a/First_edition_2014/ebook_src/trees/transversal.py b/MY_BOOK/ebook_src/trees/transversal.py similarity index 100% rename from First_edition_2014/ebook_src/trees/transversal.py rename to MY_BOOK/ebook_src/trees/transversal.py diff --git a/First_edition_2014/ebook_src/trees/trie.py b/MY_BOOK/ebook_src/trees/trie.py similarity index 100% rename from First_edition_2014/ebook_src/trees/trie.py rename to MY_BOOK/ebook_src/trees/trie.py diff --git a/README.md b/README.md index 211e6c7..f9f15db 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,67 @@ -## Book on Python, Algorithms, and Data Structures +## šŸ‘¾šŸ master algorithms with python (and my book šŸ–¤) + +
-- [PDF and source code for 1st edition (2014, published by Hanbit Media)](https://github.com/bt3gl/Python-and-Algorithms-and-Data-Structures/blob/master/First_edition_2014/ebook_pdf/book_second_edition.pdf). +

+ +

-- [PDF and source code for 2nd edition (2019, ported to Python 3.7)](https://github.com/bt3gl/Python-and-Algorithms-and-Data-Structures/blob/master/Second_edition_2019). +
+ +--- + +### šŸ“– algorithms and data structures: learn with my examples! (2023) + +
+ +* šŸ˜šŸ˜šŸ˜šŸ™. **[arrays and strings](arrays_and_strings)** +* šŸ˜šŸ˜šŸ™šŸ˜. **[bit operations](bit_operations)** +* šŸ˜šŸ˜šŸ™šŸ™. **[dynamic programming](dynamic_programming)** +* šŸ˜šŸ™šŸ˜šŸ˜. **[graphs](graphs)** +* šŸ˜šŸ™šŸ˜šŸ™. **[hash objects](hash_objects)** +* šŸ˜šŸ™šŸ™šŸ˜. **[heaps](heaps)** +* šŸ˜šŸ™šŸ™šŸ™. **[linked lists](linked_lists)** +* šŸ™šŸ˜šŸ˜šŸ˜. **[math](math)** +* šŸ™šŸ˜šŸ˜šŸ™. **[queues](queues)** +* šŸ™šŸ˜šŸ™šŸ˜. **[searching](searching)** +* šŸ™šŸ˜šŸ™šŸ™. **[sets](sets)** +* šŸ™šŸ™šŸ˜šŸ˜. **[sorting](sorting)** +* šŸ™šŸ™šŸ˜šŸ™. **[stacks](stacks)** +* šŸ™šŸ™šŸ™šŸ˜. **[trees](trees)** +* šŸ™šŸ™šŸ™šŸ™. **[tries](tries)** + +
+ + +--- + +### [šŸ“– my book on algorithms and data structure: open-source for you! (2014)](MY_BOOK) + +
+ +- **āž”ļø [one of the first-ever publications solving classic algorithm and data structure problems in python, published by hanbit media](https://www.hanbit.co.kr/store/books/look.php?p_code=B8465804191)**. +- **āž”ļø [last time i checked, it had 4.6/5 stars and 33 reviews (not bad for a book written in school by a self-taught!)](https://www.hanbit.co.kr/store/books/look.php?p_code=B8465804191)**. +- **āž”ļø [this repo used to have 600+ stars and 300 forks before šŸ’© happened šŸ˜ž (here is a proof)](MY_BOOK/600_stars.png)**. +- **āž”ļø [just for fun: this book as a reference for a CMU computer science class](https://www.andrew.cmu.edu/user/ramesh/teaching/course/48784.pdf)**. ------ +
+

+ -This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/). +
+
+
+ + + +--- + +### external resources + +
+ +* **[big-o complexities chart and explanation](https://www.bigocheatsheet.com/)** diff --git a/Second_edition_2019/README.md b/Second_edition_2019/README.md deleted file mode 100644 index 8de71bf..0000000 --- a/Second_edition_2019/README.md +++ /dev/null @@ -1,4 +0,0 @@ -## Python, Algorithms, and Data Structures (2nd Edition, 2019) - - - diff --git a/arrays_and_strings/3rd_distinct_n.py b/arrays_and_strings/3rd_distinct_n.py new file mode 100644 index 0000000..dbaa778 --- /dev/null +++ b/arrays_and_strings/3rd_distinct_n.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +''' +Given an integer array nums, return the third distinct maximum +number in this array. If the third maximum does not exist, +return the maximum number. + +Do it O(n). +''' + +import math + + +def third_max(nums: list[int]) -> int: + + first_max = -math.inf + second_max = -math.inf + third_max = -math.inf + + for n in nums: + + if n == first_max or n == second_max or n == third_max: + continue + elif n > first_max: + third_max = second_max + second_max = first_max + first_max = n + elif n > second_max: + third_max = second_max + second_max = n + elif n > third_max: + third_max = n + + if third_max == -math.inf: + third_max = max(second_max, first_max) + + return third_max + + +if __name__ == "__main__": + + nums = [3,2,1] + assert(third_max(nums) == 1) diff --git a/arrays_and_strings/README.md b/arrays_and_strings/README.md new file mode 100644 index 0000000..7ea7cdc --- /dev/null +++ b/arrays_and_strings/README.md @@ -0,0 +1,406 @@ +## arrays and strings + +
+ +* arrays and strings hold values of the same type at contiguous memory locations with the advantage of storing multiple elements of the same type with one single variable name. + +* we are usually concerned with two things: the index of an element, and the element itself. + +* accessing elements is fast as long as you have the index (as opposed to linked lists that need to be traversed from the head). + +* addition or removal of elements into/from the middle of an array is slow because the remaining elements need to be shifted to accommodate the new/missing element (unless they are inserted/removed at the end of the list). + +* **subarray**: a range of contiguous values within an array (for example, in `[2, 3, 6, 1, 5, 4]`, `[3, 6, 1]` is a subarray while `[3, 1, 5]` is not). + +* **subsequence**: a sequence derived from the given sequence without changing the order (for example, in `[2, 3, 6, 1, 5, 4]`, `[3, 1, 5]` is a subsequence but `[3, 5, 1]` is not). + +
+ +---- + +### two-pointer technique + +
+ +* a typical scenario is when you want to iterate the array from two ends to the middle or when pointers can cross each others (or even be in different arrays). + +* another scenario is when you need one slow-runner and one fast-runner at the same time (so that you can determine the movement strategy for both pointers). + +* in any case, this technique is usually used when the array is sorted. + +* in the **sliding window** technique, the two pointers usually move in the same direction and never overtake each other. examples are: longest substring without repeating characters, minimum size subarray sum, and minimum window substring. + +
+ +---- + +### intervals + +
+ +* checking if two intervals overlap: + +```python +def is_overlap(a, b): + return a[0] < b[1] and b[0] < a[1] +``` + +
+ +* merging two intervals: + +```python +def merge_overlapping_intervals(a, b): + return [min(a[0], b[0]), max(a[1], b[1])] +``` + + +
+ +--- + +### two-dimensional arrays + +
+ +* a matrix is a 2d array. they can be used to represent graphs where each node is a cell on the matrix which has 4 neighbors (except those cells on the edge and corners). + +* in some languages (like C++), 2d arrays are represented as 1d, so an array of `m * n` elements represents `array[i][j]` as `array[i * n + j]`. + +* creating an empty matrix: + +
+ +```python +zero_matrix = [ [0 for _ in range(len(matrix[0]))] for _ in range(len(matrix)) ] +``` + +
+ +* copying a matrix: + +
+ +```python +copied_matrix = [ row[:] for row in mattrix ] +``` + +
+ +* the transpose of a matrix can be found by interchanging its rows into columns or columns into rows: + +
+ +```python +transposed = zip(*matrix) +``` + +
+ +--- + +### check if mountain + +
+ +```python +def valid_mountain_array(arr: list[int]) -> bool: + + last_number, mountain_up = arr[0], True + + for i, n in enumerate(arr[1:]): + + if n > last_number: + if mountain_up == False: + return False + + elif n < last_number: + if i == 0: + return False + mountain_up = False + + else: + return False + + last_number = n + + return not mountain_up +``` + +
+ +--- + +### duplicate zeros in place + +
+ +```python +def duplicate_zeros(arr: list[int]) -> list[int]: + + i = 0 + while i < len(arr): + + if arr[i] == 0 and i != len(arr) - 1: + + range_here = len(arr) - (i + 2) + while range_here > 0: + arr[i + range_here + 1] = arr[i + range_here] + range_here -= 1 + + arr[i+1] = 0 + i += 2 + + else: + i += 1 + + return arr +``` + +
+ +---- + +### remove duplicates in place + +
+ +```python +def remove_duplicates(nums: list[int]) -> int: + + arr_i, dup_i = 0, 1 + + while arr_i < len(nums) and dup_i < len(nums): + + if nums[arr_i] == nums[dup_i]: + dup_i += 1 + + else: + arr_i += 1 + nums[arr_i] = nums[dup_i] + + for i in range(arr_i + 1, dup_i): + nums[i] = '_' + + return dup_i - arr_i - 1, nums +``` + +
+ +--- + +### anagrams + +
+ +* to determine if two strings are anagrams, there are a few approaches: + - sorting both strings should produce the same string (`O(N log(N))` time and `O(log(N))` space. + - if we map each character to a prime number and we multiply each mapped number together, anagrams should have the same multiple (prime factor decomposition, `O(N)`). + - frequency counting of characters can determine whether they are anagram (`O(N)`). + +
+ +```python +def is_anagram(string1, string2) -> bool: + + string1 = string1.lower() + string2 = string2.lower() + + if len(string1) != len(string2): + return False + + for c in string1: + if c not in string2: + return False + + return True +``` + +
+ + + +---- + +### palindromes + +
+ +* ways to determine if a string is a palindrome: + * reverse the string and they should be equal. + * have two pointers at the start and end of the string, moving the pointers until they meet. + +
+ +```python +def is_palindrome(sentence): + + sentence = sentence.strip(' ') + if len(sentence) < 2: + return True + + if sentence[0] == sentence[-1]: + return is_palindrome(sentence[1:-1]) + + return False +``` + + +
+ +```python +def is_permutation_of_palindromes(some_string): + + aux_dict = {} + + for c in some_string.strip(): + + if c in aux_dict.keys(): + aux_dict[c] -= 1 + else: + aux_dict[c] = 1 + + for v in aux_dict.values(): + if v != 0: + return False + + return True +``` + + +
+ + + +--- + +### intersection of two arrays + +
+ +```python +def intersect(nums1: list[int], nums2: list[int]) -> list[int]: + + result = [] + set_nums = set(nums1) & set(nums2) + counter = Counter(nums1) & Counter(nums2) + + for n in set_nums: + result.extend([n] * counter[n]) + + return result +``` + +
+ +--- + + +### check if isomorphic + +
+ +```python +def is_isomorphic(s: str, t: str) -> bool: + + map_s_to_t = {} + map_t_to_s = {} + + for ss, tt in zip(s, t): + + if (ss not in map_s_to_t) and (tt not in map_t_to_s): + map_s_to_t[ss] = tt + map_t_to_s[tt] = ss + + elif (map_s_to_t.get(ss) != tt) or (map_t_to_s.get(tt) != ss): + return False + + return True +``` + +
+ +--- + +### absolute difference between the sums of a matrix's diagonals + +
+ +```python + +def diagonal_difference(arr): + + diag_1 = 0 + diag_2 = 0 + + i, j = 0, len(arr) - 1 + + while i < len(arr) and j >= 0: + + diag_1 += arr[i][i] + diag_2 += arr[i][j] + i += 1 + j -= 1 + + return diag_1, diag_2, abs(diag_1 - diag_2) +``` + + + +
+ +--- + +### find permutations + +
+ +```python + + +def permutations(string) -> list: + + if len(string) == 1: + return [string] + + result = [] + for i, char in enumerate(string): + for perm in permutation(string[:i] + string[i+1:]): + result += [char + perm] + + return result +``` + +
+ +--- + +### length of the longest substring + +
+ +```python +def length_longest_substring(s) -> int: + + result = "" + this_longest_string = "" + i = 0 + + for c in s: + j = 0 + + while j < len(this_longest_string): + + if c == this_longest_string[j]: + if len(this_longest_string) > len(result): + result = this_longest_string + this_longest_string = this_longest_string[j+1:] + + j += 1 + + this_longest_string += c + + return result, this_longest_string +``` + +
diff --git a/arrays_and_strings/check_if_exist.py b/arrays_and_strings/check_if_exist.py new file mode 100644 index 0000000..15ec58b --- /dev/null +++ b/arrays_and_strings/check_if_exist.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +""" +Given an array arr of integers, check if there exist two indices i and j such that : + +i != j +0 <= i, j < arr.length +arr[i] == 2 * arr[j] +""" + + +def check_if_exist(arr: list[int]) -> bool: + + aux_dict = {} + + for i, num in enumerate(arr): + aux_dict[2 * num] = i + + for j, num in enumerate(arr): + if num in aux_dict.keys() and j != aux_dict[num]: + return (j, aux_dict[num]) + + + return False + +if __name__ == "__main__": + + arr = [-2, 0, 10, -19, 4, 6, -8] + print(check_if_exist(arr)) diff --git a/arrays_and_strings/check_mountain.py b/arrays_and_strings/check_mountain.py new file mode 100644 index 0000000..c29cd00 --- /dev/null +++ b/arrays_and_strings/check_mountain.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +''' +Given an array of integers arr, return true if and only if it is a valid mountain array. +An array is a moutain array if and only if: + +arr.length >= 3 +There exists some i with 0 < i < arr.length - 1 such that: +arr[0] < arr[1] < ... < arr[i - 1] < arr[i] +arr[i] > arr[i + 1] > ... > arr[arr.length - 1] +''' + +def valid_mountain_array(arr: list[int]) -> bool: + + last_number, mountain_up = arr[0], True + + for i, n in enumerate(arr[1:]): + + if n > last_number: + if mountain_up == False: + return False + + elif n < last_number: + if i == 0: + return False + mountain_up = False + + else: + return False + + last_number = n + + return not mountain_up + diff --git a/arrays_and_strings/check_permutation_strings_is_palindrome.py b/arrays_and_strings/check_permutation_strings_is_palindrome.py new file mode 100644 index 0000000..44efd9f --- /dev/null +++ b/arrays_and_strings/check_permutation_strings_is_palindrome.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def is_permutation_of_palindromes(some_string): + + aux_dict = {} + + for c in some_string.strip(): + + if c in aux_dict.keys(): + aux_dict[c] -= 1 + else: + aux_dict[c] = 1 + + for v in aux_dict.values(): + if v != 0: + return False + + return True diff --git a/arrays_and_strings/duplicate_zeros_inplace.py b/arrays_and_strings/duplicate_zeros_inplace.py new file mode 100644 index 0000000..055d41a --- /dev/null +++ b/arrays_and_strings/duplicate_zeros_inplace.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +# Given a fixed-length integer array arr, duplicate each occurrence of zero, +# shifting the remaining elements to the right. + + +def duplicate_zeros(arr: list[int]) -> list[int]: + + i = 0 + while i < len(arr): + + if arr[i] == 0 and i != len(arr) - 1: + + range_here = len(arr) - (i + 2) + while range_here > 0: + arr[i + range_here + 1] = arr[i + range_here] + range_here -= 1 + + arr[i+1] = 0 + i += 2 + + else: + i += 1 + + return arr diff --git a/arrays_and_strings/intersection_two_arrays.py b/arrays_and_strings/intersection_two_arrays.py new file mode 100644 index 0000000..cc91c7e --- /dev/null +++ b/arrays_and_strings/intersection_two_arrays.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def intersect(nums1: list[int], nums2: list[int]) -> list[int]: + + result = [] + set_nums = set(nums1) & set(nums2) + counter = Counter(nums1) & Counter(nums2) + + for n in set_nums: + result.extend([n] * counter[n]) + + return result + diff --git a/arrays_and_strings/is_anagram.py b/arrays_and_strings/is_anagram.py new file mode 100644 index 0000000..989409b --- /dev/null +++ b/arrays_and_strings/is_anagram.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def is_anagram(string1, string2) -> bool: + + string1 = string1.lower() + string2 = string2.lower() + + if len(string1) != len(string2): + return False + + for c in string1: + if c not in string2: + return False + + return True diff --git a/arrays_and_strings/is_isomorphic.py b/arrays_and_strings/is_isomorphic.py new file mode 100644 index 0000000..c5fd564 --- /dev/null +++ b/arrays_and_strings/is_isomorphic.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def is_isomorphic(s: str, t: str) -> bool: + + map_s_to_t = {} + map_t_to_s = {} + + for ss, tt in zip(s, t): + + if (ss not in map_s_to_t) and (tt not in map_t_to_s): + map_s_to_t[ss] = tt + map_t_to_s[tt] = ss + + elif (map_s_to_t.get(ss) != tt) or (map_t_to_s.get(tt) != ss): + return False + + return True diff --git a/arrays_and_strings/longest_non_repeating.py b/arrays_and_strings/longest_non_repeating.py new file mode 100644 index 0000000..cd9d0d4 --- /dev/null +++ b/arrays_and_strings/longest_non_repeating.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def length_longest_substring(s) -> int: + + result = "" + this_longest_string = "" + i = 0 + + for c in s: + j = 0 + + while j < len(this_longest_string): + + if c == this_longest_string[j]: + if len(this_longest_string) > len(result): + result = this_longest_string + this_longest_string = this_longest_string[j+1:] + + j += 1 + + this_longest_string += c + + return result, this_longest_string + diff --git a/arrays_and_strings/matrix_sum_diagonals.py b/arrays_and_strings/matrix_sum_diagonals.py new file mode 100644 index 0000000..ae00c52 --- /dev/null +++ b/arrays_and_strings/matrix_sum_diagonals.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +def diagonal_difference(arr): + + diag_1 = 0 + diag_2 = 0 + + i, j = 0, len(arr) - 1 + + while i < len(arr) and j >= 0: + + diag_1 += arr[i][i] + diag_2 += arr[i][j] + i += 1 + j -= 1 + + return diag_1, diag_2, abs(diag_1 - diag_2) + diff --git a/arrays_and_strings/merge_inplace.py b/arrays_and_strings/merge_inplace.py new file mode 100644 index 0000000..d6703e9 --- /dev/null +++ b/arrays_and_strings/merge_inplace.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +""" +You are given two integer arrays nums1 and nums2, sorted in non-decreasing order, and two +integers m and n, representing the number of elements in nums1 and nums2 respectively. + +Merge nums1 and nums2 into a single array sorted in non-decreasing order. + +The final sorted array should not be returned by the function, +but instead be stored inside the array nums1. +To accommodate this, nums1 has a length of m + n, where the first m elements denote the elements +that should be merged, and the last n elements are set to 0 and should be ignored. nums2 has a length of n. +""" + +def merge(big_list: list[int], m: int, small_list: list[int], n: int) -> None: + + index_big, index_small, index_backward = m - 1, n - 1, m + n - 1 + + while index_big >= 0 and index_small >= 0: + + if small_list[index_small] > big_list[index_big]: + big_list[index_backward] = small_list[index_small] + index_small -= 1 + else: + big_list[index_backward] = big_list[index_big] + index_big -= 1 + + index_backward -= 1 + + while index_backward >= 0 and index_big >= 0: + big_list[index_backward] = big_list[index_big] + index_backward -= 1 + index_big -= 1 + + while index_backward >= 0 and index_small >= 0: + big_list[index_backward] = small_list[index_small] + index_backward -= 1 + index_small -= 1 diff --git a/arrays_and_strings/move_zeros_inplace.py b/arrays_and_strings/move_zeros_inplace.py new file mode 100644 index 0000000..ae8cf7d --- /dev/null +++ b/arrays_and_strings/move_zeros_inplace.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +''' +Given an integer array nums, move all 0's to the end of it while +maintaining the relative order of the non-zero elements. + +Note that you must do this in-place without making a copy of the array. +Example 1: + +Input: nums = [0,1,0,3,12] +Output: [1,3,12,0,0] +Example 2: + +Input: nums = [0] +Output: [0] +''' + + +def move_zeroes(nums: list[int]) -> list[int]: + + i = 0 + + while i < len(nums) - 1: + + if nums[i] == 0: + j = i + 1 + while nums[j] == 0 and j < len(nums) - 1: + j += 1 + + nums[i], nums[j] = nums[j], nums[i] + + i += 1 + + + return nums diff --git a/arrays_and_strings/palindrome.py b/arrays_and_strings/palindrome.py new file mode 100644 index 0000000..ad39b0c --- /dev/null +++ b/arrays_and_strings/palindrome.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +def is_palindrome(sentence): + + sentence = sentence.strip(' ') + if len(sentence) < 2: + return True + + if sentence[0] == sentence[-1]: + return is_palindrome(sentence[1:-1]) + + return False diff --git a/arrays_and_strings/permutations.py b/arrays_and_strings/permutations.py new file mode 100644 index 0000000..f778c12 --- /dev/null +++ b/arrays_and_strings/permutations.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def permutations(string) -> list: + + if len(string) == 1: + return [string] + + result = [] + for i, char in enumerate(string): + for perm in permutation(string[:i] + string[i+1:]): + result += [char + perm] + + return result diff --git a/arrays_and_strings/pivot_index_array.py b/arrays_and_strings/pivot_index_array.py new file mode 100644 index 0000000..cb1336b --- /dev/null +++ b/arrays_and_strings/pivot_index_array.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def pivot_index(nums): + + s, left_sum = sum(nums), 0 + + for i, x in enumerate(nums): + + if left_sum == (s - left_sum - x): + return i + + left_sum += x + + return -1 diff --git a/arrays_and_strings/playing_with_strings.py b/arrays_and_strings/playing_with_strings.py new file mode 100644 index 0000000..1d1bb67 --- /dev/null +++ b/arrays_and_strings/playing_with_strings.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +def reverse_array_in_place(array): + return array[::-1] diff --git a/arrays_and_strings/remove_dups_inplace.py b/arrays_and_strings/remove_dups_inplace.py new file mode 100644 index 0000000..a63a715 --- /dev/null +++ b/arrays_and_strings/remove_dups_inplace.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + + +def remove_duplicates(nums: list[int]) -> int: + + arr_i, dup_i = 0, 1 + + while arr_i < len(nums) and dup_i < len(nums): + + if nums[arr_i] == nums[dup_i]: + dup_i += 1 + + else: + arr_i += 1 + nums[arr_i] = nums[dup_i] + + for i in range(arr_i + 1, dup_i): + nums[i] = '_' + + return dup_i - arr_i - 1, nums + diff --git a/arrays_and_strings/return_matrix_in_spiral.py b/arrays_and_strings/return_matrix_in_spiral.py new file mode 100644 index 0000000..ba13ae3 --- /dev/null +++ b/arrays_and_strings/return_matrix_in_spiral.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def spiral_order(matrix): + + result = [] + rows, cols = len(matrix), len(matrix[0]) + up = left = 0 + right = cols - 1 + down = rows - 1 + + while len(result) < rows * cols: + for col in range(left, right + 1): + result.append(matrix[up][col]) + + for row in range(up + 1, down + 1): + result.append(matrix[row][right]) + + if up != down: + for col in range(right - 1, left - 1, -1): + result.append(matrix[down][col]) + + if left != right: + for row in range(down - 1, up, -1): + result.append(matrix[row][left]) + + left += 1 + right -= 1 + up += 1 + down -= 1 + + return result diff --git a/arrays_and_strings/two_sums.py b/arrays_and_strings/two_sums.py new file mode 100644 index 0000000..ac133e5 --- /dev/null +++ b/arrays_and_strings/two_sums.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def two_sum(nums: list[int], target: int) -> list[int]: + + aux_dict = {} + for i, n in enumerate(nums): + complement = target - n + + if complement in aux_dict: + return [aux_dict[complement][0], i] + + aux_dict[n] = (i, n) + diff --git a/arrays_and_strings/unique_word_abbreviation.py b/arrays_and_strings/unique_word_abbreviation.py new file mode 100644 index 0000000..6394018 --- /dev/null +++ b/arrays_and_strings/unique_word_abbreviation.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +''' +The abbreviation of a word is a concatenation of its first letter, +the number of characters between the first and last letter, and its +last letter. If a word has only two characters, then it is an abbreviation of itself. +Returns true if either of the following conditions are met (otherwise returns false): +- There is no word in dictionary whose abbreviation is equal to word's abbreviation. +- For any word in dictionary whose abbreviation is equal to word's abbreviation, +that word and word are the same. +''' + +class ValidWordAbbr: + + def __init__(self, dictionary): + + self.dict = collections.defaultdict(set) + for w in dictionary: + aux_dict[self.get_abr(w)].add(w) + + return aux_dict + + def get_abr(self, word): + + return word[0] + str(len(word[1:-1])) + word[-1] if len(word) != 2 else word + + def is_unique(self, word: str) -> bool: + + abr = self.get_abr(word) + words = self.dict[abr] + + return len(words) == 0 or (len(words) == 1 and word in words) + diff --git a/bit_operations/README.md b/bit_operations/README.md new file mode 100644 index 0000000..d41b63c --- /dev/null +++ b/bit_operations/README.md @@ -0,0 +1,299 @@ +## bit manipulation + + +
+ +### techniques + +
+ +* test if kth bit is set: `num & (1 << k) != 0` + +* set kth bit: `num |= (1 << k)` + +* turn off kth bit: `num &= ~(1 << k)` + +* toggle the kth bit: `num ^= (1 << k)` + +* multiply by `2^k`: `num << k` + +* divide by `2^k`: `num >> k` + +* check if a number is a power of 2: `(num & num - 1) == 0` or `(num & (-num)) == num` + +* swapping two variables: `num1 ^= num2; num2 ^= num`; `num1 ^= num2` + + + +
+ +--- + +### converting a decimal to another base (`X`) + +
+ +* the **base** is a carry counting system with fixed digital symbols and rules. + +* you need to convert the integer part and the fractional part separately. + +* to convert the integer part, integer divide it by `X` until it reaches `0`, and record the remainder each time. + +* traversing the remainder in reverse order will give the representation in the base-X system. + +``` +50/2 = 25, 25/2 = 12, 12/2 = 6, 6/2 = 3, 3/2 = 1, 1/2 = 0 +50%2 = 0, 25%2 = 1, 12%2 = 0, 6%2 = 0, 3%2 = 1, 1%2 = 1 +--> 110010 +``` + +* to convert a fractional part, multiply the fractional part of the decimal number by `X `until it becomes `0`, and record the integer part each time. + +``` +0.6875x2 = 1.375 with integer 1, +0.375x2 = 0.75 with integer 0, +0.75x2 = 1.5 with integer 1, +0.5x2 = 1, with integer 1 +--> 0.1011 +``` + +
+ +```python +def convert_to_any_base(base: int, num: int) -> str: + + if num == 0: + return "0" + + n = abs(num) + result = "" + + while n: + result += str(n % base) + n //= base + + if num < 0: + result += '-' + + return result[::-1] +``` + +
+ + + +---- + +### hexadecimal + +
+ +* an integer of type int can be represented as a `32-bit` binary number. + +* since a one-digit hexadecimal number corresponds to a four-digit binary number, an integer of type int can also be represented as an `8-bit` hexadecimal number. + +
+ +```python +def convert_to_hex(num: int) -> str: + + hex_chars = "0123456789abcdef" + size = 32 + base = 16 + + if num == 0: + return "0" + + if num < 1: + num += 2**size + + result = "" + while num: + result += hex_chars[num % base] + num //= base + + return result[::-1] +``` + +
+ +---- + +### binary in computers + +
+ +* a single binary digit has two possible values, and a `k`-digit binary number can take `2^k` possible values. + +* `1-byte` number (`8` digits binary number) has `2^8` possible values. + +* in a `1-byte` signed integer, when the highest bit is `0`, the `1-byte` number ranges from `0` to `127` (`2^7 - 1`), when the highest bit is `1`, it ranges from `-128` to `-1` (*i.e.,* `-128` to `127`, which is `-2^7` to `2^7 -1`). + +* the binary representation of a number in a computer is called its **machine number**. it's a signed number, and the highest bit of the machine number is the sign bit `0`. + +* inverse code: for non-negative numbers, it's the same, for negative numbers, you flip every bit of the original code, except the sign bit. + * introducing the inverse code solves the problem of subtraction errors, but the issue of dual representation of `0` remains. +* complement code: is obtained from the inverse code, for non-negative numbers it's the same, for negative numbers it's obtained by adding `1` to the inverse code. for example, for `-10`, the original code is `10001010`, the inverse code is `11110101`, and the complement code is `11110110`. + * the complement code solves both the subtraction error and dual representation of the `0` problem (in complement code, there is no `-0`)., + +
+ +---- + +### bit operations + +
+ +- there are 6 types of bit operations: `AND`, `OR`, `XOR`, `negation`, `left shift`, and `right shift` (shift operations are further divided into the arithmetic shift and logical shift): + - `OR` and `AND`: symmetrical operations. + - `XOR`: when the corresponding bits of the two numbers are the same, the result is `1`. + - negation is unary: just flips the bit. + - left shift operation, `<<`, all binary bits are shifted to the left (the high bits are discarded, and the low bits are filled with 0). for left shift operations, arithmetic shift and logical shift are the same. + - right shift operation, `>>`, all binary bits are shift to the right (low bits are discarded). how the high bits get filled differs between arithmetic shift and logical shift: + - when shifting right arithmetically, the high bits are filled with the highest bit. + - when shifting right logically, the high bits are filled with `0`. + - for non-negative numbers, the arithmetic right shift and logical right shift are identical. + - for signed types, the right shift operation is an arithmetic right shift; for unsigned types, the right shift operation is a logical right shift. + - for languages without unsigned data type, the arithmetic right shift is `>>` and the logical shift is `>>>`. + +
+ +---- + +### relationship with multiplication/division + +
+ +* left shift corresponds to multiplication: shifting a number to the left by `k` bits is equivalent to multiplying the number by `2^k`. + * when the multiplier is not an integer power of `2`, the multiplier can be split into the sum of the integer power of two. + * for example, `a x 6` is equivalent to `(a << 2) + (a << 1)`. + * be careful against overflow. + +* arithmetic right-shift corresponds to the division: shifting a number to the right by k bits is equivalent to dividing the number by `2^k` for non-negative numbers. + * integer division is rounded to `0`, and the right shift operation is rounded down, which is also rounded to `0`. + * for negative numbers, integer division is rounded to `0`, while the right shift is rounded down -> so it's not equivalent to dividing a number by `2^k`. + +
+ +----- + +### properties of bitwise operations + +
+ +* idempotent: `a & a = a` + +* commutativity: `a & b = b &a` + +* associativity: `(a & b) & c = a * (b & c)` + +* distributive: `(a & b} | c = (a | c) & (b | c)` + +* de morgan's law: `~(a & b) = (~a) | (~b); ~ (a | b) = (~a) & (~b)` + +* 0 XOR: `a XOR 0 = a`; `a XOR a = 0` + +* `a & (a - 1)` changes the last 1 in the binary representation of a to 0. + +* ` a & (-a)` (equivalent to `a & (~(a - 1)`) keeps only the last 1 of the binary representation of a and sets the remaining 1s to 0. + + +
+ +---- + +### two's complement method + +
+ +* the complement is the number with respect to `2**n`: + +1. start with the equivalent positive number +2. invert (or flip) all bits, changing every `0` to `1`, and every `1` to `0` +3. add `1` to the entire inverted number, ignoring overflow + +
+ +--- + +### playing with bits + +
+ +```python +def count_ones(n: int) -> int: + + counter = 0 + + while n: + + if n & 1: + counter += 1 + + n >>= 1 + + return counter + +def set_bit(num, i): + mask = 1 << i + return bin( num | mask ) + +def update_bit(num, i, v): + mask = ~ (1 << i) + return bin( (num & mask) | (v << i) ) + +def count_bits_swapped(a, b): + count = 0 + m = a^b + while m: + count +=1 + m = m & (m-1) + return count + +def clear_bit(num, i): + mask = ~ (1 << i) # -0b10001 + return bin(num & mask) + +def swap_bit_in_place(a, b): + a = a^b + b = a^b + a = a^b + return a, b + +def find_how_many_1_in_a_binary(num): + + counter = 0 + while num: + if num & 1: + counter += 1 + num >>= 1 + return counter + +def reverse_bits(n: int) -> int: + + result, base = 0, 31 + while n: + result += (n & 1) << base + n >>= 1 + base -= 1 + + return result + +def get_sum(self, a: int, b: int) -> int: + + if a == -b: + return 0 + + if abs(a) > abs(b): + a, b = b, a + + if a < 0: + return - get_sum(-a, -b) + + while b: + + c = a & b + a, b = a ^ b, c << 1 + + return a +``` diff --git a/bit_operations/convert_any_base.py b/bit_operations/convert_any_base.py new file mode 100644 index 0000000..e24830d --- /dev/null +++ b/bit_operations/convert_any_base.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def convert_to_any_base(base: int, num: int) -> str: + + if num == 0: + return "0" + + n = abs(num) + result = "" + + while n: + result += str(n % base) + n //= base + + if num < 0: + result += '-' + + return result[::-1] diff --git a/bit_operations/convert_to_hex.py b/bit_operations/convert_to_hex.py new file mode 100644 index 0000000..0b25aec --- /dev/null +++ b/bit_operations/convert_to_hex.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def convert_to_hex(num: int) -> str: + + hex_chars = "0123456789abcdef" + size = 32 + base = 16 + + if num == 0: + return "0" + + if num < 1: + num += 2**size + + result = "" + while num: + result += hex_chars[num % base] + num //= base + + return result[::-1] diff --git a/bit_operations/count_1s.py b/bit_operations/count_1s.py new file mode 100644 index 0000000..b841a8e --- /dev/null +++ b/bit_operations/count_1s.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def count_ones(n: int) -> int: + + counter = 0 + + while n: + + if n & 1: + counter += 1 + + n >>= 1 + + return counter diff --git a/bit_operations/playing_with_bits.py b/bit_operations/playing_with_bits.py new file mode 100644 index 0000000..b03fa18 --- /dev/null +++ b/bit_operations/playing_with_bits.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def set_bit(num, i): + mask = 1 << i + return bin( num | mask ) + +def update_bit(num, i, v): + mask = ~ (1 << i) + return bin( (num & mask) | (v << i) ) + +def count_bits_swapped(a, b): + count = 0 + m = a^b + while m: + count +=1 + m = m & (m-1) + return count + +def clear_bit(num, i): + mask = ~ (1 << i) # -0b10001 + return bin(num & mask) + +def swap_bit_in_place(a, b): + a = a^b + b = a^b + a = a^b + return a, b + +def find_how_many_1_in_a_binary(num): + + counter = 0 + while num: + if num & 1: + counter += 1 + num >>= 1 + return counter + + +if __name__ == '__main__': + + binary_number = '10010000' + binary_number2 = '01011010' + integer_number = int(binary_number, 2) + integer_number2 = int(binary_number2, 2) + + print(f'Integer number: {integer_number}') + print(f'Binary number: {binary_number}') + print(f'\nUpdate bit: {update_bit(integer_number, 2, 1)}') + print(f'Set bit: {set_bit(integer_number, 2)}') + print(f'Clear bit: {clear_bit(integer_number, 4)}') + print(f'\nI: {integer_number}, I2: {integer_number2}') + print(f'B: {binary_number}, B2: {binary_number2}') + print(f'Count bits swapped: {count_bits_swapped(integer_number, integer_number2)}') + print(f'\nSwap bit in place: {swap_bit_in_place(integer_number, integer_number2)}') + print(f'Find how many 1 in a binary: {find_how_many_1_in_a_binary(integer_number)}') + \ No newline at end of file diff --git a/bit_operations/reverse_bits.py b/bit_operations/reverse_bits.py new file mode 100644 index 0000000..cf2984f --- /dev/null +++ b/bit_operations/reverse_bits.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +def reverse_bits(n: int) -> int: + + result, base = 0, 31 + + while n: + result += (n & 1) << base + n >>= 1 + base -= 1 + + return result diff --git a/bit_operations/sum_with_bitwise.py b/bit_operations/sum_with_bitwise.py new file mode 100644 index 0000000..cb0b004 --- /dev/null +++ b/bit_operations/sum_with_bitwise.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def get_sum(self, a: int, b: int) -> int: + + if a == -b: + return 0 + + if abs(a) > abs(b): + a, b = b, a + + if a < 0: + return - get_sum(-a, -b) + + while b: + + c = a & b + a, b = a ^ b, c << 1 + + return a diff --git a/dynamic_programming/README.md b/dynamic_programming/README.md new file mode 100644 index 0000000..0398542 --- /dev/null +++ b/dynamic_programming/README.md @@ -0,0 +1,253 @@ +## dynamic programming + +
+ +* dynamic programming is the process of taking a recursive algorithm and cache overlapping problems (repeated calls). the runtime is given by the number of calls. + +* **top-down** (**memoization**): how can we divide the problem into sub-problems? + +* **bottom-up** (**tabulation**): solve for a simple case, then figure out for more elements. + + +
+ +--- + +### recursion + +
+ +* recursion is an approach to solving problems using a function that calls itself as a subroutine. every time the function calls itself, it reduces the problem into subproblems. the recursion calls continue until it reaches a point where the subproblem can be solved without further recursion. + +* a recursive function should have the following properties so it does not result in an infinite loop: + * one or more base cases (a terminating scenario that does not use recursion to produce an answer) + * a set of rules, also known as a recurrence relation, that reduces all other cases towards the base case. + +
+ +```python +def fib(n): + if n <= 1: + return n + return fib(n - 1) + fib(n - 2) +``` + +
+ +* there can be multiple places where the function may call itself. + +* any recursion problem can be solved iteratively and vice-versa. + +* the **master theorem** is an advanced technique of asymptotic analysis (time complexity) for many of the recursion algorithms that follow the pattern of divide-and-conquer. + +
+ + +#### vizualing the stack + +
+ +* to visualize how the stack operates during recursion calls, check the example below where we reverse a string: + +
+ +```python +def reverse(s): + if len(s) == 0: + return s + else: + return reverse(s[1:]) + s[0] +``` + +
+ + +#### tail recursion + +
+ +* tail recursion is a recursion where the recursive call is the final instruction in the recursion function. and there should be only one recursive call in the function. + +* tail recursion is exempted from the space overhead discussed above, ad it skips an entire chain of recursive calls returning and returns straight to the original caller (it does not need a call stack for all the recursive calls - instead of allocating new space on the stack, the system could simply reuse the space allocated earlier for this second recursion call). + +* when stack overflows, tail recursion might help. + +* some languages' compiler recognizes tail recursion pattern and optimizes their execution (e.g., C and C++, but not Java, Rust, or Python - although it's possible to implement ad-hoc). + +
+ +```python +def sum_non_tail_recursion(ls): + if len(ls) == 0: + return 0 + + # not a tail recursion because it does some computation after the recursive call returned + return ls[0] + sum_non_tail_recursion(ls[1:]) + + +def sum_tail_recursion(ls): + + def helper(ls, acc): + if len(ls) == 0: + return acc + + # this is a tail recursion because the final instruction is a recursive call + return helper(ls[1:], ls[0] + acc) + + return helper(ls, 0) +``` + +
+ + + +--- + +### memoization + +
+ +* memoization is an optimization technique that avoids recursion's duplicate calculations. + +* it's primarily used to speed up code by storing the intermediate results in a cache so that it can be reused later. + +* for example, a hash table can be used as a cache and should be passed along each subroutine call. + +
+ +```python +function dp(dp_state, memo_dict): + + if dp_state is the base cases + return things like 0 or null + + if dp_state in memo_dict + return memo_dict[dp_state] + + calculate dp(dp_state) from dp(other_state) + save dp_state and the result into memo_dict + +function answerToProblem(input) + return dp(start_state, empty_memo_dict) +``` + +
+ +* classic examples where memoization can be used are fibonacci and the "climbing stairs" problem: + +
+ +```python +def climb_stairs_memoization(n: int) -> int: + + memo = {} + + def helper(n: int, memo: dict[int, int]) -> int: + if n == 0 or n == 1: + return 1 + if n not in memo: + memo[n] = helper(n-1, memo) + helper(n-2, memo) + return memo[n] + + return helper(n, memo) +``` + +
+ +* another good example is calculating all possible subnodes in a tree: + +
+ +```python +class Node: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +def all_possible_bst(start, end, memo): + + result = [] + if start > end: + return result + + if (start, end) in memo: + return memo[(start, end)] + + for i in range(start, end + 1): + left = all_possible_bst(start, i - 1, memo) + right = all_possible_bst(i + 1, end, memo) + + for l in left: + for r in right: + result.append(Node(i, l, r)) + + memo[(start, end)] = result + return result + + +def generate_trees(n): + + memo = {} + return all_possible_bst(1, n, memo) +``` + +
+ +---- + +### time complexity + +
+ +* the time complexity of a recursion algorithm is the product of the number of recursion invocations and the time complexity of the calculation, `R*O(s)`. + +* you can also look at the "execution tree", which is a tree that is used to denote the execution flow of a recursive function in particular. each node represents an invocation of the recursive function. therefore, the total number of nodes in the tree corresponds to the number of recursion calls during the execution. + * the execution tree of a recursive function would form a n-ary tree, with as the number of times recursion appears in the recurrence relation (for example, fibonacci would be a binary tree). + * in a full binary tree with n levels, the total number of nodes is `2**N - 1`, so `O(2**N)` would be the time complexity of the function. + * with memoization, fibonacci becomes `O(1)*N = O(N)`. + +
+ +---- + +### space complexity + +
+ +* there are mainly two parts of the space consumption that one should see in a recursive algorithm: + * **recursion related space**: refers to the memory cost that is incurred directly by the recursion, i.e. the stack to keep track of recursive function calls. in order to complete a recursive call, the system allocates some space in the stack to hold: the returning address of the function call, the parameters that are passed to the function call, the local variables within the function call. once the function call is done, the space is freed. for recursive algorithms, the function calls chain up successively until it reaches a base case, so the space used for each call is accumulated. overconsumption can cause stack overflow. + * **non-recursive related space**: refers to the memory that is not directly related to recursion, which includes the space (normally in heap) that is allocated for global variables. + +
+ + +--- + +### backtracking + +
+ +* backtracking is a general algorithm for finding solutions to some computation problems, which incrementally builds candidates to the solution and abandons a candidate ("backtracks") as soon as it determines that the candidate cannot lead to a valid solution. + +* you can imagine the procedure as the tree traversal. + +
+ +```python +def backtrack(candidate): + if find_solution(candidate): + output(candidate) + return + + for next in list_of_candidates: + + if is_valid(next_candidate): + place(next_candidate) + backtrack(next_candidate) + remove(next_candidate) +```` + +
+ diff --git a/dynamic_programming/bst_all_subnodes.py b/dynamic_programming/bst_all_subnodes.py new file mode 100644 index 0000000..4ac8255 --- /dev/null +++ b/dynamic_programming/bst_all_subnodes.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +class Node: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +def all_possible_bst(start, end, memo): + + result = [] + if start > end: + return result + + if (start, end) in memo: + return memo[(start, end)] + + for i in range(start, end + 1): + left = all_possible_bst(start, i - 1, memo) + right = all_possible_bst(i + 1, end, memo) + + for l in left: + for r in right: + result.append(Node(i, l, r)) + + memo[(start, end)] = result + return result + + +def generate_trees(n): + + memo = {} + return all_possible_bst(1, n, memo) diff --git a/dynamic_programming/climbing_stairs.py b/dynamic_programming/climbing_stairs.py new file mode 100644 index 0000000..c699acd --- /dev/null +++ b/dynamic_programming/climbing_stairs.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +''' +You are climbing a staircase. It takes n steps to reach the top. +Each time you can either climb 1 or 2 steps. +In how many distinct ways can you climb to the top? +''' + + +def climb_stairs_o2n(n: int) -> int: + + if n == 0 or n == 1: + return 1 + return climb_stairs_o2n(n - 1) + climb_stairs_o2n(n - 2) + + +def climb_stairs_memoization(n: int) -> int: + + memo = {} + + def helper(n: int, memo: dict[int, int]) -> int: + if n == 0 or n == 1: + return 1 + if n not in memo: + memo[n] = helper(n - 1, memo) + helper(n - 2, memo) + return memo[n] + + return helper(n, memo) + + +def climb_stairs_optimized(n: int) -> int: + + if n == 0 or n == 1: + return 1 + prev, curr = 1, 1 + + for i in range(2, n + 1): + temp = curr + curr = prev + curr + prev = temp + + return curr + + +def climb_stairs_tabulation(n: int) -> int: + + if n == 0 or n == 1: + return 1 + + dp = [0] * (n + 1) + dp[0] = dp[1] = 1 + + for i in range(2, n + 1): + dp[i] = dp[i - 1] + dp[i - 2] + + return dp[n] diff --git a/graphs/README.md b/graphs/README.md new file mode 100644 index 0000000..e12cea3 --- /dev/null +++ b/graphs/README.md @@ -0,0 +1,339 @@ +## graphs + +
+ +* graph is a non-linear data structure consisting of vertices and edges. + + +* graphs can be represented by adjacent matrices, adjacent lists, and hash table of hash tables. + + +* in **undirected graphs**, the edges between any two vertices do not have a direction, indicating a two-way relationship. + + +* in **directed graphs**, the edges between any two vertices are directional. + + +* in **weighted graphs**, each edge has an associated weight. if the sum of the weights of all edges of a cycle is a negative values, it's a negative weight cycle. + + +* the **degree of a vertex** is the number of edges connecting the vertex. in directed, graphs, if the **in-degree** of a vertex is `d`, there are **d** directional edges incident to the vertex (and similarly, **out-degree** from the vertex). + + +* with `|V|` the number of vertices and `|E|` is the number of edges, search in a graph (either bfs of dfs) is `O(|V| + |E|)`. + +
+ +--- + +### traversals + +
+ +#### breath first search + +
+ +* check **[../trees/#breath-first-search](https://github.com/go-outside-labs/master-algorithms-py/blob/master/trees/README.md#tree-traversal-depth-first-search)** + +
+ +```python +def bfs(matrix): + + if not matrix: + return [] + + rows, cols = len(matrix), len(matrix[0]) + visited = set() + directions = ((0, 1), (0, -1), (1, 0), (-1, 0)) + + def traverse(i, j): + queue = deque([(i, j)]) + while queue: + curr_i, curr_j = queue.popleft() + if (curr_i, curr_j) not in visited: + visited.add((curr_i, curr_j)) + + for direction in directions: + next_i, next_j = curr_i + direction[0], curr_j + direction[1] + if 0 <= next_i < rows and 0 <= next_j < cols: + + queue.append((next_i, next_j)) + + for i in range(rows): + for j in range(cols): + traverse(i, j) +``` + +
+ +* or as a class: + +
+ +```python + +from collections import deque + + +class Graph: + + def __init__(self, edges, n): + + self.adj_list = [[] for _ in range(n)] + + for (src, dest) in edges: + self.adj_list[src].append(dest) + self.adj_list[dest].append(src) + + +def bfs(graph, v, discovered): + + queue = deque(v) + discovered[v] = True + + while queue: + + v = queue.popleft() + print(v, end=' ') + + for u in graph.adj_list[v]: + if not discovered[u]: + discovered[u] = True + queue.append(u) + + +def recursive_bfs(graph, queue, discovered): + + if not queue: + return + + v = queue.popleft() + print(v, end=' ') + + for u in graph.adj_list[v]: + if not discovered[u]: + discovered[u] = True + queue.append(u) + + recursive_bfs(graph, queue, discovered) +``` + +
+ +---- + +#### depth first search + +
+ +* and **[../trees/#depth-first-search](https://github.com/go-outside-labs/master-algorithms-py/blob/master/trees/README.md#tree-traversal-breath-first-search-level-order)** + +
+ +```python +def dfs(matrix): + if not matrix: + return [] + + rows, cols = len(matrix), len(matrix[0]) + visited = set() + directions = ((0, 1), (0, -1), (1, 0), (-1, 0)) + + def traverse(i, j): + if (i, j) in visited: + return + + visited.add((i, j)) + for direction in directions: + next_i, next_j = i + direction[0], j + direction[1] + if 0 <= next_i < rows and 0 <= next_j < cols: + traverse(next_i, next_j) + + for i in range(rows): + for j in range(cols): + traverse(i, j) +``` + +
+ +* or as a class: + +
+ +```python +from collections import deque + +class Graph: + + def __init__(self, edges, n): + + self.adj_list = [[] for _ in range(n)] + + for (src, dest) in edges: + self.adj_list[src].append(dest) + self.adj_list[dest].append(src) + + +def dfs(graph, v, discovered): + + discovered[v] = True + print(v, end=' ') + + for u in graph.adj_list[v]: + if not discovered[u]: + dfs(graph, u, discovered) + + + +def iterative_dfs(graph, v, discovered): + + stack = [v] + + while stack: + + v = stack.pop() + + if discovered[v]: + continue + + discovered[v] = True + print(v, end=' ') + + adj_list = graph.adjList[v] + for i in reversed(range(len(adj_list))): + u = adj_list[i] + if not discovered[u]: + stack.append(u) +``` + +
+ +--- + +### matrix bfs and dfs + +
+ +* given an `m x n` grid rooms initialized with these three possible values: + * -1 A wall or an obstacle. + * 0 A gate. + * `INF` Infinity means an empty room (`2^31 - 1 = 2147483647` to represent `INF`) + +* fill the empty room with the distance to its nearest gate. if it is impossible to reach a gate, it should be filled with `INF`. + +
+ +```python + + +def matrix_bfs(rooms) -> None: + + m = len(rooms) + if m == 0: + return rooms + n = len(rooms[0]) + + GATE = 0 + EMPTY = 2147483647 + DIRECTIONS = ((1, 0), (-1, 0), (0, 1), (0, -1)) + + queue = collections.deque() + + for i in range(m): + for j in range(n): + + if rooms[i][j] == GATE: + q.append((i, j)) + + while queue: + + row, col = queue.popleft() + + for (x, y) in DIRECTIONS: + + r = row + x + c = col + y + + if (r < 0) or (c < 0) or (r >= m) or (c >= n) or rooms[r][c] != EMPTY: + continue + + rooms[r][c] = rooms[row][col] + 1 + queue.append((r, c)) +``` + +
+ + +* given an `m x n` 2D binary grid grid which represents a map of '1's (land) and '0's (water), return the number of islands. + +
+ +```python +def num_island_dfs(grid) -> int: + + LAND = '1' + answer = 0 + + def dfs(row, col): + + if row < 0 or row >= len(grid) or col < 0 or col >= len(grid[0]) or grid[row][col] != LAND: + return + + grid[row][col] = 'x' + dfs(row + 1, col) + dfs(row - 1, col) + dfs(row, col - 1) + dfs(row, col + 1) + + for i in range(len(grid)): + for j in range(len(grid[0])): + if grid[i][j] == LAND: + answer += 1 + dfs(i, j) + + return answer +``` + +
+ +* and a solution for the problem above, using bfs: + +
+ +```python +def num_island_bfs(grid) -> int: + + LAND = '1' + answer = 0 + queue = collections.deque() + + def bfs(row, col, queue): + + delta = [(1, 0), (0, 1), (-1, 0), (0, -1)] + + while queue: + x, y = queue.popleft() + + for dx, dy in delta: + + px, py = x + dx, y + dy + if px < 0 or px >= len(grid) or py < 0 or py >= len(grid[0]) or grid[px][py] != LAND: + continue + + grid[px][py] = 'x' + queue.append((px, py)) + + + for i in range(len(grid)): + for j in range(len(grid[0])): + + if grid[i][j] == LAND: + answer += 1 + queue.append((i, j)) + bfs(i, j, queue) + + + return answer +``` diff --git a/graphs/bfs.py b/graphs/bfs.py new file mode 100644 index 0000000..edafb4c --- /dev/null +++ b/graphs/bfs.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +from collections import deque + + +class Graph: + + def __init__(self, edges, n): + + self.adj_list = [[] for _ in range(n)] + + for (src, dest) in edges: + self.adj_list[src].append(dest) + self.adj_list[dest].append(src) + + +def bfs(graph, v, discovered): + + queue = deque(v) + discovered[v] = True + + while queue: + + v = queue.popleft() + print(v, end=' ') + + for u in graph.adj_list[v]: + if not discovered[u]: + discovered[u] = True + queue.append(u) + + +def recursive_bfs(graph, queue, discovered): + + if not queue: + return + + v = queue.popleft() + print(v, end=' ') + + for u in graph.adj_list[v]: + if not discovered[u]: + discovered[u] = True + queue.append(u) + + recursive_bfs(graph, queue, discovered) + diff --git a/graphs/dfs.py b/graphs/dfs.py new file mode 100644 index 0000000..a65a85b --- /dev/null +++ b/graphs/dfs.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +from collections import deque + +class Graph: + + def __init__(self, edges, n): + + self.adj_list = [[] for _ in range(n)] + + for (src, dest) in edges: + self.adj_list[src].append(dest) + self.adj_list[dest].append(src) + + +def dfs(graph, v, discovered): + + discovered[v] = True + print(v, end=' ') + + for u in graph.adj_list[v]: + if not discovered[u]: # + dfs(graph, u, discovered) + + + +def iterative_dfs(graph, v, discovered): + + stack = [v] + + while stack: + + v = stack.pop() + + if discovered[v]: + continue + + discovered[v] = True + print(v, end=' ') + + adj_list = graph.adjList[v] + for i in reversed(range(len(adj_list))): + u = adj_list[i] + if not discovered[u]: + stack.append(u) + diff --git a/graphs/dijkstra_adj_matrix.py b/graphs/dijkstra_adj_matrix.py new file mode 100644 index 0000000..732e9c9 --- /dev/null +++ b/graphs/dijkstra_adj_matrix.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +# dijkstra's single source shortest path algorithm +# with adjacency matrix representation + +import sys + + +class Graph(): + + def __init__(self, vertices): + + self.V = vertices + self.graph = [[0 for col in range(vertices)] for row in range(vertices)] + + def print_solution(self, dist): + + print("Vertex \tDistance from Source") + for node in range(self.V): + print(node, "\t", dist[node]) + + def min_distance(self, dist, sset): + + # minimum distance for next node + min_ = sys.maxsize + + # search not nearest vertex not in the shortest path tree + for u in range(self.V): + if dist[u] < min_ and sset[u] == False: + min_ = dist[u] + min_index = u + + return min_index + + + def dijkstra(self, src): + + dist = [sys.maxsize] * self.V + dist[src] = 0 + sset = [False] * self.V + + for c in range(self.V): + x = self.min_distance(dist, sset) + sset[x] = True + + for y in range(self.V): + if self.graph[x][y] > 0 and \ + sset[y] == False and \ + dist[y] > dist[x] + self.graph[x][y]: + dist[y] = dist[x] + self.graph[x][y] + + self.print_solution(dist) + + +if __name__ == "__main__": + g = Graph(9) + g.graph = [ [0, 4, 0, 0, 0, 0, 0, 8, 0], + [4, 0, 8, 0, 0, 0, 0, 11, 0], + [0, 8, 0, 7, 0, 4, 0, 0, 2], + [0, 0, 7, 0, 9, 14, 0, 0, 0], + [0, 0, 0, 9, 0, 10, 0, 0, 0], + [0, 0, 4, 14, 10, 0, 2, 0, 0], + [0, 0, 0, 0, 0, 2, 0, 1, 6], + [8, 11, 0, 0, 0, 0, 1, 0, 7], + [0, 0, 2, 0, 0, 0, 6, 7, 0] + ] + + g.dijkstra(0) diff --git a/graphs/matrix_bfs.py b/graphs/matrix_bfs.py new file mode 100644 index 0000000..edf4737 --- /dev/null +++ b/graphs/matrix_bfs.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def matrix_bfs(rooms) -> None: + + m = len(rooms) + if m == 0: + return rooms + n = len(rooms[0]) + + GATE = 0 + EMPTY = 2147483647 + DIRECTIONS = ((1, 0), (-1, 0), (0, 1), (0, -1)) + + queue = collections.deque() + + for i in range(m): + for j in range(n): + + if rooms[i][j] == GATE: + q.append((i, j)) + + while queue: + + row, col = queue.popleft() + + for (x, y) in DIRECTIONS: + + r = row + x + c = col + y + + if (r < 0) or (c < 0) or (r >= m) or (c >= n) or rooms[r][c] != EMPTY: + continue + + rooms[r][c] = rooms[row][col] + 1 + queue.append((r, c)) diff --git a/graphs/matrix_dfs_and_bfs.py b/graphs/matrix_dfs_and_bfs.py new file mode 100644 index 0000000..f85a251 --- /dev/null +++ b/graphs/matrix_dfs_and_bfs.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def num_island_dfs(grid) -> int: + + LAND = '1' + answer = 0 + + def dfs(row, col): + + if row < 0 or row >= len(grid) or col < 0 or col >= len(grid[0]) or grid[row][col] != LAND: + return + + grid[row][col] = 'x' + dfs(row + 1, col) + dfs(row - 1, col) + dfs(row, col - 1) + dfs(row, col + 1) + + for i in range(len(grid)): + for j in range(len(grid[0])): + if grid[i][j] == LAND: + answer += 1 + dfs(i, j) + + return answer + + +def num_island_bfs(grid) -> int: + + LAND = '1' + answer = 0 + queue = collections.deque() + + def bfs(row, col, queue): + + delta = [(1, 0), (0, 1), (-1, 0), (0, -1)] + + while queue: + x, y = queue.popleft() + + for dx, dy in delta: + + px, py = x + dx, y + dy + if px < 0 or px >= len(grid) or py < 0 or py >= len(grid[0]) or grid[px][py] != LAND: + continue + + grid[px][py] = 'x' + queue.append((px, py)) + + + for i in range(len(grid)): + for j in range(len(grid[0])): + + if grid[i][j] == LAND: + answer += 1 + queue.append((i, j)) + bfs(i, j, queue) + + + return answer diff --git a/hash_objects/README.md b/hash_objects/README.md new file mode 100644 index 0000000..dbb0bc4 --- /dev/null +++ b/hash_objects/README.md @@ -0,0 +1,298 @@ +## hash objects + + +
+ +* hash tables (also known as hash maps) are data structure that organizes data using hash functions, in order to support quick insertion and search (map keys to buckets). + +
+ + + +

+ +

+ + +
+ +--- + +### collision + +
+ +* ideally, a perfect hash function will be a 1-1 mapping between the key and the buckets. however, in most cases, a hash function is not perfect and there is a tradeoff between the amount of buckets and the capacity of a bucket. if the hash function is not a 1-1 mapping, collisions must be handled: + - how to organize the values in the same bucket? + - what if too many values are assigned to the same bucket? + - how to search for a target value in a specific bucket? + +* popular collision techniques are: + - separate chaining: a linked list is used for each value, so that it stores all the collided items. + - open addressing: all entry records are stored in the bucket array itself. when a new entry has to be inserted, the buckets are examined, starting with the hashed-to slot and proceeding in some probe sequence, until an unoccupied slot is found. + + +
+ +---- + +### keys + +
+ +* when the order of each element in the string/array doesn't matter, you can use the sorted string/array to calculate a key. + +* if you only care about the offset of each value, you can use it as the key. + +* in a tree, you might want to directly use the `Node()` class as key sometimes, but in general, the serialization of the subtree might be a better idea. + +* in a matrix, you might want to use the row index or the column index as key. sometimes you might want to aggregate the values in the same diagonal line. + +
+ +--- + +### implementing a hash set + +
+ +* the difference between a hash set and a hash map is that the set can never have repeated elements. + +* to implement a hash set data structure, you need to implement: + - a hash function (to assign an address to store a given value), and + - a collision handling (since the nature of a hash function is to map a value from a space A to a corresponding smaller space B). + + +* overall, there are several strategies to resolve the collisions: + - separate chaining: for value with the same hash key, we keep them in a bucket, and each bucket is independent of each other. + - open addressing: whenever there is a collision, we keep on probing the main space with certain strategy until a free slot is found. + - 2-choices hashing: we use two hash functions rather than one, and pick the generated address with fewer collisions. + + +* if we were to implement separate chaining, the primary storage underneath a hashset is a continuous memory as array, where each element in this array corresponds to a bucket that stores the actual values. + * given a value, first we generate a key for the value via the hash function (the generated key serves as the index to locate the bucket). + * once the bucket is located, we then perform the desired operation on the bucket (such as add, remove, and contain). + * use a prime number as the base of the module to reduce collisions. + +
+ +```python +class HashSet: + + def __init__(self, size): + self.size = size + self.bucket = [Bucket() for _ in range(self.size)] + + def _get_hash_key(self, key): + return key % self.size + + def add(self, element: int): + bucket_index = self._get_hash_key(element) + self.bucket[bucket_index].insert(element) + + def remove(self, element: int)L + bucket_index = self._get_hash_key(element) + self.bucket[bucket_index].delete(element) + + def contains(self, element: int: + bucket_index = self._get_hash_key(element) + return self.bucket[bucket_index].exists(element) +```` + +
+ + +#### buckets as linked lists + +
+ +* a good choice for buckets are linked lists, as their time complexity for `insert` and `delete` is constant (once the position to be updated is located). + * you just need to be sure you never insert repeated elements. + +* time complexity for search is `O(N/K)` where `N` is the number of all possible values and `K` is the number of predefined buckets (the average size of bucket is `N/K`). + +* space complexity is `O(K+M)`, where `K` is the number of predefined buckets, and `M` is the number of unique values that have been inserted in the HashSet. + +* lastly, to optimize search, we could maintain the buckets as sorted lists (and obtain `O(log(N))` time complexity for the lookup operation). + * however, `insert` and `delete` are linear time (as elements would need to be shifted). + +
+ +```python +class Node: + def __init__(self, value=None, next=None): + self.value = value + self.next = next + + +class Bucket: + def __init__(self): + self.head = Node(0) + + def insert(self, value): + if not self.exists(value): + self.head.next = Node(value, self.head.next) + else: + print(f'node {value} already exists') + + def delete(self, value): + prev = self.head + current = self.head.next + while current is not None: + if current.value == value: + prev.next = current.next + return True + prev = current + current = current.next + return False + + def exists(self, value): + current = self.head.next + while current is not None: + if current.value == value: + return True + current = current.next + return False +``` + + +
+ +#### buckets as binary search trees + +
+ +* another option for a bucket is a binary search tree, with `O(log(N))` time complexity for search, insert, and delete. in addition, bst can not hold repeated elements, just like sets. + +* time complexity for search is `O(log(N/K)`, where `N` is the number of all possible values and `K` is the number of predefined buckets. + +* space complexity is `O(K+M)` where `K` is the number of predefined buckets, and `M` is the number of unique values in the hash set. + +
+ +```python +class Node: + def __init__(self, value=None): + self.val = value + self.left = None + self.right = None + + +class Bucket: + def __init__(self): + self.tree = BSTree() + + def insert(self, value): + self.tree.root = self.tree.insert(self.tree.root, value) + + def delete(self, value): + self.tree.root = self.tree.delete(self.tree.root, value) + + def exists(self, value): + return (self.tree.search(self.tree.root, value) is not None) + + +class BSTree(): + def __init__(self): + self.root = None + + def search(self, root, value) -> Node: + if root is None or value == root.val: + return root + + return self.search(root.left, value) if val < root.value \ + else self.search(root.right, value) + + def insert(self, root, value) -> Node: + if not root: + return Node(value) + + if value > root.val: + root.right = self.insert(root.right, value) + elif value == root.val: + return root + else: + root.left = self.insert(root.left, value) + + def successor(self, root): + # one step right and then all left + root = root.right + while root.left: + root = root.left + return root.value + + def predecessor(self, root): + # one step left and then always right + root = root.left + while root.right: + root = root.right + return root.value + + def delete(self, root, key) -> Node: + if not root: + return None + + if key > root.val: + root.right = self.delete(root.right, key) + + elif key < root.val: + root.left = self.delete(root.left, key) + + else: + if not (root.left or root.right): + root = None + elif root.right: + root.val = self.sucessor(root) + root.right = self.delete(root.right, root.val) + else: + root.val = self.predecessor(root) + root.left = self.delete(root.left, root.val) + + return root +``` + +
+ +---- + +### implementing a hash map + +
+ +* same as before, we need to tackle two main issues: hash function design and collision handling. + +* a good approach is using a module function with an array or linked list. at this time, there is no constraint for repeated numbers. + +
+ +```python +class Bucket: + + def __init__(self): + self.bucket = [] + + def get(self, key): + for (k, v) in self.bucket: + if k == key: + return v + return -1 + + def put(self, key, value): + found = False + for i, k in enumerate(self.bucket): + if key == k[0]: + self.bucket[i] = (key, value) + found = True + break + if not found: + self.bucket.append((key, value)) + + def remove(self, key): + for i, k in enumerate(self.bucket): + if key == k[0]: + del self.bucket[i] +``` +
+ +* del is an `O(N)` operation, as we would copy all the `i` elements. to make it `O(1)` we could swap the element we want to remove with the last element in the bucket. + diff --git a/hash_objects/hash_map_array.py b/hash_objects/hash_map_array.py new file mode 100644 index 0000000..6508815 --- /dev/null +++ b/hash_objects/hash_map_array.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +class Bucket: + + def __init__(self): + self.bucket = [] + + def get(self, key): + for (k, v) in self.bucket: + if k == key: + return v + return -1 + + def put(self, key, value): + found = False + for i, k in enumerate(self.bucket): + if key == k[0]: + self.bucket[i] = (key, value) + found = True + break + if not found: + self.bucket.append((key, value)) + + def remove(self, key): + for i, k in enumerate(self.bucket): + if key == k[0]: + # del is an O(N) operation, as we would copy all the i: elements + # to make it O(1) we could swap the element we want to remove + # with the last element in the bucket + del self.bucket[i] + + +class HashMap: + + def __init__(self, size): + self.size = size + self.table = [Bucket() for _ in range(self.size)] + + def _get_hash_key(self, key): + return key % self.size + + def put(self, key: int, value: int): + hash_key = self._get_hash_key(key) + self.table[hash_key].put(key, value) + + def get(self, key: int): + hash_key = self._get_hash_key(key) + return self.table[hash_key].get(key) + + def remove(self, key: int): + hash_key = self._get_hash_key(key) + self.table[hash_key].remove(key) + + + diff --git a/hash_objects/hash_set_bst.py b/hash_objects/hash_set_bst.py new file mode 100644 index 0000000..08cb8b4 --- /dev/null +++ b/hash_objects/hash_set_bst.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +class HashSet: + + def __init__(self, size): + self.size = size + self.bucket = [Bucket() for _ in range(self.size)] + + def _get_hash_key(self, key): + return key % self.size + + def add(self, element: int) -> None: + bucket_index = self._get_hash_key(element) + self.bucket[bucket_index].insert(element) + + def remove(self, element: int) -> None: + bucket_index = self._get_hash_key(element) + self.bucket[bucket_index].delete(element) + + def contains(self, element: int) -> bool: + bucket_index = self._get_hash_key(element) + return self.bucket[bucket_index].exists(element) + + +class Node: + def __init__(self, value=None): + self.val = value + self.left = None + self.right = None + + +class Bucket: + def __init__(self): + self.tree = BSTree() + + def insert(self, value): + self.tree.root = self.tree.insert(self.tree.root, value) + + def delete(self, value): + self.tree.root = self.tree.delete(self.tree.root, value) + + def exists(self, value): + return (self.tree.search(self.tree.root, value) is not None) + + +class BSTree(): + def __init__(self): + self.root = None + + def search(self, root, value) -> Node: + if root is None or value == root.val: + return root + + return self.search(root.left, value) if val < root.value \ + else self.search(root.right, value) + + def insert(self, root, value) -> Node: + if not root: + return Node(value) + + if value > root.val: + root.right = self.insert(root.right, value) + elif value == root.val: + return root + else: + root.left = self.insert(root.left, value) + + def successor(self, root): + # one step right and then all left + root = root.right + while root.left: + root = root.left + return root.value + + def predecessor(self, root): + # one step left and then always right + root = root.left + while root.right: + root = root.right + return root.value + + def delete(self, root, key) -> Node: + if not root: + return None + + if key > root.val: + root.right = self.delete(root.right, key) + + elif key < root.val: + root.left = self.delete(root.left, key) + + else: + if not (root.left or root.right): + root = None + elif root.right: + root.val = self.sucessor(root) + root.right = self.delete(root.right, root.val) + else: + root.val = self.predecessor(root) + root.left = self.delete(root.left, root.val) + + return root + diff --git a/hash_objects/hash_set_linked_list.py b/hash_objects/hash_set_linked_list.py new file mode 100644 index 0000000..823bc0d --- /dev/null +++ b/hash_objects/hash_set_linked_list.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +class HashSet: + + def __init__(self, size): + self.size = size + self.bucket = [Bucket() for _ in range(self.size)] + + def _get_hash_key(self, key): + return key % self.size + + def add(self, element: int) -> None: + bucket_index = self._get_hash_key(element) + self.bucket[bucket_index].insert(element) + + def remove(self, element: int) -> None: + bucket_index = self._get_hash_key(element) + self.bucket[bucket_index].delete(element) + + def contains(self, element: int) -> bool: + bucket_index = self._get_hash_key(element) + return self.bucket[bucket_index].exists(element) + + +class Node: + def __init__(self, value=None, next=None): + self.value = value + self.next = next + + +class Bucket: + def __init__(self): + self.head = Node(0) + + def insert(self, value): + if not self.exists(value): + self.head.next = Node(value, self.head.next) + else: + print(f'node {value} already exists') + + def delete(self, value): + prev = self.head + current = self.head.next + while current is not None: + if current.value == value: + prev.next = current.next + return True + prev = current + current = current.next + return False + + def exists(self, value): + current = self.head.next + while current is not None: + if current.value == value: + return True + current = current.next + return False + diff --git a/heaps/README.md b/heaps/README.md new file mode 100644 index 0000000..63106d7 --- /dev/null +++ b/heaps/README.md @@ -0,0 +1,518 @@ +## heaps + +
+ +

+ +

+ + + +
+ +* heap is a data structure capable of giving you the smallest (or the largest) element in constant time, while adding or removing the smallest (or the largest) element in logarithmic time. + +* a heap is like a binary tree with these properties: + * it must have all of **its nodes in a specific order**, and + * its shape must be **complete** (all the levels of the tree must be completely filled except maybe for the last one and the last level must have the left-most nodes filled, always). + * a max heap's **root node must** have all its children either **greater than or equal** to its children. + * a min heap is the opposite. duplicate values are allowed. + +* heaps can be represented with linked lists, queues (arrays), or binary trees. + +* in the case of an array heap: + * the parent node index is given by `n / 2` + * the left children index is `2 * n` + * the right children index is `2 * n + 1` + * a node is a leaf when its index `> n / 2` + +* common applications of heap are: sort (heap sort), getting the top-k elements, and finding the kth element. + +* in python you can use `heapq.heapify(array)` with `heapq.heappush(array, value)` and `heapq.heappop()`. + +
+ +```python +class Heap: + + def __init__(self): + + self.heap = [] + + def heapify(self, n, i): + + largest = i + left_children = 2 * i + 1 + right_children = 2 * i + 2 + + if left_children < n and self.heap[i] < self.heap[left_children]: + largest = left_children + + if right_children < n and self.heap[largest] < self.heap[right_children]: + largest = right_children + + if largest != i: + self.heap[i], self.heap[largest] = self.heap[largest], self.heap[i] + self.heapify(n, largest) + + + def insert(self, num): + + size = len(self.heap) + + if size == 0: + self.heap.append(num) + + else: + self.heap.append(num) + for i in range((size // 2) - 1, -1, -1): + self.heapify(size, i) + + + def delete_node(self, num): + + size = len(self.heap) + + i = 0 + for i in range(size): + if num == self.heap[i]: + break + + self.heap[i], self.heap[size - 1] = self.heap[size - 1], self.heap[i] + + self.heap.remove(size - 1) + + for i in range((len(self.heap) // 2) - 1, -1, -1): + self.heapify(len(self.heap), i) +``` + +
+ +--- + +### heapify + +
+ +* it's cheaper to heapify an array of data (`O(N)`) than create an empty heap and inserting each element (`O(N log(N))`). + * heapify means create a binary tree and then comparing each nodes with their children (and swapping when necessary). + * parents node can simply exchange with their smallest child (so the max number of exchanges is `N/2`) and leaves are left out. + +* python's built-in heap differs from the standard implementation of a heap in two ways: + * firstly, it uses zero-based indexing, so it stores the root node at index zero instead of the size of the heap. + * secondly, the built-in module does not offer a direct way to create a max heap, instead, we must modify the values of each element when inserting in the heap, and when removing it from the heap. + +
+ +```python +import heapq + +min_heap = [3,1,2] +heapq.heapify(min_heap) + +max_heap = [-x for x in min_heap] +heapq.heapify(max_heap) + +heapq.heappush(min_heap, 5) +heapq.heappush(min_heap, -5) + +min_elem = min_heap[0] +max_elem = -1 * max_heap[0 + +heapq.heappop(min_heap) +heapq.heappop(max_heap) + +size_min_heap = len(min_heap) +size_max_heap = len(max_heap) +``` + +
+ +---- + +### priority queues + +
+ +* a priority queue is an abstract data type with the following properties: + 1. every item has a priority (usually an integer). + 2. an item with a high priority is dequeued before an item with low priority. + 3. two items with an equal priority are dequeued based on their order in the queue. + +* priority queues can be implemented with a stack, queue, or linked list data structures. + +
+ +--- + +### min heaps + +
+ +* a **min heap** is a complete binary tree where each node is smaller than its children (the root is the min element). + +* `insert`: + - insert the element at the bottom, by finding the most rightmost node and checking its children: if left is empty, insert there, otherwise, insert on right. + - then compare this node to each parent, exchanging them until the tree's properties are correct. + +* `extract_min`: + - first, remove/return the top and then replace the tree's top with its latest element (the bottom-most rightmost). + - then bubble down, swapping it with one of its children until the min-heap is properly restored + - there is no need for order between right and left, so this operation would only take `O(log N)` runtime. + +* the code below is an example of an ad-hoc heap class in python. + +
+ +```python +class MinHeap: + + def __init__(self, size): + + self.heapsize = size + self.minheap = [0] * (size + 1) + self.realsize = 0 + + def add(self, element): + + if self.realsize + 1 > self.heapsize: + print("Too many elements!") + return False + + self.realsize += 1 + self.minheap[self.realsize] = element + + index = self.realsize + parent = index // 2 + + while self.minheap[index] < self.minheap[parent] and index > 1: + + self.minheap[parent], self.minheap[index] = self.minheap[index], self.minheap[parent] + index = parent + parent = index // 2 + + def peek(self): + + return self.minheap[1] + + def pop(self): + + if self.realsize < 1: + print("Heap is empty.") + return False + + else: + remove_element = self.minheap[1] + self.minheap[1] = self.minheap[self.realsize] + self.realsize -= 1 + index = 1 + + while index <= self.realsize // 2: + + left_children = index * 2 + right_children = (index * 2) + 1 + + if self.minheap[index] > self.minheap[left_children] or \ + self.minheap[index] > self.minheap[right_children]: + + if self.minheap[left_children] < self.minheap[right_children]: + + self.minheap[left_children], self.minheap[index] = self.minheap[index], self.minheap[left_children] + index = left_children + + else: + + self.minheap[right_children], self.minheap[index] = self.minheap[index], self.minheap[right_children] + index = right_children + else: + break + + return remove_element + + def size(self): + return self.realsize + + def __str__(self): + return str(self.minheap[1 : self.realsize + 1]) +``` + +
+ +--- + +### max heaps + +
+ +* a **max heap** is a complete binary tree where each node is larger than its children (the root is the max element). + +* `insert`: + * insert the element at the bottom, at the leftmost node. + * then compare the node to each parent, exchanging them until the tree's properties are correct. + +* `extract_max`: + * remove/return the top and then replace the tree's top with its bottom rightmost element. + * swap up until the max element is on the top. + +* the code below is an example of a max heap class built in python: + +
+ +```python +class MaxHeap: + def __init__(self, heapsize): + + self.heapsize = heapsize + self.maxheap = [0] * (heapsize + 1) + self.realsize = 0 + + def add(self, element): + + self.realsize += 1 + if self.realsize > self.heapsize: + print("Too many elements!") + self.realsize -= 1 + return False + + self.maxheap[self.realsize] = element + index = self.realsize + parent = index // 2 + + while self.maxheap[index] > self.maxheap[parent] and index > 1: + self.maxheap[parent], self.maxheap[index] = self.maxheap[index], self.maxheap[parent] + index = parent + parent = index // 2 + + def peek(self): + + return self.maxheap[1] + + def pop(self): + + if self.realsize < 1: + print("Heap is empty.") + return False + else: + remove_element = self.maxheap[1] + self.maxheap[1] = self.maxheap[self.realsize] + self.realsize -= 1 + index = 1 + + while (index <= self.realsize // 2): + + left_children = index * 2 + right_children = (index * 2) + 1 + + if (self.maxheap[index] < self.maxheap[left_children] or self.maxheap[index] < self.maxheap[right_children]): + if self.maxheap[left_children] > self.maxheap[right_children]: + self.maxheap[left_children], self.maxheap[index] = self.maxheap[index], self.maxheap[left_children] + index = left_children + else: + self.maxheap[right_children], self.maxheap[index] = self.maxheap[index], self.maxheap[right_children] + index = right_children + else: + break + return remove_element + + def size(self): + return self.realsize + + def __str__(self): + return str(self.maxheap[1 : self.realsize + 1]) +``` + +
+ +--- + + +### heap sort + +
+ + +* the core concept of the heap sort involves constructing a heap from our input and then repeatedly removing the min/max element to sort the array. + +* the key idea for in-place heap sort involves a balance of these two ideas: + - building a heap from an unsorted array through a "bottom-up heapification" process, and + - using the heap to sort the input array + +* heapsort traditionally uses a max-heap to sort the array, although a min-heap also works. + +* this is not a stable sort. + +
+ +```python +def heap_sort(self, array) -> None: + + def max_heapify(heap_size, index): + + left, right = 2 * index + 1, 2 * index + 2 + largest = index + + if left < heap_size and array[left] > array[largest]: + largest = left + elif if right < heap_size and array[right] > array[largest]: + largest = right + elif largest != index: + array[index], array[largest] = array[largest], array[index] + max_heapify(heap_size, largest) + + for i in range(len(lst) // 2 - 1, -1, -1): + max_heapify(len(array), i) + + for i in range(len(array) - 1, 0, -1): + array[i], array[0] = array[0], array[i] + max_heapify(i, 0) + + return array +``` + +
+ +---- + +### compare two tops + + +
+ +```python +def compare_two_tops(array) -> int: + + for i in range(len(array)): + array[i] *= -1 + + heapq.heapify(array) + + while len(array) > 1: + + val1 = heapq.heappop(array) + val2 = heapq.heappop(array) + + if val1 != val2: + heapq.heappush(array, val1 - val2) + + if array: + return -heapq.heappop(array) + + return 0 +``` + +
+ +---- + +### find non-overlapping intervals + +
+ +* given an array of `intervals[i] = [start_i, end_i]`, return the minimum the non-overlapping intervals: + +
+ +```python +def non_overlapping_invervals(intervals): + + if not intervals: + return 0 + + result = [] + intervals.sort(key=lambda x: x[0]) + heapq.heappush(result, intervals[0][-1]) + + for interval in intervals[1:]: + + if result[0] <= interval[0]: + heapq.heappop(result) + + heapq.heappush(result, interval[1]) + + return len(result) +``` + +
+ +---- + +### top k frequent values + +
+ +```python +def top_k_frequent_values(list, k): + + if k == len(nums): + return nums + + # hashmap element: frequency + counter = Counter(nums) + return heapq.nlargest(k, counter.keys(), key=counter.get) +``` + +
+ +---- + +### kth largest element in a stream + +
+ +```python +class KthLargest: + + def __init__(self, k, nums): + + self.k = k + self.heap = nums + heapq.heapify(self.heap) + + while len(self.heap) > k: + heapq.heappop(self.heap) + + def add(self, val: int) -> int: + + heapq.heappush(self.heap, val) + if len(self.heap) > self.k: + heapq.heappop(self.heap) + + return self.heap[0] +``` + +
+ +---- + +### kth smallest element in a matrix + +
+ +* given an `n x n` matrix where each of the rows and columns is sorted in ascending order, return the `kth` smallest element in the matrix. + +
+ +```python +def kth_smallest(matrix, k) -> int: + + min_heap = [] + + for row in range(min(k, len(matrix))): + min_heap.append((matrix[row][0], row, 0)) + + heapq.heapify(min_heap) + + while k: + + element, row, col = heapq.heappop(min_heap) + if col < len(matrix) - 1: + heapq.heappush(min_heap, (matrix[row][cow + 1], row, col + 1)) + k -= 1 + + return element +``` + +
+ + + diff --git a/heaps/compare_two_tops.py b/heaps/compare_two_tops.py new file mode 100644 index 0000000..2aedb6a --- /dev/null +++ b/heaps/compare_two_tops.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def compare_two_tops(array) -> int: + + for i in range(len(array)): + array[i] *= -1 + + heapq.heapify(array) + + while len(array) > 1: + + val1 = heapq.heappop(array) + val2 = heapq.heappop(array) + + if val1 != val2: + heapq.heappush(array, val1 - val2) + + if array: + return -heapq.heappop(array) + + return 0 + diff --git a/heaps/heap.py b/heaps/heap.py new file mode 100644 index 0000000..4b376f4 --- /dev/null +++ b/heaps/heap.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +class Heap: + + def __init__(self): + + self.heap = [] + + def heapify(self, n, i): + + largest = i + left_children = 2 * i + 1 + right_children = 2 * i + 2 + + if left_children < n and self.heap[i] < self.heap[left_children]: + largest = left_children + + if right_children < n and self.heap[largest] < self.heap[right_children]: + largest = right_children + + if largest != i: + self.heap[i], self.heap[largest] = self.heap[largest], self.heap[i] + self.heapify(n, largest) + + + def insert(self, num): + + size = len(self.heap) + + if size == 0: + self.heap.append(num) + + else: + self.heap.append(num) + for i in range((size // 2) - 1, -1, -1): + self.heapify(size, i) + + + def delete_node(self, num): + + size = len(self.heap) + + i = 0 + for i in range(size): + if num == self.heap[i]: + break + + self.heap[i], self.heap[size - 1] = self.heap[size - 1], self.heap[i] + + self.heap.remove(size - 1) + + for i in range((len(self.heap) // 2) - 1, -1, -1): + self.heapify(len(self.heap), i) + + + +if __name__ == '__main__': + + h = Heap() + for n in [10, 4, 9, 8, 1, 2]: + h.insert(n) + + print (f'Max-heap array: {h.heap}') + + n = 2 + h.delete_node(n) + print(f'After deleting {n}: {h.heap}') diff --git a/heaps/heap_sort.py b/heaps/heap_sort.py new file mode 100644 index 0000000..5e4bd58 --- /dev/null +++ b/heaps/heap_sort.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def heap_sort(self, array) -> list: + + def max_heapify(heap_size, index): + + left, right = 2 * index + 1, 2 * index + 2 + largest = index + + if left < heap_size and array[left] > array[largest]: + largest = left + elif if right < heap_size and array[right] > array[largest]: + largest = right + elif largest != index: + array[index], array[largest] = array[largest], array[index] + max_heapify(heap_size, largest) + + for i in range(len(lst) // 2 - 1, -1, -1): + max_heapify(len(array), i) + + for i in range(len(array) - 1, 0, -1): + array[i], array[0] = array[0], array[i] + max_heapify(i, 0) + + return array diff --git a/heaps/heapify.py b/heaps/heapify.py new file mode 100644 index 0000000..b9cafde --- /dev/null +++ b/heaps/heapify.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +import heapq + +min_heap = [3,1,2] +heapq.heapify(min_heap) + +max_heap = [-x for x in min_heap] +heapq.heapify(max_heap) + +heapq.heappush(min_heap, 5) +heapq.heappush(min_heap, -5) + +min_elem = min_heap[0] +max_elem = -1 * max_heap[0 + +heapq.heappop(min_heap) +heapq.heappop(max_heap) + +size_min_heap = len(min_heap) +size_max_heap = len(max_heap) diff --git a/heaps/k_smallest_matrix.py b/heaps/k_smallest_matrix.py new file mode 100644 index 0000000..79789cf --- /dev/null +++ b/heaps/k_smallest_matrix.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + + +def kth_smallest(matrix, k) -> int: + + min_heap = [] + + for row in range(min(k, len(matrix))): + min_heap.append((matrix[row][0], row, 0)) + + heapq.heapify(min_heap) + + while k: + + element, row, col = heapq.heappop(min_heap) + if col < len(matrix) - 1: + heapq.heappush(min_heap, (matrix[row][cow + 1], row, col + 1)) + k -= 1 + + return element diff --git a/heaps/kth_largest_stream.py b/heaps/kth_largest_stream.py new file mode 100644 index 0000000..8a007fd --- /dev/null +++ b/heaps/kth_largest_stream.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +class KthLargest: + + def __init__(self, k, nums): + + self.k = k + self.heap = nums + heapq.heapify(self.heap) + + while len(self.heap) > k: + heapq.heappop(self.heap) + + def add(self, val: int) -> int: + + heapq.heappush(self.heap, val) + if len(self.heap) > self.k: + heapq.heappop(self.heap) + + return self.heap[0] + diff --git a/heaps/max_heap.py b/heaps/max_heap.py new file mode 100644 index 0000000..5d831d2 --- /dev/null +++ b/heaps/max_heap.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +class MaxHeap: + def __init__(self, heapsize): + + self.heapsize = heapsize + self.maxheap = [0] * (heapsize + 1) + self.realsize = 0 + + def add(self, element): + + self.realsize += 1 + if self.realsize > self.heapsize: + print("Too many elements!") + self.realsize -= 1 + return False + + self.maxheap[self.realsize] = element + index = self.realsize + parent = index // 2 + + while self.maxheap[index] > self.maxheap[parent] and index > 1: + self.maxheap[parent], self.maxheap[index] = self.maxheap[index], self.maxheap[parent] + index = parent + parent = index // 2 + + def peek(self): + + return self.maxheap[1] + + def pop(self): + + if self.realsize < 1: + print("Heap is empty.") + return False + else: + remove_element = self.maxheap[1] + self.maxheap[1] = self.maxheap[self.realsize] + self.realsize -= 1 + index = 1 + + while (index <= self.realsize // 2): + + left_children = index * 2 + right_children = (index * 2) + 1 + + if (self.maxheap[index] < self.maxheap[left_children] or self.maxheap[index] < self.maxheap[right_children]): + if self.maxheap[left_children] > self.maxheap[right_children]: + self.maxheap[left_children], self.maxheap[index] = self.maxheap[index], self.maxheap[left_children] + index = left_children + else: + self.maxheap[right_children], self.maxheap[index] = self.maxheap[index], self.maxheap[right_children] + index = right_children + else: + break + return remove_element + + def size(self): + return self.realsize + + def __str__(self): + return str(self.maxheap[1 : self.realsize + 1]) + + +if __name__ == "__main__": + + h = MaxHeap(5) + h.add(1) + h.add(2) + h.add(3) + print(h) + + print(h.peek()) + print(h.pop()) + print(h.pop()) + print(h.pop()) + h.add(4) + h.add(5) + print(h) + diff --git a/heaps/min_heap.py b/heaps/min_heap.py new file mode 100644 index 0000000..f0c8b00 --- /dev/null +++ b/heaps/min_heap.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +class MinHeap: + + def __init__(self, size): + + self.heapsize = size + self.minheap = [0] * (size + 1) + self.realsize = 0 + + def add(self, element): + + if self.realsize + 1 > self.heapsize: + print("Too many elements!") + return False + + self.realsize += 1 + self.minheap[self.realsize] = element + + index = self.realsize + parent = index // 2 + + while self.minheap[index] < self.minheap[parent] and index > 1: + + self.minheap[parent], self.minheap[index] = self.minheap[index], self.minheap[parent] + index = parent + parent = index // 2 + + def peek(self): + + return self.minheap[1] + + def pop(self): + + if self.realsize < 1: + print("Heap is empty.") + return False + + else: + remove_element = self.minheap[1] + self.minheap[1] = self.minheap[self.realsize] + self.realsize -= 1 + index = 1 + + while index <= self.realsize // 2: + + left_children = index * 2 + right_children = (index * 2) + 1 + + if self.minheap[index] > self.minheap[left_children] or \ + self.minheap[index] > self.minheap[right_children]: + + if self.minheap[left_children] < self.minheap[right_children]: + + self.minheap[left_children], self.minheap[index] = self.minheap[index], self.minheap[left_children] + index = left_children + + else: + + self.minheap[right_children], self.minheap[index] = self.minheap[index], self.minheap[right_children] + index = right_children + else: + break + + return remove_element + + def size(self): + return self.realsize + + def __str__(self): + return str(self.minheap[1 : self.realsize + 1]) + + +if __name__ == "__main__": + # Test cases + h = MinHeap(5) + h.add(3) + h.add(1) + h.add(2) + + print(h) + print(h.peek()) + print(h.pop()) + print(h.pop()) + print(h.pop()) + h.add(4) + h.add(5) + print(h) diff --git a/heaps/non_overlapping_invervals.py b/heaps/non_overlapping_invervals.py new file mode 100644 index 0000000..0f5f04e --- /dev/null +++ b/heaps/non_overlapping_invervals.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def non_overlapping_invervals(intervals): + + if not intervals: + return 0 + + result = [] + intervals.sort(key=lambda x: x[0]) + heapq.heappush(result, intervals[0][-1]) + + for interval in intervals[1:]: + + if result[0] <= interval[0]: + heapq.heappop(result) + + heapq.heappush(result, interval[1]) + + return len(result) diff --git a/heaps/top_k_frequent.py b/heaps/top_k_frequent.py new file mode 100644 index 0000000..3918f50 --- /dev/null +++ b/heaps/top_k_frequent.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def top_k_frequent_values(list, k): + + if k == len(nums): + return nums + + # hashmap element: frequency + counter = Counter(nums) + return heapq.nlargest(k, counter.keys(), key=counter.get) diff --git a/linked_lists/README.md b/linked_lists/README.md new file mode 100644 index 0000000..baa9bee --- /dev/null +++ b/linked_lists/README.md @@ -0,0 +1,267 @@ +## linked list + +
+ +* like arrays, a linked list is used to represent sequential data. it's a linear collection of data elements (nodes) whose order is not given by their physical placement in memory (as opposed to arrays where data is stored in sequential blocks of memory). instead, each element contains an address of the next element. + +
+ +```python +class Node: + + def __init__(self, val, next=None): + self.val = val + self.next = next +``` + +
+ +* unlike an array, a linked list does not provide constant time access to an index (as it needs to interact through all `k` elements), however addition and removal of elements are constant time (`O(1)`). + * if you need to add or delete a node frequently, a linked list could be a good choice. + * if you need to access an element by index often, an array might be a better choice than a linked list. + +* linked lists can be of the following types: + * **singled linked list**: a linked list where each node points to the next node and the last node points to `None`. + * **doubly linked list**: a linked list where each node has two pointers, `next` and `prev`. the `prev` pointer of the first node and the `next` pointer of the last node point to `None`. + * **circular linked list**: a singly linked list where the past node points back to the first node. if it's doubly, the `prev` pointer of the first node points to the last node, and the `next` pointer of the last node points to the first node. + +* each node in a singly-linked list contains a value and a reference field to link to the next node. the head node (first node) usually represents the whole list. + * nodes can be added at the beginning, head needs to be updated (`current -> head` and `head = current`). + * to remove a node you set `prev.next` equal to `node.next`. if it's a double list, you also update `node.next` with `node.next.prev` to `node.prev` (and deallocate the memory). + +* adding a sentinel/dummy node at the head and/or tail might help handle many edge cases where operations have to be performed at the head or the tail. + * the presence of dummy nodes ensures that operations will never be done on the head or the tail (removing the need of conditional checks to deal with `None` pointers). the only extra steps is that they need to be removed at the end of the operation. + * examples are LRU cache (where sentinel nodes are used as pseudo-head and pseudo-tail) and tree level order traversal (where sentinel nodes are used to mark level end). + +* two pointers can be used to solve several problems: + * getting the kth from last node: have two pointers, where one is `k` nodes ahead of the other, when the node ahead reaches the end, the other node is `k` behind. + * detecting cycles: have two pointers, where one pointer increments twice as much as the other. if the two pointers meet, there is a cycle. if there is no cycle, the faster pointer takes `N/2` to reach the end of the list (`N` being the length). + * getting in the middle node: have two pointers, where one pointer increments twice as much as the other. when the faster node reaches the end of the list, the slower node will be at the middle. + + +
+ +

+ +

+ + +
+ + +---- + +### detecting cycles + +
+ +```python +def has_cycle(head) -> bool: + + if not head: + return False + + p1 = head + p2 = head.next + + while p1 != p2: + + if not p1 or not p2 or not p2.next: + return False + + p1 = p1.next + p2 = p2.next.next + + return True +``` + +
+ +---- + +### reversing the list + +
+ +* keep track of the original head node and the new head node (for instance, with two pointers). + +
+ +```python +def reverse_list(head): + + if head is None: + return head + + prev = None + curr = head + + while curr: + next_temp = curr.next // save the pointer for the next node so we can continue the loop + curr.next = prev // revert the list + prev = curr // save for the next node revert + curr = next_temp // receive the pointer for the next node so we can continue the loop + + return prev +``` + + +
+ +--- + +### removing elements + +
+ +* given a head of a linked list and a value, how to remove all the nodes of the list that have that value? + +
+ +```python +def delete_node_without_head(node): + + node.val = node.next.val + node.next = node.next.next +``` +
+ +* this problem is easy if one has to delete a node in the middle, as all you need to do is loop until the predecessor node and change the pointers. + +* however, if the node to be deleted is in the head of the list, the best way is to use a sentinel node. sentinel nodes are widely used in trees and linked lists as pseudo-heads, pseudo-tails, markers of level end, etc. they are purely functional and usually do not hold any data. their main purpose is to standardize the process (by making the list never empty or headless). + + +
+ +```python +def remove_elements(head, val): + + sentinel = ListNode(0) + sentinel.next = head + prev, node = sentinel, head + + while node: + if node.val == val: + prev.next = node.next + else: + prev = node + node = node.next + + return sentinel.next +``` + +
+ + +--- + +### remove kth node + +
+ +```python +def remove_kth_node(self, head, n): + + if head is None or head.next is None: + return None + + # find the length of the list + node, length = head, 0 + while node: + node = node.next + length += 1 + + # if n is the entire list, remove head + if n == length: + return head.next + + # loop to kth element + node, i = head, 0 + while i <= length - n: + node = node.next + i += 1 + + # remove the kth element + node.next = node.next.next + + return head +``` + +
+ +---- + +### doubly linked lists + +
+ +* in doubly linked lists, a node has a `previous` field. + +* when it comes to add and delete operations, extra attention is needed when you want to delete or insert at the beginning or at the end of the list. + + + +
+ +---- + +### swap every two nodes + +
+ +```python +def swap_pairs(head): + + if not head or not head.next: + return head + + first_node = head + second_node = head.next + + first_node.next = swap_pairs(second_node.next) + second_node.next = first_node + + return second_node +``` + +
+ +---- + + +### rotate list by k + +
+ +* the nodes in the list are already linked, so the rotation means: + * to close the linked list in the ring + * to break the ring after the new tail and in front of the new head + +* the new head will be at `n - k`, and the new tail will be at `n - k - 1` (found with `n - k % n - 1`). + +
+ +```python3 +def rotate_list_by_k(head, k): + + if head is None: + return head + + # get the size of the list + end, n = head, 1 + while end.next: + end = end.next + n += 1 + + # rotate + end.next = head + new_end, i = head, 0 + while i < n - (k % n) - 1: + new_end = new_end.next + i += 1 + + # remove cycle + new_head = new_end.next + + return new_head +``` diff --git a/linked_lists/add_two_numbers.py b/linked_lists/add_two_numbers.py new file mode 100644 index 0000000..ae53ca7 --- /dev/null +++ b/linked_lists/add_two_numbers.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +class Node: + + def __init__(self, val): + self.val = val + self.next = None + + +def add_two_numbers(l1, l2): + + n1, n2, i = '', '', 1 + + while l1: + n1 += str(l1.val) + l1 = l1.next + + while l2: + n2 += str(l2.val) + l2 = l2.next + + n1 = int(n1[::-1]) + n2 = int(n2[::-1]) + n = str(n1 + n2)[::-1] + + current = Node(n[0]) + head = current + + while i < len(n): + current.next = Node(n[i]) + current = current.next + i += 1 + + return head + diff --git a/linked_lists/delete_node_without_head.py b/linked_lists/delete_node_without_head.py new file mode 100644 index 0000000..62509d0 --- /dev/null +++ b/linked_lists/delete_node_without_head.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def delete_node_without_head(node): + + node.val = node.next.val + node.next = node.next.next + diff --git a/linked_lists/detect_cycle.py b/linked_lists/detect_cycle.py new file mode 100644 index 0000000..93475eb --- /dev/null +++ b/linked_lists/detect_cycle.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +class Node: + + def __init__(self, val): + self.val = val + self.next = None + + +def has_cycle(head) -> bool: + + if not head: + return False + + p1 = head + p2 = head.next + + while p1 != p2: + + if not p1 or not p2 or not p2.next: + return False + + p1 = p1.next + p2 = p2.next.next + + return True + diff --git a/linked_lists/detect_cycle_II.py b/linked_lists/detect_cycle_II.py new file mode 100644 index 0000000..6cb6de7 --- /dev/null +++ b/linked_lists/detect_cycle_II.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +class Node: + def __init__(self, x): + self.val = x + self.next = None + + +def detect_cycle(head): + + seen = set() + node = head + + while node is not None: + + if node in seen: + return node + + else: + seen.add(node) + node = node.next + + return None + diff --git a/linked_lists/doubly_linked_list.py b/linked_lists/doubly_linked_list.py new file mode 100644 index 0000000..51f4ab6 --- /dev/null +++ b/linked_lists/doubly_linked_list.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +class Node: + + def __init__(self, data, previous=None): + self.data = data + self.next = None + self.previous = previous + + self.address = self + + def insert(self, data): + + while self.next: + self = self.next + node = Node(data, previous=self) + + if self.next is None: + node = Node(data, previous=self) + + self.next = node + + + +if __name__ == "__main__": + + ll = Node(0) + for n in range(1, 10): + ll.insert(n) + + while ll.next: + + this_data = ll.data + this_address = ll.address + + if ll.next is None: + this_next_data = None + this_next_address = None + else: + this_next_data = ll.next.data + this_next_address = ll.next.address + + if ll.previous is None: + this_previous_data = None + this_previous_address = None + else: + this_previous_data = ll.previous.data + this_previous_address = ll.previous.address + + + print(f'This node -> data: {this_data}, address: {this_address} | Previous node -> data: {this_previous_data}, address: {this_previous_address} | Next node -> data: {this_next_data}, address: {this_next_address}') + ll = ll.next diff --git a/linked_lists/doubly_linked_list_II.py b/linked_lists/doubly_linked_list_II.py new file mode 100644 index 0000000..827a5c0 --- /dev/null +++ b/linked_lists/doubly_linked_list_II.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +class Node: + + def __init__(self, val): + self.val = val + self.next = None + self.prev = None + +class DoublyList: + + def __init__(self): + self.head = Node(0) + self.len = 0 + + def _loop(self , index): + node = self.head + p = 0 + while p < index + 1: + node = node.next + p += 1 + + return node + + def get(self, index: int) -> int: + if self.len <= index or index < 0: + return -1 + node = self._loop(index) + return node.val + + def add_at_head(self, val: int) -> None: + self.add_at_index(0, val) + + def add_at_tail(self, val: int) -> None: + self.add_at_index(self.len, val) + + def add_at_index(self, index: int, val: int) -> None: + if self.len < index: + return -1 + + if index < 0: + index = 0 + + self.len += 1 + + node = self.head + for _ in range(index): + node = node.next + + new_node = Node(val) + new_node.next = node.next + new_node.prev = node + node.next = new_node + + def delete_at_index(self, index: int) -> None: + + if self.len <= index or index < 0: + return -1 + + self.len -= 1 + + node = self.head + for _ in range(index): + node = node.next + + node.next.prev = node + node.next = node.next.next diff --git a/linked_lists/finding_intersection.py b/linked_lists/finding_intersection.py new file mode 100644 index 0000000..24e8155 --- /dev/null +++ b/linked_lists/finding_intersection.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +class Node: + def __init__(self, val): + self.val = val + self.next = None + + +def get_intersection_node(self, head_a: Node, head_b: Node) -> Optional[Node]: + + seen_b = set() + + while head_b is not None: + + seen_b.add(head_b) + head_b = head_b.next + + while head_a is not None: + + if head_a in seen_b: + return head_a + + head_a = head_a.next + diff --git a/linked_lists/flatten_list.py b/linked_lists/flatten_list.py new file mode 100644 index 0000000..b4e7900 --- /dev/null +++ b/linked_lists/flatten_list.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + + +class Node: + def __init__(self, val, prev, next, child): + self.val = val + self.prev = prev + self.next = next + self.child = child + + +def dfs(prev, node): + + if not node: + return prev + + node.prev = prev + prev.next = node + temp_next = node.next + + last = dfs(node, node.child) + node.child = None + + return dfs(last, temp_next) + + +def flatten(head): + + if head is None: + return head + + sentinel = Node(None, None, head, None) + + dfs(prev=sentinel, node=head) + + # erase the pointer to sentinel and return + sentinel.next.prev = None + return sentinel.next diff --git a/linked_lists/group_odd_and_even.py b/linked_lists/group_odd_and_even.py new file mode 100644 index 0000000..30542c9 --- /dev/null +++ b/linked_lists/group_odd_and_even.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +class Node: + + def __init__(self, val): + self.val = val + self.next = None + + +def group_odd_and_even(head): + + if not head: + return None + + odd = head + even = odd.next + even_head = even + + while even is not None and even.next is not None: + + odd.next = even.next + odd = odd.next + even.next = odd.next + even = even.next + + odd.next = even_head + + return head + diff --git a/linked_lists/linked_list_II.py b/linked_lists/linked_list_II.py new file mode 100644 index 0000000..2ea6c25 --- /dev/null +++ b/linked_lists/linked_list_II.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +class Node: + + def __init__(self, val): + + self.val = val + self.next = None + +class LinkedList: + + def __init__(self): + + self.head = Node(0) + self.len = 0 + + def _loop(self , index): + + node = self.head + p = 0 + while p < index + 1: + node = node.next + p += 1 + + return node + + def get(self, index: int) -> int: + if self.len <= index or index < 0: + return -1 + + node = self._loop(index) + + return node.val + + def add_at_head(self, val: int) -> None: + + self.add_at_index(0, val) + + + def add_at_tail(self, val: int) -> None: + + self.add_at_index(self.len, val) + + + def add_at_index(self, index: int, val: int) -> None: + + if self.len < index: + return -1 + + if index < 0: + index = 0 + + self.len += 1 + + node = self.head + for _ in range(index): + node = node.next + + new_node = Node(val) + new_node.next = node.next + node.next = new_node + + def delete_at_index(self, index: int) -> None: + + if self.len <= index or index < 0: + return -1 + + self.len -= 1 + + node = self.head + for _ in range(index): + node = node.next + + node.next = node.next.next diff --git a/linked_lists/linked_list_fifo.py b/linked_lists/linked_list_fifo.py new file mode 100644 index 0000000..4a2b1ec --- /dev/null +++ b/linked_lists/linked_list_fifo.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +class Node: + + def __init__(self, value=None): + self.value = value + self.left = None + self.right = None + + def __repr__(self): + """Prints the node""" + return f'{self.value}' + + +class LinkedListFIFO: + + def __init__(self): + self.head = None + self.tail = None + self.length = 0 + + def add_first(self, value): + self.length = 1 + node = Node(value) + self.head = node + self.tail = node + + def delete_first(self): + self.length = 0 + self.head = None + self.tail = None + + def add(self, value): + self.length += 1 + node = Node(value) + if self.tail: + self.tail.right = node + self.tail = node + + def _delete(self, node): + if not self.head or not self.head.pointer: + self.delete_first() + else: + node, prev, i = self._find(index) + if i == index and node: + self.length -= 1 + if i == 0 or not prev: + self.head = node.pointer + else: + prev.pointer = node.pointer + if not self.tail == node: + self.tail = prev + + def find(self, index): + prev = None + node = self.head + i = 0 + while node and i < index: + prev = node + node = node.right + i += 1 + return node, prev, i + + +if __name__ == '__main__': + + print('Linked List FIFO') + ll = LinkedListFIFO() + print(f'Add 1: {ll.add(1)}') + print(f'Add 2: {ll.add(2)}') + print(f'Add 3: {ll.add(3)}') + + print(f'Length: {ll.length}') + print(f'Find 1: {ll.find(1)}') + + print(f'Delete 1: {ll._delete(1)}') + print(f'Length: {ll.length}') + print(f'Find 1: {ll.find(1)}') diff --git a/linked_lists/merge_two_lists.py b/linked_lists/merge_two_lists.py new file mode 100644 index 0000000..261286d --- /dev/null +++ b/linked_lists/merge_two_lists.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +class Node: + def __init__(self, val=0, next=None): + self.val = val + self.next = next + + +def merge_two_list(list1: Optional[Node], list2: Optional[Node]) -> Optional[Node]: + + if not list1: + return list2 + + if not list2: + return list1 + + if list1.val < list2.val: + list1.next = merge_two_list(list1.next, list2) + return list1 + else: + list2.next = merge_two_list(list1, list2.next) + return list2 + diff --git a/linked_lists/remove_elements.py b/linked_lists/remove_elements.py new file mode 100644 index 0000000..81e25f1 --- /dev/null +++ b/linked_lists/remove_elements.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +def remove_elements(head, val): + + sentinel = Node(0) + sentinel.next = head + prev, current = sentinel, head + + while current: + if current.val == val: + prev.next = current.next + else: + prev = current + current = current.next + + return sentinel.next + diff --git a/linked_lists/remove_kth_node.py b/linked_lists/remove_kth_node.py new file mode 100644 index 0000000..db7e14e --- /dev/null +++ b/linked_lists/remove_kth_node.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def remove_kth_node(self, head, n): + + if head is None or head.next is None: + return None + + # find the length of the list + node, length = head, 0 + while node: + node = node.next + length += 1 + + # if n is the entire list, remove head + if n == length: + return head.next + + # loop to kth element + node, i = head, 0 + while i <= length - n: + node = node.next + i += 1 + + # remove the kth element + node.next = node.next.next + + return head diff --git a/linked_lists/reverse_linked_list.py b/linked_lists/reverse_linked_list.py new file mode 100644 index 0000000..6e0a013 --- /dev/null +++ b/linked_lists/reverse_linked_list.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +class Node: + def __init__(self, val=0, next): + self.val = val + self.next = next + + +def reverse_list(head: Optional[Node]) -> Optional[Node]: + + if not head or not head.next: + return head + + new_head = reverse_list(head.next) + head.next.next = head + head.next = None + + return new_head diff --git a/linked_lists/reverse_linked_list_II.py b/linked_lists/reverse_linked_list_II.py new file mode 100644 index 0000000..281a031 --- /dev/null +++ b/linked_lists/reverse_linked_list_II.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +class Node: + def __init__(self, val=0, next): + self.val = val + self.next = next + + +def reverse_list(head: Optional[Node]) -> Optional[Node]: + + if head is None: + return head + + prev = None + curr = head + + while curr: + next_temp = curr.next + curr.next = prev + prev = curr + curr = next_temp + + return prev + diff --git a/linked_lists/rotate_list_by_k.py b/linked_lists/rotate_list_by_k.py new file mode 100644 index 0000000..14ede5a --- /dev/null +++ b/linked_lists/rotate_list_by_k.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def rotate_list_by_k(head, k): + + if head is None: + return head + + # get the size of the list + end, n = head, 1 + while end.next: + end = end.next + n += 1 + + # rotate + end.next = head + new_end, i = head, 0 + while i < n - (k % n) - 1: + new_end = new_end.next + i += 1 + + # remove cycle + new_head = new_end.next + + return new_head diff --git a/linked_lists/swap_every_two_nodes.py b/linked_lists/swap_every_two_nodes.py new file mode 100644 index 0000000..3e1de31 --- /dev/null +++ b/linked_lists/swap_every_two_nodes.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +class Node: + def __init__(self, val=0, next=None): + self.val = val + self.next = next + + +def swap_pairs(head: Optional[Node]) -> Optional[Node]: + + if not head or not head.next: + return head + + first_node = head + second_node = head.next + + first_node.next = swap_pairs(second_node.next) + second_node.next = first_node + + return second_node diff --git a/math/README.md b/math/README.md new file mode 100644 index 0000000..afe3de4 --- /dev/null +++ b/math/README.md @@ -0,0 +1,174 @@ +## math + +
+ +* when dealing with floating point numbers, take note of rounding mistakes. consider using epsilon comparisons instead of equality checks (`abs(x - y) <= 1e-6` instead of `x == y`). + +
+ +--- + +### fibonnaci + +
+ +* check the dynamic programming chapter to learn how to solve this with memoization. + +
+ +```python +def fibonacci(n): + + if n == 0 or n == 1: + return n + return fibonacci(n - 1) + fibonacci(n - 2) +``` + +
+ +--- + +### determine whether a sudoku board is valid + +
+ + +
+ +```python + +''' +Input: board = +[["5","3",".",".","7",".",".",".","."] +,["6",".",".","1","9","5",".",".","."] +,[".","9","8",".",".",".",".","6","."] +,["8",".",".",".","6",".",".",".","3"] +,["4",".",".","8",".","3",".",".","1"] +,["7",".",".",".","2",".",".",".","6"] +,[".","6",".",".",".",".","2","8","."] +,[".",".",".","4","1","9",".",".","5"] +,[".",".",".",".","8",".",".","7","9"]] +Output: true +''' + +def is_valid_sudoku(board) -> bool: + + N = 9 + + rows = [set() for _ in range(N)] + cols = [set() for _ in range(N)] + boxes = [set() for _ in range(N)] + + for r in range(N): + for c in range(N): + val = board[r][c] + if val == '.': + continue + + if val in rows[r]: + return False + rows[r].add(val) + + if val in cols[c]: + return False + cols[c].add(val) + + index = (r // 3) * 3 + c // 3 + if val in boxes[index]: + return False + boxes[index].add(val) + + return True + +``` + +
+ +--- + +### check if happy number + +
+ +```python +def get_next(n): + + total_sum = 0 + while n > 0: + n, digit = divmod(n, 10) + total_sum += digit**2 + + return total_sum + + +def is_happy(self, n: int) -> bool: + + seen = set() + while n != 1 and n not in seen: + seen.add(n) + n = get_next(n) + + return n == 1 +``` + + +
+ +--- + +### get a row in a pascal triangle + +
+ +```python +def get_row(self, row: int) -> list[int]: + + if row == 0: + return [1] + + result = self.get_row(row - 1) + + return [1] + [sum(_) for _ in zip(result, result[1:])] + [1] +``` + +
+ +--- + +### work with primes + +
+ +```python +import math +import random + + +def find_greatest_common_divider(a, b) -> int: + + while(b != 0): + result = b + a, b = b, a % b + + return result + + +def _is_prime(number) -> bool: + + if number < 2: + return False + + for i in range(2, int(math.sqrt(number))): + if number % i == 0: + return False + + return True + + +def find_prime_factors(number) -> list: + + divisors = [d for d in range(2, number//2 + 1) if number % d == 0] + primes = [d for d in divisors if _is_prime(d)] + + return primes +``` diff --git a/math/binary_exponentiation.py b/math/binary_exponentiation.py new file mode 100644 index 0000000..74e293a --- /dev/null +++ b/math/binary_exponentiation.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +''' +Binary exponentiation, also known as exponentiation by squaring, is a technique for +efficiently computing the power of a number. By repeatedly squaring x and halving n, +we can quickly compute x^n using a logarithmic number of multiplications. +''' + +def binary_exp(x: float, n: int) -> float: + + if n == 0: + return 1 + + if n < 0: + return 1.0 / binary_exp(x, -1 * n) + + if n % 2 == 1: + return x * binary_exp(x * x, (n - 1) // 2) + + return binary_exp(x * x, n // 2) diff --git a/math/check_sudoku_board.py b/math/check_sudoku_board.py new file mode 100644 index 0000000..9b195d4 --- /dev/null +++ b/math/check_sudoku_board.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +''' +Determine if a 9 x 9 Sudoku board is valid. Only the filled cells need to be validated according to the following rules: + +- Each row must contain the digits 1-9 without repetition. +- Each column must contain the digits 1-9 without repetition. +- Each of the nine 3 x 3 sub-boxes of the grid must contain the digits 1-9 without repetition. + +Input: board = +[["5","3",".",".","7",".",".",".","."] +,["6",".",".","1","9","5",".",".","."] +,[".","9","8",".",".",".",".","6","."] +,["8",".",".",".","6",".",".",".","3"] +,["4",".",".","8",".","3",".",".","1"] +,["7",".",".",".","2",".",".",".","6"] +,[".","6",".",".",".",".","2","8","."] +,[".",".",".","4","1","9",".",".","5"] +,[".",".",".",".","8",".",".","7","9"]] +Output: true +''' + +def is_valid_sudoku(board) -> bool: + + N = 9 + + rows = [set() for _ in range(N)] + cols = [set() for _ in range(N)] + boxes = [set() for _ in range(N)] + + for r in range(N): + for c in range(N): + val = board[r][c] + if val == '.': + continue + + if val in rows[r]: + return False + rows[r].add(val) + + if val in cols[c]: + return False + cols[c].add(val) + + index = (r // 3) * 3 + c // 3 + if val in boxes[index]: + return False + boxes[index].add(val) + + return True + diff --git a/math/fibonacci.py b/math/fibonacci.py new file mode 100644 index 0000000..6554fd0 --- /dev/null +++ b/math/fibonacci.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def fibonacci(n): + + if n == 0 or n == 1: + return n + return fibonacci(n - 1) + fibonacci(n - 2) diff --git a/math/happy_number.py b/math/happy_number.py new file mode 100644 index 0000000..235d1e6 --- /dev/null +++ b/math/happy_number.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def get_next(n): + + total_sum = 0 + while n > 0: + n, digit = divmod(n, 10) + total_sum += digit**2 + + return total_sum + + +def is_happy(self, n: int) -> bool: + + seen = set() + while n != 1 and n not in seen: + seen.add(n) + n = get_next(n) + + return n == 1 + diff --git a/math/pascal_triangle.py b/math/pascal_triangle.py new file mode 100644 index 0000000..d1c5424 --- /dev/null +++ b/math/pascal_triangle.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def get_row(self, row: int) -> list[int]: + + if row == 0: + return [1] + + result = self.get_row(row - 1) + + return [1] + [sum(_) for _ in zip(result, result[1:])] + [1] diff --git a/math/playing_with_math.py b/math/playing_with_math.py new file mode 100644 index 0000000..45d6a24 --- /dev/null +++ b/math/playing_with_math.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +import math +import random + + +def find_greatest_common_divider(a, b) -> int: + + while(b != 0): + result = b + a, b = b, a % b + + return result + + +def _is_prime(number) -> bool: + + if number < 2: + return False + + for i in range(2, int(math.sqrt(number))): + if number % i == 0: + return False + + return True + + +def find_prime_factors(number) -> list: + + divisors = [d for d in range(2, number//2 + 1) if number % d == 0] + primes = [d for d in divisors if _is_prime(d)] + + return primes diff --git a/queues/README.md b/queues/README.md new file mode 100644 index 0000000..2ae858b --- /dev/null +++ b/queues/README.md @@ -0,0 +1,376 @@ +## queues + +
+ +* queues are **first in, first out (FIFO) abstract structures** (*i.e.*, items are removed in the same order they are added) that can be implemented with two arrays or a dynamic array (linked list), as long as items are added and removed from opposite sides. + +* queues support **enqueue** (add to one end) and **dequeue** (remove from the other end, or tail). + +* if implemented with a dynamic array, a more efficient solution is to use a circular queue (ring buffer), *i.e.*, a fixed-size array and two pointers to indicate the starting and ending positions. + * an advantage of circular queues is that we can use the spaces in front of the queue. + * in a normal queue, once the queue becomes full, we cannot insert the next element even if there is a space in front of the queue. but using the circular queue, we can use the space to store new values. + +* queues are often used in **breath-first search** (where you store a list of nodes to be processed) or when implementing a cache. + +* in python, queues can be called with `collections.deque()` (and methods `popleft()` and `insert()`). + + + +
+ +--- + +### designing a circular queue + +
+ +* a circular queue can be built with either arrays or linked lists (nodes). + +
+ +#### with an array + +
+ +* to build a ring with a fixed size array, any of the elements could be considered as the head. + + +
+ + +```python +class CircularQueue: + + def __init__(self, size): + self.head = 0 + self.tail = 0 + self.size = size + self.queue = [None] * self.size + + def enqueue(self, value: int) -> bool: + if self.is_full(): + return False + + if self.is_empty(): + self.head = 0 + + while self.queue[self.tail] is not None: + self.tail += 1 + if self.tail == self.size: + self.tail = 0 + self.queue[self.tail] = value + + return True + + def dequeue(self) -> bool: + if self.is_empty(): + return False + + value = self.queue[self.head] + self.queue[self.head] = None + self.head += 1 + + if self.head == self.size: + self.head = 0 + + return True + + def front(self) -> int: + return self.queue[self.head] or False + + def rear(self) -> int: + return self.queue[self.tail] or False + + def is_empty(self) -> bool: + for n in self.queue: + if n is not None: + return False + return True + + def is_full(self) -> bool: + for n in self.queue: + if n is None: + return False + return True +``` + +
+ +* to enqueue, you loop the queue with the tail index until you find a `None` (even if it has to loop back): + +
+ +```python +while queue[self.tail]: + + self.tail += 1 + if self.tail == self.size: + self.tail = 0 +self.queue[self.tail] = value +``` + +
+ +* to dequeue, you simply pop the head value: + +
+ +```python +value = self.queue[self.head] +self.queue[self.head] = None +self.head += 1 +``` + +
+ +* as long as we know the length of the queue, we can instantly locate its tails based on this formula: + +
+ +``` +tail_index = (head_index + current_length - 1) % size +``` + +
+ +* there is one occasion when `tail` index is set to zero: + * when the enqueue operation adds to the last position in the array and tail has to loop back (`self.tail == self.size`). + +* there are two occasions when `head` index is set to zero: + * when the queue is checked as empty + * when the dequeue operation popped the last element in the array and head has to loop back (`self.head == self.size`). + + + +
+ +* another version used only one index (for `head`) and calculate the tail with the equation: + +
+ +```python +(self.head + self.count - 1) % self.size +```` + +
+ +* and the next tail with: + +
+ +```python +(self.head + self.count) % self.size +``` + +
+ +* and the next `head` is always given by: + +
+ +```python +(self.head + 1) % self.size +``` + +
+ +* * in the example below we also implement the methods `is_empty` and `is_full` using an extra counter variable `self.count` that can be compared to `self.size` or `0` and used to validate `rear` and `front`. + +
+ +```python +class CircularQueue: + + def __init__(self, size): + self.head = 0 + self.count = 0 + self.queue = [0] * size + self.size = size + + def _get_tail(self): + return (self.head + self.count - 1) % self.size + + def _get_next_tail(self): + return (self.head + self.count) % self.size + + def _get_next_head(self): + return (self.head + 1) % self.size + + def enqueue(self, value: int) -> bool: + + if self.is_empty(): + return False + + self.queue[self._get_next_tail()] = value + self.count += 1 + + return True + + def dequeue(self) -> bool: + + if self.is_empty(): + return False + + self.head = self._get_next_head() + self.count -= 1 + + return True + + def Front(self) -> int: + if self.is_empty(): + return False + + return self.queue[self.head] + + def Rear(self) -> int: + if self.is_empty(): + return False + + return self.queue[self._get_tail()] + + def isEmpty(self) -> bool: + return self.count == 0 + + def isFull(self) -> bool: + return self.count == self.size +``` + +
+ + + +#### with linked lists + +
+ + +* a linked list could be more memory efficient, since it does not pre-allocate memory for unused capacity (and size of the queue is not "artificial" like before). + +* note that this queue is not thread-safe: the data structure could be corrupted in a multi-threaded environment (as race-condition could occur). to mitigate this problem, one could add the protection of a lock. + +
+ +```python +class Node: + def __init__(self, value, next=None): + + self.value = value + self.next = next + + +class Queue: + + def __init__(self, size): + + self.size = size + self.count = 0 + self.head = None + self.tail = None + + def enqueue(self, value: int) -> bool: + + if self.is_full(): + return False + + if self.is_empty(): + self.head = self.tail = Node(value) + + else: + self.tail.next = Node(value) + self.tail = self.tail.next + + self.count += 1 + + return True + + def dequeue(self) -> bool: + + if self.is_empty(): + return False + + self.head = self.head.next + self.count -= 1 + + return True + + def front(self) -> int: + if self.is_empty(): + return False + return self.head.value + + def rear(self) -> int: + if self.is_empty(): + return False + return self.tail.value + + def is_empty(self) -> bool: + return self.count == 0 + + def is_full(self) -> bool: + return self.count == self.size +``` + +
+ +--- + +### a stream with rate limiter + +
+ +```python +class Logger: + + def __init__(self): + self.msg_set = set() + self.msg_queue = deque() + + def print_message(self, timestamp, message) -> bool: + + while self.msg_queue: + msg, msg_timestamp = self.msg_queue[0] + if timestamp - msg_timestamp >= 10: + self.msg_queue.popleft() + self.msg_set.remove(msg) + else: + break + + if message not in self.msg_set: + self.msg_set.add(message) + self.msg_queue.append((message, timestamp)) + return True + + return False +``` + +
+ +--- + +### moving average + +
+ +* given a stream of integers and a window size, the moving average in the sliding window can be calculated with a queue: + +
+ +```python + +class MovingAverage: + + def __init__(self, size: int): + + self.queue = [] + self.size = size + + + def next(self, val: int) -> float: + + self.queue.append(val) + + if len(self.queue) > self.size: + self.queue.pop(0) + + return sum(self.queue) / len(self.queue) +``` diff --git a/queues/circular_queue_array.py b/queues/circular_queue_array.py new file mode 100644 index 0000000..e56fddd --- /dev/null +++ b/queues/circular_queue_array.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +class CircularQueue: + + def __init__(self, size): + self.head = 0 + self.tail = 0 + self.size = size + self.queue = [None] * self.size + + def enqueue(self, value: int) -> bool: + + if self.is_full(): + return False + + if self.is_empty(): + self.head = 0 + + while self.queue[self.tail] is not None: + self.tail += 1 + if self.tail == self.size: + self.tail = 0 + + self.queue[self.tail] = value + + return True + + def dequeue(self) -> bool: + + if self.is_empty(): + return False + + value = self.queue[self.head] + self.queue[self.head] = None + self.head += 1 + + if self.head == self.size: + self.head = 0 + + return True + + def front(self) -> int: + return self.queue[self.head] or False + + def rear(self) -> int: + return self.queue[self.tail] or False + + def is_empty(self) -> bool: + for n in self.queue: + if n is not None: + return False + return True + + def is_full(self) -> bool: + for n in self.queue: + if n is None: + return False + return True + diff --git a/queues/moving_average.py b/queues/moving_average.py new file mode 100644 index 0000000..292ae7a --- /dev/null +++ b/queues/moving_average.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +''' +Given a stream of integers and a window size, calculate the moving average in the sliding window. +''' + +class MovingAverage: + + def __init__(self, size: int): + + self.queue = [] + self.size = size + + + def next(self, val: int) -> float: + + self.queue.append(val) + + if len(self.queue) > self.size: + self.queue.pop(0) + + return sum(self.queue) / len(self.queue) + diff --git a/queues/queue_list.py b/queues/queue_list.py new file mode 100644 index 0000000..f650535 --- /dev/null +++ b/queues/queue_list.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +class Node: + def __init__(self, value, next=None): + + self.value = value + self.next = next + + +class Queue: + + def __init__(self, size): + + self.size = size + self.count = 0 + self.head = None + self.tail = None + + def enqueue(self, value: int) -> bool: + + if self.is_full(): + return False + + if self.is_empty(): + self.head = self.tail = Node(value) + + else: + self.tail.next = Node(value) + self.tail = self.tail.next + + self.count += 1 + + return True + + def dequeue(self) -> bool: + + if self.is_empty(): + return False + + self.head = self.head.next + self.count -= 1 + + return True + + def front(self) -> int: + if self.is_empty(): + return False + return self.head.value + + def rear(self) -> int: + if self.is_empty(): + return False + return self.tail.value + + def is_empty(self) -> bool: + return self.count == 0 + + def is_full(self) -> bool: + return self.count == self.size + diff --git a/queues/queue_list_II.py b/queues/queue_list_II.py new file mode 100644 index 0000000..cdaecaa --- /dev/null +++ b/queues/queue_list_II.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +class CircularQueue: + + def __init__(self, size): + self.head = 0 + self.count = 0 + self.queue = [0] * size + self.size = size + + def _get_tail(self): + return (self.head + self.count - 1) % self.size + + def _get_next_tail(self): + return (self.head + self.count) % self.size + + def _get_next_head(self): + return (self.head + 1) % self.size + + def enqueue(self, value: int) -> bool: + + if self.is_empty(): + return False + + self.queue[self._get_next_tail()] = value + self.count += 1 + + return True + + def dequeue(self) -> bool: + + if self.is_empty(): + return False + + self.head = self._get_next_head() + self.count -= 1 + + return True + + def Front(self) -> int: + if self.is_empty(): + return False + + return self.queue[self.head] + + def Rear(self) -> int: + if self.is_empty(): + return False + + return self.queue[self._get_tail()] + + def isEmpty(self) -> bool: + return self.count == 0 + + def isFull(self) -> bool: + return self.count == self.size diff --git a/queues/stream_with_rate_limiter.py b/queues/stream_with_rate_limiter.py new file mode 100644 index 0000000..da58743 --- /dev/null +++ b/queues/stream_with_rate_limiter.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +class Logger: + + def __init__(self): + self.msg_set = set() + self.msg_queue = deque() + + def print_message(self, timestamp, message) -> bool: + + while self.msg_queue: + msg, msg_timestamp = self.msg_queue[0] + if timestamp - msg_timestamp >= 10: + self.msg_queue.popleft() + self.msg_set.remove(msg) + else: + break + + if message not in self.msg_set: + self.msg_set.add(message) + self.msg_queue.append((message, timestamp)) + return True + + return False + diff --git a/searching/README.md b/searching/README.md new file mode 100644 index 0000000..bdeaccd --- /dev/null +++ b/searching/README.md @@ -0,0 +1,288 @@ +## binary search + +
+ +* a binary search operates on a (sorted) contiguous sequence with a specified left and right index (this is called the **search space**). + +* binary searching is composed of 3 sections: + * **pre-processing**: sort if collection is unsorted + * **binary search**: using a loop or recursion to divide search space in half after each comparison (`O(log(N)`) + * **post-processing**: determine viable candidates in the remaining space + +* there are 3 "templates" when writing a binary search: + * `while left < right`, with `left = mid + 1` and `right = mid - 1` + * `while left < right`, with `left = mid + 1` and `right = mid`, and `left` is returned + * `while left + 1 < right`, with `left = mid` and `right = mid`, and `left` and `right` are returned + +* in python, `bisect.bisect_left()` returns the index at which the `val` should be inserted in the sorted array. + +
+ +```python +from bisect import bisect_left + +def binary_search(array, val, low=0, high=None): + + high = high or len(array) + position = bisect_left(array, val, low, high) + + if position != high and array[position] == value: + return position + + return -1 +``` + +---- + +### iterative + +
+ +```python + if lens(nums) == 0: + return False + + lower, higher = 0, len(array) + + while lower < higher: + mid = (higher + lower) // 2 + + if array[mid] == item: + return mid + elif array[mid] > item: + higher = mid - 1 + else: + lower = mid + 1 + + return False +``` + +
+ +---- + +### recursive + +
+ +```python +def binary_search_recursive(array, item, higher=None, lower=0): + + higher = higher or len(array) + + if higher < lower: + return False + + mid = (higher + lower) // 2 + + if item == array[mid]: + return mid + + elif item < array[mid]: + return binary_search_recursive(array, item, mid - 1, lower) + + else: + return binary_search_recursive(array, item, higher, mid + 1) +``` + +
+ +* or a slightly different version that does not carry `lower` and `higher`: + +
+ +```python +def binary_search_recursive(array, item): + + start, end = 0, len(array) + mid = (end - start) // 2 + + while len(array) > 0: + if array[mid] == item: + return True + elif array[mid] > item: + return binary_search_recursive(array[mid + 1:], item) + else: + return binary_search_recursive(array[:mid], item) + + return False +``` + +
+ +--- + +### in a matrix + +
+ +```python +def binary_search_matrix(matrix, item, lower=0, higher=None): + + if not matrix: + return False + + rows = len(matrix) + cols = len(matrix[0]) + higher = higher or rows * cols + + if higher > lower: + mid = (higher + lower) // 2 + row = mid // cols + col = mid % cols + + if item == matrix[row][col]: + return row, col + elif item < matrix[row][col]: + return binary_search_matrix(matrix, item, lower, mid - 1) + else: + return binary_search_matrix(matrix, item, mid + 1, higher) + + return False +``` + +
+ +--- + +### find the square root + +
+ +```python + +def sqrt(x) -> int: + + if x < 2: + return x + + left, right = 2, x // 2 + + while left <= right: + + mid = (right + left) // 2 + num = mid * mid + + if num > x: + right = mid - 1 + elif num < x: + left = mid + 1 + else: + return mid + + return right +``` + +
+ +--- + +### find min in a rotated array + +
+ +```python +def find_min(nums): + + left, right = 0, len(nums) - 1 + + while nums[left] > nums[right]: + + mid = (left + right) // 2 + + if nums[mid] < nums[right]: + right = mid + else: + left = mid + 1 + + return nums[left] +``` + +
+ +--- + +### find a peak element + +
+ +* a peak element is an element that is strictly greater than its neighbors. + +
+ +```python +def peak_element(nums): + + left, right = 0, len(nums) - 1 + + while left < right: + + mid = (left + right) // 2 + + if nums[mid + 1] < nums[mid]: + right = mid + else: + left = mid + 1 + + return left +``` + +
+ +--- + +### find a desired sum + +
+ +* if the array is sorted: + +
+ +```python +def find_pairs_max_sum(array, desired_sum): + + i, j = 0, len(array) - 1 + + while i < j: + this_sum = array[i] + array[j] + if this_sum == desired_sum: + return array[i], array[j] + elif this_sum > desired_sum: + j -= 1 + else: + i += 1 + + return False +``` + +
+ +* if the array is not sorted, use a hash table: + +
+ +```python +def find_pairs_not_sorted(array, desired_sum): + + lookup = {} + + for item in array: + key = desired_sum - item + + if key in lookup.keys(): + lookup[key] += 1 + else: + lookup[key] = 1 + + for item in array: + key = desired_sum - item + + if item in lookup.keys(): + if lookup[item] == 1: + return (item, key) + else: + lookup[item] -= 1 + + return False +``` diff --git a/searching/binary_search.py b/searching/binary_search.py new file mode 100644 index 0000000..fb8052f --- /dev/null +++ b/searching/binary_search.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def binary_search_recursive(array, item, higher=None, lower=0): + + higher = higher or len(array) + + if higher < lower: + return False + + mid = (higher + lower) // 2 + + if item == array[mid]: + return mid + + elif item < array[mid]: + return binary_search_recursive(array, item, mid - 1, lower) + + else: + return binary_search_recursive(array, item, higher, mid + 1) + + +def binary_search_iterative(array, item): + + if lens(nums) == 0: + return False + + lower, higher = 0, len(array) + + while lower < higher: + mid = (higher + lower) // 2 + + if array[mid] == item: + return mid + + elif array[mid] > item: + higher = mid - 1 + + else: + lower = mid + 1 + + return False + + +def binary_search_matrix(matrix, item, lower=0, higher=None): + + if not matrix: + return False + + rows = len(matrix) + cols = len(matrix[0]) + higher = higher or rows * cols + + if higher > lower: + mid = (higher + lower) // 2 + row = mid // cols + col = mid % cols + + if item == matrix[row][col]: + return row, col + elif item < matrix[row][col]: + return binary_search_matrix(matrix, item, lower, mid - 1) + else: + return binary_search_matrix(matrix, item, mid + 1, higher) + + return False diff --git a/searching/find_minimum_rotated_array.py b/searching/find_minimum_rotated_array.py new file mode 100644 index 0000000..b72d547 --- /dev/null +++ b/searching/find_minimum_rotated_array.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def find_min(nums): + + left, right = 0, len(nums) - 1 + + while nums[left] > nums[right]: + + mid = (left + right) // 2 + + if nums[mid] < nums[right]: + # note above that it's on right + right = mid + else: + left = mid + 1 + + return nums[left] + diff --git a/searching/find_pair_nums.py b/searching/find_pair_nums.py new file mode 100644 index 0000000..d860497 --- /dev/null +++ b/searching/find_pair_nums.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def bs(array, item): + + start, end = 0, len(array) + mid = (end - start) // 2 + + while len(array) > 0: + if array[mid] == item: + return True + elif array[mid] > item: + return bs(array[mid + 1:], item) + else: + return bs(array[:mid], item) + + return False + + +def find_pairs_bs(array, desired_sum): + + for i in range(len(array)): + num1 = array[i] + desired_num = desired_sum - num1 + if bs(array[i + 1:], desired_num) == True: + return (num1, desired_num) + + return False + + +def find_pairs_max_sum(array, desired_sum): + + i, j = 0, len(array) - 1 + + while i < j: + this_sum = array[i] + array[j] + if this_sum == desired_sum: + return array[i], array[j] + elif this_sum > desired_sum: + j -= 1 + else: + i += 1 + + return False + + +def find_pairs_not_sorted(array, desired_sum): + + lookup = {} + + for item in array: + key = desired_sum - item + + if key in lookup.keys(): + lookup[key] += 1 + else: + lookup[key] = 1 + + for item in array: + key = desired_sum - item + + if item in lookup.keys(): + if lookup[item] == 1: + return (item, key) + else: + lookup[item] -= 1 + + return False + + + +if __name__ == "__main__": + + desired_sum = 8 + array1 = [1, 2, 3, 9] + array2 = [1, 2, 4, 5, 4] + array3 = [2, 1, 6, 3, 11, 2] + + assert(find_pairs_bs(array1, desired_sum) == False) + assert(find_pairs_bs(array2, desired_sum) == (4, 4)) + assert(find_pairs_max_sum(array1, desired_sum) == False) + assert(find_pairs_max_sum(array2, desired_sum) == (4,4)) + assert(find_pairs_not_sorted(array1, desired_sum) == False) + assert(find_pairs_not_sorted(array2, desired_sum) == (4, 4)) + assert(find_pairs_not_sorted(array3, desired_sum) == (2, 6)) diff --git a/searching/find_peak_element.py b/searching/find_peak_element.py new file mode 100644 index 0000000..7a47dca --- /dev/null +++ b/searching/find_peak_element.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +# A peak element is an element that is strictly greater than its neighbors. + +def peak_element(nums): + + left, right = 0, len(nums) - 1 + + while left < right: + + mid = (left + right) // 2 + + if nums[mid + 1] < nums[mid]: + right = mid + else: + left = mid + 1 + + return left + diff --git a/searching/sqrt_x.py b/searching/sqrt_x.py new file mode 100644 index 0000000..3fda6a3 --- /dev/null +++ b/searching/sqrt_x.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def sqrt(x) -> int: + + if x < 2: + return x + + left, right = 2, x // 2 + + while left <= right: + + mid = (right + left) // 2 + num = mid * mid + + if num > x: + right = mid - 1 + + elif num < x: + left = mid + 1 + + else: + return mid + + return right diff --git a/sets/README.md b/sets/README.md new file mode 100644 index 0000000..2af7932 --- /dev/null +++ b/sets/README.md @@ -0,0 +1,61 @@ +## sets + +
+ + +### implementing an `O(1)` randomized set class + +
+ +* a set structure where we would implement `insert`, `delete`, and `get_random` at `O(1)` time. + +* this type of structure is widely used in statistical algorithms such as markov chain monte carlo and metropolis-hastings algorithms, which needs sampling from a probability distribution when it's difficult to compute the distribution itself. + +* candidates for `O(1)` average insert time are: + * **hashmaps (or hashsets)**: to be able to implement `get_random` at `O(1)` (choose a random index and retrieve a random element), we would have to convert hashmap keys in a list, which is `O(N)`. a solution is to build a list of keys aside and use this list to compute `get_random` in `O(1)`. + * **array lists**: we would have `O(N)` with `delete`. the solution would be delete the last value (first swap the element to delete with the last one, then pop the last element out). for that, we need to compute an index of each element in `O(N)`, and we need a hashmap that stores `element -> index`. + +* either way, we need the same combination of data structures: a hashmap and an array. + * an array keeps the values appended in order. `delete` always replace elements to the end. + * an dictionary maps the values (key) to the corresponding length of the array (their index) so it guarantees `O(1)` lookup and provides a list for `random.choice()`. + +
+ +```python +import random + +class RandomizedSet: + + def __init__(self): + self.random_set = {} + self.index_list = [] + + def insert(self, val: int) -> bool: + + if val in self.random_set.keys(): + return False + + self.index_list.append(val) + self.random_set[val] = len(self.index_list) + + return True + + def remove(self, val: int) -> bool: + + if val in self.random_set.keys(): + + last_element = self.index_list[-1] + index_val = self.random_set[val] + self.index_list[index_val] = last_element + self.random_set[last_element] = index_val + + self.index_list.pop() + del self.random_set[val] + + return True + + return False + + def get_random(self) -> int: + return random.choice(self.index_list) + ``` diff --git a/sets/random_set.py b/sets/random_set.py new file mode 100644 index 0000000..b74b978 --- /dev/null +++ b/sets/random_set.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +import random + + +class RandomizedSet: + + def __init__(self): + + self.random_set = {} + self.index_list = [] + + def insert(self, val: int) -> bool: + + if val in self.random_set.keys(): + return False + + self.index_list.append(val) + self.random_set[val] = len(self.index_list) + + return True + + def remove(self, val: int) -> bool: + + if val in self.random_set.keys(): + + last_element = self.index_list[-1] + index_val = self.random_set[val] + self.index_list[index_val] = last_element + self.random_set[last_element] = index_val + + self.index_list.pop() + del self.random_set[val] + + return True + + return False + + def get_random(self) -> int: + + return random.choice(self.index_list) + diff --git a/sorting/README.md b/sorting/README.md new file mode 100644 index 0000000..3da1f9a --- /dev/null +++ b/sorting/README.md @@ -0,0 +1,55 @@ +## sorting + +
+ +* **inversions** in a sequence are pair of elements that are out of order with respect to the ordering relation. a sorting algorithm is a sequence of operations that reduces inversions to zero. + +* a **topological sort** of a directed graph is a way of ordering the list of nodes such that if `(a, b)` is an edge of the graph, then `a` appears before `b`. this type of sorting does not work if a graph has cycles or is not directed. + +* because of their efficiencies, you usually want to use either merge sort or quick sort (`O(N log (N)`). + +* other type of sorting algorithms can be seen below and in this directory's source code: + +
+ + +

+ +

+ +
+ +--- + +### merge sort + +
+ +```python +def ms(array): + + if len(array) < 2: + return array + + mid = len(array) // 2 + left = array[:mid] + right = array[mid:] + + result, i, j = [], 0, 0 + + while i < len(left) and j < len(right): + + if left[i] <= right[j]: + result.append(left[i]) + i += 1 + else: + result.append(right[j]) + j += 1 + + if left[i:]: + result.extend(left[i:]) + if right[j:]: + result.extend(right[j:]) + + return result +``` diff --git a/sorting/bubble_sort.py b/sorting/bubble_sort.py new file mode 100644 index 0000000..c4dd7bf --- /dev/null +++ b/sorting/bubble_sort.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + + +def bubble_sort(array) + + has_swapped = True + + while has_swapped: + has_swapped = False + + for i in range(len(array) - 1): + if array[i] > array[i + 1]: + array[i], array[i + 1] = array[i + 1], array[i] + has_swapped = True diff --git a/sorting/bucket_sort.py b/sorting/bucket_sort.py new file mode 100644 index 0000000..693c0d3 --- /dev/null +++ b/sorting/bucket_sort.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + + +def bucket_sort(array, k): + + buckets = [[] for _ in range(k)] + + shift = min(array) + max_val = max(array) - shift + bucket_size = max(1, max_val / k) + + for i, e in enumerate(array): + + index = (e - shift) // bucket_size + + if index == k: + buckets[k - 1].append(e) + else: + buckets[index].append(e) + + for bucket in buckets: + bucket.sort() + + sorted_array = [] + for bucket in buckets: + sorted_array.extend(bucket) + + for i in range(len(array)): + array[i] = sorted_array[i] diff --git a/sorting/counting_sort.py b/sorting/counting_sort.py new file mode 100644 index 0000000..b4979d8 --- /dev/null +++ b/sorting/counting_sort.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + + +def counting_sort(array): + + k = max(array) + counts = [0] * (k + 1) + + for e in array: + counts[e] += 1 + + starting_index = 0 + for i, count in enumerate(counts): + counts[i] = starting_index + starting_index += count + + sorted_list = [0] * len(array) + + for e in array: + + sorted_list[counts[e]] = e + counts[e] += 1 + + for i in range(len(array)): + array[i] = sorted_list[i] diff --git a/sorting/insertion_sort.py b/sorting/insertion_sort.py new file mode 100644 index 0000000..b60c520 --- /dev/null +++ b/sorting/insertion_sort.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + + +def insertion_sort(array): + + for i in range(1, len(array)): + current_index = i + + while current_index > 0 and array[current_index - 1] > array[current_index]: + + array[current_index], array[current_index - 1] = array[current_index - 1], array[current_index] + current_index -= 1 diff --git a/sorting/merge_sort.py b/sorting/merge_sort.py new file mode 100644 index 0000000..9f8b227 --- /dev/null +++ b/sorting/merge_sort.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def merge_sort(array): + + # part 1: recursively divide the array into subarrays + if len(array) < 2: + return array + + mid = len(array) // 2 + left = merge_sort(array[:mid]) + right = merge_sort(array[mid:]) + + # part 2: merge the subarrays + result = [] + i, j = 0, 0 + + while i < len(left) and j < len(right): + if left[i] <= right[j]: + result.append(left[i]) + i +=1 + else: + result.append(right[j]) + j +=1 + + if left[i:]: + result.extend(left[i:]) + if right[j:]: + result.extend(right[j:]) + + return result + diff --git a/sorting/quick_sort.py b/sorting/quick_sort.py new file mode 100644 index 0000000..144cbd9 --- /dev/null +++ b/sorting/quick_sort.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def quick_sort_partition(array): + + pivot, array = array[0], array[1:] + + lower = [i for i in array if i <= pivot] + higher = [i for i in array if i > pivot] + + return lower, pivot, higher + + +def quick_sort_divided(array): + + if len(array) < 2: + return array + + lower, pivot, higher = quick_sort_partition(array) + + return quick_sort_divided(lower) + [pivot] + quick_sort_divided(higher) + diff --git a/sorting/seletction_sort.py b/sorting/seletction_sort.py new file mode 100644 index 0000000..4e71206 --- /dev/null +++ b/sorting/seletction_sort.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + + +def selection_sort(array): + + for i in range(len(array)): + min_index = i + + for j in range(i + 1, len(array)): + if array[j] < array[min_index]: + min_index = j + + array[min_index], array[i] = array[i], array[min_index] diff --git a/stacks/README.md b/stacks/README.md new file mode 100644 index 0000000..5b877c9 --- /dev/null +++ b/stacks/README.md @@ -0,0 +1,42 @@ +## stacks + + +
+ +* stacks are **last in, first out** (LIFO) abstract structures, where the newest element is first one to be removed from the structure. + +* a stack support `push` and `pop` at `O(1)`, and they be implemented with arrays or singly linked list. + +* stacks are useful in **depth-first search** algorithms, where you can push temp data as you recurse, and remove them from the (memory or data structure) stack as you backtrace. + +* to keep `find_min()` at `O(1)`, you can keep track of "prior minimum" when pushing and poping: + +
+ +```python +class Stack: + + def __init__(self): + self.stack = [] + self.min = None + + def push(self, val: int) -> None: + self.stack.append((val, self.min)) + if self.min is not None: + self.min = min(self.min, val) + else: + self.min = val + + def pop(self) -> None: + if self.stack: + (val, prior_min) = self.stack.pop() + self.min = prior_min + return val + + def top(self) -> int: + if self.stack: + return self.stack[-1][0] + + def get_min(self) -> int: + return self.min + ``` diff --git a/stacks/stack.py b/stacks/stack.py new file mode 100644 index 0000000..f805b72 --- /dev/null +++ b/stacks/stack.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +class Stack: + + def __init__(self): + self.stack = [] + self.min = None + + def push(self, val: int) -> None: + self.stack.append((val, self.min)) + if self.min is not None: + self.min = min(self.min, val) + else: + self.min = val + + def pop(self) -> None: + if self.stack: + (val, prior_min) = self.stack.pop() + self.min = prior_min + return val + + def top(self) -> int: + if self.stack: + return self.stack[-1][0] + + def get_min(self) -> int: + return self.min + diff --git a/trees/README.md b/trees/README.md new file mode 100644 index 0000000..eb9ede1 --- /dev/null +++ b/trees/README.md @@ -0,0 +1,903 @@ +## trees + +
+ +* a tree is an undirected and connected acyclic graph with no cycle or loops. + +* trees are widely used abstract data type that represents a hierarchical structure with a set of connected nodes. + +* each node in the tree can be connected to many children, but must be connected to exactly one parent (except for the root node). + + + +
+ +--- + +### binary trees + +
+ +* **binary trees** are trees that have up to two children. + +* `search`, `remove`, and `insert` are all `O(log(N)` runtime. + +* the space complexity of traversing balanced trees is `O(h)` where `h` is the height of the tree (while very skewed trees will be `O(N)`). + +* the **width** of a binary tree is the number of nodes in a level. + +* the **degree** of a binary tree is the number of children of a node. + + +
+ +--- + +### full, complete, and perfect binary trees + +
+ +* a **full binary tree** has each node with either zero or two children, i.e., no node has only one child. + +* a **complete tree** is a tree on which every level is fully filled (except perhaps for the last). + +* a **perfect tree** is both full and complete (it must have exactly `2^k - 1` nodes, where `k` is the number of levels). + +
+ +```python +def is_full(node): + + if node is None: + return True + + return bool(node.right and node.left) and is_full(node.right) and is_full(node.left) +``` + +
+ +* a node is called **leaf** if it has no children. + +
+ +```python +def is_leaf(node): + + return bool(not node.right and not node.left) +``` + +
+ + +---- + +### depth of a binary tree + +
+ +* the **depth** (or level) of a node is the number of edges from the tree's root node until a certain node. + +* the **height of tree** is the height of its root node, or the depth of its deepest node. + +
+ +```python +def max_depth(root) -> int: + + if root is None: + return -1 + + return 1 + max(height(root.left), height(root.right)) +``` + +
+ +--- + +### balanced trees + +
+ +* a **balanced tree** is a binary tree in which the left and right subtrees of every node differ in height by no more than 1. + +
+ +```python +def is_balanced(root): + + if root is None: + return True + + return abs(height(root.left) - height(root.right)) < 2 and \ + is_balanced(root.left) and is_balanced(root.right) +``` + +
+ +--- + +### tree traversal: breath-first search (level-order) + +
+ +* bfs gives all elements **in order** with time `O(log(N)` and it's used to traverse a tree by level. + +* iterative solutions use a queue for traversal or find the shortest path from the root node to a target node: + - in the first round, it processes the root node. + - in the second round, it processes the nodes next to the root node. + - in the third round, it processes the nodes which are two steps from the root node, etc. + - newly-added nodes will not be traversed immediately but will be processed in the next round. + - if node `X` is added to the `kth` round queue, the shortest path between the root node and `X` is exactly `k`. + +
+ +```python +def bfs_iterative(root): + + result = [] + queue = collections.deque([root]) + + while queue: + + node = queue.popleft() + + if node: + result.append(node.val) + queue.append(node.left) + queue.append(node.right) + + return result + +``` + +
+ +--- + +### tree traversal: depth-first search + +
+ +- dfs is used to find the path from the root node to a target node if you want to visit every node and/or search the deepest paths first. + +- recursive solutions are easier to implement, however, if the recursion depth is too high, stack overflow might occur. + - you might want to use bfs instead or implement dfs using an explicit stack (i.e., with a while loop and a stack structure). + +- dfs only traces back and try another path after it reaches the deepest node. as a result, the first path found in dfs is not always the shortest path: + - push the root node to the stack. + - try the first neighbor and push its node to the stack. + - when it reaches the deepest node, trace back by popping the deepest node from the stack (the last node pushed). therefore, he processing order of the nodes is exactly the opposite order of how they are added to the stack. + +
+ +--- + +#### in-order + +
+ +- `left -> node -> right` + +
+ +```python +def inorder_recursive(root): + + if root is None: + return [] + + return inorder_recursive(root.left) + [root.val] + inorder_recursive(root.right) + + +def inorder_iterative(root): + + node = root + result, stack = [], [] + + while stack or node: + + if node: + stack.append(node) + node = node.left + else: + node = stack.pop() + result.append(node.val) + node = node.right + + return result +``` + +
+ + +* we can also build an interator: + +
+ +```python +class inorder_iterator: + + def __init__(self, root): + self.stack = [] + self.left_inorder(root) + + def left_inorder(self, root): + while root: + self.stack.append(root) + root = root.left + + def next(self) -> int: + node = self.stack.pop() + if node.right: + self.left_inorder(node.right) + return node.val + + def has_next(self) -> bool: + return len(self.stack) > 0 +``` + +
+ + +- in a binary search tree, in-order traversal will be sorted in the ascending order. + +- converting a sorted array to a binary search tree with inorder has no unique solution (in another hand, both preorder and postorder are unique identifiers of a bst). + +
+ +--- + +#### pre-order + +
+ +- `node -> left -> right` + +
+ +```python +def preorder_recursive(root): + + if root is None: + return [] + + return [root.val] + preorder(root.left) + preorder(root.right) + + +def preorder_iterative(root) -> list: + + result = [] + stack = [root] + + while stack: + + node = stack.pop() + + if node: + result.append(node.val) + stack.append(node.right) # note the order (stacks are fifo) + stack.append(node.left) + + return result +``` + +
+ +* note that preorder dfs looks similar to bfs, but using a stack instead of queue, and calling `node.right` first than `node.left` (as it pops in the right not in the left). + +
+ +--- + +#### post-order + +
+ +- `left -> right -> node` + +- deletion process is always post-order: when you delete a node, you will delete its left child and its right child before you delete the node itself. + +- post-order can be used in mathematical expressions as it's easier to write a program to parse a post-order expression. + - using a stack, each time when you meet an operator, you can just pop 2 elements from the stack, calculate the result and push the result back into the stack. + +
+ +```python +def postorder(root): + + if root is None: + return [] + + return postorder(root.left) + postorder(root.right) + [root.val] + + +def postorder_iterative(root): + + node = root + stack, result = [], [] + + while node or stack: + + while node: + stack.append(node.right) + stack.append(node) + node = node.left + + node = stack.pop() + + if stack and node.right == stack[-1]: + stack[-1] = node + node = node.right + + else: + result.append(node.val) + node = None + + return result +``` + +
+ + +---- + +### is same tree? + +
+ +```python +def is_same_trees(p, q): + + if not p and not q: + return True + + if (not p and q) or (not q and p): + return False + + if p.val != q.val: + return False + + return is_same_trees(p.right, q.right) and is_same_trees(p.left, q.left) +```` + +
+ +--- + +### is symmetric? + +
+ +```python +def is_symmetric(root): + + stack = [(root, root)] + + while stack: + + node1, node2 = stack.pop() + + if (not node1 and node2) or (not node2 and node1): + return False + + if node1 and node2: + + if node1.val != node2.val: + return False + + stack.append([node1.left, node2.right]) + stack.append([node1.right, node2.left]) + + return True + + +def is_symmetric_recursive(root) -> bool: + + def helper(node1, node2): + if (not node1 and node2) or \ + (not node2 and node1) or \ + (node1 and node2 and node1.val != node2.val): + return False + + if (not node1 and not node2): + return True + + return helper(node1.left, node2.right) and helper(node2.left, node1.right) + + return helper(root.left, root.right) +``` + +
+ +--- + +### lowest common ancestor + +
+ +```python +def lowest_common_ancestor(root, p, q): + + stack = [root] + parent = {root: None} + + while p not in parent or q not in parent: + + node = stack.pop() + if node: + parent[node.left] = node + parent[node.right] = node + stack.append(node.left) + stack.append(node.right) + + ancestors = set() + while p: + ancestors.add(p) + p = parent[p] + + while q not in ancestors: + q = parent[q] + + return q +``` + +
+ +--- + +### has path sum? + +
+ + +```python +def has_path_sum(root, target_sum) -> bool: + + def transverse(node, sum_here=0): + + if not node: + return sum_here == target_sum + + sum_here += node.val + + if not node.left: + return transverse(node.right, sum_here) + if not node.right: + return transverse(node.left, sum_here) + else: + return transverse(node.left, sum_here) or transverse(node.right, sum_here) + + if not root: + return False + + return transverse(root) +``` + +
+ +--- + +### build tree from inorder with preorder or postorder + +
+ +* building with preorder: + +
+ +```python +def build_tree(preorder, inorder): + + def helper(left, right, index_map): + + if left > right: + return None + + node = preorder.pop(0) # this order change from postorder + root = Node(node.val) + index_here = index_map[node.val] + + root.left = helper(left, index_here - 1, index_map) # this order change from postorder + root.right = helper(index_here + 1, right, index_map) + + return root + + index_map = {value: i for i, value in enumerate(inorder)} + + return helper(0, len(inorder) - 1, index_map) +``` + +
+ +* build with postorder: + +
+ +```python +def build_tree(left, right, index_map, postorder): + + if left > right: + return None + + node = postorder.pop() # this order change from preorder + root = Node(node.val) + index_here = index_map[node.val] + + root.right = build_tree(index_here + 1, right, index_map, postorder) # this order change from preorder + root.left = build_tree(left, index_here - 1, index_map, postorder) + + return root + + +def build_tree(inorder, postorder): + + index_map = {val: i for i, value in enumerate(inorder)} + return fill_tree(0, len(inorder) - 1, index_map, postorder) +``` + + +
+ +--- + +### return number of unival subtrees + +
+ +* a unival subtree means all nodes of the subtree have the same value + +
+ +```python +def count_unival(root) -> int: + + global count = 0 + + def dfs(node): + if node is None: + return True + + if dfs(node.left) and dfs(node.right): + if (node.left and node.left.val != node.val) or \ + (node.right and node.right.val != node.val): + return False + self.count += 1 + return True + + return False + + dfs(root) + return count +``` + +
+ +--- + +### successors and precessors + +
+ +```python + +def successor(root): + + root = root.right + while root: + root = root.left + + return root + + +def predecessor(root): + + root = root.left + while root: + root = root.right + + return root +``` + +
+ + +---- + +### binary search trees + +
+ +* **binary search trees** are binary trees where all nodes on the left are smaller than the root, which is smaller than all nodes on the right. + +* if a bst is **balanced**, it guarantees `O(log(N))` for `insert` and `search` (as we keep the tree's height as `h = log(N)`). + +* common types of balanced trees are **red-black** and **avl**. + + + +
+ +--- + +### insert a node + +
+ +* to insert a node, the main strategy is to find a proper leaf position for the target (therefore, insertion will begin as a search). + +* the time complexity is `O(h)` where `h` is the tree height. that results in `O(log(N))` in the average case, and `O(N)` worst case. + +
+ +```python +def bst_insert_iterative(root, val): + + node = root + while node: + + if val > node.val: + if not node.right: + node.right = Node(val) + break + else: + node = node.right + + else: + if not node.left: + node.left = Node(val) + break + else: + node = node.left + + return root + + +def bst_insert_recursive(root, val): + + if root is None: + return Node(val) + + if val > root.val: + root.right = self.bst_insert_recursive(root.right, val) + + else: + root.left = self.bst_insert_recursive(root.left, val) + + return root +``` + +
+ + +--- + +### delete a node + +
+ +* deletion is a more complicated operation, and there are several strategies. + +* one of them is to replace the target node with a proper child: + - if the target node has no child (it's a leaf): simply remove the node + - if the target node has one child, use the child to replace the node + - if the target node has two child, replace the node with its in-order successor or predecessor node and delete the node + +* similar to the recursion solution of the search operation, the time complexity is `O(h)` in the worst case. + +* according to the depth of recursion, the space complexity is also `O(h)` in the worst case. we can also represent the complexity using the total number of nodes `N`. + +* the time complexity and space complexity will be `O(log(N))` in the best case but `O(N)` in the worse case. + + +
+ +```python + +def delete_node(root, key): + + if root is None: + return root + + if key > root.val: + root.right = delete_node(root.right, key) + + elif key < root.val: + root.left = delete_node(root.left, key) + + else: + if not (root.left or root.right): + root = None + + elif root.right: + root.val = successor(root) + root.right = delete_node(root.right, root.val) + + else: + root.val = predecessor(root) + root.left = delete_node(root.left, root.val) + + return root +``` + +
+ +--- + +### search for a value + +
+ +* for the recursive solution, the depth of the recursion is equal to the height of the tree in the worst case. therefore, the time complexity would be `O(h)`. the space complexity is also `O(h)`. + +* for the iterative solution, the time complexity is equal to the loop time which is also `O(h)`, while the space complexity is `O(1)`. + +
+ +```python +def search_bst_recursive(root, val): + + if root is None or root.val == val: + return root + if val > root.val: + return search_bst_recursive(root.right, val) + else: + return search_bst_recursive(root.left, val) + + +def search_bst_iterative(root, val): + + while root: + + if root.val == val: + break + if root.val < val: + root = root.right + else: + root = root.left + + return root +``` + + +
+ +--- + +### find successor of two nodes inorder + +
+ +```python +def find_successor(node1, node2): + + successor = None + + while node1: + + if node1.val <= node2.val: + node1 = node1.right + else: + successor = node1 + node1 = node1.left + + return successor +``` + +
+ + +--- + +### convert sorted array to bst + +
+ +* note that there is no unique solution. + +
+ +```python +def convert_sorted_array_to_bst(nums): + + def helper(left, right): + + if left > right: + return None + + p = (left + right) // 2 + + root = Node(nums[p]) + root.left = helper(left, p - 1) + root.right = helper(p + 1, right) + + return root + + return helper(0, len(nums) - 1) +``` + +
+ + +--- + +### lowest common ancestor for a bst + +
+ +```python +def lowest_common_ancestor(root, p, q): + + node, result = root, root + + while node: + + result = node + + if node.val > p.val and node.val > q.val: + node = node.left + elif node.val < p.val and node.val < q.val: + node = node.right + else: + break + + return result +``` + +
+ +--- + +### checking if bst is valid + +
+ +```python +def is_valid_bst_iterative(root): + + queue = deque((root, float(-inf), float(inf))) + + while queue: + + node, min_val, max_val = queue.popleft() + + if node: + if min_val >= node.val or node.val >= max_val: + return False + queue.append((node.left, min_val, node.val)) + queue.append((node.right, node.val, max_val)) + + return True + + +def is_valid_bst_recursive(root, min_val=float(-inf), max_val=float(inf)): + + if root is None: + return True + + return (min_val < root.val < max_val) and \ + is_valid_bst_recursive(root.left, min_val, root.val) and \ + is_valid_bst_recursive(root.right, root.val, max_val) + + +def is_valid_bst_inorder(root): + + def inorder(node): + if node is None: + return True + + inorder(node.left) + stack.append(node.val) + inorder(node.right) + + stack = [] + inorder(root) + + for i in range(1, len(stack)): + if queue[i] <= queue[i - 1]: + return False + + return True +``` + +
+ diff --git a/trees/bs_sucessor_and_precessor.py b/trees/bs_sucessor_and_precessor.py new file mode 100644 index 0000000..e7e8c45 --- /dev/null +++ b/trees/bs_sucessor_and_precessor.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def successor(root): + + root = root.right + while root: + root = root.left + + return root + + +def predecessor(root): + + root = root.left + while root: + root = root.right + + return root diff --git a/trees/bst_avl.py b/trees/bst_avl.py new file mode 100644 index 0000000..134587f --- /dev/null +++ b/trees/bst_avl.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +class Node(): + def __init__(self, val): + self.val = val + self.left = None + self.right = None + self.height = 1 + + +class AVLTree(): + + def __init__(self): + self.root = None + self.size = 0 + + def height(self, node): + if node: + return node.height + return 0 + + def set_height(self, node): + if node is None: + return 0 + return 1 + max(self.height(node.left), self.height(node.right)) + + def right_rotate(self, node): + new_root = node.left + node.left = node.left.right + new_root.right = node + node.height = self.set_height(node) + new_root.height = self.set_height(new_root) + return new_root + + def left_rotate(self, node): + new_root = node.right + node.right = node.right.left + new_root.left = node + node.height = self.set_height(node) + new_root.height = self.set_height(new_root) + return new_root + + def insert(self, node, val): + if node == self.root: + self.size += 1 + if node is None: + return Node(val) + if node.val < val: + node.right = self.insert(node.right, val) + else: + node.left = self.insert(node.left, val) + balance = self.height(node.left) - self.height(node.right) + if balance > 1: + if self.height(node.left.left) > self.height(node.left.right): + node = self.right_rotate(node) + else: + node.left = self.left_rotate(node.left) + node = self.right_rotate(node) + elif balance < -1: + if self.height(node.right.right) > self.height(node.right.left): + node = self.left_rotate(node) + else: + node.right = self.right_rotate(node.right) + node = self.left_rotate(node) + else: + node.height = self.set_height(node) + return node + + def get_min_val(self, node): + if node is None or node.left is None: + return node + return self.get_min_val(node.left) + + def remove(self, node, val): + if node is None: + return node + if node.val < val: + node.right = self.remove(node.right, val) + elif node.val > val: + node.left = self.remove(node.left, val) + else: + if node.left is None: + return node.right + elif node.right is None: + return node.left + else: + right_min_val_node = self.get_min_val(node.right) + node.val = right_min_val_node.val + node.right = self.remove(node.right, right_min_val_node.val) + + node.height = self.set_height(node) + balance = self.height(node.left) - self.height(node.right) + if balance > 1: + if self.height(node.left.left) > self.height(node.left.right): + node = self.right_rotate(node) + else: + node.left = self.left_rotate(node.left) + node = self.right_rotate(node) + elif balance < -1: + if self.height(node.right.right) > self.height(node.right.left): + node = self.left_rotate(node) + else: + node.right = self.right_rotate(node.right) + node = self.left_rotate(node) + else: + node.height = self.set_height(node) + return node + + def predecessor(self, node, val): + if node is None: + return node + if node.val == val: + return val + elif node.val > val: + return self.predecessor(node.left, val) + else: + right_res = self.predecessor(node.right, val) + return right_res if right_res else node.val + + def successor(self, node, val): + if node is None: + return node + if node.val == val: + return val + elif node.val < val: + return self.successor(node.right, val) + else: + left_res = self.successor(node.left, val) + return left_res if left_res else node.val + + +def contains_duplicate_near(self, nums, k, t): + + avltree = AVLTree() + root = avltree.root + + + for i, num in enumerate(nums): + predecessor = avltree.predecessor(root, num) + if predecessor is not None and abs(predecessor - num) <= t: + return True + successor = avltree.successor(root, num) + if successor is not None and abs(successor - num) <= t: + return True + + root = avltree.insert(root, num) + + if avltree.size > k: + root = avltree.remove(root, nums[i-k]) + + return False + diff --git a/trees/bst_convert_sorted_array.py b/trees/bst_convert_sorted_array.py new file mode 100644 index 0000000..1d7a7db --- /dev/null +++ b/trees/bst_convert_sorted_array.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def convert_sorted_array_to_bst(nums): + + def helper(left, right): + + if left > right: + return None + + p = (left + right) // 2 + + root = Node(nums[p]) + root.left = helper(left, p - 1) + root.right = helper(p + 1, right) + + return root + + return helper(0, len(nums) - 1) + diff --git a/trees/bst_delete_node.py b/trees/bst_delete_node.py new file mode 100644 index 0000000..de122e3 --- /dev/null +++ b/trees/bst_delete_node.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def successor(root): + + root = root.right + while root.left: + root = root.left + return root.val + + +def predecessor(root): + + root = root.left + while root.right: + root = root.right + return root.val + + +def delete_node(root, key): + + if root is None: + return root + + if key > root.val: + root.right = delete_node(root.right, key) + + elif key < root.val: + root.left = delete_node(root.left, key) + + else: + if not (root.left or root.right): + root = None + + elif root.right: + root.val = successor(root) + root.right = delete_node(root.right, root.val) + + else: + root.val = predecessor(root) + root.left = delete_node(root.left, root.val) + + return root + diff --git a/trees/bst_insert_node.py b/trees/bst_insert_node.py new file mode 100644 index 0000000..65928ae --- /dev/null +++ b/trees/bst_insert_node.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def bst_insert_iterative(root, val): + + node = root + while node: + + if val > node.val: + if not node.right: + node.right = Node(val) + break + else: + node = node.right + + else: + if not node.left: + node.left = Node(val) + break + else: + node = node.left + + return root + + +def bst_insert_recursive(root, val): + + if root is None: + return Node(val) + + if val > root.val: + root.right = self.bst_insert_recursive(root.right, val) + + else: + root.left = self.bst_insert_recursive(root.left, val) + + return root diff --git a/trees/bst_is_valid.py b/trees/bst_is_valid.py new file mode 100644 index 0000000..3ff3199 --- /dev/null +++ b/trees/bst_is_valid.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def is_valid_bst_iterative(root): + + queue = deque((root, float(-inf), float(inf))) + + while queue: + + node, min_val, max_val = queue.popleft() + + if node: + if min_val >= node.val or node.val >= max_val: + return False + queue.append((node.left, min_val, node.val)) + queue.append((node.right, node.val, max_val)) + + return True + + +def is_valid_bst_recursive(root, min_val=float(-inf), max_val=float(inf)): + + if root is None: + return True + + return (min_val < root.val < max_val) and \ + is_valid_bst_recursive(root.left, min_val, root.val) and \ + is_valid_bst_recursive(root.right, root.val, max_val) + + +def is_valid_bst_inorder(root): + + def inorder(node): + if node is None: + return True + + inorder(node.left) + stack.append(node.val) + inorder(node.right) + + stack = [] + inorder(root) + + for i in range(1, len(stack)): + if queue[i] <= queue[i - 1]: + return False + + return True diff --git a/trees/bst_lowest_common_ancestor.py b/trees/bst_lowest_common_ancestor.py new file mode 100644 index 0000000..db1a945 --- /dev/null +++ b/trees/bst_lowest_common_ancestor.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def lowest_common_ancestor(root, p, q): + + node, result = root, root + + while node: + + result = node + + if node.val > p.val and node.val > q.val: + node = node.left + + elif node.val < p.val and node.val < q.val: + node = node.right + + else: + break + + return result diff --git a/trees/bst_search.py b/trees/bst_search.py new file mode 100644 index 0000000..648a82c --- /dev/null +++ b/trees/bst_search.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def search_bst_recursive(root, val): + + if root is None or root.val == val: + return root + + if val > root.val: + return search_bst_recursive(root.right, val) + + else: + return search_bst_recursive(root.left, val) + + + +def search_bst_iterative(root, val): + + while root: + + if root.val == val: + break + + if root.val < val: + root = root.right + + else: + root = root.left + + return root diff --git a/trees/bst_sucessor_two_nodes.py b/trees/bst_sucessor_two_nodes.py new file mode 100644 index 0000000..200a893 --- /dev/null +++ b/trees/bst_sucessor_two_nodes.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def find_successor(root, p): + + successor = None + + while root: + + if root.val <= p.val: + root = root.right + else: + successor = root + root = root.left + + return successor diff --git a/trees/bt.py b/trees/bt.py new file mode 100644 index 0000000..a44aec1 --- /dev/null +++ b/trees/bt.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +class Node(object): + + def __init__(self, value): + self.value = value + self.right = None + self.left = None + + def add(self, value): + new_node = Node(value) + if not self.left: + self.left = new_node + elif not self.right: + self.right = new_node + else: + self.left = self.left.add(value) + + def search(self, item): + return self.value == item or \ + (self.left and self.left.search(item)) or \ + (self.right and self.right.search(item)) + + def preorder(self): + yield self.value + if self.left: + for node in self.left.preorder(): + yield node + if self.right: + for node in self.right.preorder(): + yield node + + def postorder(self): + yield self.value + if self.left: + for node in self.left.postorder(): + yield node + if self.right: + for node in self.right.postorder(): + yield node + + def inorder(self): + yield self.value + if self.left: + for node in self.left.inorder(): + yield node + if self.right: + for node in self.right.inorder(): + yield node + + +class BinaryTree(object): + def __init__(self): + self.root = None + + def add(self, value): + if not self.root: + self.root = Node(value) + else: + self.root.add(value) + + def search(self, item): + if self.root: + return self.root.search(item) + + def preorder(self): + if self.root: + return list(self.root.preorder()) + + def inorder(self): + if self.root: + return list(self.root.inorder()) + + def postorder(self): + if self.root: + return list(self.root.postorder()) + + +if __name__ == '__main__': + + + print("\n\n🌳🌳🌳 Testing BinaryTree 🌳🌳🌳") + bt = BinaryTree() + array1 = [4, 1, 4, 6, 7, 9, 10, 5, 11, 5] + print(f'\n🟔 Adding {array1} to the tree...') + for i in array1: + bt.add(i) + print(f"🟢 Print the tree preorder: {bt.preorder()}") + print(f"🟢 Print the tree inorder: {bt.inorder()}") + print(f"🟢 Print the tree postorder: {bt.postorder()}") + + print(f'\n🟢 Search for node 5: {bt.search(5)}') + print(f'āŒ Search for node 15: {bt.search(15)}') diff --git a/trees/bt_bfs.py b/trees/bt_bfs.py new file mode 100644 index 0000000..be713c6 --- /dev/null +++ b/trees/bt_bfs.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def bfs(root) -> list: + + result = [] + queue = collections.deque([root]) + + while queue: + + node = queue.popleft() + + if node: + result.append(node.val) + queue.append(node.left) + queue.append(node.right) + + return result diff --git a/trees/bt_construct_inorder_postorder.py b/trees/bt_construct_inorder_postorder.py new file mode 100644 index 0000000..eabd644 --- /dev/null +++ b/trees/bt_construct_inorder_postorder.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def build_tree(left, right, index_map, postorder): + + if left > right: + return None + + node = postorder.pop() # this order change from preorder + root = Node(node.val) + index_here = index_map[node.val] + + root.right = build_tree(index_here + 1, right, index_map, postorder) # this order change from preorder + root.left = build_tree(left, index_here - 1, index_map, postorder) + + return root + + +def build_tree(inorder, postorder): + + index_map = {val: i for i, value in enumerate(inorder)} + + return fill_tree(0, len(inorder) - 1, index_map, postorder) + diff --git a/trees/bt_construct_inorder_preorder.py b/trees/bt_construct_inorder_preorder.py new file mode 100644 index 0000000..9087fe5 --- /dev/null +++ b/trees/bt_construct_inorder_preorder.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def build_tree(preorder, inorder): + + def helper(left, right, index_map): + + if left > right: + return None + + node = preorder.pop(0) # this order change from postorder + root = Node(node.val) + index_here = index_map[node.val] + + root.left = helper(left, index_here - 1, index_map) # this order change from postorder + root.right = helper(index_here + 1, right, index_map) + + return root + + index_map = {value: i for i, value in enumerate(inorder)} + + return helper(0, len(inorder) - 1, index_map) + diff --git a/trees/bt_count_unival.py b/trees/bt_count_unival.py new file mode 100644 index 0000000..f68f846 --- /dev/null +++ b/trees/bt_count_unival.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + + +def count_unival(root) -> int: + + global count = 0 + + def dfs(node): + if node is None: + return True + + if dfs(node.left) and dfs(node.right): + if (node.left and node.left.val != node.val) or \ + (node.right and node.right.val != node.val): + return False + self.count += 1 + return True + + return False + + dfs(root) + return count diff --git a/trees/bt_cute.py b/trees/bt_cute.py new file mode 100644 index 0000000..b1b3826 --- /dev/null +++ b/trees/bt_cute.py @@ -0,0 +1,296 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +class SimpleTree: + """Implementation of a simple tree""" + + def __init__(self, value=None, node=None): + self.value = value + self.node = node or [] + + def __repr__(self, level=0): + repr = '\t' * level + self.value + '\n' + for n in self.node: + repr += n.__repr__(level + 1) + return repr + + +class Node(): + """Implementation of a Node for a binary tree""" + + def __init__(self, value=None): + self.value = value + self.left = None + self.right = None + + ############################ + # Private Methods + ############################ + def __repr__(self): + """Prints the node""" + return f'{self.value}' + + def _repr_preorder(self): + """Prints the tree in preorder traversal (root, left, right)""" + + print(self.value) + if self.left: + self.left._repr_preorder() + if self.right: + self.right._repr_preorder() + + def _add_node_binary_tree(self, value): + """Adds a node to a simple binary tree""" + + if self.value is None: + self.value = value + + else: + if not self.left: + self.left = Node(value) + elif not self.right: + self.right = Node(value) + else: + self.left = self.left._add_node_binary_tree(value) + + return self + + def _add_node_binary_search_tree(self, value) -> bool: + """Adds a node to a binary search tree""" + + if not self.value: + self.value = value + + else: + new_node = Node(value) + if value < self.value: + self.left = self.left and self.left._add_node_binary_search_tree(value) or new_node + elif value > self.value: + self.right = self.right and self.right._add_node_binary_search_tree(value) or new_node + else: + print(f'āŒ Item {value} not added as BSTs do not support repetition.') + + return self + + def _search_node_preorder(self, query) -> bool: + """Searches through preorder traversal (node, left, right)""" + + if self.value == query: + return True + + found = False + if self.left: + # recursively search left + found = self.left._search_node_preorder(query) + + if self.right: + # recursively search right + found = found or self.right._search_node_preorder(query) + + return found + + def _search_node_binary_search_tree(self, query) -> bool: + """Searches the tree for a value, considering the BST property""" + + this_node_value = self.value + + if this_node_value is not None: + if this_node_value == query: + return True + + elif this_node_value > query: + if self.left is not None: + return self.left._search_node_binary_search_tree(query) + + elif this_node_value < query: + if self.right is not None: + return self.right._search_node_binary_search_tree(query) + + return False + + def _is_leaf(self) -> bool: + """If node has no children, it is a leaf""" + + return bool(not self.right and not self.left) + + def _is_full(self) -> bool: + """If node has two children, it is full""" + return bool(self.right and self.left) + + +class BinaryTreeInterface(): + + def __init__(self): + self.root = Node() + + ############################ + # Interface Methods + ############################ + def add_node(self, value): + """Adds a new node to the tree""" + pass + + def search_node(self, value): + """Searches the tree for a value""" + pass + + ############################ + # Public Methods + ############################ + def is_leaf(self) -> bool: + """Returns True if the node is a leaf""" + + if self.root is not None: + return self.root._is_leaf() + + def is_full(self) -> bool: + """Returns True if the node is full""" + + if self.root is not None: + return self.root._is_full() + + def print_preorder(self): + """Prints the BST in preorder""" + + if self.root is not None: + self.root._repr_preorder() + + ############################ + # Class Methods + ############################ + @classmethod + def is_balanced(cls, node, left=0, right=0) -> bool: + """Returns True if the tree is balanced""" + + if node is None: + return (left - right) < 2 + + else: + return cls.is_balanced(node.left, left + 1, right) and \ + cls.is_balanced(node.right, left, right + 1) + + @classmethod + def is_binary_search_tree(cls, node, min_node=None, max_node=None) -> bool: + """Returns True if the tree is a BST""" + + min_node = min_node or float('-inf') + max_node = max_node or float('inf') + + if not node: + return True + + if node.value < min_node or node.value > max_node: + return False + + return cls.is_binary_search_tree(node.left, min_node, node.value) and \ + cls.is_binary_search_tree(node.right, node.value, max_node) + + + +class BinaryTree(BinaryTreeInterface): + """Implementation of a binary tree""" + + def add_node(self, value): + """Adds a new node to the tree""" + + if self.root is None: + self.root = Node(value) + else: + self.root._add_node_binary_tree(value) + + def search_node(self, value): + """Searches the tree for a value""" + + if self.root: + return self.root._search_node_preorder(value) + + +class BinarySearchTree(BinaryTreeInterface): + + def add_node(self, value): + """Adds a new node to the tree""" + + if self.root is None: + self.root = Node(value) + else: + self.root._add_node_binary_search_tree(value) + + def search_node(self, value): + """Searches the tree for a value""" + + if self.root.value is not None: + return self.root._search_node_binary_search_tree(value) + + @classmethod + def largest_node(cls, node): + """Returns the largest node in the tree""" + + if node.right: + return cls.largest_node(node.right) + else: + return node + + @classmethod + def smallest_node(cls, node): + """Returns the smallest node in the tree""" + + if node.left: + return cls.smallest_node(node.left) + else: + return node + + + + +if __name__ == '__main__': + + ############################ + # Test SimpleTree + ############################ + print("\n\n🌓🌓🌓 Testing SimpleTree 🌓🌓🌓") + t = SimpleTree('a', [SimpleTree('b', [SimpleTree('d'), SimpleTree('e')]), \ + SimpleTree('c', [SimpleTree('h'), SimpleTree('g')])]) + print(t) + + + ############################ + # Test binary tree + ############################ + print("\n\n🌳🌳🌳 Testing BinaryTree 🌳🌳🌳") + bt = BinaryTree() + array1 = [4, 1, 4, 6, 7, 9, 10, 5, 11, 5] + print(f'\n🟔 Adding {array1} to the tree...') + for i in array1: + bt.add_node(i) + print("🟢 Printing the tree in preorder...") + bt.print_preorder() + print(f'\n🟢 Searching for node 5: {bt.search_node(5)}') + print(f'āŒ Searching for node 15: {bt.search_node(15)}') + print(f'āŒ Is root a leaf? {bt.is_leaf()}') + print(f'🟢 Is root full? {bt.is_full()}') + print(f'āŒ Is the tree balanced? {BinaryTree.is_balanced(bt.root)}') + print(f'āŒ Is the tree a binary search tree? {BinaryTree.is_binary_search_tree(bt.root)}') + + + + ############################## + # Test binary search tree + ############################## + print("\n\nšŸŽ„šŸŽ„šŸŽ„ Testing BinarySearchTree šŸŽ„šŸŽ„šŸŽ„") + bst = BinarySearchTree() + array1 = [4, 1, 4, 6, 7, 9, 10, 5, 11, 5] + print(f'\n🟔 Adding {array1} to the tree...') + for i in array1: + bst.add_node(i) + print("🟢 Printing the tree in preorder:") + bst.print_preorder() + print(f'\n🟢 Searching for node 5: {bst.search_node(5)}') + print(f'āŒ Searching for node 15: {bst.search_node(15)}') + print(f'āŒ Is root a leaf? {bst.is_leaf()}') + print(f'🟢 Is root full? {bst.is_full()}') + print(f'🟢 Largest node? {bst.largest_node(bst.root)}') + print(f'🟢 Smallest node? {bst.smallest_node(bst.root)}') + print(f'āŒ Is the tree balanced? {bst.is_balanced(bst.root)}') + print(f'🟢 Is the tree a binary search tree? {bst.is_binary_search_tree(bst.root)}') diff --git a/trees/bt_find_max_depth.py b/trees/bt_find_max_depth.py new file mode 100644 index 0000000..dbe2109 --- /dev/null +++ b/trees/bt_find_max_depth.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def max_depth(root) -> int: + + if root is None: + return 0 + + return max(max_depth(root.left) + 1, max_depth(root.right) + 1) + diff --git a/trees/bt_has_path_sum.py b/trees/bt_has_path_sum.py new file mode 100644 index 0000000..b58d8af --- /dev/null +++ b/trees/bt_has_path_sum.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def has_path_sum(root, target_sum) -> bool: + + def transverse(node, sum_here=0): + + if not node: + return sum_here == target_sum + + sum_here += node.val + + if not node.left: + return transverse(node.right, sum_here) + if not node.right: + return transverse(node.left, sum_here) + else: + return transverse(node.left, sum_here) or transverse(node.right, sum_here) + + if not root: + return False + + return transverse(root) + diff --git a/trees/bt_height.py b/trees/bt_height.py new file mode 100644 index 0000000..b10b178 --- /dev/null +++ b/trees/bt_height.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def height(root): + + if root is None: + return 0 + + return 1 + max(height(root.left), height(root.right)) + diff --git a/trees/bt_inorder.py b/trees/bt_inorder.py new file mode 100644 index 0000000..6a955cf --- /dev/null +++ b/trees/bt_inorder.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def inorder(root) -> list: + + if root is None: + return [] + + return inorder(root.left) + [root.val] + inorder(root.right) + + +def inorder_iterative(root) -> list: + + result = [] + stack = [] + node = root + + while stack or node: + + if node: + stack.append(node) + node = node.left + else: + node = stack.pop() + result.append(node.val) + node = node.right + + return result + diff --git a/trees/bt_inorder_iterator.py b/trees/bt_inorder_iterator.py new file mode 100644 index 0000000..3006da9 --- /dev/null +++ b/trees/bt_inorder_iterator.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +class Node: + def __init__(self, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class InorderIterator: + + def __init__(self, root): + self.stack = [] + self.left_inorder(root) + + def left_inorder(self, root): + while root: + self.stack.append(root) + root = root.left + + def next(self) -> int: + top_node = self.stack.pop() + if top_node.right: + self.left_inorder(top_node.right) + return top_node.val + + def has_next(self) -> bool: + return len(self.stack) > 0 + diff --git a/trees/bt_is_balanced.py b/trees/bt_is_balanced.py new file mode 100644 index 0000000..1d3772b --- /dev/null +++ b/trees/bt_is_balanced.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def height(root): + + if root is None: + return -1 + + return 1 + max(height(root.left), height(root.right)) + + +def is_balanced(root): + + if root is None: + return True + + return abs(height(root.left) - height(root.right)) < 2 and \ + is_balanced(root.left) and is_balanced(root.right) + diff --git a/trees/bt_is_same_trees.py b/trees/bt_is_same_trees.py new file mode 100644 index 0000000..8cd7a28 --- /dev/null +++ b/trees/bt_is_same_trees.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def is_same_trees(p, q): + + if not p and not q: + return True + + if (not p and q) or (not q and p) or p.val != q.val: + return False + + return is_same_trees(p.right, q.right) and is_same_trees(p.left, q.left) diff --git a/trees/bt_is_tree_symmetric.py b/trees/bt_is_tree_symmetric.py new file mode 100644 index 0000000..27f3faa --- /dev/null +++ b/trees/bt_is_tree_symmetric.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def is_symmetric(root) -> bool: + + stack = [(root, root)] + + while stack: + + node1, node2 = stack.pop() + + if (not node1 and node2) or (not node2 and node1): + return False + + elif node1 and node2 and node1.val != node2.val: + return False + + elif not node1 and not node2: + continue + + stack.append([node1.left, node2.right]) + stack.append([node1.right, node2.left]) + + return True + + +def is_symmetric_recursive(root) -> bool: + + def helper(node1, node2): + if (not node1 and node2) or \ + (not node2 and node1) or \ + (node1 and node2 and node1.val != node2.val): + return False + + if (not node1 and not node2): + return True + + return helper(node1.left, node2.right) and helper(node2.left, node1.right) + + return helper(root.left, root.right) + diff --git a/trees/bt_lowest_common_ancestor.py b/trees/bt_lowest_common_ancestor.py new file mode 100644 index 0000000..429b41e --- /dev/null +++ b/trees/bt_lowest_common_ancestor.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def lowest_common_ancestor(root, p, q): + + stack = [root] + parent = {root: None} + + while p not in parent or q not in parent: + + node = stack.pop() + if node: + parent[node.left] = node + parent[node.right] = node + stack.append(node.left) + stack.append(node.right) + + ancestors = set() + while p: + ancestors.add(p) + p = parent[p] + + while q not in ancestors: + q = parent[q] + + return q + diff --git a/trees/bt_postorder.py b/trees/bt_postorder.py new file mode 100644 index 0000000..d3aa1a1 --- /dev/null +++ b/trees/bt_postorder.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +def postorder(root) -> list: + + if root is None: + return [] + + return postorder(root.left) + postorder(root.right) + [root.val] + + +def postorder_iterative(root) -> list: + + stack, result = [], [] + node = root + + while node or stack: + + while node: + if node.right: + stack.append(node.right) + stack.append(node) + node = node.left + + node = stack.pop() + + if stack and node.right == stack[-1]: + stack[-1] = node + node = node.right + + else: + result.append(node.val) + node = None + + return result + diff --git a/trees/bt_preorder.py b/trees/bt_preorder.py new file mode 100644 index 0000000..352cc05 --- /dev/null +++ b/trees/bt_preorder.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +def preorder(root) -> list: + + if root is None: + return [] + + return [root.val] + preorder(root.left) + preorder(root.right) + + +def preorder_iterative(root) -> list: + + result = [] + stack = [root] + + while stack: + + node = stack.pop() + + if node: + result.append(node.val) + stack.append(node.right) # not the order + stack.append(node.left) + + return result diff --git a/tries/README.md b/tries/README.md new file mode 100644 index 0000000..81e478c --- /dev/null +++ b/tries/README.md @@ -0,0 +1,202 @@ +## tries + +
+ +* tries, also called prefix tree, are a variant of n-ary tree in which characters are stored in each node. they are used to make searching and storing more efficient, as search, insert, and remove are `O(m)` (`m` being the length of the string). + +* tries structures can be represented by arrays and maps or trees. + * comparying with a hash table, they lose in terms of time complexity, as hash table insert is usually `O(1)` (worst case `O(log(N))`, and trie's are `O(m)` (where `m` is the maximum length of a key). + * however, trie wins in terms of space complexity. both `O(m * N)` in theory, but tries can be much smaller as there will be a lot of words that have similar prefix. + + + +* each trie node represents a string (a prefix) and each path down the tree represents a word. note that not all the strings represented by trie nodes are meaningful. the root is associated with the empty string. + * the `*` nodes (`None` nodes) are often used to indicate complete words (usually represented by a special type of child) or a boolean flag that terminates the parent node. + * a node can have anywhere from 1 through `alphabet_size + 1` child. + +* tries can be used to store the entire english language for quick prefix lookup. they are also widely used on autocompletes, spell checkers, and ip routing (longest prefix matching). + + + +
+ +```python +class Trie: + + def __init__(self): + self.root = {} + + def insert(self, word: str)L + node = self.root + for c in word: + if c not in node: + node[c] = {} + node = node[c] + node['$'] = None + + def match(self, seq, prefix=False): + node = self.root + for c in seq: + if c not in node: + return False + node = node[c] + return prefix or ('$' in node) + + def search(self, word: str) -> bool: + return self.match(word) + + def starts_with(self, prefix: str) -> bool: + return self.match(prefix, True) +``` + +
+ +---- + +### insertion + +
+ +* similar to a bst, when we insert a value to a trie, we need to decide which path to go depending on the target value we insert. + + +* the root node needs to be initialized before you insert strings. + +
+ + +--- + +### search + +
+ +* all the descendants of a node have a common prefix of the string associated with that node, so it should be easy to search if there are any words in the trie that starts with the given prefix. + + +* we go down the tree depending on the given prefix, once we cannot find the child node, the search fails. + + +* we can also search for a specific word rather than a prefix, treating this word as a prefix and searching in the same way as above. + + +* if the search succeeds, we need to check if the target word is only a prefix of words in the trie or if it's exactly a word (for example, by adding a boolean flag). + +
+ +#### bfs + +
+ +```python +def level_orders(root): + + if root is None: + return [] + + result = [] + queue = collections.deque([root]) + + while queue: + level = [] + + for _ in range(len(queue)): + node = queue.popleft() + level.append(node.val) + queue.extend(node.children) + result.append(level) + + return result +``` + +
+ +#### post order + +
+ +```python +def postorder(self, root: 'Node'): + + if root is None: + return [] + + stack, result = [root, ], [] + + while stack: + + node = stack.pop() + + if node is not None: + result.append(node.val) + + for c in node.children: + stack.append(c) + + return result[::-1] +``` + +
+ +#### pre-order + +
+ +```python +def preorder(root: 'Node'): + + if root is None: + return [] + + stack, result = [root, ], [] + + while stack: + + node = stack.pop() + result.append(node.val) + stack.extend(node.children[::-1]) + + return result +``` + +
+ +---- + +### max depth + +
+ +```python +def max_depth_recursive(root): + + if root is None: + return 0 + + if root.children: is None: + return 1 + + height = [max_depth_recursive(children) for children in root.children] + + return max(height) + 1 + + +def max_depth_iterative(root): + + stack, depth = [], 0 + + if root is not None: + stack.append((1, root)) + + while stack: + + this_depth, node = stack.pop() + + if node is not None: + + depth = max(depth, this_depth) + for c in node.children: + stack.append((this_depth + 1, c)) + + return depth +``` diff --git a/tries/trie.py b/tries/trie.py new file mode 100644 index 0000000..c5dd85f --- /dev/null +++ b/tries/trie.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + +class Trie: + + def __init__(self): + self.root = {} + + def insert(self, word: str) -> None: + node = self.root + for c in word: + if c not in node: + node[c] = {} + node = node[c] + node['$'] = None + + def match(self, seq, prefix=False) -> bool: + node = self.root + for c in seq: + if c not in node: + return False + node = node[c] + return prefix or ('$' in node) + + def search(self, word: str) -> bool: + return self.match(word) + + def starts_with(self, prefix: str) -> bool: + return self.match(prefix, True) diff --git a/tries/trie_bfs.py b/tries/trie_bfs.py new file mode 100644 index 0000000..378b0c9 --- /dev/null +++ b/tries/trie_bfs.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +class Node: + def __init__(self, val=None, children=None): + self.val = val + self.children = children + + +def level_orders(root): + + if root is None: + return [] + + result = [] + queue = collections.deque([root]) + + while queue: + level = [] + + for _ in range(len(queue)): + node = queue.popleft() + level.append(node.val) + queue.extend(node.children) + result.append(level) + + return result diff --git a/tries/trie_find_height.py b/tries/trie_find_height.py new file mode 100644 index 0000000..618ff8a --- /dev/null +++ b/tries/trie_find_height.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +class Node: + + def __init__(self, val=None, children=None): + self.val = val + self.children = children + + +def max_depth_recursive(root): + + if root is None: + return 0 + + if root.children: is None: + return 1 + + height = [max_depth_recursive(children) for children in root.children] + + return max(height) + 1 + + +def max_depth_iterative(root): + + stack, depth = [], 0 + + if root is not None: + stack.append((1, root)) + + while stack: + + this_depth, node = stack.pop() + + if node is not None: + + depth = max(depth, this_depth) + for c in node.children: + stack.append((this_depth + 1, c)) + + return depth diff --git a/tries/trie_postorder.py b/tries/trie_postorder.py new file mode 100644 index 0000000..07aa755 --- /dev/null +++ b/tries/trie_postorder.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def postorder(self, root: 'Node'): + + if root is None: + return [] + + stack, result = [root, ], [] + + while stack: + + node = stack.pop() + + if node is not None: + result.append(node.val) + + for c in node.children: + stack.append(c) + + return result[::-1] + diff --git a/tries/trie_preorder.py b/tries/trie_preorder.py new file mode 100644 index 0000000..6f2044e --- /dev/null +++ b/tries/trie_preorder.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# author: bt3gl + + +def preorder(root: 'Node'): + + if root is None: + return [] + + stack, result = [root, ], [] + + while stack: + + node = stack.pop() + result.append(node.val) + stack.extend(node.children[::-1]) + + return result