Repository: jaychsu/algorithm Branch: master Commit: 91892fd64281 Files: 638 Total size: 761.9 KB Directory structure: gitextract_pg3fa3pn/ ├── .editorconfig ├── .gitignore ├── .travis.yml ├── README.md ├── bin/ │ ├── test_javascript.js │ └── test_python.py ├── codewars/ │ ├── delete_occurrences_of_an_element_if_it_occurs_more_than_n_times.js │ ├── double_cola.js │ ├── iq_test.js │ ├── is_a_number_prime.js │ ├── multiply.js │ ├── rectangle_into_squares.js │ ├── reverse_words.js │ ├── simple_encryption_1_alternating_split.js │ ├── sudoku_solution_validator.js │ ├── take_a_ten_minute_walk.js │ ├── tribonacci_sequence.js │ ├── unique_in_order.js │ └── who_likes_it.js ├── leetcode/ │ ├── 101_symmetric_tree.py │ ├── 105_construct_binary_tree_from_preorder_and_inorder_traversal.py │ ├── 108_convert_sorted_array_to_binary_search_tree.py │ ├── 112_path_sum.py │ ├── 113_path_sum_ii.py │ ├── 116_populating_next_right_pointers_in_each_node.py │ ├── 117_populating_next_right_pointers_in_each_node_ii.py │ ├── 118_pascal_s_triangle.py │ ├── 11_container_with_most_water.py │ ├── 124_binary_tree_maximum_path_sum.py │ ├── 134_gas_station.py │ ├── 136_single_number.py │ ├── 149_max_points_on_a_line.py │ ├── 14_longest_common_prefix.py │ ├── 150_evaluate_reverse_polish_notation.py │ ├── 157_read_n_characters_given_read4.py │ ├── 160_intersection_of_two_linked_lists.py │ ├── 165_compare_version_numbers.py │ ├── 166_fraction_to_recurring_decimal.py │ ├── 169_majority_element.py │ ├── 171_excel_sheet_column_number.py │ ├── 172_factorial_trailing_zeroes.py │ ├── 174_dungeon_game.py │ ├── 179_largest_number.py │ ├── 17_letter_combinations_of_a_phone_number.py │ ├── 190_reverse_bits.py │ ├── 191_number_of_1_bits.py │ ├── 19_remove_nth_node_from_end_of_list.py │ ├── 202_happy_number.py │ ├── 204_count_primes.py │ ├── 208_implement_trie_prefix_tree.py │ ├── 20_valid_parentheses.py │ ├── 215_kth_largest_element_in_an_array.py │ ├── 217_contains_duplicate.py │ ├── 218_the_skyline_problem.py │ ├── 224_basic_calculator.py │ ├── 227_basic_calculator_ii.py │ ├── 229_majority_element_ii.py │ ├── 22_generate_parentheses.py │ ├── 230_kth_smallest_element_in_a_bst.py │ ├── 234_palindrome_linked_list.py │ ├── 235_lowest_common_ancestor_of_a_binary_search_tree.py │ ├── 236_lowest_common_ancestor_of_a_binary_tree.py │ ├── 237_delete_node_in_a_linked_list.py │ ├── 242_valid_anagram.py │ ├── 269_alien_dictionary.py │ ├── 277_find_the_celebrity.py │ ├── 280_wiggle_sort.py │ ├── 282_expression_add_operators.py │ ├── 285_inorder_successor_in_bst.py │ ├── 288_unique_word_abbreviation.py │ ├── 289_game_of_life.py │ ├── 295_find_median_from_data_stream.py │ ├── 299_bulls_and_cows.py │ ├── 303_range_sum_query_immutable.py │ ├── 304_range_sum_query_2d_immutable.py │ ├── 307_range_sum_query_mutable.py │ ├── 308_range_sum_query_2d_mutable.py │ ├── 30_substring_with_concatenation_of_all_words.py │ ├── 315_count_of_smaller_numbers_after_self.py │ ├── 323_number_of_connected_components_in_an_undirected_graph.py │ ├── 324_wiggle_sort_ii.py │ ├── 326_power_of_three.py │ ├── 329_longest_increasing_path_in_a_matrix.py │ ├── 334_increasing_triplet_subsequence.py │ ├── 33_search_in_rotated_sorted_array.py │ ├── 340_longest_substring_with_at_most_k_distinct_characters.py │ ├── 344_reverse_string.py │ ├── 347_top_k_frequent_elements.py │ ├── 348_design_tic_tac_toe.py │ ├── 353_design_snake_game.py │ ├── 36_valid_sudoku.py │ ├── 377_combination_sum_iv.py │ ├── 378_kth_smallest_element_in_a_sorted_matrix.py │ ├── 37_sudoku_solver.py │ ├── 380_insert_delete_getrandom_o1.py │ ├── 381_insert_delete_getrandom_o1_duplicates_allowed.py │ ├── 382_linked_list_random_node.py │ ├── 384_shuffle_an_array.py │ ├── 386_lexicographical_numbers.py │ ├── 387_first_unique_character_in_a_string.py │ ├── 388_longest_absolute_file_path.py │ ├── 389_find_the_difference.py │ ├── 38_count_and_say.py │ ├── 390_elimination_game.py │ ├── 391_perfect_rectangle.py │ ├── 395_longest_substring_with_at_least_k_repeating_characters.py │ ├── 398_random_pick_index.py │ ├── 399_evaluate_division.py │ ├── 406_queue_reconstruction_by_height.py │ ├── 416_partition_equal_subset_sum.py │ ├── 417_pacific_atlantic_water_flow.py │ ├── 41_first_missing_positive.py │ ├── 437_path_sum_iii.py │ ├── 454_4sum_ii.py │ ├── 461_hamming_distance.py │ ├── 480_sliding_window_median.py │ ├── 486_predict_the_winner.py │ ├── 48_rotate_image.py │ ├── 490_the_maze.py │ ├── 494_target_sum.py │ ├── 499_the_maze_iii.py │ ├── 505_the_maze_ii.py │ ├── 518_coin_change_2.py │ ├── 51_n_queens.py │ ├── 524_longest_word_in_dictionary_through_deleting.py │ ├── 52_n_queens_ii.py │ ├── 530_minimum_absolute_difference_in_bst.py │ ├── 542_01_matrix.py │ ├── 54_spiral_matrix.py │ ├── 592_fraction_addition_and_subtraction.py │ ├── 593_valid_square.py │ ├── 59_spiral_matrix_ii.py │ ├── 5_longest_palindromic_substring.py │ ├── 616_add_bold_tag_in_string.py │ ├── 643_maximum_average_subarray_i.py │ ├── 66_plus_one.py │ ├── 676_implement_magic_dictionary.py │ ├── 679_24_game.py │ ├── 681_next_closest_time.py │ ├── 682_baseball_game.py │ ├── 683_k_empty_slots.py │ ├── 684_redundant_connection.py │ ├── 685_redundant_connection_ii.py │ ├── 688_knight_probability_in_chessboard.py │ ├── 689_maximum_sum_of_3_non_overlapping_subarrays.py │ ├── 697_degree_of_an_array.py │ ├── 719_find_k_th_smallest_pair_distance.py │ ├── 721_accounts_merge.py │ ├── 734_sentence_similarity.py │ ├── 737_sentence_similarity_ii.py │ ├── 744_find_smallest_letter_greater_than_target.py │ ├── 746_min_cost_climbing_stairs.py │ ├── 747_largest_number_at_least_twice_of_others.py │ ├── 748_shortest_completing_word.py │ ├── 749_contain_virus.py │ ├── 750_number_of_corner_rectangles.py │ ├── 758_bold_words_in_string.py │ ├── 769_max_chunks_to_make_sorted.py │ ├── 776_split_bst.py │ ├── 777_swap_adjacent_in_lr_string.py │ ├── 778_swim_in_rising_water.py │ ├── 779_k_th_symbol_in_grammar.py │ ├── 783_minimum_distance_between_bst_nodes.py │ ├── 784_letter_case_permutation.py │ ├── 785_is_graph_bipartite.py │ ├── 786_k_th_smallest_prime_fraction.py │ ├── 787_cheapest_flights_within_k_stops.py │ ├── 788_rotated_digits.py │ ├── 789_escape_the_ghosts.py │ ├── 790_domino_and_tromino_tiling.py │ ├── 791_custom_sort_string.py │ ├── 7_reverse_integer.py │ ├── 81_search_in_rotated_sorted_array_ii.py │ └── 8_string_to_integer_atoi.py ├── lintcode/ │ ├── 102_linked_list_cycle.py │ ├── 103_linked_list_cycle_ii.py │ ├── 104_merge_k_sorted_lists.py │ ├── 105_copy_list_with_random_pointer.py │ ├── 107_word_break.py │ ├── 108_palindrome_partitioning_ii.py │ ├── 109_triangle.py │ ├── 10_string_permutation_ii.py │ ├── 110_minimum_path_sum.py │ ├── 111_climbing_stairs.py │ ├── 114_unique_paths.py │ ├── 115_unique_paths_ii.py │ ├── 116_jump_game.py │ ├── 117_jump_game_ii.py │ ├── 118_distinct_subsequences.py │ ├── 119_edit_distance.py │ ├── 11_search_range_in_binary_search_tree.py │ ├── 120_word_ladder.py │ ├── 121_word_ladder_ii.py │ ├── 122_largest_rectangle_in_histogram.py │ ├── 123_word_search.py │ ├── 124_longest_consecutive_sequence.py │ ├── 125_backpack_ii.py │ ├── 126_max_tree.py │ ├── 127_topological_sorting.py │ ├── 128_hash_function.py │ ├── 129_rehashing.py │ ├── 12_min_stack.py │ ├── 130_heapify.py │ ├── 131_building_outline.py │ ├── 132_word_search_ii.py │ ├── 134_lru_cache.py │ ├── 135_combination_sum.py │ ├── 1365_minimum_cycle_section.py │ ├── 1366_directed_graph_loop.py │ ├── 1367_police_distance.py │ ├── 1368_same_number.py │ ├── 136_palindrome_partitioning.py │ ├── 137_clone_graph.py │ ├── 138_subarray_sum.py │ ├── 139_subarray_sum_closest.py │ ├── 13_strstr.py │ ├── 141_sqrtx.py │ ├── 142_o1_check_power_of_2.py │ ├── 143_sort_colors_ii.py │ ├── 148_sort_colors.py │ ├── 149_best_time_to_buy_and_sell_stock.py │ ├── 14_first_position_of_target.py │ ├── 150_best_time_to_buy_and_sell_stock_ii.py │ ├── 151_best_time_to_buy_and_sell_stock_iii.py │ ├── 153_combination_sum_ii.py │ ├── 154_regular_expression_matching.py │ ├── 155_minimum_depth_of_binary_tree.py │ ├── 156_merge_intervals.py │ ├── 158_two_strings_are_anagrams.py │ ├── 159_find_minimum_in_rotated_sorted_array.py │ ├── 15_permutations.py │ ├── 160_find_minimum_in_rotated_sorted_array_ii.py │ ├── 165_merge_two_sorted_lists.py │ ├── 167_add_two_numbers.py │ ├── 168_burst_balloons.py │ ├── 16_permutations_ii.py │ ├── 171_anagrams.py │ ├── 175_invert_binary_tree.py │ ├── 178_graph_valid_tree.py │ ├── 17_subsets.py │ ├── 183_wood_cut.py │ ├── 18_subsets_ii.py │ ├── 190_next_permutation_ii.py │ ├── 191_maximum_product_subarray.py │ ├── 192_wildcard_matching.py │ ├── 196_find_the_missing_number.py │ ├── 197_permutation_index.py │ ├── 198_permutation_index_ii.py │ ├── 1_a_b_problem.py │ ├── 204_singleton.py │ ├── 211_string_permutation.py │ ├── 215_rate_limiter.py │ ├── 221_add_two_numbers_ii.py │ ├── 231_typeahead.py │ ├── 232_tiny_url.py │ ├── 234_webpage_crawler.py │ ├── 236_swap_bits.py │ ├── 242_convert_binary_tree_to_linked_lists_by_depth.py │ ├── 243_amicable_pair │ ├── 245_subtree.py │ ├── 246_binary_tree_path_sum_ii.py │ ├── 24_lfu_cache.py │ ├── 254_drop_eggs.py │ ├── 272_climbing_stairs_ii.py │ ├── 28_search_a_2d_matrix.py │ ├── 29_interleaving_string.py │ ├── 30_insert_interval.py │ ├── 31_partition_array.py │ ├── 32_minimum_window_substring.py │ ├── 35_reverse_linked_list.py │ ├── 360_sliding_window_median.py │ ├── 362_sliding_window_maximum.py │ ├── 363_trapping_rain_water.py │ ├── 364_trapping_rain_water_ii.py │ ├── 368_expression_evaluation.py │ ├── 36_reverse_linked_list_ii.py │ ├── 376_binary_tree_path_sum.py │ ├── 378_convert_binary_search_tree_to_doubly_linked_list.py │ ├── 382_triangle_count.py │ ├── 384_longest_substring_without_repeating_characters.py │ ├── 386_longest_substring_with_at_most_k_distinct_characters.py │ ├── 38_search_a_2d_matrix_ii.py │ ├── 390_find_peak_element_ii.py │ ├── 391_number_of_airplanes_in_the_sky.py │ ├── 392_house_robber.py │ ├── 393_best_time_to_buy_and_sell_stock_iv.py │ ├── 394_coins_in_a_line.py │ ├── 395_coins_in_a_line_ii.py │ ├── 396_coins_in_a_line_iii.py │ ├── 397_longest_increasing_continuous_subsequence.py │ ├── 3_digit_counts.py │ ├── 401_kth_smallest_number_in_sorted_matrix.py │ ├── 402_continuous_subarray_sum.py │ ├── 406_minimum_size_subarray_sum.py │ ├── 40_implement_queue_by_two_stacks.py │ ├── 414_divide_two_integers.py │ ├── 415_valid_palindrome.py │ ├── 417_valid_number.py │ ├── 418_integer_to_roman.py │ ├── 419_roman_to_integer.py │ ├── 41_maximum_subarray.py │ ├── 42_maximum_subarray_ii.py │ ├── 430_scramble_string.py │ ├── 431_connected_component_in_undirected_graph.py │ ├── 432_find_the_weak_connected_component_in_the_directed_graph.py │ ├── 433_number_of_islands.py │ ├── 434_number_of_islands_ii.py │ ├── 437_copy_books.py │ ├── 43_maximum_subarray_iii.py │ ├── 440_backpack_iii.py │ ├── 442_implement_trie.py │ ├── 443_two_sum_greater_than_target.py │ ├── 447_search_in_a_big_sorted_array.py │ ├── 450_reverse_nodes_in_k_group.py │ ├── 453_flatten_binary_tree_to_linked_list.py │ ├── 457_classical_binary_search.py │ ├── 458_last_position_of_target.py │ ├── 459_closest_number_in_sorted_array.py │ ├── 45_maximum_subarray_difference.py │ ├── 460_k_closest_numbers_in_sorted_array.py │ ├── 461_kth_smallest_numbers_in_unsorted_array.py │ ├── 462_total_occurrence_of_target.py │ ├── 465_kth_smallest_sum_in_two_sorted_arrays.py │ ├── 469_identical_binary_tree.py │ ├── 471_top_k_frequent_words.py │ ├── 472_binary_tree_path_sum_iii.py │ ├── 473_add_and_search_word.py │ ├── 474_lowest_common_ancestor_ii.py │ ├── 475_binary_tree_maximum_path_sum_ii.py │ ├── 479_second_max_of_array.py │ ├── 480_binary_tree_paths.py │ ├── 486_merge_k_sorted_arrays.py │ ├── 494_implement_stack_by_two_queues.py │ ├── 496_toy_factory.py │ ├── 497_shape_factory.py │ ├── 498_parking_lot.py │ ├── 499_word_count.py │ ├── 4_ugly_number_ii.py │ ├── 500_inverted_index.py │ ├── 501_mini_twitter.py │ ├── 502_mini_cassandra.py │ ├── 503_anagram_map_reduce.py │ ├── 504_inverted_index_map_reduce.py │ ├── 505_web_logger.py │ ├── 509_mini_yelp.py │ ├── 510_maximal_rectangle.py │ ├── 512_decode_ways.py │ ├── 513_perfect_squares.py │ ├── 515_paint_house.py │ ├── 516_paint_house_ii.py │ ├── 517_ugly_number.py │ ├── 518_super_ugly_number.py │ ├── 519_consistent_hashing.py │ ├── 51_previous_permutation.py │ ├── 520_consistent_hashing_ii.py │ ├── 521_remove_duplicate_numbers_in_array.py │ ├── 522_tiny_url_ii.py │ ├── 523_url_parser.py │ ├── 525_mini_uber.py │ ├── 526_load_balancer.py │ ├── 527_trie_serialization.py │ ├── 528_flatten_nested_list_iterator.py │ ├── 529_geohash.py │ ├── 52_next_permutation.py │ ├── 530_geohash_ii.py │ ├── 531_six_degrees.py │ ├── 532_reverse_pairs.py │ ├── 533_two_sum_closest_to_target.py │ ├── 534_house_robber_ii.py │ ├── 538_memcache.py │ ├── 539_move_zeroes.py │ ├── 53_reverse_words_in_a_string.py │ ├── 540_zigzag_iterator.py │ ├── 541_zigzag_iterator_ii.py │ ├── 543_kth_largest_in_n_arrays.py │ ├── 544_top_k_largest_numbers.py │ ├── 545_top_k_largest_numbers_ii.py │ ├── 547_intersection_of_two_arrays.py │ ├── 548_intersection_of_two_arrays_ii.py │ ├── 54_string_to_integer_ii.py │ ├── 551_nested_list_weight_sum.py │ ├── 552_create_maximum_number.py │ ├── 553_bomb_enemy.py │ ├── 555_counting_bloom_filter.py │ ├── 556_standard_bloom_filter.py │ ├── 559_trie_service.py │ ├── 560_friendship_service.py │ ├── 563_backpack_v.py │ ├── 564_backpack_vi.py │ ├── 565_heart_beat.py │ ├── 566_gfs_client.py │ ├── 56_two_sum.py │ ├── 573_build_post_office_ii.py │ ├── 574_build_post_office.py │ ├── 575_expression_expand.py │ ├── 578_lowest_common_ancestor_iii.py │ ├── 57_3sum.py │ ├── 582_word_break_ii.py │ ├── 584_drop_eggs_ii.py │ ├── 585_maximum_number_in_mountain_sequence.py │ ├── 586_sqrtx_ii.py │ ├── 587_two_sum_unique_pairs.py │ ├── 589_connecting_graph.py │ ├── 58_4sum.py │ ├── 590_connecting_graph_ii.py │ ├── 591_connecting_graph_iii.py │ ├── 594_strstr_ii.py │ ├── 595_binary_tree_longest_consecutive_sequence.py │ ├── 596_minimum_subtree.py │ ├── 597_subtree_with_maximum_average.py │ ├── 598_zombie_in_matrix.py │ ├── 599_insert_into_a_cyclic_sorted_list.py │ ├── 59_3sum_closest.py │ ├── 5_kth_largest_element.py │ ├── 600_smallest_rectangle_enclosing_black_pixels.py │ ├── 601_flatten_2d_vector.py │ ├── 602_russian_doll_envelopes.py │ ├── 603_largest_divisible_subset.py │ ├── 604_window_sum.py │ ├── 605_sequence_reconstruction.py │ ├── 606_kth_largest_element_ii.py │ ├── 607_two_sum_data_structure_design.py │ ├── 608_two_sum_input_array_is_sorted.py │ ├── 609_two_sum_less_than_or_equal_to_target.py │ ├── 610_two_sum_difference_equals_to_target.py │ ├── 611_knight_shortest_path.py │ ├── 612_k_closest_points.py │ ├── 613_high_five.py │ ├── 614_binary_tree_longest_consecutive_sequence_ii.py │ ├── 615_course_schedule.py │ ├── 616_course_schedule_ii.py │ ├── 617_maximum_average_subarray.py │ ├── 618_search_graph_nodes.py │ ├── 619_binary_tree_longest_consecutive_sequence_iii.py │ ├── 61_search_for_a_range.py │ ├── 620_maximum_subarray_iv.py │ ├── 621_maximum_subarray_v.py │ ├── 622_frog_jump.py │ ├── 623_k_edit_distance.py │ ├── 624_remove_substrings.py │ ├── 625_partition_array_ii.py │ ├── 630_knight_shortest_path_ii.py │ ├── 633_find_the_duplicate_number.py │ ├── 634_word_squares.py │ ├── 635_boggle_game.py │ ├── 636_132_pattern.py │ ├── 646_first_position_unique_character.py │ ├── 647_substring_anagrams.py │ ├── 64_merge_sorted_array.py │ ├── 654_sparse_matrix_multiplication.py │ ├── 655_big_integer_addition.py │ ├── 656_big_integer_multiplication.py │ ├── 65_median_of_two_sorted_arrays.py │ ├── 662_guess_number_game.py │ ├── 664_counting_bits.py │ ├── 667_longest_palindromic_subsequence.py │ ├── 668_ones_and_zeroes.py │ ├── 669_coin_change.py │ ├── 66_binary_tree_preorder_traversal.py │ ├── 676_decode_ways_ii.py │ ├── 67_binary_tree_inorder_traversal.py │ ├── 689_two_sum_bst_edtion.py │ ├── 68_binary_tree_postorder_traversal.py │ ├── 69_binary_tree_level_order_traversal.py │ ├── 6_merge_two_sorted_arrays.py │ ├── 70_binary_tree_level_order_traversal_ii.py │ ├── 717_tree_longest_path_with_same_value.py │ ├── 718_repeat_string.py │ ├── 71_binary_tree_zigzag_level_order_traversal.py │ ├── 724_minimum_partition.py │ ├── 725_boolean_parenthesization.py │ ├── 729_last_digit_by_factorial_divide.py │ ├── 745_palindromic_ranges.py │ ├── 74_first_bad_version.py │ ├── 752_rogue_knight_sven.py │ ├── 75_find_peak_element.py │ ├── 76_longest_increasing_subsequence.py │ ├── 772_group_anagrams.py │ ├── 775_palindrome_pairs.py │ ├── 776_strobogrammatic_number_ii.py │ ├── 77_longest_common_subsequence.py │ ├── 784_the_longest_common_prefix_ii.py │ ├── 790_parser.py │ ├── 791_merge_number.py │ ├── 792_kth_prime_number.py │ ├── 793_intersection_of_arrays.py │ ├── 7_binary_tree_serialization.py │ ├── 813_find_anagram_mappings.py │ ├── 81_data_stream_median.py │ ├── 823_input_stream.py │ ├── 824_single_number_iv.py │ ├── 826_computer_maintenance.py │ ├── 830_string_sort.py │ ├── 831_3sum_ii.py │ ├── 832_count_negative_number.py │ ├── 833_process_sequence.py │ ├── 85_insert_node_in_a_binary_search_tree.py │ ├── 86_binary_search_tree_iterator.py │ ├── 87_remove_node_in_binary_search_tree.py │ ├── 88_lowest_common_ancestor.py │ ├── 89_k_sum.py │ ├── 8_rotate_string.py │ ├── 900_closest_binary_search_tree_value.py │ ├── 901_closest_binary_search_tree_value_ii.py │ ├── 919_meeting_rooms_ii.py │ ├── 920_meeting_rooms.py │ ├── 92_backpack.py │ ├── 93_balanced_binary_tree.py │ ├── 95_validate_binary_search_tree.py │ ├── 96_partition_list.py │ ├── 97_maximum_depth_of_binary_tree.py │ ├── 98_sort_list.py │ └── 9_fizz_buzz.py ├── other/ │ ├── anti_queue_reconstruction_by_height.py │ ├── binary_tree_maximum_path_product.py │ ├── candy_crush.py │ ├── card_shuffler.py │ ├── deep_fetch.js │ ├── deep_fetch.test.js │ ├── find_treasure_in_maze.py │ ├── find_ways_in_board_game.py │ ├── freq_iterator.py │ ├── gcd_and_lcm.py │ ├── get_most_popular_word.py │ ├── guess_secret.py │ ├── inorder_non_threaded_binary_tree_traversal.py │ ├── is_valid_relation.py │ ├── merge_mail.py │ ├── reservation.py │ ├── robot_cleaner.py │ ├── snake_and_ladder_problem.py │ ├── stock_stream.py │ ├── string_abbreviation.py │ ├── the_point_inside_polygon.py │ ├── uneven_random_get.py │ ├── unique_paths_with_followups.py │ ├── unique_word_abbreviation_ii.py │ └── upside_down_numbers.py ├── pramp/ │ ├── array_index_and_element_equality.py │ ├── array_of_array_products.py │ ├── award_budget_cuts.py │ ├── bracket_match.py │ ├── busiest_time_in_the_mall.py │ ├── decrypt_message.py │ ├── deletion_distance.py │ ├── drone_flight_planner.py │ ├── find_the_duplicates.py │ ├── flatten_a_dictionary.py │ ├── h_tree_construction.py │ ├── k_messed_array_sort.py │ ├── largest_smaller_bst_node.py │ ├── number_of_paths.py │ ├── pairs_with_specific_difference.py │ ├── pancake_sort.py │ ├── root_of_number.py │ ├── sales_path.py │ ├── sentence_reverse.py │ ├── smallest_substring_of_all_characters.py │ ├── time_planner.py │ └── word_count_engine.py └── topic/ ├── README.md ├── _test/ │ └── python/ │ ├── __init__.py │ └── test_base.py ├── balanced_search_tree/ │ └── python/ │ ├── __init__.py │ ├── _helper.py │ └── red_black_tree.py ├── bit_manipulation/ │ ├── README.md │ └── python/ │ ├── __init__.py │ ├── calculator_in_bit.py │ └── calculator_in_bit__test.py ├── complexity_analysis.md ├── dynamic_programming/ │ └── README.md ├── graph/ │ ├── README.md │ └── python/ │ ├── __init__.py │ ├── union_find.py │ └── union_find__test.py ├── hash/ │ └── python/ │ ├── __init__.py │ ├── geohash.py │ ├── geohash__test.py │ ├── hashtable.py │ └── hashtable__test.py ├── heap/ │ └── python/ │ ├── __init__.py │ ├── binary_hash_heap.py │ ├── binary_heap.py │ ├── heap__test.py │ ├── lazy_removable_heapq.py │ └── removable_heapq.py ├── language/ │ ├── javascript/ │ │ ├── README.md │ │ └── traversal.md │ ├── php/ │ │ └── README.md │ ├── python/ │ │ ├── README.md │ │ ├── matrix.md │ │ ├── runtime_of_builtins.md │ │ └── traversal.md │ └── sql/ │ └── README.md ├── linked_list/ │ ├── README.md │ └── python/ │ ├── __init__.py │ ├── _helper.py │ ├── cyclic_doubly_list.py │ ├── cyclic_list.py │ ├── dummy_tail_doubly_list.py │ ├── dummy_tail_list.py │ ├── head_tail_doubly_list.py │ ├── head_tail_list.py │ ├── linked_list__test.py │ ├── two_dummy_doubly_list.py │ └── two_dummy_list.py ├── problem_set/ │ ├── subarray_sum.md │ └── substring_with_distinct_characters.md ├── range_query/ │ └── python/ │ ├── __init__.py │ ├── _helper.py │ ├── binary_indexed_tree.py │ ├── interval_tree.py │ ├── prefix_sum.py │ ├── range_tree.py │ └── segment_tree.py ├── searching/ │ ├── binary_search.md │ └── python/ │ ├── __init__.py │ └── _helper.py ├── sorting/ │ └── python/ │ ├── __init__.py │ ├── _helper.py │ ├── merge_sort.py │ ├── quick_sort.py │ └── sorting__test.py ├── system_design/ │ └── latency.md ├── traversal/ │ └── python/ │ ├── __init__.py │ ├── _helper.py │ ├── binary_tree_traversal__test.py │ ├── iterative_traversal.py │ ├── morris_traversal.py │ └── recursive_traversal.py └── tree/ ├── binary_tree.md └── python/ ├── __init__.py ├── _helper.py ├── binary_tree.py ├── binary_tree__test.py ├── trie.py └── trie__test.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ root = true [*] end_of_line = lf insert_final_newline = true charset = utf-8 indent_size = 2 indent_style = space [*.py] indent_size = 4 [*.{md,markdown}] indent_size = 4 [Makefile] indent_style = tab ================================================ FILE: .gitignore ================================================ __pycache__ node_modules *-lock.json ================================================ FILE: .travis.yml ================================================ matrix: include: - language: python python: "3.6" script: - $TRAVIS_BUILD_DIR/bin/test_python.py - language: node_js node_js: - "lts/carbon" before_script: - npm install -g jest script: jest --config=$TRAVIS_BUILD_DIR/bin/test_javascript.js ================================================ FILE: README.md ================================================ Algorithm ====== [![Build Status](https://travis-ci.org/jaychsu/algorithm.svg?branch=master)](https://travis-ci.org/jaychsu/algorithm) The challenges for algorithm contests, and summary the implementation. ## Structure | Top-level Folder | Note | | :--- | :--- | | bin | commands | | topic | implementation and summaries of algorithms and data structures | | other | for problems met in life | | codeforces | for [Codeforces](http://codeforces.com) | | codejam | for [PastContests](https://code.google.com/codejam/past-contests) in [code jam](https://code.google.com/codejam) from Google | | codelab | for [Code Lab](https://codelab.interviewbit.com) from Facebook | | codewars | for [Codewars](https://www.codewars.com) | | geeksforgeeks | for [GeeksforGeeks](https://www.geeksforgeeks.org) | | hackerrank | for [HackerRank](https://www.hackerrank.com/contests) | | leetcode | for [LeetCode](https://leetcode.com) | | lintcode | for [LintCode](http://www.lintcode.com) | | pramp | for [Pramp](https://www.pramp.com) | | topcoder | for [ActiveContests](https://community.topcoder.com/longcontest/?module=ViewActiveContests), [Practices](https://community.topcoder.com/longcontest/?module=ViewPractice), and [ProblemArchives](https://community.topcoder.com/tc?module=ProblemArchive) in [Topcoder](https://www.topcoder.com) | | Scaler | for [Scaler Topics](https://www.scaler.com/topics/) | | InterviewBit | for [Technical Interview](https://www.interviewbit.com/technical-interview-questions/), [Data Structures and Algorithms](https://www.interviewbit.com/courses/programming/), [Contests](https://www.interviewbit.com/contests/), and [Mock Interviews](https://www.interviewbit.com/mock-interview/) | ## Testing | Language | Command | | :--- | :--- | | Python | [./bin/test_python.py](./bin/test_python.py) | | Javascript | | ================================================ FILE: bin/test_javascript.js ================================================ // TODO: wrap this file to a executable command // [Configuring Jest](https://facebook.github.io/jest/docs/en/configuration.html) module.exports = { "rootDir": "..", "testEnvironment": "node", "testRegex": "/(other|topic)/.*(\\.|/)(test|spec)\\.jsx?$", "verbose": true, } ================================================ FILE: bin/test_python.py ================================================ #!/usr/bin/env python3 # -*- coding: utf-8 -*- import os import sys import unittest import doctest sys.path.append(os.path.join( os.path.dirname(__file__), '../topic' )) from _test.python.test_base import TaskTimer def get_unittests(dirname): ROOT_DIR = os.path.join( os.path.dirname(__file__), '../{dirname}'.format(dirname=dirname) ) tests = [] for path, _, files in os.walk(ROOT_DIR): if ('pycache' in path or 'python' not in path or '__init__.py' not in files): continue tests.append(unittest.defaultTestLoader.discover( path, pattern='*__test.py', top_level_dir=ROOT_DIR )) return tests def get_doctests(dirname): ROOT_DIR = os.path.join( os.path.dirname(__file__), '../{dirname}'.format(dirname=dirname) ) tests = [] sys.path.append(ROOT_DIR) for path, _, files in os.walk(ROOT_DIR): if 'pycache' in path: continue for file in files: if (file.startswith('_') or not file.endswith('.py')): continue timer = TaskTimer() tests.append(doctest.DocTestSuite( file[:-3], setUp=lambda globs: timer.reset_time(), tearDown=lambda globs: timer.print_duration() )) sys.path.pop() return tests if __name__ == '__main__': suite = unittest.TestSuite() for tests in ( get_unittests('topic'), get_doctests('leetcode'), get_doctests('pramp'), get_doctests('other'), ): suite.addTests(tests) unittest.TextTestRunner(verbosity=2).run(suite) ================================================ FILE: codewars/delete_occurrences_of_an_element_if_it_occurs_more_than_n_times.js ================================================ function deleteNth(nums, n) { const freq = {} for (let i = 0; i < nums.length; i++) { const num = nums[i] if (!freq.hasOwnProperty(num)) { freq[num] = 0 } freq[num] += 1 } for (let i = nums.length - 1; i >= 0; i--) { const num = nums[i] if (freq[num] <= n) { continue } nums.splice(i, 1) freq[num] -= 1 } return nums } ================================================ FILE: codewars/double_cola.js ================================================ function whoIsNext(names, r) { let total = names.length while (r > total) { r -= total total *= 2 } return names[parseInt(names.length * (r - 1) / total)] } ================================================ FILE: codewars/iq_test.js ================================================ function iqTest(nums) { nums = nums.split(' ').map(c => +c & 1) for (let i = 2; i < nums.length; i++) { if (nums[0] ^ nums[i] && nums[1] ^ nums[i]) { return i + 1 } else if (nums[i] ^ nums[0] && nums[1] ^ nums[0]) { return 1 } else if (nums[0] ^ nums[1] && nums[i] ^ nums[1]) { return 2 } } return -1 } ================================================ FILE: codewars/is_a_number_prime.js ================================================ function isPrime(num) { if (!num || num <= 1) return false const factors = [2, 3, 5, 7, 9, 11, 13, 17, 19].filter(base => { if (num <= base) return false if (num % base === 0) return true return false }) return factors.length === 0 } ================================================ FILE: codewars/multiply.js ================================================ function multiply(a, b) { return a * b } ================================================ FILE: codewars/rectangle_into_squares.js ================================================ function sqInRect(lng, wdth) { if (!lng || !wdth || lng === wdth) return null const ans = [] while (lng != 1 || wdth != 1) { if (lng > wdth) { ans.push(wdth) lng -= wdth } else if (lng < wdth) { ans.push(lng) wdth -= lng } else { ans.push(lng) break } } if (lng == 1 && wdth == 1) { ans.push(1) } return ans } ================================================ FILE: codewars/reverse_words.js ================================================ function reverseWords(s) { return s.split(' ').map(word => { return word.split('').reverse().join('') }).join(' ') } ================================================ FILE: codewars/simple_encryption_1_alternating_split.js ================================================ function encrypt(text, n) { if (!text || !n || n < 0) return text const TEXT_SIZE = text.length while (n--) { const _text = [] for (let i = 1; i < TEXT_SIZE; i += 2) { _text.push(text[i]) } for (let i = 0; i < TEXT_SIZE; i += 2) { _text.push(text[i]) } text = _text.join('') } return text } function decrypt(text, n) { if (!text || !n || n < 0) return text const TEXT_SIZE = text.length const HALF_TEXT_SIZE = parseInt(text.length / 2) while (n--) { const _text = [] for (let i = 0; i < HALF_TEXT_SIZE + 1; i++) { if (HALF_TEXT_SIZE + i < TEXT_SIZE) { _text.push(text[HALF_TEXT_SIZE + i]) } if (i < HALF_TEXT_SIZE) { _text.push(text[i]) } } text = _text.join('') } return text } ================================================ FILE: codewars/sudoku_solution_validator.js ================================================ function validSolution(board) { if ( !Array.isArray(board) || !Array.isArray(board[0]) || board.length !== board[0].length) { return false } const n = board.length const g = Math.sqrt(n) // group counts const cnts = new Array(n).fill(0) let cnt = 0 let i, j, x, y for (x = 0; x < n; x++) { for (y = 0; y < n; y++) { if (board[x][y] <= 0 || board[x][y] > n) return false if (cnts[board[x][y] - 1] !== cnt) return false cnts[board[x][y] - 1] += 1 } cnt += 1 for (y = 0; y < n; y++) { if (board[y][x] <= 0 || board[y][x] > n) return false if (cnts[board[y][x] - 1] !== cnt) return false cnts[board[y][x] - 1] += 1 } cnt += 1 } let x_from, x_to, y_from, y_to for (i = 0; i < g; i++) { for (j = 0; j < g; j++) { x_from = i * g x_to = i * g + g y_from = j * g y_to = j * g + g for (x = x_from; x < x_to; x++) { for (y = y_from; y < y_to; y++) { if (cnts[board[x][y] - 1] !== cnt) return false cnts[board[x][y] - 1] += 1 } } cnt += 1 } } return true } ================================================ FILE: codewars/take_a_ten_minute_walk.js ================================================ function isValidWalk(walk) { if (walk.length != 10) { return false } let dx = 0 let dy = 0 walk.forEach(c => { switch (c) { case 'n': dy++ break case 's': dy-- break case 'e': dx++ break case 'w': dx-- break default: return false } }) return dx === 0 && dy === 0 } ================================================ FILE: codewars/tribonacci_sequence.js ================================================ function tribonacci(signature, n) { if (!n) return [] const ans = signature.slice() for (let i = 3; i < n; i++) { ans.push(ans[i - 3] + ans[i - 2] + ans[i - 1]) } return ans.slice(0, n) } ================================================ FILE: codewars/unique_in_order.js ================================================ function uniqueInOrder(iterable) { if (!iterable) { return [] } const ans = [iterable[0]] for (let i = 1; i < iterable.length; i++) { if (iterable[i] != ans[ans.length - 1]) { ans.push(iterable[i]) } } return ans } ================================================ FILE: codewars/who_likes_it.js ================================================ function likes(names) { if (!Array.isArray(names) || names.length === 0) { return 'no one likes this' } switch (names.length) { case 1: return `${names[0]} likes this` case 2: return `${names[0]} and ${names[1]} like this` case 3: return `${names[0]}, ${names[1]} and ${names[2]} like this` default: return `${names[0]}, ${names[1]} and ${names.length - 2} others like this` } } ================================================ FILE: leetcode/101_symmetric_tree.py ================================================ """ Definition for a binary tree node. class TreeNode: def __init__(self, x): self.val = x self.left = None self.right = None """ class Solution: def isSymmetric(self, root): """ :type root: TreeNode :rtype: bool """ if not root: return True return self.divide_conquer(root.left, root.right) def divide_conquer(self, left, right): if not left and not right: return True if not left or not right: return False if left.val != right.val: return False if not self.divide_conquer(left.left, right.right): return False if not self.divide_conquer(left.right, right.left): return False return True ================================================ FILE: leetcode/105_construct_binary_tree_from_preorder_and_inorder_traversal.py ================================================ # Definition for a binary tree node. # class TreeNode(object): # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution(object): def buildTree(self, P, I): """ :type P: List[int] :type I: List[int] :rtype: TreeNode """ if not P or not I: return i = I.index(P.pop(0)) node = TreeNode(I[i]) node.left = self.buildTree(P, I[:i]) node.right = self.buildTree(P, I[i + 1:]) return node ================================================ FILE: leetcode/108_convert_sorted_array_to_binary_search_tree.py ================================================ # Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def sortedArrayToBST(self, nums): """ :type nums: List[int] :rtype: TreeNode """ if not nums: return i = len(nums) // 2 node = TreeNode(nums[i]) node.left = self.sortedArrayToBST(nums[:i]) node.right = self.sortedArrayToBST(nums[i + 1:]) return node ================================================ FILE: leetcode/112_path_sum.py ================================================ """ Definition for a binary tree node. class TreeNode: def __init__(self, x): self.val = x self.left = None self.right = None """ class Solution: def hasPathSum(self, root, target): """ :type root: TreeNode :type target: int :rtype: bool """ if not root: return False if not root.left and not root.right: return root.val == target if root.left and self.hasPathSum(root.left, target - root.val): return True if root.right and self.hasPathSum(root.right, target - root.val): return True return False ================================================ FILE: leetcode/113_path_sum_ii.py ================================================ """ Definition for a binary tree node. class TreeNode: def __init__(self, x): self.val = x self.left = None self.right = None """ class Solution: def pathSum(self, root, target): """ :type root: TreeNode :type target: int :rtype: List[List[int]] """ ans = [] if not root: return ans self.dfs(root, target, ans, []) return ans def dfs(self, node, remaining, ans, path): if not node: return path.append(node.val) if not node.left and not node.right and remaining == node.val: ans.append(path[:]) else: self.dfs(node.left, remaining - node.val, ans, path) self.dfs(node.right, remaining - node.val, ans, path) path.pop() ================================================ FILE: leetcode/116_populating_next_right_pointers_in_each_node.py ================================================ """ Definition for binary tree with next pointer. class TreeLinkNode: def __init__(self, x): self.val = x self.left = None self.right = None self.next = None """ class Solution: """ Recursion """ def connect(self, root): """ :type root: TreeLinkNode :rtype: void """ if not root: return if root.left: root.left.next = root.right if root.right and root.next: root.right.next = root.next.left # leave to None if root.right: root.right.next = None self.connect(root.left) self.connect(root.right) class Solution: """ Iteration """ def connect(self, root): """ :type root: TreeLinkNode :rtype: void """ if not root: return head = root nxt = None # this loop only for find left while head: nxt = head head = nxt.left # this loop find for every next, do level move while nxt: if nxt.left: nxt.left.next = nxt.right if nxt.right and nxt.next: nxt.right.next = nxt.next.left nxt = nxt.next ================================================ FILE: leetcode/117_populating_next_right_pointers_in_each_node_ii.py ================================================ """ Definition for binary tree with next pointer. class TreeLinkNode: def __init__(self, x): self.val = x self.left = None self.right = None self.next = None """ class Solution: """ Recursion 1. recursion from right to left 2. first scan the nxt by level """ def connect(self, root): """ :type root: TreeLinkNode :rtype: void """ if not root: return # scan the nxt by level head = root.next nxt = None while head and not nxt: if head.left: nxt = head.left elif head.right: nxt = head.right else: head = head.next if root.right: root.right.next = nxt nxt = root.right if root.left: root.left.next = nxt self.connect(root.right) self.connect(root.left) class Solution: """ Iteration """ def connect(self, root): """ :type root: TreeLinkNode :rtype: void """ if not root: return dummy = TreeLinkNode(0) dummy.next = root nxt = tail = None while dummy.next: tail = dummy nxt = dummy.next dummy.next = None while nxt: if nxt.left: tail.next = nxt.left tail = tail.next if nxt.right: tail.next = nxt.right tail = tail.next nxt = nxt.next class Solution: """ Level Traversal """ def connect(self, root): """ :type root: TreeLinkNode :rtype: void """ if not root: return queue, _queue = [root], [] while queue: n = len(queue) for i in range(n): if i < n - 1: queue[i].next = queue[i + 1] if queue[i].left: _queue.append(queue[i].left) if queue[i].right: _queue.append(queue[i].right) queue, _queue = _queue, [] ================================================ FILE: leetcode/118_pascal_s_triangle.py ================================================ class Solution: def generate(self, num_rows): """ :type num_rows: int :rtype: List[List[int]] """ ans = [] if not num_rows: return ans ans.append([1]) for i in range(1, num_rows): path = [ans[i - 1][0]] for j in range(1, len(ans[i - 1])): path.append(ans[i - 1][j] + ans[i - 1][j - 1]) path.append(ans[i - 1][-1]) ans.append(path) return ans class Solution: def generate(self, num_rows): """ :type num_rows: int :rtype: List[List[int]] """ ans = [] if not num_rows: return ans ans.append([1]) for i in range(1, num_rows): ans.append([ (ans[i - 1] + [0])[j] + ([0] + ans[i - 1])[j] for j in range(len(ans[i - 1]) + 1) ]) return ans ================================================ FILE: leetcode/11_container_with_most_water.py ================================================ class Solution: def maxArea(self, H): """ :type H: List[int] :rtype: int """ ans = 0 if not H: return ans left, right = 0, len(H) - 1 while left < right: area = min(H[left], H[right]) * (right - left) if area > ans: ans = area if H[left] < H[right]: left += 1 else: right -= 1 return ans ================================================ FILE: leetcode/124_binary_tree_maximum_path_sum.py ================================================ """ Definition for a binary tree node. class TreeNode: def __init__(self, x): self.val = x self.left = None self.right = None """ class Solution: def maxPathSum(self, root): """ :type root: TreeNode :rtype: int """ if not root: return 0 ans, _ = self.divide_conquer(root) return ans def divide_conquer(self, node): if not node: return float('-inf'), 0 max_left, left = self.divide_conquer(node.left) max_right, right = self.divide_conquer(node.right) # 0 means discard the negative path res = max(max_left, max_right, node.val + left + right) path = max(node.val + left, node.val + right, 0) return res, path class Solution: def maxPathSum(self, root): """ :type root: TreeNode :rtype: int """ if not root: return 0 self.ans = float('-inf') self.divide_conquer(root) return self.ans def divide_conquer(self, node): if not node: return 0 left = max(0, self.divide_conquer(node.left)) right = max(0, self.divide_conquer(node.right)) if node.val + left + right > self.ans: self.ans = node.val + left + right return node.val + max(left, right) ================================================ FILE: leetcode/134_gas_station.py ================================================ class Solution: """ Main Concept: if the tank is enough, go to next, otherwise back to previous to get gas """ def canCompleteCircuit(self, gas, cost): """ :type gas: List[int] :type cost: List[int] :rtype: int """ NOT_FOUND = -1 if not gas or not cost or len(gas) != len(cost): return NOT_FOUND end, start = -1, len(gas) - 1 # since its a circle, end start from `-1` means `n - 1` tank = gas[start] - cost[start] while start > end: if tank >= 0: end += 1 tank += gas[end] - cost[end] else: start -= 1 tank += gas[start] - cost[start] return start if tank >= 0 else NOT_FOUND class Solution: """ TLE: Simulate the process """ def canCompleteCircuit(self, gas, cost): """ :type gas: List[int] :type cost: List[int] :rtype: int """ NOT_FOUND = -1 if not gas or not cost or len(gas) != len(cost): return NOT_FOUND n = len(gas) RANGE = list(range(n)) for start in range(n): tank = 0 is_failed = False for mid in RANGE[start:n] + RANGE[:start]: tank += gas[mid] if tank < cost[mid]: is_failed = True break tank -= cost[mid] if not is_failed: return start return NOT_FOUND ================================================ FILE: leetcode/136_single_number.py ================================================ class Solution: def singleNumber(self, nums): """ :type nums: List[int] :rtype: int """ ans = 0 for num in nums: ans ^= num return ans ================================================ FILE: leetcode/149_max_points_on_a_line.py ================================================ # Definition for a point. # class Point: # def __init__(self, a=0, b=0): # self.x = a # self.y = b class Solution: def maxPoints(self, P): """ :type P: List[Point] :rtype: int """ ans = 0 if not P: return ans n = len(P) if n <= 2: return n for i in range(n): S = {} points = dups = 0 for j in range(i + 1, n): dx = P[i].x - P[j].x dy = P[i].y - P[j].y if dx == 0 and dy == 0: dups += 1 continue gcd = self.get_gcd(dx, dy) if gcd: dx //= gcd dy //= gcd key = (dx, dy) S[key] = S.get(key, 0) + 1 if S[key] > points: points = S[key] if points + dups + 1 > ans: ans = points + dups + 1 return ans def get_gcd(self, a, b): if b == 0: return a return self.get_gcd(b, a % b) ================================================ FILE: leetcode/14_longest_common_prefix.py ================================================ class Solution: def longestCommonPrefix(self, strs): """ :type strs: List[str] :rtype: str """ if not strs or not strs[0]: return '' t = strs[0] for i in range(len(t)): for s in strs: if i >= len(s) or s[i] != t[i]: return t[:i] return t ================================================ FILE: leetcode/150_evaluate_reverse_polish_notation.py ================================================ class Solution: def evalRPN(self, E): """ :type E: List[str] :rtype: int """ if not E: return 0 stack = [] operation = { '+': lambda a, b: a + b, '-': lambda a, b: a - b, '*': lambda a, b: a * b, '/': lambda a, b: a / b, } for char in E: if char not in operation: stack.append(int(char)) continue b = stack.pop() a = stack.pop() stack.append(int(operation[char](a, b))) return stack[0] ================================================ FILE: leetcode/157_read_n_characters_given_read4.py ================================================ """ The read4 API is already defined for you. @param buf, a list of characters @return an integer def read4(buf): pass """ class Solution: def read(self, buf, n): """ :type buf: List[str], Destination buffer :type n: int, Maximum number of characters to read :rtype: int, The number of characters read """ if not buf or n <= 0: return 0 i = 0 k = 4 tmp = [0] * k while i < n and k == 4: k = read4(tmp) j = 0 while i < n and j < k: buf[j] = tmp[j] i += 1 j += 1 return i if __name__ == '__main__': data = 'abcdferrdsjfklsdjfdsr' n = len(data) i = 0 k = 4 def read4(buf): global i j = 0 while i < n and j < k: buf[j] = data[i] i += 1 j += 1 return j s = Solution() res = s.read([0] * 4, 4) print(res) ================================================ FILE: leetcode/160_intersection_of_two_linked_lists.py ================================================ # Definition for singly-linked list. # class ListNode(object): # def __init__(self, x): # self.val = x # self.next = None class Solution(object): def getIntersectionNode(self, A, B): """ :type A, B: ListNode :rtype: ListNode """ if not A or not B: return X, Y = A, B while X and Y: X = X.next Y = Y.next while X: X = X.next A = A.next while Y: Y = Y.next B = B.next while A is not B: A = A.next B = B.next return A ================================================ FILE: leetcode/165_compare_version_numbers.py ================================================ class Solution: def compareVersion(self, version1, version2): """ :type version1: str :type version2: str :rtype: int """ if not version1 and not version2: return 0 if not version1: return -1 if not version2: return 1 v = version1.split('.') w = version2.split('.') m, n = len(v), len(w) for i in range(max(m, n)): a = self.get_int(v[i]) if i < m else 0 b = self.get_int(w[i]) if i < n else 0 if a < b: return -1 elif a > b: return 1 return 0 def get_int(self, s): if not s or not s.isdigit(): return 0 res = 0 zero = ord('0') for c in s: res = res * 10 + ord(c) - zero return res ================================================ FILE: leetcode/166_fraction_to_recurring_decimal.py ================================================ class Solution: def fractionToDecimal(self, a, b): """ :type a: int :type b: int :rtype: str """ if not b: return '' if not a: return '0' ans = [] if a ^ b < 0: ans.append('-') if a < 0: a = -a if b < 0: b = -b ans.append(str(a // b)) a %= b if a == 0: return ''.join(ans) ans.append('.') D = {a: len(ans)} # the index of first occurrence of `a` while a: a *= 10 ans.append(str(a // b)) a %= b if a in D: ans.insert(D[a], '(') ans.append(')') break D[a] = len(ans) return ''.join(ans) ================================================ FILE: leetcode/169_majority_element.py ================================================ class Solution: def majorityElement(self, nums): """ :type nums: List[int] :rtype: int """ if not nums: return 0 ans = None cnt = 0 for num in nums: if cnt == 0: ans, cnt = num, 1 elif ans == num: cnt += 1 else: cnt -= 1 return ans class Solution: def majorityElement(self, nums): """ :type nums: List[int] :rtype: int """ if not nums: return 0 nums.sort() return nums[len(nums) // 2] class Solution: def majorityElement(self, nums): """ :type nums: List[int] :rtype: int """ NOT_FOUND = 0 if not nums: return NOT_FOUND freq = {} for a in nums: freq[a] = freq.get(a, 0) + 1 for a, cnt in freq.items(): if cnt > len(nums) // 2: return a return NOT_FOUND ================================================ FILE: leetcode/171_excel_sheet_column_number.py ================================================ class Solution: def titleToNumber(self, s): """ :type s: str :rtype: int """ if not s: return 0 s = s.upper() ans = 0 BASE = ord('A') - 1 for char in s: ans = ans * 26 + ord(char) - BASE return ans ================================================ FILE: leetcode/172_factorial_trailing_zeroes.py ================================================ class Solution: def trailingZeroes(self, n): """ :type n: int :rtype: int """ ans = 0 if not n: return ans i = 5 while n // i > 0: ans += n // i i *= 5 return ans ================================================ FILE: leetcode/174_dungeon_game.py ================================================ class Solution: def calculateMinimumHP(self, G): """ :type G: List[List[int]] :rtype: int """ INFINITY = float('inf') m, n = len(G), len(G[0]) dp = [[INFINITY] * (n + 1) for _ in range(m + 1)] dp[m][n - 1] = 1 dp[m - 1][n] = 1 for i in range(m - 1, -1, -1): for j in range(n - 1, -1, -1): dp[i][j] = min(dp[i + 1][j], dp[i][j + 1]) - G[i][j] if dp[i][j] <= 0: dp[i][j] = 1 return dp[0][0] ================================================ FILE: leetcode/179_largest_number.py ================================================ """ REF: https://leetcode.com/articles/largest-number/ """ class LgSort(str): def __lt__(a, b): return a + b > b + a class Solution: def largestNumber(self, A): """ :type A: List[int] :rtype: str """ A = ''.join(sorted(map(str, A), key=LgSort)) return '0' if A[0] == '0' else A ================================================ FILE: leetcode/17_letter_combinations_of_a_phone_number.py ================================================ class Solution: def letterCombinations(self, s): """ :type s: str :rtype: List[str] """ if not s: return [] L = { '2': 'abc', '3': 'def', '4': 'ghi', '5': 'jkl', '6': 'mno', '7': 'pqrs', '8': 'tuv', '9': 'wxyz', } queue, _queue = [], [] for d in s: if d not in L: return [] if not queue: queue.extend(list(L[d])) continue for c in L[d]: for _c in queue: _queue.append(_c + c) queue, _queue = _queue, [] queue.sort() return queue ================================================ FILE: leetcode/190_reverse_bits.py ================================================ # Reverse bit every 4 bits class Solution: # @param n, an integer # @return an integer def reverseBits(self, n): if not n: return 0 # the reversed bins for i = 0 -> 15 rev = [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15] ans = 0 msk = 15 # 15 (10) == 1111 (2) for i in range(8): ans <<= 4 curr = n & msk ans |= rev[curr] n >>= 4 return ans # Reverse bit every 1 bits class Solution: # @param n, an integer # @return an integer def reverseBits(self, n): ans = 0 if not n: return ans for i in range(32): ans <<= 1 ans |= n & 1 n >>= 1 return ans ================================================ FILE: leetcode/191_number_of_1_bits.py ================================================ class Solution: def hammingWeight(self, n): """ :type n: int :rtype: int """ ans = 0 if not n: return ans while n != 0: n = n & (n - 1) ans += 1 return ans ================================================ FILE: leetcode/19_remove_nth_node_from_end_of_list.py ================================================ """ Definition for singly-linked list. class ListNode: def __init__(self, x): self.val = x self.next = None """ class Solution: def removeNthFromEnd(self, head, n): """ :type head: ListNode :type n: int :rtype: ListNode """ if not head or not n: return head dummy = slow = ListNode(0) dummy.next = fast = head while fast and n: n -= 1 fast = fast.next while slow and fast: slow = slow.next fast = fast.next if slow and slow.next: slow.next = slow.next.next return dummy.next ================================================ FILE: leetcode/202_happy_number.py ================================================ class Solution: def isHappy(self, n): """ :type n: int :rtype: bool """ slow = self.square_sum(n) fast = self.square_sum(slow) while slow != fast: slow = self.square_sum(slow) fast = self.square_sum( self.square_sum(fast) ) return True if slow == 1 else False def square_sum(self, a): res = b = 0 while a != 0: b = a % 10 res += b * b a //= 10 return res ================================================ FILE: leetcode/204_count_primes.py ================================================ """ REF: https://discuss.leetcode.com/topic/14036/fast-python-solution Count the number of prime numbers LESS THAN a non-negative number, n. """ class Solution: def countPrimes(self, n): """ :type n: int :rtype: int """ # note that 2 is prime # but there is no prime less than 2 # so return 0 if n == 2 if not n or n < 3: return 0 is_prime = [True] * n is_prime[0] = is_prime[1] = False for i in range(2, int(n ** 0.5) + 1): if not is_prime[i]: continue for j in range(i * i, n, i): is_prime[j] = False return sum(is_prime) ================================================ FILE: leetcode/208_implement_trie_prefix_tree.py ================================================ class TrieNode: def __init__(self, val=None): self.end_at = val self.children = {} class Trie: def __init__(self): self.trie = TrieNode() def insert(self, word): """ Inserts a word into the trie. :type word: str :rtype: void """ if not word: return node = self.trie for char in word: if char not in node.children: node.children[char] = TrieNode() node = node.children[char] node.end_at = word def search(self, word): """ Returns if the word is in the trie. :type word: str :rtype: bool """ if not word: return False node = self.trie for char in word: if char not in node.children: return False node = node.children[char] return node.end_at == word def startsWith(self, prefix): """ Returns if there is any word in the trie that starts with the given prefix. :type prefix: str :rtype: bool """ if not prefix: return False node = self.trie for char in prefix: if char not in node.children: return False node = node.children[char] return True # Your Trie object will be instantiated and called as such: # obj = Trie() # obj.insert(word) # param_2 = obj.search(word) # param_3 = obj.startsWith(prefix) ================================================ FILE: leetcode/20_valid_parentheses.py ================================================ class Solution: def isValid(self, s): """ :type s: str :rtype: bool """ stack = [] pairs = {')': '(', ']': '[', '}': '{'} for c in s: if c in '([{': stack.append(c) elif c not in ')]}': return False elif not stack or pairs[c] != stack.pop(): return False return not stack ================================================ FILE: leetcode/215_kth_largest_element_in_an_array.py ================================================ from heapq import heappop, heappush class Solution: def findKthLargest(self, A, k): """ :type A: List[int] :type k: int :rtype: int """ heap = [] for a in A: heappush(heap, a) if len(heap) > k: heappop(heap) return heappop(heap) ================================================ FILE: leetcode/217_contains_duplicate.py ================================================ class Solution: def containsDuplicate(self, A): """ :type A: List[int] :rtype: bool """ if not A: return False S = set() for a in A: if a in S: return True S.add(a) return False ================================================ FILE: leetcode/218_the_skyline_problem.py ================================================ """ REF: https://briangordon.github.io/2014/08/the-skyline-problem.html """ import heapq class HashHeapq: def __init__(self): self.heap = [] self.deleted = {} def push(self, val): heapq.heappush(self.heap, val) def pop(self): if self.is_empty(): return -1 return heapq.heappop(self.heap) def remove(self, val): if self.is_empty(): return if val not in self.deleted: self.deleted[val] = 0 self.deleted[val] += 1 def top(self): if self.is_empty(): return -1 return self.heap[0] def is_empty(self): while self.heap and self.deleted.get(self.heap[0]): val = heapq.heappop(self.heap) self.deleted[val] -= 1 return not self.heap class Solution: def getSkyline(self, buildings): """ :type buildings: List[List[int]] :rtype: List[List[int]] """ ans = [] if not buildings: return ans time = [] for x, _x, height in buildings: time.append((x, height, True)) time.append((_x, height, False)) time.sort() heap = HashHeapq() for x, height, is_start in time: if is_start: heap.push(-height) else: heap.remove(-height) max_h = -heap.top() if not heap.is_empty() else 0 if ans and ans[-1][0] == x: ans.pop() if ans and ans[-1][1] == max_h: continue ans.append([x, max_h]) return ans ================================================ FILE: leetcode/224_basic_calculator.py ================================================ class Solution: def calculate(self, s): """ :type s: str :rtype: int """ if not s: return 0 self.OP = { '+': lambda a, b: a + b, '-': lambda a, b: a - b, } s = self.to_rpn(s) if not s: return 0 return self.eval_rpn(s) def to_rpn(self, s): stack, res = [], [] for i in range(len(s)): char = s[i] if i > 0 and s[i - 1].isdigit() and char.isdigit(): res[-1] += char elif char.isdigit(): res.append(char) elif char in self.OP: while stack and stack[-1] in self.OP: res.append(stack.pop()) stack.append(char) elif char == '(': stack.append(char) elif char == ')': while stack and stack[-1] != '(': res.append(stack.pop()) stack.pop() while stack: res.append(stack.pop()) return res def eval_rpn(self, s): stack = [] for char in s: if char.isdigit(): stack.append(int(char)) elif char in self.OP: b = stack.pop() a = stack.pop() stack.append(self.OP[char](a, b)) return stack[0] class Solution: def calculate(self, s): """ :type s: str :rtype: int """ if not s: return 0 s = self.to_rpn(s) if not s: return 0 return self.eval_rpn(s) def to_rpn(self, s): stack, res = [], [] for i in range(len(s)): char = s[i] if i > 0 and s[i - 1].isdigit() and char.isdigit(): res[-1] += char elif char.isdigit(): res.append(char) elif char in '+-': while stack and stack[-1] in '+-': res.append(stack.pop()) stack.append(char) elif char == '(': stack.append(char) elif char == ')': while stack and stack[-1] != '(': res.append(stack.pop()) stack.pop() while stack: res.append(stack.pop()) return res def eval_rpn(self, s): stack = [] for char in s: if char.isdigit(): stack.append(int(char)) elif char in '+-': b = stack.pop() a = stack.pop() if char == '+': stack.append(a + b) elif char == '-': stack.append(a - b) return stack[0] ================================================ FILE: leetcode/227_basic_calculator_ii.py ================================================ class Solution: def calculate(self, s): """ :type s: str :rtype: int """ if not s: return 0 s = self.to_rpn(s, { '+': 1, '-': 1, '*': 2, '/': 2, }) if not s: return 0 return self.eval_rpn(s, { '+': lambda a, b: a + b, '-': lambda a, b: a - b, '*': lambda a, b: a * b, '/': lambda a, b: a // b, }) def to_rpn(self, s, P): stack, res = [], [] for i in range(len(s)): char = s[i] if i > 0 and s[i - 1].isdigit() and char.isdigit(): res[-1] += char elif char.isdigit(): res.append(char) elif char in P: while stack and stack[-1] in P and P[char] <= P[stack[-1]]: res.append(stack.pop()) stack.append(char) elif char == '(': stack.append(char) elif char == ')': while stack and stack[-1] != '(': res.append(stack.pop()) stack.pop() while stack: res.append(stack.pop()) return res def eval_rpn(self, s, OP): stack = [] for char in s: if char.isdigit(): stack.append(int(char)) elif char in OP: b = stack.pop() a = stack.pop() stack.append(OP[char](a, b)) return stack[0] class Solution: def calculate(self, s): """ :type s: str :rtype: int """ if not s: return 0 s = self.to_rpn(s) if not s: return 0 return self.eval_rpn(s) def to_rpn(self, s): stack, res = [], [] for i in range(len(s)): char = s[i] if i > 0 and s[i - 1].isdigit() and char.isdigit(): res[-1] += char elif char.isdigit(): res.append(char) elif char in '+-*/': while stack and stack[-1] in '+-*/': if char in '*/' and stack[-1] in '+-': break res.append(stack.pop()) stack.append(char) elif char == '(': stack.append(char) elif char == ')': while stack and stack[-1] != '(': res.append(stack.pop()) stack.pop() while stack: res.append(stack.pop()) return res def eval_rpn(self, s): stack = [] for char in s: if char.isdigit(): stack.append(int(char)) elif char in '+-*/': b = stack.pop() a = stack.pop() if char == '+': stack.append(a + b) elif char == '-': stack.append(a - b) elif char == '*': stack.append(a * b) elif char == '/': stack.append(a // b) return stack[0] ================================================ FILE: leetcode/229_majority_element_ii.py ================================================ class Solution: def majorityElement(self, nums): """ :type nums: List[int] :rtype: List[int] """ ans = [] if not nums: return [] a1 = a2 = None c1 = c2 = 0 for num in nums: if a1 == num: c1 += 1 elif a2 == num: c2 += 1 elif c1 == 0: a1, c1 = num, 1 elif c2 == 0: a2, c2 = num, 1 else: c1 -= 1 c2 -= 1 c1 = c2 = 0 for num in nums: if num == a1: c1 += 1 elif num == a2: c2 += 1 for a, c in ((a1, c1), (a2, c2)): if c > len(nums) // 3: ans.append(a) return ans ================================================ FILE: leetcode/22_generate_parentheses.py ================================================ class Solution: def generateParenthesis(self, n): """ :type n: int :rtype: list[str] """ ans = [] if not n: return ans self.dfs(n, 1, 0, ans, ['(']) return ans def dfs(self, n, lcnt, rcnt, ans, path): if len(path) == 2 * n: ans.append(''.join(path)) return if rcnt < lcnt: path.append(')') self.dfs(n, lcnt, rcnt + 1, ans, path) path.pop() if lcnt < n: path.append('(') self.dfs(n, lcnt + 1, rcnt, ans, path) path.pop() ================================================ FILE: leetcode/230_kth_smallest_element_in_a_bst.py ================================================ """ Definition for a binary tree node. class TreeNode: def __init__(self, x): self.val = x self.left = None self.right = None """ class Solution: def kthSmallest(self, root, k): """ :type root: TreeNode :type k: int :rtype: int """ if not root: return stack = [] node = root while node or stack: while node: stack.append(node) node = node.left node = stack.pop() k -= 1 if k == 0: return node.val node = node.right ================================================ FILE: leetcode/234_palindrome_linked_list.py ================================================ # Definition for singly-linked list. # class ListNode: # def __init__(self, x): # self.val = x # self.next = None class Solution: def isPalindrome(self, head): """ :type head: ListNode :rtype: bool """ rev = None slow = fast = head while fast and fast.next: fast = fast.next.next rev, rev.next, slow = slow, rev, slow.next if fast: slow = slow.next while slow and slow.val == rev.val: slow = slow.next rev = rev.next return not rev class Solution: def isPalindrome(self, head): """ :type head: ListNode :rtype: bool """ rev = nxt = None slow = fast = head while fast and fast.next: fast = fast.next.next nxt = slow.next slow.next = rev rev = slow slow = nxt if fast: slow = slow.next while slow and slow.val == rev.val: slow = slow.next rev = rev.next return not rev ================================================ FILE: leetcode/235_lowest_common_ancestor_of_a_binary_search_tree.py ================================================ # Definition for a binary tree node. # class TreeNode(object): # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution(object): def lowestCommonAncestor(self, root, p, q): """ :type root: TreeNode :type p: TreeNode :type q: TreeNode :rtype: TreeNode """ while root: if root.val > p.val and root.val > q.val: root = root.left elif root.val < p.val and root.val < q.val: root = root.right else: return root ================================================ FILE: leetcode/236_lowest_common_ancestor_of_a_binary_tree.py ================================================ # Definition for a binary tree node. # class TreeNode(object): # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution(object): def lowestCommonAncestor(self, root, p, q): """ :type root: TreeNode :type p: TreeNode :type q: TreeNode :rtype: TreeNode """ if not root or root is p or root is q: return root left = self.lowestCommonAncestor(root.left, p, q) right = self.lowestCommonAncestor(root.right, p, q) if left and right: return root if left: return left if right: return right ================================================ FILE: leetcode/237_delete_node_in_a_linked_list.py ================================================ # Definition for singly-linked list. # class ListNode(object): # def __init__(self, x): # self.val = x # self.next = None class Solution(object): def deleteNode(self, node): """ :type node: ListNode :rtype: void Do not return anything, modify node in-place instead. """ if not node or not node.next: return node.val = node.next.val node.next = node.next.next ================================================ FILE: leetcode/242_valid_anagram.py ================================================ class Solution: def isAnagram(self, s, t): """ :type s: str :type t: str :rtype: bool """ if s == t: return True if not s or not t or len(s) != len(t): return False freq = {} for c in s: freq[c] = freq.get(c, 0) + 1 for c in t: if c not in freq: return False freq[c] -= 1 return all(v == 0 for v in freq.values()) ================================================ FILE: leetcode/269_alien_dictionary.py ================================================ class Solution: def alienOrder(self, words): """ :type words: list[str] :rtype: str """ if not words: return '' ans = [] gotcha = set() max_size = max(len(word) for word in words) for j in range(max_size): for word in words: if j >= len(word): continue if word[j] in gotcha: continue ans.append(word[j]) gotcha.add(word[j]) return ''.join(ans) class Solution: """ REF: https://discuss.leetcode.com/topic/28308/java-ac-solution-using-bfs/2 """ def alienOrder(self, words): if not words: return '' ans = [] edges = {} indeg = {} for w in words: for c in w: indeg[c] = 0 for i in range(len(words) - 1): cur = words[i] nxt = words[i + 1] for j in range(min(len(cur), len(nxt))): if cur[j] == nxt[j]: continue if cur[j] not in edges: edges[cur[j]] = set() if nxt[j] not in edges[cur[j]]: edges[cur[j]].add(nxt[j]) indeg[nxt[j]] = indeg.get(nxt[j], 0) + 1 break queue = [c for c, deg in indeg.items() if deg == 0] for c in queue: ans.append(c) if c not in edges: continue for _c in edges[c]: indeg[_c] -= 1 if indeg[_c] == 0: queue.append(_c) return ''.join(ans) if len(ans) == len(indeg) else '' ================================================ FILE: leetcode/277_find_the_celebrity.py ================================================ """ REF: https://discuss.leetcode.com/topic/25720/java-python-o-n-calls-o-1-space-easy-to-understand-solution """ class Solution: def findCelebrity(self, n): if not n: return -1 x = 0 for i in range(n): if knows(x, i): x = i for i in range(x): if knows(x, i): return -1 for i in range(n): if not knows(i, x): return -1 return x ================================================ FILE: leetcode/280_wiggle_sort.py ================================================ class Solution: def wiggleSort(self, nums): """ :type nums: list[int] :rtype: void, Do not return anything, modify A in-place instead. """ if not nums: return for i in range(1, len(nums)): if i & 1 == 1 and nums[i] >= nums[i - 1]: continue if i & 1 == 0 and nums[i] <= nums[i - 1]: continue nums[i], nums[i - 1] = nums[i - 1], nums[i] ================================================ FILE: leetcode/282_expression_add_operators.py ================================================ """ Main Concept: in product case, needs to remove product in last recursion, and adds the product in current. """ class Solution: def addOperators(self, s, target): """ :type s: str :type target: int :rtype: List[str] """ ans = [] if not s: return ans self.dfs(s, 0, target, 0, 0, ans, []) return ans def dfs(self, s, start, target, val, multi, ans, path): if start == len(s) and target == val: ans.append(''.join(path)) return if start >= len(s): return for i in range(start, len(s)): if i > start and s[start] == '0': # only allow i == start and its `0` break sa = s[start:i + 1] a = int(sa) if start == 0: self.dfs(s, i + 1, target, a, a, ans, [sa]) continue self.dfs(s, i + 1, target, val + a, a, ans, path + ['+', sa]) self.dfs(s, i + 1, target, val - a, -a, ans, path + ['-', sa]) self.dfs(s, i + 1, target, val - multi + multi * a, multi * a, ans, path + ['*', sa]) ================================================ FILE: leetcode/285_inorder_successor_in_bst.py ================================================ """ Definition for a binary tree node. >>> class TreeNode: ... def __init__(self, x): ... self.val = x ... self.left = None ... self.right = None ... self.parent = None >>> trees = [] >>> tree_infos = [ ... (( ... (1, None, None, 2), ... (2, 1, None, None), ... ), '1,2'), ... (( ... (20, None, 8, 22), ... (8, 20, 4, 12), ... (22, 20, None, None), ... (4, 8, None, None), ... (12, 8, 10, 14), ... (10, 12, None, None), ... (14, 12, None, None), ... ), '4,8,10,12,14,20,22'), ... (( ... (10, None, 5, 100), ... (5, 10, None, None), ... (100, 10, 80, 120), ... (80, 100, None, None), ... (120, 100, None, None), ... ), '5,10,80,100,120'), ... (( ... (1, None, None, 2), ... (2, 1, None, 3), ... (3, 2, None, 4), ... (4, 3, None, 5), ... (5, 4, None, 6), ... (6, 5, None, 7), ... (7, 6, None, None), ... ), '1,2,3,4,5,6,7'), ... ] >>> for info, _ in tree_infos: ... nodes = {node[0]: TreeNode(node[0]) for node in info} ... ... for val, parent, left, right in info: ... if parent: ... nodes[val].parent = nodes[parent] ... if left: ... nodes[val].left = nodes[left] ... if right: ... nodes[val].right = nodes[right] ... ... trees.append(nodes[info[0][0]]) >>> gotcha = [] >>> for _in, _out in ( ... ((trees[0], trees[0]), trees[0].right), ... ((trees[1], trees[1].left.right.right), trees[1]), ... ): ... for s in (Solution(), Solution2(), Solution3(), Solution4()): ... res = s.inorderSuccessor(*_in) ... if res is not _out: print(_in[0].val, res.val) ... gotcha.append(res is _out) >>> bool(gotcha) and all(gotcha) True """ class Solution: """ time: O(log n) or O(h), `n` is the number of nodes and `h` is the tree height 1. if `target` has right child, then successor lies at the most-left child in right child of `target` 2. otherwise, just traverse down from root, and record `ans` when every time go left """ def inorderSuccessor(self, root, target): """ :type root: TreeNode :type target: TreeNode :rtype: TreeNode """ if not root or not target: return ans = None if target.right: ans = target.right while ans and ans.left: ans = ans.left return ans while root and target.val != root.val: if target.val < root.val: ans = root root = root.left else: root = root.right return ans class Solution2: """ time: O(n), `n` is the number of nodes just do inorder traverse """ def inorderSuccessor(self, root, target): """ :type root: TreeNode :type target: TreeNode :rtype: TreeNode """ if not root or not target: return stack = [] node = root got_target = False while node or stack: while node: stack.append(node) node = node.left node = stack.pop() if got_target: return node if node.val == target.val: got_target = True node = node.right class Solution3: """ * every node has `parent` pointer time: O(log n) or O(h), `n` is the number of nodes and `h` is the tree height 1. if `target` has right child, then successor lies at the most-left child in right child of `target` 2. otherwise, just traverse top to root """ def inorderSuccessor(self, root, target): """ :type root: TreeNode :type target: TreeNode :rtype: TreeNode """ if not root or not target: return ans = None if target.right: ans = target.right while ans and ans.left: ans = ans.left return ans ans = target.parent while ans and target is ans.right: target = ans ans = ans.parent return ans class Solution4: """ * every node has `parent` pointer time: O(log n) or O(h), `n` is the number of nodes and `h` is the tree height 1. if `target` has right child, then successor lies at the most-left child in right child of `target` 2. otherwise, just traverse top to root """ def inorderSuccessor(self, root, target): """ :type root: TreeNode :type target: TreeNode :rtype: TreeNode """ if not root or not target: return if target.right: ans = target.right while ans and ans.left: ans = ans.left return ans ans = target.parent while ans and ans.val < target.val: ans = ans.parent return ans ================================================ FILE: leetcode/288_unique_word_abbreviation.py ================================================ """ Your ValidWordAbbr object will be instantiated and called as such: vwa = ValidWordAbbr(dictionary) vwa.isUnique("word") vwa.isUnique("anotherWord") Testing: >>> s = ValidWordAbbr(['deer', 'door', 'cake', 'card']) >>> all(( ... s.isUnique('dear') is False, ... s.isUnique('cart') is True, ... s.isUnique('cane') is False, ... s.isUnique('make') is True, ... )) True """ class ValidWordAbbr: def __init__(self, dictionary): """ initialize your data structure here. :type dictionary: List[str] """ self.abbrs = {} for word in dictionary: abbr = self.abbreviation(word) self.abbrs[abbr] = word def isUnique(self, word): """ check if a word is unique. :type word: str :rtype: bool """ abbr = self.abbreviation(word) return abbr not in self.abbrs def abbreviation(self, word): if len(word) < 3: return word cnt = len(word) - 2 return '{}{}{}'.format( word[0], str(cnt), word[-1] ) ================================================ FILE: leetcode/289_game_of_life.py ================================================ """ REF: https://leetcode.com/problems/game-of-life/discuss/73223 lives < 2 => 1 -> 0 lives == 2 or 3 => 1 -> 1 lives > 3 => 1 -> 0 lives == 3 => 0 -> 1 """ class Solution: """ Use bits to save the status in next round """ def gameOfLife(self, board): """ :type board: List[List[int]] :rtype: void Do not return anything, modify board in-place instead. """ if not board or not board[0]: return m, n = len(board), len(board[0]) for x in range(m): for y in range(n): lives = self.get_live_neibs(board, x, y) if board[x][y] == 1 and lives in (2, 3): board[x][y] = 3 elif board[x][y] == 0 and lives == 3: board[x][y] = 2 for x in range(m): for y in range(n): board[x][y] >>= 1 def get_live_neibs(self, board, x, y): cnt = 0 m, n = len(board), len(board[0]) for dx in (-1, 0, 1): for dy in (-1, 0, 1): if dx == 0 and dy == 0: continue _x = x + dx _y = y + dy if not (0 <= _x < m and 0 <= _y < n): continue cnt += board[_x][_y] & 1 return cnt class Solution: """ Not in-place solution """ def gameOfLife(self, board): """ :type board: List[List[int]] :rtype: void Do not return anything, modify board in-place instead. """ if not board or not board[0]: return m, n = len(board), len(board[0]) ans = [[0] * n for _ in range(m)] for x in range(m): for y in range(n): lives = self.get_live_neibs(board, x, y) ans[x][y] = board[x][y] if board[x][y] == 1 and lives < 2: ans[x][y] = 0 elif board[x][y] == 1 and lives in (2, 3): ans[x][y] = 1 elif board[x][y] == 1 and lives > 3: ans[x][y] = 0 elif board[x][y] == 0 and lives == 3: ans[x][y] = 1 # return ans # hacking for in-place for x in range(m): board[x][:] = ans[x][:] def get_live_neibs(self, board, x, y): cnt = 0 m, n = len(board), len(board[0]) for dx in (-1, 0, 1): for dy in (-1, 0, 1): if dx == 0 and dy == 0: continue _x = x + dx _y = y + dy if not ( 0 <= _x < m and 0 <= _y < n and board[_x][_y] == 1 ): continue cnt += 1 return cnt ================================================ FILE: leetcode/295_find_median_from_data_stream.py ================================================ """ Your MedianFinder object will be instantiated and called as such: obj = MedianFinder() obj.addNum(num) param_2 = obj.findMedian() """ import heapq class MedianFinder: def __init__(self): self.minheap = [] self.maxheap = [] def addNum(self, num): """ :type num: int :rtype: void """ if self.minheap and num < self.minheap[0]: heapq.heappush(self.maxheap, -num) else: heapq.heappush(self.minheap, num) def findMedian(self): """ :rtype: float """ if not self.minheap: return 0.0 # to handle odd case, it make sure `minheap` has one more child than `maxheap` while len(self.minheap) > len(self.maxheap) + 1: heapq.heappush(self.maxheap, - heapq.heappop(self.minheap)) # to handle even case, it make sure `minheap` and `maxheap` are same size while len(self.maxheap) > len(self.minheap): heapq.heappush(self.minheap, - heapq.heappop(self.maxheap)) if len(self.minheap) > len(self.maxheap): return self.minheap[0] * 1.0 # since the child in maxheap is negative return (self.minheap[0] - self.maxheap[0]) / 2.0 ================================================ FILE: leetcode/299_bulls_and_cows.py ================================================ class Solution: def getHint(self, secret, guess): """ :type secret: str :type guess: str :rtype: str """ if not secret or not guess or len(secret) != len(guess): return '' TMPL = '{}A{}B' bulls = 0 cows = 0 cnts = [0] * 10 for i in range(len(secret)): s = ord(secret[i]) - ord('0') g = ord(guess[i]) - ord('0') if s == g: bulls += 1 continue cnts[s] += 1 cnts[g] -= 1 if cnts[s] <= 0: cows += 1 if cnts[g] >= 0: cows += 1 return TMPL.format(bulls, cows) class Solution: def getHint(self, secret, guess): """ :type secret: str :type guess: str :rtype: str """ if not secret or not guess or len(secret) != len(guess): return '' TMPL = '{}A{}B' bulls = 0 cows = 0 cnt_s = [0] * 10 cnt_g = [0] * 10 for i in range(len(secret)): if secret[i] == guess[i]: bulls += 1 else: cnt_s[int(secret[i])] += 1 cnt_g[int(guess[i])] += 1 for i in range(10): cows += min(cnt_s[i], cnt_g[i]) return TMPL.format(bulls, cows) ================================================ FILE: leetcode/303_range_sum_query_immutable.py ================================================ """ Your NumArray object will be instantiated and called as such: obj = NumArray(nums) param_1 = obj.sumRange(i,j) """ class NumArray: def __init__(self, nums): """ :type nums: List[int] """ if not nums: return n = len(nums) self.prefix_sum = [0] * (n + 1) for i in range(1, n + 1): self.prefix_sum[i] = self.prefix_sum[i - 1] + nums[i - 1] def sumRange(self, i, j): """ :type i: int :type j: int :rtype: int """ if ( not self.prefix_sum or i < 0 or j + 1 >= len(self.prefix_sum) ): return 0 return self.prefix_sum[j + 1] - self.prefix_sum[i] ================================================ FILE: leetcode/304_range_sum_query_2d_immutable.py ================================================ """ Your NumMatrix object will be instantiated and called as such: obj = NumMatrix(matrix) param_1 = obj.sumRegion(x1, y1, x2, y2) """ class NumMatrix: def __init__(self, matrix): """ :type matrix: List[List[int]] """ if not matrix or not matrix[0]: return m, n = len(matrix), len(matrix[0]) self.prefix_sum = [[0] * (n + 1) for _ in range(m + 1)] for x in range(1, m + 1): for y in range(1, n + 1): self.prefix_sum[x][y] = sum(( self.prefix_sum[x - 1][y], self.prefix_sum[x][y - 1], - self.prefix_sum[x - 1][y - 1], matrix[x - 1][y - 1], )) def sumRegion(self, x1, y1, x2, y2): """ :type x1: int :type y1: int :type x2: int :type y2: int :rtype: int """ if not all(( self.prefix_sum, self.prefix_sum[0], 0 <= x1 < len(self.prefix_sum), 0 <= x2 + 1 < len(self.prefix_sum), 0 <= y1 < len(self.prefix_sum[0]), 0 <= y2 + 1 < len(self.prefix_sum[0]), )): return 0 return sum(( self.prefix_sum[x2 + 1][y2 + 1], - self.prefix_sum[x2 + 1][y1], - self.prefix_sum[x1][y2 + 1], self.prefix_sum[x1][y1], )) ================================================ FILE: leetcode/307_range_sum_query_mutable.py ================================================ """ REF: https://cs.stackexchange.com/questions/10538/bit-what-is-the-intuition-behind-a-binary-indexed-tree-and-how-was-it-thought-a REF: http://www.cnblogs.com/grandyang/p/4985506.html Your NumArray object will be instantiated and called as such: obj = NumArray(nums) obj.update(i,val) param_2 = obj.sumRange(i,j) """ class NumArray: def __init__(self, nums): """ :type nums: List[int] """ if not nums: return n = len(nums) self.bits = [0] * (n + 1) # bits self.incr = [0] * (n + 1) # increments for i in range(n): self.update(i, nums[i]) def update(self, i, val): """ :type i: int :type val: int :rtype: void It must increase `i` here, since this api is public, so look from outside, the `i` is just the index of `nums` """ j = i + 1 delta = val - self.incr[j] self.incr[j] = val while j < len(self.incr): self.bits[j] += delta j += (j & -j) def sumRange(self, i, j): """ :type i: int :type j: int :rtype: int """ return self.sum(j + 1) - self.sum(i) def sum(self, i): res = 0 j = i while j > 0: res += self.bits[j] j -= (j & -j) return res ================================================ FILE: leetcode/308_range_sum_query_2d_mutable.py ================================================ """ Your NumMatrix object will be instantiated and called as such: obj = NumMatrix(matrix) obj.update(x,y,val) param_1 = obj.sumRegion(x1,y1,x2,y2) """ class NumMatrix: def __init__(self, matrix): """ :type matrix: List[List[int]] """ if not matrix or not matrix[0]: return m, n = len(matrix), len(matrix[0]) self.bits = [[0] * (n + 1) for _ in range(m + 1)] # bits self.incr = [[0] * (n + 1) for _ in range(m + 1)] # increments for x in range(m): for y in range(n): self.update(x, y, matrix[x][y]) def update(self, x, y, val): """ :type x: int :type y: int :type val: int :rtype: void """ i = x + 1 j = y + 1 delta = val - self.incr[i][j] self.incr[i][j] = val m, n = len(self.incr), len(self.incr[0]) while i < m: j = y + 1 while j < n: self.bits[i][j] += delta j += (j & -j) i += (i & -i) def sumRegion(self, x1, y1, x2, y2): """ :type x1: int :type y1: int :type x2: int :type y2: int :rtype: int """ return sum(( self.sum(x2 + 1, y2 + 1), - self.sum(x1, y2 + 1), - self.sum(x2 + 1, y1), self.sum(x1, y1), )) def sum(self, x, y): res = 0 i = x j = y while i > 0: j = y while j > 0: res += self.bits[i][j] j -= (j & -j) i -= (i & -i) return res ================================================ FILE: leetcode/30_substring_with_concatenation_of_all_words.py ================================================ class Solution: def findSubstring(self, s, S): """ :type s: str :type S: List[str] :rtype: List[int] """ ans = [] if not s or not S or len(s) < len(S) * len(S[0]): return ans n, m, k = len(s), len(S), len(S[0]) F = {} for c in S: F[c] = F.get(c, 0) + 1 for start in range(k): _F = {} cnt = 0 left = start for right in range(start, n - k + 1, k): sr = s[right:right + k] if sr not in F: _F = {} cnt = 0 left = right + k continue _F[sr] = _F.get(sr, 0) + 1 if _F[sr] <= F[sr]: cnt += 1 while _F[sr] > F[sr]: sl = s[left:left + k] if _F[sl] == F[sl]: cnt -= 1 _F[sl] -= 1 left += k if cnt == m: ans.append(left) sl = s[left:left + k] cnt -= 1 _F[sl] -= 1 left += k return ans ================================================ FILE: leetcode/315_count_of_smaller_numbers_after_self.py ================================================ class Solution: """ REF: https://leetcode.com/problems/count-of-smaller-numbers-after-self/discuss/76657/ """ def countSmaller(self, nums): """ :type nums: List[int] :rtype: List[int] """ if not nums: return [] n = len(nums) ans = [0] * n cands = sorted(set(nums)) v2i = {cands[i]: i for i in range(len(cands))} self.bits = [0] * (len(v2i) + 1) for i in range(n - 1, -1, -1): j = v2i[nums[i]] ans[i] = self.sum(j) self.update(j) return ans def update(self, i): i += 1 while i < len(self.bits): self.bits[i] += 1 i += (i & -i) def sum(self, i): res = 0 while i > 0: res += self.bits[i] i -= (i & -i) return res class Solution: """ Brute Force: TLE """ def countSmaller(self, nums): """ :type nums: List[int] :rtype: List[int] """ ans = [] if not nums: return ans n = len(nums) for i in range(n): ans.append(0) for j in range(i, n): if nums[j] < nums[i]: ans[-1] += 1 return ans ================================================ FILE: leetcode/323_number_of_connected_components_in_an_undirected_graph.py ================================================ """ Testing: >>> gotcha = [] >>> for s in (Solution(), Solution2()): ... for _in, _out in ( ... ((5, [[0, 1], [1, 2], [3, 4]]), 2), ... ((5, [[0, 1], [1, 2], [2, 3], [3, 4]]), 1), ... ): ... res = s.countComponents(*_in) ... if res != _out: print(_in, res) ... gotcha.append(res == _out) >>> bool(gotcha) and all(gotcha) True """ class Solution: """ Union Find """ def countComponents(self, n, edges): """ :type n: int :type edges: list[int] :rtype: int """ if not n or not edges: return 0 nodes = [i for i in range(n)] ans = n for a, b in edges: if self.union(nodes, a, b): ans -= 1 return ans def union(self, nodes, a, b): _a = self.find(nodes, a) _b = self.find(nodes, b) if _a == _b: return False nodes[_b] = _a return True def find(self, nodes, a): if a not in nodes: nodes[a] = a return a if nodes[a] == a: return a nodes[a] = self.find(nodes[a]) return nodes[a] class Solution2: """ DFS """ def countComponents(self, n, edges): """ :type n: int :type edges: list[int] :rtype: int """ ans = 0 if not n or not edges: return ans adj = {} for i in range(n): adj[i] = set() for a, b in edges: adj[a].add(b) adj[b].add(a) visited = set() for i in range(n): if i in visited: continue ans += 1 self.dfs(i, adj, visited) return ans def dfs(self, a, adj, visited): if a not in adj: return visited.add(a) for b in adj[a]: if b in visited: continue self.dfs(b, adj, visited) ================================================ FILE: leetcode/324_wiggle_sort_ii.py ================================================ """ Follow Up: https://discuss.leetcode.com/topic/32929/o-n-o-1-after-median-virtual-indexing """ class Solution: def wiggleSort(self, A): """ :type A: List[int] :rtype: void Do not return anything, modify A in-place instead. """ if not A: return n = len(A) S = sorted(A) for i in range(1, n, 2): A[i] = S.pop() for i in range(0, n, 2): A[i] = S.pop() ================================================ FILE: leetcode/326_power_of_three.py ================================================ class Solution: def isPowerOfThree(self, n): """ :type n: int :rtype: bool """ if not n: return False for i in (81, 27, 9, 3): while n % i == 0: n //= i return n == 1 ================================================ FILE: leetcode/329_longest_increasing_path_in_a_matrix.py ================================================ class Solution: def longestIncreasingPath(self, G): """ :type G: List[List[int]] :rtype: int """ if not G or not G[0]: return 0 self.V = ( (-1, 0), ( 1, 0), ( 0, -1), ( 0, 1), ) ans = 1 m, n = len(G), len(G[0]) memo = [[0] * n for _ in range(m)] for x in range(m): for y in range(n): size = self.dfs(G, x, y, memo) if size > ans: ans = size return ans def dfs(self, G, x, y, memo): if memo[x][y] > 0: return memo[x][y] res = 1 for dx, dy in self.V: _x = x + dx _y = y + dy if not (0 <= _x < len(G) and 0 <= _y < len(G[0])): continue if G[x][y] >= G[_x][_y]: continue size = 1 + self.dfs(G, _x, _y, memo) if size > res: res = size memo[x][y] = res return res ================================================ FILE: leetcode/334_increasing_triplet_subsequence.py ================================================ class Solution: def increasingTriplet(self, A): """ :type A: List[int] :rtype: bool """ if not A: return False a = b = float('inf') for x in A: if x <= a: a = x elif x <= b: b = x else: return True return False ================================================ FILE: leetcode/33_search_in_rotated_sorted_array.py ================================================ class Solution: def search(self, nums, target): """ :type nums: List[int] :type target: int :rtype: int """ NOT_FOUND = -1 if not nums: return NOT_FOUND left, right = 0, len(nums) - 1 while left + 1 < right: mid = (left + right) // 2 if nums[mid] == target: return mid if nums[mid] < nums[0]: if nums[mid] < target <= nums[right]: left = mid else: right = mid else: if nums[left] <= target < nums[mid]: right = mid else: left = mid for mid in (left, right): if nums[mid] == target: return mid return NOT_FOUND ================================================ FILE: leetcode/340_longest_substring_with_at_most_k_distinct_characters.py ================================================ """ - count chars: minimum length: `while cnt == k` to reduce window size if possible - count chars: maximum length: `while cnt > k` to record the maximum window size """ import collections class Solution: def lengthOfLongestSubstringKDistinct(self, s, k): """ :type s: str :type k: int :rtype: int """ ans = 0 if not s or not k or k < 0: return ans freqs = collections.defaultdict(int) i = cnt = 0 for j in range(len(s)): freqs[s[j]] += 1 if freqs[s[j]] == 1: cnt += 1 while cnt > k: freqs[s[i]] -= 1 if freqs[s[i]] == 0: cnt -= 1 i += 1 ans = max(ans, j - i + 1) return ans ================================================ FILE: leetcode/344_reverse_string.py ================================================ class Solution: def reverseString(self, s): """ :type s: str :rtype: str """ return ''.join(reversed(s)) ================================================ FILE: leetcode/347_top_k_frequent_elements.py ================================================ class Solution: def topKFrequent(self, A, k): """ :type A: List[int] :type k: int :rtype: List[int] """ ans = [] if not A: return ans F = {} for a in A: F[a] = F.get(a, 0) + 1 for a, _ in sorted(F.items(), key=lambda x: -x[1]): if k == 0: break ans.append(a) k -= 1 return ans ================================================ FILE: leetcode/348_design_tic_tac_toe.py ================================================ class TicTacToe(object): PLER_A = 1 PLER_B = 2 def __init__(self, n): """ Initialize your data structure here. :type n: int """ self.R = [0] * n self.C = [0] * n self.DR = 0 # only one self.DL = 0 # only one def move(self, x, y, player): """ Player {player} makes a move at ({x}, {y}). :type x: int The row of the board. :type y: int The column of the board. :type player: int The player, can be either 1 or 2. :rtype: int The current winning condition, can be either: 0: No one wins. 1: Player 1 wins. 2: Player 2 wins. """ n = len(self.R) delta = 1 if player == self.PLER_A else -1 self.R[x] += delta self.C[y] += delta self.DR += delta if x == y else 0 # x - y == 0 self.DL += delta if x == n - 1 - y else 0 # x + y == n - 1 return (abs(self.R[x]) == n or abs(self.C[y]) == n or abs(self.DR) == n or abs(self.DL) == n) # Your TicTacToe object will be instantiated and called as such: # obj = TicTacToe(n) # param_1 = obj.move(x,y,player) ================================================ FILE: leetcode/353_design_snake_game.py ================================================ """ 0. self.height is for `m`, self.width is for `n` 1. note that, REMOVE FIRST, MUST remove the snake tail **BEFORE** check it hit its body 2. this game is over if - hit wall - hit it self - no food 3. dont pop food in every move, just track its index 4. the score is just the len(body) - 1, except the head 5. what its return if no food or food is all taken? 6. does allow the snake just back if its body only 2? Initially the snake appears at position (0,0) and the food at (1,2). Your SnakeGame object will be instantiated and called as such: obj = SnakeGame(width, height, food) param_1 = obj.move(direction) >>> snake = SnakeGame(3, 2, [[1, 2], [0, 1]]) >>> [snake.move(d) for d in 'RDRULU'] [0, 0, 1, 1, 2, -1] >>> all(snake.move(d) == -1 for d in 'LUDR') True >>> snake = SnakeGame(3, 3, [[2, 0], [0, 0]]) >>> [snake.move(d) for d in 'DDUU'] [0, 1, 1, 2] >>> all(snake.move(d) == -1 for d in 'RUDL') True """ import collections class SnakeGame: def __init__(self, width, height, food): """ :type width: int, screen width :type height: int, screen height :type food: List[List[int]], A list of food positions E.g food = [[1,1], [1,0]] means the first food is positioned at [1,1], the second is at [1,0]. """ if not width or not height or not food: # raise error return self.width = width self.height = height self.food = food self.fi = 0 self.is_over = False self.SCORE_IN_OVER = -1 pos = [(0, 0)] self.snake = collections.deque(pos) self.body = set(pos) self.dn = { 'U': (-1, 0), 'D': ( 1, 0), 'L': ( 0, -1), 'R': ( 0, 1), } def move(self, direction): """Moves the snake. :type direction: str, 'U' = Up, 'L' = Left, 'R' = Right, 'D' = Down :rtype: int, The game's score after the move. Return -1 if game over. Game over when snake crosses the screen boundary or bites its body. """ if direction not in self.dn: # treat this move as invalid action return len(self.snake) - 1 if self.is_over: # this game is over return self.SCORE_IN_OVER """ new head will hit wall? """ x, y = self.snake[0] dx, dy = self.dn[direction] hx = x + dx hy = y + dy if not (0 <= hx < self.height and 0 <= hy < self.width): self.is_over = True return self.SCORE_IN_OVER """ eat food or not """ fx, fy = self.food[self.fi] if fx == hx and fy == hy: # eat that food self.fi += 1 else: # move to empty cell and need to remove tail tail = self.snake.pop() self.body.discard(tail) """ new head will hit its self? this detection MUST AFTER removing tail """ if (hx, hy) in self.body: self.is_over = True return self.SCORE_IN_OVER """ new head is valid, track it """ self.snake.appendleft((hx, hy)) self.body.add((hx, hy)) """ There is no food anymore """ if self.fi >= len(self.food): self.is_over = True return len(self.snake) - 1 ================================================ FILE: leetcode/36_valid_sudoku.py ================================================ class Solution: def isValidSudoku(self, board): """ :type board: List[List[str]] :rtype: bool """ if not board or not board[0] or len(board) != len(board[0]): return False n = len(board) EMPTY = '.' CANDS = '123456789' for x in range(n): used = set() for y in range(n): if board[x][y] == EMPTY: continue if board[x][y] not in CANDS: return False if board[x][y] in used: return False used.add(board[x][y]) for y in range(n): used = set() for x in range(n): if board[x][y] == EMPTY: continue if board[x][y] in used: return False used.add(board[x][y]) for i in range(3): for j in range(3): used = set() for x in range(i * 3, i * 3 + 3): for y in range(j * 3, j * 3 + 3): if board[x][y] == EMPTY: continue if board[x][y] in used: return False used.add(board[x][y]) return True ================================================ FILE: leetcode/377_combination_sum_iv.py ================================================ """ - item can only pick once (even diff item with same val): num loop -> amount loop, amount from end to start, + break - item can pick multiple but diff order is same path: num loop -> amount loop, amount from start to end - item can pick multiple but diff order is diff path: amount loop -> num loop, amount from start to end """ class Solution: """ Dynamic Programming """ def combinationSum4(self, nums, target): """ :type nums: List[int] :type target: int :rtype: int """ if not nums: return 0 dp = [0] * (target + 1) dp[0] = 1 # if iterate num first, then the answer will become the number of unique set # see the last Solution in this file for amount in range(1, target + 1): for num in nums: if amount >= num: dp[amount] += dp[amount - num] return dp[target] class Solution: """ Memory Search / Dynamic Programming / DFS """ def combinationSum4(self, nums, target): """ :type nums: List[int] :type target: int :rtype: int """ if not nums: return 0 dp = [-1] * (target + 1) dp[0] = 1 self.memo_search(nums, target, dp) return dp[target] def memo_search(self, nums, remain, dp): if dp[remain] > -1: return dp[remain] res = 0 for a in nums: if remain < a: continue res += self.memo_search(nums, remain - a, dp) dp[remain] = res return res class Solution: """ DFS: TLE """ def combinationSum4(self, nums, target): """ :type nums: List[int] :type target: int :rtype: int """ if not nums: return 0 ans = [] nums.sort(reverse=True) self.dfs(nums, target, ans, []) return len(ans) def dfs(self, nums, remain, ans, path): if remain == 0: ans.append(path[::-1]) return for a in nums: if remain < a: continue path.append(a) self.dfs(nums, remain - a, ans, path) path.pop() # ====== class Solution: """ Dynamic Programming: wrong answer for this question This approach is to find the unique combination """ def combinationSum4(self, nums, target): """ :type nums: List[int] :type target: int :rtype: int """ if not nums: return 0 dp = [0] * (target + 1) dp[0] = 1 for num in nums: for amount in range(num, target + 1): dp[amount] += dp[amount - num] return dp[target] ================================================ FILE: leetcode/378_kth_smallest_element_in_a_sorted_matrix.py ================================================ from heapq import heappop, heappush class Solution: def kthSmallest(self, G, k): """ :type G: List[List[int]] :type k: int :rtype: int """ if not G or not G[0]: return 0 heap = [] for x in range(len(G)): heappush(heap, (G[x][0], x, 0)) while heap: a, x, y = heappop(heap) k -= 1 if k == 0: return a if y + 1 < len(G[x]): heappush(heap, (G[x][y + 1], x, y + 1)) return 0 ================================================ FILE: leetcode/37_sudoku_solver.py ================================================ class Solution: def solveSudoku(self, board): """ :type board: List[List[str]] :rtype: void Do not return anything, modify board in-place instead. """ if not board or not board[0] or len(board) != len(board[0]): return self.dfs(board, 0, 0) def dfs(self, board, x, y): n = len(board) if x == n: return True _x, _y = x, y + 1 if y == n - 1: _x = x + 1 _y = 0 if board[x][y] != '.': if not self.is_valid(board, x, y): return False return self.dfs(board, _x, _y) for i in range(1, n + 1): board[x][y] = str(i) if ( self.is_valid(board, x, y) and self.dfs(board, _x, _y) ): return True board[x][y] = '.' return False def is_valid(self, board, x, y): if board[x][y] not in '123456789': return False n = len(board) for i in range(n): if y != i and board[x][y] == board[x][i]: return False if x != i and board[x][y] == board[i][y]: return False r = x // 3 * 3 c = y // 3 * 3 for i in range(r, r + 3): for j in range(c, c + 3): if x == i and y == j: continue if board[x][y] == board[i][j]: return False return True ================================================ FILE: leetcode/380_insert_delete_getrandom_o1.py ================================================ """ Your RandomizedSet object will be instantiated and called as such: obj = RandomizedSet() param = obj.insert(val) param = obj.remove(val) param = obj.getRandom() """ import random class RandomizedSet: def __init__(self): self.nums = [] self.val2idx = {} def insert(self, val): """ Inserts a value to the set. Returns true if the set did not already contain the specified element. :type val: int :rtype: bool """ self.val2idx[val] = len(self.nums) self.nums.append(val) def remove(self, val): """ Removes a value from the set. Returns true if the set contained the specified element. :type val: int :rtype: bool """ if val not in self.val2idx: return False i = self.val2idx[val] key = self.nums[-1] self.val2idx[key] = i self.nums[i] = self.nums[-1] self.nums.pop() del self.val2idx[val] return True def getRandom(self): """ Get a random element from the set. :rtype: int """ i = random.randrange(len(self.nums)) return self.nums[i] ================================================ FILE: leetcode/381_insert_delete_getrandom_o1_duplicates_allowed.py ================================================ import random class RandomizedCollection: def __init__(self): self.A = [] self.I = {} def insert(self, val): """ Inserts a value to the collection. Returns true if the collection did not already contain the specified element. :type val: int :rtype: bool """ A, I = self.A, self.I if val not in I: I[val] = set() A.append(val) I[val].add(len(A) - 1) return len(I[val]) == 1 def remove(self, val): """ Removes a value from the collection. Returns true if the collection contained the specified element. :type val: int :rtype: bool """ A, I = self.A, self.I if val not in I or not I[val]: return False i = I[val].pop() _val = A[-1] I[_val].add(i) I[_val].discard(len(A) - 1) A[i] = _val A.pop() return True def getRandom(self): """ Get a random element from the collection. :rtype: int """ return random.choice(self.A) # Your RandomizedCollection object will be instantiated and called as such: # obj = RandomizedCollection() # param_1 = obj.insert(val) # param_2 = obj.remove(val) # param_3 = obj.getRandom() ================================================ FILE: leetcode/382_linked_list_random_node.py ================================================ # Definition for singly-linked list. # class ListNode: # def __init__(self, x): # self.val = x # self.next = None from random import randint class Solution: def __init__(self, head): """ @param head The linked list's head. Note that the head is guaranteed to be not null, so it contains at least one node. :type head: ListNode """ self.head = head def getRandom(self): """ Returns a random node's value. :rtype: int """ res = node = self.head i = 0 while node: if randint(0, i) == i: res = node node = node.next i += 1 return res.val # Your Solution object will be instantiated and called as such: # obj = Solution(head) # param_1 = obj.getRandom() ================================================ FILE: leetcode/384_shuffle_an_array.py ================================================ from random import randrange class Solution: def __init__(self, nums): """ :type nums: List[int] """ self.origin = nums[:] self.nums = nums def reset(self): """ Resets the array to its original configuration and return it. :rtype: List[int] """ self.nums = self.origin[:] return self.nums def shuffle(self): """ Returns a random shuffling of the array. :rtype: List[int] """ a = self.nums n = len(a) for i in range(n): _i = randrange(i, n) a[i], a[_i] = a[_i], a[i] return a # Your Solution object will be instantiated and called as such: # obj = Solution(nums) # param_1 = obj.reset() # param_2 = obj.shuffle() ================================================ FILE: leetcode/386_lexicographical_numbers.py ================================================ class Solution: def lexicalOrder(self, n): """ :type n: int :rtype: List[int] """ ans = [] if not n: return ans stack = [1] while stack: x = stack.pop() ans.append(x) # considering the case no carry up if x + 1 # that is, x in [1, 8] if x < n and x % 10 < 9: stack.append(x + 1) if x * 10 <= n: stack.append(x * 10) return ans ================================================ FILE: leetcode/387_first_unique_character_in_a_string.py ================================================ class Solution: def firstUniqChar(self, s): """ :type s: str :rtype: int """ if not s: return -1 freq = {} for c in s: freq[c] = freq.get(c, 0) + 1 for i in range(len(s)): if freq[s[i]] == 1: return i return -1 ================================================ FILE: leetcode/388_longest_absolute_file_path.py ================================================ """ Main Concept: if meet file, record the maximum size of the abs path `root/dir/file` if meet dir, save current size for `root/dir` for files: two cases are at same depth 1. for hidden files 2. for extension in file for dirs: do NOT save the max size in each depth since the children in same depth may be under different parent - root - p1 - f-loooooong-1 - p2 - f-short-2 there is error in that case if save the max size """ class Solution: def lengthLongestPath(self, path): """ :type path: str :rtype: int """ ans = 0 if not path: return ans dep2size = {0: 0} for line in path.split('\n'): name = line.lstrip('\t') size = len(name) depth = len(line) - len(name) if '.' in name: ans = max(ans, dep2size[depth] + size) else: dep2size[depth + 1] = dep2size[depth] + size + 1 return ans ================================================ FILE: leetcode/389_find_the_difference.py ================================================ class Solution: def findTheDifference(self, s, t): """ :type s: str :type t: str :rtype: str """ a = ord('a') ans = ord(t[-1]) - a for i in range(len(s)): ans ^= ord(s[i]) - a ans ^= ord(t[i]) - a return chr(ans + a) class Solution: def findTheDifference(self, s, t): """ :type s: str :type t: str :rtype: str """ if not t: return '' freq = {} for c in s: if c not in freq: freq[c] = 0 freq[c] += 1 for c in t: if c not in freq: return c freq[c] -= 1 for c, cnt in freq.items(): if cnt: return c return '' ================================================ FILE: leetcode/38_count_and_say.py ================================================ class Solution: def countAndSay(self, N): """ :type N: int :rtype: str """ queue = '1' if not N: return queue _queue = [] for _ in range(N - 1): cnt = 0 char = queue[0] for c in queue: if c == char: cnt += 1 continue _queue.extend((str(cnt), char)) cnt = 1 char = c _queue.extend((str(cnt), char)) queue, _queue = ''.join(_queue), [] return queue ================================================ FILE: leetcode/390_elimination_game.py ================================================ class Solution: def lastRemaining(self, n): """ :type n: int :rtype: int """ ans = gap = 1 turn = 0 while n > 1: turn += 1 n //= 2 gap *= 2 delta = gap // 2 + gap * (n - 1) if turn & 1: ans += delta else: ans -= delta return ans ================================================ FILE: leetcode/391_perfect_rectangle.py ================================================ import collections class Solution: def isRectangleCover(self, recs): """ :type recs: List[List[int]] :rtype: bool """ if not recs: return False left = bottom = float('inf') right = top = float('-inf') points = collections.defaultdict(int) for l, b, r, t in recs: left = min(left, l) bottom = min(bottom, b) right = max(right, r) top = max(top, t) for x, y, val in ( (l, b, 1), (r, b, 2), (r, t, 4), (l, t, 8), ): if points[x, y] & val: return False points[x, y] |= val if any( # only check the mid-points val not in (3, 6, 9, 12, 15) for (x, y), val in points.items() if left < x < right or bottom < y < top ): return False return True ================================================ FILE: leetcode/395_longest_substring_with_at_least_k_repeating_characters.py ================================================ class Solution: def longestSubstring(self, s, k): """ :type s: str :type k: int :rtype: int """ ans = 0 if not s: return ans n = len(s) _F = dict.fromkeys(s, 0) for m in range(1, len(_F) + 1): F = _F.copy() left = right = 0 kind = valid = 0 while right < n: if kind <= m: F[s[right]] += 1 if F[s[right]] == 1: kind += 1 if F[s[right]] == k: valid += 1 right += 1 else: if F[s[left]] == 1: kind -= 1 if F[s[left]] == k: valid -= 1 F[s[left]] -= 1 left += 1 if kind == valid == m and right - left > ans: ans = right - left return ans ================================================ FILE: leetcode/398_random_pick_index.py ================================================ from random import randint class Solution: def __init__(self, A): """ :type A: List[int] """ self.A = A def pick(self, target): """ :type target: int :rtype: int """ res = -1 cnt = 0 for i in range(len(self.A)): if self.A[i] != target: continue cnt += 1 if randint(1, cnt) == cnt: res = i return res # Your Solution object will be instantiated and called as such: # obj = Solution(nums) # param_1 = obj.pick(target) ================================================ FILE: leetcode/399_evaluate_division.py ================================================ import collections class Solution: def calcEquation(self, equations, values, queries): """ :type equations: List[List[str]] :type values: List[float] :type queries: List[List[str]] :rtype: List[float] """ ans = [] if not ( equations and values and queries and len(equations) == len(values) ): return ans nexts = collections.defaultdict(set) evals = collections.defaultdict(float) for i in range(len(equations)): a, b = equations[i] nexts[a].add(b) nexts[b].add(a) evals[a, b] = 1.0 * values[i] evals[b, a] = 1.0 / values[i] for a, b in queries: res = self.dfs(a, b, 1, nexts, evals, set()) ans.append(float(res)) return ans def dfs(self, a, b, val, nexts, evals, visited): res = -1 if a not in nexts: return res if a == b: # this condition must be after `a not in nexts` # to prevent the node not in graph return val visited.add(a) for c in nexts[a]: if c in visited or (a, c) not in evals: continue res = self.dfs(c, b, val * evals[a, c], nexts, evals, visited) if res != -1: break visited.discard(a) return res ================================================ FILE: leetcode/406_queue_reconstruction_by_height.py ================================================ """ Main Concept: 1. group the mans with same height and record origin index 2. record all heights 3. sort heights and iterate from end to start 4. get the corresponding mans and insert to `k` index input: [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]] heights: [4, 5, 6, 7] process: 7 [(0, 0), (1, 2)] # input[0], input[2] [[7, 0]] [[7, 0], [7, 1]] 6 [(1, 4)] [[7, 0], [6, 1], [7, 1]] 5 [(0, 3), (2, 5)] [[5, 0], [7, 0], [6, 1], [7, 1]] [[5, 0], [7, 0], [5, 2], [6, 1], [7, 1]] 4 [(4, 1)] [[5, 0], [7, 0], [5, 2], [6, 1], [4, 4], [7, 1]] """ class Solution: def reconstructQueue(self, people): """ :type people: List[List[int]] :rtype: List[List[int]] """ ans = [] if not people: return ans people.sort(key=lambda p: (p[0], -p[1])) for i in range(len(people) - 1, -1, -1): ans.insert(people[i][1], people[i]) return ans class Solution: def reconstructQueue(self, people): """ :type people: List[List[int]] :rtype: List[List[int]] """ ans = [] if not people: return ans h2mans = {} heights = [] for i in range(len(people)): h, k = people[i] if h in h2mans: h2mans[h].append((k, i)) else: h2mans[h] = [(k, i)] heights.append(h) heights.sort() for height in heights[::-1]: for k, i in sorted(h2mans[height]): ans.insert(k, people[i]) return ans ================================================ FILE: leetcode/416_partition_equal_subset_sum.py ================================================ """ REF: https://leetcode.com/problems/partition-equal-subset-sum/discuss/90592 `dp[s]` means the specific sum `s` can be gotten from the sum of subset in `nums` """ class Solution: def canPartition(self, nums): """ :type nums: List[int] :rtype: bool """ if not nums: return True target = sum(nums) if target & 1 == 1: return False target //= 2 dp = [False] * (target + 1) dp[0] = True for a in nums: for s in range(target, a - 1, -1): if dp[s]: continue dp[s] = dp[s - a] return dp[target] ================================================ FILE: leetcode/417_pacific_atlantic_water_flow.py ================================================ class Solution: """ BFS 1. for both p-side and a-side, init with queue and visit set 2. add the edge and do bfs 3. only add the cell which higher or equal the previous cell 4. get the intersection in both set """ def pacificAtlantic(self, matrix): """ :type matrix: List[List[int]] :rtype: List[List[int]] """ if not matrix or not matrix[0]: return [] m, n = len(matrix), len(matrix[0]) pqueue = [] aqueue = [] for x in range(m): pqueue.append((x, 0)) aqueue.append((x, n - 1)) for y in range(n): pqueue.append((0, y)) aqueue.append((m - 1, y)) pvisited = set(pqueue) avisited = set(aqueue) self.bfs(matrix, pqueue, pvisited) self.bfs(matrix, aqueue, avisited) return list(pvisited & avisited) def bfs(self, matrix, queue, visited): m, n = len(matrix), len(matrix[0]) for x, y in queue: for dx, dy in ( (0, -1), (0, 1), (-1, 0), (1, 0), ): _x = x + dx _y = y + dy if not (0 <= _x < m and 0 <= _y < n): continue if (_x, _y) in visited: continue if matrix[_x][_y] < matrix[x][y]: continue queue.append((_x, _y)) visited.add((_x, _y)) class Solution: """ DFS """ def pacificAtlantic(self, matrix): """ :type matrix: List[List[int]] :rtype: List[List[int]] """ if not matrix or not matrix[0]: return [] m, n = len(matrix), len(matrix[0]) pvisited = set() avisited = set() for x in range(m): self.dfs(matrix, x, 0, pvisited) self.dfs(matrix, x, n - 1, avisited) for y in range(n): self.dfs(matrix, 0, y, pvisited) self.dfs(matrix, m - 1, y, avisited) return list(pvisited & avisited) def dfs(self, matrix, x, y, visited): visited.add((x, y)) for dx, dy in ( (0, -1), (0, 1), (-1, 0), (1, 0), ): _x = x + dx _y = y + dy if not ( 0 <= _x < len(matrix) and 0 <= _y < len(matrix[0]) ): continue if (_x, _y) in visited: continue if matrix[_x][_y] < matrix[x][y]: continue self.dfs(matrix, _x, _y, visited) ================================================ FILE: leetcode/41_first_missing_positive.py ================================================ """ REF: http://www.cnblogs.com/yuzhangcmu/p/4200096.html """ class Solution: def firstMissingPositive(self, A): """ :type A: List[int] :rtype: int """ left, right = 0, len(A) - 1 while left <= right: """ for `A[left]`, the index it should be at is `A[left] - 1` 1. if it is already at `i` => pass 2. if it out of range or duplicated => let `A[right]` in 3. if it is legal => swap to let `A[left]` go to `i` """ i = A[left] - 1 if i == left: left += 1 elif i < 0 or i > right or A[i] == A[left]: A[left], A[right] = A[right], A[left] # `A[left] = A[right]` is also ok, since no need to visit `A[right]` again right -= 1 else: A[left], A[i] = A[i], A[left] return left + 1 ================================================ FILE: leetcode/437_path_sum_iii.py ================================================ """ Definition for a binary tree node. class TreeNode: def __init__(self, x): self.val = x self.left = None self.right = None """ class Solution: """ find path count """ def pathSum(self, root, target): """ :type root: TreeNode :type target: int :rtype: int """ if not root: return 0 return sum(( self.count_valid_path(root, target), self.pathSum(root.left, target), self.pathSum(root.right, target), )) def count_valid_path(self, node, remaining): if not node: return 0 return sum(( int(node.val == remaining), self.count_valid_path(node.left, remaining - node.val), self.count_valid_path(node.right, remaining - node.val), )) class Solution: """ print path """ def pathSum(self, root, target): """ :type root: TreeNode :type target: int :rtype: list[list[int]] """ ans = [] if not root: return ans self.dfs(root, target, ans, []) return ans def dfs(self, node, target, ans, path): if not node: return path.append(node.val) remaining = target for i in range(len(path) - 1, -1, -1): remaining -= path[i] if remaining == 0: ans.append(path[i:]) self.dfs(node.left, target, ans, path) self.dfs(node.right, target, ans, path) path.pop() ================================================ FILE: leetcode/454_4sum_ii.py ================================================ class Solution: def fourSumCount(self, A, B, C, D): """ :type A: List[int] :type B: List[int] :type C: List[int] :type D: List[int] :rtype: int """ ans = 0 if not A or not B or not C or not D: return ans S = {} for c in C: for d in D: key = - (c + d) S[key] = S.get(key, 0) + 1 for a in A: for b in B: if a + b in S: ans += S[a + b] return ans ================================================ FILE: leetcode/461_hamming_distance.py ================================================ class Solution: def hammingDistance(self, x, y): """ :type x: int :type y: int :rtype: int """ n = x ^ y ans = 0 if not n: return ans while n != 0: n = n & (n - 1) ans += 1 return ans ================================================ FILE: leetcode/480_sliding_window_median.py ================================================ import heapq class HashHeapq: def __init__(self): self.__heap = [] def __repr__(self): return repr(self.__heap) def __len__(self): return len(self.__heap) def __bool__(self): return bool(self.__heap) def push(self, val): heapq.heappush(self.__heap, val) def pop(self): if not self.__heap: return return heapq.heappop(self.__heap) def remove(self, val): if not self.__heap: return i = 0 n = len(self.__heap) while i < n and self.__heap[i] != val: i += 1 if i == n: return if i == n - 1: self.__heap.pop() else: self.__heap[i] = self.__heap[-1] self.__heap.pop() heapq._siftup(self.__heap, i) heapq._siftdown(self.__heap, 0, i) def top(self): if not self.__heap: return return self.__heap[0] class Solution: def medianSlidingWindow(self, nums, k): """ :type nums: List[int] :type k: int :rtype: List[float] """ ans = [] if not nums or k <= 0 or len(nums) < k: return ans self.minheap = HashHeapq() self.maxheap = HashHeapq() for i in range(len(nums)): # remove nums[i - k] if i >= k: if self.minheap and nums[i - k] >= self.minheap.top(): self.minheap.remove(nums[i - k]) else: self.maxheap.remove(- nums[i - k]) # add nums[i] if self.minheap and nums[i] >= self.minheap.top(): self.minheap.push(nums[i]) else: self.maxheap.push(- nums[i]) # get median if i >= k - 1: ans.append(self.get_median()) return ans def get_median(self): if not self.minheap and not self.maxheap: return 0.0 while len(self.minheap) > len(self.maxheap) + 1: self.maxheap.push(- self.minheap.pop()) while len(self.maxheap) > len(self.minheap): self.minheap.push(- self.maxheap.pop()) if len(self.minheap) > len(self.maxheap): return self.minheap.top() * 1.0 return (self.minheap.top() - self.maxheap.top()) / 2.0 ================================================ FILE: leetcode/486_predict_the_winner.py ================================================ """ DP: time: O(n^2) space: O(n) """ class Solution: def PredictTheWinner(self, nums): """ :type nums: List[int] :rtype: bool """ if not nums: return False n = len(nums) dp = [0] * n for i in range(n - 1, -1, -1): for j in range(i + 1, n): dp[j] = max( nums[i] - dp[j], nums[j] - dp[j - 1] ) return dp[n - 1] >= 0 """ DP: time: O(n^2) space: O(n^2) """ class Solution: def PredictTheWinner(self, nums): """ :type nums: List[int] :rtype: bool """ if not nums: return False n = len(nums) dp = [[0] * n for _ in range(n)] for i in range(n - 1, -1, -1): for j in range(i + 1, n): dp[i][j] = max( nums[i] - dp[i + 1][j], nums[j] - dp[i][j - 1] ) return dp[0][n - 1] >= 0 ================================================ FILE: leetcode/48_rotate_image.py ================================================ class Solution: def rotate(self, matrix): """ :type matrix: List[List[int]] :rtype: void Do not return anything, modify matrix in-place instead. """ if not matrix or not matrix[0] or len(matrix) != len(matrix[0]): return n = len(matrix) # swap by diagonal axis for i in range(n - 1): for j in range(n - 1 - i): x = n - 1 - j y = n - 1 - i matrix[i][j], matrix[x][y] = matrix[x][y], matrix[i][j] # swap by x-mid axis for i in range(n // 2): for j in range(n): x = n - 1 - i y = j matrix[i][j], matrix[x][y] = matrix[x][y], matrix[i][j] class Solution: def rotate(self, matrix): """ :type matrix: List[List[int]] :rtype: void Do not return anything, modify matrix in-place instead. """ if not matrix or not matrix[0] or len(matrix) != len(matrix[0]): return n = len(matrix) ans = [[0] * n for _ in range(n)] for x in range(n): for y in range(n): ans[y][n - 1 - x] = matrix[x][y] matrix[:] = ans[:] ================================================ FILE: leetcode/490_the_maze.py ================================================ class Solution: def hasPath(self, maze, start, destination): """ :type maze: List[List[int]] :type start: List[int] :type destination: List[int] :rtype: bool >>> s = Solution() >>> maze = [[0, 0, 1, 0, 0], ... [0, 0, 0, 0, 0], ... [0, 0, 0, 1, 0], ... [1, 1, 0, 1, 1], ... [0, 0, 0, 0, 0]] >>> s.hasPath(maze, [0, 4], [4, 4]) True >>> s.hasPath(maze, [0, 4], [3, 2]) False """ if not maze or not maze[0] or not start or not destination: return False x, y = start tx, ty = destination visited = set() return self.dfs(maze, x, y, tx, ty, visited) def dfs(self, maze, x, y, tx, ty, visited): if x == tx and y == ty: return True if (x, y) in visited: return False visited.add((x, y)) m, n = len(maze), len(maze[0]) for dx, dy in ((-1, 0), (1, 0), (0, -1), (0, 1)): _x = x + dx _y = y + dy while 0 <= _x < m and 0 <= _y < n and maze[_x][_y] == 0: _x += dx _y += dy # since for now its means the position of wall _x -= dx _y -= dy if self.dfs(maze, _x, _y, tx, ty, visited): return True return False ================================================ FILE: leetcode/494_target_sum.py ================================================ """ REF: https://leetcode.com/problems/target-sum/discuss/97334 P: children used in positive, N: children used in negative, A: all children sum(P) - sum(N) = target => sum(P) - sum(N) + sum(P) + sum(N) = target + sum(P) + sum(N) => 2 * sum(P) = target + sum(A) => sum(P) = (target + sum(A)) // 2 => find the subset sum """ class Solution: def findTargetSumWays(self, A, target): """ :type A: List[int] :type target: int :rtype: int """ if not A: return 0 _sum = sum(A) if _sum < target or (_sum + target) % 2 == 1: return 0 return self.subset_sum(A, (_sum + target) // 2) def subset_sum(self, A, target): """ `dp[i]` means the number of ways to make sum `i` using non-repeated children in `A` `i` from `target` to `a - 1` => to make sure `i >= a` """ dp = [0] * (target + 1) dp[0] = 1 for a in A: for i in range(target, a - 1, -1): dp[i] += dp[i - a] return dp[target] ================================================ FILE: leetcode/499_the_maze_iii.py ================================================ class Solution: """ BFS """ def findShortestWay(self, maze, ball, hole): """ :type maze: List[List[int]] :type ball: List[int] :type hole: List[int] :rtype: str >>> s = Solution() >>> maze = [[0, 0, 0, 0, 0], ... [1, 1, 0, 0, 1], ... [0, 0, 0, 0, 0], ... [0, 1, 0, 0, 1], ... [0, 1, 0, 0, 0]] >>> s.findShortestWay(maze, [4, 3], [0, 1]) 'lul' >>> s.findShortestWay(maze, [4, 3], [3, 0]) 'impossible' """ NOT_FOUND = 'impossible' if not maze or not maze[0]: return NOT_FOUND m, n = len(maze), len(maze[0]) sx, sy = ball tx, ty = hole queue = [(sx, sy)] paths = {(sx, sy): []} distance = {(sx, sy): 0} for x, y in queue: for dx, dy, dn in ( (-1, 0, 'u'), (1, 0, 'd'), (0, -1, 'l'), (0, 1, 'r'), ): _x = x + dx _y = y + dy _step = 0 while ( 0 <= _x < m and 0 <= _y < n and maze[_x][_y] == 0 and not (_x == tx and _y == ty) ): _x += dx _y += dy _step += 1 if not (_x == tx and _y == ty): _x -= dx _y -= dy _step -= 1 if ((_x, _y) in distance and distance[x, y] + _step > distance[_x, _y]): continue if ((_x, _y) in paths and paths[x, y] + [dn] > paths[_x, _y]): continue distance[_x, _y] = distance[x, y] + _step paths[_x, _y] = paths[x, y] + [dn] queue.append((_x, _y)) return ''.join(paths[tx, ty]) if (tx, ty) in paths else NOT_FOUND import heapq class Solution2: """ Dijkstra """ def findShortestWay(self, maze, ball, hole): """ :type maze: List[List[int]] :type ball: List[int] :type hole: List[int] :rtype: str >>> s = Solution2() >>> maze = [[0, 0, 0, 0, 0], ... [1, 1, 0, 0, 1], ... [0, 0, 0, 0, 0], ... [0, 1, 0, 0, 1], ... [0, 1, 0, 0, 0]] >>> s.findShortestWay(maze, [4, 3], [0, 1]) 'lul' >>> s.findShortestWay(maze, [4, 3], [3, 0]) 'impossible' """ NOT_FOUND = 'impossible' if not maze or not maze[0]: return NOT_FOUND m, n = len(maze), len(maze[0]) sx, sy = ball tx, ty = hole heap = [(sx, sy)] paths = {(sx, sy): []} distance = {(sx, sy): 0} while heap: x, y = heapq.heappop(heap) for dx, dy, dn in ( (-1, 0, 'u'), (1, 0, 'd'), (0, -1, 'l'), (0, 1, 'r'), ): _x = x + dx _y = y + dy while ( 0 <= _x < m and 0 <= _y < n and maze[_x][_y] == 0 and not (_x == tx and _y == ty) ): _x += dx _y += dy if not (_x == tx and _y == ty): _x -= dx _y -= dy _step = distance[x, y] + abs(_x - x) + abs(_y - y) if (_x, _y) in distance and _step > distance[_x, _y]: continue if (_x, _y) in paths and paths[x, y] + [dn] > paths[_x, _y]: continue distance[_x, _y] = _step paths[_x, _y] = paths[x, y] + [dn] heapq.heappush(heap, (_x, _y)) return ''.join(paths[tx, ty]) if (tx, ty) in paths else NOT_FOUND ================================================ FILE: leetcode/505_the_maze_ii.py ================================================ class Solution: """ BFS """ def shortestDistance(self, maze, start, destination): """ :type maze: List[List[int]] :type start: List[int] :type destination: List[int] :rtype: int >>> s = Solution() >>> maze = [[0, 0, 1, 0, 0], ... [0, 0, 0, 0, 0], ... [0, 0, 0, 1, 0], ... [1, 1, 0, 1, 1], ... [0, 0, 0, 0, 0]] >>> s.shortestDistance(maze, [0, 4], [4, 4]) 12 >>> s.shortestDistance(maze, [0, 4], [3, 2]) -1 """ if not maze or not maze[0]: return -1 m, n = len(maze), len(maze[0]) sx, sy = start tx, ty = destination queue = [(sx, sy)] distance = {(sx, sy): 0} for x, y in queue: for dx, dy in ((-1, 0), (1, 0), (0, -1), (0, 1)): _x = x + dx _y = y + dy _step = 0 while 0 <= _x < m and 0 <= _y < n and maze[_x][_y] == 0: _x += dx _y += dy _step += 1 _x -= dx _y -= dy if ((_x, _y) in distance and distance[x, y] + _step >= distance[_x, _y]): continue distance[_x, _y] = distance[x, y] + _step if _x == tx and _y == ty: return distance[_x, _y] queue.append((_x, _y)) return -1 import heapq class Solution2: """ Dijkstra """ def shortestDistance(self, maze, start, destination): """ :type maze: List[List[int]] :type start: List[int] :type destination: List[int] :rtype: int >>> s = Solution2() >>> maze = [[0, 0, 1, 0, 0], ... [0, 0, 0, 0, 0], ... [0, 0, 0, 1, 0], ... [1, 1, 0, 1, 1], ... [0, 0, 0, 0, 0]] >>> s.shortestDistance(maze, [0, 4], [4, 4]) 12 >>> s.shortestDistance(maze, [0, 4], [3, 2]) -1 """ if not maze or not maze[0]: return -1 m, n = len(maze), len(maze[0]) sx, sy = start tx, ty = destination heap = [(sx, sy)] distance = {(sx, sy): 0} while heap: x, y = heapq.heappop(heap) for dx, dy in ((-1, 0), (1, 0), (0, -1), (0, 1)): _x = x + dx _y = y + dy while 0 <= _x < m and 0 <= _y < n and maze[_x][_y] == 0: _x += dx _y += dy _x -= dx _y -= dy _step = distance[x, y] + abs(_x - x) + abs(_y - y) if (_x, _y) in distance and _step >= distance[_x, _y]: continue if _x == tx and _y == ty: return _step distance[_x, _y] = _step heapq.heappush(heap, (_x, _y)) return -1 ================================================ FILE: leetcode/518_coin_change_2.py ================================================ class Solution: def change(self, amount, coins): """ :type amount: int :type coins: List[int] :rtype: int """ """ `dp[a]` means the ways to make up amount `a` """ dp = [0] * (amount + 1) dp[0] = 1 # the ways to make up `0` is just only 1 """ iterate coin first, and then amount otherwise it will add the dup ways in re-count if `[1, 2]` and `[2, 1]`, but they are same """ for c in coins: for a in range(c, amount + 1): # if a < c: continue dp[a] += dp[a - c] return dp[amount] ================================================ FILE: leetcode/51_n_queens.py ================================================ class Solution: def solveNQueens(self, n): """ :type n: int :rtype: List[List[str]] """ ans = [] if not n: return ans G = [['.'] * n for _ in range(n)] self.dfs(G, 0, ans) return ans def dfs(self, G, y, ans): if y == len(G): ans.append(self.clone_board(G)) return for x in range(len(G)): if self.is_valid(G, x, y): G[x][y] = 'Q' self.dfs(G, y + 1, ans) G[x][y] = '.' def is_valid(self, G, x, y): """ traverse left half => i in [0, n], j in [0, y - 1] to 1. avoid `j == y` and speed up and return False if 2. `x == i` => at same row 3. `x - i = y - j` => `x + j = y + i` => left diagonal line 4. `x - i = -(y - j)` => `x + y = i + j` => right diagonal line """ for i in range(len(G)): for j in range(y): if G[i][j] != 'Q': continue if (x + j == y + i or x + y == i + j or x == i): return False return True def clone_board(self, G): res = [] for R in G: res.append(''.join(R)) return res ================================================ FILE: leetcode/524_longest_word_in_dictionary_through_deleting.py ================================================ class Solution: """ 1. to check the word in list is subsequence of given s 2. ignoring if the length less than current ans 3. ignoring if the length equal current ans but has larger lexicographical order """ def findLongestWord(self, s, words): """ :type s: str :type words: List[str] :rtype: str """ ans = '' for w in words: if any(( not self.is_subseq(s, w), len(w) < len(ans), len(w) == len(ans) and w >= ans, # means w has larger lexicographical order )): continue ans = w return ans def is_subseq(self, s, t): """ return True if `t` is subsequence of `s` """ m, n = len(s), len(t) i = j = 0 while i < m and j < n: if s[i] == t[j]: j += 1 i += 1 return j == n class Solution: """ Brute Force: TLE """ def findLongestWord(self, s, words): """ :type s: str :type words: List[str] :rtype: str """ cands = [] self.find_cands(s, 0, cands, []) ans = '' target = set(words) for w in cands: if any(( w not in target, len(w) < len(ans), len(w) == len(ans) and w >= ans, )): continue ans = w return ans def find_cands(self, s, i, cands, path): if i == len(s): cands.append(''.join(path)) return # keep s[i] path.append(s[i]) self.find_cands(s, i + 1, cands, path) path.pop() # ignore s[i] self.find_cands(s, i + 1, cands, path) ================================================ FILE: leetcode/52_n_queens_ii.py ================================================ class Solution: def totalNQueens(self, n): """ :type n: int :rtype: int """ Xs = set() DLs = set() # left diagonal lines DRs = set() # right diagonal lines return self.divide_conquer(n, 0, 0, Xs, DLs, DRs) def divide_conquer(self, n, y, cnt, Xs, DLs, DRs): for x in range(n): if x in Xs: continue dl = x - y if dl in DLs: continue dr = x + y if dr in DRs: continue if y == n - 1: cnt += 1 continue Xs.add(x) DLs.add(dl) DRs.add(dr) cnt = self.divide_conquer(n, y + 1, cnt, Xs, DLs, DRs) Xs.discard(x) DLs.discard(dl) DRs.discard(dr) return cnt ================================================ FILE: leetcode/530_minimum_absolute_difference_in_bst.py ================================================ """ Definition for a binary tree node. class TreeNode: def __init__(self, x): self.val = x self.left = None self.right = None """ class Solution: """ time: O(n) space: O(1) """ ans = float('inf') pre = None def getMinimumDifference(self, root): """ :type root: TreeNode :rtype: int """ if not root: return self.ans self.getMinimumDifference(root.left) if self.pre and root.val - self.pre.val < self.ans: self.ans = root.val - self.pre.val self.pre = root self.getMinimumDifference(root.right) return self.ans ================================================ FILE: leetcode/542_01_matrix.py ================================================ class Solution: def updateMatrix(self, matrix): """ :type matrix: List[List[int]] :rtype: List[List[int]] """ if not matrix or not matrix[0]: return [] m, n = len(matrix), len(matrix[0]) ans = [[float('inf')] * n for _ in range(m)] queue = [] for x in range(m): for y in range(n): if matrix[x][y] == 0: ans[x][y] = 0 queue.append((x, y)) for x, y in queue: for dx, dy in ( (-1, 0), (1, 0), (0, -1), (0, 1), ): _x = x + dx _y = y + dy if not (0 <= _x < m and 0 <= _y < n): continue if ans[_x][_y] < ans[x][y] + 1: continue ans[_x][_y] = ans[x][y] + 1 queue.append((_x, _y)) return ans ================================================ FILE: leetcode/54_spiral_matrix.py ================================================ class Solution: def spiralOrder(self, matrix): """ :type matrix: List[List[int]] :rtype: List[int] """ ans = [] if not matrix or not matrix[0]: return ans # need keep its order to go right, bottom, left, top delta = ( (0, 1), (1, 0), (0, -1), (-1, 0), ) m, n = len(matrix), len(matrix[0]) x = y = turn = 0 for _ in range(m * n): ans.append(matrix[x][y]) matrix[x][y] = None _x = x + delta[turn][0] _y = y + delta[turn][1] if not (0 <= _x < m and 0 <= _y < n) or matrix[_x][_y] is None: turn = (turn + 1) % len(delta) _x = x + delta[turn][0] _y = y + delta[turn][1] x = _x y = _y return ans ================================================ FILE: leetcode/592_fraction_addition_and_subtraction.py ================================================ import re class Solution: def fractionAddition(self, E): """ :type E: str :rtype: str """ S = [] # signs if E[0] != '-': S.append('+') for c in E: if c == '+' or c == '-': S.append(c) a, b = 0, 1 i = 0 for frac in re.split('\+|-', E): if not frac: continue _a, _b = frac.split('/') _a = int(_a) _b = int(_b) # if needs to prevent overflow, `// gcd` if S[i] == '+': a = a * _b + _a * b else: a = a * _b - _a * b b = b * _b gcd = self.get_gcd(a, b) a //= gcd b //= gcd i += 1 return '{}/{}'.format(a, b) def get_gcd(self, a, b): while b: a, b = b, a % b return a ================================================ FILE: leetcode/593_valid_square.py ================================================ class Solution: def validSquare(self, p1, p2, p3, p4): """ :type p1: List[int] :type p2: List[int] :type p3: List[int] :type p4: List[int] :rtype: bool """ ps = [] for p in (p1, p2, p3, p4): if not p: return False ps.append(p) ps.sort() # 0: lb, 1: lt, 2:rb, 3: rt l01 = self.get_distance(ps[0], ps[1]) l02 = self.get_distance(ps[0], ps[2]) l13 = self.get_distance(ps[1], ps[3]) l23 = self.get_distance(ps[2], ps[3]) l03 = self.get_distance(ps[0], ps[3]) l12 = self.get_distance(ps[1], ps[2]) return all(( l01 == l02 == l13 == l23 > 0, l03 == l12, )) def get_distance(self, a, b): """ find the size of 'ab' """ dx = a[0] - b[0] dy = a[1] - b[1] return (dx * dx + dy * dy) ** 0.5 ================================================ FILE: leetcode/59_spiral_matrix_ii.py ================================================ class Solution: def generateMatrix(self, n): """ :type n: int :rtype: List[List[int]] """ if not n or n < 1: return [] if n == 1: return [[1]] ans = [[0] * n for _ in range(n)] delta = ( (0, 1), (1, 0), (0, -1), (-1, 0), ) x = y = turn = 0 for i in range(1, n * n + 1): ans[x][y] = i _x = x + delta[turn][0] _y = y + delta[turn][1] if not (0 <= _x < n and 0 <= _y < n) or ans[_x][_y] != 0: turn = (turn + 1) % len(delta) _x = x + delta[turn][0] _y = y + delta[turn][1] x = _x y = _y return ans ================================================ FILE: leetcode/5_longest_palindromic_substring.py ================================================ class Solution: def longestPalindrome(self, s): """ :type s: str :rtype: str """ if not s: return '' start = size = 0 for i in range(len(s)): _start, _size = self.check_palindrome(s, i, i) if _size > size: size = _size start = _start _start, _size = self.check_palindrome(s, i, i + 1) if _size > size: size = _size start = _start return s[start:start + size] def check_palindrome(self, s, left, right): n = len(s) while left >= 0 and right < n and s[left] == s[right]: left -= 1 right += 1 return left + 1, right - left - 1 ================================================ FILE: leetcode/616_add_bold_tag_in_string.py ================================================ """ >>> gotcha = [] >>> for s in (Solution(),): ... for _in, _out in ( ... (('', ['']), ''), ... (('abcxyz123', ['abc', '123']), 'abcxyz123'), ... (('aaabbcc', ['aaa','aab','bc']), 'aaabbcc'), ... (('aabcd', ['ab', 'bc']), 'aabcd'), ... ): ... res = s.addBoldTag(*_in) ... if res != _out: print(_in, res) ... gotcha.append(res == _out) >>> bool(gotcha) and all(gotcha) True """ class Solution: def addBoldTag(self, s, words): """ :type s: str :type words: List[str] :rtype: str """ if not s or not words: return '' TMPL = '{}' n = len(s) ans = [] is_bold = [False] * n left = right = 0 for left in range(n): for w in words: size = len(w) if s[left:left + size] == w and left + size > right: right = left + size is_bold[left] = right > left left = right = 0 while left < n: if not is_bold[left]: ans.append(s[left]) left += 1 continue right = left while right < n and is_bold[right]: right += 1 ans.append(TMPL.format(s[left:right])) left = right # imply left' = left + 1 return ''.join(ans) ================================================ FILE: leetcode/643_maximum_average_subarray_i.py ================================================ class Solution: def findMaxAverage(self, nums, k): """ :type nums: List[int] :type k: int :rtype: float """ """ Assuming the answer we want is `Ans = Max(A[i] + A[i+1] + ... + A[i+k-1])`, and we define the `P[i] = A[0] + A[1] + ... + A[i-1]`, so that `Ans = Max(P[i+k] - P[i])`, and `i+k-1 < n -> i < n-k+1` * max index in P is `i+k`, so in A is `(i+k)-1`, `i+k-1 < n` """ """ P[0] = 0 P[1] = P[0] + A[0] P[2] = P[1] + A[1] P[i] = P[i-1] + A[i-1] = A[0] + A[1] + ... + A[i-1] """ P = [0] for x in nums: P.append(P[-1] + x) max_sum = max(P[i+k] - P[i] for i in range(len(nums) - k + 1)) return max_sum / float(k) class Solution: def findMaxAverage(self, nums, k): """ :type nums: List[int] :type k: int :rtype: float """ """ Assuming k = 3 i: 0 1 2 3 |--> Start to find max sum |--> Start to remove past child """ max_sum, tmp_sum = float('-inf'), 0 for i in range(len(nums)): tmp_sum += nums[i] if i >= k: tmp_sum -= nums[i-k] if i + 1 >= k: max_sum = max(max_sum, tmp_sum) return max_sum / float(k) ================================================ FILE: leetcode/66_plus_one.py ================================================ class Solution: def plusOne(self, digits): """ :type digits: List[int] :rtype: List[int] """ ans = [] if not digits: return ans carry = 1 for i in range(len(digits) - 1, -1, -1): carry += digits[i] ans.append(carry % 10) carry //= 10 if carry: ans.append(carry) ans.reverse() return ans class Solution: def plusOne(self, digits): """ :type digits: List[int] :rtype: List[int] """ if not digits: return [] carry = 0 digits[-1] += 1 for i in range(len(digits) - 1, -1, -1): carry += digits[i] digits[i] = carry % 10 carry //= 10 if carry: digits.append(carry) for i in range(len(digits) - 1, 0, -1): digits[i], digits[i - 1] = digits[i - 1], digits[i] return digits ================================================ FILE: leetcode/676_implement_magic_dictionary.py ================================================ """ Your MagicDictionary object will be instantiated and called as such: obj = MagicDictionary() obj.buildDict(dict) param_2 = obj.search(word) """ class MagicDictionary: def __init__(self): """ Initialize your data structure here. """ self.words = collections.defaultdict(set) def buildDict(self, words): """ Build a dictionary through a list of words :type words: List[str] :rtype: void """ for word in words: for i in range(len(word)): key = '{0},{1}'.format(word[:i], word[i + 1:]) if key not in self.words: self.words[key] = set() # add char to distinct word if its same self.words[key].add(word[i]) def search(self, word): """ Returns if there is any word in the trie that equals to the given word after modifying exactly one character :type word: str :rtype: bool """ for i in range(len(word)): key = '{0},{1}'.format(word[:i], word[i + 1:]) if key not in self.words: continue words = self.words[key] # 1. word[i] not in words => means not same word # 2. len(words) > 1 => if got same but still can mapping other if word[i] not in words or len(words) > 1: return True return False ================================================ FILE: leetcode/679_24_game.py ================================================ class Solution: EPS = 1e-6 OP = ( lambda a, b: a + b, lambda a, b: a * b, lambda a, b: a - b, lambda a, b: a / b, ) def judgePoint24(self, nums): """ :type nums: List[int] :rtype: bool """ if not nums: return False n = len(nums) if n == 1: return abs(nums[0] - 24) < self.EPS for i in range(n): for j in range(n): if i == j: continue nxts = [nums[k] for k in range(n) if i != k != j] # i != j != k is different for k in range(len(self.OP)): if i < j and k < 2: # since a + b == b + a, so just do half in j >= i # same for `*` continue if nums[j] == 0 and k == 3: # divide by 0 continue nxts.append(self.OP[k](nums[i], nums[j])) if self.judgePoint24(nxts): return True nxts.pop() return False ================================================ FILE: leetcode/681_next_closest_time.py ================================================ """ it's very similar with `next permutation` just a `next combination` 1. `digits` save unique num and in order 2. `ids` record current used num 3. keep doing plus one and check valid >>> gotcha = [] >>> s = Solution() >>> for _in, _out in ( ... ('19:34', '19:39'), ('23:59', '22:22'), ... ('22:22', '22:22'), ('24:24', ''), ... ('23:23', '23:32'), ('12:34', '12:41'), ... ('00:00', '00:00'), ('01:32', '01:33'), ... ): ... res = s.nextClosestTime(_in) ... if res != _out: print('in: {}, out: {}, exp: {}'.format(_in, res, _out)) ... gotcha.append(res == _out) >>> bool(gotcha) and all(gotcha) True """ class Solution: def nextClosestTime(self, time): """ :type time: str :rtype: str """ if not ( time and len(time) == 5 and time[2] == ':' and 0 <= int(time[:2]) < 24 and 0 <= int(time[3:]) < 60 ): return '' times = [int(t) for t in time if t != ':'] digits = [] for a in sorted(times): if digits and a == digits[-1]: continue digits.append(a) ids = [digits.index(t) for t in times] ids[-1] += 1 while not self.is_valid(ids, digits): ids[-1] += 1 h = digits[ids[0]] * 10 + digits[ids[1]] m = digits[ids[2]] * 10 + digits[ids[3]] return '{}:{}'.format( '0' + str(h) if h < 10 else str(h), '0' + str(m) if m < 10 else str(m) ) def is_valid(self, ids, digits): n = len(digits) carry = 0 i = len(ids) - 1 while i >= 0: carry += ids[i] ids[i] = carry % n carry = carry // n i -= 1 if carry: ids[:] = [0] * len(ids) return True h = digits[ids[0]] * 10 + digits[ids[1]] m = digits[ids[2]] * 10 + digits[ids[3]] if 0 <= h < 24 and 0 <= m < 60: return True return False ================================================ FILE: leetcode/682_baseball_game.py ================================================ class Solution: def calPoints(self, ops): """ :type ops: List[str] :rtype: int """ if not ops: return 0 stack = [] for op in ops: if op == 'C': stack.pop() elif op == 'D': stack.append(2 * stack[-1]) elif op == '+': stack.append(stack[-1] + stack[-2]) else: stack.append(int(op)) return sum(stack) ================================================ FILE: leetcode/683_k_empty_slots.py ================================================ """ >>> gotcha = [] >>> for s in (Solution(), Solution2()): ... for _in, _out in ( ... (([1,3,2], 1), 2), ... (([1,2,3], 1), -1), ... ): ... res = s.kEmptySlots(*_in) ... if res != _out: print(_in, res) ... gotcha.append(res == _out) >>> bool(gotcha) and all(gotcha) True """ import bisect class Solution: """ Maintain pos by bloom order """ def kEmptySlots(self, flowers, k): """ :type flowers: List[int] :type k: int :rtype: int """ bloom = [] for day in range(len(flowers)): x = flowers[day] i = bisect.bisect_left(bloom, x) for _x in bloom[max(0, i - 1):i + 1]: if abs(_x - x) - 1 == k: return day + 1 # changed to 1-based bloom.insert(i, x) return -1 class Solution2: """ Two Pointer: https://blog.csdn.net/magicbean2/article/details/79235465 """ def kEmptySlots(self, flowers, k): """ :type flowers: List[int] :type k: int :rtype: int """ n = len(flowers) x2day = [0] * n for day in range(n): """ day: 0-based => 1-based x: 1-based => 0-based """ x = flowers[day] x2day[x - 1] = day + 1 ans = INF = float('inf') left, right = 0, k + 1 i = 0 while right < n: if any(( x2day[i] < x2day[left], x2day[i] <= x2day[right], )): if i == right: ans = min(ans, max(x2day[left], x2day[right])) left, right = i, k + i + 1 i += 1 return ans if ans < INF else -1 ================================================ FILE: leetcode/684_redundant_connection.py ================================================ """ given input is an undirected graph 1. iterate edges 2. if u and v are connected before we add edge in nodes (graph) => that is the edge should be removed """ class Solution: """ UnionFind """ def findRedundantConnection(self, edges): """ :type edges: List[List[int]] :rtype: List[int] """ if not edges: return [] nodes = {} for u, v in edges: if not self.union(nodes, u, v): return [u, v] return [] def union(self, nodes, u, v): a = self.find(nodes, u) b = self.find(nodes, v) if a == b: return False nodes[a] = b return True def find(self, nodes, u): if u not in nodes: nodes[u] = u return u if nodes[u] == u: return u nodes[u] = self.find(nodes, nodes[u]) return nodes[u] import collections class Solution: """ DFS """ def findRedundantConnection(self, edges): """ :type edges: List[List[int]] :rtype: List[int] """ if not edges: return [] nodes = collections.defaultdict(set) for u, v in edges: # dfs to check u and v are connected already => cycle if u in nodes and v in nodes and self.dfs(nodes, u, v, set()): return [u, v] nodes[u].add(v) nodes[v].add(u) return [] def dfs(self, nodes, u, v, visited): if u == v: return True if u in visited: return False visited.add(u) for x in nodes[u]: if self.dfs(nodes, x, v, visited): return True return False ================================================ FILE: leetcode/685_redundant_connection_ii.py ================================================ """ given input is an directed graph 3 corner cases: 1. There is a loop in the graph, and no vertex has more than 1 parent. 2. A vertex has more than 1 parent, but there isn’t a loop in the graph. 3. A vertex has more than 1 parent, and is part of a loop. REF: https://leetcode.com/problems/redundant-connection-ii/discuss/108070/Python-O(N)-concise-solution-with-detailed-explanation-passed-updated-testcases """ import collections class Solution: def findRedundantDirectedConnection(self, edges): """ :type edges: List[List[int]] :rtype: List[int] """ ans = edge = None # `edge` is the last edge in a loop adj = collections.defaultdict(set) uf = collections.defaultdict(int) has_parent = set() for u, v in edges: adj[u].add(v) if v in has_parent: ans = (u, v) if not self.union(uf, u, v): edge = (u, v) has_parent.add(v) if not ans: return edge res = self.dfs(ans[1], adj, set()) return res if res else ans def union(self, uf, u, v): a = self.find(uf, u) b = self.find(uf, v) if a == b: return False uf[b] = a return True def find(self, uf, u): if uf[u] == 0: uf[u] = u return u if uf[u] == u: return u uf[u] = self.find(uf, uf[u]) return uf[u] def dfs(self, u, adj, visited): # to detect cycle visited.add(u) for v in adj[u]: if v in visited: return (u, v) res = self.dfs(v, adj, visited) if res: return res ================================================ FILE: leetcode/688_knight_probability_in_chessboard.py ================================================ import collections class Solution: """ DP: 1. init the first pos as 1 2. keep simulate the process and divide the probability 3. sum the values """ def knightProbability(self, n, k, r, c): """ :type n: int :type k: int :type r: int :type c: int :rtype: float """ dp = collections.defaultdict(int) dp[r, c] = 1.0 for _ in range(k): nxt = collections.defaultdict(int) for x in range(n): for y in range(n): for dx, dy in ( (-1, -2), ( 1, -2), (-2, -1), ( 2, -1), (-2, 1), ( 2, 1), (-1, 2), ( 1, 2), ): _x = x + dx _y = y + dy if not (0 <= _x < n and 0 <= _y < n): continue nxt[_x, _y] += dp[x, y] / 8.0 dp = nxt return sum(dp.values()) class Solution: """ BFS: TLE """ def knightProbability(self, n, k, r, c): """ :type n: int :type k: int :type r: int :type c: int :rtype: float """ if n == 1 and k == 0: return 1.0 queue, _queue = [(r, c)], [] total = 8 ** k valid = 0 while queue and k: k -= 1 for x, y in queue: for dx, dy in ( (-1, -2), ( 1, -2), (-2, -1), ( 2, -1), (-2, 1), ( 2, 1), (-1, 2), ( 1, 2), ): _x = x + dx _y = y + dy if not (0 <= _x < n and 0 <= _y < n): continue if k == 0: valid += 1 if k > 0: _queue.append((_x, _y)) queue, _queue = _queue, [] return valid / total ================================================ FILE: leetcode/689_maximum_sum_of_3_non_overlapping_subarrays.py ================================================ """ REF: https://leetcode.com/problems/maximum-sum-of-3-non-overlapping-subarrays/discuss/108231 """ class Solution: def maxSumOfThreeSubarrays(self, A, k): """ :type A: List[int] :type k: int :rtype: List[int] """ ans = [-1] * 3 if not A or len(A) < 3 * k: return ans n = len(A) S = [0] * (n + 1) # prefix sum L = [0] * n # the starting index of the maximum interval sum with length k in [0, i] R = [n - k] * n # the starting index of the maximum interval sum with length k in [i, n - 1] for i in range(1, n + 1): S[i] = S[i - 1] + A[i - 1] max_sum = S[k] - S[0] # maximum interval sum _sum = 0 for i in range(k, n): L[i] = L[i - 1] _sum = S[i + 1] - S[i + 1 - k] if _sum > max_sum: L[i] = i + 1 - k max_sum = _sum max_sum = S[n] - S[n - k] _sum = 0 for i in range(n - k - 1, -1, -1): R[i] = R[i + 1] _sum = S[i + k] - S[i] if _sum >= max_sum: R[i] = i max_sum = _sum max_sum = _sum = 0 for i in range(k, n - 2 * k + 1): left = L[i - 1] right = R[i + k] _sum = S[i + k] - S[i] + S[left + k] - S[left] + S[right + k] - S[right] if _sum > max_sum: max_sum = _sum ans[:] = left, i, right return ans ================================================ FILE: leetcode/697_degree_of_an_array.py ================================================ class Solution: def findShortestSubArray(self, A): """ :type A: List[int] :rtype: int """ if not A: return 0 n = len(A) L, R, C = {}, {}, {} for i in range(n): if A[i] not in L: L[A[i]] = i R[A[i]] = i C[A[i]] = C.get(A[i], 0) + 1 ans = len(A) degree = max(C.values()) for a, c in C.items(): if c == degree and R[a] - L[a] + 1 < ans: ans = R[a] - L[a] + 1 return ans ================================================ FILE: leetcode/719_find_k_th_smallest_pair_distance.py ================================================ class Solution: def smallestDistancePair(self, A, k): """ :type A: List[int] :type k: int :rtype: int """ if not A or not k: return -1 A.sort() left, right = 0, A[-1] - A[0] while left + 1 < right: mid = (left + right) // 2 if self.check_valid(A, mid, k): right = mid else: left = mid return left if self.check_valid(A, left, k) else right def check_valid(self, A, mid, k): """ valid if there are at least `k` pairs when distance is `mid` """ cnt = left = 0 for right in range(len(A)): while A[right] - A[left] > mid: left += 1 cnt += right - left if cnt >= k: return True return False ================================================ FILE: leetcode/721_accounts_merge.py ================================================ class Solution: def accountsMerge(self, A): """ :type A: List[List[str]] :rtype: List[List[str]] """ if not A: return [] M = {} # mails M2N = {} # mail to name for L in A: for i in range(1, len(L)): M2N[L[i]] = L[0] self.connect(M, L[i], L[1]) for a in M: self.find(M, a) res = {} for m1, m0 in M.items(): if m0 not in res: res[m0] = [] res[m0].append(m1) return [[M2N[m]] + sorted(M) for m, M in res.items()] def connect(self, N, a, b): _a = self.find(N, a) _b = self.find(N, b) if _a is not _b: N[_a] = _b def find(self, N, a): if a not in N: N[a] = a return a if N[a] is a: return a N[a] = self.find(N, N[a]) return N[a] ================================================ FILE: leetcode/734_sentence_similarity.py ================================================ """ >>> pairs = [['great', 'fine'], ['acting', 'drama'], ['skills', 'talent']] >>> gotcha = [] >>> s = Solution() >>> for _in, _out in ( ... ((['great', 'acting'], ['fine', 'drama'], pairs), True), ... ((['great', 'acting'], ['fine', 'talent'], pairs), False), ... ((['great'], ['great'], []), True), ... ((['great'], ['fine', 'drama'], pairs), False), ... ): ... res = s.areSentencesSimilar(*_in) ... if res != _out: print(_in, res) ... gotcha.append(res == _out) >>> bool(gotcha) and all(gotcha) True """ import collections class Solution: def areSentencesSimilar(self, words1, words2, pairs): """ :type words1: List[str] :type words2: List[str] :type pairs: List[List[str]] :rtype: bool """ if len(words1) != len(words2): return False simils = collections.defaultdict(set) for a, b in pairs: simils[a].add(b) simils[b].add(a) for i in range(len(words1)): a = words1[i] b = words2[i] if a != b and b not in simils[a]: return False return True ================================================ FILE: leetcode/737_sentence_similarity_ii.py ================================================ """ >>> pairs = [['great', 'fine'], ['acting', 'drama'], ['skills', 'talent']] >>> gotcha = [] >>> for s in (Solution(), Solution2()): ... for _in, _out in ( ... ((['great', 'acting'], ['fine', 'drama'], pairs), True), ... ((['great', 'acting'], ['fine', 'talent'], pairs), False), ... ((['great'], ['great'], []), True), ... ((['great'], ['fine', 'drama'], pairs), False), ... ): ... res = s.areSentencesSimilarTwo(*_in) ... if res != _out: print(_in, res) ... gotcha.append(res == _out) >>> bool(gotcha) and all(gotcha) True """ class Solution: """ UnionFind """ def areSentencesSimilarTwo(self, words1, words2, pairs): """ :type words1: List[str] :type words2: List[str] :type pairs: List[List[str]] :rtype: bool """ if len(words1) != len(words2): return False nodes = {} for a, b in pairs: self.union(nodes, a, b) for i in range(len(words1)): a = words1[i] b = words2[i] _a = self.find(nodes, a) _b = self.find(nodes, b) if a != b and _a != _b: return False return True def union(self, nodes, a, b): _a = self.find(nodes, a) _b = self.find(nodes, b) if _a is not _b: nodes[_a] = _b return _b def find(self, nodes, a): if a not in nodes: nodes[a] = a return a if nodes[a] is a: return a nodes[a] = self.find(nodes, nodes[a]) return nodes[a] import collections class Solution2: """ DFS """ def areSentencesSimilarTwo(self, words1, words2, pairs): """ :type words1: List[str] :type words2: List[str] :type pairs: List[List[str]] :rtype: bool """ if len(words1) != len(words2): return False simils = collections.defaultdict(set) for a, b in pairs: simils[a].add(b) simils[b].add(a) for i in range(len(words1)): a = words1[i] b = words2[i] if a != b and not self.dfs(a, b, simils, set()): return False return True def dfs(self, start, end, simils, path): # check start and end are connected if start == end: return True if start not in simils or start in path: return False path.add(start) for nxt in simils[start]: if nxt in path: continue res = self.dfs(nxt, end, simils, path) if res: return True path.discard(start) return False ================================================ FILE: leetcode/744_find_smallest_letter_greater_than_target.py ================================================ class Solution: def nextGreatestLetter(self, L, target): """ :type L: List[str] :type target: str :rtype: str """ n = len(L) left, right = 0, n - 1 while left + 1 < right: mid = (left + right) // 2 if L[mid] <= target: left = mid else: right = mid if target < L[left]: return L[left] if target < L[right]: return L[right] return L[0] ================================================ FILE: leetcode/746_min_cost_climbing_stairs.py ================================================ class Solution: def minCostClimbingStairs(self, cost): """ :type cost: List[int] :rtype: int """ if not cost: return 0 n = len(cost) """ `dp[i]` means the min cost to possible to reach previous `i` steps """ dp = [0] * (n + 1) for i in range(2, n + 1): """ If you decide to come from some step, and then pay the fee to the from step """ dp[i] = min( dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2] ) return dp[n] ================================================ FILE: leetcode/747_largest_number_at_least_twice_of_others.py ================================================ class Solution: def dominantIndex(self, A): """ :type A: List[int] :rtype: int """ _max = __max = float('-inf') _max_i = -1 for i in range(len(A)): if A[i] > _max: __max = _max _max = A[i] _max_i = i continue if A[i] > __max: __max = A[i] if _max >= __max * 2: return _max_i return -1 ================================================ FILE: leetcode/748_shortest_completing_word.py ================================================ class Solution: def shortestCompletingWord(self, P, words): """ :type P: str :type words: List[str] :rtype: str """ ans = '' if not P or not words: return ans p_times = self.get_times(P) _min_size = float('inf') for word in words: times = self.get_times(word) if len(word) < _min_size and self.is_included(p_times, times): ans = word _min_size = len(word) return ans def is_included(self, a_times, b_times): """True if A is a subset of B""" for char, times in a_times.items(): if char not in b_times: return False if b_times[char] < times: return False return True def get_times(self, word): times = {} ord_a = ord('a') ord_z = ord('z') for char in word.lower(): if ord_a <= ord(char) <= ord_z: times[char] = times.get(char, 0) + 1 return times ================================================ FILE: leetcode/749_contain_virus.py ================================================ """ Test Case: [[1,1,1], [1,0,1], [1,1,1]] [[1,1,1,0,0,0,0,0,0], [1,0,1,0,1,1,1,1,1], [1,1,1,0,0,0,0,0,0]] # In the process of spreading will intersect [[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,1,0,0],[1,0,0,0,0,0,0,0,0,0],[0,0,1,0,0,0,1,0,0,0],[0,0,0,0,0,0,1,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,1,0],[0,0,0,0,1,0,1,0,0,0],[0,0,0,0,0,0,0,0,0,0]] # needs to dedup `ex_virus` and `spreading` [[0,1,0,1,1,1,1,1,1,0],[0,0,0,1,0,0,0,0,0,0],[0,0,1,1,1,0,0,0,1,0],[0,0,0,1,1,0,0,1,1,0],[0,1,0,0,1,0,1,1,0,1],[0,0,0,1,0,1,0,1,1,1],[0,1,0,0,1,0,0,1,1,0],[0,1,0,1,0,0,0,1,1,0],[0,1,1,0,0,1,1,0,0,1],[1,0,1,1,0,1,0,1,0,1]] """ class Solution: NORMAL = 0 VIRUS = 1 EX_VIRUS = -1 V = ( (-1, 0), ( 1, 0), ( 0, -1), ( 0, 1), ) def containVirus(self, G): """ :type G: List[List[int]] :rtype: int """ walls = 0 if not G or not G[0]: return walls while True: _walls = self.build_walls(G) if _walls == 0: break walls += _walls return walls def build_walls(self, G): m, n = len(G), len(G[0]) ex_virus = [] spreading = [] walls = [] visited = [[False] * n for _ in range(m)] for x in range(m): for y in range(n): if G[x][y] == self.VIRUS and not visited[x][y]: ex_virus.append(set()) spreading.append(set()) walls.append(0) self.dfs(x, y, G, visited, ex_virus, spreading, walls) _max_save = _max_i = -1 s = len(spreading) for i in range(s): t = len(spreading[i]) if t > _max_save: _max_save = t _max_i = i if _max_save == -1: return 0 for i in range(s): if i == _max_i: for x, y in ex_virus[i]: G[x][y] = self.EX_VIRUS else: for x, y in spreading[i]: G[x][y] = self.VIRUS return walls[_max_i] def dfs(self, x, y, G, visited, ex_virus, spreading, walls): m, n = len(G), len(G[0]) if not (0 <= x < m and 0 <= y < n) or visited[x][y]: return if G[x][y] == self.VIRUS: visited[x][y] = True ex_virus[-1].add((x, y)) for dx, dy in self.V: _x = x + dx _y = y + dy self.dfs(_x, _y, G, visited, ex_virus, spreading, walls) elif G[x][y] == self.NORMAL: spreading[-1].add((x, y)) walls[-1] += 1 ================================================ FILE: leetcode/750_number_of_corner_rectangles.py ================================================ class Solution: def countCornerRectangles(self, G): """ :type G: List[List[int]] :rtype: int iterate every row and save all the pairs of 1 in current row if there is some pair with same indices in passed row and then we can ensure rectangles exist note that: 1 pair: 0 rec 2 pairs: 0+1= 1 rec 4 pairs: 0+1+2+3= 6 recs n pairs: C(n, 2) = n * (n - 1) / 2 = 0+1+2+...+(n-1) example: 4 pairs => C(4, 2) = 6 recs 1 1 1 1 1 1 1 1 """ ans = 0 if not G: return ans n = 0 count = {} for R in G: for end in range(1, len(R)): if R[end] == 0: continue for start in range(end): if R[start] == 0: continue if (start, end) not in count: count[start, end] = 0 continue count[start, end] += 1 ans += count[start, end] return ans ================================================ FILE: leetcode/758_bold_words_in_string.py ================================================ """ >>> gotcha = [] >>> for s in (Solution(),): ... for _in, _out in ( ... (([''], ''), ''), ... ((['abc', '123'], 'abcxyz123'), 'abcxyz123'), ... ((['aaa','aab','bc'], 'aaabbcc'), 'aaabbcc'), ... ((['ab', 'bc'], 'aabcd'), 'aabcd'), ... ): ... res = s.boldWords(*_in) ... if res != _out: print(_in, res) ... gotcha.append(res == _out) >>> bool(gotcha) and all(gotcha) True """ class Solution: def boldWords(self, words, s): """ :type words: List[str] :type s: str :rtype: str """ if not s or not words: return '' TMPL = '{}' n = len(s) ans = [] is_bold = [False] * n left = right = 0 for left in range(n): for w in words: size = len(w) if s[left:left + size] == w and left + size > right: right = left + size is_bold[left] = right > left left = right = 0 while left < n: if not is_bold[left]: ans.append(s[left]) left += 1 continue right = left while right < n and is_bold[right]: right += 1 ans.append(TMPL.format(s[left:right])) left = right # imply left' = left + 1 return ''.join(ans) ================================================ FILE: leetcode/769_max_chunks_to_make_sorted.py ================================================ """ Main Concept: given nums: 0, 2, 1, 4, 3, 5, 7, 6 1. the sorted nums is just its index 2. maintain a `M` to record, and `M[i]` means the max in [0:i] in nums 3. if `M[i] == i`, ans += 1 sorted: 0, 1, 2, 3, 4, 5, 6, 7 index: 0, 1, 2, 3, 4, 5, 6, 7 max: 0, 2, 2, 4, 4, 5, 7, 7 ans: 1, 1, 2, 2, 3, 4, 4, 5 chunks: 0| 2, 1| 4, 3| 5| 7, 6 Improvement: just compare when visit it space: O(n) -> O(1) """ class Solution: def maxChunksToSorted(self, nums): """ :type nums: List[int] :rtype: int """ ans = 0 if not nums: return ans _max = 0 # since 0 is the min in nums for i in range(len(nums)): _max = max(_max, nums[i]) if _max == i: ans += 1 return ans class Solution: def maxChunksToSorted(self, nums): """ :type nums: List[int] :rtype: int """ ans = 0 if not nums: return ans n = len(nums) M = [0] * n # `M[i]` means the max in [0:i] in nums for i in range(n): M[i] = nums[i] if i > 0 and M[i - 1] > M[i]: M[i] = M[i - 1] for i in range(n): if M[i] == i: ans += 1 return ans ================================================ FILE: leetcode/776_split_bst.py ================================================ # Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def splitBST(self, root, target): """ :type root: TreeNode :type target: int :rtype: List[TreeNode] """ if not root: return None, None if root.val > target: left, right = self.splitBST(root.left, target) root.left = right return left, root else: left, right = self.splitBST(root.right, target) root.right = left return root, right ================================================ FILE: leetcode/777_swap_adjacent_in_lr_string.py ================================================ class Solution: def canTransform(self, start, end): """ :type start: str :type end: str :rtype: bool """ if len(start) != len(end): return False m, n = len(start), len(end) i = j = 0 while i < m and j < n: while i < m and start[i] == 'X': i += 1 while j < n and end[j] == 'X': j += 1 if i == m and j == n: return True if i == m or j == n: return False if start[i] != end[j]: return False if start[i] == 'L' and j > i: return False if start[i] == 'R' and i > j: return False i += 1 j += 1 return True ================================================ FILE: leetcode/778_swim_in_rising_water.py ================================================ from heapq import heappush, heappop class Solution: def swimInWater(self, G): """ :type G: List[List[int]] :rtype: int """ ans = 0 if not G or not G[0]: return ans n = len(G) V = ((-1, 0), (1, 0), (0, -1), (0, 1)) heap = [(G[0][0], 0, 0)] visited = {(0, 0): True} while heap: depth, x, y = heappop(heap) if depth > ans: ans = depth if x == y == n - 1: return ans for dx, dy in V: _x = x + dx _y = y + dy if not (0 <= _x < n and 0 <= _y < n): continue if (_x, _y) in visited: continue visited[_x, _y] = True heappush(heap, (G[_x][_y], _x, _y)) return ans ================================================ FILE: leetcode/779_k_th_symbol_in_grammar.py ================================================ class Solution: def kthGrammar(self, N, K): """ :type N: int :type K: int :rtype: int """ if N == 1: return 0 if K % 2 == 0: if self.kthGrammar(N - 1, K // 2) == 0: return 1 else: return 0 else: if self.kthGrammar(N - 1, (K + 1) // 2) == 0: return 0 else: return 1 ================================================ FILE: leetcode/783_minimum_distance_between_bst_nodes.py ================================================ """ Definition for a binary tree node. class TreeNode: def __init__(self, x): self.val = x self.left = None self.right = None """ class Solution: """ Recursion time: O(n) space: O(1) """ ans = float('inf') pre = None def minDiffInBST(self, root): """ :type root: TreeNode :rtype: int """ if not root: return self.ans self.minDiffInBST(root.left) if self.pre and root.val - self.pre.val < self.ans: self.ans = root.val - self.pre.val self.pre = root self.minDiffInBST(root.right) return self.ans class Solution: """ Iteration time: O(n) space: O(n) """ def minDiffInBST(self, root): """ :type root: TreeNode :rtype: int """ if not root: return ans = float('inf') pre = None stack = [] node = root while node or stack: while node: stack.append(node) node = node.left node = stack.pop() if pre and node.val - pre.val < ans: ans = node.val - pre.val pre = node node = node.right return ans ================================================ FILE: leetcode/784_letter_case_permutation.py ================================================ class Solution: def letterCasePermutation(self, s): """ :type s: str :rtype: List[str] """ if not s: return [''] ans = [] self.dfs(s, 0, ans, []) return ans def dfs(self, s, i, ans, path): if i == len(s): ans.append(''.join(path)) return options = [s[i]] if s[i].isdigit() else [s[i].lower(), s[i].upper()] for c in options: path.append(c) self.dfs(s, i + 1, ans, path) path.pop() ================================================ FILE: leetcode/785_is_graph_bipartite.py ================================================ class Solution: def isBipartite(self, graph): """ :type graph: List[List[int]] :rtype: bool """ color = {} for node in range(len(graph)): if node not in color: color[node] = 0 for nei in graph[node]: if nei not in color: color[nei] = color[node] ^ 1 elif color[nei] == color[node]: return False return True ================================================ FILE: leetcode/786_k_th_smallest_prime_fraction.py ================================================ """ Main Concept: just like found kth smallest num in matrix example: [1, 2, 5, 7] 7 5 2 1 1 1/7 1/5 1/2 1/1 2 2/7 2/5 ... 5 ... 7 ... """ from heapq import heappush, heappop class Solution: def kthSmallestPrimeFraction(self, A, K): """ :type A: List[int] :type K: int :rtype: List[int] """ heap = [] n = len(A) A.sort() for i in range(n): heappush(heap, (A[i]/A[-1], i, n - 1)) for _ in range(K - 1): _, i, j = heappop(heap) j -= 1 if j >= 0: heappush(heap, (A[i]/A[j], i, j)) _, i, j = heappop(heap) return [A[i], A[j]] ================================================ FILE: leetcode/787_cheapest_flights_within_k_stops.py ================================================ class Solution: def findCheapestPrice(self, n, flights, src, dst, K): """ :type n: int :type flights: List[List[int]] :type src: int :type dst: int :type K: int :rtype: int """ min_cost = [float('inf')] * n # the minimum cost to get to node costs = [float('inf')] * n min_cost[src] = costs[src] = 0 for _ in range(K + 1): for u, v, cost in flights: costs[v] = min(costs[v], min_cost[u] + cost) min_cost = costs return costs[dst] if costs[dst] < float('inf') else -1 class Solution: def findCheapestPrice(self, n, flights, src, dst, K): """ :type n: int :type flights: List[List[int]] :type src: int :type dst: int :type K: int :rtype: int """ if src == dst: return 0 INF = float('inf') """ `dp[i][k]` means the cost when the end is `i` with `k` stop """ dp = [[INF] * (K + 1) for _ in range(n)] dp[src][0] = 0 for start, end, cost in flights: if start == src and cost < dp[end][0]: dp[end][0] = cost for k in range(1, K + 1): for i in range(n): dp[i][k] = dp[i][k - 1] for start, end, cost in flights: dp[end][k] = min( dp[end][k], dp[start][k - 1] + cost ) return dp[dst][K] if dp[dst][K] < INF else -1 ================================================ FILE: leetcode/788_rotated_digits.py ================================================ class Solution: def rotatedDigits(self, N): """ :type N: int :rtype: int """ ans = 0 for i in range(1, N + 1): if self.is_good(i): ans += 1 return ans def is_good(self, N): res = False while N > 0: D = N % 10 if D in (3, 4, 7): return False if D in (2, 5, 6, 9): res = True N = N // 10 return res ================================================ FILE: leetcode/789_escape_the_ghosts.py ================================================ class Solution: def escapeGhosts(self, ghosts, target): """ :type ghosts: List[List[int]] :type target: List[int] :rtype: bool """ R, C = target pacman_dist = abs(R) + abs(C) # (R - 0) + (C - 0) for x, y in ghosts: ghost_dist = abs(R - x) + abs(C - y) if ghost_dist <= pacman_dist: return False return True ================================================ FILE: leetcode/790_domino_and_tromino_tiling.py ================================================ class Solution: def numTilings(self, N): """ :type N: int :rtype: int """ if N < 3: return N MOD = 10 ** 9 + 7 dp = [0] * (N + 1) dp[:3] = 1, 1, 2 for i in range(3, N + 1): dp[i] = (dp[i - 1] * 2 + dp[i - 3]) % MOD return dp[N] ================================================ FILE: leetcode/791_custom_sort_string.py ================================================ class Solution: def customSortString(self, S, T): """ :type S: str :type T: str :rtype: str """ return ''.join(sorted(T, key=lambda c: S.find(c))) ================================================ FILE: leetcode/7_reverse_integer.py ================================================ class Solution: def reverse(self, x): """ :type x: int :rtype: int """ ans = 0 if not x: return ans INT_MAX = 0x7FFFFFFF _x = x if x > 0 else -x while _x: ans = ans * 10 + _x % 10 _x //= 10 if ans >= INT_MAX: return 0 return ans if x > 0 else -ans ================================================ FILE: leetcode/81_search_in_rotated_sorted_array_ii.py ================================================ class Solution: def search(self, nums, target): """ :type nums: List[int] :type target: int :rtype: bool """ if not nums: return False for num in nums: if num == target: return True return False ================================================ FILE: leetcode/8_string_to_integer_atoi.py ================================================ class Solution: def myAtoi(self, s): """ :type s: str :rtype: int """ ans = 0 if not s: return ans INT_MAX = 0x7FFFFFFF N = len(s) ZERO = ord('0') sign = 1 i = 0 while i < N and s[i] == ' ': i += 1 if i < N and s[i] in ('+', '-'): sign = -1 if s[i] == '-' else 1 i += 1 while i < N and s[i].isdigit(): val = ord(s[i]) - ZERO if (ans > INT_MAX // 10 or (ans == INT_MAX // 10 and val > 7)): return INT_MAX if sign == 1 else ~INT_MAX ans = ans * 10 + val i += 1 return sign * ans ================================================ FILE: lintcode/102_linked_list_cycle.py ================================================ """ Definition of ListNode class ListNode(object): def __init__(self, val, next=None): self.val = val self.next = next """ class Solution: """ @param: head: The first node of linked list. @return: True if it has a cycle, or false """ def hasCycle(self, head): """ if its a cycle, and then we can ensure the 2-pace pointer and the 1-pace pointer will eventually meet otherwise its a list => at some point there will be no `node.next` """ if not head or not head.next: return False slow = head fast = head.next while slow != fast: if not fast.next or not fast.next.next: return False slow = slow.next fast = fast.next.next return True ================================================ FILE: lintcode/103_linked_list_cycle_ii.py ================================================ """ Definition of ListNode class ListNode(object): def __init__(self, val, next=None): self.val = val self.next = next """ class Solution: """ @param: head: The first node of linked list. @return: The node where the cycle begins. if there is no cycle, return null """ def detectCycle(self, head): """ example: 1->2->3 ^ v 5<-4 * h: head, s: slow, f: fast, i: intersection node r1/ s f 1 -> 2 -> 3 ^ v 5 <- 4 r2/ s 1 -> 2 -> 3 ^ v 5 <- 4 f r3/ f s 1 -> 2 -> 3 ^ v 5 <- 4 r4/ h i 1 -> 2 -> 3 ^ v 5 <- 4 s, f h->i == 1 s->i == 2 """ if not head or not head.next: return """ if its a cycle, and then we can ensure the 2-pace pointer and the 1-pace pointer will eventually meet otherwise its a list => at some point there will be no `node.next` """ slow, fast = head, head.next while slow is not fast: if not fast or not fast.next: return slow = slow.next fast = fast.next.next """ at this point, slow meet fast and at the intersection node the steps from the first node is equal to from meet node plus 1 """ while head is not slow.next: head = head.next slow = slow.next return head ================================================ FILE: lintcode/104_merge_k_sorted_lists.py ================================================ """ Definition of ListNode class ListNode(object): def __init__(self, val, next=None): self.val = val self.next = next """ import heapq class Solution: def mergeKLists(self, lists): """ :type lists: list[ListNode] :rtype: ListNode """ if not lists: return dummy = tail = ListNode(-1) heap = [] for i in range(len(lists)): if not lists[i]: continue heapq.heappush(heap, (lists[i].val, i)) while heap: val, i = heapq.heappop(heap) tail.next = ListNode(val) tail = tail.next if lists[i].next: lists[i] = lists[i].next heapq.heappush(heap, (lists[i].val, i)) return dummy.next ================================================ FILE: lintcode/105_copy_list_with_random_pointer.py ================================================ """ Definition for singly-linked list with a random pointer. class RandomListNode: def __init__(self, x): self.label = x self.next = None self.random = None """ """ using hashmap time: O(2n) => O(n) space: O(n) """ class Solution: def copyRandomList(self, head): """ :type head: RandomListNode :rtype: RandomListNode """ N = {} dummy = tail = RandomListNode(-1) while head: node = RandomListNode(head.label) node.random = head.random tail.next = node N[head] = node tail = tail.next head = head.next head = dummy.next while head: if head.random: head.random = N[head.random] head = head.next return dummy.next """ temply save in n.next time: O(3n) => O(n) space: O(1) example: 1->2->3 copy_next/ |--------->| 1 -> 1' -> 2 -> 2' -> 3 -> 3' replace_random/ |--------->| |----+---->| | 1 -> 1' -> 2 -> 2' -> 3 -> 3' split_list/ |--------->| 1 -> 2 -> 3 1' -> 2' -> 3' |--------->| """ class Solution: def copyRandomList(self, head): """ :type head: RandomListNode :rtype: RandomListNode """ if not head: return tail = head node = None while tail: node = RandomListNode(tail.label) node.random = tail.random node.next = tail.next tail.next = node tail = tail.next.next tail = head while tail: if tail.next and tail.random: tail.next.random = tail.random.next tail = tail.next.next node = tail = head.next while tail and tail.next: tail.next = tail.next.next tail = tail.next return node ================================================ FILE: lintcode/107_word_break.py ================================================ class Solution: """ `dp[i]` means `s[:i]` is segmented by words """ def wordBreak(self, s, words): """ :type s: str :type words: List[str] :rtype: bool """ if not s and not words: return True if not s or not words: return False max_size = max(len(w) for w in words) word_set = set(words) n = len(s) dp = [False] * (n + 1) dp[0] = True for i in range(1, n + 1): for j in range(1, min(i, max_size) + 1): if dp[i - j] and s[i - j:i] in word_set: dp[i] = True break return dp[n] ================================================ FILE: lintcode/108_palindrome_partitioning_ii.py ================================================ class Solution: # @param S, a string # @return an integer def minCut(self, S): if not S: return -1 n = len(S) INFINITY = float('inf') is_palindrome = self.get_palin_map(S) """ `dp[i]` means the minimum palindrome count broken from the substring ended at `i` """ dp = [INFINITY] * (n + 1) dp[0] = 0 for end in range(1, n + 1): for start in range(end): if (not is_palindrome[start][end - 1] or dp[start] is INFINITY): continue if dp[start] + 1 < dp[end]: dp[end] = dp[start] + 1 return dp[n] - 1 def get_palin_map(self, S): n = len(S) is_palindrome = [[False] * n for _ in range(n)] is_palindrome[0][0] = True for end in range(1, n): is_palindrome[end][end] = True start = end - 1 is_palindrome[start][end] = (S[start] == S[end]) for start in range(n - 1 - 2, -1, -1): for end in range(start + 2, n): if not is_palindrome[start + 1][end - 1]: continue is_palindrome[start][end] = (S[start] == S[end]) return is_palindrome ================================================ FILE: lintcode/109_triangle.py ================================================ """ DP: Memory Searching """ class Solution: """ @param: triangle: a list of lists of integers @return: An integer, minimum path sum """ def minimumTotal(self, triangle): if not triangle or not triangle[0]: return 0 return self.memo_search(0, 0, triangle, {}) def memo_search(self, depth, start, triangle, memo): if depth == len(triangle) - 1: return triangle[depth][start] key = (depth, start) if key in memo: return memo[key] memo[key] = min( self.memo_search(depth + 1, start, triangle, memo), self.memo_search(depth + 1, start + 1, triangle, memo) ) memo[key] += triangle[depth][start] return memo[key] """ DP: Top-down Recuring + Rolling Array """ class Solution: """ @param: triangle: a list of lists of integers @return: An integer, minimum path sum """ def minimumTotal(self, triangle): if not triangle or not triangle[0]: return 0 INFINITY = float('inf') m = len(triangle) dp = [[INFINITY] * (m + 1) for _ in range(2)] prev = curr = 0 for i in range(1, m + 1): prev = curr curr = 1 - curr for j in range(1, i + 1): """ dp[prev][j] == dp[i - 1][j] dp[curr][j] == dp[i][j] """ dp[curr][j] = triangle[i - 1][j - 1] if dp[prev][j - 1] < INFINITY or dp[prev][j] < INFINITY: """ NO need to calculate first, since only one path from top """ dp[curr][j] += min( dp[prev][j - 1], dp[prev][j] ) return min(dp[curr]) """ DP: Bottom-up Recuring + Rolling Array """ class Solution: """ @param: triangle: a list of lists of integers @return: An integer, minimum path sum """ def minimumTotal(self, triangle): if not triangle or not triangle[0]: return 0 INFINITY = float('inf') m = len(triangle) dp = [[INFINITY] * (m + 1) for _ in range(m + 1)] prev = curr = 0 for i in range(m - 1, -1, -1): prev = curr curr = 1 - curr for j in range(i + 1): """ dp[prev][j] == dp[i + 1][j] dp[curr][j] == dp[i][j] """ dp[curr][j] = triangle[i][j] if dp[prev][j] < INFINITY or dp[prev][j + 1] < INFINITY: """ MUST be calculated first, since there are two paths from bottom and `dp[curr][j]` maybe negative """ dp[curr][j] = min( dp[curr][j] + dp[prev][j], dp[curr][j] + dp[prev][j + 1] ) return dp[curr][0] ================================================ FILE: lintcode/10_string_permutation_ii.py ================================================ class Solution: """ @param: S: A string @return: all permutations """ def stringPermutation2(self, S): if not S: return [''] S = sorted(S) ans = [] self.dfs(S, ans, []) return ans def dfs(self, S, ans, path): if not S: ans.append(''.join(path)) return for i in range(len(S)): if i > 0 and S[i] == S[i - 1]: continue path.append(S[i]) self.dfs(S[:i] + S[i + 1:], ans, path) path.pop() ================================================ FILE: lintcode/110_minimum_path_sum.py ================================================ class Solution: """ @param: grid: a list of lists of integers @return: An integer, minimizes the sum of all numbers along its path """ def minPathSum(self, grid): if not grid: return 0 m, n = len(grid), len(grid[0]) dp = [[0] * n for _ in range(m)] for j in range(n): if j == 0: dp[0][j] = grid[0][j] continue dp[0][j] = grid[0][j] + dp[0][j - 1] for i in range(1, m): dp[i][0] = grid[i][0] + dp[i - 1][0] for j in range(1, n): if dp[i - 1][j] < dp[i][j - 1]: dp[i][j] = grid[i][j] + dp[i - 1][j] else: dp[i][j] = grid[i][j] + dp[i][j - 1] return dp[m - 1][n - 1] ================================================ FILE: lintcode/111_climbing_stairs.py ================================================ """ DP: rolling array with `n + 1` """ class Solution: """ @param n: An integer @return: An integer """ def climbStairs(self, n): if n <= 0: return 0 dp = [0] * 3 pre2, pre1, curr = 0, 0, 1 dp[0] = dp[1] = 1 for i in range(2, n + 1): pre2 = pre1 pre1 = curr curr = i % 3 dp[curr] = dp[pre1] + dp[pre2] return dp[curr] """ DP: origin `n` """ class Solution: """ @param n: An integer @return: An integer """ def climbStairs(self, n): if n <= 0: return 0 dp = [0] * n dp[0] = 1 dp[1] = 2 for i in range(2, n): dp[i] = dp[i - 1] + dp[i - 2] return dp[n - 1] """ DP: origin `n + 1` """ class Solution: """ @param n: An integer @return: An integer """ def climbStairs(self, n): if n <= 0: return 0 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] ================================================ FILE: lintcode/114_unique_paths.py ================================================ class Solution: def uniquePaths(self, m, n): """ :type m: int :type n: int :rtype: int """ dp = [[1] * n for _ in range(m)] for x in range(1, m): for y in range(1, n): dp[x][y] = dp[x - 1][y] + dp[x][y - 1] return dp[m - 1][n - 1] ================================================ FILE: lintcode/115_unique_paths_ii.py ================================================ class Solution: """ @param: G: A list of lists of integers @return: An integer """ def uniquePathsWithObstacles(self, G): if not G or not G[0]: return 0 OBSTACLE = 1 m, n = len(G), len(G[0]) dp = [[0] * n for _ in range(2)] prev = curr = 0 for j in range(n): if G[0][j] == OBSTACLE: break dp[curr][j] = 1 for i in range(1, m): prev = curr curr = 1 - curr dp[curr][0] = 0 if G[i][0] == OBSTACLE else dp[prev][0] for j in range(1, n): if G[i][j] == OBSTACLE: dp[curr][j] = 0 continue dp[curr][j] = dp[prev][j] + dp[curr][j - 1] return dp[curr][n - 1] ================================================ FILE: lintcode/116_jump_game.py ================================================ """ Greedy https://leetcode.com/articles/jump-game/ """ class Solution: def canJump(self, A): """ :type A: List[int] :rtype: bool """ if not A: return False last_at = len(A) - 1 for i in range(last_at, -1, -1): if i + A[i] >= last_at: last_at = i return last_at == 0 """ DP """ class Solution: def canJump(self, A): """ :type A: List[int] :rtype: bool """ if not A: return False n = len(A) dp = [False] * n """ `dp[i]` means `i` could be reached """ dp[0] = True for i in range(1, n): for j in range(i): """ backtracking if `j` could be reached """ if dp[j] and j + A[j] >= i: """ if jump from `j` can reach `i` """ dp[i] = True break return dp[n - 1] ================================================ FILE: lintcode/117_jump_game_ii.py ================================================ """ Greedy """ class Solution: """ @param: A: A list of integers @return: An integer """ def jump(self, A): if not A: return -1 target = len(A) - 1 start = end = jumps = 0 while end < target: jumps += 1 furthest = end for i in range(start, end + 1): if i + A[i] > furthest: furthest = i + A[i] start = end + 1 end = furthest return jumps """ DP """ class Solution: """ @param: A: A list of integers @return: An integer """ def jump(self, A): if not A: return -1 INFINITY = float('inf') n = len(A) dp = [INFINITY] * n dp[0] = 0 for i in range(1, n): for j in range(i): if (dp[j] < INFINITY and j + A[j] >= i and dp[j] + 1 < dp[i]): dp[i] = dp[j] + 1 return dp[n - 1] ================================================ FILE: lintcode/118_distinct_subsequences.py ================================================ class Solution: """ @param: : A string @param: : A string @return: Count the number of distinct subsequences """ def numDistinct(self, S, T): if S is None or T is None: return 0 if S is '' and '': return 1 m, n = len(S), len(T) """ `dp[i][j]` means the count of distinct subsequences (the substr end at `T[j - 1]`) in the substr end at `S[i - 1]` """ dp = [[0] * (n + 1) for _ in range(2)] prev = curr = 0 dp[curr][0] = 1 for i in range(1, m + 1): prev = curr curr = 1 - curr dp[curr][0] = 1 for j in range(1, n + 1): """ case 1: `S[i - 1]` and `T[j - 1]` is not a pair so keep `T[j - 1]` in candidates """ dp[curr][j] = dp[prev][j] """ case 2: `S[i - 1]` and `T[j - 1]` is a pair do NOT `+1` -> its for size, this problem is for count """ if S[i - 1] == T[j - 1]: dp[curr][j] += dp[prev][j - 1] return dp[curr][n] ================================================ FILE: lintcode/119_edit_distance.py ================================================ class Solution: def minDistance(self, s, t): """ :type s: str :type t: str :rtype: int """ if s is None or t is None: return 0 m, n = len(s), len(t) """ `dp[i][j]` means the minimum operations to convert the substr end at `A[i - 1]` to the substr end at `B[j - 1]` """ dp = [[0] * (n + 1) for _ in range(m + 1)] for i in range(1, m + 1): dp[i][0] = i for j in range(1, n + 1): dp[0][j] = j for i in range(1, m + 1): for j in range(1, n + 1): """ no need to init dp[curr][j] case 1: no need to do any operations """ if s[i - 1] == t[j - 1]: dp[i][j] = dp[i - 1][j - 1] continue """ case 2: remove last char in A case 3: add `B[j - 1]` to the end of A case 4: replace laster char in A """ dp[i][j] = 1 + min( dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1] ) return dp[m][n] ================================================ FILE: lintcode/11_search_range_in_binary_search_tree.py ================================================ """ Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ class Solution: def searchRange(self, root, a, b): """ :type root: TreeNode :type a: int :type b: int :rtype: list[int] """ ans = [] if not root: return ans self.dfs(root, a, b, ans) return ans def dfs(self, node, a, b, ans): if not node: return self.dfs(node.left, a, b, ans) if a <= node.val <= b: ans.append(node.val) self.dfs(node.right, a, b, ans) ================================================ FILE: lintcode/120_word_ladder.py ================================================ """ Test Case: "a" "a" ["b"] => should check again words in queue """ class Solution: def ladderLength(self, s, e, D): """ :type s: str :type e: str :type D: List[str] :rtype: int """ if (not s or not e or len(s) != len(e) or not D): return 0 if s == e: return 1 if s not in D: D.append(s) if e not in D: D.append(e) n = len(s) next_words = [None] * n for i in range(n): next_words[i] = _words = {} for word in D: key = word[:i] + word[i + 1:] if key not in _words: _words[key] = set() _words[key].add(word) queue = [e] distance = {e: 1} for word in queue: for _word in self.get_next_word(word, next_words): if _word in distance: continue distance[_word] = distance[word] + 1 if _word == s: return distance[_word] queue.append(_word) return 0 def get_next_word(self, word, next_words): for i in range(len(word)): key = word[:i] + word[i + 1:] if key not in next_words[i]: continue for _word in next_words[i][key]: if _word == word: continue yield _word ================================================ FILE: lintcode/121_word_ladder_ii.py ================================================ """ Main Concept: 1. building `next_words` in advance to speed up 2. using BFS from `B` to `A` to calculate the distance guide 3. using DFS step by step to find all possible path to get `B` """ class Solution: """ @param: A: a string @param: B: a string @param: D: a set of string @return: a list of lists of string """ def findLadders(self, A, B, D): ans = [] if (D is None or A is None or B is None or len(A) != len(B)): return ans if A not in D: D.add(A) if B not in D: D.add(B) n = len(A) next_words = [None] * n for i in range(n): next_words[i] = W = {} for word in D: key = word[:i] + word[i + 1:] if key not in W: W[key] = set() W[key].add(word) queue = [B] distance = {B: 1} for word in queue: if word == A: break for _word in self.get_next_word(word, next_words): if _word in distance: continue distance[_word] = distance[word] + 1 queue.append(_word) self.dfs(A, B, next_words, distance, ans, [A]) return ans def dfs(self, word, B, next_words, distance, ans, path): if word == B: ans.append(path[:]) return for _word in self.get_next_word(word, next_words): if (_word not in distance or distance[_word] != distance[word] - 1): continue path.append(_word) self.dfs(_word, B, next_words, distance, ans, path) path.pop() def get_next_word(self, word, next_words): for i in range(len(word)): key = word[:i] + word[i + 1:] if key not in next_words[i]: continue for _word in next_words[i][key]: if _word == word: continue yield _word ================================================ FILE: lintcode/122_largest_rectangle_in_histogram.py ================================================ """ REF: https://aaronice.gitbooks.io/lintcode/content/data_structure/largest_rectangle_in_histogram.html """ """ Brute Force: TLE """ class Solution: def largestRectangleArea(self, H): """ :type H: List[int] :rtype: int """ ans = 0 if not H: return ans n = len(H) L = [0] * n # lowest height for left in range(n): for right in range(left, n): L[right] = H[right] if right > left and L[right - 1] < H[right]: L[right] = L[right - 1] area = L[right] * (right - left + 1) if area > ans: ans = area return ans """ Brute Force with Pruning """ class Solution: def largestRectangleArea(self, H): """ :type H: List[int] :rtype: int """ ans = 0 if not H: return ans n = len(H) for right in range(len(H)): if right < n - 1 and H[right] <= H[right + 1]: continue Hmin = H[right] for left in range(right, -1, -1): if H[left] < Hmin: Hmin = H[left] area = Hmin * (right - left + 1) if area > ans: ans = area return ans """ Mono-stack """ class Solution: def largestRectangleArea(self, H): """ :type H: List[int] :rtype: int """ ans = 0 if not H: return ans H.append(0) stack = [] for right in range(len(H)): while stack and H[stack[-1]] >= H[right]: height = H[stack.pop()] left = stack[-1] if stack else -1 area = height * (right - left - 1) if area > ans: ans = area stack.append(right) return ans ================================================ FILE: lintcode/123_word_search.py ================================================ class Solution: V = ( (-1, 0), ( 1, 0), ( 0, -1), ( 0, 1), ) """ @param: G: A list of lists of character @param: s: A string @return: A boolean """ def exist(self, G, s): if G is None or G[0] is None or s is None: return False m, n = len(G), len(G[0]) visited = [[False] * n for _ in range(m)] for x in range(m): for y in range(n): if (G[x][y] == s[0] and self.dfs(G, x, y, s, 1, visited)): return True return False def dfs(self, G, x, y, s, i, visited): if i >= len(s): return True for dx, dy in self.V: _x = x + dx _y = y + dy if not (0 <= _x < len(G) and 0 <= _y < len(G[0])): continue if visited[_x][_y] or G[_x][_y] != s[i]: continue visited[_x][_y] = True if self.dfs(G, _x, _y, s, i + 1, visited): return True visited[_x][_y] = False return False ================================================ FILE: lintcode/124_longest_consecutive_sequence.py ================================================ class Solution: """ maintain a set to record if there is unused cands """ def longestConsecutive(self, nums): """ :type nums: list[int] :rtype: int """ ans = 0 if not nums: return ans cands = set(nums) # dedup for a in nums: if a not in cands: continue cands.discard(a) size = 1 b, c = a - 1, a + 1 while b in cands: cands.discard(b) b -= 1 size += 1 while c in cands: cands.discard(c) c += 1 size += 1 if size > ans: ans = size return ans class Solution: """ 1. sorted 2. if its consecutive, add 1 for size 3. save the maximum size """ def longestConsecutive(self, nums): """ :type nums: list[int] :rtype: int """ ans = 0 if not nums: return ans nums.sort() size = 1 for i in range(1, len(nums)): if nums[i] == nums[i - 1]: continue if nums[i] == nums[i - 1] + 1: size += 1 else: size = 1 if size > ans: ans = size return ans if ans > 0 else size ================================================ FILE: lintcode/125_backpack_ii.py ================================================ """ Rolling Array """ class Solution: """ @param: m: An integer m denotes the size of a backpack @param: A: Given n items with size A[i] @param: V: Given n items with value V[i] @return: The maximum value """ def backPackII(self, m, A, V): if not m or not A or not V: return 0 # `dp[i][w]` means the maximum value # with weight `w` in the former `i` items dp = [[0] * (m + 1) for _ in range(2)] prev = curr = 0 for i in range(1, len(A) + 1): prev = curr curr = 1 - curr for w in range(1, m + 1): dp[curr][w] = dp[prev][w] if w >= A[i - 1]: dp[curr][w] = max( dp[curr][w], dp[prev][w - A[i - 1]] + V[i - 1] ) return dp[curr][m] """ Origin """ class Solution: """ @param: m: An integer m denotes the size of a backpack @param: A: Given n items with size A[i] @param: V: Given n items with value V[i] @return: The maximum value """ def backPackII(self, m, A, V): if not m or not A or not V: return 0 n = len(A) dp = [[0] * (m + 1) for _ in range(n + 1)] for i in range(1, n + 1): for w in range(1, m + 1): dp[i][w] = dp[i - 1][w] if w >= A[i - 1]: dp[i][w] = max( dp[i][w], dp[i - 1][w - A[i - 1]] + V[i - 1] ) return dp[n][m] ================================================ FILE: lintcode/126_max_tree.py ================================================ """ Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ class Solution: """ @param: A: Given an integer array with no duplicates. @return: The root of max tree. """ """ Assuming A = [2,5,6,0,3,1] step1/ val: 2, stack: [2] step2/ val: 5, stack: [5{2,}] step3/ val: 6, stack: [6{5{2,},}] step4/ val: 0, stack: [6{5{2,},0}, 0] step5/ val: 3, stack: [6{5{2,},3{0,}}, 3{0,}] step6/ val: 1, stack: [6{5{2,},3{0,1}}, 3{0,1}, 1] """ def maxTree(self, A): stack = [] for val in A: node = TreeNode(val) while stack and val > stack[-1].val: node.left = stack.pop() # current val less than the last node in stack if stack: stack[-1].right = node stack.append(node) return stack[0] ================================================ FILE: lintcode/127_topological_sorting.py ================================================ """ Definition for a Directed graph node class DirectedGraphNode: def __init__(self, x): self.label = x self.neighbors = [] """ class Solution: """ @param: graph: A list of Directed graph node @return: Any topological order for the given graph. """ def topSort(self, graph): ans = [] if not graph: return ans indegs = {} for node in graph: if node not in indegs: indegs[node] = 0 for _node in node.neighbors: if _node not in indegs: indegs[_node] = 0 indegs[_node] += 1 queue = [node for node, indeg in indegs.items() if indeg == 0] for node in queue: ans.append(node) for _node in node.neighbors: indegs[_node] -= 1 if indegs[_node] == 0: queue.append(_node) return ans ================================================ FILE: lintcode/128_hash_function.py ================================================ class Solution: """ @param: key: A string you should hash @param: HASH_SIZE: An integer @return: An integer """ def hashCode(self, key, HASH_SIZE): if not key: return 0 MAGIC_NUMBER = 33 _code = 0 for char in key: _code = (_code * MAGIC_NUMBER + ord(char)) % HASH_SIZE return _code ================================================ FILE: lintcode/129_rehashing.py ================================================ """ Definition of ListNode class ListNode(object): def __init__(self, val, next=None): self.val = val self.next = next """ class Solution: """ @param hash_table: A list of The first node of linked list @return: A list of The first node of linked list which have twice size """ def rehashing(self, hash_table): if not hash_table: return CAPACITY = len(hash_table) * 2 heads = [None] * CAPACITY tails = [None] * CAPACITY curr = _node = i = None for node in hash_table: curr = node while curr: i = curr.val % CAPACITY _node = ListNode(curr.val) if heads[i]: tails[i].next = _node else: heads[i] = _node tails[i] = _node curr = curr.next return heads ================================================ FILE: lintcode/12_min_stack.py ================================================ class MinStack: def __init__(self): self.stack = [] self.mins = [] def push(self, x): """ :type x: int :rtype: void """ self.stack.append(x) if not self.mins or x <= self.mins[-1]: self.mins.append(x) def pop(self): """ :rtype: int """ if not self.stack: return -1 x = self.stack.pop() if self.mins and x == self.mins[-1]: self.mins.pop() return x def top(self): """ :rtype: int """ if not self.stack: return -1 return self.stack[-1] def min(self): """ :rtype: int """ if not self.mins: return -1 return self.mins[-1] # Your MinStack object will be instantiated and called as such: # obj = MinStack() # obj.push(x) # obj.pop() # param_3 = obj.top() # param_4 = obj.min() ================================================ FILE: lintcode/130_heapify.py ================================================ class Solution: """ @param: A: Given an integer array @return: nothing """ def heapify(self, A): # start from mid-depth to sift down for i in range(len(A) // 2, -1, -1): self.siftdown(A, i) def siftdown(self, A, i): """ sift down 1. pick the smaller child to swap 2. if parent is already small than both children, no need to continue 3. continue to sift down in next depth """ n = len(A) while i * 2 + 1 < n: # left child _i = i * 2 + 1 if _i + 1 < n and A[_i + 1] < A[_i]: # right child _i += 1 if A[_i] >= A[i]: # if its already steady break A[i], A[_i] = A[_i], A[i] i = _i ================================================ FILE: lintcode/131_building_outline.py ================================================ """ this problem familiar with `leetcode/218_the_skyline_problem.py` with different output """ import heapq class HashHeapq: def __init__(self): self.heap = [] self.deleted = {} def push(self, val): heapq.heappush(self.heap, val) def pop(self): if self.is_empty(): return -1 return heapq.heappop(self.heap) def remove(self, val): if self.is_empty(): return if val not in self.deleted: self.deleted[val] = 0 self.deleted[val] += 1 def top(self): if self.is_empty(): return -1 return self.heap[0] def is_empty(self): while self.heap and self.deleted.get(self.heap[0]): val = heapq.heappop(self.heap) self.deleted[val] -= 1 return not self.heap class Solution: def buildingOutline(self, buildings): """ :type buildings: List[List[int]] :rtype: List[List[int]] """ ans = [] if not buildings: return ans time = [] for x, _x, height in buildings: time.append((x, height, True)) time.append((_x, height, False)) time.sort() heap = HashHeapq() tmp = [] for x, height, is_start in time: if is_start: heap.push(-height) else: heap.remove(-height) max_h = -heap.top() if not heap.is_empty() else 0 if tmp and tmp[-1][0] == x: tmp.pop() if tmp and tmp[-1][1] == max_h: continue tmp.append([x, max_h]) _x = pre_h = 0 for x, height in tmp: if pre_h > 0: ans.append([_x, x, pre_h]) _x = x pre_h = height return ans ================================================ FILE: lintcode/132_word_search_ii.py ================================================ class Solution: def __init__(self): self.root = self.new_node() self.row_vector = [1, -1, 0, 0] self.col_vector = [0, 0, 1, -1] def new_node(self): return { 'end_of': '', 'children': {} } def put(self, parent, string): if not string: return for char in string: if char in parent['children']: parent = parent['children'][char] else: parent['children'][char] = self.new_node() parent = parent['children'][char] parent['end_of'] = string """ @param: board: A list of lists of character @param: words: A list of string @return: A list of string """ def wordSearchII(self, board, words): if not words or len(words) < 1 \ or not board or len(board) < 1 \ or len(board[0]) < 1: return [] self.m, self.n = len(board), len(board[0]) self.board = board for word in words: self.put(self.root, word) result = {} for row in range(self.m): for col in range(self.n): if board[row][col] in self.root['children']: self.find(row, col, self.root, result) return result.keys() def find(self, x, y, parent, result): char = self.board[x][y] if char not in parent['children']: return parent = parent['children'][char] if parent['end_of']: result[parent['end_of']] = 1 parent['end_of'] = '' # To avoid returning along the original path, just simply set the last visited cell to `'#'` self.board[x][y] = '#' for d in range(4): _x = x + self.row_vector[d] _y = y + self.col_vector[d] if 0 <= _x < self.m \ and 0 <= _y < self.n \ and self.board[_x][_y] in parent['children']: self.find(_x, _y, parent, result) self.board[x][y] = char ================================================ FILE: lintcode/134_lru_cache.py ================================================ """ Main Concept: Dm <-> a <-> b <-> c <-> dm |<- cache_list (dll) 1. if cache is updated (set/get) => move to the end of cache_list 2. if cache is full => evict the most left node in cache first, that is `a` in above diagram => add the new cache to the end of the cache_list as new tail """ class LRUCache: def __init__(self, capacity): """ :type capacity: int """ self.cap = capacity self.nodes = {} self.D = CacheNode(-1) self.d = CacheNode(-1) self.D.nxt = self.d self.d.pre = self.D def get(self, key): """ :type key: int :rtype: int """ if key not in self.nodes: return -1 self._update(key) return self.nodes[key].val def set(self, key, val): """ :type key: int :type val: int :rtype: void """ if self.cap <= 0: return if key in self.nodes: self._update(key, val) return while len(self.nodes) >= self.cap: self._evict() self._add(key, val) def _evict(self): node = self._pop_head() del self.nodes[node.key] def _update(self, key, val=None): node = self.nodes[key] if val: node.val = val node.unlink() self._add_tail(node) def _add(self, key, val): self.nodes[key] = CacheNode(key, val) self._add_tail(self.nodes[key]) def _pop_head(self): node = self.D.nxt node.unlink() return node def _add_tail(self, node): node.link(self.d.pre, self.d) class CacheNode: def __init__(self, key, val=None, pre=None, nxt=None): self.key = key self.val = val self.pre = pre self.nxt = nxt def link(self, pre, nxt): self.pre = pre self.nxt = nxt pre.nxt = self nxt.pre = self def unlink(self): self.pre.nxt = self.nxt self.nxt.pre = self.pre self.pre = self.nxt = None ================================================ FILE: lintcode/135_combination_sum.py ================================================ class Solution: """ @param: A: A list of integers @param: target: An integer @return: A list of lists of integers """ def combinationSum(self, A, target): ans = [] if not A: return ans A.sort() self.dfs(A, 0, target, ans, []) return ans def dfs(self, A, start, remaining, ans, path): if remaining == 0: ans.append(path[:]) return for i in range(start, len(A)): if remaining < A[i]: # note that, its `return` here # since `remaining < A[i]` and `A[i] <= A[i + 1] <= ...` # so once it continued, all iteration after i is no need return path.append(A[i]) self.dfs(A, i, remaining - A[i], ans, path) path.pop() ================================================ FILE: lintcode/1365_minimum_cycle_section.py ================================================ class Solution: """ @param array: an integer array @return: the length of the minimum cycle section """ def minimumCycleSection(self, array): if not array: return 0 n = len(array) for size in range(1, n + 1): gotcha = True for i in range(size): if any(array[i] != array[j] for j in range(i + size, n, size)): gotcha = False if gotcha: return size return n ================================================ FILE: lintcode/1366_directed_graph_loop.py ================================================ import collections class Solution: """ @param start: The start points set @param end: The end points set @return: Return if the graph is cyclic """ def isCyclicGraph(self, start, end): if not start or not end or len(start) != len(end): return False n = len(start) nxt = collections.defaultdict(set) visited = set() rec_stack = set() for i in range(n): nxt[start[i]].add(end[i]) for i in range(n): if start[i] in visited: continue if self.dfs(start[i], nxt, visited, rec_stack): return True return False def dfs(self, u, nxt, visited, rec_stack): if u not in nxt: return False visited.add(u) rec_stack.add(u) for v in nxt[u]: if v in rec_stack: return True if v not in visited and self.dfs(v, nxt, visited, rec_stack): return True rec_stack.discard(u) return False ================================================ FILE: lintcode/1367_police_distance.py ================================================ class Solution: """ @param matrix : the martix @return: the distance of grid to the police """ def policeDistance(self, matrix): m, n = len(matrix), len(matrix) POLICE = 1 WALL = -1 EMPTY = 0 ans = [[float('inf')] * n for _ in range(m)] queue = [] for x in range(m): for y in range(n): if matrix[x][y] == WALL: ans[x][y] = -1 elif matrix[x][y] == POLICE: ans[x][y] = 0 queue.append((x, y)) for x, y in queue: for dx, dy in ( (-1, 0), (1, 0), (0, -1), (0, 1), ): _x = x + dx _y = y + dy if not (0 <= _x < m and 0 <= _y < n): continue if matrix[_x][_y] == WALL: continue if ans[_x][_y] <= ans[x][y] + 1: continue ans[_x][_y] = ans[x][y] + 1 queue.append((_x, _y)) return ans ================================================ FILE: lintcode/1368_same_number.py ================================================ class Solution: """ @param nums: the arrays @param k: the distance of the same number @return: the ans of this question """ def sameNumber(self, nums, k): RES = ('NO', 'YES') if not nums or not k: return RES[0] idx = {} gotcha = False for i in range(len(nums)): if nums[i] in idx and i - idx[nums[i]] < k: gotcha = True idx[nums[i]] = i return RES[int(gotcha)] ================================================ FILE: lintcode/136_palindrome_partitioning.py ================================================ class Solution: ans = [] n = 0 is_palindrome = None """ @param: s: A string @return: A list of lists of string """ def partition(self, s): if not s: return self.ans self.n = len(s) self.check_palindrome(s) self.dfs(s, 0, []) return self.ans def check_palindrome(self, s): """ assuming string = 'aabb' s: start_index, e: end_index `is_palindrome[s][e] == T` means the substring(string[s:e+1]) is a palindrome the benefit to have the `is_palindrome` in advance is we won't need to traverse the whole string again when every time we need e 0 1 2 3 s a a b b 0 a [[T, T, F, F], 1 a [F, T, F, F], 2 b [F, F, T, T], 3 b [F, F, F, T]] and the traversal order to init this matrix is below: x: means `start > end`, its impossible [[r1, r2, r4, r4], [ x, r1, r2, r3], [ x, x, r1, r2], [ x, x, x, r1]] """ self.is_palindrome = [[False] * self.n for _ in range(self.n)] start = end = 0 # check the diagonal line `r1` and `r2` # the traversal order is top-left -> bottom-right, see graph above # since the status of `r3`, `r4`, ... depends on that for end in range(self.n): self.is_palindrome[end][end] = True if end > 0: start = end - 1 self.is_palindrome[start][end] = (s[start] == s[end]) # check the remaining triangle and traverse by line: `r3`, `r4`, ... # the traversal order is bottom -> top, see graph above # n - 3 = (n - 1) - 2 # start + 2 for start in range(self.n - 3, -1, -1): for end in range(start + 2, self.n): self.is_palindrome[start][end] = ( self.is_palindrome[start + 1][end - 1] and s[start] == s[end] ) # traverse all of the possible substring from start # if is a palindrome, continue to traverse # otherwise will be ignored # and catch all result at the end def dfs(self, s, start, palindromes): if start >= self.n: self.ans.append(palindromes) next_start = 0 for end in range(start, self.n): if self.is_palindrome[start][end]: # `palindromes + [s[start:next_start]]` # will create and return new list next_start = end + 1 self.dfs(s, next_start, palindromes + [s[start:next_start]]) ================================================ FILE: lintcode/137_clone_graph.py ================================================ """ Definition for a undirected graph node class UndirectedGraphNode: def __init__(self, x): self.label = x self.neighbors = [] """ """ Iteration """ class Solution: def cloneGraph(self, node): """ :type node: UndirectedGraphNode :rtype: UndirectedGraphNode """ if not node: return queue = [node] root = UndirectedGraphNode(node.label) N = {root.label: root} for node in queue: for neighbor in node.neighbors: _node = None if neighbor.label in N: _node = N[neighbor.label] else: _node = UndirectedGraphNode(neighbor.label) N[neighbor.label] = _node queue.append(neighbor) N[node.label].neighbors.append(_node) return root """ Recursion """ class Solution: def cloneGraph(self, node): """ :type node: UndirectedGraphNode :rtype: UndirectedGraphNode """ if not node: return return self.dfs(node, {}) def dfs(self, node, N): if node.label in N: return N[node.label] N[node.label] = UndirectedGraphNode(node.label) for neighbor in node.neighbors: N[node.label].neighbors.append(self.dfs(neighbor, N)) return N[node.label] ================================================ FILE: lintcode/138_subarray_sum.py ================================================ class Solution: """ @param: nums: A list of integers @return: A list of integers includes the index of the first number and the index of the last number """ def subarraySum(self, nums): """ len(nums) == 5 if `A[1] + A[2] + A[3] == 0` the cumulative sum in the `i == 3` iteration is same as the cumulative sum in `A[0]` => save every sum in hashmap => if got the same sum in following iteration => start = hashmap[sum] + 1, end = i """ if not nums: return [] sum_to_index = {} sum_to_index[0] = -1 prefix_sum = 0 for i in range(len(nums)): prefix_sum += nums[i] if prefix_sum in sum_to_index: return [ sum_to_index[prefix_sum] + 1, i ] sum_to_index[prefix_sum] = i return [] ================================================ FILE: lintcode/139_subarray_sum_closest.py ================================================ from collections import namedtuple Pair = namedtuple('Pair', ['sum', 'index']) class Solution: """ @param: nums: A list of integers @return: A list of integers includes the index of the first number and the index of the last number """ def subarraySumClosest(self, nums): """ Prefix Sum prefix_sum[i] means the culmulative sum before `i + 1` in `nums` => if we want to know the sum of [1, 2] => sum(nums[1:3]) == sum(nums[0:3]) - sum(nums[0:0]) """ ans = [0] * 2 if not nums: return ans n = len(nums) if n == 1: return ans prefix_sum = [0] * n prefix_sum[0] = Pair(nums[0], 0) for i in range(1, n): prefix_sum[i] = Pair(prefix_sum[i - 1].sum + nums[i], i) # since the closest sum occurred when the sum is closest # so we can simply calculate the difference # between every two adjacent indexes prefix_sum.sort(key=lambda p: p.sum) closest_sum = float('inf') tmp_sum = 0 for i in range(1, n): # calculate all the closest sum occurred in two adjacent indexes tmp_sum = prefix_sum[i].sum - prefix_sum[i - 1].sum # keep finding the minimum closest sum if tmp_sum >= closest_sum: continue closest_sum = tmp_sum ans = sorted([prefix_sum[i].index, prefix_sum[i - 1].index]) # sum(nums[1:3]) == sum(nums[0:3]) - sum(nums[0:0]) # so start need `+1` ans[0] += 1 return ans ================================================ FILE: lintcode/13_strstr.py ================================================ class Solution: def strStr(self, haystack, needle): """ :type haystack: str :type needle: str :rtype: int """ NOT_FOUND = -1 if haystack is None or needle is None: return NOT_FOUND if haystack == needle: return 0 m, n = len(haystack), len(needle) for i in range(m - n + 1): if haystack[i:i + n] == needle: return i return NOT_FOUND ================================================ FILE: lintcode/141_sqrtx.py ================================================ class Solution: def sqrt(self, x): """ :type x: int :rtype: int """ if not x or x <= 1: return x left, right = 0, x while left + 1 < right: mid = (left + right) // 2 square = mid * mid if square == x: return mid if square < x: left = mid else: right = mid return left ================================================ FILE: lintcode/142_o1_check_power_of_2.py ================================================ class Solution: """ @param: n: An integer @return: True or false """ def checkPowerOf2(self, n): if not n or n <= 0: return False return n & (n - 1) == 0 ================================================ FILE: lintcode/143_sort_colors_ii.py ================================================ """ Rainbow Sort time: O(nlogk) """ class Solution: """ @param: colors: A list of integer @param: k: An integer @return: nothing """ def sortColors2(self, colors, k): if not colors or not k: return self.rainbow_sort(colors, 0, len(colors) - 1, 1, k) def rainbow_sort(self, colors, start, end, color_from, color_to): """ like quick sort """ if color_from >= color_to: return if start >= end: return left, right = start, end color_mid = (color_from + color_to) // 2 while left <= right: while left <= right and colors[left] <= color_mid: left += 1 while left <= right and colors[right] > color_mid: right -= 1 if left <= right: colors[left], colors[right] = colors[right], colors[left] left += 1 right -= 1 self.rainbow_sort(colors, start, right, color_from, color_mid) self.rainbow_sort(colors, left, end, color_mid + 1, color_to) """ TLE Extend from sort color time: O(nk) """ class Solution: """ @param: colors: A list of integer @param: k: An integer @return: nothing """ def sortColors2(self, colors, k): """ 1. ensure the `min_color` in the `left_scope`, and the `max_color` in the `right_scope` in every iteration 2. keep sorting the `mid_scope` to pick min/max color to the left/right scope """ count = 0 n = len(colors) left, right = 0, n - 1 i = _min = _max = 0 while count < k: _min = _max = colors[left] for i in range(left + 1, right + 1): if colors[i] < _min: _min = colors[i] if colors[i] > _max: _max = colors[i] i = left while i <= right: if colors[i] == _min: colors[left], colors[i] = colors[i], colors[left] left += 1 i += 1 elif _min < colors[i] < _max: # leave it to # the next iteration of `while count < k` to sort i += 1 else: colors[i], colors[right] = colors[right], colors[i] right -= 1 count += 2 ================================================ FILE: lintcode/148_sort_colors.py ================================================ class Solution: """ @param: A: A list of integer which is 0, 1 or 2 @return: nothing """ def sortColors(self, A): if not A: return left, right = 0, len(A) - 1 i = 0 while i <= right: if A[i] == 0: A[left], A[i] = A[i], A[left] left += 1 i += 1 elif A[i] == 1: """ temply ignore it it will be swapped if there is `0` later """ i += 1 else: """ cannot `i += 1` since the swapped value still need to check """ A[right], A[i] = A[i], A[right] right -= 1 ================================================ FILE: lintcode/149_best_time_to_buy_and_sell_stock.py ================================================ class Solution: """ @param: P: Given an integer array @return: Maximum profit """ def maxProfit(self, P): ans = 0 if not P: return ans Pmin = P[0] for i in range(1, len(P)): if P[i] - Pmin > ans: ans = P[i] - Pmin if P[i] < Pmin: Pmin = P[i] return ans ================================================ FILE: lintcode/14_first_position_of_target.py ================================================ class Solution: # @param A: The integer array # @param target: Target number to find # @return the first position of target in A, position start from 0 def binarySearch(self, A, target): if not A: return -1 left, mid, right = 0, 0, len(A) - 1 while left + 1 < right: mid = left + (right - left) // 2 if A[mid] < target: left = mid else: right = mid if A[left] == target: return left elif A[right] == target: return right return -1 ================================================ FILE: lintcode/150_best_time_to_buy_and_sell_stock_ii.py ================================================ class Solution: """ @param: P: Given an integer array @return: Maximum profit """ def maxProfit(self, P): ans = 0 if not P: return ans for i in range(1, len(P)): if P[i] > P[i - 1]: ans += P[i] - P[i - 1] return ans ================================================ FILE: lintcode/151_best_time_to_buy_and_sell_stock_iii.py ================================================ class Solution: """ @param: P: Given an integer array @return: Maximum profit """ def maxProfit(self, P): if not P: return 0 K = 2 STAGE = 2 * K + 1 """ `dp[i][j]` means the `i`th day at the `j`th stage stage 0: before the first buying stage 1: holding the first stock stage 2: after the first selling, before the second buying stage 3: holding the second stock stage 4: after the second selling note that, `dp[i][0]` means always stay in stage 0, so its never going to be profitable """ dp = [[0] * STAGE for _ in range(2)] i = j = prev = curr = profit = 0 for i in range(1, len(P)): prev = curr curr = 1 - curr profit = P[i] - P[i - 1] for j in range(1, STAGE, 2): """ in stage 1 and 3, holding a stock profit comes from: 1. still holding a stock yesterday, and gains profit (may be negative) today 2. holding no any stock yesterday, just makes a buying today, so no profit choose the maximum """ dp[curr][j] = max(dp[prev][j] + profit, dp[prev][j - 1]) for j in range(2, STAGE, 2): """ in stage 2 and 4, holding no any stock profit comes from: 1. still holding no any stock yesterday, just makes a buying today, so no profit 2. holding a stock yesterday, and gains profit (may be negative) today choose the maximum """ dp[curr][j] = max(dp[prev][j], dp[prev][j - 1] + profit) return max(dp[curr]) ================================================ FILE: lintcode/153_combination_sum_ii.py ================================================ class Solution: """ @param: A: Given the candidate numbers @param: target: Given the target number @return: All the combinations that sum to target """ def combinationSum2(self, A, target): ans = [] if not A: return ans A.sort() self.dfs(A, 0, target, ans, []) return ans def dfs(self, A, start, remaining, ans, path): if remaining == 0: ans.append(path[:]) return for i in range(start, len(A)): if remaining < A[i]: return # to prevent [1', 2, 5] and [1", 2, 5] # appear in result at same time if i > start and A[i] == A[i - 1]: continue path.append(A[i]) self.dfs(A, i + 1, remaining - A[i], ans, path) path.pop() ================================================ FILE: lintcode/154_regular_expression_matching.py ================================================ """ Main Concept: end by '*' and has no matched case 1-1: `P[j-2:j]` is `c*` and have no matched in `S` => `j-2` in `dp[i][j-2]` means ignored `c` and `*` end by `*` and is the last matched in `*` case 1-2: `P[j-2:j]` is `.*` and `.` always matched `S[i-1]` case 1-3: `P[j-2:j]` is `a*` and `a` == `P[j-2]` == `S[i-1]` => `i-1` in `dp[i-1][j]` means to check the `?` below '...a?a' \| '...xa*' case 2: `P[j-1]` is `.` and `.` always matched `S[i-1]` case 3: `P[j-1]` is `a` and `a` == `P[j-1]` == `S[i-1]` => `-1` in `dp[i-1][j-1]` means previous char """ class Solution: def isMatch(self, s, p): """ :type s: str :type p: str :rtype: bool """ if s == p == '': return True m, n = len(s), len(p) MULTI = '*' ANY = '.' """ `dp[i][j]` means the substr end at `s[i - 1]` was matched by the substr end at `p[j - 1]` """ dp = [[False] * (n + 1) for _ in range(m + 1)] dp[0][0] = True # dp[i][0] = False # i = 1 -> m + 1 # dp[0][j] -> ?, need to check for i in range(m + 1): for j in range(1, n + 1): if i > 0 and p[j - 1] == s[i - 1] and dp[i - 1][j - 1]: dp[i][j] = True elif i > 0 and p[j - 1] == ANY and dp[i - 1][j - 1]: dp[i][j] = True elif j > 1 and p[j - 1] == MULTI: if dp[i][j - 2]: dp[i][j] = True elif i > 0 and p[j - 2] == s[i - 1] and dp[i - 1][j]: dp[i][j] = True elif i > 0 and p[j - 2] == ANY and dp[i - 1][j]: dp[i][j] = True return dp[m][n] ================================================ FILE: lintcode/155_minimum_depth_of_binary_tree.py ================================================ """ Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ class Solution: """ @param: root: The root of binary tree @return: An integer """ def minDepth(self, root): ans = 0 if not root: return ans queue = [root] while queue: _queue = [] ans += 1 for node in queue: if not node.left and not node.right: return ans if node.left: _queue.append(node.left) if node.right: _queue.append(node.right) queue = _queue return ans class Solution: """ @param: root: The root of binary tree @return: An integer """ def minDepth(self, root): if not root: return 0 if not root.left and not root.right: return 1 left = self.minDepth(root.left) right = self.minDepth(root.right) if left == 0: return right + 1 if right == 0: return left + 1 return min(left, right) + 1 ================================================ FILE: lintcode/156_merge_intervals.py ================================================ """ Definition of Interval. class Interval(object): def __init__(self, start, end): self.start = start self.end = end """ class Solution: def merge(self, intvs): """ :type intvs: List[Interval] :rtype: List[Interval] """ ans = [] if not intvs: return ans intvs.sort(key=lambda intv: (intv.start, intv.end)) for intv in intvs: if not ans or intv.start > ans[-1].end: ans.append(intv) elif intv.end > ans[-1].end: ans[-1].end = intv.end return ans ================================================ FILE: lintcode/158_two_strings_are_anagrams.py ================================================ class Solution: """ @param s: The first string @param b: The second string @return true or false """ def anagram(self, s, t): if s == '' and t == '': return True if not s or not t: return False s = sorted(s) t = sorted(t) return s == t ================================================ FILE: lintcode/159_find_minimum_in_rotated_sorted_array.py ================================================ class Solution: """ @param: nums: a rotated sorted array @return: the minimum number in the array """ def findMin(self, nums): if not nums: return -1 l, m, r = 0, 0, len(nums) - 1 """ since the children between `nums[0:maximum]` are all great than `nums[minimum:-1]` so we can pick the last child, and do the binary searching, if: 1. child in `nums[0:maximum]`, then the left boundary will continue to move to the maximum 2. child in `nums[minimum:-1]`, then the right boundary will to the minimum """ last = nums[-1] while l + 1 < r: m = l + (r - l) // 2 if nums[m] > last: l = m else: r = m return min(nums[l], nums[r]) ================================================ FILE: lintcode/15_permutations.py ================================================ class Solution: def permute(self, nums): """ :type nums: list[int] :rtype: list[list[int]] """ if not nums: return [[]] ans = [] nums.sort() self.dfs(nums, ans, []) return ans def dfs(self, nums, ans, path): if not nums: ans.append(path[:]) return for i in range(len(nums)): path.append(nums[i]) self.dfs(nums[:i] + nums[i + 1:], ans, path) path.pop() ================================================ FILE: lintcode/160_find_minimum_in_rotated_sorted_array_ii.py ================================================ """ Iteration """ class Solution: """ @param: A: a rotated sorted array @return: the minimum number in the array """ def findMin(self, A): if not A: return -1 _min = A[0] for i in range(1, len(A)): if A[i] < _min: _min = A[i] break return _min """ Binary Searching """ class Solution: """ @param: A: a rotated sorted array @return: the minimum number in the array """ def findMin(self, A): """ all chilren before the pivot are great than or equal the child at end all chilren after the pivot are less than or equal the child at end """ if not A: return -1 left, right = 0, len(A) - 1 while left + 1 < right: mid = (left + right) // 2 if A[mid] == A[right]: # means it's ok to remove the end child right -= 1 elif A[mid] < A[right]: # mid at the right side of pivot right = mid else: # mid at the left side of pivot left = mid return A[left] if A[left] < A[right] else A[right] ================================================ FILE: lintcode/165_merge_two_sorted_lists.py ================================================ # Definition for singly-linked list. # class ListNode: # def __init__(self, x): # self.val = x # self.next = None class Solution: def mergeTwoLists(self, a, b): """ :type a: ListNode :type b: ListNode :rtype: ListNode """ dummy = tail = ListNode(-1) while a and b: if a.val < b.val: tail.next = ListNode(a.val) a = a.next else: tail.next = ListNode(b.val) b = b.next tail = tail.next while a: tail.next = ListNode(a.val) a = a.next tail = tail.next while b: tail.next = ListNode(b.val) b = b.next tail = tail.next return dummy.next ================================================ FILE: lintcode/167_add_two_numbers.py ================================================ """ Definition for singly-linked list. class ListNode: def __init__(self, x): self.val = x self.next = None """ class Solution: def addLists(self, A, B): """ :type A: ListNode :type B: ListNode :rtype: ListNode """ dummy = tail = ListNode(-1) carry = 0 while A and B: carry += A.val + B.val tail.next = ListNode(carry % 10) carry //= 10 tail = tail.next A = A.next B = B.next while A: carry += A.val tail.next = ListNode(carry % 10) carry //= 10 tail = tail.next A = A.next while B: carry += B.val tail.next = ListNode(carry % 10) carry //= 10 tail = tail.next B = B.next if carry: tail.next = ListNode(carry) return dummy.next ================================================ FILE: lintcode/168_burst_balloons.py ================================================ class Solution: """ @param: V: A list of integer @return: An integer, maximum coins """ def maxCoins(self, V): if not V: return 0 """ the value of last balloons is `1 * v * 1` """ V = [1] + V + [1] n = len(V) """ `dp[i][j]` means the maximum value when all the balloons in [i+1, j-1] was bursted """ dp = [[0] * n for _ in range(n)] # pi = [[0] * n for _ in range(n)] for i in range(n - 1 - 2, -1, -1): for j in range(i + 2, n): """ leave last balloon `k` to burst `i + 1 <= k <= j - 1` """ for k in range(i + 1, j): dp[i][j] = max( dp[i][j], dp[i][k] + dp[k][j] + V[i] * V[k] * V[j] ) # if dp[i][j] == dp[i][k] + dp[k][j] + V[i] * V[k] * V[j]: # pi[i][j] = k # self.print_paths(0, n - 1, V, pi) return dp[0][n - 1] # def print_paths(self, i, j, V, pi): # if i + 1 == j: # return # self.print_paths(i, pi[i][j], V, pi) # self.print_paths(pi[i][j], j, V, pi) # print("burst {vk}, get coins {vi} * {vk} * {vj} = {vsum}".format( # vi=V[i], # vk=V[pi[i][j]], # vj=V[j], # vsum=V[i] * V[pi[i][j]] * V[j] # )) ================================================ FILE: lintcode/16_permutations_ii.py ================================================ class Solution: """ dfs with ignoring self and same num """ def permuteUnique(self, nums): """ :type nums: List[int] :rtype: List[List[int]] """ if not nums: return [[]] ans = [] nums.sort() self.dfs(nums, ans, []) return ans def dfs(self, nums, ans, path): if not nums: ans.append(path[:]) return for i in range(len(nums)): """ ignore same num """ if i > 0 and nums[i] == nums[i - 1]: continue """ ignore self """ path.append(nums[i]) self.dfs(nums[:i] + nums[i + 1:], ans, path) path.pop() class Solution: """ dfs with visited indices """ def permuteUnique(self, nums): """ :type nums: List[int] :rtype: List[List[int]] """ if not nums: return [[]] ans = [] visited = [False] * len(nums) nums.sort() self.dfs(nums, visited, ans, []) return ans def dfs(self, nums, visited, ans, path): if len(path) == len(nums): ans.append(path[:]) return for i in range(len(nums)): if visited[i]: continue """ example: [0, 3, 3', 3"] if current iteration is `3"` we need to ensure `3`, `3'` is picked otherwise repeated result will be included """ if i > 0 and not visited[i - 1] and nums[i] == nums[i - 1]: continue visited[i] = True path.append(nums[i]) self.dfs(nums, visited, ans, path) visited[i] = False path.pop() ================================================ FILE: lintcode/171_anagrams.py ================================================ class Solution: """ @param: S: A list of strings @return: A list of strings """ def anagrams(self, S): ans = [] if not S: return ans D = {} for s in S: _s = ''.join(sorted(s)) if _s not in D: D[_s] = [] D[_s].append(s) for k, S in D.items(): if len(S) > 1: ans.extend(S) return ans ================================================ FILE: lintcode/175_invert_binary_tree.py ================================================ """ Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ class Solution: """ @param: root: a TreeNode, the root of the binary tree @return: nothing """ def invertBinaryTree(self, root): self.divide_conquer(root) def divide_conquer(self, node): if not node: return self.divide_conquer(node.left) self.divide_conquer(node.right) node.left, node.right = node.right, node.left ================================================ FILE: lintcode/178_graph_valid_tree.py ================================================ class Solution: def validTree(self, n, edges): """ :type n: int :type edges: list[int] :rtype: bool """ if len(edges) != n - 1: return False nodes = [i for i in range(n)] for a, b in edges: _a = self.find(nodes, a) _b = self.find(nodes, b) if _a is _b: return False nodes[_a] = _b return True def find(self, nodes, a): if nodes[a] is a: return a nodes[a] = self.find(nodes, nodes[a]) return nodes[a] ================================================ FILE: lintcode/17_subsets.py ================================================ """ DFS """ class Solution: """ @param: A: A set of numbers @return: A list of lists """ def subsets(self, A): if not A: return [[]] ans = [] self.dfs(sorted(A), 0, ans, []) return ans def dfs(self, A, start, ans, subset): ans.append(subset[:]) if start >= len(A): return for i in range(start, len(A)): self.dfs(A, i + 1, ans, subset + [A[i]]) """ Bit Manipulation """ class Solution: """ @param: A: A set of numbers @return: A list of lists """ def subsets(self, A): if not A: return [[]] ans = [] n = len(A) A.sort() for i in range(1 << n): subset = [] for j in range(n): """ check `j`th digit in `bin(i)` example: i == 011 j == 0 => 1 << 0 == 001 => 011 & 001 == 1 j == 1 => 1 << 1 == 010 => 011 & 010 == 1 j == 2 => 1 << 2 == 100 => 011 & 100 == 0 """ if i & (1 << j): subset.append(A[j]) ans.append(subset) return ans ================================================ FILE: lintcode/183_wood_cut.py ================================================ class Solution: """ @param: L: Given n pieces of wood with length L[i] @param: k: An integer @return: The maximum length of the small pieces """ def woodCut(self, L, k): """ Assuming the `m` is the maximum length len | ... m-2 m-1 m m+1 m+2 ... check | T T T T F F F * check: is it ok to cut into at least `k` pieces """ if not L or not k: return 0 left = 1 total_len = right = L[0] for i in range(1, len(L)): if L[i] > right: right = L[i] total_len += L[i] if total_len < k: return 0 while left + 1 < right: mid = (left + right) // 2 if self.check_if_possible(L, mid, k): left = mid else: right = mid return right if self.check_if_possible(L, right, k) else left def check_if_possible(self, L, size, max_pieces): pieces = 0 for i in range(len(L)): pieces += L[i] // size if pieces >= max_pieces: return True return False ================================================ FILE: lintcode/18_subsets_ii.py ================================================ """ DFS """ class Solution: """ @param: A: A set of numbers. @return: A list of lists. All valid subsets. """ def subsetsWithDup(self, A): if not A: return [[]] ans = [] self.dfs(sorted(A), 0, ans, []) return ans def dfs(self, A, start, ans, subset): ans.append(subset[:]) if start >= len(A): return for i in range(start, len(A)): if i - 1 >= start and A[i] == A[i - 1]: continue self.dfs(A, i + 1, ans, subset + [A[i]]) """ backtracking if using same list """ # subset.append(A[i]) # self.dfs(A, i + 1, ans, subset) # subset.pop() ================================================ FILE: lintcode/190_next_permutation_ii.py ================================================ class Solution: def nextPermutation(self, nums): """ :type nums: list[int] :rtype: list[int] """ if not nums or len(nums) < 2: return nums n = len(nums) i = n - 2 while i >= 0 and nums[i] >= nums[i + 1]: i -= 1 if i >= 0: j = n - 1 while i < j and nums[i] >= nums[j]: j -= 1 nums[i], nums[j] = nums[j], nums[i] i = i + 1 j = n - 1 while i < j: nums[i], nums[j] = nums[j], nums[i] i += 1 j -= 1 ================================================ FILE: lintcode/191_maximum_product_subarray.py ================================================ """ since the fact: the minimum negative number * -1 -> the maximum the maximum positive number -> the maximum so we need record the minimum and the maximum number for each child in nums """ class Solution: """ @param: A: An array of integers @return: An integer """ def maxProduct(self, A): if not A: return 0 ans = Pmin = Pmax = A[0] for i in range(1, len(A)): """ adding `A[i]` to reset `min` and `max` if its so lowest or highest """ C = (A[i], Pmin * A[i], Pmax * A[i]) Pmin, Pmax = min(C), max(C) if Pmax > ans: ans = Pmax return ans ================================================ FILE: lintcode/192_wildcard_matching.py ================================================ """ Main Concept: case 1-1: `dp[i-1][j]` means `*` may start to matched new char => `i-1` in `dp[i-1][j]` means to check the `?` below '...a?a' \| '...xa*' case 1-2: `dp[i][j-1]` means `*` continue to matched same char => `j-1` in `dp[i][j-1]` means to check the `?` below '...aa?' / '...xa*' case 2: `P[j-1]` is `.` and `.` always matched `S[i-1]` case 3: `P[j-1]` is `a` and `a` == `P[j-1]` == `S[i-1]` => `-1` in `dp[i-1][j-1]` means previous char """ class Solution: def isMatch(self, s, p): """ :type s: str, target string :type p: str, regex :rtype: bool """ if s is None or p is None: return False if s == '' and p == '': return True ANY = '?' ANY_MULTI = '*' m, n = len(s), len(p) """ `dp[i][j]` means the substr end at `S[i - 1]` was matched by the substr end at `P[j - 1]` """ dp = [[False] * (n + 1) for _ in range(m + 1)] dp[0][0] = True # dp[i][0] = False # dp[0][j] -> need to check for i in range(m + 1): for j in range(1, n + 1): if p[j - 1] == ANY_MULTI: dp[i][j] = dp[i - 1][j] or dp[i][j - 1] elif p[j - 1] == ANY and dp[i - 1][j - 1]: dp[i][j] = True elif p[j - 1] == s[i - 1] and dp[i - 1][j - 1]: dp[i][j] = True return dp[m][n] ================================================ FILE: lintcode/196_find_the_missing_number.py ================================================ class Solution: def findMissing(self, nums): """ :type nums: List[int] :rtype: int """ if not nums: return 0 ans = n = len(nums) for i in range(n): ans ^= i ^ nums[i] return ans class Solution: def findMissing(self, nums): """ :type nums: List[int] :rtype: int """ if not nums: return 0 nums.sort() for i in range(len(nums)): if i != nums[i]: return i return i + 1 ================================================ FILE: lintcode/197_permutation_index.py ================================================ """ https://segmentfault.com/a/1190000004683277 in the origin/unsorted `A`, if there are `cnt` child after and less than `A[i]`, meaning there are `cnt * (n - i - 1)!` permutations before `A[i]`. """ class Solution: """ @param: A: An array of integers @return: A long integer """ def permutationIndex(self, A): ans = 1 if not A: return ans n = len(A) factorial = 1 for i in range(n - 1, -1, -1): cnt = 0 for j in range(i + 1, n): if A[i] > A[j]: cnt += 1 """ use the `factorial` from previous iteration `i + 1` that is, `(n - i - 1)! = (n - (i + 1))!` """ ans += cnt * factorial factorial *= n - i return ans ================================================ FILE: lintcode/198_permutation_index_ii.py ================================================ """ https://segmentfault.com/a/1190000004683277 if there are `dups`, dividing `factorial` with `dup_fact` e.g., 3 `A[i]`s and 4 `A[j]`s in `A` `dup_fact = 3! * 4!` """ class Solution: """ @param: A: An array of integers @return: A long integer """ def permutationIndexII(self, A): ans = 1 if not A: return ans n = len(A) factorial = dup_fact = 1 dups = {} for i in range(n - 1, -1, -1): dups[A[i]] = dups.get(A[i], 0) + 1 dup_fact *= dups[A[i]] cnt = 0 for j in range(i + 1, n): if A[i] > A[j]: cnt += 1 ans += cnt * factorial // dup_fact factorial *= n - i return ans ================================================ FILE: lintcode/1_a_b_problem.py ================================================ class Solution: """ @param: a: An integer @param: b: An integer @return: The sum of a and b """ def aplusb(self, a, b): if not a: return b if not b: return a INT_RANGE = 0xFFFFFFFF while b != 0: a, b = a ^ b, (a & b) << 1 a &= INT_RANGE return a if a >> 31 <= 0 else a ^ ~INT_RANGE ================================================ FILE: lintcode/204_singleton.py ================================================ class Solution: instance = None # @return: The same instance of this class every time @classmethod def getInstance(cls): if not cls.instance: cls.instance = Solution() return cls.instance ================================================ FILE: lintcode/211_string_permutation.py ================================================ class Solution: """ @param: A: a string @param: B: a string @return: a boolean """ def Permutation(self, A, B): if A is '' and B is '': return True if not A or not B: return False cnts = {} for char in A: cnts[char] = cnts.get(char, 0) + 1 for char in B: if char not in cnts or cnts[char] == 0: return False cnts[char] -= 1 for cnt in cnts.values(): if cnt != 0: return False return True ================================================ FILE: lintcode/215_rate_limiter.py ================================================ import collections class Solution: """ maintain a list to record the timestamps for every event and if a query coming => we need ensure there is no more `freq` logs between `begin_time` and `timestamp`(current) 1. split rate 2. cal begin time 3. do binary searching to find the closest index of `begin time` 4. count the child between res of (3) to the end of list 5. if cnt >= freq => is limited """ def __init__(self): self.times = { 's': 1, 'm': 60, 'h': 3600, 'd': 86400, } self.logs = collections.defaultdict(list) def isRatelimited(self, timestamp, event, rate, increment): """ :type timestamp: int :type event: str :type rate: str :type increment: bool :rtype: bool """ if '/' not in rate: return False freq, t = rate.split('/') freq = int(freq) begin_time = timestamp - self.times.get(t, 1) + 1 is_limited = self.check_limited(event, freq, begin_time) if increment and not is_limited: self.logs[event].append(timestamp) return is_limited def check_limited(self, event, freq, begin_time): logs = self.logs[event] if not logs or logs[-1] < begin_time: # if freq is 0 => is limited return freq == 0 left, right = 0, len(logs) - 1 while left + 1 < right: mid = (left + right) // 2 if logs[mid] < begin_time: left = mid else: right = mid mid = left if logs[left] >= begin_time else right return len(logs) - mid >= freq ================================================ FILE: lintcode/221_add_two_numbers_ii.py ================================================ """ Definition for singly-linked list. class ListNode: def __init__(self, x): self.val = x self.next = None """ class Solution: def addLists2(self, a, b): """ :type a: ListNode :type b: ListNode :rtype: ListNode """ if not a and not b: return if not a: return b if not b: return a a = self.rev_list(a) b = self.rev_list(b) dummy = tail = ListNode(0) carry = 0 while a and b: carry += a.val + b.val tail.next = ListNode(carry % 10) carry //= 10 a = a.next b = b.next tail = tail.next while a: carry += a.val tail.next = ListNode(carry % 10) carry //= 10 a = a.next tail = tail.next while b: carry += b.val tail.next = ListNode(carry % 10) carry //= 10 b = b.next tail = tail.next if carry: tail.next = ListNode(carry) return self.rev_list(dummy.next) def rev_list(self, head): pre = nxt = None while head: nxt = head.next head.next = pre pre = head head = nxt return pre ================================================ FILE: lintcode/231_typeahead.py ================================================ class Typeahead: """ @param: dict: A dictionary of words dict """ def __init__(self, dict): self.map = {} n = 0 for key in dict: n = len(key) for l in range(n): for r in range(l + 1, n + 1): substr = key[l:r] if substr not in self.map: self.map[substr] = [key] elif self.map[substr][-1] != key: self.map[substr].append(key) """ @param: str: a string @return: a list of words """ def search(self, str): if str in self.map: return self.map[str] return [] # Solution2 ''' This solution does not meet the demand. Since the description said 'return all words that contains the string as a substring.' But its a good solution for the auto-completion ''' class Trie: def __init__(self): self.root = self.new_node() def new_node(self): return { 'result': [], 'children': {} } def put(self, key): if not key: return for word in key.split(): self._put(word, key) def _put(self, word, key): parent = self.root for char in word.lower(): if char not in parent['children']: parent['children'][char] = self.new_node() parent = parent['children'][char] parent['result'].append(key) def search(self, key): if not key: return [] parent = self.root for char in key.lower(): if char not in parent['children']: return [] parent = parent['children'][char] return parent['result'] # To support search with 2+ word # def search(self, key): # if not key: # return [] # result = [] # for word in key.split(): # result += self._search(word) # return result # def _search(self, word): # parent = self.root # for char in word.lower(): # if char not in parent['children']: # return [] # parent = parent['children'][char] # return parent['result'] class Typeahead: """ @param: dict: A dictionary of words dict """ def __init__(self, dict): self.trie = Trie() for word in dict: self.trie.put(word) """ @param: str: a string @return: a list of words """ def search(self, str): return self.trie.search(str) ================================================ FILE: lintcode/232_tiny_url.py ================================================ import random class TinyUrl: def __init__(self): self.chars = [str(i) for i in range(10)] self.chars.extend(chr(i) for i in range(ord('a'), ord('z') + 1)) self.chars.extend(chr(i) for i in range(ord('A'), ord('Z') + 1)) self.host = 'http://tiny.url/' self.size = 6 self.lg2st = {} self.st2lg = {} def longToShort(self, url): """ :type url: str :rtype: str """ if not url: return 'error' if url in self.lg2st: return self.get_tiny_url(self.lg2st[url]) key = self.get_hash_key(self.size) while key in self.st2lg: key = self.get_hash_key(self.size) self.lg2st[url] = key self.st2lg[key] = url return self.get_tiny_url(key) def shortToLong(self, url): """ :type url: str :rtype: str """ if not url: return 'error' key = url.replace(self.host, '') if key in self.st2lg: return self.st2lg[key] return 'error' def get_tiny_url(self, hash_key): return '{}{}'.format(self.host, hash_key) def get_hash_key(self, size): return ''.join( random.choice(self.chars) for _ in range(size) ) ================================================ FILE: lintcode/234_webpage_crawler.py ================================================ from threading import Thread from Queue import Queue from urlparse import urlparse queue = Queue() results = {} class CrawlerThread(Thread): def run(self): global queue, results while True: url = queue.get() if url not in results \ and urlparse(url).hostname.endswith("wikipedia.org"): results[url] = True urls = HtmlHelper.parseUrls(url) for url in urls: queue.put(url) queue.task_done() #class HtmlHelper: # @classmethod # def parseUrls(cls, url) # # Get all urls from a webpage of given url. class Solution: # @param {string} url a url of root page # @return {string[]} all urls def crawler(self, url): # Write your code here global queue, results thread_pools = [] for i in xrange(10): thread_pools.append(CrawlerThread()) thread_pools[i].setDaemon(True) thread_pools[i].start() queue.put(url) queue.join() rt = [] for key, value in results.items(): rt.append(key) return rt ================================================ FILE: lintcode/236_swap_bits.py ================================================ class Solution: """ @param: x: An integer @return: An integer """ def swapOddEvenBits(self, x): """ 0x55555555 == 01010101010101010101010101010101 (2) 0xAAAAAAAA == 10101010101010101010101010101010 (2) """ return ( ((x & 0xAAAAAAAA) >> 1) | ((x & 0x55555555) << 1) ) ================================================ FILE: lintcode/242_convert_binary_tree_to_linked_lists_by_depth.py ================================================ """ Definition of TreeNode: class TreeNode: def __init__(self, val): this.val = val this.left, this.right = None, None Definition for singly-linked list. class ListNode: def __init__(self, x): self.val = x self.next = None """ class Solution: # @param {TreeNode} root the root of binary tree # @return {ListNode[]} a lists of linked list def binaryTreeToLists(self, root): ans = [] if not root: return ans queue = [root] while queue: _queue = [] dummy = tail = ListNode(-1) for node in queue: tail.next = ListNode(node.val) tail = tail.next if node.left: _queue.append(node.left) if node.right: _queue.append(node.right) queue = _queue ans.append(dummy.next) return ans ================================================ FILE: lintcode/243_amicable_pair ================================================ class Solution: """ @param: n: An integer @return: all amicable pairs """ def amicablePair(self, n): ans = [] if not n or n < 2: return ans for a in range(2, n + 1): b = self.get_factor_sum(a) if a < b <= n and self.get_factor_sum(b) == a: ans.append([a, b]) return ans def get_factor_sum(self, a): """ if `b` is a factor of `a` => `b` and `a // b` are both factor of `a` => only needs to check til `sqrt(a)` """ _sum = 1 _a = int(math.sqrt(a)) for b in range(2, _a): if a % b == 0: _sum += b + a // b if _a * _a == a: _sum += _a return _sum ================================================ FILE: lintcode/245_subtree.py ================================================ """ Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ class Solution: """ @param: A: The roots of binary tree A. @param: B: The roots of binary tree B. @return: True if B is a subtree of A, or false. """ def isSubtree(self, A, B): if not B: # Empty tree is also treated as subtree in Lintcode return True if not A: return False return ( self.isEqual(A, B) or self.isSubtree(A.left, B) or self.isSubtree(A.right, B) ) def isEqual(self, A, B): if not A and not B: return True if not A or not B: return False return ( A.val == B.val and self.isEqual(A.left, B.left) and self.isEqual(A.right, B.right) ) ================================================ FILE: lintcode/246_binary_tree_path_sum_ii.py ================================================ """ Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ class Solution: """ @param: root: the root of binary tree @param: target: An integer @return: all valid paths """ def binaryTreePathSum2(self, root, target): ans = [] self.dfs(root, target, ans, []) return ans def dfs(self, node, target, ans, path): if not node: return path.append(node.val) remaining = target for i in range(len(path) - 1, -1, -1): remaining -= path[i] if remaining == 0: ans.append(path[i:]) self.dfs(node.left, target, ans, path) self.dfs(node.right, target, ans, path) path.pop() ================================================ FILE: lintcode/24_lfu_cache.py ================================================ """ Main Concept: Dm <-> 2 <-> 3 <-> 8 <-> dm |<- freq_list (dll) | | | a c d |<- cache_list (dll) & & b e 1. if cache is updated (set/get) => freq += 1 => move to the end of the cache_list in new freq_list 2. if cache is full => evict the most top-left in cache first, that is `a` in above diagram => add the new cache to the end of the cache_list in freq `0` """ class LFUCache: def __init__(self, capacity): """ :type capacity: int """ self.cap = capacity self.nodes = {} self.D = FreqNode(-1) self.d = FreqNode(-1) self.D.nxt = self.d self.d.pre = self.D def get(self, key): """ :type key: int :rtype: int """ if key not in self.nodes: return -1 self._update(key) return self.nodes[key].val def set(self, key, val): """ :type key: int :type val: int :rtype: void """ if self.cap <= 0: return if key in self.nodes: self._update(key, val) return while len(self.nodes) >= self.cap: self._evict() self._add(key, val) def _evict(self): freq_head = self.D.nxt cache_node = freq_head.pop_head() del self.nodes[cache_node.key] if freq_head.is_empty(): freq_head.unlink() def _update(self, key, val=None): cache_node = self.nodes[key] if val: cache_node.val = val from_freq = cache_node.freq_node to_freq = None if from_freq.nxt and from_freq.nxt.freq == from_freq.freq + 1: to_freq = from_freq.nxt else: to_freq = FreqNode(from_freq.freq + 1) from_freq.after(to_freq) cache_node.unlink() to_freq.append_tail(cache_node) if from_freq.is_empty(): from_freq.unlink() def _add(self, key, val): cache_node = CacheNode(key, val) self.nodes[key] = cache_node freq_head = self.D.nxt if freq_head and freq_head.freq == 0: freq_head.append_tail(cache_node) return freq_head = FreqNode(0) freq_head.append_tail(cache_node) self.D.after(freq_head) class CacheNode: def __init__(self, key, val=None, freq_node=None, pre=None, nxt=None): self.key = key self.val = val self.freq_node = freq_node self.pre = pre self.nxt = nxt # to change self in cache nodes def unlink(self): self.pre.nxt = self.nxt self.nxt.pre = self.pre self.freq_node = self.pre = self.nxt = None class FreqNode: def __init__(self, freq, pre=None, nxt=None): self.freq = freq self.pre = pre self.nxt = nxt self.D = CacheNode(-1) self.d = CacheNode(-1) self.D.nxt = self.d self.d.pre = self.D # to change self in freq nodes def unlink(self): self.pre.nxt = self.nxt self.nxt.pre = self.pre self.pre = self.nxt = self.D = self.d = None # to change self in freq nodes def after(self, freq_node): freq_node.pre = self freq_node.nxt = self.nxt self.nxt.pre = freq_node self.nxt = freq_node # to manage cache nodes def is_empty(self): return self.D.nxt is self.d # to manage cache nodes def pop_head(self): if self.is_empty(): return head = self.D.nxt head.unlink() return head # to manage cache nodes def append_tail(self, cache_node): cache_node.freq_node = self cache_node.pre = self.d.pre cache_node.nxt = self.d self.d.pre.nxt = cache_node self.d.pre = cache_node ================================================ FILE: lintcode/254_drop_eggs.py ================================================ """ REF: http://blog.csdn.net/shaya118/article/details/40823225 Main Concept: assuming the minimum times to drop is `x` according to REF, the relation of `x` is below | | x |....| . | |....| x + 1 0 + 1 + 2 + ... + x => x(x + 1) / 2 >= n """ """ cumulative the sum of the triangle """ class Solution: """ @param: n: An integer @return: The sum of a and b """ def dropEggs(self, n): if n <= 0: return 0 _sum = 0 for i in range(n): _sum += i if _sum >= n: return i return n """ optimized: cumulative the sum of the triangle x(x + 1) / 2 >= n => sqrt(x(x + 1)) >= sqrt(2n) => sqrt(x(x + 1)) >= sqrt(x^2) == x >= sqrt(2n) so `x` can start from `sqrt(2n)` """ from math import sqrt class Solution: """ @param: n: An integer @return: The sum of a and b """ def dropEggs(self, n): if n <= 0: return 0 x = int(sqrt(2 * n)) while x * (x + 1) // 2 < n: x += 1 return x ================================================ FILE: lintcode/272_climbing_stairs_ii.py ================================================ """ dp: space optimized by rolling array """ class Solution: """ @param: n: An integer @return: An integer """ def climbStairs2(self, n): if not n or n <= 1: return 1 if n == 2: return 2 a, b, c = 1, 1, 2 for i in range(3, n + 1): a, b, c = b, c, a + b + c return c """ dp """ class Solution: """ @param: n: An integer @return: An integer """ def climbStairs2(self, n): if not n or n <= 1: return 1 if n == 2: return 2 dp = [0] * (n + 1) dp[0] = dp[1] = 1 dp[2] = 2 for i in range(3, n + 1): dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3] return dp[n] ================================================ FILE: lintcode/28_search_a_2d_matrix.py ================================================ class Solution: def searchMatrix(self, matrix, target): """ :type matrix: list[list[int]] :type target: int :rtype: bool """ if not matrix or not matrix[0]: return False m, n = len(matrix), len(matrix[0]) left, right = 0, m * n - 1 while left + 1 < right: mid = (left + right) // 2 x = mid // n y = mid % n if matrix[x][y] < target: left = mid elif matrix[x][y] > target: right = mid else: return True return any( matrix[mid // n][mid % n] == target for mid in (left, right) ) ================================================ FILE: lintcode/29_interleaving_string.py ================================================ class Solution: """ @param: A: A string @param: B: A string @param: C: A string @return: Determine whether C is formed by interleaving of A and B """ def isInterleave(self, A, B, C): if A is None or B is None or C is None: return False if A is '' and B is '' and C is '': return True a, b, c = len(A), len(B), len(C) if c != a + b: return False """ `dp[i][j]` means the substr end at `C[i + j - 1]` is mixed by the substr end at `A[i - 1]` and the substr end at `B[j - 1]` """ dp = [[False] * (b + 1) for _ in range(2)] prev = curr = 0 dp[curr][0] = True for j in range(1, b + 1): """ dp[0][j] = (dp[0][j - 1] and B[j - 1] == C[j - 1]) """ if not dp[curr][j - 1]: break if B[j - 1] == C[j - 1]: dp[curr][j] = True for i in range(1, a + 1): prev = curr curr = 1 - curr """ dp[i][0] = (dp[i - 1][0] and A[i - 1] == C[i - 1]) """ if dp[prev][0] and A[i - 1] == C[i - 1]: dp[curr][0] = True for j in range(1, b + 1): dp[curr][j] = False if dp[prev][j] and A[i - 1] == C[i + j - 1]: dp[curr][j] = True continue if dp[curr][j - 1] and B[j - 1] == C[i + j - 1]: dp[curr][j] = True return dp[curr][b] ================================================ FILE: lintcode/30_insert_interval.py ================================================ """ Main Concept: 1. iterate from end, find the position to insert new one 2. append new one to `intvs` 3. iterate from end, and swap `intvs[i]` and `intvs[i - 1]` til meet the position found in (1) 4. iterate from start, do merge intv Definition of Interval. class Interval(object): def __init__(self, start, end): self.start = start self.end = end """ class Solution: def insert(self, intvs, intv): """ :type intvs: list[Interval] :type intv: Interval :rtype: list[Interval] """ if not intvs and not intv: return [] if not intvs: return [intv] if not intv: return intvs ans = [] index = len(intvs) for i in range(len(intvs) - 1, -1, -1): if intvs[i].start <= intv.start: break index -= 1 intvs.append(intv) for i in range(len(intvs) - 1, index, -1): intvs[i], intvs[i - 1] = intvs[i - 1], intvs[i] for i in range(len(intvs)): # since there is one more child in intvs if ans and intvs[i].start <= ans[-1].end: ans[-1].end = max(ans[-1].end, intvs[i].end) else: ans.append(intvs[i]) return ans ================================================ FILE: lintcode/31_partition_array.py ================================================ class Solution: """ @param: A: The integer array you should partition @param: k: An integer @return: The index after partition """ def partitionArray(self, A, k): if not A: return 0 left, right = 0, len(A) - 1 while left <= right: while left <= right and A[left] < k: left += 1 while left <= right and A[right] >= k: right -= 1 if left <= right: A[left], A[right] = A[right], A[left] left += 1 right -= 1 return left ================================================ FILE: lintcode/32_minimum_window_substring.py ================================================ class Solution: def minWindow(self, s, t): """ :type s: str :type t: str :rtype: str """ if not s or not t: return '' F = {} for c in t: F[c] = F.get(c, 0) + 1 n, cnt = len(s), len(F) start = size = INFINITY = float('inf') left = right = 0 while right < n: if s[right] in F: F[s[right]] -= 1 if F[s[right]] == 0: cnt -= 1 right += 1 while cnt == 0: if s[left] in F: F[s[left]] += 1 if F[s[left]] == 1: cnt += 1 if right - left < size: size = right - left start = left left += 1 return s[start:start + size] if size < INFINITY else '' ================================================ FILE: lintcode/35_reverse_linked_list.py ================================================ """ Definition of ListNode class ListNode(object): def __init__(self, val, next=None): self.val = val self.next = next """ class Solution: """ @param: head: n @return: The new head of reversed linked list. """ def reverse(self, head): """ example: 1->2->3 r0: head: 1 pre: None nxt: None r1: head: 2 pre: 1 nxt: 2->3 r2: head: 3 pre: 2->1 nxt: 3 r3: head: None pre: 3->2->1 nxt: None """ pre = nxt = None while head: nxt = head.next # save the remaining children head.next = pre # break the link pre = head # save the new head head = nxt # pointer the old head return pre ================================================ FILE: lintcode/360_sliding_window_median.py ================================================ import heapq class HashHeapqWithLazy: def __init__(self): self.__heap = [] self.__deleted = {} self.__size = 0 def __len__(self): return self.__size def __bool__(self): return self.__size > 0 def push(self, val): heapq.heappush(self.__heap, val) self.__size += 1 def pop(self): if self._is_empty(): return self.__size -= 1 return heapq.heappop(self.__heap) def remove(self, val): if self._is_empty(): return self.__size -= 1 self.__deleted[val] = self.__deleted.get(val, 0) + 1 def top(self): if self._is_empty(): return return self.__heap[0] def _is_empty(self): while self.__heap and self.__deleted.get(self.__heap[0]): val = heapq.heappop(self.__heap) self.__deleted[val] -= 1 return self.__size == 0 class Solution: def medianSlidingWindow(self, nums, k): """ :type nums: List[int] :type k: int :rtype: List[float] """ ans = [] if not nums or k <= 0 or len(nums) < k: return ans self.minheap = HashHeapqWithLazy() self.maxheap = HashHeapqWithLazy() for i in range(len(nums)): # remove nums[i - k] if i >= k: if self.minheap and nums[i - k] >= self.minheap.top(): self.minheap.remove(nums[i - k]) else: self.maxheap.remove(-1 * nums[i - k]) # add nums[i] if self.minheap and nums[i] >= self.minheap.top(): self.minheap.push(nums[i]) else: self.maxheap.push(-1 * nums[i]) # get median if i >= k - 1: ans.append(self.get_median()) return ans def get_median(self): if not self.maxheap and not self.minheap: return 0 while len(self.maxheap) > len(self.minheap) + 1: self.minheap.push(-1 * self.maxheap.pop()) while len(self.minheap) > len(self.maxheap): self.maxheap.push(-1 * self.minheap.pop()) return -1 * self.maxheap.top() ================================================ FILE: lintcode/362_sliding_window_maximum.py ================================================ from heapq import heappush, heappop class HashHeapq: def __init__(self): self.heap = [] self.deleted = {} def push(self, val): heappush(self.heap, val) def pop(self): if self.is_empty(): return heappop(self.heap) def remove(self, val): if self.is_empty(): return self.deleted[val] = self.deleted.get(val, 0) + 1 def top(self): if self.is_empty(): return return self.heap[0] def is_empty(self): while self.heap and self.deleted.get(self.heap[0]): self.deleted[self.heap[0]] -= 1 heappop(self.heap) return not self.heap class Solution: def maxSlidingWindow(self, A, k): """ :type A: List[int] :type k: int :rtype: List[int] """ ans = [] if not A: return ans heap = HashHeapq() for i in range(len(A)): heap.push(-A[i]) if i >= k - 1: ans.append(-heap.top()) heap.remove(-A[i - k + 1]) return ans """ Failed Solution Since its a O(n*k) solution """ class Solution: """ @param: A: A list of integers @param: k: An integer @return: The maximum number inside the window at each moving """ def maxSlidingWindow(self, A, k): ans = [] if not A or len(A) < 1: return ans for r in range(len(A)): if r >= k - 1: ans.append(max(A[r - k + 1 : r + 1])) return ans ================================================ FILE: lintcode/363_trapping_rain_water.py ================================================ class Solution: """ @param: heights: a list of integers @return: a integer """ def trapRainWater(self, heights): if not heights: return 0 # mx_l: max height for index `l` # mx_r: max height for index `r` mx_l = mx_r = ans = 0 l, r = 0, len(heights) - 1 while l < r: # To find the max height in heights if heights[l] < heights[r]: mx_l = max(mx_l, heights[l]) ans += mx_l - heights[l] l += 1 else: mx_r = max(mx_r, heights[r]) ans += mx_r - heights[r] r -= 1 return ans ================================================ FILE: lintcode/364_trapping_rain_water_ii.py ================================================ from heapq import heappush, heappop class Solution: """ @param: heights: a matrix of integers @return: an integer """ def trapRainWater(self, heights): if not heights: return 0 m, n = len(heights), len(heights[0]) dx, dy = [1, -1, 0, 0], [0, 0, 1, -1] ans = bound = x = y = i = 0 bounds = [] visited = [[0 for _ in range(n)] for _ in range(m)] # Put the cells on the matrix boundaries into `bounds` for i in range(m): heappush(bounds, (heights[i][0], i, 0)) visited[i][0] = 1 heappush(bounds, (heights[i][n - 1], i, n - 1)) visited[i][n - 1] = 1 for i in range(1, n - 1): heappush(bounds, (heights[0][i], 0, i)) visited[0][i] = 1 heappush(bounds, (heights[m - 1][i], m - 1, i)) visited[m - 1][i] = 1 while bounds: # Find the min bound of any current boundary bound, x, y = heappop(bounds) # To keep the water in, keep finding the boundary for i in range(4): _x = x + dx[i] _y = y + dy[i] if 0 <= _x < m and 0 <= _y < n and not visited[_x][_y]: visited[_x][_y] = 1 # Choosing the boundary of current cell # if its lower than the bound outside # than this cell will store water # otherwise this cell will become the new boundary _bound = max(bound, heights[_x][_y]) heappush(bounds, (_bound, _x, _y)) if _bound > heights[_x][_y]: ans += _bound - heights[_x][_y] return ans ================================================ FILE: lintcode/368_expression_evaluation.py ================================================ """ REF: [Reverse Polish Notation](https://zh.wikipedia.org/wiki/%E9%80%86%E6%B3%A2%E5%85%B0%E8%A1%A8%E7%A4%BA%E6%B3%95) REF: [Direct Algebraic Logic to Reverse Polish Notation](http://blog.csdn.net/sgbfblog/article/details/8001651) """ class Solution: P = { '+': 1, '-': 1, '*': 2, '/': 2, } """ @param: E: a list of strings @return: an integer """ def evaluateExpression(self, E): if not E: return 0 E = self.dal2rpn(E) """ for cases like `["(","(","(","(","(",")",")",")",")",")"]` """ if not E: return 0 return self.eval_rpn(E) def dal2rpn(self, E): """ `stack` to save operators and brackets temply `res` is the RPN of `E`, to save digits and operators """ stack, res = [], [] for char in E: if char.isdigit(): """ if its digit then directly output """ res.append(char) continue if char in self.P: """ if `stack` is not empty and `stack[-1]` is an operator and its priority is higher or same ('*' == '/' > '+' == '-') then pop and output """ while (stack and stack[-1] in self.P and self.P[stack[-1]] >= self.P[char]): res.append(stack.pop()) stack.append(char) elif char == '(': stack.append(char) elif char == ')': """ if its ')' then continue to pop and output until meet '(' """ while stack and stack[-1] != '(': res.append(stack.pop()) stack.pop() # evict '(' """ output the remaining in `stack` """ while stack: res.append(stack.pop()) return res def eval_rpn(self, E): stack = [] for char in E: if char.isdigit(): """ if its digit then temply save in `stack` """ stack.append(int(char)) continue """ the first poped one is base, otherwise there is error occurred when '/' and '-' """ b = stack.pop() a = stack.pop() if char == '+': stack.append(a + b) elif char == '-': stack.append(a - b) elif char == '*': stack.append(a * b) elif char == '/': stack.append(a // b) return stack[0] ================================================ FILE: lintcode/36_reverse_linked_list_ii.py ================================================ """ Definition of ListNode class ListNode(object): def __init__(self, val, next=None): self.val = val self.next = next """ class Solution: """ @param: head: ListNode head is the head of the linked list @param: m: An integer @param: n: An integer @return: The head of the reversed ListNode """ def reverseBetween(self, head, m, n): if not head: return """ to get to `n`th node from `m`th node needs `n - m` operations """ n -= m """ `A` will stay at `m-1`th node """ A = dummy = ListNode(-1, head) while m > 1: m -= 1 A = A.next """ `B` will stay at `n+1`th node `cur` stay at (`m`th -> `n`th) node """ B = cur = A.next pre = nxt = None while n >= 0: n -= 1 nxt = B.next B.next = pre pre = B B = nxt A.next = pre cur.next = B return dummy.next ================================================ FILE: lintcode/376_binary_tree_path_sum.py ================================================ """ Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ class Solution: """ @param: root: the root of binary tree @param: target: An integer @return: all valid paths """ def binaryTreePathSum(self, root, target): ans = [] self.dfs(root, target, ans, []) return ans def dfs(self, node, remaining, ans, path): if not node: return path.append(node.val) remaining -= node.val if remaining == 0 and not node.left and not node.right: ans.append(path[:]) self.dfs(node.left, remaining, ans, path) self.dfs(node.right, remaining, ans, path) path.pop() ================================================ FILE: lintcode/378_convert_binary_search_tree_to_doubly_linked_list.py ================================================ """ Definition of TreeNode: class TreeNode: def __init__(self, val): this.val = val this.left, this.right = None, None Definition of Doubly-ListNode class DoublyListNode(object): def __init__(self, val, next=None): self.val = val self.next = self.prev = next """ class Solution: """ @param: root: The root of tree @return: the head of doubly list node """ def bstToDoublyList(self, root): if not root: return dummy = tail = DoublyListNode(-1) stack = [] node = root while node or stack: while node: stack.append(node) node = node.left node = stack.pop() _node = DoublyListNode(node.val) _node.prev = tail tail.next = _node tail = tail.next node = node.right return dummy.next ================================================ FILE: lintcode/382_triangle_count.py ================================================ class Solution: """ @param: S: A list of integers @return: An integer """ def triangleCount(self, S): ans = 0 if not S or len(S) < 3: return ans n = len(S) S.sort() """ just like two sum `c` is the target """ for c in range(n - 1, 1, -1): a = 0 b = c - 1 while a < b: if S[a] + S[b] > S[c]: ans += b - a b -= 1 else: a += 1 return ans ================================================ FILE: lintcode/384_longest_substring_without_repeating_characters.py ================================================ import collections class Solution: def lengthOfLongestSubstring(self, s): """ :type s: str :rtype: int """ ans = 0 if not s: return ans freqs = collections.defaultdict(int) i = rep = 0 for j in range(len(s)): if freqs[s[j]] == 1: rep += 1 freqs[s[j]] += 1 while rep > 0: freqs[s[i]] -= 1 if freqs[s[i]] == 1: rep -= 1 i += 1 ans = max(ans, j - i + 1) return ans ================================================ FILE: lintcode/386_longest_substring_with_at_most_k_distinct_characters.py ================================================ import collections class Solution: def lengthOfLongestSubstringKDistinct(self, s, k): """ :type s: str :type k: int :rtype: int """ ans = 0 if not s or not k: return ans freq = collections.defaultdict(int) n = len(s) left = right = cnt = 0 while right < n: freq[s[right]] += 1 if freq[s[right]] == 1: cnt += 1 right += 1 while cnt > k: freq[s[left]] -= 1 if freq[s[left]] == 0: cnt -= 1 left += 1 if right - left > ans: ans = right - left return ans ================================================ FILE: lintcode/38_search_a_2d_matrix_ii.py ================================================ """ Heap """ import heapq class Solution: def searchMatrix(self, matrix, target): """ :type matrix: list[list[int]] :type target: int :rtype: int """ ans = 0 if not matrix or not matrix[0]: return ans heap = [] m, n = len(matrix), len(matrix[0]) for i in range(m): heapq.heappush(heap, (matrix[i][0], i, 0)) while heap and heap[0][0] <= target: num, x, y = heapq.heappop(heap) if num == target: ans += 1 y += 1 if y < n: heapq.heappush(heap, (matrix[x][y], x, y)) return ans """ Iteration start from bottom-left of matrix if `G[x][y] > target`: need to check `x - 1` all cells before `y - 1` are confirmed in last iteration else: all cells before `x + 1` are confirmed in last iteration need to check `y + 1` """ class Solution: def searchMatrix(self, matrix, target): """ :type matrix: list[list[int]] :type target: int :rtype: int """ ans = 0 if not matrix or not matrix[0]: return ans m, n = len(matrix), len(matrix[0]) x, y = m - 1, 0 while x >= 0 and y < n: if matrix[x][y] < target: y += 1 elif matrix[x][y] > target: x -= 1 else: ans += 1 x -= 1 y += 1 return ans ================================================ FILE: lintcode/390_find_peak_element_ii.py ================================================ """ Test Case [[ 1, 2, 3, 6, 5 ] ,[ 16, 41, 23, 22, 6 ] ,[ 15, 17, 24, 21, 7 ] ,[ 14, 18, 19, 20, 10 ] ,[ 13, 14, 11, 10, 9 ]] [[ 1, 3, 2 ] ,[ 4, 6, 5 ] ,[ 7, 9, 8 ] ,[ 13, 15, 14 ] ,[ 10, 12, 11 ]] """ class Solution: NULL_POS = [-1, -1] dx = [1, -1, 0, 0] dy = [0, 0, 1, -1] """ @param: A: An integer matrix @return: The index of the peak """ """ step0/ given matrix and its size is 6*6 step1/ chosen the mid_col is 3, the max_value on that col at (2, 3), and the value at (2, 2) great than it step2/ chosen the mid_row is 2, the max_value in the segment[0:3] on that row at (2, 1), and the value at (3, 1) great than it step3/ keep slicing row or col if its range is more wide ... step-1/ find a square and check the vertices """ def findPeakII(self, A): if not A: return self.NULL_POS self.m, self.n = len(A), len(A[0]) left, col, right = 0, 0, self.n - 1 up, row, down = 0, 0, self.m - 1 while left + 1 < right or up + 1 < down: if down - up > right - left: row = up + (down - up) // 2 col = self.findRowMax(A, row, left, right) if self.isPeak(A, row, col): return [row, col] if A[row][col] < A[row-1][col]: down = row else: up = row else: col = left + (right - left) // 2 row = self.findColMax(A, col, up, down) if self.isPeak(A, row, col): return [row, col] if A[row][col] < A[row][col-1]: right = col else: left = col for r in [up, down]: for c in [left, right]: if self.isPeak(A, r, c): return [r, c] return self.NULL_POS # given col index, return the row index of the max value on that col def findColMax(self, A, col, up, down): row = 0 for r in range(up, down + 1): if A[row][col] < A[r][col]: row = r return row # given row index, return the col index of the max value on that row def findRowMax(self, A, row, left, right): col = 0 for c in range(left, right + 1): if A[row][col] < A[row][c]: col = c return col def isPeak(self, A, row, col): _r = _c = 0 for i in range(4): _r = row + self.dx[i] _c = col + self.dy[i] if 0 <= _r < self.m \ and 0 <= _c < self.n \ and A[row][col] < A[_r][_c]: return 0 return 1 ================================================ FILE: lintcode/391_number_of_airplanes_in_the_sky.py ================================================ """ Definition of Interval. class Interval(object): def __init__(self, start, end): self.start = start self.end = end """ class Solution: """ @param: airplanes: An interval array @return: Count of airplanes are in the sky. """ def countOfAirplanes(self, airplanes): ans = 0 if not airplanes: return ans cnt = 0 timeline = [] for interval in airplanes: timeline.append((interval.start, 1)) timeline.append((interval.end, 0)) timeline.sort() for _, in_sky in timeline: if in_sky: cnt += 1 else: cnt -= 1 ans = max(ans, cnt) return ans ================================================ FILE: lintcode/392_house_robber.py ================================================ """ to borrow the idea of `./lintcode/515_paint_house.py` a house can be at one of two status: steal or not """ class Solution: """ @param: A: An array of non-negative integers @return: The maximum amount of money you can rob tonight """ def houseRobber(self, A): if not A: return 0 n = len(A) prev = curr = 0 dp = [[0] * 2 for _ in range(2)] dp[0][1] = A[0] for i in range(1, n): prev = curr # (i - 1) % 2 curr = i % 2 dp[curr][0] = max(dp[prev]) dp[curr][1] = dp[prev][0] + A[i] return max(dp[curr]) class Solution: """ @param: A: An array of non-negative integers @return: The maximum amount of money you can rob tonight """ def houseRobber(self, A): if not A: return 0 if len(A) == 1: return A[0] n = len(A) # `dp[i]`: the maximum amount of money we can get until house `i` dp = [0] * 3 dp[0] = A[0] dp[1] = max(A[0], A[1]) prev2, prev1, curr = 0, 0, 1 for i in range(2, n): prev2, prev1 = prev1, curr curr = i % 3 """ if house `i` was stolen, we can't steal house `i - 1`, the amount is `dp[i - 2] + A[i]` if house `i` is not stolen, we can steal house `i - 1`, but we lose the `A[i]`, the amount is `dp[i - 1] + 0` """ dp[curr] = max(dp[prev1], dp[prev2] + A[i]) return dp[curr] class Solution: def houseRobber(self, A): """ :type A: List[int] :rtype: int """ if not A: return 0 n = len(A) dp = [0] * (n + 1) dp[1] = A[0] for i in range(2, n + 1): dp[i] = max( dp[i - 2] + A[i - 1], dp[i - 1] ) return dp[n] ================================================ FILE: lintcode/393_best_time_to_buy_and_sell_stock_iv.py ================================================ class Solution: """ @param: K: An integer @param: P: An integer array @return: Maximum profit """ def maxProfit(self, K, P): if not K or not P: return 0 n = len(P) """ if `K >= n`, this problem is just `./lintcode/150_best_time_to_buy_and_sell_stock_ii.py` so we dont need to use `dp`, since it lead to overflow """ if K >= n: profit = 0 for i in range(1, n): if P[i] > P[i - 1]: profit += P[i] - P[i - 1] return profit """ the main concept is in `./lintcode/151_best_time_to_buy_and_sell_stock_iii.py` """ STAGE = 2 * K + 1 dp = [[0] * STAGE for _ in range(2)] i = j = prev = curr = profit = 0 for i in range(1, n): prev = curr curr = 1 - prev profit = P[i] - P[i - 1] for j in range(1, STAGE, 2): dp[curr][j] = max(dp[prev][j] + profit, dp[prev][j - 1]) for j in range(2, STAGE, 2): dp[curr][j] = max(dp[prev][j], dp[prev][j - 1] + profit) return max(dp[curr]) ================================================ FILE: lintcode/394_coins_in_a_line.py ================================================ """ There are n coins in a line. Two players take turns to take one or two coins from right side until there are no more coins left. The player who take the last coin wins. Could you please decide the first play will win or lose? `dp[i]` means first play will win if left `i` stones in line `dp[i]` = True, if dp[i - 1] == False and dp[i - 2] == False True, if dp[i - 1] == True and dp[i - 2] == False True, if dp[i - 1] == False and dp[i - 2] == True False, if dp[i - 1] == True and dp[i - 2] == True => dp[i - 1] == False or dp[i - 2] == False the meaning is if the p2 is possible to lose when stones == i - 1 or i - 2 => p1 will win """ class Solution: def firstWillWin(self, n): """ :type n: int :rtype: bool """ if not n: return False if n < 3: return True dp = [False] * n dp[0] = dp[1] = True for i in range(2, n): if ( dp[i - 1] is False or dp[i - 2] is False ): dp[i] = True return dp[n - 1] ================================================ FILE: lintcode/395_coins_in_a_line_ii.py ================================================ """ There are n coins with different value in a line. Two players take turns to take one or two coins from left side until there are no more coins left. The player who take the coins with the most value wins. Could you please decide the first player will win or lose? `dp[i]` means the p1 earned values if game start from `i` case 1: if p2 will take the `i + 1` => values[i] - dp[i + 1] case 2: if p2 will take the `i + 2` => values[i] + values[i + 1] - dp[i + 2] """ class Solution: def firstWillWin(self, values): """ :type values: list[int] :rtype: bool """ if not values: return False n = len(values) if n < 3: return True dp = [False] * n dp[-1] = values[-1] dp[-2] = values[-1] + values[-2] for i in range(n - 3, -1, -1): dp[i] = max(( values[i] - dp[i + 1], values[i] + values[i + 1] - dp[i + 2], )) return dp[0] >= 0 ================================================ FILE: lintcode/396_coins_in_a_line_iii.py ================================================ """ There are n coins in a line. Two players take turns to take a coin from one of the ends of the line until there are no more coins left. The player with the larger amount of money wins. Could you please decide the first player will win or lose? `dp[i][j]` means the maximum value when the current player chosen from [i, j] in `values` recurring from the end of the game to start so init with `dp[i][i]` i i+1 j-1 j c1 c2 c3 c4 c5 there are two cases: 1. if the current player chosen `values[i]`, then the cost is `dp[i + 1][j]` 2. if the current player chosen `values[j]`, then the cost is `dp[i][j - 1]` chosen the maximum """ class Solution: def firstWillWin(self, values): """ :type values: list[int] :rtype: bool """ if not values: return False n = len(values) if n < 2: return True dp = [[0] * n for _ in range(n)] for i in range(n): dp[i][i] = values[i] for i in range(n - 1 - 1, -1, -1): for j in range(i + 1, n): dp[i][j] = max(( values[i] - dp[i + 1][j], values[j] - dp[i][j - 1], )) return dp[0][n - 1] >= 0 ================================================ FILE: lintcode/397_longest_increasing_continuous_subsequence.py ================================================ """ DP + print path remove the single line comment to see the path in result """ class Solution: """ @param: A: An array of Integer @return: an integer """ def longestIncreasingContinuousSubsequence(self, A): if not A: return 0 size = self.get_lics_size(A) A.reverse() _size = self.get_lics_size(A) return max(size, _size) def get_lics_size(self, A): ans = 0 n = len(A) """ `dp[i]` means the size of LICS ended at `A[i]` note that there is size, so init with `1` """ dp = [1] * n # pi = [-1] * n # end_at = -1 for i in range(n): if i > 0 and A[i] > A[i - 1]: dp[i] = dp[i - 1] + 1 # pi[i] = i - 1 if dp[i] > ans: ans = dp[i] # end_at = i # paths = [0] * ans # for i in range(ans - 1, -1, -1): # paths[i] = A[end_at] # end_at = pi[end_at] # print(paths) return ans """ Optimized """ class Solution: """ @param: A: An array of Integer @return: an integer """ def longestIncreasingContinuousSubsequence(self, A): if not A: return 0 size = self.get_lics_size(A) A.reverse() _size = self.get_lics_size(A) return max(size, _size) def get_lics_size(self, A): ans = size = 1 for i in range(1, len(A)): if A[i] > A[i - 1]: size += 1 else: size = 1 if size > ans: ans = size return ans ================================================ FILE: lintcode/3_digit_counts.py ================================================ class Solution: """ @param: k: An integer @param: n: An integer @return: An integer denote the count of digit k in 1..n """ def digitCounts(self, k, n): ans = 0 for i in range(n + 1): ans += self.count(k, i) return ans def count(self, k, a): if k == a == 0: return 1 cnt = 0 while a: if a % 10 == k: cnt += 1 a //= 10 return cnt ================================================ FILE: lintcode/401_kth_smallest_number_in_sorted_matrix.py ================================================ import heapq class Solution: """ @param: matrix: a matrix of integers @param: k: An integer @return: the kth smallest number in the matrix """ def kthSmallest(self, matrix, k): ans = j = 0 heap, m, n = [], len(matrix), len(matrix[0]) for i in range(min(k, m)): heapq.heappush(heap, (matrix[i][0], i, 0)) while k > 0: ans = heapq.heappop(heap) j = ans[2] + 1 if j < n: heapq.heappush(heap, (matrix[ans[1]][j], ans[1], j)) k -= 1 return ans[0] ================================================ FILE: lintcode/402_continuous_subarray_sum.py ================================================ class Solution: """ @param: A: An integer array @return: A list of integers includes the index of the first number and the index of the last number """ def continuousSubarraySum(self, A): ans = [-1, -1] if not A: return ans left = right = 0 _max = _sum = float('-inf') for i in range(len(A)): if _sum < 0: _sum = A[i] left = right = i else: _sum += A[i] right = i if _sum > _max: _max = _sum ans = [left, right] return ans ================================================ FILE: lintcode/406_minimum_size_subarray_sum.py ================================================ class Solution: """ @param: nums: an array of integers @param: s: An integer @return: an integer representing the minimum size of subarray """ def minimumSize(self, nums, s): if not nums or len(nums) == 0: return -1 n = len(nums) min_len = n + 1 l = r = t = 0 while r < n: # Keep adding the next int into total until total >= s while r < n and t < s: t += nums[r] r += 1 # Terminate iteration if all the children in nums have been added if r >= n and t < s: break # Keep substracting the prev int from total until total < s while l < r and t >= s: t -= nums[l] l += 1 # Save the min_size min_len = min(min_len, r - l + 1) return -1 if min_len > n else min_len ================================================ FILE: lintcode/40_implement_queue_by_two_stacks.py ================================================ class MyQueue: def __init__(self, ): self.stack1 = [] self.stack2 = [] """ @param: element: An integer @return: nothing """ def push(self, element): self.stack1.append(element) """ @return: An integer """ def pop(self, ): self.top() return self.stack2.pop() """ @return: An integer """ def top(self, ): # While `self.stack2` is empty, get element from `self.stack1` # step0/ stack1: [1, 2, 3], stack2: [] # step1/ stack1: [], stack2: [3, 2, 1] # step2/ stack1: [4, 5, 6], stack2: [3, 2] // 1 # step3/ stack1: [4, 5, 6], stack2: [] // 2, 3 # step4/ stack1: [7, 8], stack2: [6, 5, 4] if not self.stack2: while self.stack1: self.stack2.append(self.stack1.pop()) return self.stack2[-1] ================================================ FILE: lintcode/414_divide_two_integers.py ================================================ class Solution: def divide(self, a, b): """ :type a: int :type b: int :rtype: int """ INT_MAX = 0x7FFFFFFF ans = 0 if not b: return INT_MAX if not a: return ans _a = -a if a < 0 else a _b = -b if b < 0 else b for i in range(31, -1, -1): """ let `ci = 1 << i = 2 ** i` if `_a // _b >= ci`, that is `_a // ci >= _b` => `(_a - _b * ci) // _b >= 0`, and `ans += ci` start to approch `_a' // _b >= ci'` where _a' = _a - _b * ci, i' = i - 1 """ if (_a >> i) < _b: continue ans += 1 << i _a -= _b << i if a ^ b < 0: ans = -ans if ans > INT_MAX: return INT_MAX if ans < ~INT_MAX: return ~INT_MAX return ans ================================================ FILE: lintcode/415_valid_palindrome.py ================================================ class Solution: def isPalindrome(self, s): """ :type s: str :rtype: bool """ if not s: return True s = s.lower() n = len(s) left, right = 0, n - 1 while left < right: while left < right and not s[left].isalnum(): left += 1 while left < right and not s[right].isalnum(): right -= 1 if s[left] != s[right]: return False left += 1 right -= 1 return True ================================================ FILE: lintcode/417_valid_number.py ================================================ class Solution: """ @param: s: the string that represents a number @return: whether the string is a valid number """ def isNumber(self, s): if not s: return False n = len(s) left, right = 0, n - 1 while left < n and s[left] == ' ': left += 1 while right >= 0 and s[right] == ' ': right -= 1 if left < n and s[left] in ('+', '-'): left += 1 if left > right: return False if left != 0 or right != n - 1: s = s[left:right + 1] zero = ord('0') nine = ord('9') is_contained_dot = False is_contained_num = False for char in s: if char == '.' and is_contained_dot: return False if not (char == '.' or zero <= ord(char) <= nine): return False if char == '.': is_contained_dot = True if zero <= ord(char) <= nine: is_contained_num = True return is_contained_num ================================================ FILE: lintcode/418_integer_to_roman.py ================================================ class Solution: def intToRoman(self, num): """ :type num: int :rtype: str """ if not num: return '' # I, V, X, L, C, D, M symbs = ( ('M', 1000), ('CM', 900), ('D', 500), ('CD', 400), ('C', 100), ('XC', 90), ('L', 50), ('XL', 40), ('X', 10), ('IX', 9), ('V', 5), ('IV', 4), ('I', 1), ) ans = [] for symb, amount in symbs: if not num: break while num >= amount: # num - amount >= 0 num -= amount ans.append(symb) return ''.join(ans) ================================================ FILE: lintcode/419_roman_to_integer.py ================================================ class Solution: def romanToInt(self, s): """ :type s: str :rtype: int """ ans = 0 if not s: return ans symbs = { 'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000, } ans += symbs[s[-1]] for i in range(len(s) - 2, -1, -1): if symbs[s[i]] >= symbs[s[i + 1]]: ans += symbs[s[i]] else: ans -= symbs[s[i]] return ans ================================================ FILE: lintcode/41_maximum_subarray.py ================================================ """ Prefix Sum: space optimization """ class Solution: """ @param: A: A list of integers @return: A integer indicate the sum of max subarray """ def maxSubArray(self, A): """ the answer is the maximum segment sum, that is, `S[i] - Smin` """ if not A: return 0 ans = float('-inf') S = Smin = 0 for i in range(len(A)): S += A[i] if S - Smin > ans: ans = S - Smin """ since the sum of [i, j] in A is `S[j] - S[i - 1]` so we need to find the `Smin` at the end of iteration """ if S < Smin: Smin = S return ans """ Prefix Sum """ class Solution: """ @param: A: A list of integers @return: A integer indicate the sum of max subarray """ def maxSubArray(self, A): """ the answer is the maximum segment sum, that is, `S[i] - Smin` """ if not A: return 0 n = len(A) ans = float('-inf') Smin = 0 S = [0] * (n + 1) for i in range(1, n + 1): S[i] = S[i - 1] + A[i - 1] if S[i] - Smin > ans: ans = S[i] - Smin """ since the sum of [i, j] in A is `S[j] - S[i - 1]` so we need to find the `Smin` at the end of iteration """ if S[i] < Smin: Smin = S[i] return ans ================================================ FILE: lintcode/42_maximum_subarray_ii.py ================================================ """ main concept there is MUST a separator to distinct that two subarrays `left[i]` means the maxsum before `i + 1` `right[i]` means the maxsum after `i - 1` |<- the separator nums[i] | nums[i + 1] the `ans` is to find the maximum of `left[i] + right[i + 1]` """ class Solution: def maxTwoSubArrays(self, nums): """ :type nums: list[int] :rtype: int """ NOT_FOUND = 0 if not nums: return NOT_FOUND n = len(nums) left = self.get_max_sums(nums, range(n)) right = self.get_max_sums(nums, range(n - 1, -1, -1)) ans = _INF = float('-inf') for i in range(n - 1): s = left[i] + right[i + 1] if s > ans: ans = s return ans if ans > _INF else NOT_FOUND def get_max_sums(self, nums, num_range): res = [0] * len(nums) smax = float('-inf') s = smin = 0 for i in num_range: s += nums[i] if s - smin > smax: smax = s - smin if s < smin: smin = s res[i] = smax return res ================================================ FILE: lintcode/430_scramble_string.py ================================================ class Solution: """ @param: s1: A string @param: s2: Another string @return: whether s2 is a scrambled string of s1 """ def isScramble(self, s1, s2): if not s1 or not s2 or len(s1) != len(s2): return False n = len(s1) """ `dp[i][j][k]` means the substring in `s1` (start: `i`, len: `k`) could be transformed into the substring in `s2` (start: `j`, len: `k`) """ dp = [[[False] * (n + 1) for _ in range(n)] for _ in range(n)] for i in range(n): for j in range(n): dp[i][j][1] = (s1[i] == s2[j]) for k in range(2, n + 1): for i in range(n): """ allow: i < n - k + 1 => i <= n - k disallow: i > n - k """ if i + k > n: continue for j in range(n): if j + k > n: continue for l in range(1, k): """ If its already calculated and possible to transform """ if dp[i][j][k]: continue """ * u1, u2: substring in s1 * v1, v2: substring in s2 * `l` == len(u1) == len(v1) * `k - l` == len(u2) == len(v2) case1: u1 -> v1, u2 -> v2 `(dp[i][j][l] and dp[i + l][j + l][k - l])` - `dp[i][j][l]` means its possible to u1 -> v1 - `dp[i + l][j + l][k - l]` => u2 -> v2 u1 u2 s1: |`i`---`l`--->|`i+l`---`k-l`--->| v1 v2 s2: |`j`---`l`--->|`j+l`---`k-l`--->| case2: u1 -> v2, u2 -> v1 `(dp[i][j + k - l][l] and dp[i + l][j][k - l])` - `dp[i][j + k - l][l]` => u1 -> v2 - `dp[i + l][j][k - l]` => u2 -> v1 u1 u2 s1: |`i`---`l`--->|`i+l`---`k-l`--->| v1 v2 s2: |`j`---`k-l`--->|`j+k-l`--`l`-->| """ if dp[i][j][l] and dp[i + l][j + l][k - l]: dp[i][j][k] = True continue if dp[i][j + k - l][l] and dp[i + l][j][k - l]: dp[i][j][k] = True return dp[0][0][n] ================================================ FILE: lintcode/431_connected_component_in_undirected_graph.py ================================================ """ Definition for a undirected graph node class UndirectedGraphNode: def __init__(self, x): self.label = x self.neighbors = [] """ class Solution: """ Union Find """ def connectedSet(self, nodes): """ :type nodes: list[UndirectedGraphNode] :rtype: list[list[UndirectedGraphNode]] """ if not nodes: return [] uf = {} for node in nodes: for neib in node.neighbors: self.union(uf, node, neib) ans = {} for node in nodes: # to correct root again root = self.find(uf, node) if root not in ans: ans[root] = [] ans[root].append(node.label) return list(ans.values()) def union(self, nodes, a, b): _a = self.find(nodes, a) _b = self.find(nodes, b) if _a is not _b: nodes[_b] = _a def find(self, nodes, a): if a not in nodes: nodes[a] = a return a if nodes[a] is a: return a nodes[a] = self.find(nodes, nodes[a]) return nodes[a] class Solution: """ DFS """ def connectedSet(self, nodes): """ :type nodes: list[UndirectedGraphNode] :rtype: list[list[UndirectedGraphNode]] """ ans = [] if not nodes: return ans visited = set() for node in nodes: if node in visited: continue path = [] self.dfs(node, visited, path) ans.append(sorted(path)) return ans def dfs(self, a, visited, path): visited.add(a) path.append(a.label) for b in a.neighbors: if b in visited: continue self.dfs(b, visited, path) ================================================ FILE: lintcode/432_find_the_weak_connected_component_in_the_directed_graph.py ================================================ """ Definition for a directed graph node class DirectedGraphNode: def __init__(self, x): self.label = x self.neighbors = [] """ class Solution: def __init__(self): self.nodes = {} """ @param {DirectedGraphNode[]} nodes a array of directed graph node @return {int[][]} a connected set of a directed graph """ def connectedSet2(self, nodes): # Build UnionFind for node in nodes: for nei in node.neighbors: self.connect(nei.label, node.label) # Categorify result result = {} root_label = '' for node in nodes: root_label = self.find(node.label) if root_label not in result: result[root_label] = [] result[root_label].append(node.label) return result.values() def connect(self, a, b): root_a = self.find(a) root_b = self.find(b) if root_a is not root_b: self.nodes[root_a] = root_b def find(self, a): if a not in self.nodes: self.nodes[a] = a return a elif self.nodes[a] is a: return a self.nodes[a] = self.find(self.nodes[a]) return self.nodes[a] ================================================ FILE: lintcode/433_number_of_islands.py ================================================ class Solution: def numIslands(self, grid): """ :type grid: list[list[int]] :rtype: int """ ans = 0 if not grid or not grid[0]: return ans m, n = len(grid), len(grid[0]) for x in range(m): for y in range(n): if grid[x][y] == 1: ans += 1 self.dfs(grid, x, y) return ans def dfs(self, grid, x, y): m, n = len(grid), len(grid[0]) grid[x][y] = 0 for dx, dy in ( (0, -1), (0, 1), (-1, 0), (1, 0), ): _x = x + dx _y = y + dy if not ( 0 <= _x < m and 0 <= _y < n and grid[_x][_y] == 1 ): continue self.dfs(grid, _x, _y) ================================================ FILE: lintcode/434_number_of_islands_ii.py ================================================ """ Definition for a point. class Point: def __init__(self, a=0, b=0): self.x = a self.y = b """ class Solution: def numIslands2(self, m, n, operators): """ :type m: int :type n: int :type operators: Point :rtype: list[int] """ ans = [] if not m or not n or not operators: return ans cnt = 0 nodes = {} for op in operators: node = (op.x, op.y) if node not in nodes: nodes[node] = node cnt += 1 for dx, dy in ( (0, -1), (0, 1), (-1, 0), (1, 0), ): _x = op.x + dx _y = op.y + dy if not ( 0 <= _x < m and 0 <= _y < n and (_x, _y) in nodes ): continue if self.union(nodes, node, (_x, _y)): cnt -= 1 ans.append(cnt) return ans def union(self, nodes, a, b): _a = self.find(nodes, a) _b = self.find(nodes, b) if _a == _b: return False nodes[_b] = _a return True def find(self, nodes, a): if a not in nodes: nodes[a] = a return a if nodes[a] == a: return a nodes[a] = self.find(nodes, nodes[a]) return nodes[a] ================================================ FILE: lintcode/437_copy_books.py ================================================ """ DP: TLE """ class Solution: """ @param: P: an array of integers @param: k: An integer @return: an integer """ def copyBooks(self, P, k): if not P or not k: return 0 n = len(P) if n == 1: return P[0] if k > n: k = n INFINITY = float('inf') """ `dp[i][j]` means the minimum time to assign `i` books to `j` people """ dp = [[INFINITY] * k for _ in range(n)] for j in range(k): dp[0][j] = P[0] for i in range(1, n): dp[i][0] = dp[i - 1][0] + P[i] for i in range(1, n): for j in range(1, k): if j > i: """ if `j > i`, means books more than copiers the people after `j`th people dont have to work """ dp[i][j] = dp[i][j - 1] continue for h in range(j - 1, i + 1): """ `copied_pages` is the maximum copied pages, and also means the maximum time spent by the `j - 1` people before `j` if the `j - 1` people can copy as much as they can then `j`th man will be able to spend less time finishing it """ copied_pages = dp[i][0] - dp[h][0] if dp[h][j - 1] > copied_pages: copied_pages = dp[h][j - 1] if copied_pages < dp[i][j]: dp[i][j] = copied_pages return dp[n - 1][k - 1] """ DP """ class Solution: """ @param: P: an array of integers @param: k: An integer @return: an integer """ def copyBooks(self, P, k): if not P or not k: return 0 n = len(P) if n == 1: return P[0] if k > n: k = n """ `dp[i][j]` means the minimum time to assign `i` books to `j` people """ dp = [[0] * k for _ in range(n)] for j in range(k): dp[0][j] = P[0] for i in range(1, n): dp[i][0] = dp[i - 1][0] + P[i] for j in range(1, k): for i in range(1, j): """ if `j > i`, means books more than copiers the people after `j`th people dont have to work """ dp[i][j] = dp[i][j - 1] h = copied_pages = 0 for i in range(j, n): """ `h` means the maximum books `j - 1` men copied in shortest time """ while h < i and dp[h][j - 1] < dp[i][0] - dp[h][0]: h += 1 dp[i][j] = dp[h][j - 1] if h == 0: continue """ check again the `h - 1` books `copied_pages` is the maximum copied pages, and also means the maximum time spent by the `j - 1` people before `j` if the `j - 1` people can copy as much as they can then `j`th man will be able to spend less time finishing it """ copied_pages = dp[i][0] - dp[h - 1][0] if dp[h - 1][j - 1] > copied_pages: copied_pages = dp[h - 1][j - 1] if copied_pages < dp[i][j]: dp[i][j] = copied_pages return dp[n - 1][k - 1] """ Binary Search 1. the minimum spent time, that is `m`, locates between `max(pages)` and `sum(pages)` (1 page spent 1 min) 2. the `p` below means possible to finish work, if the spent time less than `m` and then the `k` people are impossible to finish that t | ... m-2 m-1 m m+1 m+2 ... p | ..F F F T T T T.. """ class Solution: """ @param: P: an array of integers @param: k: An integer @return: an integer """ def copyBooks(self, P, k): if not P or not k: return 0 n = len(P) if n == 1: return P[0] if k > n: k = n left = right = P[0] for i in range(1, len(P)): if P[i] > left: left = P[i] right += P[i] while left + 1 < right: mid = (left + right) // 2 if self.check_if_possible(P, mid, k): right = mid else: left = mid """ MUST check `left` first, since we need the min spent time """ return left if self.check_if_possible(P, left, k) else right def check_if_possible(self, P, spent_time, max_copiers): """ check if possible to copy all `pages` in `spent_time` and participation is not more than `max_copiers` """ copied_pages, copiers = 0, 1 for i in range(len(P)): """ if a copier will spend more than `spent_time` add one more copier in """ if copied_pages + P[i] > spent_time: copied_pages = 0 copiers += 1 if copiers > max_copiers: return False copied_pages += P[i] return True ================================================ FILE: lintcode/43_maximum_subarray_iii.py ================================================ """ http://www.cnblogs.com/sherylwang/p/5635665.html """ class Solution: """ @param: A: A list of integers @param: k: An integer denote to find k non-overlapping subarrays @return: An integer denote the sum of max k non-overlapping subarrays """ def maxSubArray(self, A, k): if not A or not k or len(A) < k: return 0 _INFINITY = float('-inf') n = len(A) """ `G[i][j]` means the global max sum, to pick `j` arrays in [0, i) `L[i][j]` means the local max sum, to pick `j` arrays in [0, i), and the `i - 1`th child MUST be included """ G = [[_INFINITY] * (k + 1) for _ in range(n + 1)] L = [[_INFINITY] * (k + 1) for _ in range(n + 1)] for i in range(n + 1): L[i][0] = 0 G[i][0] = 0 for i in range(1, n + 1): end = i if k < end: end = k for j in range(1, end + 1): L[i][j] = A[i - 1] + max(L[i - 1][j], G[i - 1][j - 1]) G[i][j] = max(L[i][j], G[i - 1][j]) return G[n][k] ================================================ FILE: lintcode/440_backpack_iii.py ================================================ """ Optimize Space """ class Solution: """ @param: A: an integer array @param: V: an integer array @param: m: an integer @return: an integer """ def backPackIII(self, A, V, m): if not A or not V or not m: return 0 # `dp[w]` means the maximum value # with weight `w` dp = [0] * (m + 1) _val = 0 for i in range(len(A)): for w in range(A[i], m + 1): _val = dp[w - A[i]] + V[i] if _val > dp[w]: dp[w] = _val return dp[m] """ Origin """ class Solution: """ @param: A: an integer array @param: V: an integer array @param: m: an integer @return: an integer """ def backPackIII(self, A, V, m): if not A or not V or not m: return 0 n = len(A) # `dp[i][w]` means the maximum value # with weight `w` in the former `i` items dp = [[0] * (m + 1) for _ in range(n + 1)] for i in range(1, n + 1): for w in range(1, m + 1): dp[i][w] = dp[i - 1][w] if w >= A[i - 1]: dp[i][w] = max( dp[i][w], dp[i][w - A[i - 1]] + V[i - 1] ) return dp[n][w] ================================================ FILE: lintcode/442_implement_trie.py ================================================ class TrieNode: def __init__(self): self.end_of = None self.children = {} class Trie: def __init__(self): self.root = TrieNode() """ @param: word: a word @return: nothing """ def insert(self, word): if word is None: return node = self.root for c in word: if c not in node.children: node.children[c] = TrieNode() node = node.children[c] node.end_of = word """ @param: word: A string @return: if the word is in the trie. """ def search(self, word): if word is None: return False node = self.root for c in word: if c not in node.children: return False node = node.children[c] return node.end_of == word """ @param: prefix: A string @return: if there is any word in the trie that starts with the given prefix. """ def startsWith(self, prefix): if prefix is None: return False node = self.root for c in prefix: if c not in node.children: return False node = node.children[c] return True ================================================ FILE: lintcode/443_two_sum_greater_than_target.py ================================================ class Solution: """ @param: A: an array of integer @param: target: An integer @return: an integer """ def twoSum2(self, A, target): ans = 0 if not A or len(A) < 2: return ans A.sort() left, right = 0, len(A) - 1 while left < right: # if minimum + maximum still <= target # ignore the 2nd, 3rd maximum if A[left] + A[right] <= target: left += 1 continue # if minimum + maximum > target # we can ensure the 2nd, 3rd minimum also fit demand ans += right - left right -= 1 return ans ================================================ FILE: lintcode/447_search_in_a_big_sorted_array.py ================================================ """ Test Case: # if `reader.get(m) == target` when binary searching # should set the `mid` as the right bound to keep looking for the minimum index [1,1,1,1,2,2,3,3,3,4,4,4,5,5,5,5,5,5,5,6,6,6,6,6,6,7,7,7,8,8,8,8,9,9,9,9,10,10,10,10,10,10,10,10,10,11,11,11,11,12,12,12,13,13,13,13,13,14,14,14,14,14,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,18,18,19,19,19,19,20,20,20,20,20,20,20,20,20] 4 # error occurred when `if not reader or not target:` [0,0,1,1] 0 """ class Solution: """ @param: reader: An instance of ArrayReader. @param: target: An integer @return: An integer which is the first index of target. """ def searchBigSortedArray(self, reader, target): end = 0 while reader.get(end) < target: # `+1` is to avoid `end == 0` end = end * 2 + 1 l, m, r = 0, 0, end while l + 1 < r: m = l + (r - l) // 2 if reader.get(m) < target: l = m else: r = m for i in [l, r]: if reader.get(i) == target: return i return -1 ================================================ FILE: lintcode/450_reverse_nodes_in_k_group.py ================================================ """ Definition for singly-linked list. class ListNode: def __init__(self, x): self.val = x self.next = None """ class Solution: """ @param: head: a ListNode @param: k: An integer @return: a ListNode """ def reverseKGroup(self, head, k): if not head: return dummy = ListNode(0) dummy.next = head head = dummy while head: head = self.reverse_next_kth(head, k) return dummy.next def find_kth(self, head, k): for i in range(k): if not head: return head = head.next return head def reverse(self, head): pre = nxt = None while head: nxt = head.next head.next = pre pre = head head = nxt return pre def reverse_next_kth(self, head, k): nk = self.find_kth(head, k) if not nk: return nk_nxt = nk.next n1_pre = head n1 = head.next nk.next = None self.reverse(n1) n1_pre.next = nk n1.next = nk_nxt return n1 ================================================ FILE: lintcode/453_flatten_binary_tree_to_linked_list.py ================================================ """ Main Concept: For given tree: 1-5-6 =2-4 =3 take postorder traversal, and the visited order will be `3,4,2,1,5,6` 1. rebase the right child with the last right child in left child when visit `2` 1-5-6 =2=3-4 2. move the left child to the right when visit `2` 1-5-6 =2-3-4 3. keep doing (1) and (2) when visit `1` 1-2-3-4-5-6 Definition of TreeNode: class TreeNode: def __init__(self, val): this.val = val this.left, this.right = None, None """ class Solution: """ @param: root: a TreeNode, the root of the binary tree @return: """ def flatten(self, root): if not root: return self.flatten(root.left) self.flatten(root.right) if not root.left: return node = root.left while node.right: node = node.right node.right = root.right root.right = root.left root.left = None ================================================ FILE: lintcode/457_classical_binary_search.py ================================================ class Solution: """ @param: A: An integer array sorted in ascending order @param: target: An integer @return: An integer """ def findPosition(self, A, target): if not A: return -1 left, right = 0, len(A) - 1 while left + 1 < right: mid = (left + right) // 2 if A[mid] == target: return mid if A[mid] < target: left = mid else: right = mid if A[left] == target: return left if A[right] == target: return right return -1 ================================================ FILE: lintcode/458_last_position_of_target.py ================================================ class Solution: """ @param: nums: An integer array sorted in ascending order @param: target: An integer @return: An integer """ def lastPosition(self, nums, target): if not nums or not target: return -1 l, m, r = 0, 0, len(nums) - 1 while l + 1 < r: m = l + (r - l) // 2 if nums[m] > target: r = m else: l = m """ considering the edge case: [1, 1, 1, 1] we've compared all child above but the both ends so we should check the both ends """ if nums[r] == target: return r elif nums[l] == target: return l else: return -1 ================================================ FILE: lintcode/459_closest_number_in_sorted_array.py ================================================ class Solution: """ @param: A: an integer array sorted in ascending order @param: target: An integer @return: an integer """ def closestNumber(self, A, target): if not A or not target: return -1 l, m, r = 0, 0, len(A) - 1 while l + 1 < r: m = l + (r - l) // 2 if A[m] == target: return m elif A[m] > target: r = m else: l = m if A[r] - target > target - A[l]: return l else: return r ================================================ FILE: lintcode/45_maximum_subarray_difference.py ================================================ class Solution: """ @param: A: A list of integers @return: An integer indicate the value of maximum difference between two substrings """ def maxDiffSubArrays(self, A): if not A: return 0 n = len(A) B = [-1 * num for num in A] Lmin = self.get_sum(B, range(n), factor=-1) Lmax = self.get_sum(A, range(n), factor=1) Rmin = self.get_sum(B, range(n - 1, -1, -1), factor=-1) Rmax = self.get_sum(A, range(n - 1, -1, -1), factor=1) ans = float('-inf') for i in range(n - 1): ans = max( ans, Lmax[i] - Rmin[i + 1], Rmax[i + 1] - Lmin[i] ) return ans def get_sum(self, A, scope, factor): """ factor == 1: max sum factor == -1: min sum """ M = [0] * len(A) Smax = float('-inf') S = Smin = 0 for i in scope: S += A[i] if S - Smin > Smax: Smax = S - Smin if S < Smin: Smin = S M[i] = Smax * factor return M ================================================ FILE: lintcode/460_k_closest_numbers_in_sorted_array.py ================================================ class Solution: """ @param: A: an integer array @param: target: An integer @param: k: An integer @return: an integer array """ def kClosestNumbers(self, A, target, k): if not A: return [] n = len(A) """ if `b` in `A`: [a, b, b, b, c] l r else: [a, c, d, e, f] l r """ left, mid, right = 0, 0, n - 1 while left + 1 < right: mid = left + (right - left) // 2 if A[mid] <= target: left = mid else: right = mid """ # handle out of range if `left` less than `0` only append `A[right]` if `right` great than `n - 1` only append `A[left]` # handle closest append first if that `num` is more closer `target` """ ans = [0] * k for i in range(k): if left < 0: ans[i] = A[right] right += 1 elif right >= n: ans[i] = A[left] left -= 1 elif A[right] - target < target - A[left]: ans[i] = A[right] right += 1 else: ans[i] = A[left] left -= 1 return ans ================================================ FILE: lintcode/461_kth_smallest_numbers_in_unsorted_array.py ================================================ class Solution: """ @param: K: An integer @param: A: An integer array @return: kth smallest element """ def kthSmallest(self, K, A): """ the index of `K`th child is `K - 1` """ return self.quick_select(K - 1, A, 0, len(A) - 1) def quick_select(self, k, A, start, end): if start >= end: return A[end] left, right = start, end pivot = A[(start + end) // 2] while left <= right: while left <= right and A[left] < pivot: left += 1 while left <= right and A[right] > pivot: right -= 1 if left <= right: A[left], A[right] = A[right], A[left] left += 1 right -= 1 if start <= k <= right: return self.quick_select(k, A, start, right) elif left <= k <= end: return self.quick_select(k, A, left, end) else: return A[k] ================================================ FILE: lintcode/462_total_occurrence_of_target.py ================================================ class Solution: def totalOccurrence(self, A, target): """ :type A: List[int] :type target: int :rtype: int given A == [a, b, b, b, c] s e using binary searching to find `s` and `e` ans is `e - s + 1` * s: start, e: end, l: left, r: right [a, b, b, b, c] r1 l r,s r2 e,l r """ if not A: return 0 n = len(A) left, mid, right = 0, 0, n - 1 while left + 1 < right: mid = left + (right - left) // 2 if A[mid] < target: left = mid else: right = mid start = left if A[left] == target else right if A[start] != target: return 0 left, mid, right = 0, 0, n - 1 while left + 1 < right: mid = left + (right - left) // 2 if A[mid] <= target: left = mid else: right = mid end = right if A[right] == target else left return end - start + 1 ================================================ FILE: lintcode/465_kth_smallest_sum_in_two_sorted_arrays.py ================================================ import heapq """ Assuming: len(A) == len(B) == 3 To find the result(min of A[i]+B[j]), we got a 3x3 matrix: AB | 0 | 1 | 2 | 0 | a | b | c | 1 | d | e | f | 2 | g | h | i | 1. put the first column (0, j) into heap, these child is the min for each row 2. get the minest one, and move the `x` pointer forward, put the child at (x+1, y) 3. keep find the minest one in current heap, then we can find the kth child """ class Solution: """ @param: A: an integer arrays sorted in ascending order @param: B: an integer arrays sorted in ascending order @param: k: An integer @return: An integer """ def kthSmallestSum(self, A, B, k): m, n = len(A), len(B) ans = j = 0 heap = [] for i in range(min(m, k)): heapq.heappush(heap, (A[i] + B[0], i, 0)) while k > 0: ans = heapq.heappop(heap) j = ans[2] + 1 if j < n: heapq.heappush(heap, (A[ans[1]] + B[j], ans[1], j)) k -= 1 return ans[0] ================================================ FILE: lintcode/469_identical_binary_tree.py ================================================ """ Definition of TreeNode: class TreeNode: def __init__(self, val): this.val = val this.left, this.right = None, None """ class Solution: """ @param: A: the root of binary tree A. @param: B: the root of binary tree B. @return: true if they are identical, or false. """ def isIdentical(self, A, B): if not A and not B: return True if not A or not B: return False if A.val != B.val: return False return ( self.isIdentical(A.left, B.left) and self.isIdentical(A.right, B.right) ) ================================================ FILE: lintcode/471_top_k_frequent_words.py ================================================ class Solution: """ @param: words: an array of string @param: k: An integer @return: an array of string """ def topKFrequentWords(self, words, k): if not words or not k: return [] F = {} for word in words: F[word] = F.get(word, 0) + 1 W = [(freq, word) for word, freq in F.items()] W.sort(key=lambda item: (-item[0], item[1])) return [W[i][1] for i in range(k)] ================================================ FILE: lintcode/472_binary_tree_path_sum_iii.py ================================================ """ Definition of ParentTreeNode: class ParentTreeNode: def __init__(self, val): self.val = val self.parent, self.left, self.right = None, None, None """ class Solution: """ @param: root: the root of binary tree @param: target: An integer @return: all valid paths """ def binaryTreePathSum3(self, root, target): """ 1. using `dfs` to visit every node in that tree 2. once enter a node, start to find the path based on it to parent, left child, and right child. """ ans = [] self.dfs(root, target, ans) return ans def dfs(self, node, target, ans): if not node: return self.find_path(node, node, target, ans, []) self.dfs(node.left, target, ans) self.dfs(node.right, target, ans) def find_path(self, node, start, remaining, ans, path): path.append(node.val) remaining -= node.val if remaining == 0: ans.append(path[:]) if node.parent and node.parent is not start: self.find_path(node.parent, node, remaining, ans, path) if node.left and node.left is not start: self.find_path(node.left, node, remaining, ans, path) if node.right and node.right is not start: self.find_path(node.right, node, remaining, ans, path) path.pop() ================================================ FILE: lintcode/473_add_and_search_word.py ================================================ class Trie: def __init__(self): self.root = {} def insert(self, string): if not string: return parent = self.root for char in string: if char in parent: parent = parent[char] else: parent[char] = {} parent = parent[char] parent['_end'] = True def search(self, string): if not string: return False parent = self.root for char in string: if char in parent: parent = parent[char] else: return False return True def search_in_regex(self, string): if not string: return False return self._search_in_regex(string, self.root, 0) def _search_in_regex(self, string, parent, i): if i == len(string): return parent.get('_end', False) result = False if string[i] == '.': for child in parent: if child[0] != '_' and self._search_in_regex(string, parent[child], i + 1): result = True elif string[i] in parent: if self._search_in_regex(string, parent[string[i]], i + 1): result = True return result class WordDictionary: def __init__(self): self.trie = Trie() """ @param: word: Adds a word into the data structure. @return: nothing """ def addWord(self, word): if not word: return self.trie.insert(word) """ @param: word: A word could contain the dot character '.' to represent any one letter. @return: if the word is in the data structure. """ def search(self, word): if not word: return False return self.trie.search_in_regex(word) # Your WordDictionary object will be instantiated and called as such: # wordDictionary = WordDictionary() # wordDictionary.addWord("word") # wordDictionary.search("pattern") ================================================ FILE: lintcode/474_lowest_common_ancestor_ii.py ================================================ """ The node has an extra attribute parent which point to the father of itself. The root's parent is null. Definition of ParentTreeNode: class ParentTreeNode: def __init__(self, val): self.val = val self.parent, self.left, self.right = None, None, None """ class Solution: def lowestCommonAncestorII(self, root, a, b): """ :type root: ParentTreeNode :type a: ParentTreeNode :type b: ParentTreeNode :rtype: ParentTreeNode """ if not root: return root nodes = set() while a: nodes.add(a) a = a.parent while b: if b in nodes: return b b = b.parent return root ================================================ FILE: lintcode/475_binary_tree_maximum_path_sum_ii.py ================================================ """ Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ class Solution: def maxPathSum2(self, root): """ :type root: TreeNode :rtype: int """ if not root: return 0 left = self.maxPathSum2(root.left) right = self.maxPathSum2(root.right) return root.val + max(0, left, right) ================================================ FILE: lintcode/479_second_max_of_array.py ================================================ class Solution: """ @param: A: An integer array @return: The second max number in the array. """ def secondMax(self, A): if not A: return max1 = max2 = float('-inf') for a in A: if a > max1: max2 = max1 max1 = a continue if a > max2: max2 = a return max2 ================================================ FILE: lintcode/480_binary_tree_paths.py ================================================ """ Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ class Solution: def binaryTreePaths(self, root): """ :type root: TreeNode :rtype: list[str] """ ans = [] if not root: return ans self.dfs(root, ans, []) return ans def dfs(self, node, ans, path): path.append(str(node.val)) if not node.left and not node.right: ans.append('->'.join(path)) path.pop() return if node.left: self.dfs(node.left, ans, path) if node.right: self.dfs(node.right, ans, path) path.pop() ================================================ FILE: lintcode/486_merge_k_sorted_arrays.py ================================================ from heapq import heappop, heappush class Solution: """ @param: G: k sorted integer arrays @return: a sorted array """ def mergekSortedArrays(self, G): ans = [] if not G: return ans heap = [] for i in range(len(G)): if not G[i]: continue heappush(heap, (G[i][0], i, 0)) while heap: num, x, y = heappop(heap) ans.append(num) if y + 1 < len(G[x]): heappush(heap, (G[x][y + 1], x, y + 1)) return ans ================================================ FILE: lintcode/494_implement_stack_by_two_queues.py ================================================ from lintcode import ListNode class Stack: def __init__(self): self.dummy = self.tail = ListNode(-1) """ @param: x: An integer @return: nothing """ def push(self, x): node = ListNode(x) node.pre = self.tail self.tail.nxt = node self.tail = node """ @return: nothing """ def pop(self): if self.isEmpty(): return node = self.tail self.tail = node.pre self.tail.nxt = node.pre = None """ @return: An integer """ def top(self): if self.isEmpty(): return return self.tail.val """ @return: True if the stack is empty """ def isEmpty(self): return self.dummy is self.tail ================================================ FILE: lintcode/496_toy_factory.py ================================================ """ Your object will be instantiated and called as such: ty = ToyFactory() toy = ty.getToy(type) toy.talk() """ class Toy: def talk(self): raise NotImplementedError('This method should have implemented.') class Dog(Toy): def talk(self): print 'Wow' class Cat(Toy): def talk(self): print 'Meow' class ToyFactory: # @param {string} shapeType a string # @return {Toy} Get object of the type def getToy(self, type): if type == 'Dog': return Dog() if type == 'Cat': return Cat() ================================================ FILE: lintcode/497_shape_factory.py ================================================ """ Your object will be instantiated and called as such: sf = ShapeFactory() shape = sf.getShape(shapeType) shape.draw() """ class Shape: def draw(self): raise NotImplementedError('This method should have implemented.') class Triangle(Shape): def draw(self): print ' /\\' print ' / \\' print '/____\\' class Rectangle(Shape): def draw(self): print ' ----' print '| |' print ' ----' class Square(Shape): def draw(self): print ' ----' print '| |' print '| |' print ' ----' class ShapeFactory: # @param {string} shapeType a string # @return {Shape} Get object of type Shape def getShape(self, shapeType): if shapeType == 'Triangle': return Triangle() if shapeType == 'Rectangle': return Rectangle() if shapeType == 'Square': return Square() ================================================ FILE: lintcode/498_parking_lot.py ================================================ VEHICLE_ID = { 'MOTOCYCLE': 'MOTOCYCLE', 'CAR': 'CAR', 'BUS': 'BUS', } class Vehicle: def __init__(self): self.type = '' self.costs = 0 self.at_level = None self.at_spots = None def unpark(self): if not self.at_level: return for x, y in self.at_spots: self.at_level.spots[x, y] = None self.at_level = None self.at_spots = None class Motorcycle(Vehicle): def __init__(self): self.type = VEHICLE_ID['MOTOCYCLE'] self.costs = 1 class Car(Vehicle): def __init__(self): self.type = VEHICLE_ID['CAR'] self.costs = 1 class Bus(Vehicle): def __init__(self): self.type = VEHICLE_ID['BUS'] self.costs = 5 class Level: def __init__(self, id, m, n): self.id = id self.m = m self.n = n self.spots = {} def get_range(self, vehicle_type): quarter = self.n // 4 if vehicle_type == VEHICLE_ID['BUS']: return range(quarter * 3, self.n) if vehicle_type == VEHICLE_ID['CAR']: return range(quarter, self.n) return range(self.n) def park_vehicle(self, vehicle): """ :type vehicle: Vehicle :rtype: bool """ RANGE = self.get_range(vehicle.type) for x in range(self.m): gotcha = 0 for y in RANGE: if self.spots.get((x, y)): gotcha = 0 continue gotcha += 1 if gotcha == vehicle.costs: spots = [] for i in range(y, y - gotcha, -1): spots.append((x, i)) self.spots[x, i] = vehicle vehicle.at_level = self vehicle.at_spots = spots return True return False class ParkingLot: def __init__(self, k, m, n): """ :type k: int, number of levels :type m: int, each level has m rows of spots :type n: int, each row has n spots """ self.levels = [Level(i, m, n) for i in range(k)] def park_vehicle(self, vehicle): """ :type vehicle: Vehicle :rtype: bool """ if any( level.park_vehicle(vehicle) for level in self.levels ): return True return False def unpark_vehicle(self, vehicle): """ :type vehicle: Vehicle """ vehicle.unpark() ================================================ FILE: lintcode/499_word_count.py ================================================ class WordCount: # @param {str} line a text, for example "Bye Bye see you next" def mapper(self, _, line): for word in line.split(): yield word, 1 # @param key is from mapper # @param values is a set of value with the same key def reducer(self, key, values): yield key, sum(values) ================================================ FILE: lintcode/4_ugly_number_ii.py ================================================ """ Main Concept: 1. start from 1 => `ans = [1]` 2. use `i2, i3, i5` to record current used in `ans` 3. if the candidates is not ahead `ans[-1]`, move forward 4. append the minimum candidate to `ans` """ class Solution: def nthUglyNumber(self, n): """ :type n: int :rtype: int """ if not n: return 0 ans = [1] i2 = i3 = i5 = 0 for _ in range(1, n): while ans[i2] * 2 <= ans[-1]: i2 += 1 while ans[i3] * 3 <= ans[-1]: i3 += 1 while ans[i5] * 5 <= ans[-1]: i5 += 1 ans.append(min(( ans[i2] * 2, ans[i3] * 3, ans[i5] * 5, ))) return ans[-1] ================================================ FILE: lintcode/500_inverted_index.py ================================================ ''' Definition of Document class Document: def __init__(self, id, cotent): self.id = id self.content = content ''' class Solution: # @param {Document[]} docs a list of documents # @return {dict(string, int[])} an inverted index def invertedIndex(self, docs): if not docs or len(docs) < 1: return {} result = {} for doc in docs: if not doc.content: continue for word in doc.content.split(): if word not in result: result[word] = [] if doc.id not in result[word]: result[word].append(doc.id) return result ================================================ FILE: lintcode/501_mini_twitter.py ================================================ """ Definition of Tweet: class Tweet: @classmethod def create(cls, user_id, tweet_text): # This will create a new tweet object, # and auto fill id """ class MiniTwitter: def __init__(self): self.timestamp = 0 self.tweets = {} self.followings = {} """ @param: user_id: An integer @param: tweet_text: a string @return: a tweet """ def postTweet(self, user_id, tweet_text): if user_id not in self.tweets: self.tweets[user_id] = [] self.timestamp += 1 self.tweets[user_id].append(( self.timestamp, Tweet.create(user_id, tweet_text), )) return self.tweets[user_id][-1][1] """ @param: user_id: An integer @return: a list of 10 new feeds recently and sort by timeline """ def getNewsFeed(self, user_id): res = [] if user_id in self.tweets: res.extend(self.tweets[user_id][-10:]) if user_id in self.followings: for follow_id in self.followings[user_id]: if follow_id in self.tweets: res.extend(self.tweets[follow_id][-10:]) if not res: return [] res.sort() return [tweet for _, tweet in res[-10:]][::-1] """ @param: user_id: An integer @return: a list of 10 new posts recently and sort by timeline """ def getTimeline(self, user_id): if user_id not in self.tweets: return [] return [tweet for _, tweet in self.tweets[user_id][-10:]][::-1] """ @param: from_id: An integer @param: to_id: An integer @return: nothing """ def follow(self, from_id, to_id): if from_id not in self.followings: self.followings[from_id] = set() if to_id in self.followings[from_id]: return self.followings[from_id].add(to_id) """ @param: from_id: An integer @param: to_id: An integer @return: nothing """ def unfollow(self, from_id, to_id): if from_id not in self.followings: return if to_id not in self.followings[from_id]: return self.followings[from_id].discard(to_id) ================================================ FILE: lintcode/502_mini_cassandra.py ================================================ """ Definition of Column: class Column: def __init__(self, key, value): self.key = key self.value = value """ class MiniCassandra: storage = {} """ @param: raw_key: a string @param: column_key: An integer @param: column_value: a string @return: nothing """ def insert(self, raw_key, column_key, column_value): if raw_key not in self.storage: self.storage[raw_key] = {} self.storage[raw_key][column_key] = Column(column_key, column_value) """ @param: raw_key: a string @param: column_start: An integer @param: column_end: An integer @return: a list of Columns """ def query(self, raw_key, column_start, column_end): if raw_key not in self.storage: return [] result = [ column for column_key, column in self.storage[raw_key].items() if column_start <= column_key <= column_end ] return sorted(result, key=lambda column: column.key) ================================================ FILE: lintcode/503_anagram_map_reduce.py ================================================ class Anagram: # @param {str} line a text, for example "Bye Bye see you next" def mapper(self, _, line): for word in line.split(): yield ''.join(sorted(word.lower())), word # @param key is from mapper # @param values is a set of value with the same key def reducer(self, key, values): yield key, values ================================================ FILE: lintcode/504_inverted_index_map_reduce.py ================================================ ''' Definition of Document class Document: def __init__(self, id, cotent): self.id = id self.content = content ''' class InvertedIndex: # @param {Document} value is a document def mapper(self, _, value): for word in value.content.split(): yield word, value.id # @param key is from mapper # @param values is a set of value with the same key def reducer(self, key, values): yield key, sorted(list(set(values))) ================================================ FILE: lintcode/505_web_logger.py ================================================ class WebLogger: def __init__(self): dummy = [] dummy[:] = [dummy, dummy, -1] self.dummy = dummy self.size = 0 """ @param: timestamp: An integer @return: nothing """ def hit(self, timestamp): tail = self.dummy[0] tail[1] = self.dummy[0] = _n = [None, None, timestamp] _n[0] = tail _n[1] = self.dummy self.size += 1 """ @param: timestamp: An integer @return: An integer """ def get_hit_count_in_last_5_minutes(self, timestamp): head = self.dummy[1] while (head is not self.dummy and head[2] + 300 <= timestamp): _, nxt, _ = head self.dummy[1] = nxt nxt[0] = self.dummy head[0] = head[1] = None head = nxt self.size -= 1 return self.size ================================================ FILE: lintcode/509_mini_yelp.py ================================================ """ Test Case: addRestaurant("Lint Cafe", 12.4999999, 11.599999) addRestaurant("Code Cafe", 10.4999999, 11.512109) neighbors(10.5, 11.6, 6.7) removeRestaurant(1) addRestaurant("Cafe2", 11.4999999, 11.599999) addRestaurant("Cafe3", 12.4999999, 11.512109) neighbors(10.5, 13.6, 8896.7) neighbors(8.5, 11.6, 6996.7) addRestaurant("Cafe4", 11.4999999, 11.599999) addRestaurant("Cafe5", 12.4999999, 78.512109) removeRestaurant(3) neighbors(8.5, 70.6, 3200.7) : if `k` > the maximum error, return all hashcodes """ """ Definition of Location: class Location: # @param {double} latitude, longitude # @param {Location} @classmethod def create(cls, latitude, longitude): # This will create a new location object Definition of Restaurant: class Restaurant: # @param {str} name # @param {Location} location # @return {Restaurant} @classmethod def create(cls, name, location): # This will create a new restaurant object, # and auto fill id Definition of Helper class Helper: # @param {Location} location1, location2 @classmethod def get_distance(cls, location1, location2): # return calculate the distance between two location Definition of GeoHash class GeoHash: # @param {Location} location # @return a string @classmethom def encode(cls, location): # return convert location to a geohash string # @param {str} hashcode # @return {Location} @classmethod def decode(cls, hashcode): # return convert a geohash string to location """ """ range query from list by `bisect` """ import bisect from YelpHelper import Location, Restaurant, GeoHash, Helper class MiniYelp: ERROR_IN_KM = ( 2500, 630, 78, 20, 2.4, 0.61, 0.076, 0.01911, 0.00478, 0.0005971, 0.0001492, 0.0000186 ) restaurants = {} restr_to_geohash = {} geohashs = [] # @param {str} name # @param {Location} location # @return {int} restaurant's id def add_restaurant(self, name, location): restaurant = Restaurant.create(name, location) hashcode = self.get_restr_hashcode(restaurant) self.restaurants[hashcode] = restaurant self.restr_to_geohash[restaurant.id] = hashcode bisect.insort(self.geohashs, hashcode) return restaurant.id # @param {int} restaurant_id # @return nothing def remove_restaurant(self, restaurant_id): hashcode = self.restr_to_geohash[restaurant_id] index = bisect.bisect_left(self.geohashs, hashcode) self.geohashs.pop(index) del self.restaurants[hashcode] del self.restr_to_geohash[restaurant_id] # @param {Location} location # @param {double} k, distance smaller than k miles # @return {str[]} a list of restaurant's name and sort by # distance from near to far. def neighbors(self, location, k): length = self.get_length(k) prefix = GeoHash.encode(location)[:length] # chr(ord('z') + 1) == '{' left = bisect.bisect_left(self.geohashs, prefix) right = bisect.bisect(self.geohashs, prefix + '{') neighbors = [] hashcode = restaurant = distance = None for i in range(left, right): hashcode = self.geohashs[i] restaurant = self.restaurants[hashcode] distance = Helper.get_distance(location, restaurant.location) if distance <= k: neighbors.append((distance, restaurant)) neighbors.sort(key=lambda item: item[0]) return [ restr.name for _, restr in neighbors ] def get_length(self, k): n = len(self.ERROR_IN_KM) for i in range(n): if k > self.ERROR_IN_KM[i]: return i return n def get_restr_hashcode(self, restaurant): return '{0}:{1}'.format( GeoHash.encode(restaurant.location), restaurant.id ) """ trie """ from YelpHelper import Location, Restaurant, GeoHash, Helper class Trie: def __init__(self): self.root = self._new_node() def __repr__(self): return repr(self.root) def put(self, key): if not key: return parent = self.root parent['keys'].add(key) for char in key: if char not in parent['children']: parent['children'][char] = self._new_node() parent['children'][char]['keys'].add(key) parent = parent['children'][char] def pick(self, key): if not key: return parent = self.root parent['keys'].discard(key) for char in key: if char not in parent['children']: return parent = parent['children'][char] parent['keys'].discard(key) def get_keys_by_prefix(self, prefix): parent = self.root if not prefix: return list(parent['keys']) for char in prefix: if char not in parent['children']: return [] parent = parent['children'][char] return list(parent['keys']) def _new_node(self): return { 'keys': set(), 'children': {} } class MiniYelp: ERROR_IN_KM = ( 2500, 630, 78, 20, 2.4, 0.61, 0.076, 0.01911, 0.00478, 0.0005971, 0.0001492, 0.0000186 ) trie = Trie() restaurants = {} restr_to_geohash = {} # @param {str} name # @param {Location} location # @return {int} restaurant's id def add_restaurant(self, name, location): restaurant = Restaurant.create(name, location) hashcode = self.get_restr_hashcode(restaurant) self.restaurants[hashcode] = restaurant self.restr_to_geohash[restaurant.id] = hashcode self.trie.put(hashcode) return restaurant.id # @param {int} restaurant_id # @return nothing def remove_restaurant(self, restaurant_id): hashcode = self.restr_to_geohash[restaurant_id] del self.restaurants[hashcode] del self.restr_to_geohash[restaurant_id] self.trie.pick(hashcode) # @param {Location} location # @param {double} k, distance smaller than k miles # @return {str[]} a list of restaurant's name and sort by # distance from near to far. def neighbors(self, location, k): length = self.get_length(k) prefix = GeoHash.encode(location)[:length] hashcodes = self.trie.get_keys_by_prefix(prefix) neighbors = [] restaurant = distance = None for hashcode in hashcodes: restaurant = self.restaurants[hashcode] distance = Helper.get_distance(location, restaurant.location) if distance <= k: neighbors.append((distance, restaurant)) neighbors.sort(key=lambda item: item[0]) return [ restr.name for _, restr in neighbors ] def get_length(self, k): n = len(self.ERROR_IN_KM) for i in range(n): if k > self.ERROR_IN_KM[i]: return i return n def get_restr_hashcode(self, restaurant): return '{0}:{1}'.format( GeoHash.encode(restaurant.location), restaurant.id ) ================================================ FILE: lintcode/510_maximal_rectangle.py ================================================ class Solution: def maximalRectangle(self, G): """ :type G: List[List[str]] :rtype: int """ ans = 0 if not G or not G[0]: return ans m, n = len(G), len(G[0]) L, R, H = {}, {}, {} for i in range(m): curr = 0 # left boundary for j in range(n): if G[i][j] == '1': H[j] = H.get(j, 0) + 1 L[j] = max(L.get(j, 0), curr) else: H[j] = L[j] = 0 curr = j + 1 curr = n # right boundary for j in range(n - 1, -1, -1): if G[i][j] == '1': R[j] = min(R.get(j, n), curr) else: R[j] = n curr = j ans = max( ans, H[j] * (R[j] - L[j]) ) return ans """ Assuming matrix: [ [1, 1, 0, 0, 1], [0, 1, 0, 0, 1], [0, 0, 1, 1, 1], [0, 0, 1, 1, 1], [0, 0, 0, 0, 1] ] Concept: For each (i, j), 0 <= i < len(mat), 0 <= j < len(mat[0]) the rectangle area is (r[j] - l[j]) * h[j] m = 0 v 1 1 0 0 1 h 1 1 0 0 1 l 0 0 0 0 4 r 2 2 5 5 5 m = 1 v 0 1 0 0 1 h 0 2 0 0 2 l 0 1 0 0 4 r 5 2 5 5 5 m = 2 v 0 0 1 1 1 h 0 0 1 1 3 l 0 0 2 2 4 r 5 5 5 5 5 m = 3 v 0 0 1 1 1 h 0 0 2 2 4 l 0 0 2 2 4 r 5 5 5 5 5 m = 4 v 0 0 0 0 1 h 0 0 0 0 5 l 0 0 0 0 4 r 5 5 5 5 5 max = 6 = (5 - 2) * 2 """ # Mono Stack # This problem could be treated as histogram, see lintcode#122 class Solution: def maximalRectangle(self, G): """ :type G: List[List[str]] :rtype: int """ ans = 0 if not G or not G[0]: return ans m, n = len(G), len(G[0]) H = [0] * n for i in range(m): for j in range(n): if G[i][j] == '1': H[j] += 1 else: H[j] = 0 ans = max(ans, self.largestRectangleArea(H)) # To remove the trick `0` H.pop() return ans def largestRectangleArea(self, H): area = 0 if not H: return area # To ensure the last element in monostack will be handled H.append(0) I = [] left = height = 0 for right in range(len(H)): while I and H[I[-1]] >= H[right]: height = H[I.pop()] left = I[-1] if I else -1 area = max( area, height * (right - left - 1) ) I.append(right) return area ================================================ FILE: lintcode/512_decode_ways.py ================================================ class Solution: """ @param: s: a string, encoded message @return: an integer, the number of ways decoding """ def numDecodings(self, s): if not s or s == '0': return 0 n = len(s) """ `dp[i]` means the ways to decode `dp[0]` => '' `dp[1]` => should check if the code is 0 """ dp = [0] * (n + 1) dp[0] = 1 dp[1] = 0 if s[0] == '0' else 1 for i in range(2, n + 1): if s[i - 1] != '0': dp[i] += dp[i - 1] if 10 <= int(s[i - 2:i]) <= 26: dp[i] += dp[i - 2] return dp[n] ================================================ FILE: lintcode/513_perfect_squares.py ================================================ """ Test Case: 1 """ class Solution: """ @param: n: a positive integer @return: An integer """ def numSquares(self, n): if n <= 0: return 0 INFINITY = float('inf') # `dp[i]` means the least number of perfect square numbers of `i` dp = [INFINITY] * (n + 1) dp[0] = 0 dp[1] = 1 for i in range(1, n + 1): j = 1 while j * j <= i: dp[i] = min(dp[i], dp[i - j * j] + 1) j += 1 return dp[n] ================================================ FILE: lintcode/515_paint_house.py ================================================ class Solution: """ @param: costs: n x 3 cost matrix @return: An integer, the minimum cost to paint all houses """ def minCost(self, costs): if not costs: return 0 INFINITY = float('inf') m, n = len(costs), len(costs[0]) dp = [[0] * n for _ in range(2)] """ i: `i`th house j: `j`th color k: the used `k`th color in previous house """ i = j = k = prev = curr = 0 for j in range(n): dp[0][j] = costs[0][j] for i in range(1, m): prev = curr # (i - 1) % 2 curr = i % 2 for j in range(n): dp[curr][j] = INFINITY for k in range(n): if k != j and dp[prev][k] + costs[i][j] < dp[curr][j]: dp[curr][j] = dp[prev][k] + costs[i][j] """ curr == (m - 1) % 2 """ return min(dp[curr]) ================================================ FILE: lintcode/516_paint_house_ii.py ================================================ """ in `./lintcode/515_paint_house.py` we repeatedly iterate `dp[prev]` to find the minimum except the same color but in doing so, the time complexity is quite high `O(n * k * k)`, if the number of colors increases so in this problem, we need find some way to fix it for `dp[curr][j] = dp[prev][k] + C[i][j]`, `C[i][j]` is constant so what we want is to find the minimum except the same color => if `dp[prev][j]` is the minimum, since we can't use the same color in adjacency houses, so we use the second minimum => if not, we use the minimum and the time complexity becomes `O(n * 2k)` => `O(nk)` """ class Solution: """ @param: C: n x k cost matrix @return: an integer, the minimum cost to paint all houses """ def minCostII(self, C): if not C or not C[0]: return 0 INFINITY = float('inf') n, k = len(C), len(C[0]) dp = [[0] * k for _ in range(n)] prev = curr = 0 for j in range(k): dp[curr][j] = C[0][j] for i in range(1, n): prev = curr curr = 1 - curr min1 = min2 = INFINITY """ to find the minimum and second minimum in previous iteration """ for j in range(k): if dp[prev][j] < min1: min2 = min1 min1 = dp[prev][j] continue if dp[prev][j] < min2: min2 = dp[prev][j] """ if the `j`th color has been used, that is, `dp[prev][j]` was the minimum, in previous iteration and then we need to take the color with the second minimum in `dp[prev]` """ for j in range(k): dp[curr][j] = C[i][j] if dp[prev][j] == min1: dp[curr][j] += min2 else: dp[curr][j] += min1 return min(dp[curr]) ================================================ FILE: lintcode/517_ugly_number.py ================================================ class Solution: def isUgly(self, num): """ :type num: int :rtype: bool """ if not num: return False if num == 1: return True for factor in ( 125, 27, 8, 5, 3, 2, ): while num % factor == 0: num //= factor if num == 1: return True return num == 1 ================================================ FILE: lintcode/518_super_ugly_number.py ================================================ class Solution: def nthSuperUglyNumber(self, n, primes): """ :type n: int :type primes: list[int] :rtype: int """ if not n or n <= 1 or not primes: return 1 k = len(primes) # i -> same as `i` in `primes`, v -> track of how far `primes[i]` stay in `uglys` steps = [0] * k uglys = [0] * n uglys[0] = 1 for i in range(1, n): ugly = float('inf') for j in range(k): ugly = min(ugly, uglys[steps[j]] * primes[j]) uglys[i] = ugly for j in range(k): if uglys[steps[j]] * primes[j] == ugly: steps[j] += 1 return uglys[n - 1] ================================================ FILE: lintcode/519_consistent_hashing.py ================================================ class Solution: """ @param: n: a positive integer @return: n x 3 matrix """ def consistentHashing(self, n): res = [[0, 359, 1]] if not isinstance(n, int) \ or n < 2: return res ti = 0 # ti: target_index for the **upcoming** machine in results # mi: machine_index for the **upcoming** machines in results # for n is 5: got [1, 2, 3, 4] for mi in range(1, n): ti = 0 # emi: existing_machine_index for the **existing** machines in results # for n is 5 and will add last machine: got [0, 1, 2, 3] for emi in range(mi): # Before adding each machine, check the current maximum partition if res[emi][1] - res[emi][0] > res[ti][1] - res[ti][0]: ti = emi x, y = res[ti][0], res[ti][1] res[ti][1] = (x + y) / 2 res.append([(x + y) / 2 + 1, y, mi + 1]) return sorted(res, key=lambda item: item[0]) ================================================ FILE: lintcode/51_previous_permutation.py ================================================ """ Main Concept: 1. from end to start, finding first increasing element, which is `nums[i]` 2. from end to start, finding first element just smaller than `nums[i]`, which is `nums[j]`. and swap `nums[i]` and `nums[j]`. 3. reverse the elements between `nums[i + 1]` and `nums[n - 1]` REF: [Next Permutation](https://leetcode.com/articles/next-permutation/) """ class Solution: def previousPermuation(self, nums): """ :type nums: list[int] :rtype: list[int] """ if not nums or len(nums) < 2: return nums n = len(nums) i = n - 2 while i >= 0 and nums[i] <= nums[i + 1]: i -= 1 if i >= 0: j = n - 1 while i < j and nums[i] <= nums[j]: j -= 1 nums[i], nums[j] = nums[j], nums[i] i = i + 1 j = n - 1 while i < j: nums[i], nums[j] = nums[j], nums[i] i += 1 j -= 1 return nums ================================================ FILE: lintcode/520_consistent_hashing_ii.py ================================================ import bisect import random class Solution: """ @param {int} n a positive integer @param {int} k a positive integer @return {Solution} a Solution object """ @classmethod def create(cls, n, k): solution = cls() solution.n = n solution.k = k solution.p2l = {} # point to location solution.l2p = {} # location to points return solution """ @param: machine_id: An integer @return: a list of shard ids """ def addMachine(self, machine_id): item = self.l2p[machine_id] = [] point = -1 for i in range(self.k): point = random.randint(0, self.n - 1) while point in self.p2l: point = random.randint(0, self.n - 1) self.p2l[point] = machine_id item.append(point) item.sort() return item """ @param: hashcode: An integer @return: A machine id """ def getMachineIdByHashCode(self, hashcode): points = sorted(self.p2l.keys()) index = bisect.bisect_left(points, hashcode) % len(points) # # counterclockwise # index = bisect.bisect(points, hashcode) - 1 # if index < 0: # index = len(points) - 1 return self.p2l[points[index]] ================================================ FILE: lintcode/521_remove_duplicate_numbers_in_array.py ================================================ """ time: O(n) space: O(n) """ class Solution: """ @param: nums: an array of integers @return: the number of unique integers """ def deduplication(self, nums): ans = 0 if not nums: return ans exists = set() for i in range(len(nums)): if nums[i] not in exists: exists.add(nums[i]) ans += 1 return ans """ time: O(nlogn) space: O(1) """ class Solution: """ @param: nums: an array of integers @return: the number of unique integers """ def deduplication(self, nums): ans = 0 if not nums: return ans nums.sort() # for `nums[0]` ans = 1 for i in range(1, len(nums)): if nums[i - 1] != nums[i]: nums[ans] = nums[i] ans += 1 return ans ================================================ FILE: lintcode/522_tiny_url_ii.py ================================================ import random class TinyUrl2: def __init__(self): self.chars = [str(i) for i in range(10)] self.chars.extend(chr(i) for i in range(ord('a'), ord('z') + 1)) self.chars.extend(chr(i) for i in range(ord('A'), ord('Z') + 1)) self.host = 'http://tiny.url/' self.size = 6 self.lg2st = {} self.st2lg = {} self.custom_lg2st = {} self.custom_st2lg = {} def createCustom(self, url, key): """ :type url: str :type key: str :rtype: str """ if not url or not key: return 'error' if ( url in self.custom_lg2st and key in self.custom_st2lg ): return self.get_tiny_url(key) if ( url not in self.custom_lg2st and key not in self.custom_st2lg ): self.custom_lg2st[url] = key self.custom_st2lg[key] = url return self.get_tiny_url(key) return 'error' def longToShort(self, url): """ :type url: str :rtype: str """ if not url: return 'error' if url in self.lg2st: return self.get_tiny_url(self.lg2st[url]) if url in self.custom_lg2st: return self.get_tiny_url(self.custom_lg2st[url]) key = self.get_hash_key(self.size) while key in self.st2lg: key = self.get_hash_key(self.size) self.lg2st[url] = key self.st2lg[key] = url return self.get_tiny_url(key) def shortToLong(self, url): """ :type url: str :rtype: str """ if not url: return 'error' key = url.replace(self.host, '') if key in self.st2lg: return self.st2lg[key] if key in self.custom_st2lg: return self.custom_st2lg[key] return 'error' def get_tiny_url(self, hash_key): return '{}{}'.format(self.host, hash_key) def get_hash_key(self, size): return ''.join( random.choice(self.chars) for _ in range(size) ) ================================================ FILE: lintcode/523_url_parser.py ================================================ import re class HtmlParser: """ @param: content: content source code @return: a list of links """ def parseUrls(self, content): # r'': use raw string # [=\s]+: this block should contain `=` or blank, and at least one char here # ["\']: this block should contain `"` or `'` # [^"\'>\s]*: this block should contain any chars until `"`, `'`, `>`, or blank appears links = re.findall(r'href[=\s]+["\']([^"\'>\s]*)', content, re.I) return [link for link in links if link and not link.startswith('#')] ================================================ FILE: lintcode/525_mini_uber.py ================================================ """ Definition of Trip: class Trip: self.id; # trip's id, primary key self.driver_id, self.rider_id; # foreign key self.lat, self.lng; # pick up location def __init__(self, rider_id, lat, lng): Definition of Helper class Helper: @classmethod def get_distance(cls, lat1, lng1, lat2, lng2): # return calculate the distance between (lat1, lng1) and (lat2, lng2) """ from Trip import Trip, Helper class MiniUber: driver_to_locs = {} driver_to_trip = {} INFINITY = float('inf') # @param {int} driver_id an integer # @param {double} lat, lng driver's location # return {trip} matched trip information # if there have matched rider or null def report(self, driver_id, lat, lng): if not driver_id: return if driver_id in self.driver_to_trip: return self.driver_to_trip[driver_id] if driver_id in self.driver_to_locs: self.driver_to_locs[driver_id]['lat'] = lat self.driver_to_locs[driver_id]['lng'] = lng else: self.driver_to_locs[driver_id] = self._new_location(lat, lng) # @param rider_id an integer # @param lat, lng rider's location # return a trip def request(self, rider_id, lat, lng): if not rider_id: return trip = Trip(rider_id, lat, lng) _distance = distance = self.INFINITY driver_id = -1 for _driver_id, _loc in self.driver_to_locs.items(): _distance = Helper.get_distance(_loc['lat'], _loc['lng'], lat, lng) if _distance < distance: driver_id = _driver_id distance = _distance if driver_id == -1: return trip trip.driver_id = driver_id self.driver_to_trip[driver_id] = trip del self.driver_to_locs[driver_id] return trip def _new_location(self, lat, lng): return { 'lat': lat, 'lng': lng } ================================================ FILE: lintcode/526_load_balancer.py ================================================ import random class LoadBalancer: def __init__(self): self.servers = [] self.svr2idx = {} """ @param: server_id: add a new server to the cluster @return: nothing """ def add(self, server_id): self.servers.append(server_id) self.svr2idx[server_id] = len(self.servers) - 1 """ @param: server_id: server_id remove a bad server from the cluster @return: nothing """ def remove(self, server_id): svrs = self.servers i = self.svr2idx[server_id] key = svrs[-1] # swap `svrs[-1]` and `svrs[i]` self.svr2idx[key] = i svrs[i] = svrs[-1] svrs.pop() del self.svr2idx[server_id] """ @return: pick a server in the cluster randomly with equal probability """ def pick(self): i = random.randrange(len(self.servers)) return self.servers[i] ================================================ FILE: lintcode/527_trie_serialization.py ================================================ """ Definition of TrieNode: class TrieNode: def __init__(self): # : self.children = collections.OrderedDict() """ class Solution: ''' @param root: An object of TrieNode, denote the root of the trie. This method will be invoked first, you should design your own algorithm to serialize a trie which denote by a root node to a string which can be easily deserialized by your own "deserialize" method later. ''' ''' For `>c<>d>>>`, the visual graph of the return data just like this: < a< b< e<> > c<> d< f<> > > > ''' def serialize(self, root): if not root: return '' data = '' for key, node in root.children.items(): data += key + self.serialize(node) return '<%s>' % data ''' @param data: A string serialized by your serialize method. This method will be invoked second, the argument data is what exactly you serialized at method "serialize", that means the data is not given by system, it's given by your own serialize method. So the format of data is designed by yourself, and deserialize it here as you serialize it in "serialize" method. ''' def deserialize(self, data): if not data \ or data[0] != '<' \ or data[-1] != '>' \ or len(data) < 1: return root = TrieNode() current = root queue = [] for char in data: if char == '<': queue.append(current) elif char == '>': queue.pop() else: current = TrieNode() queue[-1].children[char] = current return root ================================================ FILE: lintcode/528_flatten_nested_list_iterator.py ================================================ """ Your NestedIterator object will be instantiated and called as such: i, v = NestedIterator(nestedList), [] while i.hasNext(): v.append(i.next()) This is the interface that allows for creating nested lists. You should not implement it, or speculate about its implementation class NestedInteger(object): def isInteger(self): # @return {boolean} True if this NestedInteger holds a single integer, # rather than a nested list. def getInteger(self): # @return {int} the single integer that this NestedInteger holds, # if it holds a single integer # Return None if this NestedInteger holds a nested list def getList(self): # @return {NestedInteger[]} the nested list that this NestedInteger holds, # if it holds a nested list # Return None if this NestedInteger holds a single integer """ class NestedIterator(object): def __init__(self, nestedList): self.stack = [[nestedList, 0]] # @return {int} the next element in the iteration def next(self): if not self.hasNext(): return -1 lst, i = self.stack[-1] self.stack[-1][1] += 1 return lst[i].getInteger() # @return {boolean} true if the iteration has more element or false def hasNext(self): stack = self.stack while stack: lst, i = stack[-1] if i >= len(lst): stack.pop() elif lst[i].isInteger(): return True else: # lst[i] is list stack[-1][1] += 1 stack.append([lst[i].getList(), 0]) return False ================================================ FILE: lintcode/529_geohash.py ================================================ """ main concept is in `../module/geohash.py` """ class GeoHash: base32 = [] """ @param: latitude: one of a location coordinate pair @param: longitude: one of a location coordinate pair @param: precision: an integer between 1 to 12 @return: a base32 string """ def encode(self, latitude, longitude, precision=5): if not self.base32: self.base32 = self.get_base32_list() times = (precision * 5) // 2 + 1 lat_codes = self._loc_to_bins( latitude, times, -90, 90) lng_codes = self._loc_to_bins(longitude, times, -180, 180) bin_codes = [] for i in range(times): bin_codes.extend((str(lng_codes[i]), str(lat_codes[i]))) hash_codes = [] hash_code = '' for i in range(0, len(bin_codes), 5): hash_code = int(''.join(bin_codes[i : i + 5]), 2) hash_codes.append(self.base32[hash_code]) return ''.join(hash_codes[:precision]) def _loc_to_bins(self, location, times, left, right): mid = 0 bins = [] for i in range(times): mid = left + (right - left) / 2.0 if location > mid: left = mid bins.append(1) else: right = mid bins.append(0) return bins def get_base32_list(self): base32_list = [str(i) for i in range(10)] ignored_char = (ord('a'), ord('i'), ord('l'), ord('o')) for i in range(ord('a'), ord('z') + 1): if i in ignored_char: continue base32_list.append(chr(i)) return base32_list ================================================ FILE: lintcode/52_next_permutation.py ================================================ """ Main Concept: 1. from end to start, finding first decreasing element, which is `nums[i]` 2. from end to start, finding first element just larger than `nums[i]`, which is `nums[j]`. and swap `nums[i]` and `nums[j]`. 3. reverse the elements between `nums[i + 1]` and `nums[n - 1]` REF: [Next Permutation](https://leetcode.com/articles/next-permutation/) """ class Solution: def nextPermutation(self, nums): """ :type nums: list[int] :rtype: list[int] """ if not nums or len(nums) < 2: return nums n = len(nums) i = n - 2 while i >= 0 and nums[i] >= nums[i + 1]: i -= 1 if i >= 0: j = n - 1 while i < j and nums[i] >= nums[j]: j -= 1 nums[i], nums[j] = nums[j], nums[i] i = i + 1 j = n - 1 while i < j: nums[i], nums[j] = nums[j], nums[i] i += 1 j -= 1 return nums ================================================ FILE: lintcode/530_geohash_ii.py ================================================ """ main concept is in `../module/geohash.py` """ class GeoHash: base32 = [] """ @param: geohash: geohash a base32 string @return: latitude and longitude a location coordinate pair """ def decode(self, geohash): if not geohash: return [] if not self.base32: self.base32 = self.get_base32_list() bin_codes = [] for char in geohash: if char not in self.base32: return [] bin_codes.extend(self._oct_to_bins(self.base32.index(char))) n = len(bin_codes) lat_codes = [bin_codes[i] for i in range(1, n, 2)] lng_codes = [bin_codes[i] for i in range(0, n, 2)] return [ self._bins_to_loc(lat_codes, -90, 90), self._bins_to_loc(lng_codes, -180, 180) ] def _bins_to_loc(self, bins, left, right): mid = 0 for code in bins: mid = left + (right - left) / 2.0 if code: left = mid else: right = mid return left + (right - left) / 2.0 def _oct_to_bins(self, val_in_oct): bins = [] for i in range(5): if val_in_oct % 2: bins.append(1) else: bins.append(0) val_in_oct = val_in_oct >> 1 return reversed(bins) def get_base32_list(self): base32_list = [str(i) for i in range(10)] ignored_char = (ord('a'), ord('i'), ord('l'), ord('o')) for i in range(ord('a'), ord('z') + 1): if i in ignored_char: continue base32_list.append(chr(i)) return base32_list ================================================ FILE: lintcode/531_six_degrees.py ================================================ """ Definition for Undirected graph node class UndirectedGraphNode: def __init__(self, x): self.label = x self.neighbors = [] """ class Solution: """ @param: graph: a list of Undirected graph node @param: s: Undirected graph node @param: t: Undirected graph nodes @return: an integer """ def sixDegrees(self, graph, s, t): if not graph or not s or not t: return -1 if s is t: return 0 degree = {s: 0} queue = [s] for node in queue: for _node in node.neighbors: if _node in degree: continue degree[_node] = degree[node] + 1 if _node is t: return degree[_node] queue.append(_node) return -1 ================================================ FILE: lintcode/532_reverse_pairs.py ================================================ class Solution: """ @param: A: an array @return: total of reverse pairs """ def reversePairs(self, A): n = len(A) tmp = [0] * n return self.merge_sort(A, 0, n - 1, tmp) def merge_sort(self, A, start, end, tmp): if start >= end: return 0 mid = (start + end) // 2 left, right = start, mid + 1 ans = self.merge_sort(A, left, mid, tmp) ans += self.merge_sort(A, right, end, tmp) i = start while left <= mid and right <= end: if A[left] > A[right]: tmp[i] = A[right] right += 1 ans += mid - left + 1 else: tmp[i] = A[left] left += 1 i += 1 while left <= mid: tmp[i] = A[left] left += 1 i += 1 while right <= end: tmp[i] = A[right] right += 1 i += 1 for i in range(start, end + 1): A[i] = tmp[i] return ans ================================================ FILE: lintcode/533_two_sum_closest_to_target.py ================================================ class Solution: """ @param: nums: an integer array @param: target: An integer @return: the difference between the sum and the target """ def twoSumClosest(self, nums, target): if not nums or len(nums) < 2: return -1 nums.sort() left, right = 0, len(nums) - 1 _sum = 0 diff = float('inf') while left < right: _sum = nums[left] + nums[right] if _sum < target: diff = min(diff, target - _sum) left += 1 else: diff = min(diff, _sum - target) right -= 1 return diff ================================================ FILE: lintcode/534_house_robber_ii.py ================================================ """ Main Concept: if we insist on not stealing one of the houses => the remaining houses become a sequence => the problem becomes `./lintcode/392_house_robber.py` we can pick any pair of adjacent houses, and to make the calculation easier, we pick the first and last houses 1. insist on not stealing the first house that is, the range becomes `[1, n - 1]` 2. insist on not stealing the last house that is, the range becomes `[0, n - 2]` and choose the maximum amount as the answer Test Case: [3,6,4] [1,3,2,1,5] """ class Solution: """ @param: A: An array of non-negative integers. @return: The maximum amount of money you can rob tonight """ def houseRobber2(self, A): if not A: return 0 if len(A) == 1: return A[0] dp = [[0] * 2 for _ in range(2)] return max( self.houseRobber(A, 0, dp), self.houseRobber(A, 1, dp) ) def houseRobber(self, A, start, dp): n = len(A) prev, curr = 0, start % 2 dp[curr][0] = 0 dp[curr][1] = A[start] for i in range(1 + start, n - 1 + start): prev = curr curr = i % 2 dp[curr][0] = max(dp[prev]) dp[curr][1] = dp[prev][0] + A[i] return max(dp[curr]) class Solution: """ @param: A: An array of non-negative integers. @return: The maximum amount of money you can rob tonight """ def houseRobber2(self, A): if not A: return 0 n = len(A) if n == 1: return A[0] if n == 2: return max(A[0], A[1]) dp = [0] * 3 return max( # range(0, n - 1) self.houseRobber(A, 0, dp), # range(1, n) self.houseRobber(A, 1, dp) ) def houseRobber(self, A, start, dp): n = len(A) prev2, prev1, curr = 0, start % 3, (start + 1) % 3 dp[prev1] = A[start] dp[curr] = max(A[start], A[start + 1]) for i in range(2 + start, n - 1 + start): prev2, prev1 = prev1, curr curr = i % 3 dp[curr] = max(dp[prev1], dp[prev2] + A[i]) return dp[curr] class Solution: def houseRobber2(self, A): """ :type A: List[int] :rtype: int """ if not A: return 0 n = len(A) if n < 2: return A[0] return max( self.rob_in_line(A, 0, n - 2), self.rob_in_line(A, 1, n - 1) ) def rob_in_line(self, A, start, end): n = end - start + 1 dp = [0] * (n + 1) dp[0] = 0 dp[1] = A[start] for i in range(2, n + 1): dp[i] = max( dp[i - 2] + A[start + i - 1], dp[i - 1] ) return dp[n] ================================================ FILE: lintcode/538_memcache.py ================================================ class Memcache: INT_MAX = 0x7FFFFFFF PERMANENT_TTL = -1 storage = {} """ @param: curtTime: An integer @param: key: An integer @return: An integer """ def get(self, curtTime, key): if key not in self.storage: return self.INT_MAX if (curtTime < self.storage[key]['expired_at'] or self.storage[key]['expired_at'] == self.PERMANENT_TTL): return self.storage[key]['val'] return self.INT_MAX """ @param: curtTime: An integer @param: key: An integer @param: value: An integer @param: ttl: An integer @return: nothing """ def set(self, curtTime, key, value, ttl): if ttl > 0: self.storage[key] = self._new_item(key, value, curtTime + ttl) else: self.storage[key] = self._new_item(key, value, self.PERMANENT_TTL) """ @param: curtTime: An integer @param: key: An integer @return: nothing """ def delete(self, curtTime, key): if key in self.storage: del self.storage[key] """ @param: curtTime: An integer @param: key: An integer @param: delta: An integer @return: An integer """ def incr(self, curtTime, key, delta): if key not in self.storage: return self.INT_MAX if (curtTime < self.storage[key]['expired_at'] or self.storage[key]['expired_at'] == self.PERMANENT_TTL): self.storage[key]['val'] += delta return self.storage[key]['val'] return self.INT_MAX """ @param: curtTime: An integer @param: key: An integer @param: delta: An integer @return: An integer """ def decr(self, curtTime, key, delta): return self.incr(curtTime, key, -1 * delta) def _new_item(self, key, value, expired_at): return { 'key': key, 'val': value, 'expired_at': expired_at } ================================================ FILE: lintcode/539_move_zeroes.py ================================================ class Solution: def moveZeroes(self, nums): """ :type nums: List[int] :rtype: void Do not return anything, modify nums in-place instead. """ if not nums: return n = len(nums) left = 0 for right in range(n): if nums[right] != 0: nums[left], nums[right] = nums[right], nums[left] left += 1 ================================================ FILE: lintcode/53_reverse_words_in_a_string.py ================================================ class Solution: """ @param: s: A string @return: A string """ def reverseWords(self, s): s = s.strip() if not s: return '' return ' '.join(reversed(s.split())) ================================================ FILE: lintcode/540_zigzag_iterator.py ================================================ """ Your ZigzagIterator object will be instantiated and called as such: solution, result = ZigzagIterator(v1, v2), [] while solution.hasNext(): result.append(solution.next()) Output result """ class ZigzagIterator: """ @param: v1: A 1d vector @param: v2: A 1d vector """ def __init__(self, v1, v2): self.g = (v1, v2) self.x = 0 self.y = 0 self.max_y = max(len(vec) for vec in self.g) """ @return: An integer """ def next(self): if not self.hasNext(): return -1 x = self.x y = self.y self.x += 1 return self.g[x][y] """ @return: True if has next """ def hasNext(self): while self.y < self.max_y: if ( self.x < len(self.g) and self.y < len(self.g[self.x]) ): return True if self.x >= len(self.g): self.x = 0 self.y += 1 if self.y >= len(self.g[self.x]): self.x += 1 return False class ZigzagIterator: """ @param: v1: A 1d vector @param: v2: A 1d vector """ def __init__(self, v1, v2): self.queue = [vec for vec in (v1, v2) if vec] """ @return: An integer """ def next(self): if not self.hasNext(): return -1 vec = self.queue.pop(0) val = vec.pop(0) if vec: self.queue.append(vec) return val """ @return: True if has next """ def hasNext(self): return bool(self.queue) ================================================ FILE: lintcode/541_zigzag_iterator_ii.py ================================================ """ Your ZigzagIterator2 object will be instantiated and called as such: solution, result = ZigzagIterator2(vecs), [] while solution.hasNext(): result.append(solution.next()) Output result """ class ZigzagIterator2: """ @param: vecs: a list of 1d vectors """ def __init__(self, vecs): self.g = vecs self.x = 0 self.y = 0 self.max_y = max(len(vec) for vec in vecs) """ @return: An integer """ def next(self): if not self.hasNext(): return -1 x = self.x y = self.y self.x += 1 return self.g[x][y] """ @return: True if has next """ def hasNext(self): while self.y < self.max_y: if ( self.x < len(self.g) and self.y < len(self.g[self.x]) ): return True if self.x >= len(self.g): self.x = 0 self.y += 1 if self.y >= len(self.g[self.x]): self.x += 1 return False class ZigzagIterator2: """ @param: vecs: a list of 1d vectors """ def __init__(self, vecs): self.queue = [vec for vec in vecs if vec] """ @return: An integer """ def next(self): if not self.hasNext(): return -1 vec = self.queue.pop(0) val = vec.pop(0) if vec: self.queue.append(vec) return val """ @return: True if has next """ def hasNext(self): return bool(self.queue) ================================================ FILE: lintcode/543_kth_largest_in_n_arrays.py ================================================ import heapq class Solution: """ @param: arrays: a list of array @param: k: An integer @return: an integer, K-th largest element in N arrays """ def KthInArrays(self, arrays, k): res = [] for arr in arrays: res += arr if k > len(res): return 'error' res.sort() return res[-k] ================================================ FILE: lintcode/544_top_k_largest_numbers.py ================================================ from heapq import heappush, heappop class Solution: """ @param: nums: an integer array @param: k: An integer @return: the top k largest numbers in array """ def topk(self, nums, k): """ min heap (normal case in heapq, max heap needs to times -1) """ if not nums: return ans = [] for num in nums: heappush(ans, num) if len(ans) > k: heappop(ans) ans.sort(reverse=True) return ans ================================================ FILE: lintcode/545_top_k_largest_numbers_ii.py ================================================ from heapq import heappop, heappush class Solution: """ @param: k: An integer """ def __init__(self, k): self.k = k self.tops = [] """ @param: num: Number to be added @return: nothing """ def add(self, num): # push in first, since we cannot ensure # the incoming num will stay in tops heappush(self.tops, num) if len(self.tops) > self.k: heappop(self.tops) """ @return: Top k element """ def topk(self): return sorted(self.tops, reverse=True) ================================================ FILE: lintcode/547_intersection_of_two_arrays.py ================================================ class Solution: def intersection(self, a, b): """ :type a: List[int] :type b: List[int] :rtype: List[int] """ ans = [] if not a or not b: return ans s = set(a) t = set(b) for x in s: if x in t: ans.append(x) return ans class Solution: def intersection(self, a, b): """ :type a: List[int] :type b: List[int] :rtype: List[int] """ ans = [] if not a or not b: return ans a.sort() b.sort() m, n = len(a), len(b) i = j = 0 while i < m and j < n: if a[i] == b[j]: if not ans or a[i] != ans[-1]: ans.append(a[i]) i += 1 j += 1 elif a[i] < b[j]: i += 1 else: j += 1 return ans ================================================ FILE: lintcode/548_intersection_of_two_arrays_ii.py ================================================ class Solution: def intersect(self, a, b): """ :type a: List[int] :type b: List[int] :rtype: List[int] """ ans = [] if not a or not b: return ans freq = {} for x in a: freq[x] = freq.get(x, 0) + 1 for x in b: if not freq.get(x): continue freq[x] -= 1 ans.append(x) return ans class Solution: def intersect(self, a, b): """ :type a: List[int] :type b: List[int] :rtype: List[int] """ ans = [] if not a or not b: return ans a.sort() b.sort() m, n = len(a), len(b) i = j = 0 while i < m and j < n: if a[i] == b[j]: ans.append(a[i]) i += 1 j += 1 elif a[i] < b[j]: i += 1 else: j += 1 return ans ================================================ FILE: lintcode/54_string_to_integer_ii.py ================================================ class Solution: """ @param: s: A string @return: An integer """ def atoi(self, s): NOT_FOUND = 0 if not s: return NOT_FOUND n = len(s) is_negative = False left, right = 0, n - 1 while left < n and s[left] == ' ': left += 1 while right >= 0 and s[right] == ' ': right -= 1 if left < n and s[left] in ('+', '-'): is_negative = (s[left] == '-') left += 1 if left > right: return NOT_FOUND ans = 0 zero = ord('0') nine = ord('9') INT_MAX = 0x7FFFFFFF INT_MIN = -0x80000000 while left <= right and zero <= ord(s[left]) <= nine: ans = ans * 10 + ord(s[left]) - zero left += 1 if is_negative: ans *= -1 if ans > INT_MAX: return INT_MAX if ans < INT_MIN: return INT_MIN return ans ================================================ FILE: lintcode/551_nested_list_weight_sum.py ================================================ """ This is the interface that allows for creating nested lists. You should not implement it, or speculate about its implementation class NestedInteger(object): def isInteger(self): # @return {boolean} True if this NestedInteger holds a single integer, # rather than a nested list. def getInteger(self): # @return {int} the single integer that this NestedInteger holds, # if it holds a single integer # Return None if this NestedInteger holds a nested list def getList(self): # @return {NestedInteger[]} the nested list that this NestedInteger holds, # if it holds a nested list # Return None if this NestedInteger holds a single integer """ class Solution(object): # @param {NestedInteger[]} L a list of NestedInteger Object # @return {int} an integer def depthSum(self, L): return self.dfs(L, 1) def dfs(self, L, depth): _sum = 0 for obj in L: if obj.isInteger(): _sum += depth * obj.getInteger() continue _sum += self.dfs(obj.getList(), depth + 1) return _sum class Solution(object): # @param {NestedInteger[]} L a list of NestedInteger Object # @return {int} an integer def depthSum(self, L): ans = 0 if not L: return ans queue = L depth = 0 while queue: _queue = [] depth += 1 for obj in queue: if obj.isInteger(): ans += depth * obj.getInteger() continue _queue.extend(obj.getList()) queue = _queue return ans ================================================ FILE: lintcode/552_create_maximum_number.py ================================================ class Solution: def maxNumber(self, a, b, k): """ :type a: list[int] :type b: list[int] :type k: int, k <= m + n :rtype: list[int] """ ans = [] for size in range( max(0, k - len(a)), min(k, len(b)) + 1 ): res = self.merge( self.get_max(a, k - size), self.get_max(b, size) ) ans = max(ans, res) return ans def get_max(self, a, size): res = [] n = len(a) for i in range(n): while ( res and len(res) + n - i > size and res[-1] < a[i] ): res.pop() if len(res) < size: res.append(a[i]) return res def merge(self, a, b): return [ max(a, b).pop(0) for _ in range(len(a) + len(b)) ] ================================================ FILE: lintcode/553_bomb_enemy.py ================================================ """ Cache sharing nodes Main Concept: 1. the number of enemies killed is same between the two walls in line y 0 1 2 3 4 x 0 W 1 k 0 k W l 2 n 3 W 4 m the number of enemies killed is `n + k` at (1, 1) 2. since the iteration order, we can optimize the space complexity in row x y 0 1 2 3 0 [[ | 0, | E, | 0, | 0 ], --1--> 1 [ | E, | 0, v W, | E ], --2--> 2 [ v 0, v E, v 0, v 0 ]] --3--> """ class Solution: WALL = 'W' ENEMY = 'E' EMPTY = '0' def maxKilledEnemies(self, grid): """ :type grid: list[list[str]] :rtype: int """ ans = 0 if not grid or not grid[0]: return ans m, n = len(grid), len(grid[0]) row, cols = 0, [0] * n for x in range(m): for y in range(n): # calculate bomb in cur section [x, 'WALL' | m) in col if x == 0 or grid[x - 1][y] == self.WALL: cols[y] = 0 for i in range(x, m): if grid[i][y] == self.WALL: break if grid[i][y] == self.ENEMY: cols[y] += 1 # calculate bomb in cur section [y, 'WALL' | n) in row if y == 0 or grid[x][y - 1] == self.WALL: row = 0 for i in range(y, n): if grid[x][i] == self.WALL: break if grid[x][i] == self.ENEMY: row += 1 if grid[x][y] == self.EMPTY and row + cols[y] > ans: ans = row + cols[y] return ans """ TLE time: O(m^2 * n^2) """ class Solution: WALL = 'W' ENEMY = 'E' EMPTY = '0' def maxKilledEnemies(self, grid): """ :type grid: list[list[str]] :rtype: int """ ans = 0 if not grid or not grid[0]: return ans for x in range(len(grid)): for y in range(len(grid[0])): if grid[x][y] == self.EMPTY: ans = max( ans, self.get_killed_cnt(grid, x, y) ) return ans def get_killed_cnt(self, grid, i, j): m, n = len(grid), len(grid[0]) cnt = 0 # up for x in range(i, -1, -1): if grid[x][j] == self.WALL: break if grid[x][j] == self.ENEMY: cnt += 1 # down for x in range(i, m): if grid[x][j] == self.WALL: break if grid[x][j] == self.ENEMY: cnt += 1 # left for y in range(j, -1, -1): if grid[i][y] == self.WALL: break if grid[i][y] == self.ENEMY: cnt += 1 # right for y in range(j, n): if grid[i][y] == self.WALL: break if grid[i][y] == self.ENEMY: cnt += 1 return cnt ================================================ FILE: lintcode/555_counting_bloom_filter.py ================================================ from random import randint class HashFunc: def __init__(self, cap, seed): self.cap = cap self.seed = seed def hash(self, key): code = 0 if not key: return code for char in key: code = (self.seed * code + ord(char)) % self.cap return code class CountingBloomFilter: def __init__(self, k): """ :type k: int """ CAP = 20000 self.bits = [0] * CAP self.hashs = [] for i in range(k): self.hashs.append(HashFunc( randint(CAP // 2, CAP), i * 2 + 3 )) def add(self, word): """ :type word: str :rtype: None """ for f in self.hashs: index = f.hash(word) self.bits[index] += 1 def remove(self, word): """ :type word: str :rtype: None """ for f in self.hashs: index = f.hash(word) self.bits[index] -= 1 def contains(self, word): """ :type word: str :rtype: bool """ for f in self.hashs: index = f.hash(word) if self.bits[index] <= 0: return False return True ================================================ FILE: lintcode/556_standard_bloom_filter.py ================================================ from random import randint class HashFunc: def __init__(self, cap, seed): self.cap = cap self.seed = seed def hash(self, key): code = 0 if not key: return code for char in key: code = (self.seed * code + ord(char)) % self.cap return code class StandardBloomFilter: def __init__(self, k): """ :type k: int """ CAP = 20000 self.bits = [0] * CAP self.hashs = [] for i in range(k): self.hashs.append(HashFunc( randint(CAP // 2, CAP), i * 2 + 3 )) def add(self, word): """ :type word: str :rtype: None """ for f in self.hashs: index = f.hash(word) self.bits[index] = 1 def contains(self, word): """ :type word: str :rtype: bool """ for f in self.hashs: index = f.hash(word) if self.bits[index] == 0: return False return True ================================================ FILE: lintcode/559_trie_service.py ================================================ """ Definition of TrieNode: class TrieNode: def __init__(self): # : self.children = collections.OrderedDict() self.top10 = [] """ class TrieService: def __init__(self): self.root = TrieNode() def get_root(self): # Return root of trie root, and # lintcode will print the tree struct. return self.root # @param {str} word a string # @param {int} frequency an integer # @return nothing def insert(self, word, frequency): if not word or len(word) < 1 \ or not frequency: return parent = self.root for char in word: if char not in parent.children: parent.children[char] = TrieNode() parent = parent.children[char] # To handle top10 parent.top10.append(frequency) parent.top10.sort(reverse=True) if len(parent.top10) > 10: parent.top10.pop() ================================================ FILE: lintcode/560_friendship_service.py ================================================ class FriendshipService: def __init__(self): self.followers = {} self.followings = {} def getFollowers(self, user_id): return self.get_followers(user_id) def getFollowings(self, user_id): return self.get_followings(user_id) """ @param: user_id: An integer @return: all followers and sort by user_id """ def get_followers(self, user_id): if user_id in self.followers: return sorted(self.followers[user_id]) return [] """ @param: user_id: An integer @return: all followings and sort by user_id """ def get_followings(self, user_id): if user_id in self.followings: return sorted(self.followings[user_id]) return [] """ @param: to_id: An integer @param: from_id: An integer @return: nothing """ def follow(self, to_id, from_id): if from_id not in self.followings: self.followings[from_id] = set() self.followings[from_id].add(to_id) if to_id not in self.followers: self.followers[to_id] = set() self.followers[to_id].add(from_id) """ @param: to_id: An integer @param: from_id: An integer @return: nothing """ def unfollow(self, to_id, from_id): if from_id in self.followings: self.followings[from_id].discard(to_id) if to_id in self.followers: self.followers[to_id].discard(from_id) ================================================ FILE: lintcode/563_backpack_v.py ================================================ """ DP: MLE """ class Solution: """ @param: A: an integer array and all positive numbers @param: target: An integer @return: An integer """ def backPackV(self, A, target): if not A: return 0 n = len(A) dp = [[0] * (target + 1) for _ in range(n + 1)] dp[0][0] = 1 for i in range(1, n + 1): for j in range(target + 1): dp[i][j] = dp[i - 1][j] if j >= A[i - 1]: dp[i][j] += dp[i - 1][j - A[i - 1]] return dp[n][target] """ DP: optimized space complexity """ class Solution: """ @param: A: an integer array and all positive numbers @param: target: An integer @return: An integer """ def backPackV(self, A, target): if not A: return 0 n = len(A) dp = [0] * (target + 1) dp[0] = 1 for i in range(n): for j in range(target, A[i] - 1, -1): dp[j] += dp[j - A[i]] return dp[target] ================================================ FILE: lintcode/564_backpack_vi.py ================================================ class Solution: """ @param: A: an integer array and all positive numbers, no duplicates @param: target: An integer @return: An integer """ def backPackVI(self, A, target): if not A: return 0 n = len(A) dp = [0] * (target + 1) dp[0] = 1 for i in range(1, target + 1): for j in range(n): if i >= A[j]: dp[i] += dp[i - A[j]] return dp[target] ================================================ FILE: lintcode/565_heart_beat.py ================================================ class HeartBeat: def __init__(self): self.slaves_ip_list = {} """ @param: slaves_ip_list: a list of slaves'ip addresses @param: k: An integer @return: nothing """ def initialize(self, slaves_ip_list, k): self.slaves_ip_list.update(dict.fromkeys(slaves_ip_list, 0)) self.ttl = 2 * k """ @param: timestamp: current timestamp in seconds @param: slave_ip: the ip address of the slave server @return: nothing """ def ping(self, timestamp, slave_ip): if slave_ip in self.slaves_ip_list: self.slaves_ip_list[slave_ip] = timestamp """ @param: timestamp: current timestamp in seconds @return: a list of slaves'ip addresses that died """ def getDiedSlaves(self, timestamp): if not timestamp: return [] return [ ip for ip, t0 in self.slaves_ip_list.items() if timestamp - t0 >= self.ttl ] ================================================ FILE: lintcode/566_gfs_client.py ================================================ ''' Definition of BaseGFSClient class BaseGFSClient: def readChunk(self, filename, chunkIndex): # Read a chunk from GFS def writeChunk(self, filename, chunkIndex, content): # Write a chunk to GFS ''' class GFSClient(BaseGFSClient): """ @param: chunkSize: An integer """ def __init__(self, chunkSize): BaseGFSClient.__init__(self) self.chunk_size = chunkSize self.chunk_num = {} """ @param: filename: a file name @return: conetent of the file given from GFS """ def read(self, filename): if filename not in self.chunk_num: return i, content = 0, '' while i < self.chunk_num[filename]: content += self.readChunk(filename, i) i += 1 return content """ @param: filename: a file name @param: content: a string @return: nothing """ def write(self, filename, content): i, j, n = 0, 0, len(content) while j < n: self.writeChunk(filename, i, content[j : j + self.chunk_size]) i += 1 j += self.chunk_size self.chunk_num[filename] = i ================================================ FILE: lintcode/56_two_sum.py ================================================ """ time: O(n) space: O(n) it works even if it's not sorted """ class Solution: """ @param: A: An array of Integer @param: target: target = A[index1] + A[index2] @return: [index1, index2] (index1 < index2) """ def twoSum(self, A, target): NOT_FOUND = [-1, -1] if not A or len(A) < 2: return NOT_FOUND remaining = {} for i in range(len(A)): if A[i] in remaining: return [ remaining[A[i]], i, ] remaining[target - A[i]] = i return NOT_FOUND """ time: O(nlogn) space: O(n) needs to sort in advance """ class Solution: """ @param: A: An array of Integer @param: target: target = A[index1] + A[index2] @return: [index1, index2] (index1 < index2) """ def twoSum(self, A, target): NOT_FOUND = [-1, -1] if not A or len(A) < 2: return NOT_FOUND n = len(A) A = [(A[i], i) for i in range(n)] A.sort() left, right = 0, n - 1 while left < right: _sum = A[left][0] + A[right][0] if _sum == target: return sorted([ A[left][1], A[right][1], ]) if _sum < target: left += 1 else: right -= 1 return NOT_FOUND ================================================ FILE: lintcode/573_build_post_office_ii.py ================================================ """ Note: - You cannot pass through wall and house, but can pass through empty. - You only build post office on an empty. """ import collections class Solution: """ BFS 1. for each house, use `BFS` in level traversal to count the distance if the cell is empty and reachable 2. find the minimum of the total distance in each empty cell, and it must be reachable for each house """ EMPTY = 0 HOUSE = 1 WALL = 2 def shortestDistance(self, grid): """ :type grid: list[list[int]] :rtype: int """ if not grid or not grid[0]: return -1 m, n = len(grid), len(grid[0]) cnt = 0 times = collections.defaultdict(int) steps = collections.defaultdict(int) for x in range(m): for y in range(n): if grid[x][y] == self.HOUSE: cnt += 1 self.bfs(grid, x, y, times, steps) ans = INF = float('inf') for (x, y), t in times.items(): if t == cnt and steps[x, y] < ans: ans = steps[x, y] return ans if ans < INF else -1 def bfs(self, grid, x, y, times, steps): m, n = len(grid), len(grid[0]) queue, _queue = [(x, y)], [] visited = set(queue) step = 0 while queue: step += 1 for x, y in queue: for dx, dy in ( (-1, 0), (1, 0), (0, -1), (0, 1), ): _x = x + dx _y = y + dy if not (0 <= _x < m and 0 <= _y < n): continue if grid[_x][_y] != self.EMPTY: continue if (_x, _y) in visited: continue visited.add((_x, _y)) _queue.append((_x, _y)) steps[_x, _y] += step times[_x, _y] += 1 queue, _queue = _queue, [] import collections class Solution: """ DFS: TLE If use DFS, need to consider update min dist so may visit a node many times """ EMPTY = 0 HOUSE = 1 WALL = 2 def shortestDistance(self, grid): """ :type grid: list[list[int]] :rtype: int """ if not grid or not grid[0]: return -1 m, n = len(grid), len(grid[0]) cnt = 0 ids = collections.defaultdict(set) # record house ids steps = collections.defaultdict(int) # total steps for all houses for x in range(m): for y in range(n): if grid[x][y] != self.HOUSE: continue cnt += 1 step = collections.defaultdict(int) # steps for current house self.dfs(grid, x, y, cnt, ids, steps, step) ans = INF = float('inf') for (x, y), hids in ids.items(): if len(hids) == cnt and steps[x, y] < ans: ans = steps[x, y] return ans if ans < INF else -1 def dfs(self, grid, x, y, id, ids, steps, step): m, n = len(grid), len(grid[0]) for dx, dy in ( (-1, 0), (1, 0), (0, -1), (0, 1), ): _x = x + dx _y = y + dy if not (0 <= _x < m and 0 <= _y < n): continue if grid[_x][_y] != self.EMPTY: continue if step[x, y] + 1 >= step[_x, _y] > 0: # > 0 means visited, since its defaultdict continue ids[_x, _y].add(id) steps[_x, _y] -= step[_x, _y] step[_x, _y] = step[x, y] + 1 steps[_x, _y] += step[_x, _y] self.dfs(grid, _x, _y, id, ids, steps, step) ================================================ FILE: lintcode/574_build_post_office.py ================================================ """ Note: - You can pass through house and empty. - You only build post office on an empty. """ class Solution: """ Prefix Sum + Binary Searching http://yuanyuanzhangcs.blogspot.hk/2017/02/build-post-office.html for `X = [1, 2, 3, 4], x = 3` and we want to get the sum of distances between `X[i]` and `x` => d = (3 - 1) + (3 - 2) + (3 - 3) + (4 - 3) so we can use binary search to find the `i` of `x` in `X` => n = 4, i = 2 (since 2 < x == 3) => d = x * i - (1 + 2) + (3 + 4) - (n - i) * x and we building prefix sum of `X` => `S = [0, 1, 3, 6, 10]` => d = x * i - S[i] + (S[n] - S[i]) - x * (n - i) """ EMPTY = 0 HOUSE = 1 def shortestDistance(self, grid): """ :type grid: list[list[int]] :rtype: int """ if not grid or not grid[0]: return -1 m, n = len(grid), len(grid[0]) xs, ys = [], [] for x in range(m): for y in range(n): if grid[x][y] != self.HOUSE: continue xs.append(x) ys.append(y) if not xs or len(xs) == m * n: return -1 ys.sort() k = len(xs) + 1 psx, psy = [0] * k, [0] * k # prefix sum for i in range(1, k): psx[i] = psx[i - 1] + xs[i - 1] psy[i] = psy[i - 1] + ys[i - 1] ans = INF = float('inf') for x in range(m): for y in range(n): if grid[x][y] != self.EMPTY: continue step = self.get_step(psx, xs, x) + self.get_step(psy, ys, y) if step < ans: ans = step return ans if ans < INF else -1 def get_step(self, ps, axis, pos): n = len(axis) if axis[0] > pos: return ps[n] - pos * n if axis[-1] < pos: return pos * n - ps[n] left, right = 0, n - 1 while left + 1 < right: mid = (left + right) // 2 if axis[mid] < pos: left = mid else: right = mid return sum(( pos * right - ps[right], ps[n] - ps[right] - pos * (n - right), )) class Solution: """ BFS: TLE time: O(mn) find the center of the shape composed of houses and then do bfs """ EMPTY = 0 HOUSE = 1 def shortestDistance(self, grid): """ :type grid: list[list[int]] :rtype: int """ if not grid or not grid[0]: return -1 m, n = len(grid), len(grid[0]) houses = [] xc = yc = 0 # the center of the shape composed of houses for x in range(m): for y in range(n): if grid[x][y] == self.HOUSE: houses.append((x, y)) xc += x yc += y xc //= len(houses) yc //= len(houses) ans = INF = float('inf') queue = [(xc, yc)] visited = set(queue) for x, y in queue: if grid[x][y] == self.EMPTY: ans = min(ans, self.get_step(houses, x, y)) for dx, dy in ( (-1, 0), (1, 0), (0, -1), (0, 1), ): _x = x + dx _y = y + dy if not (0 <= _x < m and 0 <= _y < n): continue if (_x, _y) in visited: continue visited.add((_x, _y)) queue.append((_x, _y)) return ans if ans < INF else -1 def get_step(self, houses, x, y): step = 0 for _x, _y in houses: step += abs(_x - x) + abs(_y - y) return step class Solution: """ BFS: TLE time: O((mn)^2) brute force to bfs """ EMPTY = 0 HOUSE = 1 def shortestDistance(self, grid): """ :type grid: list[list[int]] :rtype: int """ if not grid or not grid[0]: return -1 m, n = len(grid), len(grid[0]) steps = [[0] * n for _ in range(m)] for x in range(m): for y in range(n): if grid[x][y] == self.HOUSE: self.bfs(grid, x, y, steps) ans = INF = float('inf') for x in range(m): for y in range(n): if grid[x][y] != self.EMPTY: continue if steps[x][y] < ans: ans = steps[x][y] return ans if ans < INF else -1 def bfs(self, grid, x, y, steps): m, n = len(grid), len(grid[0]) queue, _queue = [(x, y)], [] visited = set(queue) step = 0 while queue: step += 1 for x, y in queue: for dx, dy in ( (-1, 0), (1, 0), (0, -1), (0, 1), ): _x = x + dx _y = y + dy if not (0 <= _x < m and 0 <= _y < n): continue if (_x, _y) in visited: continue visited.add((_x, _y)) steps[_x][_y] += step _queue.append((_x, _y)) queue, _queue = _queue, [] ================================================ FILE: lintcode/575_expression_expand.py ================================================ class Solution: def expressionExpand(self, s): """ :type s: str :rtype: str """ if not s: return '' times = 0 stack = [] for c in s: if c.isdigit(): times = times * 10 + int(c) elif c == '[': stack.append(times) times = 0 elif c == ']': part = [] while stack and isinstance(stack[-1], str): part.append(stack.pop()) cnt = int(stack.pop()) if stack else 1 stack.append(cnt * ''.join(reversed(part))) else: stack.append(c) return ''.join(stack) ================================================ FILE: lintcode/578_lowest_common_ancestor_iii.py ================================================ """ Notice: node A or node B may not exist in tree. Definition of TreeNode: class TreeNode: def __init__(self, val): this.val = val this.left, this.right = None, None """ class Solution: def lowestCommonAncestor3(self, root, a, b): """ :type root: TreeNode :type a: TreeNode :type b: TreeNode :rtype: TreeNode """ if not root: return lca, has_a, has_b = self.divide_conquer(root, a, b) return lca if has_a and has_b else None def divide_conquer(self, node, a, b): if not node: return None, False, False left, a_in_left, b_in_left = self.divide_conquer(node.left, a, b) right, a_in_right, b_in_right = self.divide_conquer(node.right, a, b) has_a = a_in_left or a_in_right or node is a has_b = b_in_left or b_in_right or node is b if node is a or node is b: return node, has_a, has_b if left and right: return node, has_a, has_b if left: return left, has_a, has_b if right: return right, has_a, has_b return None, has_a, has_b ================================================ FILE: lintcode/57_3sum.py ================================================ class Solution: def threeSum(self, nums): """ :type nums: List[int] :rtype: List[List[int]] """ ans = [] if not nums or len(nums) < 3: return ans n = len(nums) nums.sort() for a in range(n - 2): if a > 0 and nums[a] == nums[a - 1]: continue b, c = a + 1, n - 1 while b < c: total = nums[a] + nums[b] + nums[c] if total == 0: ans.append([nums[a], nums[b], nums[c]]) b += 1 c -= 1 while b < c and nums[b] == nums[b - 1]: b += 1 while b < c and nums[c] == nums[c + 1]: c -= 1 elif total < 0: b += 1 else: c -= 1 return ans ================================================ FILE: lintcode/582_word_break_ii.py ================================================ class Solution: """ dfs/dp: optimized by memory searching """ def wordBreak(self, s, words): """ :type s: str :type words: list[str] :rtype: list[str] """ return self.dfs(s, set(words), {}) def dfs(self, s, words, memo): if s in memo: return memo[s] res = [] if not s: return res n = len(s) for size in range(1, n + 1): prefix = s[:size] if prefix not in words: continue if size == n: res.append(prefix) continue for word in self.dfs(s[size:], words, memo): res.append('{0} {1}'.format(prefix, word)) memo[s] = res return res class Solution: """ dfs: TLE bad in edge case: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ["a","aa","aaa","aaaa","aaaaa","aaaaaa","aaaaaaa","aaaaaaaa","aaaaaaaaa","aaaaaaaaaa"] """ def wordBreak(self, s, words): """ :type s: str :type words: list[str] :rtype: list[str] """ ans = [] if not words: return ans self.dfs(s, words, ans, []) return ans def dfs(self, s, words, ans, path): if not s: ans.append(' '.join(path)) return for word in words: if not word or s.find(word) != 0: # 1. no word # 2. current word must be the first in s passed in prev continue path.append(word) self.dfs(s[len(word):], words, ans, path) path.pop() ================================================ FILE: lintcode/584_drop_eggs_ii.py ================================================ """ REF: http://massivealgorithms.blogspot.jp/2014/07/dynamic-programming-set-11-egg-dropping.html """ class Solution: """ @param: m: the number of eggs @param: n: the number of floors @return: the number of drops in the worst case """ def dropEggs2(self, m, n): if not m or not n: return 0 INFINITY = float('inf') """ `dp[i][j]` means the minimum drops to verify the worst case in `j` floors with `i` eggs """ dp = [[0] * (n + 1) for _ in range(m + 1)] """ only one egg """ for i in range(1, m + 1): dp[i][1] = 1 """ only one floor """ for j in range(1, n + 1): dp[1][j] = j for i in range(2, m + 1): for j in range(2, n + 1): dp[i][j] = INFINITY for k in range(1, j + 1): """ backtracking to drop one egg on arbitrary floor `k` there is two cases, if previous egg is: 1. broken: reduce to subproblem (m - 1, k - 1) dp[i - 1][k - 1] + 1 drop 2. not broken: reduce to subproblem (m, n - k) dp[i][j - k] + 1 drop """ _worst = max(dp[i - 1][k - 1], dp[i][j - k]) + 1 """ find the minimum worst case """ if _worst < dp[i][j]: dp[i][j] = _worst return dp[m][n] ================================================ FILE: lintcode/585_maximum_number_in_mountain_sequence.py ================================================ class Solution: """ @param: nums: a mountain sequence which increase firstly and then decrease @return: then mountain top """ def mountainSequence(self, nums): if not nums: return -1 l, m, r = 0, 0, len(nums) - 1 while l + 1 < r: m = l + (r - l) // 2 """ `m+1` will not out of range if len(nums) == 1 || 2, the code in this loop will not execute """ if nums[m] > nums[m+1]: r = m else: l = m return max(nums[l], nums[r]) ================================================ FILE: lintcode/586_sqrtx_ii.py ================================================ class Solution: """ @param: x: a double @return: the square root of x """ def sqrt(self, x): if not x: return x left = 0 right = x if x > 1 else 1 eps = 1e-10 # the precision needs `1e-8`, check more two digits while right - left > eps: mid = (left + right) / 2.0 if mid * mid < x: left = mid else: right = mid return left ================================================ FILE: lintcode/587_two_sum_unique_pairs.py ================================================ class Solution: """ @param: nums: an array of integer @param: target: An integer @return: An integer """ def twoSum6(self, nums, target): ans = 0 if not nums: return ans nums.sort() left, right = 0, len(nums) - 1 _sum = 0 while left < right: _sum = nums[left] + nums[right] if _sum == target: ans += 1 left += 1 right -= 1 while left < right and nums[right] == nums[right + 1]: right -= 1 while left < right and nums[left] == nums[left - 1]: left -= 1 continue if _sum > target: right -= 1 else: left += 1 return ans ================================================ FILE: lintcode/589_connecting_graph.py ================================================ class ConnectingGraph: """ @param: n: An integer """ def __init__(self, n): if not n: return self.N = {} for i in range(1, n + 1): self.N[i] = i def find(self, a): if self.N[a] == a: return a self.N[a] = self.find(self.N[a]) return self.N[a] """ @param: a: An integer @param: b: An integer @return: nothing """ def connect(self, a, b): _a = self.find(a) _b = self.find(b) if _a != _b: self.N[_a] = _b """ @param: a: An integer @param: b: An integer @return: A boolean """ def query(self, a, b): _a = self.find(a) _b = self.find(b) return _a == _b ================================================ FILE: lintcode/58_4sum.py ================================================ class Solution: def fourSum(self, nums, target): """ :type nums: List[int] :type target: int :rtype: List[List[int]] """ ans = [] if not nums or len(nums) < 4 or target is None: return ans n = len(nums) nums.sort() for a in range(n - 3): if a > 0 and nums[a] == nums[a - 1]: continue for b in range(a + 1, n - 2): if b > a + 1 and nums[b] == nums[b - 1]: continue c, d = b + 1, n - 1 while c < d: total = nums[a] + nums[b] + nums[c] + nums[d] if total == target: ans.append([nums[a], nums[b], nums[c], nums[d]]) c += 1 d -= 1 while c < d and nums[c] == nums[c - 1]: c += 1 while c < d and nums[d] == nums[d + 1]: d -= 1 elif total < target: c += 1 else: d -= 1 return ans ================================================ FILE: lintcode/590_connecting_graph_ii.py ================================================ class ConnectingGraph2: """ @param: n: An integer """ def __init__(self, n): if n < 1: return self.nodes = {} self.count = {} for i in range(n): self.nodes[i + 1] = i + 1 self.count[i + 1] = 1 def find(self, a): if self.nodes[a] == a: return a self.nodes[a] = self.find(self.nodes[a]) return self.nodes[a] """ @param: a: An integer @param: b: An integer @return: nothing """ def connect(self, a, b): root_a = self.find(a) root_b = self.find(b) if root_a != root_b: # Assign a as b's child set self.nodes[root_a] = root_b self.count[root_b] += self.count[root_a] """ @param: a: An integer @return: An integer """ def query(self, a): root_a = self.find(a) return self.count[root_a] ================================================ FILE: lintcode/591_connecting_graph_iii.py ================================================ class ConnectingGraph3: """ @param: n: An integer """ def __init__(self, n): if n < 1: return self.nodes = {} self.count = n for i in range(n): self.nodes[i + 1] = i + 1 def find(self, a): if self.nodes[a] == a: return a self.nodes[a] = self.find(self.nodes[a]) return self.nodes[a] """ @param: a: An integer @param: b: An integer @return: nothing """ def connect(self, a, b): root_a = self.find(a) root_b = self.find(b) if root_a != root_b: self.nodes[root_a] = root_b self.count -= 1 """ @return: An integer """ def query(self): return self.count ================================================ FILE: lintcode/594_strstr_ii.py ================================================ """ Rabin-Karp algorithm https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm Main Concept: given S == 'abcde' and T == 'bcd' 1. convert T into hashcode `tcode` 2. iterate S every `n` chars, and compare with `tcode`, continue to add `S[i]` and kick out `S[i - n]`, a b c d e a b c b c d c d e 3. so we need to reduce `ord(S[i - n]) * MG ** (n - 1)` every turns => calculate `MG ** (n - 1)` in advance to save time 4. if became negative, add `MOD` to back to positive Test Case: "abcde" "e" "abcdef" "bcd" """ class Solution: def strStr2(self, S, T): """ :type S: List[str] :type T: List[str] :rtype: int """ NOT_FOUND = -1 if S is not None and T is '': return 0 if not S or not T: return NOT_FOUND m, n = len(S), len(T) if n > m: return NOT_FOUND MOD = 1000000 # hashsize to mod MG = 31 # magic number A = ord('a') p = 1 # `p == MG ** (n - 1)` tcode = 0 # the code of T for i in range(n): tcode = (tcode * MG + ord(T[i]) - A) % MOD if i == 0: continue """ continue here since p only need `n - 1` times """ p = (p * MG) % MOD _code = 0 for i in range(m): """ kick out `S[i - n]` """ if i >= n: _code = (_code - (ord(S[i - n]) - A) * p) % MOD if _code < 0: _code += MOD """ Add `S[i]` """ _code = (_code * MG + ord(S[i]) - A) % MOD if _code == tcode and S[i - n + 1:i + 1] == T: return i - n + 1 return NOT_FOUND ================================================ FILE: lintcode/595_binary_tree_longest_consecutive_sequence.py ================================================ """ The longest consecutive path need to be from parent to child (cannot be the reverse). Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ class Solution: """ Bottom Up """ def longestConsecutive(self, root): """ :type root: TreeNode :rtype: int """ if not root: return 0 return self.divide_conquer(root)[0] def divide_conquer(self, node): if not node: return 0, 0 size = 1 down = 0 for branch in ('left', 'right'): child = getattr(node, branch) if not child: continue _size, _down = self.divide_conquer(child) if child.val - 1 == node.val and _down + 1 > down: down = _down + 1 if _size > size: size = _size if down + 1 > size: size = down + 1 return size, down class Solution: """ Top Down """ def longestConsecutive(self, root): """ :type root: TreeNode :rtype: int """ if not root: return 0 return self.divide_conquer(root, 0, 0) def divide_conquer(self, node, parent_val, _size): if not node: return 0 size = 1 if parent_val + 1 == node.val: size += _size left = self.divide_conquer(node.left, node.val, size) right = self.divide_conquer(node.right, node.val, size) return max(size, left, right) ================================================ FILE: lintcode/596_minimum_subtree.py ================================================ """ Definition of TreeNode: class TreeNode: def __init__(self, val): this.val = val this.left, this.right = None, None """ class Solution: min_sum = float('inf') node = None """ @param: root: the root of binary tree @return: the root of the minimum subtree """ def findSubtree(self, root): self._traversal(root) return self.node def _traversal(self, node): if not node: return 0 left_sum = self._traversal(node.left) right_sum = self._traversal(node.right) subtree_sum = left_sum + right_sum + node.val if subtree_sum < self.min_sum: self.min_sum = subtree_sum self.node = node return subtree_sum ================================================ FILE: lintcode/597_subtree_with_maximum_average.py ================================================ """ Definition of TreeNode: class TreeNode: def __init__(self, val): this.val = val this.left, this.right = None, None """ """ Test Case: {1,-5,11,1,2,4,-2} : subtree_avg = subtree_sum / subtree_size -> subtree_avg = subtree_sum * 1.0 / subtree_size {-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16} : max_avg = 0 -> max_avg = float('-inf') """ class Solution: max_avg = float('-inf') max_node = None """ @param: root: the root of binary tree @return: the root of the maximum average of subtree """ def findSubtree2(self, root): self._traversal(root) return self.max_node def _traversal(self, node): if not node: return 0, 0 left_sum, left_size = self._traversal(node.left) right_sum, right_size = self._traversal(node.right) subtree_sum = left_sum + right_sum + node.val subtree_size = left_size + right_size + 1 subtree_avg = subtree_sum * 1.0 / subtree_size if subtree_avg > self.max_avg: self.max_avg = subtree_avg self.max_node = node return subtree_sum, subtree_size ================================================ FILE: lintcode/598_zombie_in_matrix.py ================================================ class Solution: PEOPLE = 0 ZOMBIE = 1 WALL = 2 """ @param: grid: a 2D integer grid @return: an integer """ def zombie(self, grid): if not grid: return -1 vector = ( ( 0, -1), ( 0, 1), (-1, 0), ( 1, 0), ) m, n = len(grid), len(grid[0]) queue, _queue = [], None days = -1 for x in range(m): for y in range(n): if grid[x][y] == self.ZOMBIE: queue.append((x, y)) while queue: days += 1 _queue = [] for x, y in queue: for dx, dy in vector: _x = x + dx _y = y + dy if 0 <= _x < m and 0 <= _y < n \ and grid[_x][_y] == self.PEOPLE: grid[_x][_y] = self.ZOMBIE _queue.append((_x, _y)) queue = _queue for x in range(m): for y in range(n): if grid[x][y] == self.PEOPLE: return -1 return days ================================================ FILE: lintcode/599_insert_into_a_cyclic_sorted_list.py ================================================ """ Definition of ListNode class ListNode(object): def __init__(self, val, next=None): self.val = val self.next = next """ class Solution: """ @param: node: a list node in the list @param: x: An integer @return: the inserted new list node """ def insert(self, node, x): if not node: node = ListNode(x) node.next = node return node pre, cur = None, node while True: pre = cur cur = cur.next # in the list if pre.val <= x <= cur.val: break # at the boundary between minimum and maximum if (pre.val > cur.val) and (x < cur.val or x > pre.val): break # if `cur` have already traversed the list once if cur is node: break new_node = ListNode(x) new_node.next = cur pre.next = new_node return new_node ================================================ FILE: lintcode/59_3sum_closest.py ================================================ class Solution: def threeSumClosest(self, nums, target): """ :type nums: List[int] :type target: int :rtype: int """ if not nums or len(nums) < 3 or target is None: return -1 ans = INF = float('inf') n = len(nums) nums.sort() for a in range(n - 2): if a > 0 and nums[a] == nums[a - 1]: continue b, c = a + 1, n - 1 while b < c: total = nums[a] + nums[b] + nums[c] if total == target: return total if abs(total - target) < abs(ans - target): ans = total if total < target: b += 1 else: c -= 1 return ans if ans < INF else -1 ================================================ FILE: lintcode/5_kth_largest_element.py ================================================ class Solution: def kthLargestElement(self, k, nums): nums.sort() return nums[-k] ================================================ FILE: lintcode/600_smallest_rectangle_enclosing_black_pixels.py ================================================ class Solution: def minArea(self, image, x, y): """ :type image: list[str] :type x: int :type y: int :rtype: int """ if not image or not image[0]: return 0 m, n = len(image), len(image[0]) top = self.binary_search(image, 0, x, self.is_empty_row) bottom = self.binary_search(image, m - 1, x, self.is_empty_row) left = self.binary_search(image, 0, y, self.is_empty_col) right = self.binary_search(image, n - 1, y, self.is_empty_col) return (bottom - top + 1) * (right - left + 1) def binary_search(self, image, start, end, is_empty): check = None if start < end: check = lambda start, end: start + 1 < end else: check = lambda start, end: start - 1 > end while check(start, end): mid = (start + end) // 2 if is_empty(image, mid): start = mid else: end = mid return end if is_empty(image, start) else start def is_empty_row(self, image, x): for col in image[x]: if col == '1': return False return True def is_empty_col(self, image, y): for row in image: if row[y] == '1': return False return True ================================================ FILE: lintcode/601_flatten_2d_vector.py ================================================ """ Your Vector2D object will be instantiated and called as such: i, v = Vector2D(vec2d), [] while i.hasNext(): v.append(i.next()) """ class Vector2D: # @param vec2d {List[List[int]]} def __init__(self, vec2d): self.g = vec2d self.x = 0 self.y = 0 # @return {int} a next element def next(self): if not self.hasNext(): return -1 x = self.x y = self.y self.y += 1 return self.g[x][y] # @return {boolean} true if it has next element # or false def hasNext(self): while self.x < len(self.g): if self.y < len(self.g[self.x]): return True self.x += 1 self.y = 0 return False ================================================ FILE: lintcode/602_russian_doll_envelopes.py ================================================ """ DP: time => O(nlogn) REF: https://leetcode.com/problems/russian-doll-envelopes/discuss/82763 """ from bisect import bisect_left class Solution: """ @param: E: a number of envelopes with widths and heights @return: the maximum number of envelopes """ def maxEnvelopes(self, E): if not E or not E[0] or len(E[0]) != 2: return 0 E.sort(key=lambda e: (e[0], -e[1])) dp = [0] * len(E) size = 0 for _, h in E: i = bisect_left(dp, h, 0, size) dp[i] = h if i == size: size += 1 return size """ DP: TLE """ class Solution: """ @param: E: a number of envelopes with widths and heights @return: the maximum number of envelopes """ def maxEnvelopes(self, E): ans = 0 if not E: return ans n = len(E) """ `dp[i]` means the maximum number of envelopes if the `i`th envelope is outermost """ dp = [1] * n E.sort() for i in range(n): for j in range(i): if (E[j][0] < E[i][0] and E[j][1] < E[i][1] and dp[j] + 1 > dp[i]): dp[i] = dp[j] + 1 if dp[i] > ans: ans = dp[i] return ans ================================================ FILE: lintcode/603_largest_divisible_subset.py ================================================ class Solution: def largestDivisibleSubset(self, A): """ :type A: List[int] :rtype: List[int] """ ans = [] if not A: return ans n = len(A) if n == 1: return A A.sort() """ this problem similar with LIS `dp[i]` means the maximum size of subset end at `i` note that there is size, so init with `1` `pi[i]` records the previous num in ans, and allow to backtrack to find all num in ans `pe` means path end, to record the LDS end """ dp = [1] * n pi = [0] * n pe = max_size = 0 for i in range(n): for j in range(i): """ backtracking `A[i]` is larger than `A[j]`, so check `A[i] % A[j]` if its zero """ if A[i] % A[j] == 0 and dp[j] + 1 > dp[i]: dp[i] = dp[j] + 1 pi[i] = j if dp[i] > max_size: max_size = dp[i] pe = i ans = [0] * max_size for i in range(max_size - 1, -1, -1): ans[i] = A[pe] pe = pi[pe] return ans ================================================ FILE: lintcode/604_window_sum.py ================================================ class Solution: """ @param: A: a list of integers. @param: k: length of window. @return: the sum of the element inside the window at each moving. """ def winSum(self, A, k): ans = [] if (not A or not k or k <= 0 or len(A) < k): return ans _sum = 0 for i in range(len(A)): _sum += A[i] if i < k - 1: continue ans.append(_sum) _sum -= A[i - k + 1] return ans ================================================ FILE: lintcode/605_sequence_reconstruction.py ================================================ """ Main Concept: Topological Sort 1. record `edges` and `indegrees` from `seqs`, and only allow `num` in `org` 2. iterate `org` in order, check if there is only one path to get `org[-1]` """ class Solution: def sequenceReconstruction(self, org, seqs): """ :type org: list[int] :type seqs: list[list[int]] :rtype: bool """ if org is None or seqs is None: return False n = len(org) edges = dict.fromkeys(org) indeg = dict.fromkeys(org, 0) cnt = 0 for seq in seqs: if not seq: continue cnt += len(seq) for i in range(len(seq)): if not (1 <= seq[i] <= n): return False if not edges[seq[i]]: edges[seq[i]] = set() # dedup same edge if i > 0 and seq[i] not in edges[seq[i - 1]]: indeg[seq[i]] += 1 edges[seq[i - 1]].add(seq[i]) # for case: org == [1], seqs == [[], []] if cnt < n: return False for i in range(n - 1): if indeg[org[i]] != 0: return False if org[i + 1] not in edges[org[i]]: return False indeg[org[i + 1]] -= 1 return True ================================================ FILE: lintcode/606_kth_largest_element_ii.py ================================================ from heapq import heappop, heappush class Solution: """ @param: nums: an integer unsorted array @param: k: an integer from 1 to n @return: the kth largest element """ def kthLargestElement2(self, nums, k): if not nums or not k: return -1 heap = [] for num in nums: heappush(heap, num) if len(heap) > k: heappop(heap) return heappop(heap) ================================================ FILE: lintcode/607_two_sum_data_structure_design.py ================================================ class TwoSum: count = {} """ @param: number: An integer @return: nothing """ def add(self, number): if number in self.count: self.count[number] += 1 else: self.count[number] = 1 """ @param: value: An integer @return: Find if there exists any pair of numbers which sum is equal to the value. """ def find(self, value): for num in self.count: remaining = value - num if remaining not in self.count: continue if remaining != num: return True if remaining == num and self.count[num] > 1: return True return False ================================================ FILE: lintcode/608_two_sum_input_array_is_sorted.py ================================================ """ time: O(n) space: O(n) """ class Solution: """ @param: A: an array of Integer @param: target: target = A[index1] + A[index2] @return: [index1 + 1, index2 + 1] (index1 < index2) """ def twoSum(self, A, target): NOT_FOUND = [-1, -1] if not A: return NOT_FOUND remaining = {} for i in range(len(A)): if A[i] in remaining: return [ remaining[A[i]] + 1, i + 1, ] remaining[target - A[i]] = i return NOT_FOUND """ time: O(n) space: O(1) """ class Solution: """ @param: A: an array of Integer @param: target: target = A[index1] + A[index2] @return: [index1 + 1, index2 + 1] (index1 < index2) """ def twoSum(self, A, target): NOT_FOUND = [-1, -1] if not A: return NOT_FOUND left, right = 0, len(A) - 1 while left < right: _sum = A[left] + A[right] if _sum == target: return [left + 1, right + 1] if _sum < target: left += 1 else: right -= 1 return NOT_FOUND ================================================ FILE: lintcode/609_two_sum_less_than_or_equal_to_target.py ================================================ class Solution: """ @param: nums: an array of integer @param: target: an integer @return: an integer """ def twoSum5(self, nums, target): ans = 0 if not nums or len(nums) < 2: return ans nums.sort() left, right = 0, len(nums) - 1 while left < right: if nums[left] + nums[right] <= target: # the count of connections from `left` to `right` # e.g, from 1 to 4, 1-2, 1-3, 1-4, got 3 connections ans += right - left left += 1 else: right -= 1 return ans ================================================ FILE: lintcode/610_two_sum_difference_equals_to_target.py ================================================ """ time: O(n) space: O(n) it works even if it's not sorted """ class Solution: """ @param: A: an array of Integer @param: target: an integer @return: [index1 + 1, index2 + 1] (index1 < index2) """ def twoSum7(self, A, target): NOT_FOUND = [-1, -1] if not A or len(A) < 2: return NOT_FOUND remaining = {} for i in range(len(A)): """ if a - b = t => a = b + t """ if A[i] + target in remaining: return [ remaining[A[i] + target] + 1, i + 1 ] """ if b - a = t => a = b - t """ if A[i] - target in remaining: return [ remaining[A[i] - target] + 1, i + 1 ] remaining[A[i]] = i return NOT_FOUND """ time: O(n) space: O(n) needs to sort in advance """ class Solution: """ @param: A: an array of Integer @param: target: an integer @return: [index1 + 1, index2 + 1] (index1 < index2) """ def twoSum7(self, A, target): NOT_FOUND = [-1, -1] if not A or len(A) < 2: return NOT_FOUND if target < 0: target = -1 * target n = len(A) A = [(A[i], i) for i in range(n)] A.sort() left = 0 for right in range(1, n): while left + 1 < right and A[right][0] - A[left][0] > target: left += 1 if A[right][0] - A[left][0] == target: return sorted([ A[left][1] + 1, A[right][1] + 1 ]) return NOT_FOUND ================================================ FILE: lintcode/611_knight_shortest_path.py ================================================ """ Definition for a point. class Point: def __init__(self, a=0, b=0): self.x = a self.y = b """ class Solution: V = ( (-2, -1), ( 2, 1), (-2, 1), ( 2, -1), (-1, -2), ( 1, 2), (-1, 2), ( 1, -2), ) """ @param: G: a chessboard included 0 (false) and 1 (true) @param: S: a point @param: T: a point @return: the shortest path """ def shortestPath(self, G, S, T): if not G or not S or not T: return -1 INFINITY = float('inf') m, n = len(G), len(G[0]) min_steps = [[INFINITY] * n for _ in range(m)] queue = [S] _queue = None _x = _y = steps = 0 while queue: _queue = [] steps += 1 for P in queue: for dx, dy in self.V: _x = P.x + dx _y = P.y + dy if (0 <= _x < m and 0 <= _y < n and not G[_x][_y] and steps < min_steps[_x][_y]): if _x == T.x and _y == T.y: return steps min_steps[_x][_y] = steps _queue.append(Point(_x, _y)) queue = _queue return -1 ================================================ FILE: lintcode/612_k_closest_points.py ================================================ """ Definition for a point. class Point: def __init__(self, a=0, b=0): self.x = a self.y = b """ import heapq class Solution: """ max heap """ def kClosest(self, points, origin, k): """ :type points: list[Point] :type origin: Point :type k: int :rtype: list[Point] """ ans = [] if not points or not origin or not k: return ans for i in range(len(points)): distance = self.get_distance(origin, points[i]) heapq.heappush(ans, (-distance, i)) if len(ans) > k: heapq.heappop(ans) ans.sort(key=lambda a: (-a[0], points[a[1]].x, points[a[1]].y)) return [points[i] for _, i in ans] def get_distance(self, p, q): dx = p.x - q.x dy = p.y - q.y return dx * dx + dy * dy import heapq class Solution: """ min heap """ def kClosest(self, points, origin, k): """ :type points: list[Point] :type origin: Point :type k: int :rtype: list[Point] """ ans = [] if not points or not origin or not k: return ans heap = [] for i in range(len(points)): distance = self.get_distance(origin, points[i]) heapq.heappush(heap, (distance, i)) for _ in range(k): distance, i = heapq.heappop(heap) ans.append((distance, points[i])) ans.sort(key=lambda a: (a[0], a[1].x, a[1].y)) return [p for _, p in ans] def get_distance(self, p, q): dx = p.x - q.x dy = p.y - q.y return dx * dx + dy * dy ================================================ FILE: lintcode/613_high_five.py ================================================ """ Definition for a Record class Record: def __init__(self, id, score): self.id = id self.score = score """ from heapq import heappop, heappush class Solution: # @param {Record[]} results a list of # @return {dict(id, average)} find the average of 5 highest scores for each person # (student_id, average_score) def highFive(self, results): ans = {} if not results: return ans k = 5 top_k = {} for r in results: if r.id not in top_k: top_k[r.id] = [] heappush(top_k[r.id], r.score) if len(top_k[r.id]) > k: heappop(top_k[r.id]) _sum = 0 for id, scores in top_k.items(): _sum = 0 for score in scores: _sum += score ans[id] = _sum * 1.0 / k return ans ================================================ FILE: lintcode/614_binary_tree_longest_consecutive_sequence_ii.py ================================================ """ The path could be start and end at any node in the tree If use top down in this case, still need return the result from bottom => just use bottom up Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ class Solution: """ Bottom Up """ def longestConsecutive2(self, root): """ :type root: TreeNode :rtype: int """ if not root: return 0 return self.divide_conquer(root)[0] def divide_conquer(self, node): if not node: return 0, 0, 0 size = 1 up = down = 0 for branch in ('left', 'right'): child = getattr(node, branch) if not child: continue _size, _up, _down = self.divide_conquer(child) if child.val + 1 == node.val and _up + 1 > up: up = _up + 1 if child.val - 1 == node.val and _down + 1 > down: down = _down + 1 if _size > size: size = _size if up + down + 1 > size: size = up + down + 1 return size, up, down ================================================ FILE: lintcode/615_course_schedule.py ================================================ class Solution: def canFinish(self, n, P): """ :type n: int :type P: List[List[int]] :rtype: bool """ indeg = [0] * n edges = [[] for _ in range(n)] for c, p in P: indeg[c] += 1 edges[p].append(c) queue = [i for i in range(n) if indeg[i] == 0] for p in queue: for c in edges[p]: indeg[c] -= 1 if indeg[c] == 0: queue.append(c) return len(queue) == n ================================================ FILE: lintcode/616_course_schedule_ii.py ================================================ class Solution: def findOrder(self, n, prerequisites): """ :type n: int :type prerequisites: list[list[int]] :rtype: list[int] """ ans = [] if not n: return ans indeg = [0] * n edges = [[] for _ in range(n)] for c, p in prerequisites: indeg[c] += 1 edges[p].append(c) queue = [c for c in range(n) if indeg[c] == 0] for p in queue: for c in edges[p]: indeg[c] -= 1 if indeg[c] == 0: queue.append(c) if len(queue) != n: return [] return queue ================================================ FILE: lintcode/617_maximum_average_subarray.py ================================================ """ REF: https://stackoverflow.com/questions/13093602/finding-subarray-with-maximum-sum-number-of-elements#answer-13094527 You can use binary search. For a searched value `avg`, consider the array `B[i] = A[i] - avg`. Now find the maximum sum subarray of length at least `k`. This works because the average of a subarray of length `k` is `(A[i] + ... + A[i + k - 1]) / k`. So we have: `(A[i] + ... + A[i + k - 1]) / k >= avg` => `A[i] + ... + A[i + k - 1] >= avg * k` => `(A[i] - avg) + ... + (A[i + k - 1] - avg) >= 0` So, if you binary search for the average, by substracting it from each element, if you can find a non-negative sum subarray of length at least k, that is, find the maximum one and check if it's non-negative. then avg is a valid answer, continue to search in [avg, max_avg] to see if you can find a better one. If not, reduce search to [0, avg]. ans-2c ans-c ans ans+c ans+2c valid T T T F F """ class Solution: def maxAverage(self, nums, k): """ :type nums: list[int] :type k: int :rtype: float """ if not nums or not k: return 0.0 EPS = 1e-5 # ans MUST between `min(nums)` and `max(nums)` left = right = nums[0] for num in nums: if num < left: left = num if num > right: right = num # prefix sum s = [0] * (len(nums) + 1) while right - left > EPS: mid = (left + right) / 2.0 if self.is_valid(nums, k, mid, s): left = mid else: right = mid return left def is_valid(self, nums, k, mid, s): s[0] = smin = 0 for i in range(1, len(nums) + 1): s[i] = s[i - 1] + nums[i - 1] - mid if i < k: continue """ if there is a non-negative sum subarray of length at least k => it's valid even if just only one, return True immediately """ if s[i] >= smin: # s[i] - smin >= 0 return True if s[i - k + 1] < smin: smin = s[i - k + 1] return False ================================================ FILE: lintcode/618_search_graph_nodes.py ================================================ """ Test Case: {1} [0] 1 0 """ """ Definition for a undirected graph node class UndirectedGraphNode: def __init__(self, x): self.label = x self.neighbors = [] """ class Solution: """ @param {UndirectedGraphNode[]} graph a list of undirected graph node @param {dict} values a dict, @param {UndirectedGraphNode} node an Undirected graph node @param {int} target an integer @return {UndirectedGraphNode} a node """ def searchNode(self, graph, values, node, target): if not graph: return queue = [node] visited = {_node: False for _node in graph} for _node in queue: visited[_node] = True if values[_node] == target: return _node for _neighbor in _node.neighbors: if not visited[_neighbor]: queue.append(_neighbor) ================================================ FILE: lintcode/619_binary_tree_longest_consecutive_sequence_iii.py ================================================ """ The path could be start and end at any node in the tree Definition for a multi tree node. class MultiTreeNode(object): def __init__(self, x): self.val = x children = [] # children is a list of MultiTreeNode """ class Solution: """ Bottom Up """ def longestConsecutive3(self, root): """ :type root: MultiTreeNode :rtype: int """ if not root: return 0 return self.divide_conquer(root)[0] def divide_conquer(self, node): if not node: return 0, 0, 0 size = 1 up = down = 0 for child in node.children: if not child: continue _size, _up, _down = self.divide_conquer(child) if child.val + 1 == node.val and _up + 1 > up: up = _up + 1 if child.val - 1 == node.val and _down + 1 > down: down = _down + 1 if _size > size: size = _size if up + down + 1 > size: size = up + down + 1 return size, up, down ================================================ FILE: lintcode/61_search_for_a_range.py ================================================ class Solution: """ @param: A: an integer sorted array @param: target: an integer to be inserted @return: a list of length 2, [index1, index2] """ def searchRange(self, A, target): NOT_FOUND = [-1, -1] if not A: return NOT_FOUND n = len(A) left, mid, right = 0, 0, n - 1 while left + 1 < right: mid = left + (right - left) // 2 if A[mid] < target: left = mid else: right = mid start = left if A[left] == target else right left, mid, right = 0, 0, n - 1 while left + 1 < right: mid = left + (right - left) // 2 if A[mid] <= target: left = mid else: right = mid end = right if A[right] == target else left if start <= end: return [start, end] return NOT_FOUND ================================================ FILE: lintcode/620_maximum_subarray_iv.py ================================================ class Solution: """ @param: A: an array of integer @param: k: an integer @return: the largest sum """ def maxSubarray4(self, A, k): if not A: return 0 n = len(A) if n < k: return 0 ans = float('-inf') Smin = 0 S = [0] * (n + 1) for i in range(1, n + 1): S[i] = S[i - 1] + A[i - 1] """ in prefix sum, `i` means the size, and ignoring that iteration if `i` < `k` """ if i < k: continue if S[i] - Smin > ans: ans = S[i] - Smin """ to get the segment sum of `k` children and ended at `i` => [i - k + 1, i] """ if S[i - k + 1] < Smin: Smin = S[i - k + 1] return ans ================================================ FILE: lintcode/621_maximum_subarray_v.py ================================================ """ main concept: since the size of subarrays in [k1, k2] of `A` so when the iteration ended at `i` in `S` the start index should be in [i - k2, i - k1] in `S`, that is, [i - k2 + 1, i - k1 + 1] in `A` and then we using a queue to record the minimum of the index the ans is `S[i] - S[queue[0]]` """ from collections import deque class Solution: """ @param: A: an array of integers @param: k1: An integer @param: k2: An integer @return: the largest sum """ def maxSubarray5(self, A, k1, k2): if not A or not k2 or len(A) < k1: return 0 n = len(A) S = [0] * (n + 1) queue = deque() ans = float('-inf') for i in range(1, n + 1): S[i] = S[i - 1] + A[i - 1] """ if the minimum of index is less than `i - k2` kicked it off """ if queue and queue[0] < i - k2: queue.popleft() if i < k1: continue """ if the recent children are great than `S[i - k1]`, means the min sum is impossible to occur here """ while queue and S[queue[-1]] > S[i - k1]: queue.pop() queue.append(i - k1) if queue and S[i] - S[queue[0]] > ans: ans = S[i] - S[queue[0]] return ans ================================================ FILE: lintcode/622_frog_jump.py ================================================ """ BFS """ class Solution: def canCross(self, stones): """ :type stones: List[int] :rtype: bool """ if not stones: return False if len(stones) < 2: return True if stones[1] != 1: return False xs = set(stones) # to check in O(1) queue = [(stones[0], 0)] visited = set(queue) for pos, k in queue: for x in (k - 1, k, k + 1): if x <= 0 or pos + x not in xs: continue if (pos + x, x) in visited: continue if pos + x == stones[-1]: return True visited.add((pos + x, x)) queue.append((pos + x, x)) return False """ DP """ class Solution: def canCross(self, stones): """ :type stones: List[int] :rtype: bool """ if not stones: return False if len(stones) < 2: return True if stones[1] != 1: return False dp = {pos: set() for pos in stones} dp[stones[0]].add(0) for pos in stones: for k in dp[pos]: # k - 1 > 0 if k > 1 and pos + k - 1 in dp: dp[pos + k - 1].add(k - 1) if pos + k in dp: dp[pos + k].add(k) if pos + k + 1 in dp: dp[pos + k + 1].add(k + 1) return bool(dp[stones[-1]]) ================================================ FILE: lintcode/623_k_edit_distance.py ================================================ class TrieNode: def __init__(self): self.end_of = None self.children = {} class Trie: def __init__(self): self.root = TrieNode() def put(self, word): if not isinstance(word, str): return node = self.root for c in word: if c not in node.children: node.children[c] = TrieNode() node = node.children[c] node.end_of = word class Solution: def kDistance(self, words, target, k): """ :type words: list[str] :type target: str :type k: int :rtype: list[str] """ trie = Trie() for word in words: trie.put(word) ans = [] dp = [i for i in range(len(target) + 1)] self.dfs(trie.root, k, target, ans, dp) return ans def dfs(self, node, k, target, ans, pre): n = len(target) if node.end_of is not None and pre[n] <= k: ans.append(node.end_of) dp = [0] * (n + 1) for c in node.children: dp[0] = pre[0] + 1 for i in range(1, n + 1): if target[i - 1] == c: dp[i] = min( dp[i - 1] + 1, pre[i] + 1, pre[i - 1] ) else: dp[i] = min( dp[i - 1] + 1, pre[i] + 1, pre[i - 1] + 1 ) self.dfs(node.children[c], k, target, ans, dp) ================================================ FILE: lintcode/624_remove_substrings.py ================================================ class Solution: """ @param: s: a string @param: D: a set of n substrings @return: the minimum length """ def minLength(self, s, D): if not s: return 0 _min = len(s) queue = [s] visited = set([s]) """ bfs s /----/---\----\ s-d1 s-d2 s-d3 s-d4 ... ... ... ... """ for s in queue: for d in D: found = s.find(d) while found != -1: _s = s[:found] + s[found + len(d):] found = s.find(d, found + 1) if _s in visited: continue if len(_s) < _min: _min = len(_s) queue.append(_s) visited.add(_s) return _min ================================================ FILE: lintcode/625_partition_array_ii.py ================================================ """ Rainbow Sort """ class Solution: """ @param: A: an integer array @param: low: An integer @param: high: An integer @return: """ def partition2(self, A, low, high): if not A: return left, right = 0, len(A) - 1 i = 0 while i < right: if A[i] < low: A[left], A[i] = A[i], A[left] left += 1 i += 1 elif A[i] > high: A[right], A[i] = A[i], A[right] right -= 1 else: i += 1 ================================================ FILE: lintcode/630_knight_shortest_path_ii.py ================================================ """ DFS """ class Solution: """ @param: G: a chessboard included 0 and 1 @return: the shortest path """ def shortestPath2(self, G): NOT_FOUND = -1 if not G or not G[0] or G[0][0] == 1: return NOT_FOUND V = ( (-1, 2), ( 1, 2), (-2, 1), ( 2, 1), ) m, n = len(G), len(G[0]) queue = [(0, 0)] turns = {(0, 0): 0} for x, y in queue: for dx, dy in V: _x = x + dx _y = y + dy if 0 <= _x < m and 0 <= _y < n and G[_x][_y] == 0: if (_x, _y) in turns: continue turns[_x, _y] = turns[x, y] + 1 if _x == m - 1 and _y == n - 1: return turns[_x, _y] queue.append((_x, _y)) return NOT_FOUND """ DP """ class Solution: """ @param: G: a chessboard included 0 and 1 @return: the shortest path """ def shortestPath2(self, G): if not G or not G[0] or G[0][0] == 1: return -1 INFINITY = float('inf') m, n = len(G), len(G[0]) dp = [[INFINITY] * 3 for _ in range(m)] pre2 = pre1 = curr = 0 dp[0][curr] = 0 """ x and y is changed since its need from left to right """ for y in range(1, n): pre2, pre1 = pre1, curr curr = y % 3 for x in range(m): dp[x][curr] = INFINITY if G[x][y] == 1: continue if (x >= 2 and y >= 1 and dp[x - 2][pre1] < INFINITY and dp[x - 2][pre1] + 1 < dp[x][curr]): dp[x][curr] = dp[x - 2][pre1] + 1 if (x >= 1 and y >= 2 and dp[x - 1][pre2] < INFINITY and dp[x - 1][pre2] + 1 < dp[x][curr]): dp[x][curr] = dp[x - 1][pre2] + 1 if (x + 1 < m and y >= 2 and dp[x + 1][pre2] < INFINITY and dp[x + 1][pre2] + 1 < dp[x][curr]): dp[x][curr] = dp[x + 1][pre2] + 1 if (x + 2 < m and y >= 1 and dp[x + 2][pre1] < INFINITY and dp[x + 2][pre1] + 1 < dp[x][curr]): dp[x][curr] = dp[x + 2][pre1] + 1 return dp[m - 1][curr] if dp[m - 1][curr] < INFINITY else -1 ================================================ FILE: lintcode/633_find_the_duplicate_number.py ================================================ """ Binary Searching `a`: the duplicated num `cnt(b)`: the cnt of children less than or equal `b` for each `num < a`: `cnt(num) == num` for each `num >= a`: `cnt(num) > num` """ class Solution: def findDuplicate(self, A): """ :type A: List[int] :rtype: int """ if not A: return -1 left, right = 1, len(A) - 1 while left + 1 < right: mid = (left + right) // 2 if self.after_dup(A, mid): right = mid else: left = mid if self.after_dup(A, left): return left if self.after_dup(A, right): return right return -1 def after_dup(self, A, mid): cnt = 0 for a in A: if a <= mid: cnt += 1 if cnt > mid: return True return False """ Floyd Circle Detection example: [5,4,4,3,2,1] i 0, 1, 2, 3, 4, 5 a 5, 4, 4, 3, 2, 1 ------------------- s f f s s f f s sf ------------------- f s s f f s sf """ class Solution: """ @param: A: an array containing n + 1 integers which is between 1 and n @return: the duplicate one """ def findDuplicate(self, A): if not A: return -1 slow = A[0] fast = A[A[0]] while slow != fast: slow = A[slow] fast = A[A[fast]] fast = 0 while slow != fast: slow = A[slow] fast = A[fast] return slow ================================================ FILE: lintcode/634_word_squares.py ================================================ """ Trie + DFS """ """ TLE: DFS """ class Solution: def wordSquares(self, words): """ :type words: list[str] :rtype: list[list[str]] """ ans = [] if not words: return ans self.dfs(words, len(words[0]), ans, []) return ans def dfs(self, words, n, ans, path): if (len(path) == n and self.is_valid(path)): ans.append(path[:]) return if len(path) >= n: return for i in range(len(words)): path.append(words[i]) self.dfs(words, n, ans, path) path.pop() def is_valid(self, path): if not path or len(path) != len(path[0]): return False for i in range(1, len(path)): for j in range(i): if path[i][j] != path[j][i]: return False return True ================================================ FILE: lintcode/635_boggle_game.py ================================================ class TrieNode: def __init__(self): self.children = {} self.end_of = None class Solution: def boggleGame(self, board, words): """ :type board: list[list[str]] :type words: list[str] :rtype: int """ self.ans = 0 if not board or not board[0]: return self.ans root = TrieNode() for word in words: self.put(root, word) m, n = len(board), len(board[0]) visited = set() for x in range(m): for y in range(n): self.dfs(board, x, y, root, visited, 0) return self.ans def dfs(self, board, i, j, root, visited, cnt): m, n = len(board), len(board[0]) for x in range(i, m): for y in range(j, n): next_words = [] self.find_next_words(board, x, y, visited, cnt, root, next_words, []) for pos in next_words: for p in pos: visited.add(p) self.dfs(board, x, y, root, visited, cnt + 1) for p in pos: visited.discard(p) j = 0 def find_next_words(self, board, x, y, visited, cnt, node, next_words, path): if (x, y) in visited or board[x][y] not in node.children: return m, n = len(board), len(board[0]) node = node.children[board[x][y]] path.append((x, y)) visited.add((x, y)) if node.end_of is not None: next_words.append(path[:]) self.ans = max(self.ans, cnt + 1) else: for dx, dy in ( (-1, 0), (1, 0), (0, -1), (0, 1), ): _x = x + dx _y = y + dy if not (0 <= _x < m and 0 <= _y < n): continue self.find_next_words(board, _x, _y, visited, cnt, node, next_words, path) path.pop() visited.discard((x, y)) def put(self, root, word): node = root for c in word: if c not in node.children: node.children[c] = TrieNode() node = node.children[c] node.end_of = word ================================================ FILE: lintcode/636_132_pattern.py ================================================ class Solution: """ @param: A: a list of n integers @return: true if there is a 132 pattern or false """ def find132pattern(self, A): if not A: return False stack = [float('-inf')] for i in range(len(A) - 1, -1, -1): if A[i] < stack[-1]: return True v = None while stack and A[i] > stack[-1]: v = stack.pop() stack.append(A[i]) if v is not None: stack.append(v) return False ================================================ FILE: lintcode/646_first_position_unique_character.py ================================================ class Solution: """ @param: s: a string @return: it's index """ def firstUniqChar(self, s): if not s: return -1 D = {} for c in s: D[c] = D.get(c, 0) + 1 i = 0 for c in s: if D[c] == 1: return i i += 1 return -1 ================================================ FILE: lintcode/647_substring_anagrams.py ================================================ """ REF: https://leetcode.com/problems/find-all-anagrams-in-a-string/discuss/92007/ """ class Solution: def findAnagrams(self, s, t): """ :type s: str :type t: str :rtype: List[int] """ ans = [] if not s or not t or len(t) > len(s): return ans F = {} for c in t: F[c] = F.get(c, 0) + 1 n, m, cnt = len(s), len(t), len(F) left = right = 0 while right < n: if s[right] in F: F[s[right]] -= 1 if F[s[right]] == 0: cnt -= 1 right += 1 while cnt == 0: if s[left] in F: F[s[left]] += 1 if F[s[left]] == 1: cnt += 1 if right - left == m: ans.append(left) left += 1 return ans ================================================ FILE: lintcode/64_merge_sorted_array.py ================================================ class Solution: def mergeSortedArray(self, a, m, b, n): """ :type a: List[int] :type m: int :type b: List[int] :type n: int :rtype: void Do not return anything, modify nums1 in-place instead. """ i, j = m - 1, n - 1 k = m + n - 1 while i >= 0 and j >= 0: if a[i] > b[j]: a[k] = a[i] i -= 1 else: a[k] = b[j] j -= 1 k -= 1 while j >= 0: a[k] = b[j] j -= 1 k -= 1 ================================================ FILE: lintcode/654_sparse_matrix_multiplication.py ================================================ class Solution: """ @param A: a sparse matrix @param B: a sparse matrix @return: the result of A * B """ def multiply(self, A, B): if not A or not B or len(A[0]) != len(B): return [] m, n = len(A), len(B[0]) l = len(B) ans = [[0] * n for _ in range(m)] for i in range(m): for j in range(n): for k in range(l): ans[i][j] += A[i][k] * B[k][j] return ans ================================================ FILE: lintcode/655_big_integer_addition.py ================================================ class Solution: def addStrings(self, a, b): """ :type a: str :type b: str :rtype: str """ if not a and not b: return '' if not a: return b if not b: return a m, n = len(a), len(b) idx = max(m, n) ans = [''] * (idx + 1) i = m - 1 j = n - 1 carry = 0 zero = ord('0') while i >= 0 and j >= 0: carry += ord(a[i]) + ord(b[j]) - 2 * zero ans[idx] = str(carry % 10) carry //= 10 idx -= 1 i -= 1 j -= 1 while i >= 0: carry += ord(a[i]) - zero ans[idx] = str(carry % 10) carry //= 10 idx -= 1 i -= 1 while j >= 0: carry += ord(b[j]) - zero ans[idx] = str(carry % 10) carry //= 10 idx -= 1 j -= 1 if carry: ans[0] = str(carry) else: ans = ans[1:] return ''.join(ans) ================================================ FILE: lintcode/656_big_integer_multiplication.py ================================================ class Solution: def multiply(self, a, b): """ :type a: str :type b: str :rtype: str """ if not a or not b or a == '0' or b == '0': return '0' m, n = len(a), len(b) tmp = [0] * (m + n) for i in range(m - 1, -1, -1): carry = 0 for j in range(n - 1, -1, -1): carry += tmp[i + j + 1] + int(a[i]) * int(b[j]) tmp[i + j + 1] = carry % 10 carry //= 10 tmp[i] = carry i = 0 while tmp[i] == 0: i += 1 return ''.join([str(j) for j in tmp[i:]]) ================================================ FILE: lintcode/65_median_of_two_sorted_arrays.py ================================================ """ Heap """ import heapq class Solution: def findMedianSortedArrays(self, a, b): """ :type a: list :type b: list :rtype: float """ heap = [] n = 0 for nums in (a, b): if not nums: continue n += len(nums) heapq.heappush(heap, (nums[0], nums, 0)) if n == 0: return 0.0 num = 0 for _ in range((n + 1) // 2): num, nums, i = heapq.heappop(heap) i += 1 if i < len(nums): heapq.heappush(heap, (nums[i], nums, i)) if n & 1 == 1: return num * 1.0 _num = heapq.heappop(heap)[0] return (num + _num) / 2.0 """ Decreasing Approaching """ class Solution: """ @param: A: An integer array @param: B: An integer array @return: a double whose format is *.5 or *.0 """ def findMedianSortedArrays(self, A, B): n = len(A) + len(B) median = self.find_kth(A, 0, B, 0, n // 2 + 1) if n % 2 == 1: return median _median = self.find_kth(A, 0, B, 0, n // 2) return (median + _median) / 2.0 def find_kth(self, A, i, B, j, k): """ example: A: [1, 2, 3, 4, 5, 6] B: [2, 3, 4, 5] m + n = 10, median is `(5th + 6th) / 2` the process below is to find the `6th` r1/ k = 6 1 2 |3| 4 5 6 2 3 |4| 5 _a = _b = 2 a = 3, b = 4 a < b r2/ k = 3 -1--2--3-|4| 5 6 |2| 3 4 5 _a = 3, _b = 0 a = 4, b = 2 a > b r3/ k = 2 -1--2--3-|4| 5 6 -2-|3| 4 5 _a = 3, _b = 1 a = 4, b = 3 a > b r4/ k = 1 -1--2--3-|4| 5 6 -2--3-|4| 5 since k == 1 return min(4, 4) = `4` """ if i >= len(A): return B[j + k - 1] if j >= len(B): return A[i + k - 1] if k == 1: return min(A[i], B[j]) _a = i + k // 2 - 1 _b = j + k // 2 - 1 a = A[_a] if _a < len(A) else float('inf') b = B[_b] if _b < len(B) else float('inf') if a < b: return self.find_kth(A, i + k // 2, B, j, k - k // 2) else: return self.find_kth(A, i, B, j + k // 2, k - k // 2) ================================================ FILE: lintcode/662_guess_number_game.py ================================================ """ The guess API is already defined for you. @param num, your guess @return -1 if my number is lower, 1 if my number is higher, otherwise return 0 you can call Guess.guess(num) """ class Solution: def guessNumber(self, n): """ :type n: int :rtype: int """ if not n or n <= 1: return 1 left, right = 1, n while left + 1 < right: mid = (left + right) // 2 res = Guess.guess(mid) if res == 0: return mid elif res == 1: left = mid else: right = mid return left if Guess.guess(left) == 0 else right ================================================ FILE: lintcode/664_counting_bits.py ================================================ class Solution: """ @param: num: a non negative integer number @return: an array represent the number of 1's in their binary """ def countBits(self, num): if not num: return [0] upper_bound = num + 1 F = [0] * upper_bound for i in range(1, upper_bound): """ 1. `i & (i - 1)` must be less than `i`, since `0 & n` is `0` => `F[i & (i - 1)]` must have been calculated 2. `+ 1` means the removed `1` in bit """ F[i] = F[i & (i - 1)] + 1 return F ================================================ FILE: lintcode/667_longest_palindromic_subsequence.py ================================================ class Solution: """ @param: s: the maximum length of s is 1000 @return: the longest palindromic subsequence's length """ def longestPalindromeSubseq(self, s): if not s: return 0 n = len(s) if n == 1: return 1 # `dp[i][j]` means the length of the longest subsequence # included in `s[i:j+1]` dp = [[0] * n for _ in range(n)] ans = 0 start = end = 0 for end in range(n): dp[end][end] = 1 if n < 1: continue start = end - 1 if s[start] == s[end]: dp[start][end] = 2 else: dp[start][end] = 1 for size in range(3, n + 1): for start in range(n - size + 1): end = start + size - 1 dp[start][end] = max(dp[start][end - 1], dp[start + 1][end]) if s[start] == s[end]: dp[start][end] = max( dp[start][end], dp[start + 1][end - 1] + 2 ) if dp[start][end] > ans: ans = dp[start][end] return ans ================================================ FILE: lintcode/668_ones_and_zeroes.py ================================================ """ optimized space complexity """ class Solution: """ @param: strs: an array with strings include only 0 and 1 @param: m: An integer @param: n: An integer @return: find the maximum number of strings """ def findMaxForm(self, strs, m, n): if not strs: return 0 """ `dp[j][k]` means the current str can be made up of `j` 0s and `k` 1s """ dp = [[0] * (n + 1) for _ in range(m + 1)] c0 = c1 = 0 for s in strs: c0 = s.count('0') c1 = len(s) - c0 for j in range(m, c0 - 1, -1): for k in range(n, c1 - 1, -1): """ case 1: included current `strs[i - 1]` case 2: not included current `strs[i - 1]`, same as previous """ dp[j][k] = max( dp[j][k], dp[j - c0][k - c1] + 1 ) return dp[m][n] """ origin """ class Solution: """ @param: strs: an array with strings include only 0 and 1 @param: m: An integer @param: n: An integer @return: find the maximum number of strings """ def findMaxForm(self, strs, m, n): if not strs: return 0 l = len(strs) """ `dp[i][j][k]` means the pre- `i`th strs can be made up of `j` 0s and `k` 1s dp[0][j][k] = 0 """ dp = [[[0] * (n + 1) for _ in range(m + 1)] for _ in range(l + 1)] c0 = c1 = 0 for i in range(1, l + 1): c0 = strs[i - 1].count('0') c1 = len(strs[i - 1]) - c0 for j in range(m + 1): for k in range(n + 1): """ case 1: included current `strs[i - 1]` """ if j >= c0 and k >= c1: dp[i][j][k] = dp[i - 1][j - c0][k - c1] + 1 """ case 2: not included current `strs[i - 1]`, same as previous """ if dp[i - 1][j][k] > dp[i][j][k]: dp[i][j][k] = dp[i - 1][j][k] return dp[l][m][n] ================================================ FILE: lintcode/669_coin_change.py ================================================ class Solution: """ BFS """ def coinChange(self, coins, amount): """ :type coins: List[int] :type amount: int :rtype: int """ ans = 0 if not coins or not amount: return ans queue, _queue = [0], [] visited = set(queue) while queue: ans += 1 for a in queue: for c in coins: _a = a + c if _a == amount: return ans if _a > amount or _a in visited: continue visited.add(_a) _queue.append(_a) queue, _queue = _queue, [] return -1 class Solution: """ DP: TLE """ def coinChange(self, coins, amount): """ :type coins: List[int] :type amount: int :rtype: int """ if not coins or not amount: return 0 INF = float('inf') dp = [INF] * (amount + 1) dp[0] = 0 for c in coins: for a in range(c, amount + 1): # if a < c: continue dp[a] = min(dp[a], dp[a - c] + 1) return dp[amount] if dp[amount] < INF else -1 ================================================ FILE: lintcode/66_binary_tree_preorder_traversal.py ================================================ """ Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ class Solution: """ @param: root: A Tree @return: Preorder in ArrayList which contains node values. """ def preorderTraversal(self, root): ans = [] if not root: return ans self._traversal(root, ans) return ans def _traversal(self, node, res): if not node: return res.append(node.val) self._traversal(node.left, res) self._traversal(node.right, res) ================================================ FILE: lintcode/676_decode_ways_ii.py ================================================ class Solution: """ @param s: a message being encoded @return: an integer """ def numDecodings(self, s): if not s or s == '0': return 0 n = len(s) MOD = 10 ** 9 + 7 dp = [0] * (n + 1) dp[0] = 1 if s[0] == '*': dp[1] = 9 # 9 * dp[0] elif s[0] != '0': dp[1] = 1 # dp[0] for i in range(2, n + 1): if s[i - 1] == '*': dp[i] += 9 * dp[i - 1] if s[i - 2] == '*': dp[i] += 15 * dp[i - 2] elif s[i - 2] == '1': dp[i] += 9 * dp[i - 2] elif s[i - 2] == '2': dp[i] += 6 * dp[i - 2] dp[i] %= MOD continue if s[i - 1] != '0': dp[i] += dp[i - 1] if s[i - 2] == '*': if int(s[i - 1]) <= 6: dp[i] += 2 * dp[i - 2] else: dp[i] += dp[i - 2] elif 10 <= int(s[i - 2:i]) <= 26: dp[i] += dp[i - 2] dp[i] %= MOD return dp[n] ================================================ FILE: lintcode/67_binary_tree_inorder_traversal.py ================================================ """ Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ class Solution: """ @param: root: A Tree @return: Inorder in ArrayList which contains node values. """ def inorderTraversal(self, root): ans = [] if not root: return ans self._traversal(root, ans) return ans def _traversal(self, node, res): if not node: return self._traversal(node.left, res) res.append(node.val) self._traversal(node.right, res) ================================================ FILE: lintcode/689_two_sum_bst_edtion.py ================================================ """ Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ class Solution: """ @param: : the root of tree @param: : the target sum @return: two numbers from tree which sum is n """ def twoSum(self, root, n): self.left = self.right = None self.head = self.tail = root self.pre() self.nxt() while self.left != self.right: _sum = self.left.val + self.right.val if _sum == n: return [self.left.val, self.right.val] if _sum < n: self.nxt() else: self.pre() def pre(self): while self.tail: cur = self.tail.right if cur and cur != self.right: while cur.left and cur.left != self.tail: cur = cur.left if cur.left == self.tail: self.right = self.tail cur.left = None self.tail = self.tail.left break else: cur.left = self.tail self.tail = self.tail.right else: self.right = self.tail self.tail = self.tail.left break def nxt(self): while self.head: cur = self.head.left if cur and cur != self.left: while cur.right and cur.right != self.head: cur = cur.right if cur.right == self.head: self.left = self.head cur.right = None self.head = self.head.right break else: cur.right = self.head self.head = self.head.left else: self.left = self.head self.head = self.head.right break ================================================ FILE: lintcode/68_binary_tree_postorder_traversal.py ================================================ """ Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ class Solution: """ @param: root: A Tree @return: Postorder in ArrayList which contains node values. """ def postorderTraversal(self, root): ans = [] if not root: return ans stack = [] node = last_visit = root while node or stack: while node: stack.append(node) node = node.left node = stack[-1] if (not node.right or last_visit is node.right): stack.pop() ans.append(node.val) last_visit = node node = None else: node = node.right return ans ================================================ FILE: lintcode/69_binary_tree_level_order_traversal.py ================================================ """ Main Concept: 1. Use `_queue` to collect node in current level 2. Use `level_values` to collect the val of node in current level 3. After traverse the current level, append `level_values` to answer and reset `queue` as `_queue` Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ class Solution: def levelOrder(self, root): """ :type root: TreeNode :rtype: List[List[int]] """ ans = [] if not root: return ans queue, _queue = [root], [] while queue: ans.append([]) for node in queue: if node.left: _queue.append(node.left) if node.right: _queue.append(node.right) ans[-1].append(node.val) queue, _queue = _queue, [] return ans ================================================ FILE: lintcode/6_merge_two_sorted_arrays.py ================================================ class Solution: """ @param: A: sorted integer array A @param: B: sorted integer array B @return: A new sorted integer array """ def mergeSortedArray(self, A, B): if not A: return B if not B: return A m, n = len(A), len(B) ans = [0] * (m + n) i = j = index = 0 while i < m and j < n: if A[i] < B[j]: ans[index] = A[i] i += 1 else: ans[index] = B[j] j += 1 index += 1 while i < m: ans[index] = A[i] i += 1 index += 1 while j < n: ans[index] = B[j] j += 1 index += 1 return ans ================================================ FILE: lintcode/70_binary_tree_level_order_traversal_ii.py ================================================ """ Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ class Solution: """ @param: root: A tree @return: buttom-up level order a list of lists of integer """ def levelOrderBottom(self, root): ans = [] if not root: return ans preorder = [(root, 1)] self.dfs(root, ans, preorder, 0) """ cannot do append in dfs, since it may be the deepest node is at right child """ height = len(ans) for node, level in preorder: ans[height - level].append(node.val) return ans def dfs(self, node, ans, preorder, parent_at): if len(ans) < preorder[parent_at][1]: ans.append([]) depth = preorder[parent_at][1] + 1 if node.left: preorder.append((node.left, depth)) self.dfs(node.left, ans, preorder, len(preorder) - 1) if node.right: preorder.append((node.right, depth)) self.dfs(node.right, ans, preorder, len(preorder) - 1) ================================================ FILE: lintcode/717_tree_longest_path_with_same_value.py ================================================ """ REF: https://blog.csdn.net/zhaohengchuan/article/details/78833501 """ class Solution: def LongestPathWithSameValue(self, a, e): """ :type a: list[int] :type e: list[int] :rtype: int """ if not a or len(a) <= 1: return 0 neibs = [[] for _ in range(len(a) + 1)] # neighbors # to build the node connection for i in range(0, len(e), 2): neibs[e[i]].append(e[i + 1]) neibs[e[i + 1]].append(e[i]) self.ans = 0 res = self.dfs(0, 1, a, neibs) return max(self.ans, res) def dfs(self, root, curr, a, neibs): tmp = [] for neib in neibs[curr]: # ignore if the neib is curr's parent if neib == root: continue res = self.dfs(curr, neib, a, neibs) # incr the res and append to tmp # if both `neib` and `curr` have same value if a[neib - 1] == a[curr - 1]: tmp.append(res + 1) # to prevent len(tmp) == 0 tmp.extend((0, 0)) tmp.sort(reverse=True) self.ans = max(self.ans, tmp[0] + tmp[1]) return tmp[0] ================================================ FILE: lintcode/718_repeat_string.py ================================================ class Solution: def repeatedString(self, a, b): """ :type a: str :type b: str :rtype: int """ if len(b) <= len(a) and b in a: return 1 if not a or not b: return -1 ans = b.count(a) c = b.split(a) if c[0] and a.endswith(c[0]): ans += 1 if c[-1] and a.startswith(c[-1]): ans += 1 return ans if a.startswith(c[-1]) and a.endswith(c[0]) else -1 ================================================ FILE: lintcode/71_binary_tree_zigzag_level_order_traversal.py ================================================ """ Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ class Solution: """ @param: root: A Tree @return: A list of lists of integer include the zigzag level order traversal of its nodes' values. """ def zigzagLevelOrder(self, root): ans = [] if not root: return ans queue = [root] while queue: _queue = [] ans.append([]) for node in queue: if node.left: _queue.append(node.left) if node.right: _queue.append(node.right) ans[-1].append(node.val) if len(ans) % 2 == 0: ans[-1].reverse() queue = _queue return ans ================================================ FILE: lintcode/724_minimum_partition.py ================================================ class Solution: def findMin(self, nums): """ :type nums: list[int] :rtype: int """ if not nums: return 0 target = sum(nums) dp = [False] * (target + 1) dp[0] = True ans = float('inf') for num in nums: for i in range(target, num - 1, -1): dp[i] = dp[i] or dp[i - num] if dp[i]: ans = min( ans, abs(target - i * 2) ) return ans ================================================ FILE: lintcode/725_boolean_parenthesization.py ================================================ """ REF: https://blog.csdn.net/zhaohengchuan/article/details/78937943 """ class Solution: def countParenth(self, symb, oper): """ :type symb: list[str] :type oper: list[str] :rtype: int """ if not symb or not oper: return 0 n = len(symb) """ `t[l][r]` means the ways to evaluate True in `symb[i:j]` """ t = [[0] * n for _ in range(n)] f = [[0] * n for _ in range(n)] for i in range(n): if symb[i] == 'T': t[i][i] = 1 else: f[i][i] = 1 for r in range(n): for l in range(r - 1, -1, -1): t[l][r] = 0 f[l][r] = 0 for i in range(l, r): if oper[i] == '&': t[l][r] += t[l][i] * t[i + 1][r] f[l][r] += ( (t[l][i] + f[l][i]) * (t[i + 1][r] + f[i + 1][r]) - t[l][i] * t[i + 1][r] ) elif oper[i] == '|': t[l][r] += ( (t[l][i] + f[l][i]) * (t[i + 1][r] + f[i + 1][r]) - f[l][i] * f[i + 1][r] ) f[l][r] += f[l][i] * f[i + 1][r] elif oper[i] == '^': t[l][r] += t[l][i] * f[i + 1][r] + f[l][i] * t[i + 1][r] f[l][r] += t[l][i] * t[i + 1][r] + f[l][i] * f[i + 1][r] return t[0][n - 1] ================================================ FILE: lintcode/729_last_digit_by_factorial_divide.py ================================================ class Solution: def computeLastDigit(self, a, b): """ :type a: int :type b: int :rtype: int """ if not a or not b: return 0 if a == b: return 1 ans = 1 for num in range(a + 1, b + 1): if ans == 0: return 0 ans *= num % 10 ans %= 10 return ans ================================================ FILE: lintcode/745_palindromic_ranges.py ================================================ class Solution: def PalindromicRanges(self, left, right): """ :type left: int :type right: int :rtype: int """ if left > right: return 0 if left == right: return 1 dp = [0] * (right - left + 2) # n + 1, n = right - left + 1 # dp[0] = 0 for num in range(left, right + 1): if self.is_palindrome(num): dp[num - left + 1] = dp[num - left] + 1 else: dp[num - left + 1] = dp[num - left] ans = 0 for i in range(1, right - left + 2): for j in range(i): if ((dp[i] - dp[j]) & 1 == 0): ans += 1 return ans def is_palindrome(self, num): if num // 10 == 0: return True s = str(num) left, right = 0, len(s) - 1 while left < right: if s[left] != s[right]: return False left += 1 right -= 1 return True ================================================ FILE: lintcode/74_first_bad_version.py ================================================ """ Main Concept ver. | 1 2 3 4 5 good | T T F F F class SVNRepo: @classmethod def isBadVersion(cls, id) # Run unit tests to check whether verison `id` is a bad version # return true if unit tests passed else false. You can use SVNRepo.isBadVersion(10) to check whether version 10 is a bad version. """ class Solution: def findFirstBadVersion(self, n): """ :type n: int :rtype: int """ if not n: return 0 left, right = 1, n while left + 1 < right: mid = (left + right) // 2 if isBadVersion(mid): right = mid else: left = mid return left if isBadVersion(left) else right ================================================ FILE: lintcode/752_rogue_knight_sven.py ================================================ class Solution: def getNumberOfWays(self, n, m, limit, cost): """ :type n: int :type m: int :type limit: int :type cost: List[int] :rtype: int """ """ `dp[i][j]` means the ways to reach planet `i` and still keep `j` coins """ dp = [[0] * (m + 1) for _ in range(n + 1)] dp[0][m] = 1 for i in range(1, n + 1): for j in range(m + 1): for k in range(max(0, i - limit), i): if j + cost[i] > m: continue dp[i][j] += dp[k][j + cost[i]] return sum(dp[n]) ================================================ FILE: lintcode/75_find_peak_element.py ================================================ class Solution: def findPeak(self, nums): """ :type nums: list :rtype: int """ if not nums: return -1 left, right = 0, len(nums) - 1 while left + 1 < right: mid = (left + right) // 2 if nums[mid] < nums[mid + 1]: left = mid else: right = mid return right if nums[left] < nums[right] else left ================================================ FILE: lintcode/76_longest_increasing_subsequence.py ================================================ """ Binary Searching """ class Solution: def longestIncreasingSubsequence(self, A): """ :type A: List[int] :rtype: int the iteration of B: [-inf, 0, inf, inf, inf, inf, inf, inf, inf] [-inf, 0, 8, inf, inf, inf, inf, inf, inf] [-inf, 0, 4, inf, inf, inf, inf, inf, inf] [-inf, 0, 4, 12, inf, inf, inf, inf, inf] [-inf, 0, 2, 12, inf, inf, inf, inf, inf] [-inf, 0, 2, 10, inf, inf, inf, inf, inf] [-inf, 0, 2, 6, inf, inf, inf, inf, inf] [-inf, 0, 2, 6, 14, inf, inf, inf, inf] lis_size = 4 """ if not A: return 0 INFINITY = float('inf') n = len(A) P = [-INFINITY] + [INFINITY] * n for i in range(n): j = self.binary_search(P, A[i]) P[j] = A[i] for i in range(n, -1, -1): if P[i] < INFINITY: return i return 0 def binary_search(self, P, a): left, right = 0, len(P) - 1 while left + 1 < right: mid = (left + right) // 2 if P[mid] < a: left = mid else: right = mid return right """ DP + Print Paths """ class Solution: def longestIncreasingSubsequence(self, A): """ :type A: List[int] :rtype: int """ lis_size = 0 if not A: return lis_size n = len(A) """ `dp[i]` means the maximum size of LIS end at `i` note that there is size, so init with `1` """ dp = [1] * n # pi = [0] * n # end_at = -1 for i in range(n): for j in range(i): """ `dp[j]` the existing subseq end at `j` `+ 1` means included `A[i]` """ if A[j] < A[i] and dp[j] + 1 > dp[i]: dp[i] = dp[j] + 1 # pi[i] = j if dp[i] > lis_size: lis_size = dp[i] # end_at = i # paths = [0] * lis_size # for i in range(lis_size - 1, -1, -1): # paths[i] = A[end_at] # end_at = pi[end_at] # print(paths) return lis_size ================================================ FILE: lintcode/772_group_anagrams.py ================================================ class Solution: """ @param s: the given array of strings @return: The anagrams which have been divided into groups """ def groupAnagrams(self, s): if not s: return [] group = {} for w in s: key = ''.join(sorted(w)) if key not in group: group[key] = [] group[key].append(w) return sorted([sorted(g) for g in group.values()]) ================================================ FILE: lintcode/775_palindrome_pairs.py ================================================ """ REF: https://leetcode.com/problems/palindrome-pairs/discuss/79199/150-ms-45-lines-JAVA-solution Main Concept: 1. The <= in for (int j=0; j<=words[i].length(); j++) is aimed to handle empty string in the input. Consider the test case of [“a”, “”]; 2. Since we now use <= in for (int j=0; j<=words[i].length(); j++) instead of <. There may be duplicates in the output (consider test case [“abcd”, “dcba”]). Therefore I put a str2.length()!=0 to avoid duplicates. """ class Solution: def palindromePairs(self, words): """ :type words: list[str] :rtype: list[list[int]] """ ans = [] if not words: return ans n = len(words) w2i = {} for i in range(n): w2i[words[i]] = i for i in range(n): for j in range(len(words[i]) + 1): s = words[i][:j] t = words[i][j:] _s = ''.join(reversed(s)) _t = ''.join(reversed(t)) if (self.is_palindrome(s) and _t in w2i and w2i[_t] != i ): ans.append([w2i[_t], i]) if (self.is_palindrome(t) and len(t) != 0 and # since len(word) + 1, may empty here _s in w2i and w2i[_s] != i ): ans.append([i, w2i[_s]]) return ans def is_palindrome(self, word): n = len(word) left, right = 0, n - 1 while left < right: if word[left] != word[right]: return False left += 1 right -= 1 return True """ TLE: Brute Force """ class Solution: def palindromePairs(self, words): """ :type words: list[str] :rtype: list[list[int]] """ ans = [] if not words: return ans n = len(words) for i in range(n): for j in range(i): if self.is_palindrome(words, i, j): ans.append([i, j]) if self.is_palindrome(words, j, i): ans.append([j, i]) return ans def is_palindrome(self, words, i, j): s, t = words[i], words[j] a, b = len(s), len(t) n = a + b left, right = 0, n - 1 while left < right: if left >= a and t[left - a] != t[right - a]: return False elif right < a and s[left] != s[right]: return False elif left < a and right >= a and s[left] != t[right - a]: return False left += 1 right -= 1 return True """ TLE: Brute Force """ class Solution: def palindromePairs(self, words): """ :type words: list[str] :rtype: list[list[int]] """ ans = [] if not words: return ans n = len(words) for i in range(n): for j in range(n): if i == j: continue if self.is_palindrome(words[i] + words[j]): ans.append([i, j]) return ans def is_palindrome(self, s): n = len(s) left, right = 0, n - 1 while left < right: if s[left] != s[right]: return False left += 1 right -= 1 return True ================================================ FILE: lintcode/776_strobogrammatic_number_ii.py ================================================ class Solution: def findStrobogrammatic(self, n): """ :type n: int :rtype: list[str] """ ans = [''] if not n: return ans if n & 1: ans = ['0', '1', '8'] n -= 1 while n: queue = [] for s in ans: if n != 2: queue.append('0' + s + '0') queue.append('1' + s + '1') queue.append('8' + s + '8') queue.append('6' + s + '9') queue.append('9' + s + '6') ans = queue n -= 2 return ans ================================================ FILE: lintcode/77_longest_common_subsequence.py ================================================ class Solution: """ @param: A: A string @param: B: A string @return: The length of longest common subsequence of A and B """ def longestCommonSubsequence(self, A, B): """ remove the single line comment to print paths """ if not A or not B: return 0 m, n = len(A), len(B) """ `dp[i][j]` means the size of LCS, consisting of the substr end at `A[i - 1]` and the substr end at `B[j - 1]` dp[0][j] = 0 dp[i][0] = 0 """ dp = [[0] * (n + 1) for _ in range(2)] # pi = [[0] * (n + 1) for _ in range(m + 1)] prev = curr = 0 for i in range(1, m + 1): prev = curr curr = 1 - curr for j in range(1, n + 1): """ case 1: `A[i]` is not one of pairs case 2: `B[j]` is not one of pairs case 3: `A[i]` and `B[j]` is just a pair """ dp[curr][j] = max(dp[prev][j], dp[curr][j - 1]) # if dp[curr][j] == dp[prev][j]: # pi[i][j] = 1 # else: # pi[i][j] = 2 if A[i - 1] == B[j - 1]: dp[curr][j] = max(dp[curr][j], dp[prev][j - 1] + 1) # if dp[curr][j] == dp[prev][j - 1] + 1: # pi[i][j] = 3 # path = [None] * dp[curr][n] # i, j, k = m, n, dp[curr][n] - 1 # while i > 0 and j > 0: # if pi[i][j] == 1: # i -= 1 # elif pi[i][j] == 2: # j -= 1 # else: # path[k] = A[i - 1] # or B[j - 1], its same # k -= 1 # i -= 1 # j -= 1 # print(path) return dp[curr][n] ================================================ FILE: lintcode/784_the_longest_common_prefix_ii.py ================================================ class Solution: """ @param D: the n strings @param target: the target string @return: The ans """ def theLongestCommonPrefix(self, D, target): ans = 0 for word in D: i = 0 for c in word: if c != target[i]: break i += 1 if i > ans: ans = i return ans ================================================ FILE: lintcode/790_parser.py ================================================ class Solution: """ @param S: Generating set of rules. @param s: Start symbol. @param e: Symbol string. @return: Return true if the symbol string can be generated, otherwise return false. """ def canBeGenerated(self, S, start, end): if not start: start = '' N = {} for s in S: cur, nxt = s.split(' -> ') if cur not in N: N[cur] = set() N[cur].add(nxt) return self.dfs(N, end, start) def dfs(self, N, end, s): if len(s) > len(end): return False if s == end: return True for i in range(len(s)): if (not ord('A') <= ord(s[i]) <= ord('Z') or s[i] not in N): continue for _s in N[s[i]]: res = self.dfs(N, end, s[:i] + _s + s[i + 1:]) if res: return True return False ================================================ FILE: lintcode/791_merge_number.py ================================================ from heapq import heappush, heappop class Solution: """ @param A: the numbers @return: the minimum cost """ def mergeNumber(self, A): ans = 0 if not A: return ans heap = [] for a in A: heappush(heap, a) while len(heap) > 1: _sum = heappop(heap) + heappop(heap) ans += _sum heappush(heap, _sum) return ans ================================================ FILE: lintcode/792_kth_prime_number.py ================================================ class Solution: """ @param n: the number @return: the rank of the number """ def kthPrime(self, n): if not n or n < 3: return 1 is_prime = [True] * n is_prime[0] = is_prime[1] = False for i in range(2, int(n ** 0.5) + 1): if not is_prime[i]: continue for j in range(i * i, n, i): is_prime[j] = False return sum(is_prime) + 1 ================================================ FILE: lintcode/793_intersection_of_arrays.py ================================================ class Solution: """ @param A: the arrays @return: the number of the intersection of the arrays """ def intersectionOfArrays(self, A): ans = 0 if not A: return ans n = len(A) C = {} for i in range(n): if not A[i]: return ans for a in A[i]: if a not in C: C[a] = set() C[a].add(i) for a, S in C.items(): if len(S) == n: ans += 1 return ans ================================================ FILE: lintcode/7_binary_tree_serialization.py ================================================ """ Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ class Solution: EMPTY = '#' def serialize(self, root): """Encodes a tree to a single string. :type root: TreeNode :rtype: str """ TEMPLATE = '{{{}}}' # {{, }} is to escape brackets if not root: return TEMPLATE.format('') vals = [] queue = [root] for node in queue: if not node: vals.append(self.EMPTY) continue vals.append(str(node.val)) queue.append(node.left) queue.append(node.right) while vals[-1] == self.EMPTY: vals.pop() return TEMPLATE.format(','.join(vals)) def deserialize(self, data): """Decodes your encoded data to tree. :type data: str :rtype: TreeNode """ if (not data or data[0] != '{' or data[-1] != '}' or len(data) < 3 or data[1] == '#' ): return vals = data[1:-1].split(',') n = len(vals) i = 0 root = TreeNode(int(vals[i])) queue = [root] for node in queue: for branch in ('left', 'right'): i += 1 if i >= n: break if vals[i] == self.EMPTY: continue setattr(node, branch, TreeNode(int(vals[i]))) queue.append(getattr(node, branch)) return root ================================================ FILE: lintcode/813_find_anagram_mappings.py ================================================ """ Find last pos """ class Solution: def anagramMappings(self, a, b): """ :type a: list[int] :type b: list[int] :rtype: list[int] """ if not a or not b or len(a) != len(b): return [] n = len(a) ans = [-1] * n b2i = {} for i in range(n): b2i[b[i]] = i for i in range(n): if a[i] not in b2i: return [] ans[i] = b2i[a[i]] return ans """ Strict """ class Solution: def anagramMappings(self, a, b): """ :type a: list[int] :type b: list[int] :rtype: list[int] """ if not a or not b or len(a) != len(b): return [] n = len(a) ans = [-1] * n b2i = {} for i in range(n): if b[i] not in b2i: b2i[b[i]] = [] b2i[b[i]].append(i) for i in range(n): if not b2i.get(a[i]): # a[i] not in b2i # b2i[a[i]] is empty list return [] ans[i] = b2i[a[i]].pop() return ans ================================================ FILE: lintcode/81_data_stream_median.py ================================================ import heapq class Solution: def medianII(self, nums): """ :type nums: list[int] :rtype: list[int] """ ans = [] if not nums: return ans minheap = [] maxheap = [] median = 0 for num in nums: if num < median: heapq.heappush(maxheap, -num) else: heapq.heappush(minheap, num) while len(minheap) > len(maxheap): heapq.heappush(maxheap, -heapq.heappop(minheap)) while len(maxheap) > len(minheap) + 1: heapq.heappush(minheap, -heapq.heappop(maxheap)) if maxheap: median = -maxheap[0] else: median = 0 ans.append(median) return ans ================================================ FILE: lintcode/823_input_stream.py ================================================ """ Merge Sort time: O(n) space: O(1) """ class Solution: def inputStream(self, a, b): """ :type a: str :type b: str :rtype: str, 'NO' or 'YES' """ RES = ('NO', 'YES') if a == b == '': return RES[1] BACK = '<' m, n = len(a), len(b) i, j = m - 1, n - 1 acnt = bcnt = 0 # count the backspace in both a and b while i >= 0 and j >= 0: while i >= 0 and (a[i] == BACK or acnt): acnt += 1 if a[i] == BACK else -1 i -= 1 while j >= 0 and (b[j] == BACK or bcnt): bcnt += 1 if b[j] == BACK else -1 j -= 1 if a[i] != b[j]: return RES[0] i -= 1 j -= 1 while i >= 0 and (a[i] == BACK or acnt): acnt += 1 if a[i] == BACK else -1 i -= 1 while j >= 0 and (b[j] == BACK or bcnt): bcnt += 1 if b[j] == BACK else -1 j -= 1 return RES[int(i == j)] """ Stack time: O(n) space: O(n) """ class Solution: def inputStream(self, a, b): """ :type a: str :type b: str :rtype: str, 'NO' or 'YES' """ RES = ('NO', 'YES') if a == '' and b == '': return RES[1] if a is None or b is None: return RES[0] RM = '<' stack = [] for c in a: if c != RM: stack.append(c) elif stack: # c == RM stack.pop() _stack = [] for c in b: if c != RM: _stack.append(c) elif _stack: # c == RM _stack.pop() return RES[int(stack == _stack)] ================================================ FILE: lintcode/824_single_number_iv.py ================================================ class Solution: def getSingleNumber(self, nums): """ :type nums: list[int] :rtype: int """ if not nums: return -1 if len(nums) == 1: return nums[0] n = len(nums) left, right = 0, n - 1 while left + 1 < right: mid = (left + right) // 2 if mid > 0 and nums[mid] == nums[mid - 1]: if mid & 1 == 1: left = mid else: right = mid else: if mid & 1 == 1: right = mid else: left = mid for mid in (left, right): if mid > 0 and nums[mid] == nums[mid - 1]: continue if mid + 1 < n and nums[mid] == nums[mid + 1]: continue return nums[mid] return -1 ================================================ FILE: lintcode/826_computer_maintenance.py ================================================ class Solution: """ @param m: the rows of matrix @param n: the cols of matrix @param P: the bad computers @return: The answer """ def maintenance(self, m, n, P): if not m or not n or not P: return 0 dp = [[0, 0] for _ in range(m)] G = [[0] * n for _ in range(m)] for p in P: G[p.x][p.y] = 1 for i in range(m): left = right = -1 for j in range(n): if G[i][j] == 0: continue left = max(left, n - 1 - j) right = max(right, j) if i == 0: if right == -1: dp[i][0] = 0 dp[i][1] = n - 1 else: dp[i][0] = 2 * right dp[i][1] = n - 1 else: if right == -1: dp[i][0] = dp[i - 1][0] + 1 dp[i][1] = dp[i - 1][1] + 1 else: dp[i][0] = 1 + min( dp[i - 1][0] + 2 * right, dp[i - 1][1] + n - 1 ) dp[i][1] = 1 + min( dp[i - 1][1] + 2 * left, dp[i - 1][0] + n - 1 ) return min(dp[m - 1][0], dp[m - 1][1]) ================================================ FILE: lintcode/830_string_sort.py ================================================ class Solution: """ @param s: the string that needs to be sorted @return: sorted string """ def stringSort(self, s): if not s: return '' return ''.join(sorted(s, key=lambda c: (-s.count(c), c))) ================================================ FILE: lintcode/831_3sum_ii.py ================================================ class Solution: """ @param n: an integer @return: the number of solutions """ def threeSum2(self, n): ans = 0 nums = [] for a in range(n + 1): if a * a > n: break nums.append(a * a) nums.append(a * a) nums.append(a * a) m = len(nums) for a in range(m - 2): if a > 0 and nums[a] == nums[a - 1]: continue b = a + 1 c = m - 1 while b < c: _sum = nums[a] + nums[b] + nums[c] if _sum < n: b += 1 elif _sum > n: c -= 1 else: ans += 1 b += 1 c -= 1 while b < c and nums[b] == nums[b - 1]: b += 1 while b < c and nums[c] == nums[c + 1]: c -= 1 return ans class Solution: """ @param n: an integer @return: the number of solutions """ def threeSum2(self, n): ans = 0 m = int(n ** 0.5) for a in range(m + 1): target = n - a * a b, c = a, m while b <= c: _sum = b * b + c * c if _sum < target: b += 1 elif _sum > target: c -= 1 else: ans += 1 b += 1 return ans ================================================ FILE: lintcode/832_count_negative_number.py ================================================ class Solution: """ @param g: the sorted matrix @return: the number of Negative Number """ def countNumber(self, g): ans = 0 if not g or not g[0]: return ans m, n = len(g), len(g[0]) for i in range(m): left, right = 0, n - 1 while left + 1 < right: mid = (left + right) // 2 if g[i][mid] < 0: left = mid else: right = mid if g[i][left] >= 0: continue ans += left + 1 if g[i][right] >= 0 else right + 1 return ans ================================================ FILE: lintcode/833_process_sequence.py ================================================ class Solution: """ @param logs: Sequence of processes @param queries: Sequence of queries @return: Return the number of processes """ def numberOfProcesses(self, logs, queries): time = [] # 0: end, 1: start, 2: in progress for log in logs: time.append((log.start, 1)) time.append((log.end + 1, 0)) for t in queries: time.append((t, 2)) time.sort() cnt = 0 time2cnt = {} for t, status in time: if status == 0: cnt -= 1 elif status == 1: cnt += 1 time2cnt[t] = cnt return [time2cnt[t] for t in queries] ================================================ FILE: lintcode/85_insert_node_in_a_binary_search_tree.py ================================================ """ Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ # recursion class Solution: """ @param: root: The root of the binary search tree. @param: node: insert this node into the binary search tree @return: The root of the new binary search tree. """ def insertNode(self, root, node): if not root: return node if node.val < root.val: root.left = self.insertNode(root.left, node) else: root.right = self.insertNode(root.right, node) return root # iteration class Solution: """ @param: root: The root of the binary search tree. @param: node: insert this node into the binary search tree @return: The root of the new binary search tree. """ def insertNode(self, root, node): if not root: return node curr = root while curr is not node: if node.val < curr.val: if curr.left is None: curr.left = node curr = curr.left else: if curr.right is None: curr.right = node curr = curr.right return root ================================================ FILE: lintcode/86_binary_search_tree_iterator.py ================================================ """ Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None Example of iterate a tree: iterator = BSTIterator(root) while iterator.hasNext(): node = iterator.next() do something for node """ class BSTIterator: """ @param: root: The root of binary tree. """ def __init__(self, root): self.stack = [] self.node = root """ @return: True if there has next node, or false """ def hasNext(self): return self.node or self.stack """ @return: return next node """ def next(self): node = self.node stack = self.stack while node: stack.append(node) node = node.left node = stack.pop() nxt = node self.node = node.right return nxt ================================================ FILE: lintcode/87_remove_node_in_binary_search_tree.py ================================================ """ Main Concept: https://discuss.leetcode.com/topic/65792/recursive-easy-to-understand-java-solution Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ class Solution: """ @param: root: The root of the binary search tree. @param: target: Remove the node with given value. @return: The root of the binary search tree after removal. """ def removeNode(self, root, target): if not root: return root if root.val == target: if not root.left: return root.right if not root.right: return root.left min_node = self.find_min(root.right) root.val = min_node.val root.right = self.removeNode(root.right, root.val) return root if target < root.val: root.left = self.removeNode(root.left, target) else: root.right = self.removeNode(root.right, target) return root def find_min(self, node): if not node: return node while node.left: node = node.left return node ================================================ FILE: lintcode/88_lowest_common_ancestor.py ================================================ """ Notice: Assume two nodes are exist in tree. Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ class Solution: def lowestCommonAncestor(self, root, a, b): """ :type root: TreeNode :type a: TreeNode :type b: TreeNode :rtype: TreeNode """ if not root or root is a or root is b: return root left = self.lowestCommonAncestor(root.left, a, b) right = self.lowestCommonAncestor(root.right, a, b) if left and right: return root if left: return left if right: return right ================================================ FILE: lintcode/89_k_sum.py ================================================ class Solution: """ @param A: An integer array @param K: A positive integer (K <= length(A)) @param target: An integer @return: An integer """ def kSum(self, A, K, target): n = len(A) """ `dp[i][j][k]` means the ways we can take `j` in previous `i` nums and its sum equals `k` """ dp = [[[0] * (target + 1) for _ in range(K + 1)] for _ in range(n + 1)] for i in range(n + 1): dp[i][0][0] = 1 for i in range(1, n + 1): for j in range(1, min(K, i) + 1): for k in range(1, target + 1): if k >= A[i - 1]: dp[i][j][k] += dp[i - 1][j - 1][k - A[i - 1]] dp[i][j][k] += dp[i - 1][j][k] return dp[n][K][target] ================================================ FILE: lintcode/8_rotate_string.py ================================================ class Solution: """ @param: S: An array of char @param: x: An integer @return: nothing """ def rotateString(self, S, x): if not S or not x: return S n = len(S) x %= n self.reverse(S, 0, n - x - 1) self.reverse(S, n - x, n - 1) self.reverse(S, 0, n - 1) def reverse(self, S, start, end): while start < end: S[start], S[end] = S[end], S[start] start += 1 end -= 1 ================================================ FILE: lintcode/900_closest_binary_search_tree_value.py ================================================ """ Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ class Solution: def closestValue(self, root, target): """ :type root: TreeNode :type target: float :rtype: int """ if not root: return float('-inf') if root.left and target < root.val: left = self.closestValue(root.left, target) if abs(left - target) < abs(root.val - target): return left if root.right and target > root.val: right = self.closestValue(root.right, target) if abs(right - target) < abs(root.val - target): return right return root.val ================================================ FILE: lintcode/901_closest_binary_search_tree_value_ii.py ================================================ """ Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ class Solution: def closestKValues(self, root, target, k): """ :type root: TreeNode :type target: float :type k: int """ ans = [] if not root: return ans vals = [] self.inorder_traverse(root, vals) n = len(vals) i = 0 while i < n and vals[i] < target: i += 1 i, j = i - 1, i while k and i >= 0 and j < n: if target - vals[i] < vals[j] - target: ans.append(vals[i]) i -= 1 else: ans.append(vals[j]) j += 1 k -= 1 while k and i >= 0: ans.append(vals[i]) i -= 1 k -= 1 while k and j < n: ans.append(vals[j]) j += 1 k -= 1 return ans def inorder_traverse(self, root, vals): if not root: return self.inorder_traverse(root.left, vals) vals.append(root.val) self.inorder_traverse(root.right, vals) ================================================ FILE: lintcode/919_meeting_rooms_ii.py ================================================ """ Definition of Interval. class Interval(object): def __init__(self, start, end): self.start = start self.end = end """ class Solution: def minMeetingRooms(self, intervals): """ :type intervals: list[Interval] :rtype: int """ ans = 0 if not intervals: return ans time = [] for x in intervals: time.append((x.start, True)) time.append((x.end, False)) time.sort() cnt = 0 for t, is_start in time: if is_start: cnt += 1 else: cnt -= 1 if cnt > ans: ans = cnt return ans ================================================ FILE: lintcode/920_meeting_rooms.py ================================================ """ Definition of Interval. class Interval: def __init__(self, start, end): self.start = start self.end = end """ """ Sweep Line time: O(n) space: O(n) """ class Solution: def canAttendMeetings(self, intervals): """ :type intervals: list[Interval] :rtype: bool """ timeline = [] for interval in intervals: timeline.append((interval.start, True)) timeline.append((interval.end, False)) timeline.sort() cnt = 0 for time, is_start in timeline: if is_start: cnt += 1 else: cnt -= 1 if cnt > 1: return False return True """ Sorting time: O(nlogn) space: O(1) """ class Solution: def canAttendMeetings(self, intervals): """ :type intervals: list[Interval] :rtype: bool """ intervals.sort(key=lambda x: (x.start, x.end)) for i in range(1, len(intervals)): if intervals[i].start < intervals[i - 1].end: return False return True ================================================ FILE: lintcode/92_backpack.py ================================================ class Solution: # @param m: An integer m denotes the size of a backpack # @param A: Given n items with size A[i] # @return: The maximum size def backPack(self, m, A): if not A: return 0 n = len(A) dp = [[False] * (m + 1) for _ in range(n + 1)] dp[0][0] = True for i in range(1, n + 1): for j in range(m + 1): dp[i][j] = dp[i - 1][j] if (j >= A[i - 1] and dp[i - 1][j - A[i - 1]]): dp[i][j] = True for j in range(m, -1, -1): if dp[n][j]: return j return 0 ================================================ FILE: lintcode/93_balanced_binary_tree.py ================================================ """ Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ class Solution: """ @param: root: The root of binary tree. @return: True if this Binary tree is Balanced, or false. """ def isBalanced(self, root): return self._divide_conquer(root)[0] def _divide_conquer(self, node): if not node: return True, 0 is_balanced_left, maxdepth_left = self._divide_conquer(node.left) if not is_balanced_left: return False, 0 is_balanced_right, maxdepth_right = self._divide_conquer(node.right) if not is_balanced_right: return False, 0 return abs(maxdepth_left - maxdepth_right) <= 1, \ max(maxdepth_left, maxdepth_right) + 1 ================================================ FILE: lintcode/95_validate_binary_search_tree.py ================================================ """ Main Concept: since the fact, if we said a tree is NOT a binary search tree, and then the values we got by inorder traversal is must be a non-descending sequence, that is, A[i+1] >= A[i]. Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ class Solution: def isValidBST(self, root): """ :type root: TreeNode :rtype: bool """ stack = [] node = root pre = None while node or stack: while node: stack.append(node) node = node.left node = stack.pop() if pre and node.val <= pre.val: return False pre = node node = node.right return True class Solution: ans = True pre = None def isValidBST(self, root): """ :type root: TreeNode :rtype: bool """ if not root: return self.ans self.isValidBST(root.left) if self.pre and root.val <= self.pre.val: self.ans = False return self.ans self.pre = root self.isValidBST(root.right) return self.ans ================================================ FILE: lintcode/96_partition_list.py ================================================ """ Definition of ListNode class ListNode(object): def __init__(self, val, next=None): self.val = val self.next = next """ class Solution: """ @param: head: The first node of linked list @param: x: An integer @return: A ListNode """ def partition(self, head, x): if not head: return left_dummy = left_tail = ListNode(-1) right_dummy = right_tail = ListNode(-1) while head: node = ListNode(head.val) if head.val < x: left_tail.next = node left_tail = node else: right_tail.next = node right_tail = node head = head.next left_tail.next = right_dummy.next return left_dummy.next ================================================ FILE: lintcode/97_maximum_depth_of_binary_tree.py ================================================ """ Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ class Solution: """ @param root: The root of binary tree. @return: An integer """ def maxDepth(self, root): if not root: return 0 return 1 + max( self.maxDepth(root.left), self.maxDepth(root.right) ) ================================================ FILE: lintcode/98_sort_list.py ================================================ """ Definition of ListNode class ListNode(object): def __init__(self, val, next=None): self.val = val self.next = next """ class Solution: """ @param: head: The head of linked list. @return: You should return the head of the sorted linked list, using constant space complexity. """ def sortList(self, head): return self.merge_sort(head) def quick_sort(self, head): if not head or not head.next: return head mid = self.find_middle(head) left_dummy = left_tail = ListNode(0) mid_dummy = mid_tail = ListNode(0) right_dummy = right_tail = ListNode(0) while head: if head.val < mid.val: left_tail.next = head left_tail = head elif head.val > mid.val: right_tail.next = head right_tail = head else: mid_tail.next = head mid_tail = head head = head.next left_tail.next = mid_tail.next = right_tail.next = None left_dummy.next = self.quick_sort(left_dummy.next) right_dummy.next = self.quick_sort(right_dummy.next) dummy = tail = ListNode(0) for node in [left_dummy, mid_dummy, right_dummy]: tail.next = node.next tail = self.get_tail(tail) return dummy.next def merge_sort(self, head): if not head or not head.next: return head left = head mid = self.find_middle(head) right = mid.next mid.next = None left = self.merge_sort(left) right = self.merge_sort(right) dummy = tail = ListNode(0) while left and right: if left.val < right.val: tail.next = left left = left.next else: tail.next = right right = right.next tail = tail.next if left: tail.next = left else: tail.next = right return dummy.next def find_middle(self, head): slow, fast = head, head.next while fast and fast.next: slow = slow.next fast = fast.next.next return slow def get_tail(self, head): if not head: return while head.next: head = head.next return head ================================================ FILE: lintcode/9_fizz_buzz.py ================================================ class Solution: def fizzBuzz(self, n): """ :type n: int :rtype: List[str] """ ans = [] if not n: return ans for i in range(1, n + 1): if i % 3 == 0 and i % 5 == 0: ans.append('FizzBuzz') elif i % 3 == 0: ans.append('Fizz') elif i % 5 == 0: ans.append('Buzz') else: ans.append(str(i)) return ans class Solution: def fizzBuzz(self, n): """ :type n: int :rtype: List[str] """ ans = [] if not n: return ans a = i3 = i5 = 1 while a <= n: while a <= n and a < i3 * 3 and a < i5 * 5: ans.append(str(a)) a += 1 if a <= n and a == i3 * 3 and a == i5 * 5: ans.append('fizz buzz') a += 1 i3 += 1 i5 += 1 continue while a <= n and a == i3 * 3: ans.append('fizz') a += 1 i3 += 1 while a <= n and a == i5 * 5: ans.append('buzz') a += 1 i5 += 1 return ans ================================================ FILE: other/anti_queue_reconstruction_by_height.py ================================================ """ This question is related with `leetcode/406_queue_reconstruction_by_height` """ def reorder(nums): """ :type nums: list[int] :rtype: list[int] >>> reorder([0, 1, 2, 1, 0]) [4, 2, 1, 3, 5] """ if not nums: return [] n = len(nums) ans = [0] * n cands = [i for i in range(1, n + 1)] for i in range(n - 1, -1, -1): ans[i] = cands.pop(i - nums[i]) return ans ================================================ FILE: other/binary_tree_maximum_path_product.py ================================================ """ Testing: >>> class TreeNode: ... def __init__(self, val): ... self.val = val ... self.left = self.right = None >>> trees = [] >>> tree_infos = [ ... (( ... (-3, None, None), ... ), -3), ... (( ... (3, 4, 5), ... (4, None, None), ... (5, None, None), ... ), 60), ... (( ... (3, 4, -5), ... (4, None, None), ... (-5, None, None), ... ), 12), ... (( ... (3, 4, -5), ... (4, -6, 7), ... (-5, 2, -9), ... (-6, None, None), ... (7, None, None), ... (2, None, None), ... (-9, None, None), ... ), 3780), ... (( ... (0, 4, -5), ... (4, -6, 7), ... (-5, 2, -9), ... (-6, None, None), ... (7, None, None), ... (2, None, None), ... (-9, None, None), ... ), 90), ... ] >>> for info, _ in tree_infos: ... nodes = {node[0]: TreeNode(node[0]) for node in info} ... ... for val, left, right in info: ... if left: ... nodes[val].left = nodes[left] ... if right: ... nodes[val].right = nodes[right] ... ... trees.append(nodes[info[0][0]]) >>> gotcha = [] >>> s = Solution() >>> for i in range(len(trees)): ... res = s.maxPathProd(trees[i]) ... if res != tree_infos[i][1]: print(res, tree_infos[i]) ... gotcha.append(res == tree_infos[i][1]) >>> bool(gotcha) and all(gotcha) True """ class Solution: def maxPathProd(self, root): """ :type root: TreeNode :rtype: int """ if not root: return 0 ans, _, _ = self.divide_conquer(root) return ans def divide_conquer(self, node): if not node: return float('-inf'), 1, 1 res_left, max_left, min_left = self.divide_conquer(node.left) res_right, max_right, min_right = self.divide_conquer(node.right) a = node.val * max(max_left, max_right) b = node.val * min(min_left, min_right) res = max( # ignoring current (0) res_left, res_right, # only current (1) node.val, # half path (2) a, b, # go through current (3) node.val * max_left * max_right, node.val * min_left * min_right, ) return res, max(a, b), min(a, b) ================================================ FILE: other/candy_crush.py ================================================ """ Design a algorithm to initialize the board of Candy Crush Saga. With M x N board, Q types of candies. Rules: 1. with randomization 2. no 3 for run after initialization 3. must contain at least one valid move at the beginning Thought: 1. for random candies and no 3 for run at begining - random generate, and re-gen if invalid 2. prevent dead loop while checking valid - [x] iterate by order => dead loop may occur when visiting the cells arround prefilled - [v] BFS from the prefilled cell to boundary 3. at least one valid move at the beginning - just define the patterns (3 for run => 12 patterns) prefill to board, and mark as unchangable REF: 1. LC 723 2. http://massivetechinterview.blogspot.com/2015/11/how-to-initialize-board-of-candy-crush.html TODO: 1. implement `move` to move candy to D/R/U/L, and - remove the connected candy - the candy will drop if there is empty below 2. count score 3. end up game if no more valid move or the game is finished Testing: >>> gotcha = [] >>> for params in ( ... (7, 7, 3), (10, 10, 3), (20, 20, 3), ... (7, 7, 5), (10, 10, 4), (20, 20, 6), ... ): ... game = CandyCrush(*params) ... for _ in range(5): ... game.reset_board() ... valid = _check_board_valid(game.get_board()) ... if not valid: print(game._print_board()) ... gotcha.append(valid) >>> bool(gotcha) and all(gotcha) True """ import random def _check_board_valid(board): # for testing m, n = len(board), len(board[0]) for x in range(2, m): for y in range(n): if board[x][y] == board[x - 1][y] == board[x - 2][y]: return False for y in range(2, n): for x in range(m): if board[x][y] == board[x][y - 1] == board[x][y - 2]: return False return True class CandyCrush: def __init__(self, m, n, q): """ :type m: int :type n: int :type q: int """ self.width = n self.height = m self.__board = None self.__types = q self.__patterns = ( # with rotating, we can find up to 12 patterns (-1, -1, 1, 0), # /-- (-1, 1, 1, 0), # --\ (-1, -1, 1, -1), # /-\ ) self.reset_board() def reset_board(self): """ :rtype: void """ m, n = self.height, self.width b = self.__board = [[-1] * n for _ in range(m)] x, y = random.randint(1, m - 2), random.randint(1, n - 2) d = random.choice(self.__patterns) # dx1, dy1, dx2, dy2 q = random.randrange(self.__types) queue = [(x + d[0], y + d[1]), (x, y), (x + d[2], y + d[3])] visited = set(queue) for x, y in queue: b[x][y] = q for x, y in queue: for dx, dy in ( (0, -1), (0, 1), (-1, 0), (1, 0), ): _x = x + dx _y = y + dy if not (0 <= _x < m and 0 <= _y < n): continue if (_x, _y) in visited: continue visited.add((_x, _y)) queue.append((_x, _y)) while not self._check_cell_valid(_x, _y): b[_x][_y] = random.randrange(self.__types) def get_board(self): """ :rtype: list[list[int]] """ return self.__board def _check_cell_valid(self, x, y): """ :type x: int :type y: int :rtype: bool """ b = self.__board m, n = self.height, self.width if b[x][y] == -1: return False for x1, y1, x2, y2 in ( (x - 2, y, x - 1, y), (x + 1, y, x + 2, y), # most up, down (x, y - 2, x, y - 1), (x, y + 1, x, y + 2), # most left, right (x - 1, y, x + 1, y), (x, y - 1, x, y + 1), # cross middle ): if not ( 0 <= x1 < m and 0 <= y1 < n and 0 <= x2 < m and 0 <= y2 < n ): continue if b[x][y] == b[x1][y1] == b[x2][y2]: return False return True def _print_board(self): # for testing print('\n'.join(str(r) for r in self.__board)) ================================================ FILE: other/card_shuffler.py ================================================ """ >>> CASE = ( ... (['AB', [1, 0]], 2), ... (['ABCD', [1, 2, 3, 0]], 4), ... (['ABCDE', [4, 3, 2, 0, 1]], 4), ... (['ABCDE', [0, 3, 4, 0, 2]], -1), ... ) >>> all(card_shuffler(*inpt) == oupt for inpt, oupt in CASE) True """ def card_shuffler(cards, shuffles): """ :type cards: Iterable[str] :type shuffles: list[int] :rtype: int """ n = len(cards) offsets = [0] * n for i in range(n): offsets[i] = get_offset(i, shuffles) if offsets[i] == -1: return -1 return get_lcm(*offsets) def get_offset(start, shuffles): visited = set() i = start offset = 0 while i not in visited: visited.add(i) i = shuffles[i] offset += 1 return offset if i == start else -1 def get_lcm(*nums): lcm = nums[0] for i in range(1, len(nums)): lcm = lcm // get_gcd(lcm, nums[i]) * nums[i] return lcm def get_gcd(a, b): while b: a, b = b, a % b return a ================================================ FILE: other/deep_fetch.js ================================================ function deepFetch(target, path, defaultValue = undefined) { if (!target || !Array.isArray(path)) { return defaultValue } return path.reduce((level, key) => ( (level && level[key]) ? level[key] : defaultValue ), target) } module.exports = deepFetch ================================================ FILE: other/deep_fetch.test.js ================================================ const deepFetch = require('./deep_fetch') test('It could deeply fetch props from target', () => { const target = { a: 1, b: 2, c: { d: 3, e: { f: 4, g: 5, }, }, } expect(deepFetch(target, ['b'])).toBe(2) expect(deepFetch(target, ['c', 'd'])).toBe(3) expect(deepFetch(target, ['c', 'e', 'f'])).toBe(4) expect(deepFetch(target, ['c', 'e', 'h'])).toBe(undefined) expect(deepFetch(target, ['c', 'e', 'h'], -1)).toBe(-1) }) ================================================ FILE: other/find_treasure_in_maze.py ================================================ """ http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=82 Description: 一个叫ACM的寻宝者找到了一个藏宝图,它根据藏宝图找到了一个迷宫, 这是一个很特别的迷宫,迷宫里有N个编过号的门(N<=5), 它们分别被编号为A,B,C,D,E.为了找到宝藏,ACM必须打开门, 但是,开门之前必须在迷宫里找到这个打开这个门所需的所有钥匙(每个门都至少有一把钥匙), 例如:现在A门有三把钥匙,ACM就必须找全三把钥匙才能打开A门。 现在请你编写一个程序来告诉ACM,他能不能顺利的得到宝藏。 每组测试数据的第一行包含了两个整数M,N(1>> s = Solution() >>> gotcha = [] >>> for _in, _out in ( ... (['S.X.', 'a.X.', '..XG', '....'], True), ... (['S.Xa', '.aXB', 'b.AG'], False), ... (['aX.S', 'bXAB', 'cXCD', 'dXGX'], False), ... (['SbAdX.', 'a.BD.G', 'cBCdaX'], True), ... (['S.Aa.X.', 'a.Xc.C.', 'b.X..DG', '.cB..Xd'], True), ... ): ... res = s.find_treasure_in_maze(_in) ... if res != _out: print(_in, res) ... gotcha.append(res == _out) >>> bool(gotcha) and all(gotcha) True """ class Solution: START = 'S' GOLD = 'G' OBSTACLE = 'X' EMPTY = '.' DOORS = 'ABCDE' KEYS = 'abcde' def find_treasure_in_maze(self, maze): """ :type maze: list[str] :rtype: bool """ if not maze or not maze[0]: return False m, n = len(maze), len(maze[0]) k = len(self.DOORS) keys = [0] * k # all keys count has_gold = False queue = [] doors = [None] * k # doors meet when bfs holds = [0] * k # keys meet when bfs visited = set() # visited cell when bfs for x in range(m): for y in range(n): if maze[x][y] == self.START: queue.append((x, y)) elif maze[x][y] == self.GOLD: has_gold = True elif maze[x][y] in self.KEYS: i = ord(maze[x][y]) - ord('a') keys[i] += 1 if not has_gold or not queue: return False while queue or self.is_possible(maze, keys, holds, doors): if self.bfs(maze, queue, keys, holds, doors, visited): return True return False def bfs(self, maze, queue, keys, holds, doors, visited): """ return True if got gold, otherwise False :type maze: list[str] :type queue: list[tuple[int]] :type keys: list[int] :type holds: list[int] :type doors: list[tuple[int]] :type visited: set[tuple[int]] :rtype: bool """ m, n = len(maze), len(maze[0]) for x, y in queue: for dx, dy in ( (0, -1), (0, 1), (-1, 0), (1, 0), ): _x = x + dx _y = y + dy if not (0 <= _x < m and 0 <= _y < n): continue if (_x, _y) in visited or maze[_x][_y] == self.OBSTACLE: continue if maze[_x][_y] == self.GOLD: return True if maze[_x][_y] in self.DOORS: i = ord(maze[_x][_y]) - ord('A') if holds[i] < keys[i]: doors[i] = (_x, _y) continue doors[i] = None if maze[_x][_y] in self.KEYS: i = ord(maze[_x][_y]) - ord('a') holds[i] += 1 visited.add((_x, _y)) queue.append((_x, _y)) queue.clear() return False def is_possible(self, maze, keys, holds, doors): """ :type maze: list[str] :type keys: list[int] :type holds: list[int] :type doors: list[tuple[int]] :rtype: bool """ for door in doors: if not door: continue x, y = door i = ord(maze[x][y]) - ord('A') if holds[i] >= keys[i]: return True return False ================================================ FILE: other/find_ways_in_board_game.py ================================================ """ >>> gotcha = [] >>> for _in, _out in ( ... (0, 1), (1, 1), ... (2, 2), (3, 4), ... (4, 8), (5, 16), ... (6, 32), (7, 63), ... (8, 125), (9, 248), ... ): ... res = find_ways_in_board_game(_in) ... if res != _out: print(_in, res) ... gotcha.append(res == _out) >>> bool(gotcha) and all(gotcha) True """ def find_ways_in_board_game(n): if not n or n < 2: return 1 dp = [0] * (n + 1) dp[0] = 1 for i in range(1, min(n + 1, 6)): for j in range(i): dp[i] += dp[j] for i in range(6, n + 1): dp[i] = sum(( dp[i - 1], dp[i - 2], dp[i - 3], dp[i - 4], dp[i - 5], dp[i - 6], )) return dp[n] ================================================ FILE: other/freq_iterator.py ================================================ """ Testing: >>> gotcha = [] >>> for _in, _out in ( ... ( ... ['foo', 'foo', 'bar', 'foo'], ... [['foo', 2], ['bar', 1], ['foo', 1]] ... ), ... ( ... ['a', 'a', 'a', 'b', 'b', 'c', 'a', 'b', 'b'], ... [['a', 3], ['b', 2], ['c', 1], ['a', 1], ['b', 2]] ... ), ... ( ... ['a', 'a', 'a', 'b', 'b', 'c', 'a', 'b', 'c'], ... [['a', 3], ['b', 2], ['c', 1], ['a', 1], ['b', 1], ['c', 1]] ... ), ... ( ... ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'], ... [['a', 1], ['b', 1], ['c', 1], ... ['d', 1], ['e', 1], ['f', 1], ... ['g', 1], ['h', 1], ['i', 1]] ... ), ... ): ... origin_iterator = ListIterator(_in) ... res = [origin_iterator.next() for _ in range(len(_in))] ... if _in != res: print(_in, res) ... gotcha.append(_in == res) ... gotcha.append(origin_iterator.has_next() is False) ... gotcha.append(origin_iterator.next() is None) ... ... res = [] ... origin_iterator = ListIterator(_in) ... freq_iterator = FreqIterator(origin_iterator) ... while freq_iterator.has_next(): ... res.append(freq_iterator.next()) ... ... if res != _out: print(_in, res) ... gotcha.append(res == _out) >>> bool(gotcha) and all(gotcha) True """ class FreqIterator: def __init__(self, iterator): if not iterator or not iterator.has_next(): return self.iterator = iterator self.pre = None self.word = iterator.next() def next(self): if not self.has_next(): return cnt = 1 nxt = None while self.iterator.has_next(): nxt = self.iterator.next() if nxt != self.word: break cnt += 1 self.pre = self.word self.word = nxt return [self.pre, cnt] def has_next(self): return self.pre != self.word and self.word is not None class ListIterator: def __init__(self, words): self.words = words self.i = 0 def next(self): if not self.has_next(): return res = self.words[self.i] self.i += 1 return res def has_next(self): if self.i < len(self.words): return True return False ================================================ FILE: other/gcd_and_lcm.py ================================================ def get_gcd(a, b): """ :type a: int :type b: int :rtype: int >>> get_gcd(10, 2) 2 >>> get_gcd(500, 375) 125 """ while b: a, b = b, a % b return a def get_lcm(a, b): """ :type a: int :type b: int :rtype: int >>> get_lcm(10, 2) 10 >>> get_lcm(500, 375) 1500 """ return (a * b) // get_gcd(a, b) ================================================ FILE: other/get_most_popular_word.py ================================================ """ Question: Given an integer W that represents number of words and unlimited space complexity. Write functions insert(word) and getMostPopularWord() such that getMostPopularWord() will always return the most popular word in the last W number of words. Create two solutions that will optimize run-time complexity for either function while sacrificing the run-time for the other function. example1: let W = 2 insert('A') getMostPopularWord() => 'A' insert('B') getMostPopularWord() => 'B' example2: let W = 3 insert('A') insert('A') getMostPopularWord() => 'A' insert('B') getMostPopularWord() => 'A' insert('B') getMostPopularWord() => 'B' // since the first inserted 'A' is out of the scope of the last 3 words follow-up1: insert => O(1), getMostPopularWord => O(W) follow-up2: insert => O(W), getMostPopularWord => O(1) follow-up3: insert => O(1), getMostPopularWord => O(1) => like LFU - linked list: save words order - 2d linked list: level1 is freqs list, level2 is words list => bucket sort Testing: >>> gotcha = [] >>> for (freq, words), expects in ( ... ((0, 'AB'), ''), ... ((0, 'AABB'), ''), ... ((1, 'AB'), 'AB'), ... ((1, 'AABB'), 'AABB'), ... ((2, 'AB'), 'AB'), ... ((2, 'AABB'), 'AABB'), ... ((4, 'AB'), 'AB'), ... ((4, 'AABB'), 'AAAB'), ... ((8, 'AB'), 'AB'), ... ((8, 'AABB'), 'AAAB'), ... ((8, 'ABABCCCBAC'), 'ABABBCCBBC'), ... ): ... s = Solution(freq) ... for i in range(len(words)): ... gotcha.append(s.insert(words[i]) is None) ... res = s.get_most_popular_word() ... if freq == 0: ... if res != '': print(freq, words, i, '', res) ... gotcha.append(res == '') ... else: ... if res != expects[i]: print(freq, words, i, expects[i], res) ... gotcha.append(res == expects[i]) >>> bool(gotcha) and all(gotcha) True """ class Solution: def __init__(self, W): self.cap = W self.size = 0 self.words = (WordNode(''), WordNode('')) # D, d for words list self.words[0].nxt = self.words[1] self.words[1].pre = self.words[0] self.nodes = {} self.freqs = (FreqNode(0), FreqNode(0)) # D, d for freqs list self.freqs[0].nxt = self.freqs[1] self.freqs[1].pre = self.freqs[0] def insert(self, word): if not word or self.cap <= 0: return # needs to evict first, # to ensure the coming word will be added at the tail while self.size >= self.cap: self.size -= 1 self._evict_word() self.size += 1 self._add_word(word) def get_most_popular_word(self): return self.freqs[1].pre.words[1].pre.word def _add_word(self, word): # update self.words node = WordNode(word) node.link(self.words[1].pre, self.words[1]) # update self.freqs node = from_freq = to_freq = None if self.nodes.get(word): node = self.nodes[word] from_freq = node.freq_node node.unlink() else: node = WordNode(word) from_freq = self.freqs[0] self.nodes[word] = node if from_freq.nxt.freq == from_freq.freq + 1: to_freq = from_freq.nxt else: to_freq = FreqNode(from_freq.freq + 1) from_freq.after(to_freq) to_freq.append_tail(node) if from_freq.freq != 0 and from_freq.is_empty(): from_freq.unlink() def _evict_word(self): # update self.words node = self.words[0].nxt node.unlink() word = node.word # update self.freqs node = self.nodes[word] from_freq = node.freq_node to_freq = None node.unlink() if from_freq.freq == 1: self.nodes[word] = None if from_freq.is_empty(): from_freq.unlink() return if from_freq.pre.freq == from_freq.freq - 1: to_freq = from_freq.pre else: to_freq = FreqNode(from_freq.freq - 1) from_freq.before(to_freq) to_freq.append_tail(node) if from_freq.is_empty(): from_freq.unlink() class WordNode: def __init__(self, word, freq_node=None, pre=None, nxt=None): self.word = word self.freq_node = freq_node self.pre = pre self.nxt = nxt def link(self, pre, nxt): self.pre = pre self.nxt = nxt pre.nxt = self nxt.pre = self def unlink(self): self.pre.nxt = self.nxt self.nxt.pre = self.pre self.pre = self.nxt = self.freq_node = None class FreqNode: def __init__(self, freq, pre=None, nxt=None): self.freq = freq self.pre = pre self.nxt = nxt self.words = (WordNode(''), WordNode('')) # D, d for words list self.words[0].nxt = self.words[1] self.words[1].pre = self.words[0] def unlink(self): self.pre.nxt = self.nxt self.nxt.pre = self.pre self.pre = self.nxt = self.words = None def before(self, freq_node): freq_node.pre = self.pre freq_node.nxt = self self.pre.nxt = freq_node self.pre = freq_node def after(self, freq_node): freq_node.pre = self freq_node.nxt = self.nxt self.nxt.pre = freq_node self.nxt = freq_node def is_empty(self): return self.words[0].nxt is self.words[1] def append_tail(self, word_node): word_node.freq_node = self word_node.pre = self.words[1].pre word_node.nxt = self.words[1] self.words[1].pre.nxt = word_node self.words[1].pre = word_node if __name__ == '__main__': s = Solution(8) _in, _out = 'ABABCCCBAC', 'ABABBCCBBC' gotcha = [] for i in range(len(_in)): gotcha.append(s.insert(_in[i]) is None) res = s.get_most_popular_word() if res != _out[i]: print(_in, i, _in[i], res) gotcha.append(res == _out[i]) print(bool(gotcha) and all(gotcha)) ================================================ FILE: other/guess_secret.py ================================================ """ >>> gotcha = [] >>> for words, _out in ( ... (['apple', 'price', 'tuple', 'agile'], 'apple'), ... (['apple', 'apple', 'apple', 'apple'], 'apple'), ... (['aa', 'ab', 'ac', 'ad', 'ae', 'af', 'ag'], 'ae'), ... (['aa', 'ab', 'ac', 'ad', 'ae', 'af', 'ag'], 'ag'), ... (['aa', 'ab', 'ab', 'ac'], 'ab'), ... ): ... secret = Secret(_out) ... res = find_secret(secret, words) ... if res != _out: print(words, res, secret.score) ... gotcha.append(res == _out) >>> bool(gotcha) and all(gotcha) True """ def find_secret(secret, words): if not isinstance(secret, Secret): return '' while len(words) > 1: has_got, correct_cnt = secret.guess(words[-1]) guess_word = words.pop() if has_got: return guess_word _words = [] n = len(guess_word) for word in words: cnt = 0 for i in range(n): if word[i] == guess_word[i]: cnt += 1 if cnt == len(word): return word if cnt == correct_cnt: _words.append(word) words = _words return words[0] class Secret: def __init__(self, word): self.secret = word self.score = 0 def guess(self, word): if not word or len(word) != len(self.secret): return cnt = 0 for i in range(len(word)): if word[i] == self.secret[i]: cnt += 1 self.score -= 1 return [cnt == len(word), cnt] ================================================ FILE: other/inorder_non_threaded_binary_tree_traversal.py ================================================ """ Inorder Traversal in Binary Tree without Recursion or Stack or Modification Recursion: The simplest way Iteration: With Stack Morris Traversal: With Modification And the following code is for the 4th way to inorder traverse binary tree if we have parent pointers available to us. REF: Inorder Non-threaded Binary Tree Traversal without Recursion or Stack https://www.geeksforgeeks.org/inorder-non-threaded-binary-tree-traversal-without-recursion-or-stack/ Node Structure: >>> class TreeNode: ... def __init__(self, val, left=None, right=None, parent=None): ... self.val = val ... self.left = left ... self.right = right ... self.parent = parent Process: 0. go_left = True 1. keep moving to the most left 2. print val 3. if has right child, move on it. and go_left = True to repeat (1) 4. keep moving to the most parent if no right and its just the right of its parent 5. move one more to its parent and go_left = False Testing: >>> trees = [] >>> tree_infos = [ ... (( ... (1, None, None, 2), ... (2, 1, None, None), ... ), '1,2'), ... (( ... (20, None, 8, 22), ... (8, 20, 4, 12), ... (22, 20, None, None), ... (4, 8, None, None), ... (12, 8, 10, 14), ... (10, 12, None, None), ... (14, 12, None, None), ... ), '4,8,10,12,14,20,22'), ... (( ... (10, None, 5, 100), ... (5, 10, None, None), ... (100, 10, 80, 120), ... (80, 100, None, None), ... (120, 100, None, None), ... ), '5,10,80,100,120'), ... (( ... (1, None, 2, 3), ... (2, 1, 4, None), ... (3, 1, None, 5), ... (4, 2, None, 6), ... (5, 3, None, None), ... (6, 4, 7, 8), ... (7, 6, None, None), ... (8, 6, None, None), ... ), '4,7,6,8,2,1,3,5'), ... (( ... (1, None, None, 2), ... (2, 1, None, 3), ... (3, 2, None, 4), ... (4, 3, None, 5), ... (5, 4, None, 6), ... (6, 5, None, 7), ... (7, 6, None, None), ... ), '1,2,3,4,5,6,7'), ... ] >>> for info, _ in tree_infos: ... nodes = {node[0]: TreeNode(node[0]) for node in info} ... ... for val, parent, left, right in info: ... if parent: ... nodes[val].parent = nodes[parent] ... if left: ... nodes[val].left = nodes[left] ... if right: ... nodes[val].right = nodes[right] ... ... trees.append(nodes[info[0][0]]) >>> gotcha = [] >>> for i in range(len(trees)): ... res = [] ... inorder_traverse(trees[i], callback=lambda val: res.append(str(val))) ... gotcha.append(','.join(res) == tree_infos[i][1]) >>> bool(gotcha) and all(gotcha) True """ def inorder_traverse(root, *, callback): go_left = True while root: while go_left and root.left: root = root.left callback(root.val) if root.right: root = root.right go_left = True continue # if no right child, # and its just a right child of its parent while root.parent and root is root.parent.right: root = root.parent root = root.parent go_left = False ================================================ FILE: other/is_valid_relation.py ================================================ """ Question: The rules look like this: 'A NE B' - means this means point A is located northeast of point B. 'A SW C' - means that point A is southwest of C. 'A N D' - means that point A is north of D but maybe true north, northeast, or northwest. Given a list of rules, check if the sum of the rules validates. For example: ['A N B', 'B NE C', 'C N A'], returns False ['A N B', 'B NE C', 'C S A'], returns True Testing: >>> gotcha = [] >>> for _in, _out in ( ... ([], False), ... ([''], False), ... (['A N B', 'B NE C', 'C N A'], False), ... (['A N B', 'B NW C', 'C N C'], False), ... (['A N B', 'B N C', 'C N B'], False), ... (['A N B', 'B NE C', 'C S A'], True), ... (['A N B', 'B NW C', 'C S A'], True), ... (['A E B', 'B E C', 'C S D', 'D S E'], True), ... (['A N B', 'B S A', 'C E D', 'D W C'], True), ... ): ... res = is_valid_relation(_in) ... if res != _out: print(_in, res) ... gotcha.append(res == _out) >>> bool(gotcha) and all(gotcha) True """ import collections def is_valid_relation(strs): """ :type strs: list[str] :rtype: bool """ if not strs: return False # egraph => scan from east to west egraph = collections.defaultdict(set) wgraph = collections.defaultdict(set) ngraph = collections.defaultdict(set) sgraph = collections.defaultdict(set) for s in strs: if not s: return False dst, d, src = s.split() if 'E' in d: egraph[dst].add(src) wgraph[src].add(dst) elif 'W' in d: wgraph[dst].add(src) egraph[src].add(dst) if 'N' in d: ngraph[dst].add(src) sgraph[src].add(dst) elif 'S' in d: sgraph[dst].add(src) ngraph[src].add(dst) for graph in (egraph, wgraph, ngraph, sgraph): for node in graph.keys(): if dfs(graph, node, set()): return False return True def dfs(graph, node, visited): """ returns True if there is cycle in graph :type graph: dict{str: set} :type node: str :type visited: set :rtype: bool """ if node not in graph: return False if node in visited: return True visited.add(node) for nxt in graph[node]: if dfs(graph, nxt, visited): return True return False ================================================ FILE: other/merge_mail.py ================================================ def merge_mail(data): """group owners if their mails got a connection >>> merge_mail({ ... 'A1': ['a1@gmail.com', 'a2@gmail.com'], ... 'A2': ['b1@gmail.com', 'a2@gmail.com'], ... 'A3': ['c1@gmail.com'], ... 'A4': ['c1@gmail.com', 'd1@gmail.com'], ... 'A5': ['b1@gmail.com', 'e1@gmail.com'], ... }) [['A1', 'A2', 'A5'], ['A3', 'A4']] >>> merge_mail({ ... 'A1': ['a1@gmail.com', 'a2@gmail.com'], ... 'A2': ['b1@gmail.com', 'a2@gmail.com'], ... 'A3': ['b1@gmail.com', 'c1@gmail.com'], ... 'A4': ['c1@gmail.com', 'd1@gmail.com'], ... 'A5': ['b1@gmail.com', 'e1@gmail.com'], ... }) [['A1', 'A2', 'A3', 'A4', 'A5']] """ owners = {} # owners mail2owners = {} # mail to owners for owner, mails in data.items(): find(owners, owner) for mail in mails: if mail not in mail2owners: mail2owners[mail] = owner continue mail2owners[mail] = connect( owners, owner, mail2owners[mail] ) ans = {} for k, v in owners.items(): if v not in ans: ans[v] = [] ans[v].append(k) return list(ans.values()) def connect(nodes, a, b): _a = find(nodes, a) _b = find(nodes, b) if _a is not _b: nodes[_a] = _b return _b def find(nodes, a): if a not in nodes: nodes[a] = a return a if nodes[a] is a: return a nodes[a] = find(nodes, nodes[a]) return nodes[a] ================================================ FILE: other/reservation.py ================================================ """ OO Design 设计一个剧院的订座系统,user一次可以query不多于5个座位, 这个系统要能return相应数量并尽可能相连的座位。 用户可以选择订或不订这些位子,订的话这些位子就不available了, 没订的话位子就都还available, 但要求这位用户下次query还是return这些位子(防止用户不停刷系统以拿到他们想要的位子), 除非下次query前这些位子里k个被别人订了,那系统再生成k个available的位子。 My understanding r1 => 尽可能相连的座位 => MUST be in same row r2 => requery => same seats if seats are also available, otherwise find new available seats and still follow (r1) """ class Reservation: def __init__(self, m, n): pass def get_seats(self, user_id, n=0): pass ================================================ FILE: other/robot_cleaner.py ================================================ """ Given a robot cleaner in a room modeled as a grid. Each cell in the grid can be empty or blocked. The robot cleaner with 4 given APIs can move forward, turn left or turn right. When it tries to move into a blocked cell, its bumper sensor detects the obstacle and it stays on the current cell. The 4 APIs are: clean(): clean the current location. turnleft(k=1): turn left k*90 degrees. turnrigt(k=1): turn right k*90 degrees. move(direction=None): move forward for 1 position, return False if that’s not possible. - How do you clean the entire space? - How long will it take? (1 step == 1 time unit) - Can you show paths? Need to ask: - is there an api to detect the cell need to clean? - are both the robot's direction and coordinate need to same as room? - once we called the `move` api and passed a direction. will the robot turn its face in that direction? and move forward with 1 unit? REF: - http://www.hoangvancong.com/2016/10/28/bfs-backtrack-robot-don-dep-cleaning-robot - https://blog.csdn.net/aozil_yang/article/details/52177644 - http://www.1point3acres.com/bbs/thread-289514-1-1.html - http://www.1point3acres.com/bbs/thread-345555-1-1.html Testing: 1. is the mock api working? >>> room = Room([ ... [0, 0, 0, 0, 0, 0, 0, 0], ... [0, 0, 0, 1, 0, 0, 0, 0], ... [0, 2, 0, 0, 0, 0, 0, 0], ... [2, 2, 2, 0, 2, 2, 2, 2], ... [0, 0, 0, 0, 0, 0, 0, 3], ... ]) >>> robot = Robot(room) >>> all(( ... room._get_robot() == (4, 7), ... robot._get_face() == Dirs.DOWN, ... robot.move() is False, ... robot.turnleft() is None, ... robot.move() is False, ... robot.turnrigt() is None, ... robot.move() is False, ... robot.turnleft(3) is None, ... robot.move() is True, ... room._get_robot() == (4, 6), ... )) True >>> all(( ... robot.move() is True, ... robot.move(Dirs.LEFT) is True, ... robot.turnleft(2) is None, ... robot.turnrigt(2) is None, ... robot.move() is True, ... room._get_robot() == (4, 3), ... )) True >>> all(( ... robot._get_face() == Dirs.LEFT, ... robot.move(Dirs.UP) is True, ... robot.turnleft() is None, ... robot.move() is False, ... robot.turnrigt(2) is None, ... robot.move() is False, ... robot.turnleft() is None, ... )) True >>> all(( ... robot.move() is True, ... robot.turnleft() is None, ... robot.move() is True, ... robot.move() is False, ... robot.move() is False, ... robot.move() is False, ... robot.turnrigt() is None, ... robot.move() is True, ... robot.turnrigt() is None, ... )) True >>> all(( ... room._get_robot() == (1, 2), ... robot.move() is True, ... room.is_clear() is False, ... robot.clean() is None, ... room.is_clear() is True, ... robot.clean() is None, ... room.is_clear() is True, ... room.is_clear() is True, ... )) True 2. test cleaner >>> CASES = ( ... ( ... (3, 0, 0, 0, 0, 0, 1, 0), ... (0, 0, 0, 0, 0, 0, 0, 0), ... (0, 2, 0, 0, 0, 0, 0, 0), ... (2, 2, 2, 0, 2, 2, 2, 2), ... (0, 0, 1, 0, 0, 0, 1, 0), ... ), ... ( ... (0, 0, 0, 0, 0, 0, 0, 0), ... (0, 0, 1, 0, 2, 0, 0, 0), ... (1, 2, 0, 0, 2, 1, 0, 0), ... (2, 2, 2, 0, 2, 2, 2, 2), ... (1, 0, 0, 0, 0, 0, 0, 3), ... ), ... ( ... (1, 0, 0, 0, 0, 0, 0, 1), ... (0, 0, 0, 1, 0, 0, 0, 0), ... (0, 2, 0, 0, 0, 0, 1, 0), ... (2, 2, 2, 0, 2, 2, 2, 2), ... (0, 1, 0, 3, 0, 1, 0, 1), ... ), ... ( ... (0, 0, 0, 0, 0, 0, 0, 1), ... (0, 0, 0, 1, 2, 0, 3, 0), ... (0, 2, 0, 0, 2, 1, 0, 0), ... (2, 2, 2, 1, 2, 2, 2, 2), ... (0, 1, 0, 0, 1, 0, 0, 1), ... ), ... ) >>> cleaners = ( ... RobotCleanerDFS(), ... RobotCleanerDFS2(), ... # RobotCleanerBFS(), ... ) >>> gotcha = [] >>> for grid in CASES: ... for cleaner in cleaners: ... room = Room([list(r) for r in grid]) ... robot = Robot(room) ... ... gotcha.append(not room.is_clear()) ... cleaner.clean_room(robot) ... ... if not room.is_clear(): room._print_room(); print(cleaner) ... gotcha.append(room.is_clear()) >>> bool(gotcha) and all(gotcha) True """ class Dirs: """ The directions should be in order to make turnleft/right in Robot more convient if need 8-dirs, the order becomes: D, DR, R, UR, U, UL, L, DL """ DOWN = 0 RIGHT = 1 UP = 2 LEFT = 3 DELTA = ( ( 1, 0), ( 0, 1), (-1, 0), ( 0, -1), ) class Room: EMPTY = 0 CLEANUP = 1 OBSTACLE = 2 ROBOT = 3 def __init__(self, grid): """ :type grid: list[list[int]] """ self.__room = grid self.__cleanups = 0 self.__robot_at = (0, 0) m, n = len(grid), len(grid[0]) for x in range(m): for y in range(n): if grid[x][y] == self.CLEANUP: self.__cleanups += 1 elif grid[x][y] == self.ROBOT: grid[x][y] = self.EMPTY self.__robot_at = (x, y) def is_clear(self): """ :rtype: bool """ return self.__cleanups == 0 def move_robot(self, direction): """ :type direction: int, defined in Dirs :rtype: bool """ m, n = len(self.__room), len(self.__room[0]) x, y = self.__robot_at dx, dy = Dirs.DELTA[direction] _x, _y = x + dx, y + dy if not (0 <= _x < m and 0 <= _y < n): return False if self.__room[_x][_y] == self.OBSTACLE: return False self.__robot_at = (_x, _y) return True def clean(self, robot): """ :type robot: Robot :rtype: void """ if not isinstance(robot, Robot): return x, y = self.__robot_at if self.__room[x][y] == self.CLEANUP: self.__room[x][y] = self.EMPTY self.__cleanups -= 1 def _get_robot(self): # for testing return self.__robot_at def _print_room(self): # for testing print( '\n'.join(str(r) for r in self.__room), '\nRobot at: ', self.__robot_at, '\nCleanups: ', self.__cleanups, '\n' ) class Robot: def __init__(self, room): """ :type room: Room """ self.__room = room self.__face = Dirs.DOWN def move(self, direction=None): """ :type direction: int, defined in Dirs :rtype: bool """ if direction in range(len(Dirs.DELTA)): self.__face = direction return self.__room.move_robot(self.__face) is True def turnleft(self, k=1): """ :type k: int :rtype: void """ n = len(Dirs.DELTA) self.__face = (self.__face + k) % n def turnrigt(self, k=1): """ :type k: int :rtype: void """ # note that, -1 % 4 == 3 in Python, or just (x - k + n) % n n = len(Dirs.DELTA) self.__face = (self.__face - k) % n def clean(self): """ :rtype: void """ self.__room.clean(self) def _get_face(self): # for testing return self.__face class RobotCleanerDFS: """ this approach is for we need to adjust `dir` manually the `robot.move()` only can move forward with 1 step """ def clean_room(self, robot): """ :type robot: Robot """ if not isinstance(robot, Robot): return """ robot's direction and coord no needs to same as room just start as (0, 0), and face 0 (this 0 just ref of dirs, no needs to treat it as Dirs.DOWN) Dirs.DELTA => D, R, U, L (1, 0), (0, 1), (-1, 0), (0, -1) """ self.dfs(0, 0, 0, robot, set()) def dfs(self, x, y, to_dir, robot, visited): robot.clean() visited.add((x, y)) # down d = to_dir _x = x + Dirs.DELTA[d][0] _y = y + Dirs.DELTA[d][1] if (_x, _y) not in visited and robot.move(): self.dfs(_x, _y, d, robot, visited) robot.turnrigt() else: robot.turnleft() # right d = (to_dir + 1) % len(Dirs.DELTA) _x = x + Dirs.DELTA[d][0] _y = y + Dirs.DELTA[d][1] if (_x, _y) not in visited and robot.move(): self.dfs(_x, _y, d, robot, visited) else: robot.turnleft(2) # left d = (to_dir + 3) % len(Dirs.DELTA) _x = x + Dirs.DELTA[d][0] _y = y + Dirs.DELTA[d][1] if (_x, _y) not in visited and robot.move(): self.dfs(_x, _y, d, robot, visited) robot.turnleft() else: robot.turnrigt() # up d = (to_dir + 2) % len(Dirs.DELTA) _x = x + Dirs.DELTA[d][0] _y = y + Dirs.DELTA[d][1] if (_x, _y) not in visited and robot.move(): self.dfs(_x, _y, d, robot, visited) robot.turnrigt(2) # move robot when the recursion is back robot.move() class RobotCleanerDFS2: """ this approach is for we can just pass `dir` into `robot.move(dir)` """ def clean_room(self, robot): """ :type robot: Robot """ if not isinstance(robot, Robot): return """ robot's direction and coord no needs to same as room just start as (0, 0), and face 0 (this 0 just ref of dirs, no needs to treat it as Dirs.DOWN) """ self.dfs(0, 0, 0, robot, set()) def dfs(self, x, y, from_dir, robot, visited): # is there a api to detect the cell need to clean? robot.clean() visited.add((x, y)) for to_dir in range(len(Dirs.DELTA)): if to_dir == from_dir: continue # to_dir is index and also the direction defined in Dirs dx, dy = Dirs.DELTA[to_dir] _x = x + dx _y = y + dy if (_x, _y) in visited: continue if robot.move(to_dir): self.dfs(_x, _y, (to_dir + 2) % len(Dirs.DELTA), robot, visited) else: visited.add((_x, _y)) robot.move(from_dir) class RobotCleanerBFS: def clean_room(self, robot): """ :type robot: Robot """ if not isinstance(robot, Robot): return def bfs(self): pass if __name__ == '__main__': # for debugging room = Room([ [1, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 1, 0, 0, 0, 0], [0, 2, 0, 0, 0, 0, 1, 0], [2, 2, 2, 0, 2, 2, 2, 2], [0, 1, 0, 3, 0, 1, 0, 1], ]) robot = Robot(room) s = RobotCleanerDFS() room._print_room() s.clean_room(robot) room._print_room() ================================================ FILE: other/snake_and_ladder_problem.py ================================================ """ Problem: https://www.geeksforgeeks.org/snake-ladder-problem-2/ """ def get_min_dice_throws(moves): """ :type moves: list[int] :type n: int :rtype: int >>> moves = [-1] * 30 >>> for i, val in ( ... # ladders ... ( 2, 21), ... ( 4, 7), ... (10, 25), ... (19, 28), ... # snakes ... (26, 0), ... (20, 8), ... (16, 3), ... (18, 6), ... ): ... moves[i] = val >>> get_min_dice_throws(moves) 3 """ if not moves: return -1 n = len(moves) queue, _queue = [0], [] steps = {0: 0} # min steps to get i while queue: for i in queue: for j in range(i + 1, min(i + 7, n)): # 6-faced dice => max 6 steps but up to n if j == n - 1: return steps[i] if j in steps: # is visited continue # next cell k = moves[j] if moves[j] != -1 else j steps[k] = steps[i] + 1 _queue.append(k) queue, _queue = _queue, [] return -1 ================================================ FILE: other/stock_stream.py ================================================ class StockStream: def __init__(self, prices): pass def update(self, timestamp, price): """ :type timestamp: int :type price: int :rtype: void """ pass def get_highest(self): """ :rtype: int """ pass def get_lowest(self): """ :rtype: int """ pass def get_latest(self): """ :rtype: int """ pass ================================================ FILE: other/string_abbreviation.py ================================================ """ This problem should be discussed, and there might be 3 ways to solve REF: http://www.1point3acres.com/bbs/thread-218909-1-1.html 1. add specific char to separate. '@' 2. encode number anyway 3. cheating, like '1aaaaa' -> '1a4xa' >>> CASE = ( ... ('ff', 'ff'), ... ('fff', 'fff'), ... ('ffff', '4xf'), ... ('1xt', '1x1xt'), ... ('5xt', '1x5xt'), ... ('1aaaaa', '1x15xa'), ... ('3aaaaa', '1x35xa'), ... ('123aaaa', '1x11x21x34xa'), ... ('aaabbbbcccc', 'aaa4xb4xc'), ... ) >>> all(decode(encode(inpt)) == inpt for inpt, _ in CASE) True >>> all(encode(inpt) == oupt for inpt, oupt in CASE) True >>> all(decode(oupt) == inpt for inpt, oupt in CASE) True """ def encode(s): """ :type s: str :rtype: str """ if not s: return '' res = [] cnt = 1 char = s[0] for i in range(1, len(s)): if s[i] == s[i - 1]: cnt += 1 continue # s[i] != s[i - 1] if char.isdigit() or cnt > 3: res.append(str(cnt)) res.append('x') res.append(char) else: res.append(char * cnt) cnt = 1 char = s[i] if char.isdigit() or cnt > 3: res.append(str(cnt)) res.append('x') res.append(char) else: res.append(char * cnt) return ''.join(res) def decode(s): """ :type s: str :rtype: str """ if not s: return '' res = [] i = 0 n = len(s) while i < n: is_digit = s[i].isdigit() if is_digit and i + 2 >= n: return '' if is_digit: res.append(int(s[i]) * s[i + 2]) i += 3 else: res.append(s[i]) i += 1 return ''.join(res) ================================================ FILE: other/the_point_inside_polygon.py ================================================ """ Questions: 1. Choose a random point from one single rectangle. => get_random_point_inside_rectangle 2. Choose a random point from multiple rectangles, if there is no overlapping among them. => get_random_point_inside_rectangle2 3. Choose a random point from multiple rectangles, if there is overlapping among them. => get_random_point_inside_rectangle3 4. Is a point inside polygon? => is_point_inside_polygon * p.s. overlapping means the point - was shared between two recs - even the point is a vertex or on edge. """ import random def get_random_point_inside_rectangle(rectangle): """ :type rectangle: list[int] :rtype: list[int] """ if not rectangle: return [] x1, y1, x2, y2 = rectangle return [ random.randint(x1, x2), random.randint(y2, y1), ] # ====== def get_random_point_inside_rectangle2(rectangles): """ :type rectangles: list[list[int]] :rtype: list[int] """ if not rectangles: return [] k = total = 0 for i in range(len(rectangles)): x1, y1, x2, y2 = rectangles[i] area = (x2 - x1) * (y1 - y2) if random.randrange(total + area) >= total: k = i total += area return get_random_point_inside_rectangle(rectangles[k]) # ====== def get_random_point_inside_rectangle3(rectangles): """ :type rectangles: list[list[int]] :rtype: list[int] """ if not rectangles: return [] # ====== def is_point_inside_polygon(polygon, point): """ :type polygon: list[list[int]] :type point: list[int] :rtype: bool """ if not polygon or not point or len(polygon) < 3: return False # ====== if __name__ == '__main__': import collections gotcha = [] # single-rec freq = collections.defaultdict(int) for _ in range(10000): x, y = get_random_point_inside_rectangle((0, 2, 3, 0)) freq[x, y] += 1 gotcha.append(len(freq) == 12) gotcha.append(all(633 <= fq <= 1033 for fq in freq.values())) # multi-recs, no overlapping freq = collections.defaultdict(int) for _ in range(10000): x, y = get_random_point_inside_rectangle2(( (0, 2, 1, 0), (2, 2, 3, 0), )) freq[x, y] += 1 gotcha.append(len(freq) == 12) gotcha.append(all(633 <= fq <= 1033 for fq in freq.values())) # # multi-recs, overlapping # freq = collections.defaultdict(int) # for _ in range(100000): # x, y = get_random_point_inside_rectangle3(( # (3, 3, 7, -4), # (-4, 5, 1, -2), # (-2, 7, 5, 1), # )) # freq[x, y] += 1 # gotcha.append(len(freq) == 115) # gotcha.append(all(469 <= fq <= 1269 for fq in freq.values())) # # multi-recs, overlapping # freq = collections.defaultdict(int) # for _ in range(100000): # x, y = get_random_point_inside_rectangle3(( # (-6, 2, 0, -4), # (-2, 4, 4, -2), # (-4, 6, 2, 0), # )) # freq[x, y] += 1 # gotcha.append(len(freq) == 101) # gotcha.append(all(590 <= fq <= 1390 for fq in freq.values())) # for _in, _out in ( # (([[0, 0], [10, 0], [10, 10], [0, 10]], [20, 20]), False), # (([[0, 0], [10, 0], [10, 10], [0, 10]], [5, 5]), True), # (([[0, 0], [5, 5], [5, 0]], [3, 3]), False), # (([[0, 0], [5, 5], [5, 0]], [5, 1]), False), # (([[0, 0], [5, 5], [5, 0]], [8, 1]), False), # (([[0, 0], [10, 0], [10, 10], [0, 10]], [-1, 10]), False), # (([[2, 1], [3, 2], [2, 3]], [1, 2]), False), # (([[0, 0], [5, 0], [10, 10], [5, 10]], [3, 3]), True), # (([[0, 0], [5, 0], [10, 10], [5, 10]], [4, 10]), False), # (([[0, 0], [-5, 0], [-10, -10]], [0, -2]), False), # (([[2, 5], [5, 0], [8, 5], [5, 10]], [0, 0]), False), # (([[2, 5], [5, 0], [8, 5], [5, 10]], [5, 5]), True), # ): # res = is_point_inside_polygon(*_in) # if res != _out: print(_in, res) # gotcha.append(res == _out) print(bool(gotcha) and all(gotcha)) ================================================ FILE: other/uneven_random_get.py ================================================ """ TODO: 1. do expectations like `chi-square calculator` """ import random def uneven_random_get(options, rate): """unevenly fetch the option according to the corresponding rate :type options: list[str] :type rate: list[num] :rtype: str """ if not options or not rate or len(options) != len(rate): return '' num = 0 rand = random.randint(1, sum(rate)) for i in range(len(rate)): num += rate[i] if num >= rand: return options[i] return options[0] def uneven_random_get2(options, rate): """unevenly fetch the option according to the corresponding rate :type options: list[str] :type rate: list[num] :rtype: str """ if not options or not rate or len(options) != len(rate): return '' k = total = 0 for i in range(len(rate)): if random.randrange(total + rate[i]) >= total: k = i total += rate[i] return options[k] if __name__ == '__main__': gotcha = [] options = 'abc' for uneven_get in (uneven_random_get, uneven_random_get2): freq = dict.fromkeys(options, 0) for _ in range(10000): c = uneven_get(options, (10, 10, 10)) freq[c] += 1 gotcha.append(all(2833 <= freq[c] <= 3833 for c in options)) freq = dict.fromkeys(options, 0) for _ in range(10000): c = uneven_get(options, (1, 1, 1)) freq[c] += 1 gotcha.append(all(2833 <= freq[c] <= 3833 for c in options)) freq = dict.fromkeys(options, 0) for _ in range(10000): c = uneven_get(options, (8, 1, 1)) freq[c] += 1 gotcha.append(all(( 7500 <= freq['a'] <= 8500, 500 <= freq['b'] <= 1500, 500 <= freq['c'] <= 1500, ))) print(bool(gotcha) and all(gotcha)) ================================================ FILE: other/unique_paths_with_followups.py ================================================ """ 给定一个矩形的宽和长,求所有可能的路径数量 Rules: 1. 从左上角走到右上角 2. 要求每一步只能向正右、右上或右下走,即 →↗↘ followup1: 优化空间复杂度至 O(n) followup2: 给定矩形里的三个点,判断是否存在遍历这三个点的路经 followup3: 给定矩形里的三个点,找到遍历这三个点的所有路径数量 followup4: 给定一个下界 (x == H),找到能经过给定下界的所有路径数量 (x >= H) followup5: 起点和终点改成从左上到左下,每一步只能 ↓↘↙,求所有可能的路径数量 """ def find_unique_paths(m, n): """ :type m: int :type n: int :rtype: int >>> gotcha = [ ... find_unique_paths(*_in) == _out ... for _in, _out in ( ... ((2, 2), 1), ((2, 3), 2), ((3, 3), 2), ... ((5, 5), 9), ((7, 6), 21), ((6, 7), 51), ... ) ... ] >>> bool(gotcha) and all(gotcha) True """ if not m or not n: return 0 dp = [[0] * n for _ in range(m)] dp[0][0] = 1 for y in range(1, n): for x in range(m): dp[x][y] = dp[x][y - 1] if x > 0: dp[x][y] += dp[x - 1][y - 1] if x + 1 < m: dp[x][y] += dp[x + 1][y - 1] return dp[0][n - 1] def find_unique_paths1(m, n): """ :type m: int :type n: int :rtype: int >>> gotcha = [ ... find_unique_paths1(*_in) == _out ... for _in, _out in ( ... ((2, 2), 1), ((2, 3), 2), ((3, 3), 2), ... ((5, 5), 9), ((7, 6), 21), ((6, 7), 51), ... ) ... ] >>> bool(gotcha) and all(gotcha) True """ if not m or not n: return 0 dp = [0] * m dp[0] = 1 pre = cur = 0 for y in range(1, n): pre = cur = 0 for x in range(m): pre, cur = cur, dp[x] if x > 0: dp[x] += pre if x + 1 < m: dp[x] += dp[x + 1] return dp[0] def find_unique_paths2(m, n, points): """ :type m: int :type n: int :type points: list[list[int]] :rtype: bool >>> gotcha = [ ... find_unique_paths2(*_in) == _out ... for _in, _out in ( ... ((2, 3, [[1, 0], [1, 1], [1, 2]]), False), ... ((3, 3, [[1, 0], [2, 1], [1, 2]]), False), ... ((3, 3, [[1, 0], [1, 1], [1, 2]]), False), ... ((5, 5, [[0, 1], [2, 2], [1, 3]]), False), ... ((5, 5, [[1, 1], [2, 2], [1, 3]]), True), ... ((5, 5, [[2, 2], [1, 1], [1, 3]]), True), ... ((6, 8, [[0, 0], [4, 3], [0, 7]]), False), ... ((8, 6, [[1, 1], [0, 4], [1, 3]]), True), ... ((8, 6, [[1, 1], [0, 4], [2, 3]]), False), ... ) ... ] >>> bool(gotcha) and all(gotcha) True """ if not m or not n or not points or len(points) < 3: return False path = [(0, 0), (0, n - 1)] path.extend(tuple(p) for p in points) path.sort(key=lambda p: (p[1], p[0])) for i in range(1, len(path)): x, y = path[i] _x, _y = path[i - 1] delta = y - _y if not (x - delta <= _x <= x + delta): return False return True def find_unique_paths3(m, n, points): """ :type m: int :type n: int :type points: list[list[int]] :rtype: int >>> gotcha = [ ... find_unique_paths3(*_in) == _out ... for _in, _out in ( ... ((2, 3, [[1, 0], [1, 1], [1, 2]]), 0), ... ((3, 3, [[1, 0], [2, 1], [1, 2]]), 0), ... ((3, 3, [[1, 0], [1, 1], [1, 2]]), 0), ... ((5, 5, [[0, 1], [2, 2], [1, 3]]), 0), ... ((5, 5, [[1, 1], [2, 2], [1, 3]]), 1), ... ((5, 5, [[2, 2], [1, 1], [1, 3]]), 1), ... ((6, 8, [[0, 0], [4, 3], [0, 7]]), 0), ... ((8, 6, [[0, 0], [0, 5], [0, 4]]), 9), ... ((8, 6, [[1, 1], [0, 4], [2, 3]]), 0), ... ) ... ] >>> bool(gotcha) and all(gotcha) True """ NOT_FOUND = 0 if not m or not n or not points: return NOT_FOUND points.sort(key=lambda p: (p[1], p[0])) dp = [[0] * n for _ in range(m)] dp[0][0] = 1 k = len(points) i = 0 while points[i][1] == 0: i += 1 if i >= k: return NOT_FOUND for y in range(1, n): for x in range(m): dp[x][y] = dp[x][y - 1] if x > 0: dp[x][y] += dp[x - 1][y - 1] if x + 1 < m: dp[x][y] += dp[x + 1][y - 1] if i < k and y == points[i][1]: for x in range(m): if x != points[i][0]: dp[x][y] = 0 i += 1 return dp[0][n - 1] if i == k else NOT_FOUND def find_unique_paths4(m, n, h): """ :type m: int :type n: int :type h: int :rtype: int >>> gotcha = [ ... find_unique_paths4(*_in) == _out ... for _in, _out in ( ... ((2, 3, 1), 1), ((3, 3, 1), 1), ((3, 3, 2), 0), ... ((4, 4, 0), 4), ((4, 4, 1), 3), ((4, 4, 2), 0), ... ((6, 7, 0), 51), ((6, 7, 1), 50), ((6, 7, 2), 19), ... ((6, 7, 3), 1), ((6, 7, 4), 0), ((6, 7, 5), 0) ... ) ... ] >>> bool(gotcha) and all(gotcha) True """ if not m or not n: return 0 dp = [[0] * n for _ in range(m)] dp[0][0] = 1 for y in range(1, n): for x in range(m): dp[x][y] = dp[x][y - 1] if x > 0: dp[x][y] += dp[x - 1][y - 1] if x + 1 < m: dp[x][y] += dp[x + 1][y - 1] if h < 1: return dp[0][n - 1] for y in range(n): for x in range(h): dp[x][y] = 0 for y in range(1, n): for x in range(h - 1, -1, -1): dp[x][y] = dp[x][y - 1] if x > 0: dp[x][y] += dp[x - 1][y - 1] if x + 1 < m: dp[x][y] += dp[x + 1][y - 1] return dp[0][n - 1] def find_unique_paths5(m, n): """ :type m: int :type n: int :rtype: int >>> gotcha = [ ... find_unique_paths5(*_in) == _out ... for _in, _out in ( ... ((2, 2), 1), ((2, 3), 1), ((3, 3), 2), ... ((5, 5), 9), ((7, 6), 51), ((6, 7), 21), ... ) ... ] >>> bool(gotcha) and all(gotcha) True """ if not m or not n: return 0 dp = [[0] * n for _ in range(m)] dp[0][0] = 1 for x in range(1, m): for y in range(n): dp[x][y] = dp[x - 1][y] if y > 0: dp[x][y] += dp[x - 1][y - 1] if y + 1 < n: dp[x][y] += dp[x - 1][y + 1] return dp[m - 1][0] ================================================ FILE: other/unique_word_abbreviation_ii.py ================================================ """ This question is the follow-up of `leetcode/288_unique_word_abbreviation` 1. test `WordIterator` >>> gotcha = [] >>> for _in, _out in ( ... ('', []), ... ('a', ['a']), ... ('dog', ['3', 'd2', '2g', 'dog']), ... ('abcd', ['4', 'a3', '3d', 'ab2', 'a2d', '2cd', 'abcd']), ... ('price', ['5', 'p4', '4e', 'pr3', 'p3e', '3ce', 'pri2', 'pr2e', 'p2ce', '2ice', 'price']), ... ): ... res = [w for w in WordIterator(_in)] ... if res != _out: print(_in, res) ... gotcha.append(res == _out) >>> bool(gotcha) and all(gotcha) True 2. test `find_unique_abbreviations` >>> gotcha = [] >>> for _in, _out in ( ... ( ... ['internationalization', 'localization', 'dog', 'accessibility', 'automatically'], ... ['20', '12', '3', 'ac11', 'au11'] ... ), ... ( ... ['aaabcdefg', 'aaabbcdef', 'aaabbbcde'], ... ['8g', '8f', '8e'], ... ), ... ( ... ['abcdef', 'abcdef', 'abcdef'], ... ['6', '6', '6'], ... ), ... ( ... ['abcdef', 'abdeef', 'abefef'], ... ['abc3', 'abd3', 'abe3'], ... ), ... ): ... res = find_unique_abbreviations(_in) ... if res != _out: print(_in, res) ... gotcha.append(res == _out) >>> bool(gotcha) and all(gotcha) True """ def find_unique_abbreviations(words): iterators = {} # {abbr: iterator} for word in set(words): iterator = (WordIterator(word).__iter__(), word) abbr = next(iterator[0], None) if not abbr: continue if abbr in iterators: iterators[abbr].append(iterator) else: iterators[abbr] = [iterator] collisions = [key for key, iters in iterators.items() if len(iters) > 1] while collisions: for key in collisions: iters = iterators[key] del iterators[key] for iterator in iters: abbr = next(iterator[0], None) if not abbr: continue if abbr in iterators: iterators[abbr].append(iterator) else: iterators[abbr] = [iterator] collisions = [key for key, iters in iterators.items() if len(iters) > 1] reversed_index = {iters[0][1]: key for key, iters in iterators.items() if len(iters) == 1} return [reversed_index[word] for word in words] class WordIterator: def __init__(self, word): self.word = word self.TMPL = '{}{}{}' def __iter__(self): if not self.word: return n = len(self.word) if n > 1: yield str(n) for i in range(1, n - 1): for j in range(i + 1): yield self.TMPL.format( self.word[:i - j], n - i, self.word[-j:] if j > 0 else '' ) yield self.word ================================================ FILE: other/upside_down_numbers.py ================================================ def find_upside_down_numbers(n): """ :type n: int :rtype: list[int] >>> gotcha = [] >>> for _in, _out in ( ... (10, [1, 6, 8, 9]), ... (100, [1, 6, 8, 9, 16, 18, 19, 61, 66, 68, 81, 86, 89, 91, 98, 99]), ... (500, [1, 6, 8, 9, 16, 18, 19, 61, 66, 68, 81, 86, 89, 91, 98, 99, 161, 191]), ... (700, [1, 6, 8, 9, 16, 18, 19, 61, 66, 68, 81, 86, 89, 91, 98, 99, 161, 169, 189, 191, 199, 611, 661, 669, 681]), ... ): ... res = find_upside_down_numbers(_in) ... if res != _out: print(_in, res) ... gotcha.append(res == _out) >>> bool(gotcha) and all(gotcha) True """ ans = [] if not n or n <= 0: return ans cands = '01689' queue, _queue = ['1', '6', '8', '9'], [] up_down_nums = { '0': '0', '1': '1', '6': '9', '8': '8', '9': '6', } while queue: ans.extend(queue) for num in queue: for nxt in cands: res = num + nxt rev = get_reversed_number(res, up_down_nums) if rev[0] == '0' or res == rev or int(rev) > n: continue if int(res) > n: ans.extend(_queue) return [int(s) for s in ans] _queue.append(res) queue, _queue = _queue, [] return [int(s) for s in ans] def get_reversed_number(s, up_down_nums): res = '' for i in range(len(s) - 1, -1, -1): res += up_down_nums[s[i]] return res ================================================ FILE: pramp/array_index_and_element_equality.py ================================================ def index_equals_value_search(nums): if not nums: return -1 left, right = 0, len(nums) - 1 while left + 1 < right: mid = (left + right) // 2 if nums[left] == left: # lowest index return left if nums[mid] == mid: return mid if nums[mid] < mid: left = mid else: right = mid for mid in (left, right): if nums[mid] == mid: return mid return -1 ================================================ FILE: pramp/array_of_array_products.py ================================================ """ thought: => nums = [a, b, c, d] 1. fill `ans` with `1` => ans = [1, 1, 1, 1] 2. scan from left to right, and do multiply continuously => ans = [1, a, a * b, a * b * c] 3. scan from right to left, and do multiply continuously but with a temp var => prod = b * c * d, c * d, d => ans = [b * c * d, a * c * d, a * b * d, a * b * c] """ def array_of_array_products(nums): """ :type nums: list[int] :rtype: list[int] """ if not nums or len(nums) <= 1: return [] n = len(nums) ans = [1] * n # 1st scan [1, a, a * b, a * b * c] for i in range(1, n): ans[i] *= ans[i - 1] * nums[i - 1] # 2nd scan prod = 1 for i in range(n - 2, -1, -1): prod *= nums[i + 1] ans[i] *= prod return ans ================================================ FILE: pramp/award_budget_cuts.py ================================================ """ Main Concept: budget = 190 [2, 100, 35, 120, 1000] => sort, [1000, 120, 100, 35, 2] => total = sum(arr) - budget => just treat it as bar chart and iterate from start to end to remove the area between `a` and `b` from total below |-| -> a |=|=| -> b | | |-| => untill total < 0 => reach the `cap` line => remove the remaining area from total >>> EPS = 1 ** -3 >>> gotcha = [] >>> for _in, _out in ( ... (([2, 4], 3), 1.5), ... (([2, 4, 6], 3), 1.0), ... (([2, 100, 50, 120, 167], 400), 128.0), ... (([2, 100, 50, 120, 1000], 190), 47.0), ... (([2, 100, 35, 120, 1000], 190), 51.0), ... (([21, 100, 50, 120, 130, 110], 140), 23.8), ... (([210, 200, 150, 193, 130, 110, 209, 342, 117], 1530), 211.0), ... ): ... res = find_grants_cap(*_in) ... if abs(res - _out) >= EPS: print(_in, res) ... gotcha.append(abs(res - _out) < EPS) >>> bool(gotcha) and all(gotcha) True """ def find_grants_cap(grantsArray, newBudget): """ :type grantsArray: list[int] :type newBudget: int :rtype: float """ n = len(grantsArray) area = sum(grantsArray) - newBudget grantsArray.sort(reverse=True) i = segment_sum = 0 while i < n: if i == n - 1: # (i + 1) * (grantsArray[i] - 0) segment_sum = n * grantsArray[i] else: segment_sum = (i + 1) * (grantsArray[i] - grantsArray[i + 1]) if area < segment_sum: break area -= segment_sum i += 1 return grantsArray[i] - area / float(i + 1) ================================================ FILE: pramp/bracket_match.py ================================================ def bracket_match(text): ans = 0 if not text: return ans cnt = 0 for c in text: if c == '(': cnt += 1 elif c == ')': cnt -= 1 if cnt < 0: cnt = 0 ans += 1 if cnt > 0: ans += cnt return ans ================================================ FILE: pramp/busiest_time_in_the_mall.py ================================================ def find_busiest_period(data): timestamp = -1 if not data: return timestamp if len(data) == 1: # data[0][2] always 1 return data[0][0] n = len(data) cnt = maxi = 0 for i in range(len(data)): if data[i][2] == 1: cnt += data[i][1] else: cnt -= data[i][1] if (i == n - 1 or data[i][0] != data[i + 1][0]) and cnt > maxi: maxi = cnt timestamp = data[i][0] return timestamp ================================================ FILE: pramp/decrypt_message.py ================================================ """ Encryption Rules: 1. add 1 to the ascii of first letter 2. add the ascii of prev letter, form second to last 3. keep substract 26 from every letter until it is in [ord('a'), ord('z')] Testing: >>> gotcha = [] >>> for origin, encryption in ( ... ('crime', 'dnotq'), ... ('encyclopedia', 'flgxswdliefy'), ... ('qqqqq', 'rajsb'), ... ('abcdefghijklmnopqrstuvwxyz', 'bvqmjhgghjmqvbiqzjugthwmdv'), ... ('drugtrafficking', 'eobamwpnlmhklrq'), ... ('', ''), ... ): ... res = encrypt(origin) ... if res != encryption: print('L18', origin, res) ... gotcha.append(res == encryption) ... ... res = decrypt(encryption) ... if res != origin: print('L22', encryption, res) ... gotcha.append(res == origin) ... ... res = decrypt(encrypt(origin)) ... if res != origin: print('L26', origin, res) ... gotcha.append(res == origin) ... ... res = decrypt2(encryption) ... if res != origin: print('L30', encryption, res) ... gotcha.append(res == origin) ... ... res = decrypt2(encrypt(origin)) ... if res != origin: print('L34', origin, res) ... gotcha.append(res == origin) >>> bool(gotcha) and all(gotcha) True """ def encrypt(word): if not word: return '' a = ord('a') ans = [ord(c) for c in word] cumul = 1 for i in range(len(word)): cumul += ans[i] ans[i] = chr((cumul - a) % 26 + a) return ''.join(ans) def decrypt(word): if not word: return '' a = ord('a') ans = [ord(c) for c in word] for i in range(len(word) - 1, -1, -1): if i == 0: ans[i] = chr((ans[i] - 1 - a) % 26 + a) else: ans[i] = chr((ans[i] - ans[i - 1] - a) % 26 + a) return ''.join(ans) def decrypt2(word): if not word: return '' a = ord('a') ans = [ord(c) for c in word] cumul = 1 for i in range(len(word)): # substract the cumul sum ans[i] -= cumul # keep adding 26 to make ans[i] in [ord('a'), ord('z')] steps = (a - ans[i]) // 26 if (a - ans[i]) % 26: # (a - ans[i]) % 26 != 0 # add 1 more steps += 1 ans[i] += steps * 26 # update cumul cumul += ans[i] ans[i] = chr(ans[i]) return ''.join(ans) ================================================ FILE: pramp/deletion_distance.py ================================================ """ >>> gotcha = [] >>> for _in, _out in ( ... (('', ''), 0), (('', 'hit'), 3), (('neat', ''), 4), ... (('heat', 'hit'), 3), (('hot', 'not'), 2), (('some', 'thing'), 9), ... (('abc', 'adbc'), 1), (('awesome', 'awesome'), 0), (('ab', 'ba'), 2), ... ): ... for get_distance in (deletion_distance, deletion_distance2): ... res = get_distance(*_in) ... if res != _out: print(_in, res) ... gotcha.append(res == _out) >>> bool(gotcha) and all(gotcha) True """ def deletion_distance(s, t): if s == t: return 0 if not s and not t: return 0 if not s: return len(t) if not t: return len(s) m, n = len(s), len(t) dp = [[0] * (n + 1) for _ in range(m + 1)] for i in range(1, m + 1): dp[i][0] = i for j in range(1, n + 1): dp[0][j] = j for i in range(1, m + 1): for j in range(1, n + 1): if s[i - 1] == t[j - 1]: dp[i][j] = dp[i - 1][j - 1] else: dp[i][j] = 1 + min( dp[i - 1][j], dp[i][j - 1] ) return dp[m][n] def deletion_distance2(s, t): if s == t: return 0 if not s and not t: return 0 if not s: return len(t) if not t: return len(s) m, n = len(s), len(t) dp = [[0] * (n + 1) for _ in range(2)] pre = cur = 0 for j in range(1, n + 1): dp[cur][j] = j for i in range(1, m + 1): pre, cur = cur, 1 - cur dp[cur][0] = i for j in range(1, n + 1): if s[i - 1] == t[j - 1]: dp[cur][j] = dp[pre][j - 1] else: dp[cur][j] = 1 + min( dp[pre][j], dp[cur][j - 1] ) return dp[cur][n] ================================================ FILE: pramp/drone_flight_planner.py ================================================ def calc_drone_min_energy(route): ans = 0 if not route or len(route) < 2: return ans delta = 0 max_z = route[0][2] for i in range(1, len(route)): delta += route[i][2] - route[i - 1][2] if route[i][2] > max_z: max_z = route[i][2] ans = delta return ans ================================================ FILE: pramp/find_the_duplicates.py ================================================ def find_duplicates(arr1, arr2): ans = [] if not arr1 or not arr2: return ans m, n = len(arr1), len(arr2) i = j = 0 while i < m and j < n: if arr1[i] < arr2[j]: i += 1 elif arr1[i] > arr2[j]: j += 1 else: if not ans or arr1[i] != ans[-1]: ans.append(arr1[i]) i += 1 j += 1 return ans def find_duplicates2(arr1, arr2): ans = [] if not arr1 or not arr2: return ans vals = {} for num in arr1: vals[num] = False for num in arr2: if num in vals and vals[num] is False: vals[num] = True ans.append(num) return ans def find_duplicates3(arr1, arr2): if not arr1 or not arr2: return [] val1 = set(arr1) val2 = set(arr2) return list(val1 & val2) if __name__ == '__main__': print(find_duplicates([1, 2, 3, 5, 6, 7], [3, 6, 7, 8, 20])) print(find_duplicates([1, 2, 3, 3, 5, 6, 7], [3, 3, 6, 7, 8, 20])) print(find_duplicates2([1, 2, 3, 5, 6, 7], [3, 6, 7, 8, 20])) print(find_duplicates2([1, 2, 3, 3, 5, 6, 7], [3, 3, 6, 7, 8, 20])) print(find_duplicates3([1, 2, 3, 5, 6, 7], [3, 6, 7, 8, 20])) print(find_duplicates3([1, 2, 3, 3, 5, 6, 7], [3, 3, 6, 7, 8, 20])) ================================================ FILE: pramp/flatten_a_dictionary.py ================================================ def flatten_dictionary(dictionary): ans = {} if not dictionary: return ans dfs(dictionary, [], ans) return ans def dfs(dictionary, keys, ans): if not isinstance(dictionary, dict): key = '.'.join(keys) ans[key] = dictionary return for key in dictionary: if key: keys.append(key) dfs(dictionary[key], keys, ans) if key: keys.pop() ================================================ FILE: pramp/h_tree_construction.py ================================================ def draw_line(x1, y1, x2, y2): pass def draw_h_tree(x, y, length, depth): """iteration :type x: float :type y: float :type length: float :type depth: float :rtype: void """ queue, _queue = [(x, y)], [] for _ in range(depth): for x, y in queue: x1 = x - length / 2 y1 = y + length / 2 x2 = x + length / 2 y2 = y - length / 2 draw_line(x1, y1, x1, y2) draw_line(x2, y1, x2, y2) draw_line(x1, y, x2, y) _queue.append((x1, y1)) _queue.append((x1, y2)) _queue.append((x2, y1)) _queue.append((x2, y2)) queue, _queue = _queue, [] length /= 2 ** 0.5 def draw_h_tree2(x, y, length, depth): """recursion :type x: float :type y: float :type length: float :type depth: float :rtype: void """ if depth == 0: return x1 = x - length / 2 y1 = y + length / 2 x2 = x + length / 2 y2 = y - length / 2 draw_line(x1, y1, x1, y2) draw_line(x2, y1, x2, y2) draw_line(x1, y, x2, y) _length = length / (2 ** 0.5) _depth = depth - 1 draw_h_tree2(x1, y1, _length, _depth) draw_h_tree2(x1, y2, _length, _depth) draw_h_tree2(x2, y1, _length, _depth) draw_h_tree2(x2, y2, _length, _depth) ================================================ FILE: pramp/k_messed_array_sort.py ================================================ import heapq def sort_k_messed_array(nums, k): """ Given an array of integers arr where each element is at most k places away from its sorted position. time: O(n * log(k)) space: O(k) """ if not nums or not k: return nums n = len(nums) heap = [] for i in range(k + 1): heapq.heappush(heap, nums[i]) for i in range(k + 1, n): nums[i - k - 1] = heapq.heappop(heap) heapq.heappush(heap, nums[i]) for i in range(n - k - 1, n): nums[i] = heapq.heappop(heap) return nums ================================================ FILE: pramp/largest_smaller_bst_node.py ================================================ def largest_smaller_bst_node(root, target): ans = -1 if not root: return ans while root: if root.val < target: ans = root.val root = root.right else: root = root.left return ans ================================================ FILE: pramp/number_of_paths.py ================================================ def num_of_paths_to_dest(n): if not n or n == 0: return 0 if n == 1: return 1 dp = [[0] * n for _ in range(n)] for i in range(n): dp[i][0] = 1 for i in range(1, n): for j in range(1, i + 1): dp[i][j] = dp[i - 1][j] + dp[i][j - 1] return dp[n - 1][n - 1] ================================================ FILE: pramp/pairs_with_specific_difference.py ================================================ def find_pairs_with_given_difference(arr, k): ans = [] if not arr or not isinstance(k, int): return ans n = len(arr) sums = {} for i in range(n): sums[arr[i] - k] = i for j in range(n): if arr[j] in sums: i = sums[arr[j]] ans.append([arr[i], arr[j]]) return ans ================================================ FILE: pramp/pancake_sort.py ================================================ """ Main Concept: 1. find the max and do `flip` in [0,max_idx] 2. the max is already at `0`, `flip` it to the correct pos 3. repeat (1) [1,5,2,4,3] => [5,1,2,4,3] => [3,4,2,1,5] => [4,3,2,1,5] => [1,2,3,4,5] Testing: >>> gotcha = [] >>> for _in, _out in ( ... (([1, 2, 3, 4, 5], 3), [3, 2, 1, 4, 5]), ... (([1, 2, 3, 4, 5], 0), [1, 2, 3, 4, 5]), ... (([1, 2, 3, 4, 5], 5), [5, 4, 3, 2, 1]), ... (([1, 2, 3, 4, 5], 6), [5, 4, 3, 2, 1]), ... ): ... flip(*_in) ... if _in[0] != _out: print(_in, _out) ... gotcha.append(_in[0] == _out) >>> bool(gotcha) and all(gotcha) True >>> gotcha = [] >>> for _in, _out in ( ... ([1, 5, 4, 3, 2], [1, 2, 3, 4, 5]), ... ([5, 1, 3, 4, 2], [1, 2, 3, 4, 5]), ... ([1, 4, 2, 3, 5], [1, 2, 3, 4, 5]), ... ([5, 10, 1, 20, 4], [1, 4, 5, 10, 20]), ... ([5, 1, 4, 20, 10], [1, 4, 5, 10, 20]), ... ): ... res = pancake_sort(_in) ... if res != _out: print(_in, res) ... gotcha.append(res == _out) >>> bool(gotcha) and all(gotcha) True """ def pancake_sort(nums): """using `flip` to sort input in order :type nums: list[int] :rtype: list[int] """ for j in range(len(nums) - 1, -1, -1): i = get_max_index(nums, j) flip(nums, i + 1) flip(nums, j + 1) return nums def flip(nums, k): """reverses the order of the first `k` elements in the array `nums` place :type nums: list[int] :type k: int :rtype: void """ if not nums: return if not k or k <= 1: return k = min(k, len(nums)) i, j = 0, k - 1 while i < j: nums[i], nums[j] = nums[j], nums[i] i += 1 j -= 1 def get_max_index(nums, i): """return the index of the maximum in [0,i] of `nums` :type nums: list[int] :type i: int :rtype: int """ k = 0 for j in range(1, i + 1): if nums[j] > nums[k]: k = j return k ================================================ FILE: pramp/root_of_number.py ================================================ """ >>> gotcha = [] >>> for _in, _out in ( ... ((4, 2), 2), ... ((9, 3), 2.08), ... ((7, 3), 1.913), ... ((11, 4), 1.821), ... ): ... res = root(*_in) ... valid = abs(res - _out) < 0.001 ... if not valid: print(_in, res) ... gotcha.append(valid) >>> bool(gotcha) and all(gotcha) True """ def root(x, n): if not x or x in (0, 1): return x left = 0 right = max(1, x) while right - left > 1e-4: mid = (left + right) / 2.0 product = get_product(mid, n) if product < x: left = mid elif product > x: right = mid else: return mid return (left + right) / 2.0 def get_product(x, n): res = 1 for _ in range(n): res *= x return res ================================================ FILE: pramp/sales_path.py ================================================ """ 1. find the minimum cost for the path from root through leaf => get_cheapest_cost 2. find all paths which have the minimum cost => get_cheapest_cost_paths => how to find all paths by one-pass? Node Structure: >>> class TreeNode: ... def __init__(self, cost): ... self.cost = cost ... self.children = [] Testing: >>> trees = [] >>> TEST_CASE = ( ... (( ... (0, 0, [1, 2, 3]), ... (1, 5, [4]), ... (2, 3, [5]), ... (3, 6, [6]), ... (4, 4, []), ... (5, 2, []), ... (6, 1, []), ... ), 5, [[0, 3, 2]]), ... (( ... (0, 0, [1, 2, 3]), ... (1, 5, [4]), ... (2, 3, [5, 6]), ... (3, 6, [7, 8]), ... (4, 4, []), ... (5, 2, [9]), ... (6, 0, [10]), ... (7, 1, []), ... (8, 5, []), ... (9, 1, [11]), ... (10, 10, []), ... (11, 1, []), ... ), 7, [[0, 3, 2, 1, 1], [0, 6, 1]]), ... ) >>> for case, _, _ in TEST_CASE: ... nodes = {} ... for id, cost, _ in case: ... nodes[id] = TreeNode(cost) ... for id, _, children in case: ... nodes[id].children = [nodes[id] for id in children] ... trees.append(nodes[0]) 1. test get_cheapest_cost: >>> gotcha = [] >>> for i in range(len(TEST_CASE)): ... res = get_cheapest_cost(trees[i]) ... if res != TEST_CASE[i][1]: print(i, res) ... gotcha.append(res == TEST_CASE[i][1]) >>> bool(gotcha) and all(gotcha) True 2. test get_cheapest_cost_paths: >>> gotcha = [] >>> for i in range(len(TEST_CASE)): ... res = get_cheapest_cost_paths(trees[i]) ... if res != TEST_CASE[i][2]: print(i, res) ... gotcha.append(res == TEST_CASE[i][2]) >>> bool(gotcha) and all(gotcha) True """ def get_cheapest_cost(root): if not root: return 0 if not root.children: return root.cost res = float('inf') for child in root.children: tmp = get_cheapest_cost(child) if tmp < res: res = tmp return res + root.cost def get_cheapest_cost_paths(root): ans = [] if not root: return ans min_val = get_cheapest_cost(root) _dfs(root, min_val, ans, [root.cost]) return ans def _dfs(root, target, ans, path): if not root: return if not root.children and target == 0: ans.append(path[:]) return if not root.children: return for child in root.children: if target < child.cost: continue path.append(child.cost) _dfs(child, target - child.cost, ans, path) path.pop() ================================================ FILE: pramp/sentence_reverse.py ================================================ """ Test Case: >>> gotcha = [] >>> for _in, _out in ( ... ([' ', ' '], [' ', ' ']), ... (['a', ' ', ' ', 'b'], ['b', ' ', ' ', 'a']), ... (['h', 'e', 'l', 'l', 'o'], ['h', 'e', 'l', 'l', 'o']), ... ( ... ['p', 'e', 'r', 'f', 'e', 'c', 't', ' ', 'm', 'a', 'k', 'e', 's', ' ', 'p', 'r', 'a', 'c', 't', 'i', 'c', 'e'], ... ['p', 'r', 'a', 'c', 't', 'i', 'c', 'e', ' ', 'm', 'a', 'k', 'e', 's', ' ', 'p', 'e', 'r', 'f', 'e', 'c', 't'] ... ), ... ( ... ['y', 'o', 'u', ' ', 'w', 'i', 't', 'h', ' ', 'b', 'e', ' ', 'f', 'o', 'r', 'c', 'e', ' ', 't', 'h', 'e', ' ', 'm', 'a', 'y'], ... ['m', 'a', 'y', ' ', 't', 'h', 'e', ' ', 'f', 'o', 'r', 'c', 'e', ' ', 'b', 'e', ' ', 'w', 'i', 't', 'h', ' ', 'y', 'o', 'u'] ... ), ... ( ... ['g', 'r', 'e', 'a', 't', 'e', 's', 't', ' ', 'n', 'a', 'm', 'e', ' ', 'f', 'i', 'r', 's', 't', ' ', 'e', 'v', 'e', 'r', ' ', 'n', 'a', 'm', 'e', ' ', 'l', 'a', 's', 't'], ... ['l', 'a', 's', 't', ' ', 'n', 'a', 'm', 'e', ' ', 'e', 'v', 'e', 'r', ' ', 'f', 'i', 'r', 's', 't', ' ', 'n', 'a', 'm', 'e', ' ', 'g', 'r', 'e', 'a', 't', 'e', 's', 't'] ... ), ... ([' ', ' ', 'a', ' ', 'b', 'c'], ['b', 'c', ' ', 'a', ' ', ' ']), ... (['b', 'c', ' ', 'a', ' ', ' '], [' ', ' ', 'a', ' ', 'b', 'c']), ... ([' ', ' ', 'a', ' ', 'b', 'c', ' '], [' ', 'b', 'c', ' ', 'a', ' ', ' ']), ... ): ... for test_func in (reverse_words, reverse_words2): ... res = test_func(_in) ... if res != _out: print(_in, res) ... gotcha.append(res == _out) >>> bool(gotcha) and all(gotcha) True """ """ Approach 1: use built-in function """ def reverse_words(arr): return list(' '.join(reversed(''.join(arr).split(' ')))) """ Approach 2: swap children and modify in place """ def reverse_words2(arr): if not arr: return [] n = len(arr) reverse_in_range(arr, 0, n - 1) left = 0 while left < n and arr[left] == ' ': left += 1 for right in range(n): if arr[right] != ' ': continue reverse_in_range(arr, left, right - 1) left = right + 1 right = n - 1 while right >= 0 and arr[right] == ' ': right -= 1 reverse_in_range(arr, left, right) return arr def reverse_in_range(arr, i, j): """ to reverse arr[i:j + 1] """ while i < j: arr[i], arr[j] = arr[j], arr[i] i += 1 j -= 1 ================================================ FILE: pramp/smallest_substring_of_all_characters.py ================================================ def get_shortest_unique_substring(strs, s): if not strs or not s: return '' freq = dict.fromkeys(strs, 0) left = cnt = 0 start = 0 size = INF = float('inf') for right in range(len(s)): if s[right] in freq: if freq[s[right]] == 0: cnt += 1 freq[s[right]] += 1 while cnt == len(freq): if right - left + 1 < size: start = left size = right - left + 1 if s[left] in freq: freq[s[left]] -= 1 if freq[s[left]] == 0: cnt -= 1 left += 1 return s[start:start + size] if size < INF else '' ================================================ FILE: pramp/time_planner.py ================================================ def meeting_planner(slots1, slots2, duration): if not slots1 or not slots2 or not duration: return [] m, n = len(slots1), len(slots2) i = j = 0 while i < m and j < n: start = max(slots1[i][0], slots2[j][0]) end = min(slots1[i][1], slots2[j][1]) if start + duration <= end: return [start, start + duration] if slots1[i][1] < slots2[j][1]: i += 1 else: j += 1 return [] ================================================ FILE: pramp/word_count_engine.py ================================================ """ >>> gotcha = [] >>> for _in, _out in ( ... ( ... "Practice makes perfect. you'll only get Perfect by practice. just practice!", ... [['practice', 3], ['perfect', 2], ['makes', 1], ['youll', 1], ['only', 1], ['get', 1], ['by', 1], ['just', 1]], ... ), ... ( ... "Practice makes perfect. just practice! you'll only get Perfect by practice.", ... [['practice', 3], ['perfect', 2], ['makes', 1], ['just', 1], ['youll', 1], ['only', 1], ['get', 1], ['by', 1]], ... ), ... ( ... "Practice makes perfect. you'll only get Perfect by practice. just practice by yourself!", ... [['practice', 3], ['perfect', 2], ['by', 2], ['makes', 1], ['youll', 1], ['only', 1], ['get', 1], ['just', 1], ['yourself', 1]], ... ), ... ): ... res = word_count_engine(_in) ... if res != _out: print(_in, res) ... gotcha.append(res == _out) >>> bool(gotcha) and all(gotcha) True """ def word_count_engine(document): """ :type document: str :rtype: list[list[str]] count and sort time: O(n logn) """ ans = [] document = document and document.strip() if not document: return ans document = ''.join(c for c in document if c.isalnum() or c == ' ') word2idx = {} for word in document.lower().strip().split(): if not word: continue if word not in word2idx: ans.append([word, 0]) word2idx[word] = len(ans) - 1 i = word2idx[word] ans[i][1] += 1 ans.sort(key=lambda x: x[1], reverse=True) return ans def word_count_engine2(document): """ :type document: str :rtype: list[list[str]] # TODO LRU time: O(n) """ pass ================================================ FILE: topic/README.md ================================================ The time and space complexity of common algorithms and data-structures ====== - Inspired by [Big-O Algorithm Complexity Cheat Sheet @ericdrowell](http://bigocheatsheet.com) - For the complexity representation - 1 param: $\Theta(avg.)$ - 2 params: $\Theta(avg.)$ / $O(worst)$ - 3 params: $\Omega(best)$ / $\Theta(avg.)$ / $O(worst)$ ## Data Structures | Name | 结构名 | Space | Access | Search | Insert | Delete | Note | | --- | --- | --- | --- | --- | --- | --- | --- | | [Array](http://en.wikipedia.org/wiki/Array_data_structure) | 数组 | $O(n)$ | $\Theta(1)$ / $O(1)$ | $\Theta(n)$ / $O(n)$ | $\Theta(n)$ / $O(n)$ | $\Theta(n)$ / $O(n)$ | Swap with `a[-1]` => $O(1)$ in insert, delete | | [Queue](https://en.wikipedia.org/wiki/Queue_(abstract_data_type)) | 队列 | $O(n)$ | $\Theta(n)$ / $O(n)$ | $\Theta(n)$ / $O(n)$ | $\Theta(1)$ / $O(1)$ | $\Theta(1)$ / $O(1)$ | | | [Stack](https://en.wikipedia.org/wiki/Stack_(abstract_data_type)) | 栈 | $O(n)$ | $\Theta(n)$ / $O(n)$ | $\Theta(n)$ / $O(n)$ | $\Theta(1)$ / $O(1)$ | $\Theta(1)$ / $O(1)$ | | | [Heap](https://en.wikipedia.org/wiki/Heap_(data_structure)) | 堆 | | | | | | Check it in [Heap](./#heap) Section | | [Graph](https://en.wikipedia.org/wiki/Graph_(abstract_data_type)) | 图 | | | | | | Check it in [Graph](./#graph) Section | | [Singly-Linked List](https://en.wikipedia.org/wiki/Linked_list#Singly_linked_lists) | 单向链表 | $O(n)$ | $\Theta(n)$ / $O(n)$ | $\Theta(n)$ / $O(n)$ | $\Theta(1)$ / $O(1)$ | $\Theta(1)$ / $O(1)$ | | | [Doubly-Linked List](https://en.wikipedia.org/wiki/Doubly_linked_list) | 双向链表 | $O(n)$ | $\Theta(n)$ / $O(n)$ | $\Theta(n)$ / $O(n)$ | $\Theta(1)$ / $O(1)$ | $\Theta(1)$ / $O(1)$ | | | [Skip List](https://en.wikipedia.org/wiki/Skip_list) | 跳跃表 | $O(n\log{n})$ | $\Theta(\log{n})$ / $O(n)$ | $\Theta(\log{n})$ / $O(n)$ | $\Theta(\log{n})$ / $O(n)$ | $\Theta(\log{n})$ / $O(n)$ | | | [Hash Table](https://en.wikipedia.org/wiki/Hash_table) | 哈希表 | $O(n)$ | - | $\Theta(1)$ / $O(n)$ | $\Theta(1)$ / $O(n)$ | $\Theta(1)$ / $O(n)$ | | | [Binary Search Tree](https://en.wikipedia.org/wiki/Binary_search_tree) | 二叉查找树 | $O(n)$ | $\Theta(\log{n})$ / $O(n)$ | $\Theta(\log{n})$ / $O(n)$ | $\Theta(\log{n})$ / $O(n)$ | $\Theta(\log{n})$ / $O(n)$ | | | [Cartesian Tree](https://en.wikipedia.org/wiki/Cartesian_tree) | 笛卡尔树 | $O(n)$ | - | $\Theta(\log{n})$ / $O(n)$ | $\Theta(\log{n})$ / $O(n)$ | $\Theta(\log{n})$ / $O(n)$ | | | [B-Tree](https://en.wikipedia.org/wiki/B-tree) | B 树 | $O(n)$ | $\Theta(\log{n})$ / $O(\log{n})$ | $\Theta(\log{n})$ / $O(\log{n})$ | $\Theta(\log{n})$ / $O(\log{n})$ | $\Theta(\log{n})$ / $O(\log{n})$ | | | [Red-Black Tree](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree) | 红黑树 | $O(n)$ | $\Theta(\log{n})$ / $O(\log{n})$ | $\Theta(\log{n})$ / $O(\log{n})$ | $\Theta(\log{n})$ / $O(\log{n})$ | $\Theta(\log{n})$ / $O(\log{n})$ | | | [Splay Tree](https://en.wikipedia.org/wiki/Splay_tree) | 伸展树 | $O(n)$ | - | $\Theta(\log{n})$ / $O(\log{n})$ | $\Theta(\log{n})$ / $O(\log{n})$ | $\Theta(\log{n})$ / $O(\log{n})$ | | | [AVL Tree](https://en.wikipedia.org/wiki/AVL_tree) | AVL 树 | $O(n)$ | $\Theta(\log{n})$ / $O(\log{n})$ | $\Theta(\log{n})$ / $O(\log{n})$ | $\Theta(\log{n})$ / $O(\log{n})$ | $\Theta(\log{n})$ / $O(\log{n})$ | | | [KD Tree](https://en.wikipedia.org/wiki/K-d_tree) | K 维树 | $O(n)$ | $\Theta(\log{n})$ / $O(n)$ | $\Theta(\log{n})$ / $O(n)$ | $\Theta(\log{n})$ / $O(n)$ | $\Theta(\log{n})$ / $O(n)$ | | ### Heap | Implementation | 结构名 | Heapify | Access Top | Pop Top | Increase Key | Insert | Delete | Merge | | --- | --- | --- | --- | --- | --- | --- | --- | --- | | Linked List (Sorted) | 链表(已排序) | - | $\Theta(1)$ | $\Theta(1)$ | $\Theta(n)$ | $\Theta(n)$ | $\Theta(1)$ | $\Theta(m+n)$ | | Linked List (Unsorted) | 链表(未排序) | - | $\Theta(n)$ | $\Theta(n)$ | $\Theta(1)$ | $\Theta(1)$ | $\Theta(1)$ | $\Theta(1)$ | | [Binary Heap](https://en.wikipedia.org/wiki/Binary_heap) | 二叉堆 | $O(n)$ | $\Theta(1)$ | $\Theta(\log{n})$ | $\Theta(\log{n})$ | $\Theta(\log{n})$ | $\Theta(\log{n})$ | $\Theta(m+n)$ | | [Binomial Heap](https://en.wikipedia.org/wiki/Binomial_heap) | 二项堆 | - | $\Theta(1)$ | $\Theta(\log{n})$ | $\Theta(\log{n})$ | $\Theta(1)$ | $\Theta(\log{n})$ | $\Theta(\log{n})$ | | [Fibonacci Heap](https://en.wikipedia.org/wiki/Fibonacci_heap) | 斐波那契堆 | - | $\Theta(1)$ | $\Theta(\log{n})$ | $\Theta(1)$ | $\Theta(1)$ | $\Theta(\log{n})$ | $\Theta(1)$ | ### Graph - Graph with $\lvert{V}\rvert$ vertices and $\lvert{E}\rvert$ edges | Vertex / Edge Management | 结构名 | Storage | Add Vertex | Add Edge | Remove Vertex | Remove Edge | Search | | --- | --- | --- | --- | --- | --- | --- | --- | | [Adjacency List](http://reference.wolfram.com/language/ref/AdjacencyList.html) | 邻接表 | $O(\lvert{V}\rvert+\lvert{E}\rvert)$ | $\Theta(1)$ | $\Theta(1)$ | $\Theta(\lvert{V}\rvert+\lvert{E}\rvert)$ | $\Theta(\lvert{E}\rvert)$ | $\Theta(\lvert{V}\rvert)$ | | [Incidence List](http://reference.wolfram.com/language/ref/IncidenceList.html) | 关联表 | $O(\lvert{V}\rvert+\lvert{E}\rvert)$ | $\Theta(1)$ | $\Theta(1)$ | $\Theta(\lvert{E}\rvert)$ | $\Theta(\lvert{E}\rvert)$ | $\Theta(\lvert{E}\rvert)$ | | [Adjacency Matrix](http://reference.wolfram.com/language/ref/AdjacencyMatrix.html) | 邻接矩阵 | $O(\lvert{V}\rvert^2)$ | $\Theta(\lvert{V}\rvert^2)$ | $\Theta(1)$ | $\Theta(\lvert{V}\rvert^2)$ | $\Theta(1)$ | $\Theta(1)$ | | [Incidence Matrix](http://reference.wolfram.com/language/ref/IncidenceMatrix.html) | 关联矩阵 | $O(\lvert{V}\rvert\cdot\lvert{E}\rvert)$ | $\Theta(\lvert{V}\rvert\cdot\lvert{E}\rvert)$ | $\Theta(\lvert{V}\rvert\cdot\lvert{E}\rvert)$ | $\Theta(\lvert{V}\rvert\cdot\lvert{E}\rvert)$ | $\Theta(\lvert{V}\rvert\cdot\lvert{E}\rvert)$ | $\Theta(\lvert{E}\rvert)$ | ## Algorithms ### Sorting Algorithms | Name | 算法名 | Space | Time | Note | | --- | --- | --- | --- | --- | | [Quick Sort](https://en.wikipedia.org/wiki/Quicksort) | 快速排序 | $O(\log{n})$ | $\Omega(n\log{n})$ / $\Theta(n\log{n})$ / $O(n^2)$ | Basic
Unstable | | [Merge Sort](https://en.wikipedia.org/wiki/Merge_sort) | 归并排序 | $O(n)$ | $\Omega(n\log{n})$ / $\Theta(n\log{n})$ / $O(n\log{n})$ | Basic
Stable | | [Heap Sort](https://en.wikipedia.org/wiki/Heapsort) | 堆排序 | $O(1)$ | $\Omega(n\log{n})$ / $\Theta(n\log{n})$ / $O(n\log{n})$ | Basic
Unstable | | [Bubble Sort](https://en.wikipedia.org/wiki/Bubble_sort) | 冒泡排序 | $O(1)$ | $\Omega(n)$ / $\Theta(n^2)$ / $O(n^2)$ | Basic
Stable | | [Radix Sort](https://en.wikipedia.org/wiki/Radix_sort) | 基数排序 | $O(n+k)$ | $\Omega(nk)$ / $\Theta(nk)$ / $O(nk)$ | Basic
Stable | | [Selection Sort](https://en.wikipedia.org/wiki/Selection_sort) | 选择排序 | $O(1)$ | $\Omega(n^2)$ / $\Theta(n^2)$ / $O(n^2)$ | Basic
Unstable | | [Shell Sort](https://en.wikipedia.org/wiki/Shellsort) | 希尔排序 | $O(1)$ | $\Omega(n\log{n})$ / $\Theta(n(\log{n})^2)$ / $O(n(\log{n})^2)$ | Basic
Unstable | | [Insertion Sort](https://en.wikipedia.org/wiki/Insertion_sort) | 插入排序 | $O(1)$ | $\Omega(n)$ / $\Theta(n^2)$ / $O(n^2)$ | Basic
Stable | | [Tim Sort](https://en.wikipedia.org/wiki/Timsort) | 提姆排序 | $O(n)$ | $\Omega(n)$ / $\Theta(n\log{n})$ / $O(n\log{n})$ | Stable | | [Bucket Sort](https://en.wikipedia.org/wiki/Bucket_sort) | 桶排序 | $O(n)$ | $\Omega(n+k)$ / $\Theta(n+k)$ / $O(n^2)$ | Stable | | [Tree Sort](https://en.wikipedia.org/wiki/Tree_sort) | 树排序 | $O(n)$ | $\Omega(n\log{n})$ / $\Theta(n\log{n})$ / $O(n^2)$ | | | [Counting Sort](https://en.wikipedia.org/wiki/Counting_sort) | 计数排序 | $O(k)$ | $\Omega(n+k)$ / $\Theta(n+k)$ / $O(n+k)$ | Stable | | [Cube Sort](https://en.wikipedia.org/wiki/Cubesort) | 立方排序 | $O(n)$ | $\Omega(n)$ / $\Theta(n\log{n})$ / $O(n\log{n})$ | Stable | ### Searching Algorithms - Graph with $\lvert{V}\rvert$ vertices and $\lvert{E}\rvert$ edges | Name | 算法名 | Space | Time | Note | | --- | --- | --- | --- | --- | | [Depth First Search](https://en.wikipedia.org/wiki/Depth-first_search) | 深度优先搜索 | $O(\lvert{V}\rvert)$ | - / $O(\lvert{V}\rvert+\lvert{E}\rvert)$ | | | [Breadth First Search](https://en.wikipedia.org/wiki/Breadth-first_search) | 广度优先搜索 | $O(\lvert{V}\rvert)$ | - / $O(\lvert{V}\rvert+\lvert{E}\rvert)$ | | | [Binary Search](https://en.wikipedia.org/wiki/Binary_search_algorithm) | 二分搜索 | $O(1)$ | $\Theta(\log{n})$ / $O(\log{n})$ | Sorted array of n elements | | [Brute Force](https://en.wikipedia.org/wiki/Brute-force_search) | 穷举搜索 | $O(1)$ | $\Theta(n)$ / $O(n)$ | Array | | [Shortest path by Bellman-Ford](https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm) | | $O(\lvert{V}\rvert)$ | $\Theta(\lvert{V}\rvert\cdot\lvert{E}\rvert)$ / $O(\lvert{V}\rvert\cdot\lvert{E}\rvert)$ | | | [Shortest path by Dijkstra (Min-heap)](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm) | | $O(\lvert{V}\rvert)$ | $\Theta((\lvert{V}\rvert+\lvert{E}\rvert)\cdot\log{\lvert{V}\rvert})$ / $O((\lvert{V}\rvert+\lvert{E}\rvert)\cdot\log{\lvert{V}\rvert})$ | | | Shortest path by Dijkstra (Unordered Array) | | $O(\lvert{V}\rvert)$ | $\Theta(\lvert{V}\rvert^2)$ / $O(\lvert{V}\rvert^2)$ | | ================================================ FILE: topic/_test/python/__init__.py ================================================ from _test.python.test_base import TestBase ================================================ FILE: topic/_test/python/test_base.py ================================================ import unittest import datetime class TaskTimer: def __init__(self): self.reset_time() def reset_time(self): self.timestamp = datetime.datetime.now() def print_duration(self): duration = datetime.datetime.now() - self.timestamp print(duration.microseconds // 10 / 100.0, 'ms') class TestBase(unittest.TestCase): MSG_TEMPLATE = '{status}: {msg}' @classmethod def setUpClass(cls): print(cls.MSG_TEMPLATE.format( status=cls.__name__, msg='Starting tests...' )) @classmethod def tearDownClass(cls): print(cls.MSG_TEMPLATE.format( status=cls.__name__, msg='Finished tests.\n' )) def setUp(self): self.task_timer = TaskTimer() def tearDown(self): self.task_timer.print_duration() ================================================ FILE: topic/balanced_search_tree/python/__init__.py ================================================ ================================================ FILE: topic/balanced_search_tree/python/_helper.py ================================================ ================================================ FILE: topic/balanced_search_tree/python/red_black_tree.py ================================================ # TODO ================================================ FILE: topic/bit_manipulation/README.md ================================================ Bit Manipulation ====== ## Modulo ```python >>> a & 1 == a % 2 True ``` ## Division ```python >>> a >> 1 == a // 2 True ``` ## 0 -> 1, 1 -> 0 output 1 if input 0 ```python >>> True ^ 1 0 >>> 1 ^ 1 0 >>> False ^ 1 1 >>> 0 ^ 1 1 ``` ## To check a number is power of 2 if a number is power of 2, then there is only 1 in the bit format in the number. ```python >>> [(num & (num - 1)) == 0 for num in (4, 5, 11, 16, 21, 29, 32)] [True, False, False, True, False, False, True] ``` ================================================ FILE: topic/bit_manipulation/python/__init__.py ================================================ from bit_manipulation.python.calculator_in_bit import Calculator ================================================ FILE: topic/bit_manipulation/python/calculator_in_bit.py ================================================ """ 1. Addition - go through example: 1 + 5, that is 0001 + 0101 step1/ no carry(xor): 0001 ^ 0101 == 0100 step2/ considering carry(and + shift): (0001 & 0101) << 1 == 0010 the carry only occurs when the same digit is 1 step3/ repeat (1) and (2) til the result in (2) is 0 1 + 5 -> 0001 + 0101 r1: 0001 ^ 0101 == 0100, (0001 & 0101) << 1 == 0010 r2: 0100 ^ 0010 == 0110, (0100 & 0010) << 1 == 0000 got 0110 == 6 - deal with negative values if a < 0 and b == 0, => a wont be `&` (cannot enter iteration) => a >> 31 == -1 => a ^ ~INT_RANGE got wrong number so the simplest way is `a >> 31 == 0` -> `a >> 31 <= 0` 2. Subtraction the simplest way is `a + (-b)` to get the complement of `b`, that is `-b` -b = ~b + 1 3. Multiplication in oct | in bin a 12 | a 1100 x b 10 | x b 1010 -------- | ---------- 00 | 0000 12 | 1100 -------- | 0000 120 | 1100 | ---------- | 1111000 r1/ 1100 * 0 = 0000 r2/ 11000 * 1 = 11000 r3/ 110000 * 0 = 000000 r4/ 1100000 * 1 = 1100000 4. Division if a // (2 ** i) >= b: => quotient += 2 ** i => a -= b * (2 ** i) for next iteration """ class Calculator: INT_RANGE = 0xFFFFFFFF @classmethod def _plus(cls, a, b): """ recursion """ if b == 0: return a if a >> 31 <= 0 else a ^ ~cls.INT_RANGE return cls._plus((a ^ b) & cls.INT_RANGE, (a & b) << 1) @classmethod def plus(cls, a, b): """ iteration """ while b != 0: a, b = a ^ b, (a & b) << 1 a &= cls.INT_RANGE return a if a >> 31 <= 0 else a ^ ~cls.INT_RANGE @classmethod def minus(cls, a, b): return cls.plus(a, cls.plus(~b, 1)) @classmethod def times(cls, a, b): if not a or not b: return 0 x = a if a > 0 else cls.plus(~a, 1) y = b if b > 0 else cls.plus(~b, 1) product = 0 while y: if y & 1: product = cls.plus(product, x) x = x << 1 y = y >> 1 if a ^ b >= 0: return product return cls.plus(~product, 1) @classmethod def divide(cls, a, b): if not b: return float('inf') if a >= 0 else float('-inf') if not a: return 0 x = a if a > 0 else cls.plus(~a, 1) y = b if b > 0 else cls.plus(~b, 1) quotient = 0 for i in range(31, -1, -1): if x >> i >= y: quotient = cls.plus(quotient, 1 << i) x = cls.minus(x, y << i) if a ^ b >= 0: return quotient if x == 0: return cls.plus(~quotient, 1) return ~quotient ================================================ FILE: topic/bit_manipulation/python/calculator_in_bit__test.py ================================================ from _test.python import * from bit_manipulation.python import * class TestBitCalculator(TestBase): CASES = ( (-1, -1), (-1, 0), (-1, 1), ( 0, -1), ( 0, 0), ( 0, 1), ( 1, -1), ( 1, 0), ( 1, 1), # (-0x80000000, -1), # (-0x80000000, 1), # ( 0x7FFFFFFF, -1), # ( 0x7FFFFFFF, 1), # (-1, -0x80000000), # (-1, 0x7FFFFFFF), # ( 1, -0x80000000), # ( 1, 0x7FFFFFFF), (-9, -7), (-9, 7), ( 9, -7), ( 9, 7), (-7, -9), (-7, 9), ( 7, -9), ( 7, 9), (-123, 0), ( 123, 0), ( 0, -123), ( 0, 123), ) def test_plus_recursion(self): for a, b in self.CASES: self.assertEqual(a + b, Calculator._plus(a, b)) def test_plus_iteration(self): for a, b in self.CASES: self.assertEqual(a + b, Calculator.plus(a, b)) def test_minus(self): for a, b in self.CASES: self.assertEqual(a - b, Calculator.minus(a, b)) def test_times(self): for a, b in self.CASES: self.assertEqual(a * b, Calculator.times(a, b)) def test_divide(self): for a, b in self.CASES: if not b: c = float('inf') if a >= 0 else float('-inf') elif not a: c = 0 else: c = a // b self.assertEqual(c, Calculator.divide(a, b)) ================================================ FILE: topic/complexity_analysis.md ================================================ Complexity Analysis ====== ## Case #1 - time: `O(N)` - REF: https://en.wikipedia.org/wiki/1/2_%2B_1/4_%2B_1/8_%2B_1/16_%2B_%E2%8B%AF `N/2 + N/4 + N/8 + ... = N` ```java int count = 0; for (int i = N; i > 0; i /= 2) { for (int j = 0; j < i; j++) { count += 1; } } ``` ## Case #2 If X will always be a better choice for **large inputs**, then we say that **an algorithm X is asymptotically more efficient than Y**. ## Case #3 - time: `O(N)` - the `j` go through that array only once. ```java int j = 0; for(int i = 0; i < n; ++i) { while(j < n && arr[i] < arr[j]) { j++; } } ``` ## Case #4 - time: `O(2 ^ N)` - REF: https://math.stackexchange.com/questions/177405/prove-by-induction-2n-cn-0-cn-1-cdots-cn-n `C(n, 0) + C(n, 1) + ... + C(n, n) = 2 ^ n` ================================================ FILE: topic/dynamic_programming/README.md ================================================ Dynamic Programming ====== ## Steps 1. what is the meaning of `dp[i]` 2. how to do initialization 3. how you iterate that `dp` 4. what is the returning ## Space Optimization - rolling array, e.g., [lintcode/115_unique_paths_ii.py](../../lintcode/115_unique_paths_ii.py) - cache previous cell, e.g., [other/unique_paths_with_followups.py](../../other/unique_paths_with_followups.py) ## Print Paths - 1D example: [leetcode/300_longest_increasing_subsequence.py](../../leetcode/300_longest_increasing_subsequence.py) - 2D example: [lintcode/168_burst_balloons.py](../../lintcode/168_burst_balloons.py) ================================================ FILE: topic/graph/README.md ================================================ Graph ====== ## Graph Searching - Cyclic Graph: maintain a `seen` to record visited nodes. - Undirected Acyclic Graph: pick any point to start searching. - Directed Acyclic Graph: count `indegree` and do `Topological Sorting`. ================================================ FILE: topic/graph/python/__init__.py ================================================ from graph.python.union_find import UnionFind ================================================ FILE: topic/graph/python/union_find.py ================================================ class UnionFind: def __init__(self): self.nodes = {} def union(self, u, v): a = self.find(u) b = self.find(v) if a is not b: self.nodes[b] = a return a def find(self, u): if u not in self.nodes: self.nodes[u] = u return u if self.nodes[u] is u: return u self.nodes[u] = self.find(self.nodes[u]) return self.nodes[u] ================================================ FILE: topic/graph/python/union_find__test.py ================================================ from _test.python import * from graph.python import UnionFind class TestUnionFind(TestBase): def test_init(self): uf = UnionFind() self.assertEqual(uf.nodes, {}) def test_union(self): uf = UnionFind() for i in range(1, 6): query = uf.union(0, i) self.assertEqual(query, 0) for i in range(1, 6): self.assertEqual(uf.nodes[i], 0) def test_compression(self): uf = UnionFind() uf.union(2, 3) uf.union(1, 2) self.assertEqual(uf.nodes[3], 2) self.assertEqual(uf.nodes[2], 1) query = uf.find(3) self.assertEqual(query, 1) self.assertEqual(uf.nodes[3], 1) ================================================ FILE: topic/hash/python/__init__.py ================================================ from hash.python.hashtable import HashTable from hash.python.geohash import GeoHash ================================================ FILE: topic/hash/python/geohash.py ================================================ """ REF: - lintcode/529_geohash.py - lintcode/530_geohash_ii.py Main Concept: for encoding 1. use binary search to convert `lng` and `lat` to `bincode` 1: up or right 0: down or left 2. merge `lngcode` and `latcode` alternately into one `bincode` `lngcode[0] + latcode[0] + lngcode[1] + ...` 3. pick every 5 digit from `bincode` and convert that to base32 chars """ class GeoHash: def __init__(self): ignored_chars = {ord(i) for i in 'ailo'} base32 = [str(i) for i in range(10)] base32.extend( chr(i) for i in range(ord('a'), ord('z') + 1) if i not in ignored_chars ) base32i = {base32[i]: i for i in range(len(base32))} self.base32 = base32 self.base32i = base32i def encode(self, latitude, longitude, precision=5): digits = precision * 5 // 2 + 1 lngcode = self._pos_to_bin(longitude, digits, -180, 180) latcode = self._pos_to_bin( latitude, digits, -90, 90) bincode = [] for i in range(digits): bincode.append(lngcode[i]) bincode.append(latcode[i]) geohash = [ self.base32[int(''.join(bincode[i:i + 5]), 2)] for i in range(0, len(bincode), 5) ] return ''.join(geohash[:precision]) def decode(self, geohash): bincode = [] for i in geohash: if i not in self.base32i: return [] code = bin(self.base32i[i])[2:].rjust(5, '0') bincode.extend(list(code)) n = len(bincode) latcode = [bincode[i] for i in range(1, n, 2)] lngcode = [bincode[i] for i in range(0, n, 2)] return [ self._bin_to_pos(latcode, -90, 90), self._bin_to_pos(lngcode, -180, 180), ] def _bin_to_pos(self, bincode, start, end): for i in bincode: mid = (start + end) / 2.0 if i == '1': start = mid else: end = mid return (start + end) / 2.0 def _pos_to_bin(self, position, digits, start, end): bincode = [] for _ in range(digits): mid = (start + end) / 2.0 if mid < position: bincode.append('1') start = mid else: bincode.append('0') end = mid return bincode ================================================ FILE: topic/hash/python/geohash__test.py ================================================ from _test.python import * from hash.python import GeoHash class TestGeoHash(TestBase): CASES = ( ((39.92816697, 116.38954991), 'wx4g0s8q3jf9'), ((51.5171437, -0.1337183), 'gcpvhfqth5sk'), ((51.5160862, -0.1294528), 'gcpvj41wzw8b'), ((37.4846102, -122.1516928), '9q9jhrggemb1'), ((37.4219999, -122.0862462), '9q9hvu7wbq2s'), ((-90, -180), '000000000000'), ((-90, 0), '5bpbpbpbpbpb'), ((-90, 180), 'pbpbpbpbpbpb'), ((0, -180), '2pbpbpbpbpbp'), ((0, 0), '7zzzzzzzzzzz'), ((0, 180), 'rzzzzzzzzzzz'), ((90, -180), 'bpbpbpbpbpbp'), ((90, 0), 'gzzzzzzzzzzz'), ((90, 180), 'zzzzzzzzzzzz'), ) def test_encode(self): gh = GeoHash() for i, o in self.CASES: self.assertEqual(gh.encode(*i, len(o)), o) def test_decode(self): EPS = 1e-6 gh = GeoHash() for (lat1, lng1), i in self.CASES: lat2, lng2 = gh.decode(i) self.assertTrue(abs(lat1 - lat2) < EPS) self.assertTrue(abs(lng1 - lng2) < EPS) ================================================ FILE: topic/hash/python/hashtable.py ================================================ class HashTable: def __init__(self, cap=4000, power=31): self.cap = cap self.power = power self.size = 0 self.table = [None] * self.cap def __repr__(self): return '{{{}}}'.format( ', '.join( repr(key) + ': ' + repr(val) for key, val in self.items() ) ) def __len__(self): return self.size def __setitem__(self, key, val): self.set(key, val) def __getitem__(self, key): return self.get(key) def __delitem__(self, key): self.remove(key) def __iter__(self): for key, _ in self.items(): yield key def keys(self): for key, _ in self.items(): yield key def values(self): for _, val in self.items(): yield val def items(self): for head in self.table: node = head while node: yield node.key, node.val node = node.nxt def set(self, key, val): code = self._encode(key) if not self.table[code]: self.size += 1 self.table[code] = ListNode(key, val) return node = self.table[code] while node and node.nxt and node.key != key: node = node.nxt if node.key == key: node.val = val else: self.size += 1 node.nxt = ListNode(key, val) def get(self, key): code = self._encode(key) if not self.table[code]: raise KeyError(key) node = self.table[code] while node and node.key != key: node = node.nxt if node and node.key == key: return node.val raise KeyError(key) def remove(self, key): code = self._encode(key) if not self.table[code]: raise KeyError(key) if self.table[code].key == key: self.size -= 1 self.table[code] = self.table[code].nxt return node = self.table[code] while node and node.nxt and node.nxt.key != key: node = node.nxt if node and node.nxt and node.nxt.key == key: self.size -= 1 node.nxt = node.nxt.nxt return raise KeyError(key) def _encode(self, key): if isinstance(key, int): return key % self.cap code = 0 for c in key: code = (code * self.power + ord(c)) % self.cap return code class ListNode: def __init__(self, key, val, nxt=None): self.key = key self.val = val self.nxt = nxt ================================================ FILE: topic/hash/python/hashtable__test.py ================================================ from _test.python import * from hash.python import HashTable class TestHashTable(TestBase): def test_operation(self): d = HashTable() self.assertEqual(len(d), 0) d[1] = 1 self.assertEqual(d[1], 1) self.assertEqual(len(d), 1) d[2] = 2 self.assertEqual(d[2], 2) self.assertEqual(len(d), 2) d['ab'] = 3 self.assertEqual(d['ab'], 3) self.assertEqual(len(d), 3) d['ba'] = 4 self.assertEqual(d['ba'], 4) self.assertEqual(len(d), 4) self.assertEqual(d['ab'], 3) self.assertEqual(len(d), 4) del d['ab'] self.assertEqual(len(d), 3) with self.assertRaises(KeyError): d['ab'] def test_collision(self): d = HashTable(4000, 31) self.assertEqual(len(d), 0) self.assertEqual(d._encode('ab'), d._encode('bC')) self.assertEqual(d._encode('ab'), d._encode('c$')) d['ab'] = 1 self.assertEqual(d['ab'], 1) self.assertEqual(len(d), 1) d['bC'] = 2 self.assertEqual(d['bC'], 2) self.assertEqual(len(d), 2) d['c$'] = 3 self.assertEqual(d['c$'], 3) self.assertEqual(len(d), 3) del d['bC'] self.assertEqual(len(d), 2) with self.assertRaises(KeyError): d['bC'] del d['c$'] self.assertEqual(len(d), 1) with self.assertRaises(KeyError): d['c$'] d['bC'] del d['ab'] self.assertEqual(len(d), 0) with self.assertRaises(KeyError): d['ab'] d['c$'] d['bC'] ================================================ FILE: topic/heap/python/__init__.py ================================================ from heap.python.binary_hash_heap import BinaryHashHeap from heap.python.binary_heap import BinaryHeap from heap.python.lazy_removable_heapq import LazyRemovableHeapq from heap.python.removable_heapq import RemovableHeapq ================================================ FILE: topic/heap/python/binary_hash_heap.py ================================================ import collections class BinaryHashHeap: MSG_EMPTY_HEAP = 'access element from empty heap' def __init__(self, iterable=None): self.__heap = [0] self.__size = 0 self.__idxs = collections.defaultdict(set) if iterable: self.heapify(iterable) def __len__(self): return self.__size def __bool__(self): return self.__size > 0 def heapify(self, iterable): if not isinstance(iterable, collections.Iterable): return for val in iterable: self.push(val) def push(self, val): self.__heap.append(val) self.__size += 1 self.__idxs[val].add(self.__size) self._siftdown(self.__size) def pop(self): if self.__size < 1: raise IndexError(self.MSG_EMPTY_HEAP) heap = self.__heap idxs = self.__idxs val = heap[1] idxs[val].discard(1) idxs[heap[-1]].discard(self.__size) idxs[heap[-1]].add(1) heap[1] = heap[-1] heap.pop() self.__size -= 1 self._siftup(1) return val def remove(self, val): if not self.__idxs.get(val): raise KeyError(val) heap = self.__heap idxs = self.__idxs i = idxs[val].pop() idxs[heap[-1]].discard(self.__size) idxs[heap[-1]].add(i) heap[i] = heap[-1] heap.pop() self.__size -= 1 self._siftup(i) def top(self): if self.__size < 1: raise IndexError(self.MSG_EMPTY_HEAP) return self.__heap[1] def _siftdown(self, i): heap = self.__heap while i // 2 > 0: j = i // 2 if heap[i] < heap[j]: self._swap(i, j) i = j def _siftup(self, i): heap = self.__heap size = self.__size while i * 2 <= size: j = i * 2 if j + 1 <= size and heap[j + 1] < heap[j]: j += 1 if heap[i] > heap[j]: self._swap(i, j) i = j def _swap(self, i, j): heap = self.__heap idxs = self.__idxs idxs[heap[i]].discard(i) idxs[heap[j]].discard(j) idxs[heap[i]].add(j) idxs[heap[j]].add(i) heap[i], heap[j] = heap[j], heap[i] ================================================ FILE: topic/heap/python/binary_heap.py ================================================ import collections class BinaryHeap: MSG_EMPTY_HEAP = 'access element from empty heap' def __init__(self, iterable=None): self.__heap = [0] self.__size = 0 if iterable: self.heapify(iterable) def __len__(self): return self.__size def __bool__(self): return self.__size > 0 def heapify(self, iterable): if not isinstance(iterable, collections.Iterable): return for val in iterable: self.push(val) def push(self, val): self.__heap.append(val) self.__size += 1 self._siftdown(self.__size) def pop(self): if self.__size < 1: raise IndexError(self.MSG_EMPTY_HEAP) heap = self.__heap val = heap[1] heap[1] = heap[-1] heap.pop() self.__size -= 1 self._siftup(1) return val def top(self): if self.__size < 1: raise IndexError(self.MSG_EMPTY_HEAP) return self.__heap[1] def _siftdown(self, i): heap = self.__heap while i // 2 > 0: j = i // 2 if heap[i] < heap[j]: heap[i], heap[j] = heap[j], heap[i] i = j def _siftup(self, i): heap = self.__heap size = self.__size while i * 2 <= size: j = i * 2 if j + 1 <= size and heap[j + 1] < heap[j]: j += 1 if heap[i] > heap[j]: heap[i], heap[j] = heap[j], heap[i] i = j ================================================ FILE: topic/heap/python/heap__test.py ================================================ from _test.python import * from heap.python import * class TestHeap(TestBase): def _test_heapify(self, Heap): h = Heap([i for i in range(5)] * 2) self.assertEqual(len(h), 10) self.assertEqual(h.top(), 0) self.assertEqual( [h.pop() for _ in range(10)], [0, 0, 1, 1, 2, 2, 3, 3, 4, 4] ) self.assertEqual(len(h), 0) with self.assertRaises(IndexError): h.pop() h.pop() h.pop() def _test_push(self, Heap): CASES = (1, 2, 3, 1, 3, 2, 0, 1, 4) h = Heap() self.assertEqual(len(h), 0) for i in range(len(CASES)): self.assertIsNone(h.push(CASES[i])) self.assertEqual(len(h), i + 1) self.assertEqual( [h.pop() for _ in range(len(CASES))], [0, 1, 1, 1, 2, 2, 3, 3, 4] ) self.assertEqual(len(h), 0) with self.assertRaises(IndexError): h.pop() h.pop() h.pop() def _test_pop(self, Heap): CASES = list(range(10)) h = Heap(CASES) self.assertEqual(len(h), 10) for i in range(len(CASES)): self.assertEqual(h.pop(), i) self.assertEqual(len(h), len(CASES) - i - 1) self.assertEqual(len(h), 0) with self.assertRaises(IndexError): h.pop() h.pop() h.pop() def _test_remove(self, Heap): CASES = (1, 2, 3, 1, 3, 2, 0, 1, 4) h = Heap(CASES) self.assertEqual(len(h), 9) self.assertIsNone(h.remove(1)) self.assertEqual(len(h), 8) self.assertIsNone(h.remove(1)) self.assertEqual(len(h), 7) self.assertIsNone(h.remove(1)) self.assertEqual(len(h), 6) with self.assertRaises(KeyError): h.remove(1) h.remove(5) h.remove(1) self.assertEqual(h.pop(), 0) self.assertEqual(len(h), 5) self.assertEqual(h.top(), 2) self.assertEqual(len(h), 5) self.assertIsNone(h.push(1)) self.assertEqual(len(h), 6) self.assertEqual(h.top(), 1) self.assertEqual(len(h), 6) self.assertIsNone(h.remove(1)) self.assertEqual(h.top(), 2) self.assertEqual(len(h), 5) self.assertEqual(h.pop(), 2) self.assertEqual(len(h), 4) self.assertIsNone(h.remove(3)) self.assertEqual(len(h), 3) self.assertIsNone(h.remove(2)) self.assertEqual(len(h), 2) self.assertIsNone(h.remove(3)) self.assertEqual(len(h), 1) self.assertEqual(h.pop(), 4) self.assertEqual(len(h), 0) with self.assertRaises(KeyError): h.remove(-1) h.remove(0) h.remove(1) h.remove(2) h.remove(3) h.remove(4) h.remove(5) def _test_top(self, Heap): CASES = (1, 2, 3, 1, 3, 2, 0, 1, 4) h = Heap(CASES) self.assertEqual(len(h), 9) for _ in range(len(CASES)): self.assertEqual(h.top(), h.pop()) self.assertEqual(len(h), 0) with self.assertRaises(IndexError): h.top() h.top() h.top() def _run_heap_test(self, Heap): self._test_heapify(Heap) self._test_push(Heap) self._test_pop(Heap) self._test_top(Heap) def _run_removable_heap_test(self, Heap): self._test_heapify(Heap) self._test_push(Heap) self._test_pop(Heap) self._test_remove(Heap) self._test_top(Heap) def test_binary_hash_heap(self): self._run_removable_heap_test(BinaryHashHeap) def test_binary_heap(self): self._run_heap_test(BinaryHeap) def test_lazy_removable_heapq(self): self._run_removable_heap_test(LazyRemovableHeapq) def test_removable_heapq(self): self._run_removable_heap_test(RemovableHeapq) ================================================ FILE: topic/heap/python/lazy_removable_heapq.py ================================================ import collections import heapq class LazyRemovableHeapq: MSG_EMPTY_HEAP = 'access element from empty heap' def __init__(self, iterable=None): self.__heap = [] self.__size = 0 self.__cnts = collections.defaultdict(int) if iterable: self.heapify(iterable) def __len__(self): return self.__size def __bool__(self): return self.__size > 0 def heapify(self, iterable): if not isinstance(iterable, collections.Iterable): return for val in iterable: self.push(val) def push(self, val): heapq.heappush(self.__heap, val) self.__size += 1 self.__cnts[val] += 1 def pop(self): if self._is_empty(): raise IndexError(self.MSG_EMPTY_HEAP) val = heapq.heappop(self.__heap) self.__size -= 1 self.__cnts[val] -= 1 return val def remove(self, val): if self._is_empty() or self.__cnts.get(val, 0) < 1: raise KeyError(val) self.__size -= 1 self.__cnts[val] -= 1 def top(self): if self._is_empty(): raise IndexError(self.MSG_EMPTY_HEAP) return self.__heap[0] def _is_empty(self): while self.__heap and self.__cnts.get(self.__heap[0]) == 0: heapq.heappop(self.__heap) return self.__size == 0 ================================================ FILE: topic/heap/python/removable_heapq.py ================================================ """ in `remove`, we cannot record index in hashmap to speed up since the index in `heapq` changed all the time """ import collections import heapq class RemovableHeapq: MSG_EMPTY_HEAP = 'access element from empty heap' def __init__(self, iterable=None): self.__heap = [] if iterable: self.heapify(iterable) def __len__(self): return len(self.__heap) def __bool__(self): return bool(self.__heap) def heapify(self, iterable): if not isinstance(iterable, collections.Iterable): return for val in iterable: self.push(val) def push(self, val): heapq.heappush(self.__heap, val) def pop(self): if not self.__heap: raise IndexError(self.MSG_EMPTY_HEAP) return heapq.heappop(self.__heap) def remove(self, val): if not self.__heap: raise KeyError(val) i = 0 n = len(self.__heap) while i < n and self.__heap[i] != val: i += 1 if i >= n: raise KeyError(val) if i == n - 1: self.__heap.pop() else: self.__heap[i] = self.__heap[-1] self.__heap.pop() heapq._siftup(self.__heap, i) def top(self): if not self.__heap: raise IndexError(self.MSG_EMPTY_HEAP) return self.__heap[0] ================================================ FILE: topic/language/javascript/README.md ================================================ JavaScript Syntax Note ====== ## `Object` ### To check a key is in object ```js // `obj.hasOwnProperty` // check only the obj instance > obj.hasOwnProperty('key') // `in` operator // check the whole prototype chain > 'key' in obj > const someKey = 'key' > someKey in obj ``` ## `Array` ### To init array with fixed length Note that, the inited value passed into `fill` must be **IMMUTABLE**, otherwise we can use `map` to init with mutable value. ```js > new Array(3).fill(0) [ 0, 0, 0 ] > new Array(3).fill(0).map(_ => new Array(3).fill(0)) [ [ 0, 0, 0 ], [ 0, 0, 0 ], [ 0, 0, 0 ] ] ``` Explanation: ```js > let nums // bad > nums = new Array(3).fill(new Array(3).fill(0)) [ [ 0, 0, 0 ], [ 0, 0, 0 ], [ 0, 0, 0 ] ] > nums[0][0] = 1 1 > nums [ [ 1, 0, 0 ], [ 1, 0, 0 ], [ 1, 0, 0 ] ] // good > nums = new Array(3).fill(0).map(_ => new Array(3).fill(0)) [ [ 0, 0, 0 ], [ 0, 0, 0 ], [ 0, 0, 0 ] ] > nums[0][0] = 1 1 > nums [ [ 1, 0, 0 ], [ 0, 0, 0 ], [ 0, 0, 0 ] ] ``` ### To sort The default sorting is by **alphabetical order**. You can also indicate a comparator if you want to sort it by custom order. ```js > const nums = [1, 2, 4, 6, 4, 2, 2, 5, 1, 11, 22, 2, 3, 44] undefined > nums.sort() [ 1, 1, 11, 2, 2, 2, 2, 22, 3, 4, 4, 44, 5, 6 ] > nums.sort((a, b) => a - b) [ 1, 1, 2, 2, 2, 2, 3, 4, 4, 5, 6, 11, 22, 44 ] > nums.sort((a, b) => b - a) [ 44, 22, 11, 6, 5, 4, 4, 3, 2, 2, 2, 2, 1, 1 ] ``` ### To override a existing array ```js > let nums undefined > nums = [2, 1, 3, 4, 1, 2] [ 2, 1, 3, 4, 1, 2 ] > nums.splice(0, nums.length, ...[5, 6, 7, 1, 2, 3]) [ 2, 1, 3, 4, 1, 2 ] > nums [ 5, 6, 7, 1, 2, 3 ] > nums.splice(0, nums.length) [ 5, 6, 7, 1, 2, 3 ] > nums [] ``` ## `String` ### To convert a string-number to a real number ```js > +'123' 123 > +'-123' -123 > -'123' -123 > -'+123' -123 > +'0' 0 > +'1' 1 > +'0.123' 0.123 > +'0.123a' NaN > +'0.123/2' NaN ``` ## `Boolean` ### To convert a boolean to `1` or `0` ```js > +true 1 > +false 0 ``` ## `Set` ## `Map` ================================================ FILE: topic/language/javascript/traversal.md ================================================ Traversal ====== ## To simulate a queue It's also a good example to show how the pointer works for the loop. ```js > const queue = [1] undefined > for (let i = 0; i < queue.length; i++) { ... if (i === 5) break ... queue.push(i) ... } undefined > queue [ 1, 0, 1, 2, 3, 4 ] ``` ```js > const queue = [1] undefined > let i = 0 undefined > queue.forEach(num => { ... if (i === 5) return ... queue.push(i) ... }) undefined > queue [ 1, 0 ] ``` ## To do BFS in level ```js > const queue = [[0, 0]] undefined > const _queue = [] undefined > const delta = [ ... [0, -1], [0, 1], ... [-1, 0], [1, 0], ... ] undefined > > const visited = {'0,0': 0} undefined > let steps = 0 undefined > let i, x, y, _x, _y, key undefined > > while (queue.length && steps < 2) { ... steps += 1 ... ... for (i = 0; i < queue.length; i++) { ... [x, y] = queue[i] ... ... delta.forEach(([dx, dy]) => { ... _x = x + dx ... _y = y + dy ... key = `${_x},${_y}` ... ... if (visited[key] < steps) return ... ... visited[key] = steps ... _queue.push([_x, _y]) ... }) ... } ... ... queue.splice(0, queue.length, ..._queue) ... _queue.splice(0, _queue.length) ... } [ [ 0, -2 ], [ -1, -1 ], [ 1, -1 ], [ 0, 2 ], [ -1, 1 ], [ 1, 1 ], [ -1, -1 ], [ -1, 1 ], [ -2, 0 ], [ 1, -1 ], [ 1, 1 ], [ 2, 0 ] ] > > queue [ [ 0, -2 ], [ -1, -1 ], [ 1, -1 ], [ 0, 2 ], [ -1, 1 ], [ 1, 1 ], [ -1, -1 ], [ -1, 1 ], [ -2, 0 ], [ 1, -1 ], [ 1, 1 ], [ 2, 0 ] ] > visited { '0,0': 0, '0,-1': 1, '0,1': 1, '-1,0': 1, '1,0': 1, '0,-2': 2, '-1,-1': 2, '1,-1': 2, '0,2': 2, '-1,1': 2, '1,1': 2, '-2,0': 2, '2,0': 2 } ``` ================================================ FILE: topic/language/php/README.md ================================================ PHP Syntax Note ====== ## General - A PHP script starts with ``. You can also use the shorthand PHP tags, ``, as long as they're supported by the server. - PHP statements end with semicolons `;`. - Comment: single-line `//`, multi-line begins with `/*` and ends with `*/`. ## Variable and Constant ```php $variable_name = 'value'; echo $variable_name; // 'value' $var1 = 'var2'; $var2 = 'value'; echo $$var1; // 'value' ``` - A variable name can only contain alpha-numeric characters and underscores. (`A-z`, `0-9`, and `_`) - A variable name must start with a letter or an underscore. - Variable names are case-sensitive. (`$name` and `$NAME` would be two different variables) ```php define(CONSTANT_NAME, `value`, `case-insensitive`); echo CONSTANT_NAME; ``` - Constants are similar to variables except that they cannot be changed or undefined after they've been defined. - name: Specifies the name of the constant. - value: Specifies the value of the constant. - case-insensitive: Specifies whether the constant name should be case-insensitive. Default is `false`. ## Operators | Operator | Example | Note | | ------------ | ------------- | ------------------------------------------------------------ | | `+` | `$a + $b` | Addition. Supports `$a += $b`. | | `-` | `$a - $b` | Subtraction. Supports `$a -= $b`. | | `*` | `$a * $b` | Multiplication. Supports `$a *= $b`. | | `/` | `$a / $b` | Division (**NOT** Truncation Division). Supports `$a /= $b`. `10 / 3 == 3.3333...` | | `%` | `$a % $b` | Modulus. Supports `$a %= $b`. If you use floating point numbers with the modulus operator, they will be converted to **integers** before the operation. | | `++` | `$a++; ++$a;` | post-increment / pre-increment. The difference is that the post-increment returns the original value **before** it changes the variable, while the pre-increment changes the variable first and then returns the value. | | `--` | `$a--; --$a;` | post-decrement / pre-decrement. | | `==` | `$a == $b` | Returns `true` if `$a` is equal to `$b`. | | `===` | `$a === $b` | Returns `true` if `$a` is equal to `$b` and they are same type. | | `!=` | `$a != $b` | Returns `true` if `$a` is **NOT** equal to `$b`. Same to `$a <> $b`. | | `!==` | `$a !== $b` | Returns `true` if `$a` is **NOT** equal to `$b` **OR** they are **NOT** same type. | | `>` / `>=` | `$a > $b` | Greater than (or equal to). | | `<` / `<=` | `$a < $b` | Less than (or equal to). | | `&&` / `and` | `$a && $b` | Returns `true` if **both** `$a` and `$b` are `true`. | | `||` / `or` | `$a || $b` | Returns `true` if **either** `$a` and `$b` is `true`. | | `xor` | `$a xor $b` | Returns `true` if **either** `$a` and `$b` is `true`, but **NOT both**. | | `!` | `!$a` | Returns `true` if `$a` is falsy. | ## Template - `include`: continue if file was not found. `require`: throw error if file was not found. ## `String` - `""` supports escape character and html tag, but `''` do NOT. ## `Array` - Numeric Array: generic array; Associative Array: like a hashtable. ### Inited by values ```php $arr = array('a', 'b', 'c'); echo $arr[0]; // 'a' $arr[0] = 'd'; echo $arr[0]; // 'd' $arr = array('a' => 1, 'b' => 2, 'c' => 3); echo $arr['a']; // 1 $arr['a'] = 4; echo $arr['a']; // 4 ``` ### Inited by size ```php // 1d array $arr = array_fill(0, 10, 1); echo implode(',', $arr); // 1,1,1,1,1,1,1,1,1,1 // 2d array $arr = array_fill(0, 5, array_fill(0, 5, 1)); echo $arr[0][0]; // 1 $arr[0][0] = 2; echo $arr[0][0]; // 2 echo implode(',', $arr[0]); // 2,1,1,1,1 echo implode(',', $arr[1]); // 1,1,1,1,1 echo implode(',', $arr[2]); // 1,1,1,1,1 echo implode(',', $arr[3]); // 1,1,1,1,1 echo implode(',', $arr[4]); // 1,1,1,1,1 ``` ================================================ FILE: topic/language/python/README.md ================================================ Python Syntax Note ====== ## General ### Difference between `is` and `==` `is` will return True if two variables point to the same object, `==` if the objects referred to by the variables are equal. - example in `int` and `float` note that, negative `int`/`float` is special case. ```python >>> 10 != 10 False >>> 10 is not 10 False >>> 0 != 0 False >>> 0 is not 0 False >>> -9 != -9 False >>> -9 is not -9 True ``` - example in `str` and `bytes` ```python # Python `2.7.13`, `2.7.14` >>> u'test' == 'test' True >>> u'test' is 'test' False # Python `3.6.0`, `3.6.3` >>> u'test' == 'test' True >>> u'test' is 'test' True ``` - example in `list` ```python >>> a = [1, 2, 3] >>> b = [1, 2, 3] >>> a == b True >>> a is b False ``` ### Always ensures that shared vars are **IMMUTABLE** in class ```python # bad >>> class TestClass: ... x = {} >>> a = TestClass() >>> b = TestClass() >>> a.x {} >>> a.x[1] = 2 >>> b.x {1: 2} # good >>> class TestClass: ... x = None ... def __init__(self): ... self.x = {} >>> a = TestClass() >>> b = TestClass() >>> a.x {} >>> a.x[1] = 2 >>> b.x {} ``` in func ```python # bad >>> def test_func(x=[]): ... x.append(1) ... return x >>> test_func() [1] >>> test_func() [1, 1] # good >>> def test_func(x=None): ... if x is None: ... x = [] ... x.append(1) ... return x >>> test_func() [1] >>> test_func() [1] ``` ### Private prop in `class` ```python >>> class Test: ... def __init__(self): ... self.a = 1 ... self._b = 2 ... self.__c = 3 ... ... def get_c(self): ... return self.__c >>> t = Test() >>> t.a 1 >>> t._b 2 >>> t.__c AttributeError: 'Test' object has no attribute '__c' >>> t.get_c() 3 ``` ### Hoisting ```python # hoisting is in `for`, `if`, `while` >>> a NameError: name 'a' is not defined >>> i NameError: name 'i' is not defined >>> for i in range(10): ... a = 123 >>> a 123 >>> i 9 # hoisting is NOT in list comprehensions >>> b NameError: name 'b' is not defined >>> [b for b in range(10)] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> b NameError: name 'b' is not defined # hoisting is NOT in `class`, `function` >>> c NameError: name 'c' is not defined >>> def test(a = 1): ... c = 2 >>> test() >>> a NameError: name 'a' is not defined >>> c NameError: name 'c' is not defined ``` ## String `str`, `bytes` ### To find index ```python >>> 'abc'.find('b') 1 >>> 'abc'.find('z') -1 ``` ### To split by multiple delimiters ```python >>> import re >>> re.split(',\s|\s', "Jimmy has an apple, it is on the table") ['Jimmy', 'has', 'an', 'apple', 'it', 'is', 'on', 'the', 'table'] ``` ## Number `int`, `float` ### Using cascade comparison ```python >>> 0 <= 2 < 3 True >>> 0 <= 4 < 3 False ``` ### Be careful of the division ```python # Python 2 >>> 3 // 2 1 >>> 3 / 2 1 # to parse the result as float >>> 3 / 2.0 1.5 # Python 3 >>> 3 // 2 1 >>> 3 / 2 1.5 ``` conclusion: ```python # need int >>> 3 // 2 1 # need float >>> 3 / 2.0 1.5 ``` ### Infinity ```python # positive infinite >>> float('inf') inf # negative infinite >>> float('-inf') -inf ``` ## List `list` ### Iteration ```python >>> A = [['a', 'b'], ['c', 'd']] >>> A [['a', 'b'], ['c', 'd']] >>> B = map(''.join, A) >>> B >>> list(B) ['ab', 'cd'] ``` ### Iteration in reversed order ```python >>> A = list(range(10)) >>> A [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> A[::-1] [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] >>> [A[i] for i in range(len(A) - 1, -1, -1)] [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] ``` ### To find index ```python >>> ['a', 'b', 'c'].index('b') 1 >>> ['a', 'b', 'c'].index('z') ValueError: 'z' is not in list ``` ### To sort list ```python >>> l = [(2,3), (2,-3), (-2,3), (-2,-3)] # 1 key >>> sorted(l, key=lambda i: i[0]) [(-2, 3), (-2, -3), (2, 3), (2, -3)] # 2+ keys >>> sorted(l, key=lambda i: (i[0], i[1])) # that is, `key=lambda i: i` [(-2, -3), (-2, 3), (2, -3), (2, 3)] ``` ### To reverse list ```python >>> origin = [100, 1, 1000, 10] >>> a = origin[:] >>> a.sort(reverse=True) >>> a [1000, 100, 10, 1] # sorting involved >>> a is a True >>> b = origin[:] >>> _b = sorted(b, reverse=True) >>> _b [1000, 100, 10, 1] # sorting involved >>> b is _b False >>> c = origin[:] >>> _c = reversed(c) # >>> list(_c) [10, 1000, 1, 100] # only reversed >>> c is _c False >>> d = origin[:] >>> _d = d[::-1] >>> _d [10, 1000, 1, 100] # only reversed >>> d is _d False ``` ### To extend list ```python >>> a = [1, 2, 3] >>> a [1, 2, 3] >>> a.extend((4, 5, 6)) >>> a [1, 2, 3, 4, 5, 6] ``` ### To extend list with existing items Note that this is copying the pointer, not value, that is, the children must be **IMMUTABLE** in the list. ```python >>> [1] * 3 * 3 [1, 1, 1, 1, 1, 1, 1, 1, 1] >>> [1, 2, 3] * 3 [1, 2, 3, 1, 2, 3, 1, 2, 3] >>> [[0] * 3 for _ in range(3)] [[0, 0, 0], [0, 0, 0], [0, 0, 0]] ``` Explanation: ```python # error >>> nums = [[0] * 3] * 3 >>> nums [[0, 0, 0], [0, 0, 0], [0, 0, 0]] >>> nums[0][0] = 1 >>> nums [[1, 0, 0], [1, 0, 0], [1, 0, 0]] # correct >>> nums = [[0] * 3 for _ in range(3)] >>> nums [[0, 0, 0], [0, 0, 0], [0, 0, 0]] >>> nums[0][0] = 1 >>> nums [[1, 0, 0], [0, 0, 0], [0, 0, 0]] ``` ### To extend existing list and create new one ```python >>> a = [1] >>> b = a + [2] >>> a is b False ``` ### To override mutable variable and keep the pointer if need ```python >>> a = b = [1, 2, 3] >>> a is b True >>> b[:] = [4, 5, 6] >>> a [4, 5, 6] >>> a is b True >>> b = [7, 8, 9] >>> a [4, 5, 6] >>> a is b False ``` ### To clone a list ```python >>> a = [1, 2, 3] # shallow clone >>> b = a >>> b is a True >>> b = a[:] >>> b is a False # shallow clone >>> c = a >>> c is a True >>> c = a + [] >>> c is a False # deep clone >>> a = [{'a': 1}] >>> from copy import deepcopy >>> b = deepcopy(a) >>> a[0]['a'] = 2 >>> a, b ([{'a': 2}], [{'a': 1}]) ``` ## Dict `dict` ### Immutable Dict ```python # set >>> from collections import namedtuple >>> ImmutableDict = namedtuple('ImmutableDict', ['k1', 'k2']) >>> D = ImmutableDict(1, 2) >>> D ImmutableDict(k1=1, k2=2) # get >>> getattr(D, 'k1', -1) 1 >>> getattr(D, 'k3', -1) -1 ``` ### Multi-dimensional indexing in dict - with `tuple` note that, since `tuple` is also **IMMUTABLE**. ```python >>> d = {} >>> d[1, 2, 3] = 1 >>> d[1, 2, 3] 1 >>> d[(1, 2, 3)] 1 >>> d {(1, 2, 3): 1} ``` - with `defaultdict` ```python >>> import collections >>> d = collections.defaultdict(lambda: collections.defaultdict(int)) >>> d[1][2] = 1 >>> d[1][2] 1 >>> d defaultdict( at 0x10a889d90>, {1: defaultdict(, {2: 1})}) ``` ### To clone a dict ```python >>> a = {'a': 1, 'b': 2} # extend >>> a.update({'c': 3, 'd': {'e': 4}}) >>> a {'a': 1, 'b': 2, 'c': 3, 'd': {'e': 4}} # shallow clone >>> b = a.copy() >>> a, b ({'a': 1, 'b': 2, 'c': 3, 'd': {'e': 4}}, {'a': 1, 'b': 2, 'c': 3, 'd': {'e': 4}}) >>> a['a'] = 99 >>> a['d']['e'] = 99 >>> a, b ({'a': 99, 'b': 2, 'c': 3, 'd': {'e': 99}}, {'a': 1, 'b': 2, 'c': 3, 'd': {'e': 99}}) # deep clone >>> from copy import deepcopy >>> b = deepcopy(a) >>> a, b ({'a': 99, 'b': 2, 'c': 3, 'd': {'e': 99}}, {'a': 99, 'b': 2, 'c': 3, 'd': {'e': 99}}) >>> a['a'] = 111 >>> a['d']['e'] = 111 >>> a, b ({'a': 111, 'b': 2, 'c': 3, 'd': {'e': 111}}, {'a': 99, 'b': 2, 'c': 3, 'd': {'e': 99}}) ``` ================================================ FILE: topic/language/python/matrix.md ================================================ Matrix Iteration ====== ## To convert the 2D matrix to the 1D list ```python # For any m * n matrix # 0 <= x < m, 0 <= y < n # The cell at `[i, j]` can be mapped to `k` in list k = i * n + j ``` ## To iterate around the current cell means to visit the cell at the top/bottom/left/right ```python VECTOR = ( (-1, 0), ( 1, 0), ( 0,-1), ( 0, 1), ) x, y = 1, 1 for dx, dy in VECTOR: print(x + dx, y + dy) # 0, l: (0, 1) # 1, r: (2, 1) # 2, t: (1, 0) # 3, b: (1, 2) ``` ## To avoid returning along the original path, just simply set the last visited cell to `'#'` see [lintcode/132_word_search_ii.py](../lintcode/132_word_search_ii.py) ## Traverse the half triangle in matrix ```python >>> n = 4 >>> arr = [[0] * n for _ in range(n)] >>> arr [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] >>> for x in range(n - 1, -1, -1): ... for y in range(x, n): ... arr[x][y] = 1 >>> arr [[1, 1, 1, 1], [0, 1, 1, 1], [0, 0, 1, 1], [0, 0, 0, 1]] >>> for y in range(n - 1, -1, -1): ... for x in range(y, n): ... arr[x][y] = 2 >>> arr [[2, 1, 1, 1], [2, 2, 1, 1], [2, 2, 2, 1], [2, 2, 2, 2]] >>> frozen_line = 2 >>> for y in range(n - 1 - frozen_line, -1, -1): ... for x in range(y + frozen_line, n): ... arr[x][y] = 3 >>> arr [[2, 1, 1, 1], [2, 2, 1, 1], [3, 2, 2, 1], [3, 3, 2, 2]] ``` ### Need to init status by recursing from diagonal line since sometimes we need to init status from bottom-left cell, see [lintcode/136_palindrome_partitioning.py](../lintcode/136_palindrome_partitioning.py) ```python >>> n = 5 >>> level = order = 0 >>> arr = [[0] * n for _ in range(n)] >>> for y in range(n): ... order += 1 ... arr[y][y] = order ... if y > 0: ... x = y - 1 ... arr[x][y] = order >>> arr [[1, 2, 0, 0, 0], [0, 2, 3, 0, 0], [0, 0, 3, 4, 0], [0, 0, 0, 4, 5], [0, 0, 0, 0, 5]] >>> level = order = 0 >>> for x in range(n - 1 - 2, -1, -1): ... level += 10 ... order = 0 ... for y in range(x + 2, n): ... order += 1 ... arr[x][y] = level + order >>> arr [[ 1, 2, 31, 32, 33], [ 0, 2, 3, 21, 22], [ 0, 0, 3, 4, 11], [ 0, 0, 0, 4, 5], [ 0, 0, 0, 0, 5]] >>> level = order = 0 >>> for size in range(1 + 2, n + 1): ... level += 10 ... order = 0 # 0, 1, 2, 3, 4; n == 5, size == 3 # => set == {(0,1,2),(1,2,3),(2,3,4)} # => i < 3 # => `i < n - size + 1` or `i <= n - size` ... for x in range(n - size + 1): ... order += 1 ... y = x + size - 1 ... arr[x][y] = level + order >>> arr [[ 1, 2, 11, 21, 31], [ 0, 2, 3, 12, 22], [ 0, 0, 3, 4, 13], [ 0, 0, 0, 4, 5], [ 0, 0, 0, 0, 5]] ``` ================================================ FILE: topic/language/python/runtime_of_builtins.md ================================================ The time-complexity of various operations in **CPython** ====== Note that, 1. **CPython** is the official and most widespread implementation 2. `n = len(data)` ## Basic | Operation | Example | Complexity | Notes | | ---------------------------- | -------------------- | ---------- | ---------------------------------------- | | binding immutable value | `a = 1` | $O(1)$ | | | simple operators on integers | `1 + 1`,
`2 == 2` | $O(1)$ | assume small integers unless explicitly told otherwise
(whose values are small: e.g., under 12 digits) | ## List, Tuple `tuple` support all operations that do not mutate the data structure (and with the same complexity classes). When comparing two lists for equality, the complexity class above shows as $O(n)$, but in reality we would need to multiply this complexity by $O(==)$ where $O(==)$ is the complexity class for checking whether two values in the list are `==`. If they are ints, $O(==)$ would be $O(1)$; if they are strings, $O(==)$ in the worst case it would be $O(len(string))$. This issue applies any time an `==` check is done. We mostly will assume `==` checking on values in lists is $O(1)$: e.g., checking ints and small strings. | Operation | Example | Complexity | Notes | | --------------------- | --------------------- | ------------- | ---------------------------------------- | | index | `l[i]` | $O(1)$ | | | store | `l[i] = 0` | $O(1)$ | | | length | `len(l)` | $O(1)$ | | | append | `l.append(5)` | $O(1)$ | | | pop | `l.pop()` | $O(1)$ | same as `l.pop(-1)`, popping at end | | clear | `l.clear()` | $O(1)$ | similar to `l = []` | | slice | `l[a:b]` | $O(b-a)$ | `l[1:5]` -> $O(l)$
`l[:]` -> $O(len(l)-0)=O(n)$ | | extend | `l.extend(t)` | $O(m)$ | `m = len(t)` | | construction iterable | `list(l)` | $O(n)$ | | | check `==`, `!=` | `l == t` | $O(n)$ | note that, `is` -> $O(1)$, see [Difference between `is` and `==`](./python_syntax.md#difference-between-is-and-) | | insert | `l[a:b] = t` | $O(n)$ | | | delete | `del l[i]` | $O(n)$ | | | containment | `v in l`/`v not in l` | $O(n)$ | searches list | | copy | `l.copy()` | $O(n)$ | same as `l[:]` -> $O(n)$ | | remove | `l.remove(v)` | $O(n)$ | remove `v` from `l` if `v` in `l` otherwise raise an exception | | pop | `l.pop(i)` | $O(n)$ | $O(n-i)$
`l.pop(0)` -> $O(n)$ | | extreme value | `min(l)`/`max(l)` | $O(n)$ | searches list | | reverse | `l.reverse()` | $O(n)$ | | | iteration | `for v in l:` | $O(n)$ | | | sort | `l.sort()` | $O(n\log(n))$ | `key`/`reverse` mostly doesn't change | | multiply | `k * l` | $O(k×n)$ | | ## collections.deque A `deque` (double-ended queue) is represented internally as a doubly linked list. (Well, a list of arrays rather than objects, for greater efficiency.) Both ends are accessible, but even looking at the middle is slow, and adding to or removing from the middle is slower still. | Operation | Example | Complexity | Notes | | ---------- | ----------------- | ---------- | ---------------------------------------- | | append | `q.append(1)` | $O(1)$ | | | appendleft | `q.appendleft(2)` | $O(1)$ | | | pop | `q.pop()` | $O(1)$ | | | popleft | `q.popleft()` | $O(1)$ | | | extend | `q.extend(t)` | $O(m)$ | `m = len(t)` | | extendleft | `q.extendleft(t)` | $O(m)$ | | | rotate | `q.rotate(m)` | $O(m)$ | `type(m) == int`
move the `m` children in right to left if `m > 0`
otherwise move from left to right | | copy | `q.copy()` | $O(n)$ | | | remove | `q.remove(x)` | $O(n)$ | | ## Set `set` have many more operations that are $O(1)$ compared with lists and tuples. Not needing to keep values in a specific order in a set (which lists/tuples require) allows for faster set operations. Frozen sets support all operations that do not mutate the data structure (and with the same complexity classes). | Operation | Example | Complexity | Notes | | -------------------- | --------------------- | ---------- | ---------------------------------------- | | length | `len(s)` | $O(1)$ | | | add | `s.add(5)` | $O(1)$ | | | containment | `v in s`/`v not in s` | $O(1)$ | compare to list/tuple -> $O(n)$ | | remove | `s.remove(v)` | $O(1)$ | remove `v` from `s` if `v` in `s` otherwise raise an exception
compare to list/tuple -> $O(n)$ | | discard | `s.discard(v)` | $O(1)$ | remove `v` from `s` even `v` not in `s` -> no exception | | pop | `s.pop()` | $O(1)$ | | | clear | `s.clear()` | $O(1)$ | similar to `s = set()` | | construction | `set(l)` | $O(n)$ | depends on length of iterable, that is `l` here | | check `==`, `!=` | `s != t` | $O(n)$ | `len(s)` must be same as `len(t)`
if not, return False in $O(1)$ | | `<=`/`<` | `s <= t` | $O(n)$ | issubset | | `>=`/`>` | `s >= t` | $O(m)$ | `m = len(t)`
issuperset `s >= t` == `t <= s` | | union | `s \| t` | $O(m+n)$ | | | intersection | `s & t` | $O(m+n)$ | | | difference | `s - t` | $O(m+n)$ | | | symmetric difference | `s ^ t` | $O(m+n)$ | see [Symmetric Difference @wikipedia](https://en.wikipedia.org/wiki/Symmetric_difference) | | iteration | `for v in s:` | $O(n)$ | | | copy | `s.copy()` | $O(n)$ | | ## Dict Most `dict` operations are $O(1)$. `defaultdict` support all operations that dicts support, with the same complexity classes (because it inherits all the operations); this assumes that calling the constructor when a values isn't found in the defaultdict is $O(1)$ - which is true for `int()`, `list()`, `set()`, ... (the things we commonly use) | Operation | Example | Complexity | Notes | | ----------------------- | ------------- | ---------- | ---------------------------------------- | | index | `d[k]` | $O(1)$ | | | store | `d[k] = v` | $O(1)$ | | | length | `len(d)` | $O(1)$ | | | delete | `del d[k]` | $O(1)$ | | | `get`/`setdefault` | `d.get()` | $O(1)$ | | | pop | `d.pop(k)` | $O(1)$ | | | pop item | `d.popitem()` | $O(1)$ | | | clear | `d.clear()` | $O(1)$ | similar to `s = {}` or `s = dict()` | | `keys`/`values`/`items` | `d.keys()` | $O(1)$ | | | construction | `dict(t)` | $O(n)$ | `n = len(t)`
the interface of `t`: `Iterable[Iterable]`
example: `((1, 2), (2, 3), (4, 5), ('a', 6))` | | iteration | `for v in d:` | $O(n)$ | same as `for v in d.keys()`
all forms: `keys`, `values`, `items` | ## Reference - [Complexity of Python Operations](https://www.ics.uci.edu/~pattis/ICS-33/lectures/complexitypython.txt) - [TimeComplexity - Python Wiki](https://wiki.python.org/moin/TimeComplexity) ================================================ FILE: topic/language/python/traversal.md ================================================ Traversal ====== ## Iterate an iterable item - `for`: the pointer of an iterable item will be **UNCHANGED** after start - `while`: the pointer of an iterable item will be **RE-FETCHED** before every loop start ### Dynamically extend an iterable item in `for` loop - scenario: BFS - example: - [binary_tree_serialization.py](../module/binary_tree_serialization.py) - the behavior below just like `queue` ```python >>> arr = [0] >>> for child in arr: ... if len(arr) > 5: ... break ... arr.append(1) >>> arr [0, 1, 1, 1, 1, 1] ``` ### Dynamically re-assign an iterable item 1. Normal Form - scenario: BFS in level traversal - example: - [binary_tree_preorder_traversal.py](../module/binary_tree_preorder_traversal.py) - [69_binary_tree_level_order_traversal.py](../lintcode/69_binary_tree_level_order_traversal.py) - [598_zombie_in_matrix.py](../lintcode/598_zombie_in_matrix.py) ```python >>> arr = [{ ... 'val': 1, ... 'left': {'val': 2, 'left': None, 'right': None}, ... 'right': {'val': 3, 'left': None, 'right': None}, ... }] >>> _arr = None >>> level = 0 >>> while arr: ... level += 1 ... _arr = [] ... for child in arr: ... if child['left']: ... _arr.append(child['left']) ... if child['right']: ... _arr.append(child['right']) ... arr = _arr >>> level 2 ``` 2. Dedup Form - scenario: BFS in level traversal if the node may be visited twice. e.g., graph. - example: - [lintcode/120_word_ladder.py](../lintcode/120_word_ladder.py) - [lintcode/121_word_ladder_ii.py](../lintcode/121_word_ladder_ii.py) - [lintcode/531_six_degrees.py](../lintcode/531_six_degrees.py) ================================================ FILE: topic/language/sql/README.md ================================================ SQL (Structured Query Language) ====== **SQL** is used to access and manipulate a **database**. **MySQL** is a program that understands **SQL**. SQL can: - insert, update, or delete records in a database. - create new databases, table, stored procedures, views. - retrieve data from a database, etc. SQL is an ANSI (American National Standards Institute) standard, but there are different versions of the SQL language. Most SQL database programs have their own proprietary extensions in addition to the SQL standard, but all of them support the major commands. ## Concept A **database** is a collection of data that is organized in a manner that facilitates ease of access, as well as efficient management and updating. A database is made up of **tables** that store relevant information. A table stores and displays data in a structured format consisting of **columns** and **rows** that are similar to those seen in Excel spreadsheets. ### Primary Key A primary key is a field in the table that uniquely identifies the table records. The primary key's main features: - It must contain a **unique value** for each row. - It cannot contain `NULL` values. Note that, - Tables are limited to **ONE** primary key each. - The primary key's value must be different for each row. ## Syntax ### Logical Operators Note that, to compare with a text value, you need to surround the text that appears in the statement with **single quotation marks (')**. | Operators | Notes | | ----------------- | ------------------------------------------------------------ | | `=` | Equal | | `!=` | Not equal | | `>` | Greater than | | `<` | Less than | | `>=` | Greater than or equal | | `<=` | Less than or equal | | `BETWEEN a AND b` | Between an inclusive range, that is, `a <= x <= b` | | `AND` | True if **both** expressions are True | | `OR` | True if **either** expressions is True | | `a IN (b, c, d)` | True if the operand is equal to one of a list of expressions | | `NOT` | Returns True if expression is not True | | `a LIKE pattern` | SQL **pattern** matching enables you to use `_` to match any single character and `%` to match an arbitrary number of characters (including zero characters). | ### Arithmetic Operators | Operators | Notes | | --------- | -------------- | | `+` | addition | | `-` | subtraction | | `*` | multiplication | | `/` | division | ### Data Types | Type | Category | Notes | | -------------- | ------------- | ------------------------------------------------------------ | | `int` | Numeric | A normal-sized integer that can be signed or unsigned. | | `float(n, d)` | Numeric | A floating-point number that cannot be unsigned. You can optionally define the display length (`n`) and the number of decimals (`d`). | | `double(n, d)` | Numeric | A double precision floating-point number that cannot be unsigned. You can optionally define the display length (`n`) and the number of decimals (`d`). | | `date` | Date and Time | A date in `YYYY-MM-DD` format. | | `datetime` | Date and Time | A date and time combination in `YYYY-MM-DD HH:MM:SS` format. | | `time` | Date and Time | A time in `HH:MM:SS` format. | | `timestamp` | Date and Time | A timestamp, calculated from midnight, January 1, 1970. | | `char(n)` | String | Fixed-length (`n`) character string. Size is specified in parenthesis. Max 255 bytes. | | `varchar(n)` | String | Variable-length(`n`)character string. Max size is specified in parenthesis. | | `blob` | String | **Binary Large Objects** and are used to store large amounts of binary data, such as images or other types of files. | | `text` | String | Large amount of text data. | ### Multiple Queries SQL allows to run multiple queries or commands at the same time. Remember to end each SQL statement with a **semicolon** to indicate that the statement is complete and ready to be interpreted. In this tutorial, we will use **semicolon** at the end of each SQL statement. ```sql SELECT FirstName FROM customers; SELECT City FROM customers; ``` ### Case Sensitivity SQL is case **insensitive**. It is common practice to write all SQL commands in **upper-case**. ### Separator A single SQL statement can be placed on one or more text lines. In addition, multiple SQL statements can be combined on a single text line. White spaces and multiple lines are ignored in SQL. However, it is recommended to avoid unnecessary white spaces and lines. Combined with proper spacing and indenting, breaking up the commands into logical lines will make your SQL statements much easier to read and maintain. ## Command ### Database | Commands | Notes | | ---------------- | ----------------------------------------- | | `SHOW DATABASES` | list the databases managed by the server. | ### Table #### SQL constraints - `NOT NULL` - Indicates that a column cannot contain any NULL value. - `UNIQUE` - Does not allow to insert a duplicate value in a column. The `UNIQUE` constraint maintains the uniqueness of a column in a table. More than one `UNIQUE` column can be used in a table. - `AUTO_INCREMENT` - Auto-increment allows a unique number to be generated when a new record is inserted into a table. By default, the starting value for `AUTO_INCREMENT` is 1, and it will increment by 1 for each new record. - `PRIMARY KEY` - Enforces the table to accept unique data for a specific column and this constraint create a unique index for accessing the table faster. - `CHECK` - Determines whether the value is valid or not from a logical expression. - `DEFAULT` - While inserting data into a table, if no value is supplied to a column, then the column gets the value set as `DEFAULT`. #### Views In SQL, a VIEW is a **virtual table** that is based on the result-set of an SQL statement. A view contains rows and columns, just like a real table. The fields in a view are fields from one or more real tables in the database. A view always shows up-to-date data! The database engine uses the view's SQL statement to recreate the data each time a user queries a view. Views allow us to: - Structure data in a way that users or classes of users find natural or intuitive. - Restrict access to the data in such a way that a user can see and (sometimes) modify exactly what they need and no more. - Summarize data from various tables and use it to generate reports. | Commands | Notes | | ------------------------------------------------------------ | ------------------------------------------------------------ | | `SHOW TABLES` | display all of the tables in the currently selected MySQL database. | | `CREATE TABLE Users ( UserID int NOT NULL AUTO_INCREMENT, FirstName varchar(40) NOT NULL, LastName varchar(40) NOT NULL, PRIMARY KEY(UserID) )` | Note the **parentheses** in the syntax. When inserting a new record into the Users table, it's not necessary to specify a value for the `UserID` column; a unique new value will be added automatically. | | `DROP TABLE table` | delete the entire table | | `RENAME TABLE table TO new_name` | rename the entire table | | `CREATE VIEW view AS (SELECT col FROM table WHERE condition)` | The `SELECT` query can be as complex as you need it to be. It can contain multiple JOINS and other commands. | | `CREATE OR REPLACE VIEW view AS (subquery)` | update a view | | `DROP VIEW view` | delete a view | ### Column | Commands | Notes | | ----------------------------------------------- | ------------------------------------------------------------ | | `SHOW COLUMNS FROM table` | display information about the columns in a given table. | | `SELECT col AS col2 FROM table` | assign a custom name to the resulting column using the `AS` keyword. | | `ALTER TABLE table ADD new_col int` | add columns in an existing table. All rows will have the default value in the newly added column, which, in this case, is `NULL`. | | `ALTER TABLE table DROP COLUMN col` | delete the column named col in the table. The column, along with all of its data, will be completely removed from the table. | | `ALTER TABLE table CHANGE col new_name type(n)` | rename the column called col to new_name | ### Basic Query | Commands | Notes | | ------------------------------------------------------------ | ------------------------------------------------------------ | | `SELECT col FROM table` | select data from a given table. | | `SELECT col1, col2 FROM table` | select data from a given table. Do not put a comma after the **last** column name. | | `SELECT table.col FROM table` | you can provide the table name prior to the column name, by separating them with a **dot**. The term for the above-mentioned syntax is called the **fully qualified name** of that column. This form of writing is especially useful when working with multiple tables that may share the same column names. | | `SELECT * FROM table` | retrieve all of the information contained in your table, place an **asterisk (\*)** sign after the `SELECT` command, rather than typing in each column names separately. | | `SELECT col FROM table WHERE col > (SELECT AVG(col) FROM table)` | A single subquery will return the same result more easily. | ### WHERE | Commands | Notes | | ------------------------------------- | ------------------------------------------------------------ | | `SELECT * FROM table WHERE condition` | extract only those records that fulfill a specified criterion. | ### DISTINCT and LIMIT | Commands | Notes | | ----------------------------------------------- | ------------------------------------------------------------ | | `SELECT DISTINCT col1, col2 FROM table` | conjunction with `SELECT` to eliminate all duplicate records and return only unique ones | | `SELECT col1, col2 FROM table LIMIT cnt` | retrieve the first `cnt` records from the table | | `SELECT col1, col2 FROM table LIMIT start, cnt` | pick up `cnt` records, starting from the `start` position. the position is **zero-based** counting, so the position 3 == the fourth record. | ### ORDER BY | Commands | Notes | | ----------------------------------------- | ------------------------------------------------------------ | | `SELECT * FROM table ORDER BY col1, col2` | **sort** the returned data. By default, the ORDER BY keyword sorts the results in **ascending** order. | | `SELECT * FROM table ORDER BY col1 DESC` | The `DESC` keyword sorts results in **descending** order. Similarly, `ASC` sorts the results in **ascending** order. | ### JOIN | Commands | Notes | | ------------------------------------------------------------ | ------------------------------------------------------------ | | `SELECT tb1.a, tb2.a FROM tb1, tb2 WHERE tb1.a = tb2.a` | Specify multiple table names in the FROM by comma-separating them. | | `SELECT tb1.col1, tb2.col1 FROM tb1 INNER JOIN tb2 ON tb1.col1 = tb2.col1` | `INNER JOIN` is equivalent to `JOIN`. It returns rows when there is a match between the tables. Note the `ON` keyword for specifying the inner join condition. | | `SELECT customers.Name, items.Name FROM customers LEFT OUTER JOIN items ON customers.ID = items.Seller_id` | The `OUTER` keyword is optional, and can be omitted. The `LEFT JOIN` returns all rows from the left table, even if there are no matches in the right table. This means that if there are no matches for the `ON` clause in the table on the right, the join will still return the rows from the first table in the result. If no match is found for a particular row, `NULL` is returned. | | `SELECT col1 FROM tb1 UNION SELECT col2 FROM tb2` | `UNION` combines multiple datasets into a single dataset, and removes any existing duplicates. `UNION ALL` combines multiple datasets into one dataset, but does not remove duplicate rows. `UNION ALL` is faster than `UNION`, as it does not perform the duplicate removal operation over the data set.
The `UNION` operator is used to combine the result-sets of two or more `SELECT` statements. All `SELECT` statements within the `UNION` must have the **same number of columns**. The columns must also have the same **data types**. Also, the columns in each `SELECT` statement must be **in the same order**.
If your columns don't match exactly across all queries, you can use a NULL (or any other) value. | ### INSERT/UPDATE/DELETE | Commands | Notes | | ----------------------------------------------------------- | ------------------------------------------------------------ | | `INSERT INTO table VALUES (val1, val2, ...)` | add **new rows** of data to a table in the database. Make sure the order of the values is in the same order as the columns in the table. When inserting records into a table using the SQL `INSERT` statement, you must provide a value for every column that does not have a default value, or does not support `NULL`. | | `INSERT INTO table (col2, col1) VALUES (val2, val1)` | You can specify your own column order, as long as the values are specified in the same order as the columns. The **missing** column for that row automatically became its default value. | | `UPDATE table SET col1 = val1, col2 = val2 WHERE condition` | allows us to alter data in the table. You specify the column and its new value in a comma-separated list after the `SET` keyword. If you omit the `WHERE` clause, **ALL records** in the table will be updated! | | `DELETE FROM table WHERE condition` | remove data from your table. If you omit the `WHERE` clause, **ALL records** in the table will be deleted! The `DELETE` statement removes the data from the table permanently. | ### Functions | Commands | Notes | | ------------------------------------------------------------ | ------------------------------------------------------------ | | `SELECT CONCAT(a, ',', b) FROM table` | concatenate two or more text values and returns the concatenating string. | | `SELECT FirstName, UPPER(LastName) AS LastName FROM employees` | The `UPPER` function converts all letters in the specified string to uppercase. The `LOWER` function converts the string to lowercase. If there are characters in the string that are not letters, this function will have no effect on them. | | `SELECT Salary, SQRT(Salary) FROM employees` | calculate the square root of each Salary | | `SELECT AVG(Salary) FROM employees` | returns the average value of a numeric column | | `SELECT SUM(Salary) FROM employees` | get the sum of all of the salaries in the employees table | | `SELECT MIN(Salary) AS Salary FROM employees` | return the minimum value of an expression | ================================================ FILE: topic/linked_list/README.md ================================================ Linked List ====== ## Common Rule - If the list is cyclic, the 2-pace pointer will eventually meet the 1-pace. see [lintcode/102_linked_list_cycle.py](../lintcode/102_linked_list_cycle.py) - If there is a intersection node, the steps from the first node is equal to from meet node plus 1. see [lintcode/103_linked_list_cycle_ii.py](../lintcode/103_linked_list_cycle_ii.py) - If its a list, the middle node is the 1-pace pointer when the 2-pace pointer has traversed the list. see [lintcode/98_sort_list.py](../lintcode/98_sort_list.py) ## Reverse Linked List - with two extra vars ```python >>> cur = nxt = None >>> while head: ... nxt = head.next ... head.next = cur ... cur = head ... head = nxt ``` - with one extra var (**Python only**) ```python >>> cur = None >>> while head: ... head.next, cur, head = cur, head, head.next ``` ================================================ FILE: topic/linked_list/python/__init__.py ================================================ from linked_list.python.cyclic_list import CyclicList from linked_list.python.cyclic_doubly_list import CyclicDoublyList from linked_list.python.dummy_tail_list import DummyTailList from linked_list.python.dummy_tail_doubly_list import DummyTailDoublyList from linked_list.python.head_tail_list import HeadTailList from linked_list.python.head_tail_doubly_list import HeadTailDoublyList from linked_list.python.two_dummy_list import TwoDummyList from linked_list.python.two_dummy_doubly_list import TwoDummyDoublyList ================================================ FILE: topic/linked_list/python/_helper.py ================================================ """ api doc of Class java.util.LinkedList https://courses.cs.washington.edu/courses/cse341/98au/java/jdk1.2beta4/docs/api/java/util/LinkedList.html """ from abc import ABC, abstractmethod import collections class ListNode: def __init__(self, val, nxt=None): self.val = val self.nxt = nxt class DoublyListNode: def __init__(self, val, pre=None, nxt=None): self.val = val self.pre = pre self.nxt = nxt class ListBase(ABC): def __repr__(self): return repr(self.to_list()) def __str__(self): return str(self.to_list()) def __bool__(self): return bool(self._get_size()) def __len__(self): return self._get_size() def to_list(self): """ :rtype: List[any] """ return list(self.__iter__()) def contains(self, obj): """ :type obj: any :rtype: bool """ for node in self.__iter__(): if node is obj: return True return False def get_first(self): """ :rtype: any """ return self.get(0) def get_last(self): """ :rtype: any """ return self.get(-1) def add_first(self, obj): """ :type obj: any :rtype: void """ self.add(obj, 0) def add_last(self, obj): """ :type obj: any :rtype: void """ self.add(obj, -1) def remove_first(self): """ :rtype: any """ return self.remove(0) def remove_last(self): """ :rtype: any """ return self.remove(-1) def _check_index(self, index, raise_error=False): """ :type index: int :type raise_error: bool :rtype: bool return True if index is valid return False if invalid and NO need raise_error raise IndexError if invalid and need raise_error """ size = self._get_size() if any(( index >= 0 and index < size, index < 0 and -index <= size, )): # is valid return True if raise_error: raise IndexError('linked list index out of range') return False @abstractmethod def __iter__(self): pass @abstractmethod def set(self, obj, index=-1): """ :type obj: any :type index: int :rtype: any """ pass @abstractmethod def get(self, index=-1): """ :type index: int :rtype: any """ pass @abstractmethod def add(self, obj, index=-1): """ :type obj: any :type index: int :rtype: void """ pass @abstractmethod def add_all(self, objs, index=-1): """ :type objs: Iterable :type index: int :rtype: void """ if not isinstance(objs, collections.Iterable): raise ValueError('Oops! passed-in objs was not iterable.') @abstractmethod def remove(self, index=-1): """ :type index: int :rtype: any """ pass @abstractmethod def index(self, obj): """ :type obj: any :rtype: int return -1 if not found """ pass @abstractmethod def last_index(self, obj): """ :type obj: any :rtype: int return -1 if not found """ pass @abstractmethod def clear(self): """ :rtype: void """ pass @abstractmethod def clone(self): """ :rtype: LinkedList """ pass @abstractmethod def sort(self, key=None): """ :type key: function :rtype: void """ pass @abstractmethod def _get_size(self): """ :rtype: int """ pass @abstractmethod def _get_node(self, index=-1): """ :type index: int :rtype: ListNode """ pass ================================================ FILE: topic/linked_list/python/cyclic_doubly_list.py ================================================ """ D <-> x <-> y <-> z <-> D """ from linked_list.python._helper import * class CyclicDoublyList(ListBase): def __init__(self): self.clear() def __iter__(self): node = self.__dummy[2] while node is not self.__dummy: obj, _, nxt = node yield obj node = nxt def set(self, obj, index=-1): """ :type obj: any :type index: int :rtype: any """ self._check_index(index, raise_error=True) target = self._get_node(index) target[0] = obj return obj def get(self, index=-1): """ :type index: int :rtype: any """ self._check_index(index, raise_error=True) return self._get_node(index)[0] def add(self, obj, index=-1): """ :type obj: any :type index: int :rtype: void """ if not self._check_index(index): index = -1 target = self._get_node(index) if index < 0: node = self._new_node(obj, target, target[2]) target[2][1] = node target[2] = node else: node = self._new_node(obj, target[1], target) target[1][2] = node target[1] = node self.__size += 1 def add_all(self, objs, index=-1): """ :type objs: Iterable :type index: int :rtype: void """ super(CyclicDoublyList, self).add_all(objs, index) if not self._check_index(index): index = -1 dummy = tail = self._new_node(None) for obj in objs: tail[2] = self._new_node(obj, tail) tail = tail[2] target = self._get_node(index) if index < 0: dummy[2][1] = target tail[2] = target[2] target[2][1] = tail target[2] = dummy[2] else: dummy[2][1] = target[1] tail[2] = target target[1][2] = dummy[2] target[1] = tail self.__size += len(objs) def remove(self, index=-1): """ :type index: int :rtype: any """ self._check_index(index, raise_error=True) obj, pre, nxt = self._get_node(index) pre[2] = nxt nxt[1] = pre self.__size -= 1 return obj def index(self, obj): """ :type obj: any :rtype: int return -1 if not found """ i = 0 node = self.__dummy[2] while node is not self.__dummy: if node[0] is obj: return i node = node[2] i += 1 return -1 def last_index(self, obj): """ :type obj: any :rtype: int return -1 if not found """ i = 1 node = self.__dummy[1] while node is not self.__dummy: if node[0] is obj: return i node = node[1] i += 1 return -1 def clear(self): """ :rtype: void """ dummy = [] dummy[:] = self._new_node(None, dummy, dummy) self.__dummy = dummy self.__size = 0 def clone(self): """ :rtype: LinkedList """ pass def sort(self, key=None): """ :type key: function :rtype: void """ pass def _get_size(self): """ :rtype: int """ return self.__size def _get_node(self, index=-1): """ :type index: int :rtype: ListNode """ node = None if index < 0: node = self.__dummy while index != 0: index += 1 node = node[1] else: node = self.__dummy[2] while index != 0: index -= 1 node = node[2] return node def _new_node(self, obj, pre=None, nxt=None): return [obj, pre, nxt] ================================================ FILE: topic/linked_list/python/cyclic_list.py ================================================ """ D --> x --> y --> z --> D TODO: merge `_get_node`, `_get_prev_node` """ from linked_list.python._helper import * class CyclicList(ListBase): def __init__(self): self.clear() def __iter__(self): node = self.__dummy[1] while node is not self.__dummy: obj, nxt = node yield obj node = nxt def set(self, obj, index=-1): """ :type obj: any :type index: int :rtype: any """ self._check_index(index, raise_error=True) target = self._get_node(index) target[0] = obj return obj def get(self, index=-1): """ :type index: int :rtype: any """ self._check_index(index, raise_error=True) return self._get_node(index)[0] def add(self, obj, index=-1): """ :type obj: any :type index: int :rtype: void """ if not self._check_index(index): index = -1 target = None if index < 0: target = self._get_node(index) else: target = self._get_prev_node(index) node = self._new_node(obj, target[1]) target[1] = node self.__size += 1 def add_all(self, objs, index=-1): """ :type objs: Iterable :type index: int :rtype: void """ super(CyclicList, self).add_all(objs, index) if not self._check_index(index): index = -1 dummy = tail = self._new_node(None) for obj in objs: tail[1] = self._new_node(obj) tail = tail[1] target = None if index < 0: target = self._get_node(index) else: target = self._get_prev_node(index) tail[1] = target[1] target[1] = dummy[1] self.__size += len(objs) def remove(self, index=-1): """ :type index: int :rtype: any """ self._check_index(index, raise_error=True) node = self._get_prev_node(index) obj = node[1][0] node[1] = node[1][1] self.__size -= 1 return obj def index(self, obj): """ :type obj: any :rtype: int return -1 if not found """ i = 0 node = self.__dummy[1] while node is not self.__dummy: if node[0] is obj: return i node = node[1] i += 1 return -1 def last_index(self, obj): """ :type obj: any :rtype: int return -1 if not found """ head = node = self.__dummy[1] while node[0] is not obj: if node[1] is head: return -1 node = node[1] i = 0 while node[1] is not head: node = node[1] i += 1 return i def clear(self): """ :rtype: void """ dummy = [] dummy[:] = self._new_node(None, dummy) self.__dummy = dummy self.__size = 0 def clone(self): """ :rtype: LinkedList """ pass def sort(self, key=None): """ :type key: function :rtype: void """ pass def _get_size(self): """ :rtype: int """ return self.__size def _get_node(self, index=-1): """ :type index: int :rtype: ListNode """ head = slow = self.__dummy[1] if index < 0: fast = head while index != 0: index += 1 fast = fast[1] while fast[1] is not head: slow = slow[1] fast = fast[1] else: while index != 0: index -= 1 slow = slow[1] return slow def _get_prev_node(self, index=-1): """ :type index: int :rtype: ListNode This method may return dummy """ dummy = slow = self.__dummy if index < 0: fast = dummy while index != 0: index += 1 fast = fast[1] while fast[1] is not dummy: slow = slow[1] fast = fast[1] else: while index != 0: index -= 1 slow = slow[1] return slow def _new_node(self, obj, nxt=None): return [obj, nxt] ================================================ FILE: topic/linked_list/python/dummy_tail_doubly_list.py ================================================ """ t D <-> x <-> y <-> z """ from linked_list.python._helper import * class DummyTailDoublyList(ListBase): pass ================================================ FILE: topic/linked_list/python/dummy_tail_list.py ================================================ """ t D --> x --> y --> z """ from linked_list.python._helper import * class DummyTailList(ListBase): pass ================================================ FILE: topic/linked_list/python/head_tail_doubly_list.py ================================================ """ h t x <-> y <-> z """ from linked_list.python._helper import * class HeadTailDoublyList(ListBase): pass ================================================ FILE: topic/linked_list/python/head_tail_list.py ================================================ """ h t x --> y --> z """ from linked_list.python._helper import * class HeadTailList(ListBase): pass ================================================ FILE: topic/linked_list/python/linked_list__test.py ================================================ from _test.python import * from linked_list.python import * class TestLinkedList(TestBase): def _get_instance(self, LinkedList): ll = LinkedList() for i in range(10): ll.add(i) self.assertEqual( ','.join([str(i) for i in ll]), '0,1,2,3,4,5,6,7,8,9' ) return ll def _test_set(self, LinkedList): ll = self._get_instance(LinkedList) for i, val in ( (0, 11), (3, 12), (9, 15), (-1, 21), (-5, 25), (-10, 29), ): self.assertEqual(ll.set(val, i), val) self.assertEqual(ll.get(i), val) with self.assertRaises(IndexError): ll.set(31, 10) ll.set(31, 11) ll.set(31, -11) ll.set(31, -12) def _test_get(self, LinkedList): ll = self._get_instance(LinkedList) with self.assertRaises(IndexError): ll.get(10) ll.get(11) ll.get(15) ll.get(-11) ll.get(-15) self.assertEqual(ll.get(0), 0) self.assertEqual(ll.get(5), 5) self.assertEqual(ll.get(9), 9) self.assertEqual(ll.get(-1), 9) self.assertEqual(ll.get(-3), 7) self.assertEqual(ll.get(-6), 4) self.assertEqual(ll.get(-10), 0) def _test_add(self, LinkedList): ll = LinkedList() self.assertEqual(len(ll), 0) i = 0 for c in 'abcdefg': self.assertIsNone(ll.add(c)) self.assertEqual(len(ll), i + 1) self.assertEqual(ll.get(i), c) i += 1 for c in range(-1, -10, -1): self.assertIsNone(ll.add(c, c)) self.assertEqual(len(ll), i + 1) self.assertEqual(ll.get(c), c) i += 1 ll.add('Tail_A', -50) self.assertEqual(ll.get(-1), 'Tail_A') ll.add('Tail_B', 50) self.assertEqual(ll.get(-1), 'Tail_B') def _test_add_all(self, LinkedList): ll = LinkedList() self.assertEqual(len(ll), 0) with self.assertRaises(ValueError): ll.add_all(None) ll.add_all(123) self.assertIsNone(ll.add_all([i for i in range(5)])) self.assertEqual(len(ll), 5) self.assertEqual( ','.join([str(i) for i in ll]), '0,1,2,3,4' ) self.assertIsNone(ll.add_all([i for i in range(5, 10)], -3)) self.assertEqual(len(ll), 10) self.assertEqual( ','.join([str(i) for i in ll]), '0,1,2,5,6,7,8,9,3,4' ) ll.add_all('Tail_A', -50) self.assertEqual(ll.get(-1), 'A') ll.add_all('Tail_B', 50) self.assertEqual(ll.get(-1), 'B') def _test_remove(self, LinkedList): ll = self._get_instance(LinkedList) self.assertEqual(len(ll), 10) with self.assertRaises(IndexError): ll.remove(10) ll.remove(15) ll.remove(-11) ll.remove(-15) for i in range(2, -1, -1): self.assertEqual(ll.remove(i), i) self.assertEqual(len(ll), 7 + i) for i in range(2, 0, -1): self.assertEqual(ll.remove(-i), 10 - i) self.assertEqual(len(ll), 4 + i) with self.assertRaises(IndexError): ll.remove(6) ll.remove(-6) def _test_contains(self, LinkedList): ll = self._get_instance(LinkedList) self.assertTrue(ll.contains(0)) self.assertTrue(ll.contains(3)) self.assertTrue(ll.contains(9)) self.assertFalse(ll.contains(-1)) self.assertFalse(ll.contains(-5)) self.assertFalse(ll.contains(10)) self.assertFalse(ll.contains(15)) def _test_index(self, LinkedList): ll = self._get_instance(LinkedList) for i in range(10): self.assertEqual(ll.index(i), i) def _test_last_index(self, LinkedList): ll = self._get_instance(LinkedList) for i in range(10): self.assertEqual(ll.last_index(i), 10 - i) def _test_clear(self, LinkedList): ll = self._get_instance(LinkedList) self.assertEqual(len(ll), 10) ll.clear() self.assertEqual(len(ll), 0) def _test_clone(self, LinkedList): pass def _test_sort(self, LinkedList): pass def _run_list_test(self, LinkedList): self._test_set(LinkedList) self._test_get(LinkedList) self._test_add(LinkedList) self._test_add_all(LinkedList) self._test_remove(LinkedList) self._test_contains(LinkedList) self._test_index(LinkedList) self._test_last_index(LinkedList) self._test_clear(LinkedList) self._test_clone(LinkedList) self._test_sort(LinkedList) def test_cyclic_list(self): self._run_list_test(CyclicList) def test_cyclic_doubly_list(self): self._run_list_test(CyclicDoublyList) # def test_dummy_tail_list(self): # self._run_list_test(DummyTailList) # def test_dummy_tail_doubly_list(self): # self._run_list_test(DummyTailDoublyList) # def test_head_tail_list(self): # self._run_list_test(HeadTailList) # def test_head_tail_doubly_list(self): # self._run_list_test(HeadTailDoublyList) # def test_two_dummy_list(self): # self._run_list_test(TwoDummyList) # def test_two_dummy_doubly_list(self): # self._run_list_test(TwoDummyDoublyList) ================================================ FILE: topic/linked_list/python/two_dummy_doubly_list.py ================================================ """ D <-> x <-> y <-> z <-> d """ from linked_list.python._helper import * class TwoDummyDoublyList(ListBase): pass ================================================ FILE: topic/linked_list/python/two_dummy_list.py ================================================ """ D --> x --> y --> z <-- d """ from linked_list.python._helper import * class TwoDummyList(ListBase): pass ================================================ FILE: topic/problem_set/subarray_sum.md ================================================ ## Differences `leetcode/209_minimum_size_subarray_sum.py` - elements is non-negative - sum >= k - find min length `leetcode/325_maximum_size_subarray_sum_equals_k.py` - elements may be negative - sum == k - find max length `leetcode/560_subarray_sum_equals_k.py` - elements may be negative - sum == k - count how many subarrays `leetcode/862_shortest_subarray_with_sum_at_least_k.py` - elements may be negative - sum >= k - find min length ## Solutions - Non-negative => Two Pointers - Negative => Prefix Sum ================================================ FILE: topic/problem_set/substring_with_distinct_characters.md ================================================ - minimum length: ``` while cnt == k: # reduce window size if possible # and record it ``` - maximum length: ``` while cnt > k: # to evict invalid start # it's valid here, so record the maximum window size ``` ================================================ FILE: topic/range_query/python/__init__.py ================================================ ================================================ FILE: topic/range_query/python/_helper.py ================================================ ================================================ FILE: topic/range_query/python/binary_indexed_tree.py ================================================ # TODO ================================================ FILE: topic/range_query/python/interval_tree.py ================================================ # TODO ================================================ FILE: topic/range_query/python/prefix_sum.py ================================================ # TODO ================================================ FILE: topic/range_query/python/range_tree.py ================================================ # TODO ================================================ FILE: topic/range_query/python/segment_tree.py ================================================ # TODO ================================================ FILE: topic/searching/binary_search.md ================================================ Binary search ====== ```python >>> arr = [True, True, True, True, False, False] >>> left, mid, right = 0, 0, len(arr) - 1 # to prevent dead cycle >>> while left + 1 < right: ... ... # to avoid int overflow, but it could be ignored in `Python 3` ... mid = left + (right - left) // 2 ... ... if arr[mid]: ... left = mid ... else: ... right = mid # check again the end point, and fetch what we need >>> left if arr[left] else right 3 ``` ================================================ FILE: topic/searching/python/__init__.py ================================================ ================================================ FILE: topic/searching/python/_helper.py ================================================ from abc import ABC, abstractmethod class SearchBase(ABC): @classmethod @abstractmethod def search(cls, iterable, val): """ :type iterable: Iterable :type val: any :rtype: int """ pass ================================================ FILE: topic/sorting/python/__init__.py ================================================ from sorting.python.quick_sort import QuickSort from sorting.python.merge_sort import MergeSort ================================================ FILE: topic/sorting/python/_helper.py ================================================ from abc import ABC, abstractmethod import collections class SortBase(ABC): @classmethod @abstractmethod def sort(cls, iterable): """ :type iterable: Iterable :rtype: list """ pass @staticmethod def _is_valid_payload(iterable): """ :type iterable: Iterable :rtype: bool """ return isinstance(iterable, collections.Iterable) ================================================ FILE: topic/sorting/python/merge_sort.py ================================================ from sorting.python._helper import * class MergeSort(SortBase): @classmethod def sort(cls, iterable): """ :type iterable: Iterable :rtype: list """ if not cls._is_valid_payload(iterable): return [] res = list(iterable) n = len(iterable) cls._divide_conquer(res, 0, n - 1, [None] * n) return res @classmethod def _divide_conquer(cls, arr, start, end, tmp): if start >= end: return mid = (start + end) // 2 left, right = start, mid + 1 cls._divide_conquer(arr, left, mid, tmp) cls._divide_conquer(arr, right, end, tmp) idx = start while left <= mid and right <= end: if arr[left] < arr[right]: tmp[idx] = arr[left] left += 1 else: tmp[idx] = arr[right] right += 1 idx += 1 while left <= mid: tmp[idx] = arr[left] left += 1 idx += 1 while right <= end: tmp[idx] = arr[right] right += 1 idx += 1 for idx in range(start, end + 1): arr[idx] = tmp[idx] ================================================ FILE: topic/sorting/python/quick_sort.py ================================================ from sorting.python._helper import * class QuickSort(SortBase): """ remember to save pivot value in advance since the array is changing all the time """ @classmethod def sort(cls, iterable): """ :type iterable: Iterable :rtype: list """ if not cls._is_valid_payload(iterable): return [] res = list(iterable) n = len(iterable) cls._divide_conquer(res, 0, n - 1) return res @classmethod def _divide_conquer(cls, arr, start, end): if start >= end: return left, right = start, end pivot = arr[(start + end) // 2] while left <= right: while left <= right and arr[left] < pivot: left += 1 while left <= right and arr[right] > pivot: right -= 1 if left <= right: arr[left], arr[right] = arr[right], arr[left] left += 1 right -= 1 cls._divide_conquer(arr, start, right) cls._divide_conquer(arr, left, end) ================================================ FILE: topic/sorting/python/sorting__test.py ================================================ from _test.python import * from sorting.python import * class TestSorting(TestBase): CASES = ( (-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), (1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), (1, 0, 0, 0, -1, -1, -1, 0, 1, -1), (-2, -5, 3, 7, 6, 6, 0, -9, 7, 2), (-6, -3, -9, -1, 0, 2, 2, 8, 6), (18, 3, 18, 2, 15, 0, -12, -10, -12, -3), (-4, -18, -13, -10, -10, 0, 13, 5, 1, 1), (70, 91, 92, -42, -4, 13, -12, -94, -56, -70, 34, -97, 58, -14, -75), ) def _run_sorting_test(self, Sort): for case in self.CASES: a = list(case) b = Sort.sort(a) c = sorted(a) self.assertIsNot(a, b) self.assertEqual(b, c) def test_quick_sort(self): self._run_sorting_test(QuickSort) def test_merge_sort(self): self._run_sorting_test(MergeSort) ================================================ FILE: topic/system_design/latency.md ================================================ Latency Comparison Numbers ====== Sourced from [Latency Numbers Every Programmer Should Know | GitHub](https://gist.github.com/jboner/2841832) | Item | ns | us | ms | more | | :--------------------------------- | ----------: | ------: | ---: | :-------------------------- | | L1 cache reference | 0.5 | | | | | Branch mispredict | 5 | | | | | L2 cache reference | 7 | | | 14x L1 cache | | Mutex lock/unlock | 25 | | | | | Main memory reference | 100 | | | 20x L2 cache, 200x L1 cache | | Compress 1K bytes with Zippy | 3,000 | 3 | | | | Send 1K bytes over 1 Gbps network | 10,000 | 10 | | | | Read 4K randomly from SSD* | 150,000 | 150 | | ~1GB/sec SSD | | Read 1 MB sequentially from memory | 250,000 | 250 | | | | Round trip within same datacenter | 500,000 | 500 | | | | Read 1 MB sequentially from SSD* | 1,000,000 | 1,000 | 1 | ~1GB/sec SSD, 4X memory | | Disk seek | 10,000,000 | 10,000 | 10 | 20x datacenter roundtrip | | Read 1 MB sequentially from disk | 20,000,000 | 20,000 | 20 | 80x memory, 20X SSD | | Send packet CA->Netherlands->CA | 150,000,000 | 150,000 | 150 | | note that, - 1 ns = 10^-9 seconds - 1 us = 10^-6 seconds = 1,000 ns - 1 ms = 10^-3 seconds = 1,000 us = 1,000,000 ns ================================================ FILE: topic/traversal/python/__init__.py ================================================ from traversal.python.recursive_traversal import RecursiveTraversal from traversal.python.iterative_traversal import IterativeTraversal from traversal.python.morris_traversal import MorrisTraversal ================================================ FILE: topic/traversal/python/_helper.py ================================================ from abc import ABC, abstractmethod class TraversalBase(ABC): @classmethod @abstractmethod def preorder_traverse(cls, root, callback=None): """ :type root: TreeNode :type callback: function :rtype: void """ pass @classmethod @abstractmethod def inorder_traverse(cls, root, callback=None): """ :type root: TreeNode :type callback: function :rtype: void """ pass @classmethod @abstractmethod def postorder_traverse(cls, root, callback=None): """ :type root: TreeNode :type callback: function :rtype: void """ pass ================================================ FILE: topic/traversal/python/binary_tree_traversal__test.py ================================================ from _test.python import * from traversal.python import * from tree.python import BinaryTree class TestBinaryTreeTraversal(TestBase): CASES = { '{1,2,3,4,#,#,5,#,6,#,#,7,8}': { 'preorder': '1,2,4,6,7,8,3,5', 'inorder': '4,7,6,8,2,1,3,5', 'postorder': '7,8,6,4,2,5,3,1', }, '{1,#,2,#,3,#,4,#,5,#,6,#,7}': { 'preorder': '1,2,3,4,5,6,7', 'inorder': '1,2,3,4,5,6,7', 'postorder': '7,6,5,4,3,2,1', }, } trees = None def setUp(self): super(TestBinaryTreeTraversal, self).setUp() self.trees = { case: BinaryTree.deserialize(case) for case in self.CASES } def _run_traversal_test(self, Traversal): for traverse in ( Traversal.preorder_traverse, Traversal.inorder_traverse, Traversal.postorder_traverse, ): for i, o in self.CASES.items(): res = [] traverse( self.trees[i], callback=lambda val: res.append(str(val)) ) key = traverse.__name__.replace('_traverse', '') self.assertEqual(o[key], ','.join(res)) def test_recursive_traversal(self): self._run_traversal_test(RecursiveTraversal) def test_iterative_traversal(self): self._run_traversal_test(IterativeTraversal) def test_morris_traversal(self): self._run_traversal_test(MorrisTraversal) ================================================ FILE: topic/traversal/python/iterative_traversal.py ================================================ from traversal.python._helper import * class IterativeTraversal(TraversalBase): @classmethod def preorder_traverse(cls, root, callback=None): """ :type root: TreeNode :type callback: function :rtype: void """ node = root stack = [] while node or stack: while node: callback(node.val) stack.append(node) node = node.left node = stack.pop() node = node.right @classmethod def inorder_traverse(cls, root, callback=None): """ :type root: TreeNode :type callback: function :rtype: void """ node = root stack = [] while node or stack: while node: stack.append(node) node = node.left node = stack.pop() callback(node.val) node = node.right @classmethod def postorder_traverse(cls, root, callback=None): """ :type root: TreeNode :type callback: function :rtype: void """ node = last_visit = root stack = [] while node or stack: while node: stack.append(node) node = node.left node = stack[-1] if node.right and last_visit is not node.right: node = node.right else: stack.pop() callback(node.val) last_visit = node node = None ================================================ FILE: topic/traversal/python/morris_traversal.py ================================================ from traversal.python._helper import * from tree.python import TreeNode class MorrisTraversal(TraversalBase): """ REF: https://www.cnblogs.com/AnnieKim/archive/2013/06/15/MorrisTraversal.html """ @classmethod def preorder_traverse(cls, root, callback=None): """ :type root: TreeNode :type callback: function :rtype: void """ node = None while root: if not root.left: callback(root.val) root = root.right continue node = root.left while node.right and node.right is not root: node = node.right if node.right: node.right = None root = root.right else: callback(root.val) node.right = root root = root.left @classmethod def inorder_traverse(cls, root, callback=None): """ :type root: TreeNode :type callback: function :rtype: void """ node = None while root: if not root.left: callback(root.val) root = root.right continue node = root.left while node.right and node.right is not root: node = node.right if node.right: callback(root.val) node.right = None root = root.right else: node.right = root root = root.left @classmethod def postorder_traverse(cls, root, callback=None): """ :type root: TreeNode :type callback: function :rtype: void """ dummy = TreeNode(0) dummy.left = root root = dummy node = None while root: if not root.left: root = root.right continue node = root.left while node.right and node.right is not root: node = node.right if node.right: cls._postorder_reverse_visit(root.left, node, callback=callback) node.right = None root = root.right else: node.right = root root = root.left @classmethod def _postorder_reverse_visit(cls, from_node, to_node, callback): cls._postorder_reverse(from_node, to_node) node = to_node callback(node.val) while node and node is not from_node: node = node.right callback(node.val) cls._postorder_reverse(to_node, from_node) @classmethod def _postorder_reverse(cls, from_node, to_node): root = from_node node = from_node.right nxt = None while root is not to_node: nxt = node.right node.right = root root = node node = nxt ================================================ FILE: topic/traversal/python/recursive_traversal.py ================================================ from traversal.python._helper import * class RecursiveTraversal(TraversalBase): @classmethod def preorder_traverse(cls, root, callback=None): """ :type root: TreeNode :type callback: function :rtype: void """ if not root: return callback(root.val) cls.preorder_traverse(root.left, callback=callback) cls.preorder_traverse(root.right, callback=callback) @classmethod def inorder_traverse(cls, root, callback=None): """ :type root: TreeNode :type callback: function :rtype: void """ if not root: return cls.inorder_traverse(root.left, callback=callback) callback(root.val) cls.inorder_traverse(root.right, callback=callback) @classmethod def postorder_traverse(cls, root, callback=None): """ :type root: TreeNode :type callback: function :rtype: void """ if not root: return cls.postorder_traverse(root.left, callback=callback) cls.postorder_traverse(root.right, callback=callback) callback(root.val) ================================================ FILE: topic/tree/binary_tree.md ================================================ Binary Tree ====== ## Binary Tree ``` TODO ``` ## Binary Search Tree - the value of left child must be less than (<) the root node - the value of right child must be great than or equal to (>=) the root node - traversal BST by in-order, got a non-descending sequence (A[i+1] >= A[i]) ================================================ FILE: topic/tree/python/__init__.py ================================================ from tree.python.trie import * from tree.python.binary_tree import * ================================================ FILE: topic/tree/python/_helper.py ================================================ ================================================ FILE: topic/tree/python/binary_tree.py ================================================ class TreeNode: def __init__(self, val): self.val = val self.left = self.right = None class BinaryTree: EMPTY = '#' TMPL = '{{{}}}' # {{, }} is to escape brackets @classmethod def serialize(cls, root): """Encodes a tree to a single string. :type root: TreeNode :rtype: str """ if not root: return cls.TMPL.format('') values = [] queue = [root] for node in queue: if not node: values.append(cls.EMPTY) continue values.append(str(node.val)) queue.append(node.left) queue.append(node.right) while values[-1] == cls.EMPTY: values.pop() return cls.TMPL.format(','.join(values)) @classmethod def deserialize(cls, data): """Decodes your encoded data to tree. :type data: str :rtype: TreeNode """ if any(( not data, len(data) < 3, data[0] != '{', data[-1] != '}', data[1] in (cls.EMPTY, ','), )): return values = data[1:-1].split(',') i = 0 root = TreeNode(int(values[i])) queue = [root] for node in queue: for branch in ('left', 'right'): i += 1 if i >= len(values): break if values[i] == cls.EMPTY: continue setattr(node, branch, TreeNode(int(values[i]))) queue.append(getattr(node, branch)) return root ================================================ FILE: topic/tree/python/binary_tree__test.py ================================================ from _test.python import * from tree.python import BinaryTree class TestBinaryTree(TestBase): def test_serialization(self): case = '{1,2,3,4,#,#,5,#,6,#,#,7,8}' tree = BinaryTree.deserialize(case) self.assertIsNotNone(tree) self.assertEqual( case, BinaryTree.serialize(tree) ) left = right = None # level 1 self.assertEqual(1, tree.val) # level 2 left, right = tree.left, tree.right self.assertEqual(2, left.val) self.assertEqual(3, right.val) # level 3 self.assertIs(None, left.right) self.assertIs(None, right.left) left, right = left.left, right.right self.assertEqual(4, left.val) self.assertEqual(5, right.val) # level 4 self.assertIs(None, left.left) self.assertIs(None, right.left) self.assertIs(None, right.right) tree = left.right self.assertEqual(6, tree.val) # level 5 left, right = tree.left, tree.right self.assertEqual(7, left.val) self.assertEqual(8, right.val) # empty level self.assertIs(None, left.left) self.assertIs(None, left.right) self.assertIs(None, right.left) self.assertIs(None, right.right) ================================================ FILE: topic/tree/python/trie.py ================================================ import collections class TrieNode: def __init__(self): self.children = collections.defaultdict(TrieNode) self.end_of = None class Trie: def __init__(self): self.root = TrieNode() def __repr__(self): return repr(self.root) def put(self, word): if not self._is_str(word): return node = self.root for char in word: node = node.children[char] node.end_of = word def has_word(self, word): if not self._is_str(word): return False return self._search(word) is word def has_prefix(self, word): if not self._is_str(word): return False return self._search(word) is not False def get_node(self, char, node=None): if not node: node = self.root if char in node.children: return node.children[char] def _search(self, word): node = self.root if not word and node.end_of is None: return False for char in word: if char not in node.children: return False node = node.children[char] return node.end_of def _is_str(self, word): return isinstance(word, (str, bytes)) ================================================ FILE: topic/tree/python/trie__test.py ================================================ from _test.python import * from tree.python import Trie class TestTrie(TestBase): def test_init(self): trie = Trie() for attr, default_val in ( ('children', {}), ('end_of', None), ): self.assertTrue(hasattr(trie.root, attr)) self.assertEqual(getattr(trie.root, attr), default_val) def test_put(self): trie = Trie() node = None TEST_WORD = 'aabbccdefg' trie.put(TEST_WORD) for char in TEST_WORD: node = trie.get_node(char, node) if node.end_of == TEST_WORD: self.assertTrue(len(node.children) == 0) else: self.assertTrue(len(node.children) == 1) # for case in empty string '' self.assertEqual(trie.root.end_of, None) trie.put('') self.assertEqual(trie.root.end_of, '') def test_has_word_or_prefix(self): trie = Trie() trie.put('abc') self.assertTrue(trie.has_word('abc')) self.assertTrue(trie.has_prefix('a')) self.assertTrue(trie.has_prefix('ab')) self.assertTrue(trie.has_prefix('abc')) self.assertFalse(trie.has_word('')) self.assertFalse(trie.has_word('abd')) self.assertFalse(trie.has_word('abcd')) self.assertFalse(trie.has_prefix('abcd')) node = None trie.put('abd') for char in 'abd': node = trie.get_node(char, node) if char == 'a': self.assertTrue(len(node.children) == 1) elif char == 'b': self.assertTrue(len(node.children) == 2) elif node.end_of == 'abd': self.assertTrue(len(node.children) == 0) else: # should not visit here self.assertTrue(False) # for case in empty string '' self.assertEqual(trie.root.end_of, None) self.assertFalse(trie.has_word('')) self.assertFalse(trie.has_prefix('')) trie.put('') self.assertEqual(trie.root.end_of, '') self.assertTrue(trie.has_word('')) self.assertTrue(trie.has_prefix('')) def test_invalid_put(self): trie = Trie() for word, cnt, end_of in ( (None, 0, None), ({}, 0, None), ({'a': 1}, 0, None), ([], 0, None), ([1, 2, 3], 0, None), ('a', 1, None), (u'b', 2, None), (b'c', 3, None), ('', 3, ''), ): trie.put(word) self.assertTrue(len(trie.root.children) == cnt) self.assertTrue(trie.root.end_of == end_of)