├── .classpath ├── .gitignore ├── .project ├── LICENSE ├── README.md ├── bash ├── 192_word_count.sh └── 192_words.txt ├── c_plus_plus ├── findPeakElement.cpp └── rotateImage.cpp ├── input └── IntegerArray.txt ├── java ├── 130_Surrounded_Regions.java ├── 332_Reconstruct_Itinerary.java ├── 431_Encode_n-ary-Tree_to_Binary_Tree.java ├── Anagram.java ├── Atoi.java ├── BSTIterator.java ├── BinaryTree.java ├── BitOps.java ├── BucketSort.java ├── BuyAndSellStock.java ├── Candy.java ├── ClimbStairs.java ├── CloneGraph.java ├── Combination.java ├── CombinationSum.java ├── CopyRandomList.java ├── CountInversion.java ├── CourseSchedule.java ├── DungeonGame.java ├── EvaluateNotation.java ├── FactorialTrailingZeros.java ├── FindMedianFromDataStream_295.java ├── FindPeakElement.java ├── GameOfLife.java ├── GasStation.java ├── GenerateParenthese.java ├── GreyCode.java ├── HammingWeight.java ├── Histogram.java ├── InsertionSort.java ├── IntegerOps.java ├── Interval.java ├── JumpGame.java ├── JumpGameIV_1345.java ├── LRUCache.java ├── LargestNumber.java ├── LinkedListCycle.java ├── LinkedListDuplicate.java ├── LinkedListIntersection.java ├── ListNode.java ├── LongestCommonPrefix.java ├── LongestConsecutiveSequence.java ├── LongestSubstring.java ├── MajorityElement.java ├── MatrixSearch.java ├── MaxPoints.java ├── MaximalRectangle.java ├── MaximumGap.java ├── MaximumSubarray.java ├── MedianFinder.java ├── MergeIntervals.java ├── MergeKSortedList.java ├── MergeSort.java ├── MergeTwoLists.java ├── MinStack.java ├── MissingPositive.java ├── NQueen.java ├── NetworkDelayTime_743.java ├── NextPermutation.java ├── NimGame.java ├── NumberOfBST.java ├── Palindrome.java ├── PartitionList.java ├── PascalTriangle.java ├── PathSum.java ├── PeekingIterator.java ├── PermutationsII.java ├── PlusOne.java ├── Pow.java ├── QuickSort.java ├── RadixSort.java ├── RandomListNode.java ├── RemoveDuplicates.java ├── RemoveNthNode.java ├── ReorderList.java ├── ReverseLinkedList.java ├── ReverseWords.java ├── RotateList.java ├── RotatedArray.java ├── SearchRange.java ├── SingleNumber.java ├── SortColor.java ├── StackOps.java ├── StrStr.java ├── Subsequences.java ├── Subsets.java ├── SubstringOps.java ├── Sudoku.java ├── Sum.java ├── SurroundedRegion.java ├── SwapNodes.java ├── TextJustification.java ├── TreeNode.java ├── Triangle.java ├── TwoSum.java ├── UndirectedGraphNode.java ├── UniquePath.java ├── Utils.java ├── ValidNumber.java ├── WildcardMatching.java └── WordBreak.java └── python ├── 1009_complement_of_base_10_integer.py ├── 1010_combination_sum_divisible_by_60.py ├── 1011_minimal_capacity_to_ship_packages_in_D_days.py ├── 1013_partition_array_into_three_parts_with_equal_sum.py ├── 101_symmetric_tree.py ├── 1020_Number_of_Enclaves.py ├── 1021_remove_outermost_parentheses.py ├── 1026_Maximum_Difference_Between_Node_and_Ancestor.py ├── 102_binary_tree_BFS.py ├── 103_binary_zigzag_level_traversal.py ├── 1046_Last_Stone_Weight.py ├── 1047_remove_all_adjacent_duplicates_in_string.py ├── 1055_shortest_way_to_form_string.py ├── 1057_campus_bikes.py ├── 1060_missing_element_in_sorted_array.py ├── 1061_lexicographically_smallest_equivalent_string.py ├── 1066_campus_bikes_II.py ├── 1073_add_two_negabinary_numbers.py ├── 1081_Smallest_Subsequence_of_Distinct_Letters.py ├── 1087_brace_expansion.py ├── 1091_shortest_path_in_binary_matrix.py ├── 1093_statistics_from_a_large_sample.py ├── 1094_car_pooling.py ├── 1100_find_k-length_substrings_with_no_repeated_characters.py ├── 1101_earliest_moment_when_everyone_becomes_friends.py ├── 110_Balanced_Binary_Tree.py ├── 1114_Print_in_Order.py ├── 1120_maximum_average_subtree.py ├── 1135_connecting_cities_with_minimal_cost.py ├── 1150_check_majority_element_in_sorted_array.py ├── 1151_minimal_swaps_to_group_all_ones_together.py ├── 1155_number_of_dice_rolls_with_target_sum.py ├── 1162_as_far_from_land_as_possible.py ├── 1166_design_file_system.py ├── 1167_minimum_costs_to_connect_sticks.py ├── 1168_optimize_water_distribution_in_a_village.py ├── 116_populate_next_right_pointers.py ├── 1178_number_of_valid_words_in_each_puzzle.py ├── 1197_minimum_knight_moves.py ├── 1198_find_smallest_common_element_in_all_rows.py ├── 11_container_with_most_water.py ├── 1202_smallest_strings_with_swaps.py ├── 1209_remove_all_adjacent_duplicates_in_a_string_II.py ├── 1239_max_length_of_concatenated_string_with_unique_characters.py ├── 1249_minimum_remove_to_make_valid_parentheses.py ├── 1254_number_of_closed_islands.py ├── 1257_smallest_common_region.py ├── 1261_Find_Elements_in_Contaminated_Binary_Tree.py ├── 1288_remove_covered_intervals.py ├── 1293_shortest_path_in_grid_with_obstacles_elimination.py ├── 129_sum_root_to_leaf_numbers.py ├── 1302_deepest_leaves_sum.py ├── 1306_jump_game_III.py ├── 131_palindrome_partition.py ├── 1325_delete_leaves_nodes_with_a_given_value.py ├── 1328_break_a_palindrome.py ├── 132_Palindrome_Partition_II.py ├── 1345_Jump_Game_IV.py ├── 1353_maximum_number_of_events_that_can_be_attended.py ├── 1366_rank_teams_by_votes.py ├── 1372_longest_zigzag_path.py ├── 1376_time_needed_to_inform_all_employee.py ├── 1383_maximum_performance_of_team.py ├── 139_Word_Break.py ├── 140_Word_Break_II.py ├── 141_linked_list_cycle.py ├── 1420_build_arrays.py ├── 1439_find_kth_smallest_sum_of_matrix_with_sorted_row.py ├── 1448_count_good_nodes_in_binary_tree.py ├── 1456_maximum_number_of_vowels_in_a_substring_of_given_length.py ├── 145_binary_tree_postorder_traversal.py ├── 1469_Find_All_Lonely_Nodes.py ├── 147_Insertion_Sort_List.py ├── 148_sort_list.py ├── 1490_clone_N_ary_tree.py ├── 14_longest_common_prefix.py ├── 1506_find_root_of_N-ary_tree.py ├── 1508_range_sum_of_sorted_subarray_sums.py ├── 1510_Stone_Game_IV.py ├── 151_find_minimum_in_rotated_sorted_array.py ├── 1522_diameter_of_N-ary_tree.py ├── 152_Maximum_Product_Subarray.py ├── 1539_Kth_Missing_Positive_Number.py ├── 154_Find_Minimum_in_Rotated_Sorted_Array_II.py ├── 1559_detect_cycles_in_2D_grid.py ├── 1570_dot_product_of_two_sparse_vectors.py ├── 161_one_edit_distance.py ├── 1626_best_team_with_no_conflicts.py ├── 1631_path_with_minimal_efforts.py ├── 163_missing_ranges.py ├── 1650_lowest_common_ancestor_of_a_binary_tree_III.py ├── 1658_Maximum_Operations_to_Reduce_X_to_Zero.py ├── 165_compare_version_number.py ├── 1676_lowest_common_ancestor_for_multiple_nodes.py ├── 169_major_element.py ├── 1701_average_waiting_time.py ├── 170_Two_Sum_III_data_structure.py ├── 1710_maximum_capacity_on_truck.py ├── 1721_swapping_nodes_in_a_linked_list.py ├── 1722_minimum_hamming_distance_after_swapping_operations.py ├── 1730_shortest_path_to_get_food.py ├── 174_dungeon_game.py ├── 1751_maximum_number_of_events_that_can_be_attended_II.py ├── 1754_largest_merge_of_two_strings.py ├── 1759_count_number_of_homogenous_substrings.py ├── 176_Excel_Sheet_Column_Number.py ├── 17_letter_combination_of_a_phone_number.py ├── 1804_implement_trie_II.py ├── 189_Rotate_Array.py ├── 1953_maximum_number_of_weeks_for_which_you_can_work.py ├── 198_House_Robber.py ├── 199_Binary_Tree_with_Right_Side_View.py ├── 19_remove_nth_node_from_end_of_list.py ├── 1_two_sum.py ├── 200_Number_Of_Islands.py ├── 2034_stock_price_fluctuation.py ├── 206_reverse_linked_list.py ├── 2073_time_needed_to_buy_tickets.py ├── 207_course_schedule.py ├── 208_Implement_Trie_(Prefix_Tree).py ├── 20_Valid_Parentheses.py ├── 211_Add_and_Search_Word.py ├── 212_Word_Search_II.py ├── 215_kth_largest_element.py ├── 216_combination_sum_III.py ├── 220_Contains_Duplicates_III.py ├── 221_maximal_square.py ├── 224_basic_calculator.py ├── 227_Basic_Calculator_II.py ├── 228_summary_range.py ├── 229_major_elements_II.py ├── 229_minimum_size_subarray_sum.py ├── 230_kth_smallest_element_in_BST.py ├── 235_lowest_common_ancestor_in_a_binary_search_tree.py ├── 236_lowest_common_ancestor_of_a_binary_tree_no_parent_pointer.py ├── 238_product_of_array_except_self.py ├── 242_valid_anagrams.py ├── 249_group_shifted_strings.py ├── 252_meeting_room.py ├── 253_meeting_rooms_II.py ├── 257_binary_tree_paths.py ├── 261_graph_valid_tree.py ├── 265_paint_houses_II.py ├── 266_Palindrome_Permutation.py ├── 267_Palindrome_Permutations_II.py ├── 270_closest_binary_search_tree_value.py ├── 276_Paint_Fence.py ├── 279_perfect_squares.py ├── 283_move_zeroes.py ├── 285_Inorder_Successor_in_BST.py ├── 286_walls_and_gates.py ├── 289_game_of_life.py ├── 295_Find_Median_From_Data_Stream.py ├── 300_Longest_Increasing_Subsequence.py ├── 301_remove_invalid_parentheses.py ├── 306_Additive_Number.py ├── 309_Buy_and_Sell_Stock_with_Cooldown.py ├── 310_minimum_height_tree.py ├── 311_Sparse_Matrix_Multiplication.py ├── 314_binary_tree_vertical_order_traversal.py ├── 317_shortest_distance_from_all_buildings.py ├── 31_next_permutation.py ├── 320_Generalized_Abbreviation.py ├── 322_coin_change.py ├── 323_number_of_connected_components_in_graph.py ├── 325_maximum_size_subarray_sum_equals_K.py ├── 329_longest_increasing_path_in_matrix.py ├── 332_Reconstruct_Itinerary.py ├── 333_largest_BST_size.py ├── 337_house_robber_III.py ├── 339_Nest_List_Weight_Sum.py ├── 342_power_of_four.py ├── 345_reverse_vowels_of_string.py ├── 346_moving_average_from_data_stream.py ├── 347_Top_K_Frequent_Elements.py ├── 350_intersection_of_two_arrays_II.py ├── 353_design_snake_game.py ├── 359_logger_rate_limiter.py ├── 361_bomb_enemy.py ├── 364_Nested_List_Weight_Sum_II.py ├── 366_Find_Leaves_of_Binary_Tree.py ├── 367_valid_perfect_square.py ├── 370_range_addition.py ├── 375_Guess_Higher_or_Lower_Number_II.py ├── 377_combination_sum_IV.py ├── 382_Linked_List_Random_Node.py ├── 383_ransom_note.py ├── 384_Shuffle_Array.py ├── 38_count_and_say.py ├── 392_Is_Subsequence.py ├── 399_evaluate_division.py ├── 39_combination_sum.py ├── 402_Remove_K_Digits.py ├── 404_sum_of_left_leaves.py ├── 415_add_strings.py ├── 416_partition_equal_subset_sum.py ├── 419_battleships_in_a_board.py ├── 425_word_squares.py ├── 426_convert_binary_search_tree_to_sorted_doubly_linked_list.py ├── 42_trapping_rain_water.py ├── 430_flatten_multilevel_doubly_linked_list.py ├── 431_encode_n-ary-tree_to_binary_tree.py ├── 435_non_overlapping_intervals.py ├── 437_Path_Sum_III.py ├── 442_find_duplicates_in_an_array.py ├── 450_delete_node_in_BST.py ├── 452_minimal_number_of_arrows_to_burst_ballons.py ├── 456_132_pattern.py ├── 458_poor_pigs.py ├── 45_Jump_Game_II.py ├── 461_hamming_distance.py ├── 463_Island_Perimeter.py ├── 46_permutations.py ├── 470_implement_rand10_using_rand7.py ├── 47_permutations.py ├── 480_sliding_window_median.py ├── 490_The_Maze.py ├── 497_random_point_in_non-overlapping_rectangles.py ├── 513_find_bottom_left_tree_value.py ├── 516_longest_palindromic_subsequence.py ├── 518_coin_change_II.py ├── 523_Continuous_Subarray_Sum.py ├── 524_longest_word_in_a_dictionary_through_deleting.py ├── 526_beautiful_arrangement.py ├── 528_random_pick_with_weight.py ├── 530_min_absolute_difference_in_BST.py ├── 536_construct_binary_tree_from_string.py ├── 539_minimum_time_difference.py ├── 53_Maximum_Subarray.py ├── 542_01_matrix.py ├── 543_diameter_of_binary_tree.py ├── 547_number_of_provinces.py ├── 559_maximum_depth_N-ary_tree.py ├── 560_subarray_sum_equals_K.py ├── 562_longest_line_of_consecutive_one_in_a_matrix.py ├── 565_Nesting_Array.py ├── 56_merge_intervals.py ├── 572_subtree_of_another_tree.py ├── 583_delete_operation_for_two_strings.py ├── 593_valid_square.py ├── 5_longest_palindromic_substring.py ├── 606_constructing_string_from_binary_tree.py ├── 611_valid_triangle_number.py ├── 621_task_scheduler.py ├── 622_Design_Circular_Queue.py ├── 628_maximum_product_of_three_numbers.py ├── 637_average_of_levels_in_binary_tree.py ├── 641_Design_Circular_Deque.py ├── 647_palindromic_substrings.py ├── 648_replace_words.py ├── 64_minimum_path_sum.py ├── 652_find_duplicated_subtrees.py ├── 664_Strange_Printer.py ├── 667_Beautiful_Arrangements_II.py ├── 669_Trim_Binary_Search_Tree.py ├── 670_maximum_swap.py ├── 676_implement_magic_dictionary.py ├── 680_valid_palindrome_II.py ├── 684_Redundant_Connection.py ├── 696_count_binary_substrings.py ├── 697_degree_of_array.py ├── 698_Partition_To_K_Equal_Sum_Subsets.py ├── 69_sqrt.py ├── 701_Insert_into_a_Binary_Search_Tree.py ├── 705_design_hashset.py ├── 708_Insert_into_cyclic_sorted_list.py ├── 713_Subarray_Product_Less_Than_K.py ├── 718_maximum_length_repeated_subarray.py ├── 71_simplify_path.py ├── 720_longest_word_in_dictionary.py ├── 721_accounts_merge.py ├── 72_edit_distance.py ├── 735_asteroids_collision.py ├── 739_daily_temperatures.py ├── 73_set_matrix_zeroes.py ├── 743_Network_Delay_Time.py ├── 763_Partition_Labels.py ├── 767_reorganize_string.py ├── 772_basic_calculator_III.py ├── 774_Minimize_Maximum_Distance_to_Gas_Station.py ├── 783_min_difference_between_BST_nodes.py ├── 784_letter_case_permutation.py ├── 785_is_graph_bipartite.py ├── 787_Cheapest_Flights_within_K_stops.py ├── 78_subsets.py ├── 791_custom_sort_string.py ├── 792_number_of_matching_subsequence.py ├── 796_ratate_string.py ├── 79_Word_Search.py ├── 81_search_in_rotated_sorted_array_II.py ├── 821_shorest_distance_to_a_character.py ├── 826_most_profit_assign_work.py ├── 835_Image_Overlap.py ├── 841_rooms_and_keys.py ├── 844_backspace_string_compare.py ├── 849_maximize_distance_to_closest_person.py ├── 863_All_Nodes_Distance_K_in_Binary_Tree.py ├── 867_transpose_matrix.py ├── 897_Increasing_Order_Search_Tree.py ├── 89_gray_code.py ├── 905_sort_by_parity.py ├── 91_decode_ways.py ├── 921_minimal_add_to_make_parentheses_valid.py ├── 925_long_pressed_name.py ├── 92_reverse_linked_list_II.py ├── 931_minimum_falling_path_sum.py ├── 933_number_of_recent_calls.py ├── 938_range_sum_of_BST.py ├── 946_validate_stack_sequences.py ├── 949_Lagest_Time_for_Given_Digits.py ├── 94_binary_tree_inorder_traversal.py ├── 952_Largest_Component_Size_By_Common_Factor.py ├── 953_verifying_an_alien_dictionary.py ├── 956_tallest_billboard.py ├── 957_Prison_Cells_After_N_Days.py ├── 95_uniqueBST.py ├── 967_Numbers_With_Same_Consecutive_Differences.py ├── 969_pancake_sorting.py ├── 96_numberOfUniqueBST.py ├── 973_k_closest_points_to_origin.py ├── 974_Subarray_Sums_Divisable_by_K.py ├── 977_squares_of_a_sorted_array.py ├── 980_Unique_Paths_III.py ├── 981_time_based_key-value_store.py ├── 983_minimum_costs_for_tickets.py ├── 990_satisfiability_of_equality_equations.py ├── 994_rotting_oranges.py ├── 99_recovery_binary_search_tree.py ├── Search.py ├── SearchInsert.py ├── findPeakElement.py ├── insert_digit_5.py ├── lengthOfLongestSubstring.py ├── minimal_coin_changes.py ├── rotateMatrix.py └── searchMatrix.py /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .gitignore 3 | 4 | /bin/ 5 | 6 | *.class 7 | 8 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | LeetCode 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Lisong 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LeetCode Solutions # 2 | 3 | This is a project that I created to attempt some solutions for the questions on the LeetCode site. 4 | At the beginning, most of my solutions are coded in Java, though nowadays more and more are implemented in Python. 5 | 6 | 7 | ### Organization ### 8 | 9 | * A source file corresponds to a single question, which can contain several solutions. 10 | 11 | * In some cases, I aggregated several solutions into a single file to several questions that share the same context or spirit. 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /bash/192_word_count.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cat 192_words.txt | perl -pe 's/\\n//g' | perl -pe 's/\s+/\n/g' | sed '/^$/d' | sort | uniq -c | sort -k2 -n -r |awk '{print $2" "$1}' 4 | 5 | 6 | ## Breakdown of commands 7 | 8 | # 1. first perl, remove the '\n' strings 9 | # 2. second perl, replace the space with newline 10 | # 3. sed remove the empty lines 11 | # 4. sort the words 12 | # 5. uniq count the repeated words in adjacent lines 13 | # 6. sort the word counts 14 | # 7. print with format 15 | 16 | 17 | -------------------------------------------------------------------------------- /bash/192_words.txt: -------------------------------------------------------------------------------- 1 | a a b 2 | -------------------------------------------------------------------------------- /c_plus_plus/findPeakElement.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | A peak element is an element that is greater than its neighbors. 4 | 5 | Given an input array nums, where nums[i] ≠ nums[i+1], 6 | find a peak element and return its index. 7 | 8 | The array may contain multiple peaks, in that case return the index 9 | to any one of the peaks is fine. 10 | 11 | You may imagine that nums[-1] = nums[n] = -∞. 12 | 13 | @author: Lisong Guo 21 | #include 22 | using namespace std; 23 | 24 | class Solution { 25 | public: 26 | int findPeakElement(vector& nums) { 27 | 28 | int low = 0, high = nums.size()-1; 29 | 30 | if(high == -1){ 31 | // empty list 32 | return -1; 33 | } 34 | 35 | while(low < high){ 36 | int mid = (low + high)/2; 37 | if(nums[mid] > nums[mid+1]){ 38 | high = mid; 39 | } else { 40 | low = mid + 1; 41 | } 42 | } 43 | 44 | return low; 45 | } 46 | }; 47 | 48 | int main(){ 49 | Solution * s = new Solution(); 50 | 51 | int a [] = {1,2,3,1}; 52 | std::vector nums (a, a+sizeof(a)/sizeof(*a)); 53 | 54 | cout << s->findPeakElement(nums) << endl; 55 | 56 | } 57 | 58 | -------------------------------------------------------------------------------- /java/Atoi.java: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 4 | * Convert a string that represents a decimal number to an integer. 5 | * 6 | * Note: overflow isse 7 | * 8 | * @author Lisong Guo 9 | * @date Dec 30, 2014 10 | * 11 | */ 12 | 13 | public class Atoi { 14 | private static int maxDiv10 = Integer.MAX_VALUE/10; 15 | 16 | public int atoi(String s){ 17 | int num = 0; 18 | int i = 0; 19 | boolean isPos = true; 20 | 21 | while(s.charAt(i) == ' ') ++i; 22 | 23 | if(s.charAt(i) == '-') isPos = false; 24 | 25 | while(i < s.length()){ 26 | int digit = s.charAt(i) - '0'; 27 | 28 | if(0 <= digit && digit <= 9){ 29 | // overflow check 30 | if(num > maxDiv10 || (num == maxDiv10 && digit >= 8)){ 31 | return isPos ? Integer.MAX_VALUE : Integer.MIN_VALUE; 32 | } 33 | 34 | num = num * 10 + digit; 35 | } 36 | 37 | ++i; 38 | } 39 | 40 | return isPos ? num : -num; 41 | } 42 | 43 | 44 | public static void main(String[] args) { 45 | String s = "-13333323"; 46 | Atoi solution = new Atoi(); 47 | 48 | System.out.println(solution.atoi(s)); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /java/ClimbStairs.java: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 4 | You are climbing a stair case. It takes n steps to reach to the top. 5 | 6 | Each time you can either climb 1 or 2 steps. 7 | 8 | In how many distinct ways can you climb to the top? 9 | 10 | * @author Lisong Guo 11 | * @date Nov 22, 2014 12 | * 13 | */ 14 | public class ClimbStairs { 15 | 16 | /** 17 | * 1 2 18 | * 1 2 3 19 | * 1 2 3 5 20 | * @param n 21 | * @return 22 | */ 23 | public int climbStairs(int n) { 24 | if(n <= 0) return 0; 25 | 26 | if(n == 1) return 1; 27 | if(n == 2) return 2; 28 | 29 | int one_step_before = 1; 30 | int two_steps_before = 2; 31 | int all_ways = 0; 32 | 33 | // basically, it becomes fibonacci... 34 | for(int i=2; i 18 | * @date Jan 15, 2015 19 | * 20 | */ 21 | public class FindPeakElement { 22 | 23 | public int findPeakElement(int[] num) { 24 | int low=0, high=num.length, mid = 0; 25 | 26 | while(low < high){ 27 | mid = (low+high)/2; 28 | 29 | if(num[mid] > num[mid-1]){ 30 | 31 | } 32 | } 33 | 34 | return mid; 35 | } 36 | 37 | public static void main(String[] args) { 38 | 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /java/HammingWeight.java: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 4 | * @author Lisong Guo 5 | * @date Mar 15, 2015 6 | */ 7 | public class HammingWeight { 8 | 9 | // you need to treat n as an unsigned value 10 | public int hammingWeight(int n) { 11 | int c = 0; 12 | int mask = 1; 13 | 14 | while (n != 0) { 15 | if ((n & mask) == 1) { 16 | ++c; 17 | } 18 | //Should use the logical shift >>>, 19 | // instead of arithmetic shift >>, which puts 1 for left most bit, 20 | // if the number is negative. 21 | n >>>= 1; 22 | } 23 | 24 | return c; 25 | } 26 | 27 | public static void main(String[] args) { 28 | //int n = 2147483648; 29 | int n = 1; 30 | HammingWeight hw = new HammingWeight(); 31 | 32 | System.out.println(hw.hammingWeight(n)); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /java/IntegerOps.java: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Integer operations 4 | * 5 | * @author Lisong Guo 6 | * @date Dec 09, 2014 7 | * 8 | */ 9 | public class IntegerOps { 10 | 11 | /** 12 | * 13 | * Reverse digits of an integer. 14 | 15 | Example1: x = 123, return 321 16 | Example2: x = -123, return -321 17 | 18 | * @return 19 | */ 20 | public int reverse(int x) { 21 | long newInt = 0; 22 | 23 | while(x != 0){ 24 | int digit = x % 10; 25 | 26 | if(newInt != 0){ 27 | newInt *= 10; 28 | if(newInt > Integer.MAX_VALUE) 29 | return 0; // overflow; 30 | else if(newInt < Integer.MIN_VALUE) 31 | return 0; // underflow; 32 | } 33 | 34 | newInt += digit; 35 | x = x / 10; 36 | } 37 | 38 | return (int)newInt; 39 | } 40 | 41 | /** 42 | * @param args 43 | */ 44 | public static void main(String[] args) { 45 | //int x = 1534236469; // overflow test case 46 | int x = -2147483648; // underflow test case 47 | 48 | IntegerOps solution = new IntegerOps(); 49 | 50 | System.out.println(solution.reverse(x)); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /java/Interval.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | /** Definition for an interval. 5 | * 6 | */ 7 | 8 | public class Interval { 9 | int start; 10 | int end; 11 | 12 | Interval() { 13 | start = 0; 14 | end = 0; 15 | } 16 | 17 | Interval(int s, int e) { 18 | start = s; 19 | end = e; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /java/ListNode.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | /** 5 | * 6 | * Definition for singly-linked list. 7 | * 8 | */ 9 | public class ListNode { 10 | int val; 11 | ListNode next; 12 | 13 | ListNode(int x) { 14 | val = x; 15 | next = null; 16 | } 17 | 18 | 19 | /** 20 | * Reverse a linked list in place 21 | * 22 | * 1 -> 2 -> 3 -> NULL 23 | * 3 -> 2 -> 1 -> NULL 24 | * 25 | * @param head 26 | * @return 27 | */ 28 | public static ListNode reverse(ListNode head){ 29 | ListNode preIter = head, postIter = null; 30 | if(preIter == null) 31 | return null; 32 | 33 | postIter = preIter.next; 34 | 35 | while(postIter != null){ 36 | ListNode nextIter = postIter.next; 37 | postIter.next = preIter; // reverse the link 38 | 39 | // move on 40 | preIter = postIter; 41 | postIter = nextIter; 42 | } 43 | 44 | head.next = null; // end the list 45 | return preIter; 46 | } 47 | 48 | } 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /java/LongestCommonPrefix.java: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 4 | * Write a function to find the longest common prefix string amongst an array of strings. 5 | * 6 | * @author Lisong Guo 7 | * @date Jan 1, 2015 8 | * 9 | */ 10 | public class LongestCommonPrefix { 11 | 12 | public String longestCommonPrefix(String[] strs) { 13 | if(strs == null || strs.length == 0){ 14 | return ""; 15 | } 16 | 17 | int minLen = Integer.MAX_VALUE; 18 | for(String str : strs){ 19 | minLen = Math.min(minLen, str.length()); 20 | } 21 | 22 | boolean isFound = false; 23 | int pc = 0; 24 | 25 | while(! isFound && pc < minLen){ 26 | 27 | int c = strs[0].charAt(pc); 28 | 29 | for(int i=1; i 11 | * @date Dec 24, 2014 12 | * 13 | */ 14 | public class MajorityElement { 15 | 16 | 17 | public int majorityElement(int[] num) { 18 | if(num == null || num.length == 0) 19 | return 0; 20 | 21 | int count = 1; 22 | int iter = num[0]; 23 | int N = num.length; 24 | 25 | for(int i=1; i N/2) 40 | return iter; // early exit. Good for the performance. 41 | } 42 | 43 | return iter; 44 | } 45 | 46 | 47 | public static void main(String[] args) { 48 | int [] num = {2, 2, 1, 1, 1, 1}; 49 | 50 | MajorityElement solution = new MajorityElement(); 51 | 52 | System.out.println(solution.majorityElement(num)); 53 | } 54 | 55 | } 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /java/MaxPoints.java: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Given n points on a 2D plane, 4 | * find the maximum number of points that lie on the same straight line. 5 | * 6 | * @author Lisong Guo 7 | * @date Jan 12, 2014 8 | * 9 | */ 10 | public class MaxPoints { 11 | 12 | 13 | public static void main(String[] args) { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /java/MaximumGap.java: -------------------------------------------------------------------------------- 1 | import java.util.Arrays; 2 | 3 | /** 4 | * 5 | * @author Lisong Guo 6 | * @date Jan 02, 2014 7 | * 8 | */ 9 | public class MaximumGap { 10 | 11 | 12 | public int maximumGap(int[] num) { 13 | if(num.length < 2){ 14 | return 0; 15 | } 16 | 17 | Arrays.sort(num); 18 | 19 | int max_gap = Integer.MIN_VALUE; 20 | for(int i=1; i max_gap ? gap : max_gap; 23 | } 24 | 25 | return max_gap; 26 | } 27 | 28 | // another solution would be bucket sort. 29 | 30 | public static void main(String[] args) { 31 | int [] num = {1, 4, 5, 3}; 32 | 33 | MaximumGap solution = new MaximumGap(); 34 | System.out.println(solution.maximumGap(num)); 35 | 36 | } 37 | 38 | } 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /java/MaximumSubarray.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | /** 4 | * 5 | * Find the contiguous subarray within an array (containing at least one number) 6 | * which has the largest sum. 7 | 8 | For example, given the array [−2,1,−3,4,−1,2,1,−5,4], 9 | 10 | j=1 -2 1 -3 4 -1 2 1 -5 4 A 11 | 12 | j=2 -1 -2 1 3 1 3 -4 -1 13 | j=3 -4 2 0 5 2 -2 0 14 | j=4 15 | 16 | the contiguous subarray [4,−1,2,1] has the largest sum = 6. 17 | * 18 | * @author Lisong Guo 19 | * @date Dec 11, 2014 20 | * 21 | */ 22 | public class MaximumSubarray { 23 | 24 | 25 | /** 26 | * dynamic programming. 27 | */ 28 | public int maxSubArray(int[] A) { 29 | if(A.length == 0){ 30 | return 0; 31 | } 32 | 33 | int [] dp = new int[A.length]; 34 | 35 | int max_sum = A[0]; 36 | dp[0] = A[0]; 37 | 38 | for(int i=1; i 13 | * @date Nov 12, 2014 14 | * 15 | * 16 | */ 17 | 18 | public class MergeSort { 19 | 20 | public ListNode mergeSort(ListNode head){ 21 | 22 | return head; 23 | } 24 | 25 | /** 26 | * @param args 27 | */ 28 | public static void main(String[] args) { 29 | // TODO Auto-generated method stub 30 | 31 | } 32 | 33 | } 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /java/MissingPositive.java: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 4 | Given an unsorted integer array, find the first missing positive integer. 5 | 6 | For example, 7 | Given [1,2,0] return 3, 8 | and [3,4,-1,1] return 2. 9 | 10 | Your algorithm should run in O(n) time and uses constant space. 11 | 12 | * @author Lisong Guo 13 | * @date Dec 28, 2014 14 | * 15 | */ 16 | public class MissingPositive { 17 | 18 | /** 19 | * https://oj.leetcode.com/discuss/4220/a-very-nice-solution-from-ants-aasma-%40stackoverflow 20 | * 21 | */ 22 | public int firstMissingPositive(int[] A) { 23 | int n = A.length; 24 | 25 | for(int i=0; i 0 && num < n && A[num-1] != num){ 30 | // swap the values between A[i] and A[num-1] 31 | int temp = A[i]; 32 | A[i] = A[num-1]; 33 | A[num-1] = temp; 34 | 35 | num = A[i]; 36 | } 37 | } 38 | 39 | for(int i=0; i 7 | * @date Jan 04, 2015 8 | * 9 | */ 10 | public class Pow { 11 | 12 | public double pow(double x, int n) { 13 | boolean isNeg = false; 14 | 15 | if(n == 0){ 16 | return 1; 17 | }else if(n == 1 || x == 0){ 18 | return x; 19 | }else if(n < 0){ 20 | isNeg = true; 21 | n = -n; 22 | } 23 | 24 | double res = 1.0; 25 | if(n % 2 == 1){ 26 | res = x; 27 | } 28 | 29 | double half = pow(x*x, n/2); 30 | res = res * half; 31 | 32 | if(isNeg){ 33 | return 1/res; 34 | }else{ 35 | return res; 36 | } 37 | } 38 | 39 | public static void main(String[] args) { 40 | Pow solution = new Pow(); 41 | 42 | System.out.println(solution.pow(0, -1)); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /java/RandomListNode.java: -------------------------------------------------------------------------------- 1 | 2 | class RandomListNode { 3 | int label; 4 | RandomListNode next, random; 5 | 6 | RandomListNode(int x) { 7 | this.label = x; 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /java/RemoveDuplicates.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | /** 4 | * 5 | Given a sorted array, remove the duplicates in place 6 | such that each element appear only once and return the new length. 7 | 8 | Do not allocate extra space for another array, 9 | you must do this in place with constant memory. 10 | 11 | For example, 12 | Given input array A = [1,1,2], 13 | Your function should return length = 2, and A is now [1,2]. 14 | 15 | * 16 | * @author Lisong Guo 17 | * @date Dec 29, 2014 18 | * 19 | */ 20 | public class RemoveDuplicates { 21 | 22 | public int removeDuplicates(int[] A) { 23 | if(A.length == 0){ 24 | return 0; 25 | } 26 | 27 | int fillCur = 0, iterCur = 1; 28 | 29 | for(; iterCur < A.length; ++iterCur){ 30 | if(A[iterCur] != A[fillCur]){ 31 | A[++fillCur] = A[iterCur]; 32 | } 33 | } 34 | 35 | return fillCur+1; 36 | } 37 | 38 | public static void main(String[] args) { 39 | //int [] A = {1, 1, 1, 2, 2}; 40 | int [] A = {}; 41 | 42 | RemoveDuplicates solution = new RemoveDuplicates(); 43 | 44 | int newLen = solution.removeDuplicates(A); 45 | System.out.println(newLen); 46 | 47 | for(int i=0; i 13 | * @date Dec 27, 2014 14 | * 15 | */ 16 | public class SingleNumber { 17 | 18 | public int singleNumber(int[] A) { 19 | int single = 0x0; 20 | 21 | for(int i=0; i 18 | * @date Dec 30, 2014 19 | * 20 | */ 21 | 22 | public class TwoSum { 23 | 24 | public int[] twoSum(int[] numbers, int target) { 25 | Hashtable numTable = 26 | new Hashtable(); 27 | int [] res = new int[2]; 28 | 29 | for(int i=0; i neighbors; 9 | 10 | UndirectedGraphNode(int x) { 11 | label = x; 12 | neighbors = new ArrayList(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /python/1009_complement_of_base_10_integer.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def bitwiseComplement(self, n: int) -> int: 4 | bitmask = 1 5 | result, remain = n, n 6 | 7 | # exception 8 | if n == 0: 9 | return 1 10 | 11 | while remain: 12 | result = result ^ bitmask 13 | 14 | bitmask = bitmask << 1 15 | remain = remain >> 1 16 | 17 | return result -------------------------------------------------------------------------------- /python/1010_combination_sum_divisible_by_60.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def numPairsDivisibleBy60(self, time: List[int]) -> int: 3 | 4 | count = 0 5 | 6 | modulo_table = defaultdict(int) 7 | 8 | for num in time: 9 | modulo_table[num % 60] += 1 10 | 11 | for factor in [0, 30]: 12 | if factor in modulo_table: 13 | count += int(modulo_table[factor] * (modulo_table[factor]-1) / 2) 14 | 15 | for factor in range(1, 30): 16 | if factor in modulo_table: 17 | count += modulo_table[factor] * modulo_table[60 - factor] 18 | 19 | return count 20 | -------------------------------------------------------------------------------- /python/1011_minimal_capacity_to_ship_packages_in_D_days.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def shipWithinDays(self, weights: List[int], D: int) -> int: 3 | 4 | def possible(capacity): 5 | ship_weight = 0 6 | ship_count = 1 7 | for weight in weights: 8 | if weight > capacity: 9 | return False 10 | elif ship_weight + weight <= capacity: 11 | ship_weight += weight 12 | elif ship_count > D: 13 | return False 14 | else: 15 | # add a new ship 16 | ship_count += 1 17 | ship_weight = weight 18 | return (ship_count <= D) 19 | 20 | 21 | low = max(weights) 22 | high = sum(weights) 23 | while low < high: 24 | mid = (low + high) // 2 25 | is_ok = possible(mid) 26 | if is_ok: 27 | high = mid 28 | else: 29 | low = mid + 1 30 | 31 | return low -------------------------------------------------------------------------------- /python/1013_partition_array_into_three_parts_with_equal_sum.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def canThreePartsEqualSum(self, arr: List[int]) -> bool: 4 | 5 | total_sum = sum(arr) 6 | 7 | if total_sum % 3 != 0: 8 | return False 9 | 10 | subarray_sum = total_sum / 3 11 | group_sum, group_count = 0, 0 12 | 13 | for num in arr: 14 | group_sum += num 15 | if group_sum == subarray_sum: 16 | group_count += 1 17 | group_sum = 0 18 | 19 | return group_count >= 3 20 | -------------------------------------------------------------------------------- /python/1020_Number_of_Enclaves.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def numEnclaves(self, A: List[List[int]]) -> int: 3 | 4 | rows, cols = len(A), len(A[0]) 5 | 6 | borders = [] 7 | total_land = 0 8 | 9 | def isBoundary(row, col): 10 | return (row == 0) or (row==rows-1) or (col==0) or (col==cols-1) 11 | 12 | for row in range(rows): 13 | for col in range(cols): 14 | if A[row][col] == 1: 15 | total_land += 1 16 | if isBoundary(row, col): 17 | borders.append((row, col)) 18 | A[row][col] = -1 19 | 20 | queue = deque(borders) 21 | total_land -= len(borders) 22 | 23 | # BFS traversal, starting from the borders 24 | # reaching the inner land. 25 | while queue: 26 | row, col = queue.popleft() 27 | for ro, co in [(-1, 0), (0, 1), (1, 0), (0, -1)]: 28 | new_row, new_col = row + ro, col + co 29 | if (0 <= new_row < rows) and (0 <= new_col < cols): 30 | if A[new_row][new_col] == 1: 31 | queue.append((new_row, new_col)) 32 | A[new_row][new_col] = -1 33 | total_land -= 1 34 | 35 | return total_land 36 | 37 | 38 | -------------------------------------------------------------------------------- /python/1021_remove_outermost_parentheses.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def removeOuterParentheses(self, s: str) -> str: 3 | 4 | stack = [] 5 | start = 0 6 | primitives = [] 7 | # retrieve all the primitives 8 | for index, letter in enumerate(s): 9 | if letter == "(": 10 | stack.append(letter) 11 | else: 12 | stack.pop() 13 | if len(stack) == 0: 14 | primitives.append(s[start:index+1]) 15 | start = index + 1 16 | 17 | # strip the outermost parentheses 18 | stripped = [] 19 | for primitive in primitives: 20 | stripped.append(primitive[1:-1]) 21 | 22 | return "".join(stripped) 23 | 24 | -------------------------------------------------------------------------------- /python/1046_Last_Stone_Weight.py: -------------------------------------------------------------------------------- 1 | """ 2 | We have a collection of rocks, each rock has a positive integer weight. 3 | 4 | Each turn, we choose the two heaviest rocks and smash them together. 5 | Suppose the stones have weights x and y with x <= y. The result of this smash is: 6 | 7 | If x == y, both stones are totally destroyed; 8 | If x != y, the stone of weight x is totally destroyed, 9 | and the stone of weight y has new weight y-x. 10 | 11 | At the end, there is at most 1 stone left. 12 | Return the weight of this stone (or 0 if there are no stones left.) 13 | 14 | """ 15 | 16 | class Solution: 17 | def lastStoneWeight(self, stones: List[int]) -> int: 18 | import heapq 19 | 20 | # Since in Python, the heap is implemented as minimum heap, 21 | # in order to have the maximum heap, we applied a trick here, 22 | # i.e. we applied the negation on the original values. 23 | neg_stones = [-stone for stone in stones] 24 | heapq.heapify(neg_stones) 25 | 26 | while len(neg_stones) > 1: 27 | y = heapq.heappop(neg_stones) 28 | x = heapq.heappop(neg_stones) 29 | if x != y: 30 | #new_weight = -((-y) - (-x)) 31 | new_weight = y - x 32 | 33 | heapq.heappush(neg_stones, new_weight) 34 | 35 | if neg_stones: 36 | return - neg_stones[0] 37 | else: 38 | return 0 -------------------------------------------------------------------------------- /python/1047_remove_all_adjacent_duplicates_in_string.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def removeDuplicates(self, s: str) -> str: 3 | stack = [] 4 | for letter in s: 5 | if not stack: 6 | stack.append(letter) 7 | else: 8 | top = stack[-1] 9 | if top == letter: 10 | stack.pop() 11 | else: 12 | stack.append(letter) 13 | 14 | return "".join(stack) 15 | -------------------------------------------------------------------------------- /python/1061_lexicographically_smallest_equivalent_string.py: -------------------------------------------------------------------------------- 1 | class LetterUnionFind: 2 | 3 | def __init__(self): 4 | self.parent = [i for i in range(26)] 5 | self.base = ord('a') 6 | 7 | def find(self, letter): 8 | lid = ord(letter) - self.base 9 | if lid != self.parent[lid]: 10 | parent_letter = chr(self.base + self.parent[lid]) 11 | self.parent[lid] = self.find(parent_letter) 12 | return self.parent[lid] 13 | 14 | def union(self, la, lb): 15 | ga = self.find(la) 16 | gb = self.find(lb) 17 | if ga != gb: 18 | # join the group with larger letter to the one with smaller one 19 | if ga > gb: 20 | self.parent[ga] = gb 21 | else: 22 | self.parent[gb] = ga 23 | 24 | 25 | class Solution: 26 | def smallestEquivalentString(self, s1: str, s2: str, baseStr: str) -> str: 27 | 28 | luf = LetterUnionFind() 29 | 30 | for la, lb in zip(s1, s2): 31 | luf.union(la, lb) 32 | 33 | ret = [] 34 | base = ord('a') 35 | for letter in baseStr: 36 | parent_id = luf.find(letter) 37 | to_letter = chr(parent_id + base) 38 | ret.append(to_letter) 39 | 40 | return "".join(ret) 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /python/1073_add_two_negabinary_numbers.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def addNegabinary(self, arr1: List[int], arr2: List[int]) -> List[int]: 3 | 4 | p1, p2 = len(arr1)-1, len(arr2)-1 5 | carry = 0 6 | result = [] 7 | 8 | while p1 >= 0 or p2 >= 0 or carry: 9 | 10 | bit_sum = 0 11 | 12 | if p1 >= 0: 13 | bit_sum += arr1[p1] 14 | p1 -= 1 15 | 16 | if p2 >= 0: 17 | bit_sum += arr2[p2] 18 | p2 -= 1 19 | 20 | bit_sum += carry 21 | result.append(bit_sum % 2) 22 | 23 | # the difference between base 2 and base -2 24 | # The parenthese (bit_sum // 2) is absolutely necessary! 25 | carry = - (bit_sum // 2) 26 | 27 | # remove the leading zeros 28 | while len(result) > 1 and result[-1] == 0: 29 | result.pop() 30 | 31 | return result[::-1] 32 | -------------------------------------------------------------------------------- /python/1081_Smallest_Subsequence_of_Distinct_Letters.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def smallestSubsequence(self, text: str) -> str: 3 | 4 | final_list = [] 5 | seen = set() 6 | 7 | last_seen = defaultdict(int) 8 | for index, letter in enumerate(text): 9 | last_seen[letter] = index 10 | 11 | for index, letter in enumerate(text): 12 | 13 | if letter in seen: 14 | continue 15 | 16 | # greedily deduplicate the redundant letters 17 | while final_list: 18 | prev_letter = final_list[-1] 19 | if letter < prev_letter and last_seen[prev_letter] > index: 20 | final_list.pop() 21 | seen.discard(prev_letter) 22 | else: 23 | break 24 | 25 | final_list.append(letter) 26 | seen.add(letter) 27 | 28 | return "".join(final_list) -------------------------------------------------------------------------------- /python/1091_shortest_path_in_binary_matrix.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def shortestPathBinaryMatrix(self, grid: List[List[int]]) -> int: 3 | 4 | N = len(grid) 5 | 6 | def bfs(): 7 | 8 | queue = [(0, 0)] 9 | distance = 0 10 | is_found = False 11 | 12 | while queue: 13 | next_queue = [] 14 | distance += 1 15 | 16 | while queue: 17 | row, col = queue.pop() 18 | 19 | for ro, co in [(1, 1), (1, 0), (0, 1), (-1, 0), 20 | (0, -1), (1, -1), (-1, -1), (-1, 1)]: 21 | new_row, new_col = row + ro, col + co 22 | if new_row < 0 or new_row >= N or \ 23 | new_col < 0 or new_col >= N: 24 | continue 25 | 26 | if row == N-1 and col == N-1: 27 | return distance 28 | 29 | if grid[new_row][new_col] == 0: 30 | grid[new_row][new_col] = 1 31 | next_queue.append((new_row, new_col)) 32 | queue = next_queue 33 | 34 | return -1 35 | 36 | if grid[0][0] != 0: 37 | return -1 38 | 39 | if N == 1: 40 | return 1 41 | 42 | grid[0][0] = 1 43 | distance = bfs() 44 | return distance 45 | -------------------------------------------------------------------------------- /python/1094_car_pooling.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def carPooling(self, trips: List[List[int]], capacity: int) -> bool: 4 | 5 | 6 | capacity_changes = [] 7 | 8 | for psg, start, end in trips: 9 | # capacity decrease at pickup and increase at dropoff 10 | # for overlapping intervals, we should drop-off first then pickup. 11 | # as a result, the second element should also play a role in ordering 12 | # Note: we must priroitize the dropoff before pickup 13 | capacity_changes.append((start, psg)) 14 | capacity_changes.append((end, -psg)) 15 | 16 | heapq.heapify(capacity_changes) 17 | 18 | used_capacity = 0 19 | while capacity_changes: 20 | timestamp, capacity_delta = heapq.heappop(capacity_changes) 21 | used_capacity += capacity_delta 22 | 23 | if used_capacity > capacity: 24 | return False 25 | 26 | return True 27 | -------------------------------------------------------------------------------- /python/1100_find_k-length_substrings_with_no_repeated_characters.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def numKLenSubstrNoRepeats(self, s: str, k: int) -> int: 3 | if len(s) < k: 4 | return 0 5 | count = 0 6 | 7 | slide_window = defaultdict(int) 8 | for index, letter in enumerate(s): 9 | if index < k: 10 | slide_window[letter] += 1 11 | else: 12 | slide_window[letter] += 1 13 | popped = s[index-k] 14 | slide_window[popped] -= 1 15 | if slide_window[popped] == 0: 16 | del slide_window[popped] 17 | 18 | # check the characters once the sliding window is fully filled. 19 | if index >= k-1 and len(slide_window) == k: 20 | count += 1 21 | 22 | return count -------------------------------------------------------------------------------- /python/1135_connecting_cities_with_minimal_cost.py: -------------------------------------------------------------------------------- 1 | class DisjointSet: 2 | 3 | def __init__(self, N): 4 | # the id starts from 1, instead of 0 5 | self.parent = [i for i in range(N+1)] 6 | 7 | def find(self, a): 8 | if self.parent[a] != a: 9 | self.parent[a] = self.find(self.parent[a]) 10 | return self.parent[a] 11 | 12 | def union(self, a, b): 13 | pa, pb = self.find(a), self.find(b) 14 | if pa != pb: 15 | # attach the group of pb to pa 16 | self.parent[pb] = pa 17 | return pa 18 | 19 | 20 | class Solution: 21 | def minimumCost(self, N: int, connections: List[List[int]]) -> int: 22 | 23 | # at least we need N-1 edges to connect N vetex 24 | if len(connections) < N-1: 25 | return -1 26 | 27 | connections.sort(key = lambda x: x[2]) 28 | ds = DisjointSet(N) 29 | 30 | components = N 31 | total_cost = 0 32 | for (city1, city2, cost) in connections: 33 | if components == 1: 34 | break 35 | 36 | group1, group2 = ds.find(city1), ds.find(city2) 37 | if group1 != group2: 38 | ds.union(group1, group2) 39 | components -= 1 40 | total_cost += cost 41 | 42 | return -1 if components != 1 else total_cost 43 | 44 | -------------------------------------------------------------------------------- /python/1150_check_majority_element_in_sorted_array.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def isMajorityElement(self, nums: List[int], target: int) -> bool: 3 | 4 | # locate the target with the binary search 5 | low, high = 0, len(nums) 6 | while low < high: 7 | mid = (high - low) // 2 + low 8 | if nums[mid] < target: 9 | low = mid + 1 10 | elif nums[mid] > target: 11 | high = mid 12 | else: 13 | break 14 | 15 | if nums[mid] != target: 16 | return False 17 | 18 | # search around the located target for duplicate elements 19 | count = 1 20 | low, high = mid - 1, mid + 1 21 | while low >= 0: 22 | if nums[low] != target: 23 | break 24 | low -= 1 25 | count += 1 26 | 27 | while high < len(nums): 28 | if nums[high] != target: 29 | break 30 | high += 1 31 | count += 1 32 | 33 | return count > len(nums) / 2 34 | 35 | -------------------------------------------------------------------------------- /python/1151_minimal_swaps_to_group_all_ones_together.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def minSwaps(self, data: List[int]) -> int: 3 | 4 | ones = 0 5 | for bit in data: 6 | if bit == 1: 7 | ones += 1 8 | 9 | if ones in [0, 1, len(data)]: 10 | return 0 11 | 12 | swaps = 0 13 | slide_window = deque(data[0:ones]) 14 | for bit in slide_window: 15 | if bit == 0: 16 | swaps += 1 17 | 18 | min_swaps = swaps 19 | for bit in data[ones:]: 20 | 21 | slide_window.append(bit) 22 | if bit == 0: 23 | swaps += 1 24 | 25 | if slide_window.popleft() == 0: 26 | swaps -= 1 27 | 28 | min_swaps = min(min_swaps, swaps) 29 | 30 | return min_swaps 31 | -------------------------------------------------------------------------------- /python/1155_number_of_dice_rolls_with_target_sum.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def numRollsToTarget(self, d: int, f: int, target: int) -> int: 3 | 4 | modulo = 10 ** 9 + 7 5 | 6 | @functools.lru_cache(maxsize=None) 7 | def dp(dice_index, remain): 8 | 9 | if dice_index == d: 10 | return (remain == 0) 11 | elif remain < 0: 12 | return 0 13 | 14 | combs = 0 15 | for curr_dice in range(1, f+1): 16 | increment = dp(dice_index+1, remain - curr_dice) 17 | combs = (combs + increment) % modulo 18 | 19 | return combs 20 | 21 | return dp(0, target) 22 | 23 | 24 | class SolutionListComprehension: 25 | def numRollsToTarget(self, d: int, f: int, target: int) -> int: 26 | 27 | modulo = 10 ** 9 + 7 28 | 29 | @functools.lru_cache(maxsize=None) 30 | def dp(dice_index, remain): 31 | 32 | if dice_index == d: 33 | return (remain == 0) 34 | elif remain < 0: 35 | return 0 36 | 37 | return sum([dp(dice_index+1, remain - num) for num in range(1, f+1)]) % modulo 38 | 39 | return dp(0, target) 40 | -------------------------------------------------------------------------------- /python/1162_as_far_from_land_as_possible.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def maxDistance(self, grid: List[List[int]]) -> int: 3 | rows, cols = len(grid), len(grid[0]) 4 | 5 | queue = deque([]) 6 | visited = [[False] * cols for _ in range(rows)] 7 | 8 | # treat all lands as a Unit 9 | # run BFS from all lands, the water that is reached at the end is the farest one 10 | for row in range(rows): 11 | for col in range(cols): 12 | if grid[row][col] == 1: 13 | queue.append((row, col, 0)) 14 | visited[row][col] = True 15 | 16 | if len(queue) == rows * cols: 17 | # all lands, no water 18 | return -1 19 | 20 | steps = - 1 21 | while queue: 22 | row, col, steps = queue.popleft() 23 | 24 | for r_offset, c_offset in [(0, 1), (1, 0), (0, -1), (-1, 0)]: 25 | new_row, new_col = row + r_offset, col + c_offset 26 | if new_row < 0 or new_row >= rows or new_col < 0 or new_col >= cols: 27 | continue 28 | 29 | if not visited[new_row][new_col] and grid[new_row][new_col] == 0: 30 | visited[new_row][new_col] = True 31 | queue.append((new_row, new_col, steps + 1)) 32 | 33 | return steps 34 | -------------------------------------------------------------------------------- /python/1166_design_file_system.py: -------------------------------------------------------------------------------- 1 | class FileSystem: 2 | 3 | def __init__(self): 4 | self.path_map = {} 5 | 6 | def createPath(self, path: str, value: int) -> bool: 7 | if path == "" or path == "/" or path[0] != "/" or path in self.path_map: 8 | # invalid path or path exists already 9 | return False 10 | 11 | folders = path.split("/") 12 | parent_path = "" 13 | for folder in folders[1:-1]: 14 | parent_path = parent_path + "/" + folder 15 | if parent_path not in self.path_map: 16 | return False 17 | 18 | self.path_map[path] = value 19 | return True 20 | 21 | def get(self, path: str) -> int: 22 | if path in self.path_map: 23 | return self.path_map[path] 24 | else: 25 | return -1 26 | 27 | 28 | # Your FileSystem object will be instantiated and called as such: 29 | # obj = FileSystem() 30 | # param_1 = obj.createPath(path,value) 31 | # param_2 = obj.get(path) -------------------------------------------------------------------------------- /python/1167_minimum_costs_to_connect_sticks.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def connectSticks(self, sticks: List[int]) -> int: 3 | 4 | heapq.heapify(sticks) 5 | 6 | # Greedily merge two sticks together 7 | # since the sticks merged earlier would accumulate the costs over the time 8 | costs = 0 9 | while len(sticks) > 1: 10 | x = heapq.heappop(sticks) 11 | y = heapq.heappop(sticks) 12 | new_stick = x + y 13 | heapq.heappush(sticks, new_stick) 14 | costs += new_stick 15 | 16 | return costs 17 | -------------------------------------------------------------------------------- /python/116_populate_next_right_pointers.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | # Definition for a Node. 4 | class Node: 5 | def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None): 6 | self.val = val 7 | self.left = left 8 | self.right = right 9 | self.next = next 10 | """ 11 | 12 | class Solution: 13 | def connect(self, root: 'Node') -> 'Node': 14 | 15 | if not root: 16 | return root 17 | 18 | queue = deque([root, None]) 19 | while len(queue) > 1: 20 | 21 | tail = queue.pop() 22 | next_queue = deque([None]) 23 | 24 | for _ in range(len(queue)): 25 | curr = queue.pop() 26 | curr.next = tail 27 | 28 | if curr.right: 29 | next_queue.appendleft(curr.right) 30 | next_queue.appendleft(curr.left) 31 | # move the tail pointer 32 | tail = curr 33 | 34 | queue = next_queue 35 | 36 | return root 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /python/11_container_with_most_water.py: -------------------------------------------------------------------------------- 1 | 2 | class SolutionTLE: 3 | def maxArea(self, height: List[int]) -> int: 4 | 5 | length = len(height) 6 | 7 | max_size = float('-inf') 8 | for start in range(length): 9 | for end in range(start+1, length): 10 | new_size = min(height[start], height[end]) * (end - start) 11 | max_size = max(max_size, new_size) 12 | 13 | return max_size 14 | 15 | 16 | class SolutionTwoPointer: 17 | def maxArea(self, height: List[int]) -> int: 18 | 19 | left, right = 0, len(height) - 1 20 | max_size = float('-inf') 21 | while left < right: 22 | new_size = min(height[left], height[right]) * (right - left) 23 | max_size = max(max_size, new_size) 24 | 25 | if height[left] < height[right]: 26 | left += 1 27 | else: 28 | right -= 1 29 | 30 | return max_size -------------------------------------------------------------------------------- /python/1202_smallest_strings_with_swaps.py: -------------------------------------------------------------------------------- 1 | class UnionFind: 2 | def __init__(self, size): 3 | self.parent = [i for i in range(size)] 4 | 5 | def find(self, a): 6 | if self.parent[a] != a: 7 | self.parent[a] = self.find(self.parent[a]) 8 | return self.parent[a] 9 | 10 | def union(self, a, b): 11 | ga = self.find(a) 12 | gb = self.find(b) 13 | if ga != gb: 14 | self.parent[ga] = self.parent[gb] 15 | 16 | 17 | class Solution: 18 | def smallestStringWithSwaps(self, s: str, pairs: List[List[int]]) -> str: 19 | 20 | uf = UnionFind(len(s)) 21 | 22 | for a, b in pairs: 23 | uf.union(a, b) 24 | 25 | letter_groups = defaultdict(list) 26 | for index in range(len(s)): 27 | parent_id = uf.find(index) 28 | letter_groups[parent_id].append(index) 29 | 30 | output = [''] * len(s) 31 | for parent_id, group_index in letter_groups.items(): 32 | letters = [s[index] for index in group_index] 33 | letters.sort() 34 | for li, index in enumerate(group_index): 35 | output[index] = letters[li] 36 | 37 | return "".join(output) 38 | 39 | -------------------------------------------------------------------------------- /python/1209_remove_all_adjacent_duplicates_in_a_string_II.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def removeDuplicates(self, s: str, k: int) -> str: 4 | stack = [] 5 | 6 | for letter in s: 7 | if not stack: 8 | stack.append((letter, 1)) 9 | else: 10 | prev_letter, count = stack[-1] 11 | if letter == prev_letter: 12 | if count == k - 1: 13 | for _ in range(k-1): 14 | stack.pop() 15 | else: 16 | stack.append((letter, count+1)) 17 | else: 18 | stack.append((letter, 1)) 19 | 20 | return "".join([l for (l, c) in stack]) 21 | -------------------------------------------------------------------------------- /python/1249_minimum_remove_to_make_valid_parentheses.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def minRemoveToMakeValid(self, s: str) -> str: 3 | 4 | to_remove = set() 5 | stack = [] 6 | for index, letter in enumerate(s): 7 | if letter == "(": 8 | stack.append((index, letter)) 9 | elif letter == ")": 10 | if stack: 11 | stack.pop() 12 | else: 13 | # invalid parenthese 14 | to_remove.add(index) 15 | 16 | # list of unmatched (invalid) parentheses 17 | for index, letter in stack: 18 | to_remove.add(index) 19 | 20 | output = [] 21 | for index, letter in enumerate(s): 22 | if index not in to_remove: 23 | output.append(letter) 24 | 25 | return "".join(output) -------------------------------------------------------------------------------- /python/1257_smallest_common_region.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def findSmallestRegion(self, regions: List[List[str]], region1: str, region2: str) -> str: 4 | 5 | parents = dict() 6 | 7 | for region in regions: 8 | parent_region = region[0] 9 | for subregion in region[1:]: 10 | parents[subregion] = parent_region 11 | 12 | """ 13 | 1 ------o---| 1 + 2: ------o---|--o--- 14 | 2 --o---| 2 + 1: --o---|------o--- 15 | 16 | lengths after o (o---) are always same, so 2 pointers finally meet at the same node 17 | """ 18 | p1 = region1 19 | p2 = region2 20 | while p1 != p2: 21 | p1 = region2 if p1 not in parents else parents[p1] 22 | p2 = region1 if p2 not in parents else parents[p2] 23 | 24 | return p1 25 | -------------------------------------------------------------------------------- /python/1261_Find_Elements_in_Contaminated_Binary_Tree.py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class FindElements: 8 | 9 | def __init__(self, root: TreeNode): 10 | self.value_set = set() 11 | 12 | def populate(node, value): 13 | if node is None: 14 | return 15 | 16 | node.val = value 17 | self.value_set.add(value) 18 | populate(node.left, 2 * value + 1) 19 | populate(node.right, 2 * value + 2) 20 | 21 | populate(root, 0) 22 | self.root = root 23 | 24 | def find(self, target: int) -> bool: 25 | 26 | # def dfs(node): 27 | # if node is None: 28 | # return False 29 | # if target == node.val: 30 | # return True 31 | # if dfs(node.left): 32 | # return True 33 | # return dfs(node.right) 34 | 35 | return (target in self.value_set) 36 | 37 | 38 | # Your FindElements object will be instantiated and called as such: 39 | # obj = FindElements(root) 40 | # param_1 = obj.find(target) -------------------------------------------------------------------------------- /python/1288_remove_covered_intervals.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def removeCoveredIntervals(self, intervals: List[List[int]]) -> int: 4 | 5 | # Order intervals first by the starting in ascending order, 6 | # if two intervals have the same starting point, then order the ends in descending order. 7 | # Since we check the inclusion in the reserved ordre, 8 | # i.e. for intervals[i], check if it is included by any of the previous intervals[0...i-1] 9 | intervals.sort(key = lambda x: (x[0], -x[1])) 10 | 11 | remaining_intervals = len(intervals) 12 | 13 | for curr in range(len(intervals)-1, 0, -1): 14 | is_covered = False 15 | 16 | _, curr_end = intervals[curr] 17 | 18 | for prev in range(curr-1, -1, -1): 19 | _, prev_end = intervals[prev] 20 | if prev_end >= curr_end: 21 | is_covered = True 22 | break 23 | 24 | if is_covered: 25 | remaining_intervals -= 1 26 | 27 | return remaining_intervals 28 | -------------------------------------------------------------------------------- /python/129_sum_root_to_leaf_numbers.py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def sumNumbers(self, root: TreeNode) -> int: 9 | 10 | total_sum = 0 11 | 12 | def dfs(node, curr_num): 13 | nonlocal total_sum 14 | 15 | if node is None: 16 | return 0 17 | 18 | next_num = curr_num * 10 + node.val 19 | if node.left: 20 | dfs(node.left, next_num) 21 | 22 | if node.right: 23 | dfs(node.right, next_num) 24 | 25 | if node.left is None and node.right is None: 26 | # leaf node 27 | total_sum += next_num 28 | 29 | dfs(root, 0) 30 | return total_sum 31 | -------------------------------------------------------------------------------- /python/131_palindrome_partition.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def partition(self, s: str) -> List[List[str]]: 3 | 4 | def isPalindrome(ss): 5 | if len(ss) == 1: return True 6 | ss_size = len(ss) 7 | for i in range(ss_size//2): 8 | if ss[i] != ss[ss_size-i-1]: 9 | return False 10 | return True 11 | 12 | results = [] 13 | 14 | def backtrack(curr, comb): 15 | if curr == len(s): 16 | results.append(list(comb)) 17 | return 18 | 19 | for end in range(curr+1, len(s)+1): 20 | substr = s[curr:end] 21 | if isPalindrome(substr): 22 | comb.append(substr) 23 | backtrack(curr + len(substr), comb) 24 | comb.pop() 25 | 26 | backtrack(0, []) 27 | 28 | return results -------------------------------------------------------------------------------- /python/1325_delete_leaves_nodes_with_a_given_value.py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def removeLeafNodes(self, root: TreeNode, target: int) -> TreeNode: 9 | 10 | def dfs(node): 11 | if node is None: 12 | return True 13 | 14 | remove_left = dfs(node.left) 15 | if remove_left: 16 | node.left = None 17 | 18 | remove_right = dfs(node.right) 19 | if remove_right: 20 | node.right = None 21 | 22 | if node.val == target: 23 | return (remove_left and remove_right) 24 | 25 | return False 26 | 27 | pseudo_head = TreeNode(val=0, left=root) 28 | dfs(pseudo_head) 29 | 30 | return pseudo_head.left 31 | -------------------------------------------------------------------------------- /python/1328_break_a_palindrome.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | 3 | def breakPalindrome(self, palindrome: str) -> str: 4 | """ 5 | Greedy strategy to build the first non-palindrome string. 6 | 3 rules in total: 7 | - the string is of one letter 8 | - find the first non-a letter in the first half of the palindrome string, 9 | and replace it with the letter 'a'. 10 | - otherwise replace the last letter with 'a'. 11 | """ 12 | if len(palindrome) == 1: 13 | return "" 14 | 15 | mid = len(palindrome) // 2 16 | 17 | for index in range(mid): 18 | if palindrome[index] != 'a': 19 | return palindrome[0:index] + "a" + palindrome[index+1:] 20 | 21 | return palindrome[:-1] + "b" 22 | -------------------------------------------------------------------------------- /python/132_Palindrome_Partition_II.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def minCut(self, s: str) -> int: 3 | 4 | # element indicates the minimal number of partitions 5 | # we need to divide the corresponding substring 6 | dp = [0] * (len(s)+1) 7 | 8 | for right in range(1, len(s)+1): 9 | num_parts = float('inf') 10 | 11 | for left in range(0, right): 12 | substr = s[left:right] 13 | # isPalindrome(s[left:right]) 14 | if substr == substr[::-1]: 15 | num_parts = min(num_parts, dp[left]+1) 16 | if left == 0: 17 | # cannot have less than one partition 18 | break 19 | 20 | dp[right] = num_parts 21 | 22 | return dp[len(s)] - 1 23 | -------------------------------------------------------------------------------- /python/1366_rank_teams_by_votes.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def rankTeams(self, votes: List[str]) -> str: 3 | 4 | rank_counts = {} 5 | 6 | total_votes = len(votes) 7 | num_teams = len(votes[0]) 8 | 9 | for vote in votes: 10 | for rank, name in enumerate(vote): 11 | if name not in rank_counts: 12 | rank_counts[name] = defaultdict(int) 13 | keys = rank_counts[name] 14 | keys[rank] += 1 15 | 16 | rank_keys = [] 17 | for name, counts in rank_counts.items(): 18 | compound_key = [] 19 | for position in range(num_teams): 20 | compound_key.append(str(total_votes - counts[position])) 21 | compound_key.append(name) 22 | 23 | rank_keys.append(("".join(compound_key), name)) 24 | 25 | final_rank = sorted(rank_keys, key = lambda x:x[0]) 26 | return "".join([name for _, name in final_rank]) 27 | 28 | -------------------------------------------------------------------------------- /python/1376_time_needed_to_inform_all_employee.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def numOfMinutes(self, n: int, headID: int, manager: List[int], informTime: List[int]) -> int: 4 | 5 | subordinates = dict() 6 | for employee_id, manager_id in enumerate(manager): 7 | if manager_id not in subordinates: 8 | subordinates[manager_id] = [] 9 | 10 | if manager_id == -1: 11 | # pseudo head 12 | subordinates[manager_id].append((employee_id, 0)) 13 | else: 14 | subordinates[manager_id].append((employee_id, informTime[manager_id])) 15 | 16 | 17 | max_time = float('-inf') 18 | def dfs(curr, time_elapse): 19 | nonlocal max_time 20 | 21 | if curr not in subordinates: 22 | # bottom 23 | max_time = max(max_time, time_elapse) 24 | return 25 | 26 | for employee_id, added_time in subordinates[curr]: 27 | dfs(employee_id, added_time + time_elapse) 28 | 29 | dfs(-1, 0) 30 | 31 | return max_time 32 | 33 | -------------------------------------------------------------------------------- /python/1383_maximum_performance_of_team.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def maxPerformance(self, n: int, speed: List[int], efficiency: List[int], k: int) -> int: 3 | modulo = 10 ** 9 + 7 4 | 5 | # (efficiency, speed) 6 | metrics = zip(efficiency, speed) 7 | metrics = sorted(metrics, key=lambda t:t[0], reverse=True) 8 | 9 | speed_heap = [] 10 | speed_sum, perf = 0, 0 11 | 12 | for curr_efficiency, curr_speed in metrics: 13 | 14 | if len(speed_heap) > k-1: 15 | speed_sum -= heapq.heappop(speed_heap) 16 | 17 | speed_sum += curr_speed 18 | heapq.heappush(speed_heap, curr_speed) 19 | perf = max(perf, speed_sum * curr_efficiency) 20 | 21 | return perf % modulo 22 | -------------------------------------------------------------------------------- /python/141_linked_list_cycle.py: -------------------------------------------------------------------------------- 1 | # Definition for singly-linked list. 2 | # class ListNode: 3 | # def __init__(self, x): 4 | # self.val = x 5 | # self.next = None 6 | 7 | class Solution: 8 | def hasCycle(self, head: ListNode) -> bool: 9 | 10 | slow, fast = head, head 11 | 12 | while fast and fast.next: 13 | 14 | fast = fast.next.next 15 | slow = slow.next 16 | 17 | if fast == slow: 18 | return True 19 | 20 | return False -------------------------------------------------------------------------------- /python/1439_find_kth_smallest_sum_of_matrix_with_sorted_row.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def kthSmallest(self, mat: List[List[int]], k: int) -> int: 3 | 4 | queue = [] 5 | col_index = 0 6 | n_rows, n_cols = len(mat), len(mat[0]) 7 | 8 | col_array = [0] * n_rows 9 | 10 | queue_sum = sum([mat[row][0] for row in range(n_rows)]) 11 | 12 | queue.append((queue_sum, col_array)) 13 | heapq.heapify(queue) 14 | 15 | seen = set(tuple(col_array)) 16 | 17 | while k: 18 | curr_sum, col_array = heapq.heappop(queue) 19 | k -= 1 20 | 21 | for row_index, col_index in enumerate(col_array): 22 | if col_index + 1 < n_cols: 23 | new_sum = curr_sum - mat[row_index][col_index] + mat[row_index][col_index+1] 24 | new_col_array = col_array.copy() 25 | new_col_array[row_index] = col_index + 1 26 | 27 | key = tuple(new_col_array) 28 | if key in seen: 29 | continue 30 | 31 | seen.add(key) 32 | heapq.heappush(queue, (new_sum, new_col_array)) 33 | 34 | return curr_sum 35 | 36 | -------------------------------------------------------------------------------- /python/1456_maximum_number_of_vowels_in_a_substring_of_given_length.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def maxVowels(self, s: str, k: int) -> int: 4 | 5 | VOWELS = ['a', 'e', 'i', 'o', 'u'] 6 | vowel_cnt = 0 7 | for letter in s[0:k]: 8 | if letter in VOWELS: 9 | vowel_cnt += 1 10 | 11 | # starting number of vowels in a sliding window 12 | max_vowel_cnt = vowel_cnt 13 | 14 | for index in range(k, len(s)): 15 | out_of_index = index - k 16 | # two pointers that defines the boundary of the sliding window 17 | if s[index] in VOWELS: 18 | vowel_cnt += 1 19 | 20 | if s[out_of_index] in VOWELS: 21 | vowel_cnt -= 1 22 | 23 | max_vowel_cnt = max(max_vowel_cnt, vowel_cnt) 24 | 25 | return max_vowel_cnt -------------------------------------------------------------------------------- /python/145_binary_tree_postorder_traversal.py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]: 9 | 10 | output = [] 11 | 12 | def dfs(node): 13 | if not node: 14 | return 15 | 16 | dfs(node.left) 17 | dfs(node.right) 18 | output.append(node.val) 19 | 20 | dfs(root) 21 | return output 22 | 23 | 24 | # Definition for a binary tree node. 25 | # class TreeNode: 26 | # def __init__(self, val=0, left=None, right=None): 27 | # self.val = val 28 | # self.left = left 29 | # self.right = right 30 | class SolutionStack: 31 | def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]: 32 | 33 | output = deque([]) 34 | 35 | stack = [root] 36 | while stack: 37 | node = stack.pop() 38 | 39 | if node: 40 | stack.append(node.left) 41 | stack.append(node.right) 42 | 43 | output.appendleft(node.val) 44 | 45 | return output 46 | -------------------------------------------------------------------------------- /python/1469_Find_All_Lonely_Nodes.py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def getLonelyNodes(self, root: TreeNode) -> List[int]: 9 | lonely_nodes = [] 10 | 11 | def dfs(node, is_lonely): 12 | if node is None: 13 | return 14 | 15 | if is_lonely: 16 | lonely_nodes.append(node.val) 17 | 18 | if (node.left is None) ^ (node.right is None): 19 | is_lonely = True 20 | else: 21 | is_lonely = False 22 | 23 | dfs(node.left, is_lonely) 24 | dfs(node.right, is_lonely) 25 | 26 | dfs(root, False) 27 | 28 | return lonely_nodes -------------------------------------------------------------------------------- /python/147_Insertion_Sort_List.py: -------------------------------------------------------------------------------- 1 | # Definition for singly-linked list. 2 | # class ListNode: 3 | # def __init__(self, val=0, next=None): 4 | # self.val = val 5 | # self.next = next 6 | 7 | # def printList(head): 8 | # ret = [] 9 | # while head: 10 | # ret.append(head.val) 11 | # head = head.next 12 | # print(ret) 13 | 14 | class Solution: 15 | 16 | def insertionSortList(self, head: ListNode) -> ListNode: 17 | 18 | pseudo_head = ListNode() 19 | 20 | curr = head 21 | while curr: 22 | prev_node = pseudo_head 23 | next_node = prev_node.next 24 | 25 | # find the position to insert the current node 26 | while next_node: 27 | if curr.val < next_node.val: 28 | break 29 | prev_node = next_node 30 | next_node = next_node.next 31 | 32 | # insert the current node to the new list 33 | new_node = ListNode(curr.val) 34 | if next_node: 35 | new_node.next = next_node 36 | prev_node.next = new_node 37 | 38 | # moving on 39 | curr = curr.next 40 | 41 | return pseudo_head.next 42 | 43 | 44 | -------------------------------------------------------------------------------- /python/148_sort_list.py: -------------------------------------------------------------------------------- 1 | # Definition for singly-linked list. 2 | # class ListNode: 3 | # def __init__(self, x): 4 | # self.val = x 5 | # self.next = None 6 | 7 | class Solution: 8 | def partition(self, head): 9 | pre, slow, fast = None, head, head 10 | while fast and fast.next: 11 | pre, slow, fast = slow, slow.next, fast.next.next 12 | pre.next = None 13 | return head, slow 14 | 15 | def merge(self, first, second): 16 | 17 | merge_head = ListNode() 18 | pm, p1, p2 = merge_head, first, second 19 | while p1 and p2: 20 | if p1.val <= p2.val: 21 | pm.next = p1 22 | p1 = p1.next 23 | else: 24 | pm.next = p2 25 | p2 = p2.next 26 | pm = pm.next 27 | 28 | pm.next = p1 or p2 29 | return merge_head.next 30 | 31 | 32 | def sortList(self, head: ListNode) -> ListNode: 33 | """ 34 | :type head: ListNode 35 | :rtype: ListNode 36 | """ 37 | if not head or not head.next: 38 | return head 39 | 40 | first_half, second_half = self.partition(head) 41 | 42 | first_half = self.sortList(first_half) 43 | second_half = self.sortList(second_half) 44 | 45 | return self.merge(first_half, second_half) 46 | -------------------------------------------------------------------------------- /python/14_longest_common_prefix.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def longestCommonPrefix(self, strs: List[str]) -> str: 3 | 4 | def common_prefix(word_1, word_2): 5 | bound = min(len(word_1), len(word_2)) 6 | index = 0 7 | while index < bound: 8 | if word_1[index] != word_2[index]: 9 | break 10 | index += 1 11 | 12 | return word_1[0:index] 13 | 14 | prefix = strs[0] 15 | for word in strs[1:]: 16 | prefix = common_prefix(prefix, word) 17 | 18 | return prefix 19 | 20 | 21 | def longestCommonPrefix_dict(self, strs: List[str]) -> str: 22 | 23 | prefix_dict = defaultdict(int) 24 | 25 | def indexing_prefix(word): 26 | nonlocal prefix_dict 27 | for index in range(1, len(word)+1): 28 | prefix = word[:index] 29 | prefix_dict[prefix] += 1 30 | 31 | for word in strs: 32 | indexing_prefix(word) 33 | 34 | max_prefix = "" 35 | for prefix, count in prefix_dict.items(): 36 | if count == len(strs): 37 | if len(prefix) > len(max_prefix): 38 | max_prefix = prefix 39 | 40 | return max_prefix 41 | -------------------------------------------------------------------------------- /python/1506_find_root_of_N-ary_tree.py: -------------------------------------------------------------------------------- 1 | """ 2 | # Definition for a Node. 3 | class Node: 4 | def __init__(self, val=None, children=None): 5 | self.val = val 6 | self.children = children if children is not None else [] 7 | """ 8 | 9 | class Solution: 10 | def findRoot(self, tree: List['Node']) -> 'Node': 11 | 12 | node_indegree = {} 13 | 14 | for node in tree: 15 | if node not in node_indegree: 16 | node_indegree[node] = 0 17 | for child in node.children: 18 | node_indegree[child] = 1 19 | 20 | for node, indegree in node_indegree.items(): 21 | if indegree == 0: 22 | return node 23 | 24 | 25 | class SolutionSet: 26 | def findRoot(self, tree: List['Node']) -> 'Node': 27 | 28 | seen = set() 29 | 30 | for node in tree: 31 | for child in node.children: 32 | seen.add(child.val) 33 | 34 | for node in tree: 35 | if node.val not in seen: 36 | return node 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /python/1508_range_sum_of_sorted_subarray_sums.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def rangeSum(self, nums: List[int], n: int, left: int, right: int) -> int: 3 | 4 | subarray_sums = [] 5 | modulo = 10 ** 9 + 7 6 | 7 | for start_index, start_num in enumerate(nums): 8 | subarray_sum = 0 9 | for end_index, end_num in enumerate(nums[start_index:]): 10 | subarray_sum += end_num 11 | subarray_sums.append(subarray_sum) 12 | 13 | subarray_sums.sort() 14 | 15 | ret = 0 16 | for index in range(left-1, right): 17 | ret = (ret + subarray_sums[index]) % modulo 18 | 19 | return ret 20 | -------------------------------------------------------------------------------- /python/1510_Stone_Game_IV.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def winnerSquareGame(self, n: int) -> bool: 3 | 4 | @lru_cache(maxsize=None) 5 | def dfs(remain): 6 | sqrt_root = int(sqrt(remain)) 7 | # current player will win immediately by taking the square number tiles 8 | if sqrt_root ** 2 == remain: 9 | return True 10 | 11 | for i in range(1, sqrt_root+1): 12 | # if there is any chance to make the opponent lose the game in the next round, 13 | # then the current player will win. 14 | if not dfs(remain - i*i): 15 | return True 16 | 17 | return False 18 | 19 | return dfs(n) 20 | -------------------------------------------------------------------------------- /python/151_find_minimum_in_rotated_sorted_array.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand. 3 | 4 | (i.e., [0,1,2,4,5,6,7] might become [4,5,6,7,0,1,2]). 5 | 6 | Find the minimum element. 7 | 8 | You may assume no duplicate exists in the array. 9 | ''' 10 | class Solution: 11 | def findMin(self, nums: List[int]) -> int: 12 | low = 0 13 | high = len(nums)-1 14 | # the original array is either contains a single element 15 | # or is actually not rotated 16 | if low == len(nums) - 1 or nums[low] < nums[high]: 17 | return nums[low] 18 | 19 | while low < high: 20 | pivot = int((low+high)/2) 21 | #print('old', low, high, pivot) 22 | 23 | if pivot == len(nums)-1: 24 | return nums[-1] 25 | # found the rotation pivot ! 26 | if nums[pivot] > nums[pivot+1]: 27 | return nums[pivot+1] 28 | 29 | if nums[pivot] >= nums[0]: 30 | low = pivot + 1 31 | else: 32 | high = pivot 33 | 34 | #print('new', low, high, pivot) 35 | return nums[pivot] -------------------------------------------------------------------------------- /python/152_Maximum_Product_Subarray.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def maxProduct(self, nums: List[int]) -> int: 4 | """ 5 | Keep the intermediate results of the previous num 6 | """ 7 | curr_products = [] 8 | max_product = float('-inf') 9 | 10 | for num in nums: 11 | curr_products.append(1) 12 | next_products = [] 13 | for product in curr_products: 14 | new_product = num * product 15 | max_product = max(max_product, new_product) 16 | next_products.append(new_product) 17 | 18 | curr_products = next_products 19 | 20 | return max_product 21 | 22 | 23 | class SolutionDP: 24 | """ 25 | Rather than keeping all the intermediate products, 26 | we take the two extreme numbers among the intermediate results, 27 | since only these two extreme numbers can change the final result. 28 | """ 29 | def maxProduct(self, nums: List[int]) -> int: 30 | 31 | max_product = nums[0] 32 | max_sofar, min_sofar = nums[0], nums[0] 33 | 34 | for num in nums[1:]: 35 | new_min_sofar = num * min_sofar 36 | new_max_sofar = num * max_sofar 37 | min_sofar = min(num, new_min_sofar, new_max_sofar) 38 | max_sofar = max(num, new_min_sofar, new_max_sofar) 39 | 40 | max_product = max(max_product, max_sofar) 41 | 42 | return max_product -------------------------------------------------------------------------------- /python/1539_Kth_Missing_Positive_Number.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def findKthPositive(self, arr: List[int], k: int) -> int: 3 | 4 | prev = 0 5 | missing_cnt = 0 6 | 7 | for curr in arr: 8 | 9 | # case 1). we find the missing number in between the array 10 | while prev < curr - 1: 11 | prev += 1 12 | missing_cnt += 1 13 | if missing_cnt == k: 14 | return prev 15 | 16 | prev = curr 17 | 18 | # case 2). we find the missing number beyond the array 19 | return curr + (k - missing_cnt) 20 | -------------------------------------------------------------------------------- /python/154_Find_Minimum_in_Rotated_Sorted_Array_II.py: -------------------------------------------------------------------------------- 1 | """ 2 | Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand. 3 | 4 | (i.e., [0,1,2,4,5,6,7] might become [4,5,6,7,0,1,2]). 5 | 6 | Find the minimum element. 7 | 8 | The array may contain duplicates. 9 | 10 | Example 1: 11 | 12 | Input: [1,3,5] 13 | Output: 1 14 | 15 | Example 2: 16 | Input: [2,2,2,0,1] 17 | Output: 0 18 | """ 19 | class Solution: 20 | def findMin(self, nums: List[int]) -> int: 21 | low = 0 22 | high = len(nums)-1 23 | while high > low: 24 | pivot = low + (high - low) // 2 25 | # pivot = (low + high) // 2 could overflow 26 | # Case 1): 27 | if nums[pivot] < nums[high]: 28 | high = pivot 29 | # alternative: high = pivot - 1 30 | # too aggressive to move the `high` index, 31 | # it won't work for the test case of [3, 1, 3] 32 | # Case 2): 33 | elif nums[pivot] > nums[high]: 34 | low = pivot + 1 35 | # Case 3): 36 | else: 37 | high -= 1 38 | # the 'low' and 'high' index converge to the inflection point. 39 | return nums[low] 40 | -------------------------------------------------------------------------------- /python/1570_dot_product_of_two_sparse_vectors.py: -------------------------------------------------------------------------------- 1 | 2 | class SparseVector: 3 | def __init__(self, nums: List[int]): 4 | self.value_table = {} 5 | for index, num in enumerate(nums): 6 | if num != 0: 7 | self.value_table[index] = num 8 | 9 | # Return the dotProduct of two sparse vectors 10 | def dotProduct(self, vec: 'SparseVector') -> int: 11 | if len(self.value_table) < len(vec.value_table): 12 | small_table = self.value_table 13 | large_table = vec.value_table 14 | else: 15 | small_table = vec.value_table 16 | large_table = self.value_table 17 | 18 | dot_product = 0 19 | for key, value in small_table.items(): 20 | if key in large_table: 21 | dot_product += value * large_table[key] 22 | 23 | return dot_product 24 | 25 | # Your SparseVector object will be instantiated and called as such: 26 | # v1 = SparseVector(nums1) 27 | # v2 = SparseVector(nums2) 28 | # ans = v1.dotProduct(v2) -------------------------------------------------------------------------------- /python/163_missing_ranges.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def findMissingRanges(self, nums: List[int], lower: int, upper: int) -> List[str]: 3 | 4 | range_iter = lower 5 | num_iter = 0 6 | ranges = [] 7 | while range_iter < upper and num_iter < len(nums): 8 | if range_iter < nums[num_iter]: 9 | if nums[num_iter] - 1 == range_iter: 10 | ranges.append(str(range_iter)) 11 | else: 12 | ranges.append("{}->{}".format(range_iter, nums[num_iter]-1)) 13 | 14 | range_iter = nums[num_iter] + 1 15 | num_iter += 1 16 | 17 | if num_iter >= len(nums) and range_iter == upper: 18 | ranges.append("{}".format(range_iter)) 19 | elif range_iter < upper: 20 | ranges.append("{}->{}".format(range_iter, upper)) 21 | 22 | return ranges -------------------------------------------------------------------------------- /python/165_compare_version_number.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def compareVersion(self, version1: str, version2: str) -> int: 3 | 4 | revision_list_1 = [int(i) for i in version1.split(".")] 5 | revision_list_2 = [int(i) for i in version2.split(".")] 6 | 7 | def padding(revisions, to_length): 8 | for i in range(len(revisions), to_length): 9 | revisions.append(0) 10 | return revisions 11 | 12 | max_length = max(len(revision_list_1), len(revision_list_2)) 13 | padded_revision_1 = padding(revision_list_1, max_length) 14 | padded_revision_2 = padding(revision_list_2, max_length) 15 | for p in range(0, max_length): 16 | if padded_revision_1[p] > padded_revision_2[p]: 17 | return 1 18 | elif padded_revision_1[p] < padded_revision_2[p]: 19 | return -1 20 | 21 | return 0 22 | -------------------------------------------------------------------------------- /python/1676_lowest_common_ancestor_for_multiple_nodes.py: -------------------------------------------------------------------------------- 1 | 2 | # Definition for a binary tree node. 3 | # class TreeNode: 4 | # def __init__(self, x): 5 | # self.val = x 6 | # self.left = None 7 | # self.right = None 8 | 9 | class Solution: 10 | def lowestCommonAncestor(self, root: 'TreeNode', nodes: 'List[TreeNode]') -> 'TreeNode': 11 | 12 | value_subset = set([node.val for node in nodes]) 13 | 14 | result = None 15 | def get_descendants(parent): 16 | nonlocal result 17 | 18 | if not parent: 19 | return set() 20 | 21 | left_nodes = get_descendants(parent.left) 22 | if result: 23 | return set() 24 | right_nodes = get_descendants(parent.right) 25 | 26 | all_nodes = {parent.val} | left_nodes | right_nodes 27 | 28 | if value_subset.issubset(all_nodes) and not result: 29 | result = parent 30 | 31 | return all_nodes 32 | 33 | get_descendants(root) 34 | return result 35 | -------------------------------------------------------------------------------- /python/169_major_element.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def majorityElement(self, nums: List[int]) -> int: 3 | """ Boyer-Moore voting algorithm""" 4 | candidate, count = 0, 0 5 | 6 | for num in nums: 7 | if count == 0: 8 | candidate = num 9 | 10 | count += (1 if num == candidate else -1) 11 | 12 | return candidate -------------------------------------------------------------------------------- /python/1701_average_waiting_time.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def averageWaitingTime(self, customers: List[List[int]]) -> float: 3 | 4 | prev_end = customers[0][0] 5 | 6 | total_time = 0 7 | for customer in customers: 8 | arrival, duration = customer[0], customer[1] 9 | 10 | if prev_end > arrival: 11 | # delayed by the previous customer 12 | wait_time = prev_end - arrival + duration 13 | prev_end = prev_end + duration 14 | else: 15 | # the chef is idle and thus serve the customer immediately 16 | wait_time = duration 17 | prev_end = arrival + duration 18 | 19 | total_time += wait_time 20 | 21 | return total_time / len(customers) 22 | 23 | 24 | -------------------------------------------------------------------------------- /python/1710_maximum_capacity_on_truck.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Solution: 4 | def maximumUnits(self, boxTypes: List[List[int]], truckSize: int) -> int: 5 | 6 | # Greedy algorithm 7 | # load the boxes prioritized on the their capacities 8 | boxTypes.sort(key=lambda x:x[1], reverse=True) 9 | 10 | unit_sum = 0 11 | for box_num, box_units in boxTypes: 12 | if truckSize == 0: 13 | break 14 | 15 | if truckSize > box_num: 16 | truckSize -= box_num 17 | unit_sum += box_num * box_units 18 | else: 19 | unit_sum += truckSize * box_units 20 | truckSize = 0 21 | 22 | return unit_sum 23 | -------------------------------------------------------------------------------- /python/1721_swapping_nodes_in_a_linked_list.py: -------------------------------------------------------------------------------- 1 | 2 | # Definition for singly-linked list. 3 | # class ListNode: 4 | # def __init__(self, val=0, next=None): 5 | # self.val = val 6 | # self.next = next 7 | class Solution: 8 | def swapNodes(self, head: Optional[ListNode], k: int) -> Optional[ListNode]: 9 | 10 | first_switch, second_switch = None, None 11 | 12 | step = 1 13 | # the distance between the 'prev' and 'curr' is k steps, 14 | # so when the 'curr' reaches the end, the 'prev' would be the second switching point 15 | curr, prev = head, None 16 | 17 | while curr: 18 | if prev: 19 | prev = prev.next 20 | 21 | if step == k: 22 | # kick of the 'prev' pointer 23 | first_switch = curr 24 | prev = head 25 | 26 | # move on to the next step 27 | curr = curr.next 28 | step += 1 29 | 30 | second_switch = prev 31 | 32 | first_switch.val, second_switch.val = second_switch.val, first_switch.val 33 | 34 | return head -------------------------------------------------------------------------------- /python/1722_minimum_hamming_distance_after_swapping_operations.py: -------------------------------------------------------------------------------- 1 | class UnionFind: 2 | 3 | def __init__(self, size): 4 | self.parent = [i for i in range(size)] 5 | 6 | def find(self, a): 7 | if self.parent[a] != a: 8 | self.parent[a] = self.find(self.parent[a]) 9 | return self.parent[a] 10 | 11 | def union(self, a, b): 12 | ga = self.find(a) 13 | gb = self.find(b) 14 | if ga != gb: 15 | self.parent[ga] = self.parent[gb] 16 | 17 | 18 | class Solution: 19 | def minimumHammingDistance(self, source: List[int], target: List[int], allowedSwaps: List[List[int]]) -> int: 20 | 21 | str_len = len(source) 22 | 23 | uf = UnionFind(str_len) 24 | for a, b in allowedSwaps: 25 | uf.union(a, b) 26 | 27 | group_index = defaultdict(list) 28 | for index in range(str_len): 29 | group_id = uf.find(index) 30 | group_index[group_id].append(index) 31 | 32 | hamming_distance = 0 33 | for group_id, group_index in group_index.items(): 34 | source_counter = Counter([source[i] for i in group_index]) 35 | target_counter = Counter([target[i] for i in group_index]) 36 | common_elements = source_counter & target_counter 37 | hamming_distance += len(group_index) - sum(common_elements.values()) 38 | 39 | return hamming_distance 40 | 41 | 42 | -------------------------------------------------------------------------------- /python/1730_shortest_path_to_get_food.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def getFood(self, grid: List[List[str]]) -> int: 4 | 5 | start = None 6 | 7 | # 1). Locate the starting point first 8 | ROWS = len(grid) 9 | COLS = len(grid[0]) 10 | for row in range(ROWS): 11 | for col in range(COLS): 12 | if grid[row][col] == '*': 13 | start = (row, col) 14 | break 15 | 16 | grid[start[0]][start[1]] = "X" 17 | queue = deque([(start, 0)]) 18 | 19 | # Run a BFS search to find the "nearest" target/food. 20 | while queue: 21 | (row, col), distance = queue.popleft() 22 | 23 | for r_offset, c_offset in [(0, 1), (1, 0), (0, -1), (-1, 0)]: 24 | next_row, next_col = row + r_offset, col + c_offset 25 | if 0 > next_row or next_row == ROWS or 0 > next_col or next_col == COLS: 26 | continue 27 | if grid[next_row][next_col] == "X": 28 | continue 29 | elif grid[next_row][next_col] == "#": # find the food 30 | return distance + 1 31 | else: # open space 32 | # OPTIMIZATION: reduce the number of elements in queue 33 | grid[next_row][next_col] = "X" # mark it as visited 34 | queue.append(((next_row, next_col), distance+1)) 35 | 36 | return -1 -------------------------------------------------------------------------------- /python/1754_largest_merge_of_two_strings.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def largestMerge(self, word1: str, word2: str) -> str: 3 | 4 | index_1, index_2 = 0, 0 5 | len_1, len_2 = len(word1), len(word2) 6 | 7 | merge = [] 8 | while index_1 < len_1 or index_2 < len_2: 9 | 10 | if index_1 == len_1: 11 | merge.append(word2[index_2]) 12 | index_2 += 1 13 | elif index_2 == len_2: 14 | merge.append(word1[index_1]) 15 | index_1 += 1 16 | else: # both index are valid 17 | # look ahead of the rest of the substring 18 | # pick the larger substring 19 | if word1[index_1:] >= word2[index_2:]: 20 | merge.append(word1[index_1]) 21 | index_1 += 1 22 | else: 23 | merge.append(word2[index_2]) 24 | index_2 += 1 25 | 26 | return "".join(merge) 27 | -------------------------------------------------------------------------------- /python/1759_count_number_of_homogenous_substrings.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def countHomogenous(self, s: str) -> int: 4 | 5 | modulo = 10 ** 9 + 7 6 | 7 | total_substr = 0 8 | substr_len = 1 9 | prev = s[0] 10 | 11 | for curr in s[1:]: 12 | if curr == prev: 13 | substr_len += 1 14 | else: 15 | # calculate each part of the consecutive substring 16 | total_substr += (1 + substr_len) * substr_len // 2 17 | # reset 18 | prev = curr 19 | substr_len = 1 20 | 21 | # the last part of the homogenous substring 22 | total_substr += (1 + substr_len) * substr_len // 2 23 | 24 | return total_substr % modulo -------------------------------------------------------------------------------- /python/176_Excel_Sheet_Column_Number.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def titleToNumber(self, s: str) -> int: 3 | 4 | length = len(s) 5 | num = 0 6 | for i in range(0, length): 7 | num += (ord(s[length-i-1]) - ord('A') + 1) * (26 ** i) 8 | 9 | return num -------------------------------------------------------------------------------- /python/17_letter_combination_of_a_phone_number.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def letterCombinations(self, digits: str) -> List[str]: 4 | 5 | digit_map = {"2": "abc", "3": "def", "4": "ghi", '5': "jkl", 6 | "6": "mno", "7": "pqrs", "8": "tuv", "9": "wxyz"} 7 | 8 | output = [] 9 | 10 | def dfs(curr_index, comb): 11 | nonlocal output 12 | 13 | if curr_index == len(digits): 14 | output.append("".join(comb)) 15 | return 16 | 17 | digit = digits[curr_index] 18 | for letter in digit_map[digit]: 19 | comb.append(letter) 20 | dfs(curr_index+1, comb) 21 | comb.pop() 22 | 23 | if len(digits) == 0: 24 | return [] 25 | 26 | dfs(0, []) 27 | return output 28 | -------------------------------------------------------------------------------- /python/189_Rotate_Array.py: -------------------------------------------------------------------------------- 1 | class SolutionReverse: 2 | def reverse(self, nums, start, end): 3 | while start < end: 4 | nums[start], nums[end] = nums[end], nums[start] 5 | start, end = start+1, end-1 6 | 7 | def rotate(self, nums: List[int], k: int) -> None: 8 | length = len(nums) 9 | k %= length 10 | 11 | # reverse the array in three times: first the entire array, 12 | # then first and second part respectively 13 | self.reverse(nums, 0, length-1) 14 | self.reverse(nums, 0, k-1) 15 | self.reverse(nums, k, length-1) 16 | 17 | 18 | class SolutionShift: 19 | def rotate(self, nums: List[int], k: int) -> None: 20 | """ 21 | Do not return anything, modify nums in-place instead. 22 | """ 23 | for i in range(k): 24 | prev = nums[-1] 25 | # shift the array to the right, once at a time 26 | for j in range(len(nums)): 27 | nums[j], prev = prev, nums[j] -------------------------------------------------------------------------------- /python/198_House_Robber.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def rob(self, nums: List[int]) -> int: 3 | 4 | to_rob, not_to_rob = 0, 0 5 | 6 | # state machine 7 | # At each step, we calculate profits of two choices: to_rob, not_to_rob 8 | for num in nums: 9 | # if we break down into two statements, 10 | # we need some temporary values to ensure the same results. 11 | to_rob, not_to_rob = max(not_to_rob + num, to_rob), max(to_rob, not_to_rob) 12 | 13 | return max(to_rob, not_to_rob) 14 | 15 | 16 | class SolutionRecursionMemoization: 17 | def rob(self, nums: List[int]) -> int: 18 | 19 | @functools.lru_cache(maxsize=None) 20 | def dfs(start, accu): 21 | 22 | if start >= len(nums): 23 | return accu 24 | 25 | return max(dfs(start+1, accu), dfs(start+2, accu + nums[start])) 26 | 27 | return dfs(0, 0) 28 | 29 | 30 | class SolutionDP: 31 | def rob(self, nums: List[int]) -> int: 32 | 33 | N = len(nums) 34 | dp = [0] * (len(nums) + 1) 35 | 36 | dp[N-1] = nums[N-1] 37 | 38 | for i in range(N-2, -1, -1): 39 | dp[i] = max(dp[i+1], dp[i+2] + nums[i]) 40 | 41 | return dp[0] 42 | -------------------------------------------------------------------------------- /python/199_Binary_Tree_with_Right_Side_View.py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def rightSideView(self, root: TreeNode) -> List[int]: 9 | 10 | results = [] 11 | 12 | def DFS(node, depth): 13 | if node: 14 | if depth == len(results): 15 | results.append(node.val) 16 | # preorder with the priority on the right child 17 | DFS(node.right, depth+1) 18 | DFS(node.left, depth+1) 19 | 20 | DFS(root , 0) 21 | return results 22 | -------------------------------------------------------------------------------- /python/206_reverse_linked_list.py: -------------------------------------------------------------------------------- 1 | # Definition for singly-linked list. 2 | # class ListNode: 3 | # def __init__(self, val=0, next=None): 4 | # self.val = val 5 | # self.next = next 6 | class Solution: 7 | def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]: 8 | 9 | prev, curr = None, head 10 | while curr: 11 | next_curr = curr.next 12 | curr.next = prev 13 | 14 | prev = curr 15 | curr = next_curr 16 | 17 | return prev 18 | 19 | 20 | # Definition for singly-linked list. 21 | # class ListNode: 22 | # def __init__(self, val=0, next=None): 23 | # self.val = val 24 | # self.next = next 25 | class Solution: 26 | def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]: 27 | 28 | # return the new head element 29 | if head is None or head.next is None: 30 | return head 31 | 32 | new_head = self.reverseList(head.next) 33 | 34 | # reverse the pair of elements: head, head->next 35 | head.next.next = head 36 | head.next = None 37 | 38 | return new_head 39 | -------------------------------------------------------------------------------- /python/2073_time_needed_to_buy_tickets.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def timeRequiredToBuy(self, tickets: List[int], k: int) -> int: 3 | 4 | clock = 0 5 | while tickets[k]: 6 | 7 | for index, ticket in enumerate(tickets): 8 | if ticket > 0: 9 | tickets[index] -= 1 10 | clock += 1 11 | 12 | # stop the clock as soon as the target reaches zero 13 | if tickets[k] == 0: 14 | return clock 15 | 16 | return clock -------------------------------------------------------------------------------- /python/20_Valid_Parentheses.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def isValid(self, s: str) -> bool: 3 | stack = [] 4 | 5 | bracket_map = { ")": "(", "}": "{" , "]": "["} 6 | 7 | for char in s: 8 | if char not in bracket_map: 9 | stack.append(char) 10 | else: 11 | if stack and stack.pop() == bracket_map[char]: 12 | continue 13 | else: 14 | return False 15 | 16 | return len(stack) == 0 17 | -------------------------------------------------------------------------------- /python/216_combination_sum_III.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def combinationSum3(self, k: int, n: int) -> List[List[int]]: 4 | 5 | global_set = [] 6 | 7 | def backtrack(remain, comb, next_start): 8 | """ 9 | remain: remaining sum 10 | comb: the current combination 11 | next_start: the starting index for the next candidates 12 | 13 | Pick the candidates in order, 14 | so that we won't have any duplidates 15 | e.g. if we picked the number 3 in the previous step, 16 | then in the following steps, 17 | the list of candiates become [4 5, ... 9] 18 | """ 19 | if remain == 0 and len(comb) == k: 20 | # make a copy of current combination 21 | global_set.append(list(comb)) 22 | return 23 | elif remain < 0 or len(comb) == k: 24 | return 25 | 26 | for i in range(next_start, 9): 27 | comb.append(i+1) 28 | backtrack(remain-i-1, comb, i+1) 29 | comb.pop() 30 | 31 | comb = [] 32 | backtrack(n, comb, 0) 33 | 34 | return global_set 35 | -------------------------------------------------------------------------------- /python/220_Contains_Duplicates_III.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def containsNearbyAlmostDuplicate(self, nums: List[int], k: int, t: int) -> bool: 3 | """ 4 | bucket sort alike algorithm 5 | """ 6 | buckets = {} 7 | window = (t+1) 8 | 9 | if t < 0: 10 | return False 11 | 12 | def locate(num): 13 | return num // window 14 | 15 | for index, num in enumerate(nums): 16 | bucket_id = locate(num) 17 | 18 | left = bucket_id - 1 19 | right = bucket_id + 1 20 | 21 | # check [left_bucket][current_bucket][right_bucket] 22 | if bucket_id in buckets: 23 | return True 24 | if left in buckets and abs(num - buckets[left]) < window: 25 | return True 26 | if right in buckets and abs(buckets[right] - num) < window: 27 | return True 28 | 29 | buckets[bucket_id] = num 30 | 31 | if len(buckets) > k: 32 | buckets.pop(locate(nums[index-k])) 33 | 34 | return False -------------------------------------------------------------------------------- /python/228_summary_range.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def summaryRanges(self, nums: List[int]) -> List[str]: 3 | 4 | def format_range(low, high): 5 | return str(low) if low == high else "{}->{}".format(low, high) 6 | 7 | results = [] 8 | 9 | if len(nums) == 0: 10 | return results 11 | 12 | low = nums[0] 13 | for i in range(1, len(nums)): 14 | if nums[i] - nums[i-1] > 1: 15 | results.append(format_range(low, nums[i-1])) 16 | low = nums[i] 17 | 18 | results.append(format_range(low, nums[-1])) 19 | 20 | return results -------------------------------------------------------------------------------- /python/229_major_elements_II.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def majorityElement(self, nums: List[int]) -> List[int]: 3 | 4 | major_elements = defaultdict(int) 5 | 6 | # voting for the major elements 7 | for num in nums: 8 | major_elements[num] += 1 9 | if len(major_elements) == 3: 10 | to_delete = [] 11 | for k, v in major_elements.items(): 12 | major_elements[k] -= 1 13 | if v == 1: 14 | to_delete.append(k) 15 | for k in to_delete: 16 | del major_elements[k] 17 | 18 | ret = [] 19 | # a second round to verify if the major elements takes 1/3 votes 20 | for elem in major_elements: 21 | if nums.count(elem) > len(nums) / 3: 22 | ret.append(elem) 23 | 24 | return ret -------------------------------------------------------------------------------- /python/238_product_of_array_except_self.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Solution: 4 | def productExceptSelf(self, nums: List[int]) -> List[int]: 5 | 6 | num_len = len(nums) 7 | prefix_products, postfix_products = [1] * (num_len + 1), [1] * (num_len + 1) 8 | 9 | prefix_product, postfix_product = 1, 1 10 | 11 | for prefix_index in range(num_len): 12 | prefix_product *= nums[prefix_index] 13 | prefix_products[prefix_index+1] = prefix_product 14 | 15 | postfix_index = num_len - 1 - prefix_index 16 | postfix_product *= nums[postfix_index] 17 | postfix_products[postfix_index] = postfix_product 18 | 19 | infix_products = [] 20 | for index in range(num_len): 21 | infix_products.append(prefix_products[index] * postfix_products[index+1]) 22 | 23 | return infix_products 24 | 25 | -------------------------------------------------------------------------------- /python/242_valid_anagrams.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | 4 | """ 5 | class Solution: 6 | def isAnagram(self, s, t): 7 | """ 8 | :type s: str 9 | :type t: str 10 | :rtype: bool 11 | """ 12 | if (len(s) != len(t)): 13 | return False 14 | 15 | def to_dict(word): 16 | ret = {} 17 | for letter in word: 18 | if letter in ret: 19 | ret[letter] +=1 20 | else: 21 | ret[letter] = 1 22 | return ret 23 | 24 | dict_1 = to_dict(s) 25 | dict_2 = to_dict(t) 26 | return (dict_1 == dict_2) 27 | 28 | 29 | 30 | if __name__ == "__main__": 31 | 32 | solution = Solution() 33 | 34 | print(solution.isAnagram("abcd", "bcda")) 35 | 36 | 37 | -------------------------------------------------------------------------------- /python/249_group_shifted_strings.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def groupStrings(self, strings: List[str]) -> List[List[str]]: 4 | 5 | str_table = defaultdict(list) 6 | 7 | for string in strings: 8 | str_len = len(string) 9 | if str_len == 1: 10 | str_table[(1)].append(string) 11 | else: 12 | # list of distances between each adjacent letters 13 | key = [] 14 | for index in range(1, str_len): 15 | diff = (ord(string[index]) - ord(string[index-1]) + 26) % 26 16 | key.append(diff) 17 | 18 | str_table[tuple(key)].append(string) 19 | 20 | return str_table.values() 21 | -------------------------------------------------------------------------------- /python/252_meeting_room.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def canAttendMeetings(self, intervals: List[List[int]]) -> bool: 3 | 4 | intervals.sort(key = lambda x: x[0]) 5 | 6 | for curr in range(0, len(intervals)-1): 7 | curr_interval = intervals[curr] 8 | next_interval = intervals[curr+1] 9 | if curr_interval[1] > next_interval[0]: 10 | return False 11 | 12 | return True 13 | -------------------------------------------------------------------------------- /python/257_binary_tree_paths.py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def binaryTreePaths(self, root: TreeNode) -> List[str]: 9 | 10 | output = [] 11 | 12 | def backtrack(node, path): 13 | 14 | if node.left is None and node.right is None: 15 | output.append("->".join(path)) 16 | return 17 | 18 | for child in [node.left, node.right]: 19 | if child is None: 20 | continue 21 | path.append(str(child.val)) 22 | backtrack(child, path) 23 | path.pop() 24 | 25 | if root is not None: 26 | path = [str(root.val)] 27 | backtrack(root, path) 28 | 29 | return output 30 | -------------------------------------------------------------------------------- /python/266_Palindrome_Permutation.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def canPermutePalindrome(self, s: str) -> bool: 3 | char_set = set() 4 | 5 | for char in s: 6 | if char in char_set: 7 | char_set.remove(char) 8 | else: 9 | char_set.add(char) 10 | return len(char_set) <= 1 11 | -------------------------------------------------------------------------------- /python/276_Paint_Fence.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def numWays(self, n: int, k: int) -> int: 3 | 4 | if n == 0: 5 | return 0 6 | elif n == 1: 7 | return k 8 | elif (k == 1) and (n >= 3): 9 | return 0 10 | 11 | dp = [0 for i in range(n)] 12 | dp[0] = k 13 | dp[1] = k * k 14 | 15 | for i in range(2, n): 16 | # case 1). last two fences of the same colors 17 | # case 2). last two fences of different colors 18 | dp[i] = (dp[i-1] + dp[i-2]) * (k - 1) 19 | 20 | return dp[-1] 21 | -------------------------------------------------------------------------------- /python/283_move_zeroes.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def moveZeroes(self, nums: List[int]) -> None: 3 | """ 4 | Do not return anything, modify nums in-place instead. 5 | """ 6 | read_index, write_index = 0, -1 7 | len_nums = len(nums) 8 | 9 | # shift non-zero numbers to the head of the list 10 | while read_index < len_nums: 11 | if nums[read_index] != 0: 12 | write_index += 1 13 | nums[write_index] = nums[read_index] 14 | read_index += 1 15 | 16 | # reset the rest of numbers to zeroes 17 | write_index += 1 18 | while write_index < len_nums: 19 | nums[write_index] = 0 20 | write_index += 1 21 | -------------------------------------------------------------------------------- /python/300_Longest_Increasing_Subsequence.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given an unsorted array of integers, find the length of longest increasing subsequence. 3 | 4 | Example: 5 | 6 | Input: [10,9,2,5,3,7,101,18] 7 | Output: 4 8 | 9 | 10 | Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4. 11 | 12 | 13 | Note: 14 | - There may be more than one LIS combination, it is only necessary for you to return the length. 15 | - Your algorithm should run in O(n2) complexity. 16 | 17 | Follow up: 18 | Could you improve it to O(n log n) time complexity? 19 | """ 20 | class Solution(object): 21 | def lengthOfLIS(self, nums): 22 | """ 23 | :type nums: List[int] 24 | :rtype: int 25 | """ 26 | if len(nums) == 0: 27 | return 0 28 | 29 | # the longest increasing subsequence for the prefix [0...i] 30 | dp = [1] * (len(nums)) 31 | 32 | globalMaxLen = -1 33 | 34 | for i, num in enumerate(nums): 35 | localMaxLen = 1 36 | for k in range(i): 37 | if nums[i] > nums[k]: 38 | localMaxLen = max(dp[k]+1, localMaxLen) 39 | 40 | dp[i] = localMaxLen 41 | globalMaxLen = max(localMaxLen, globalMaxLen) 42 | 43 | return globalMaxLen 44 | -------------------------------------------------------------------------------- /python/301_remove_invalid_parentheses.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def removeInvalidParentheses(self, s: str) -> List[str]: 3 | 4 | def is_valid(expr): 5 | """ quick check if the expression is valid """ 6 | balance = 0 7 | # the balance should always be >= 0 8 | for char in expr: 9 | if char not in '()': 10 | continue 11 | 12 | if char == "(": 13 | balance += 1 14 | elif char == ")": 15 | balance -= 1 16 | 17 | if balance < 0: 18 | return False 19 | return balance == 0 20 | 21 | queue = deque([s]) 22 | visited = set([s]) 23 | found = False 24 | output = [] 25 | 26 | while queue: 27 | substr = queue.popleft() 28 | 29 | if is_valid(substr): 30 | output.append(substr) 31 | found = True 32 | 33 | # once found the first qualified elements, stop exploring further 34 | if found: 35 | continue 36 | 37 | for index in range(len(substr)): 38 | if substr[index] not in "()": 39 | continue 40 | new_str = substr[:index] + substr[index+1:] 41 | if new_str not in visited: 42 | visited.add(new_str) 43 | queue.append(new_str) 44 | 45 | return output 46 | 47 | -------------------------------------------------------------------------------- /python/306_Additive_Number.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def isAdditiveNumber(self, num: str) -> bool: 3 | 4 | 5 | def dfs(prev_sum_str, prev_num_str, start, count): 6 | 7 | if start > len(num) - 1: 8 | return True if count >= 3 else False 9 | elif len(num) - start + 1 < len(prev_sum_str): 10 | return False 11 | elif num[start:(start+len(prev_sum_str))] != prev_sum_str: 12 | return False 13 | else: 14 | next_start = start + len(prev_sum_str) 15 | next_sum = int(prev_sum_str) + int(prev_num_str) 16 | return dfs(str(next_sum), prev_sum_str, next_start, count+1) 17 | 18 | 19 | for first_sep in range(1, len(num)-1): 20 | first_num = int(num[0:first_sep]) 21 | # skip the numbers with leading zero, e.g. "02" 22 | if first_sep > 1 and first_num < 10: 23 | break 24 | 25 | for second_sep in range(first_sep+1, len(num)): 26 | prev_num = int(num[first_sep:second_sep]) 27 | # skip the numbers with leading zero, e.g. "02" 28 | if second_sep - first_sep > 1 and prev_num < 10: 29 | break 30 | 31 | prev_sum = first_num + prev_num 32 | if dfs(str(prev_sum), str(prev_num), second_sep, 2): 33 | return True 34 | 35 | return False 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /python/309_Buy_and_Sell_Stock_with_Cooldown.py: -------------------------------------------------------------------------------- 1 | class Solution(object): 2 | def maxProfit(self, prices): 3 | """ 4 | :type prices: List[int] 5 | :rtype: int 6 | """ 7 | L = len(prices) 8 | dp = [0] * (L + 2) 9 | 10 | for buy in range(L-1, -1, -1): 11 | max_profit = 0 12 | for sell in range(buy+1, L): 13 | profit = (prices[sell] - prices[buy]) + dp[sell+2] 14 | max_profit = max(profit, max_profit) 15 | 16 | # In addition, we need to compare with doing no transaction 17 | dp[buy] = max(max_profit, dp[buy+1]) 18 | 19 | return dp[0] 20 | -------------------------------------------------------------------------------- /python/311_Sparse_Matrix_Multiplication.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def multiply(self, mat1: List[List[int]], mat2: List[List[int]]) -> List[List[int]]: 3 | 4 | # convert the second matrix to column-based vectors 5 | col_vecs = [[] for i in range(len(mat2[0]))] 6 | for row in mat2: 7 | for col_index, col in enumerate(row): 8 | col_vecs[col_index].append(col) 9 | 10 | result = [[] for i in range(len(mat1))] 11 | for row_index, row_vec in enumerate(mat1): 12 | for col_index, col_vec in enumerate(col_vecs): 13 | dot_product = sum([a*b for (a, b) in zip(row_vec, col_vec)]) 14 | result[row_index].append(dot_product) 15 | 16 | return result 17 | -------------------------------------------------------------------------------- /python/314_binary_tree_vertical_order_traversal.py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, x): 4 | # self.val = x 5 | # self.left = None 6 | # self.right = None 7 | from collections import defaultdict 8 | class Solution: 9 | def verticalOrder(self, root: TreeNode) -> List[List[int]]: 10 | columnTable = defaultdict(list) 11 | queue = deque([(root, 0),]) 12 | 13 | while queue: 14 | node, column = queue.popleft() 15 | 16 | if node is not None: 17 | columnTable[column].append(node.val) 18 | 19 | queue.append((node.left, column - 1)) 20 | queue.append((node.right, column + 1)) 21 | 22 | return [columnTable[x] for x in sorted(columnTable.keys())] 23 | -------------------------------------------------------------------------------- /python/31_next_permutation.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def nextPermutation(self, nums: List[int]) -> None: 3 | """ 4 | Do not return anything, modify nums in-place instead. 5 | """ 6 | def swap(array, i, j): 7 | array[i], array[j] = array[j], array[i] 8 | 9 | def reverse(array, start): 10 | """ reverse the postfix subarray starting from 'start' """ 11 | end = len(array) - 1 12 | while start < end: 13 | array[start], array[end] = array[end], array[start] 14 | start += 1 15 | end -= 1 16 | 17 | # locate the pair of elements to switch 18 | i = len(nums) - 2 19 | while i >= 0: 20 | if nums[i] < nums[i+1]: 21 | break 22 | i -= 1 23 | 24 | if i >= 0: 25 | j = len(nums) - 1 26 | while j > 0: 27 | if nums[j] > nums[i]: 28 | break 29 | j -= 1 30 | 31 | swap(nums, i, j) 32 | 33 | # reverse the postfix in order to obtain the minimal permutation 34 | reverse(nums, i+1) 35 | -------------------------------------------------------------------------------- /python/323_number_of_connected_components_in_graph.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def countComponents(self, n: int, edges: List[List[int]]) -> int: 3 | 4 | graph = defaultdict(list) 5 | for edge in edges: 6 | start, end = edge[0], edge[1] 7 | graph[start].append(end) 8 | graph[end].append(start) 9 | 10 | visited = [False for _ in range(n)] 11 | 12 | def explore(node, visited): 13 | if visited[node]: 14 | return 15 | 16 | visited[node] = True 17 | for neighbor in graph[node]: 18 | if not visited[neighbor]: 19 | explore(neighbor, visited) 20 | 21 | component_cnt = 0 22 | 23 | for node in range(n): 24 | if not visited[node]: 25 | explore(node, visited) 26 | component_cnt += 1 27 | 28 | return component_cnt 29 | 30 | -------------------------------------------------------------------------------- /python/325_maximum_size_subarray_sum_equals_K.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def maxSubArrayLen(self, nums: List[int], k: int) -> int: 3 | 4 | max_subarray = 0 5 | prefix_sum_index = {} 6 | prefix_sum = 0 7 | 8 | for index in range(0, len(nums)): 9 | 10 | prefix_sum += nums[index] 11 | 12 | if prefix_sum == k: 13 | # use only the prefix 14 | max_subarray = max(max_subarray, index + 1) 15 | 16 | if prefix_sum not in prefix_sum_index: 17 | prefix_sum_index[prefix_sum] = index 18 | 19 | target = prefix_sum - k 20 | if target in prefix_sum_index: 21 | prev_index = prefix_sum_index[target] 22 | max_subarray = max(max_subarray, index - prev_index) 23 | 24 | return max_subarray 25 | 26 | 27 | -------------------------------------------------------------------------------- /python/329_longest_increasing_path_in_matrix.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def longestIncreasingPath(self, matrix: List[List[int]]) -> int: 3 | 4 | if len(matrix) == 0: 5 | return 0 6 | 7 | rows = len(matrix) 8 | cols = len(matrix[0]) 9 | 10 | @lru_cache(maxsize=None) 11 | def dfs(row, col): 12 | ans = 0 13 | for ro, co in [(0, 1), (1, 0), (0, -1), (-1, 0)]: 14 | new_row, new_col = row + ro, col + co 15 | if 0 <= new_row and new_row < rows and \ 16 | 0 <= new_col and new_col < cols and \ 17 | matrix[new_row][new_col] > matrix[row][col]: 18 | ans = max(ans, dfs(new_row, new_col)) 19 | return ans + 1 20 | 21 | max_paths = 0 22 | for row in range(rows): 23 | for col in range(cols): 24 | max_paths = max(max_paths, dfs(row, col)) 25 | 26 | return max_paths -------------------------------------------------------------------------------- /python/337_house_robber_III.py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def rob(self, root: TreeNode) -> int: 9 | 10 | @lru_cache(maxsize=None) 11 | def dfs(node, can_rob, acc_sum): 12 | if not node: 13 | return acc_sum 14 | 15 | max_sum = acc_sum 16 | 17 | if can_rob: 18 | # rob the current node 19 | left_sum = dfs(node.left, False, 0) 20 | right_sum = dfs(node.right, False, 0) 21 | max_sum += node.val + left_sum + right_sum 22 | 23 | # either cannot or choose not to rob the current node 24 | left_sum = dfs(node.left, True, 0) 25 | right_sum = dfs(node.right, True, 0) 26 | max_sum = max(max_sum, left_sum + right_sum) 27 | 28 | return max_sum 29 | 30 | first_try = dfs(root, True, 0) 31 | second_try = dfs(root, False, 0) 32 | 33 | return max(first_try, second_try) 34 | 35 | -------------------------------------------------------------------------------- /python/342_power_of_four.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def isPowerOfFour(self, num: int) -> bool: 3 | 4 | return num > 0 and math.log2(num) % 2 == 0 5 | # num & (num - 1) == 0 // power of two 6 | # num & 0xaaaaaaaa == 0 // odd number of power of twos 7 | #return num > 0 and num & (num-1) == 0 and (num & 0xaaaaaaaa) == 0 8 | -------------------------------------------------------------------------------- /python/345_reverse_vowels_of_string.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def reverseVowels(self, s: str) -> str: 4 | 5 | letter_list = [] 6 | vowel_index_list = [] 7 | 8 | VOWELS = ['a', 'e', 'i', 'o', 'u', 9 | 'A', 'E', 'I', 'O', 'U'] 10 | 11 | for index, letter in enumerate(s): 12 | letter_list.append(letter) 13 | if letter in VOWELS: 14 | vowel_index_list.append(index) 15 | 16 | vowel_index = 0 17 | total_vowels = len(vowel_index_list) 18 | 19 | left, right = 0, len(vowel_index_list) - 1 20 | 21 | while left < right: 22 | left_index = vowel_index_list[left] 23 | right_index = vowel_index_list[right] 24 | letter_list[left_index], letter_list[right_index] \ 25 | = letter_list[right_index], letter_list[left_index] 26 | 27 | left += 1 28 | right -= 1 29 | 30 | return "".join(letter_list) -------------------------------------------------------------------------------- /python/346_moving_average_from_data_stream.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a stream of integers and a window size, calculate the moving average of all integers in the sliding window. 3 | 4 | Example: 5 | 6 | MovingAverage m = new MovingAverage(3); 7 | m.next(1) = 1 8 | m.next(10) = (1 + 10) / 2 9 | m.next(3) = (1 + 10 + 3) / 3 10 | m.next(5) = (10 + 3 + 5) / 3 11 | 12 | """ 13 | 14 | class MovingAverage: 15 | """ 16 | Design a circular queue with array, 17 | with minimal calculation and space consumption 18 | """ 19 | def __init__(self, size: int): 20 | """ 21 | Initialize your data structure here. 22 | 23 | """ 24 | self.size = size 25 | self.queue = [0] * self.size 26 | self.curr = 0 27 | self.sum = 0 28 | self.count = 0 29 | 30 | def next(self, val: int) -> float: 31 | self.count += 1 32 | tail = (self.curr + self.size + 1) % self.size 33 | self.sum = self.sum - self.queue[tail] + val 34 | # move on to the next head 35 | self.curr = (self.curr + 1) % self.size 36 | self.queue[self.curr] = val 37 | 38 | return self.sum / (self.size if self.count > self.size else self.count) 39 | 40 | 41 | # Your MovingAverage object will be instantiated and called as such: 42 | # obj = MovingAverage(size) 43 | # param_1 = obj.next(val) -------------------------------------------------------------------------------- /python/347_Top_K_Frequent_Elements.py: -------------------------------------------------------------------------------- 1 | """ 2 | Given a non-empty array of integers, return the k most frequent elements. 3 | 4 | Example 1: 5 | 6 | Input: nums = [1,1,1,2,2,3], k = 2 7 | Output: [1,2] 8 | """ 9 | 10 | class Solution: 11 | def topKFrequent(self, nums: List[int], k: int) -> List[int]: 12 | """ 13 | :type nums: List[int] 14 | :type k: int 15 | :rtype: List[int] 16 | """ 17 | from collections import defaultdict 18 | numCount = defaultdict(int) 19 | for num in nums: 20 | numCount[num] += 1 21 | 22 | sorted_counts = sorted(numCount.items(), key=lambda kv: kv[1]) 23 | 24 | return [sorted_counts[-i][0] for i in range(1, k+1)] 25 | 26 | class Solution2(object): 27 | def topKFrequent(self, nums, k): 28 | """ 29 | :type nums: List[int] 30 | :type k: int 31 | :rtype: List[int] 32 | """ 33 | #cnt = collections.Counter(nums) 34 | #return [kv[0] for kv in cnt.most_common(k)] 35 | 36 | cnt = collections.Counter(nums) 37 | 38 | import heapq 39 | return heapq.nlargest(k, cnt, key=cnt.get) 40 | 41 | class Solution3(object): 42 | def topKFrequent(self, nums, k): 43 | """ 44 | :type nums: List[int] 45 | :type k: int 46 | :rtype: List[int] 47 | """ 48 | cnt = collections.Counter(nums) 49 | return [kv[0] for kv in cnt.most_common(k)] -------------------------------------------------------------------------------- /python/350_intersection_of_two_arrays_II.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]: 4 | 5 | nums1.sort() 6 | nums2.sort() 7 | 8 | p1, p2 = 0, 0 9 | 10 | nums1_len, nums2_len = len(nums1), len(nums2) 11 | 12 | result = [] 13 | while p1 < nums1_len and p2 < nums2_len: 14 | if nums1[p1] == nums2[p2]: 15 | result.append(nums1[p1]) 16 | elif nums1[p1] > nums2[p2]: 17 | p1 -= 1 18 | else: 19 | p2 -= 1 20 | 21 | p1 += 1 22 | p2 += 1 23 | 24 | return result 25 | 26 | 27 | class Solution: 28 | def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]: 29 | 30 | # https://www.geeksforgeeks.org/operations-on-python-counter/ 31 | # Operations on Counter: +, -, &, | 32 | # addition, subtraction, intersection, union 33 | counter1 = Counter(nums1) 34 | counter2 = Counter(nums2) 35 | 36 | result = [] 37 | 38 | # intersections between the frequency count 39 | intersections = counter1 & counter2 40 | for key, count in intersections.items(): 41 | result.extend([key] * count) 42 | 43 | return result 44 | -------------------------------------------------------------------------------- /python/361_bomb_enemy.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def maxKilledEnemies(self, grid: List[List[str]]) -> int: 3 | if len(grid) == 0: 4 | return 0 5 | 6 | rows, cols = len(grid), len(grid[0]) 7 | 8 | max_count = 0 9 | row_hits = 0 10 | col_hits = [0] * cols 11 | 12 | for row in range(0, rows): 13 | for col in range(0, cols): 14 | # reset the hits on the row, if necessary 15 | if col == 0 or grid[row][col-1] == 'W': 16 | row_hits = 0 17 | for k in range(col, cols): 18 | if grid[row][k] == 'W': 19 | break 20 | elif grid[row][k] == 'E': 21 | row_hits += 1 22 | 23 | # reset the hits on the col, if necessary 24 | if row == 0 or grid[row-1][col] == 'W': 25 | col_hits[col] = 0 26 | for k in range(row, rows): 27 | if grid[k][col] == 'W': 28 | break 29 | elif grid[k][col] == 'E': 30 | col_hits[col] += 1 31 | 32 | if grid[row][col] == '0': 33 | total_hits = row_hits + col_hits[col] 34 | max_count = max(max_count, total_hits) 35 | 36 | return max_count 37 | -------------------------------------------------------------------------------- /python/366_Find_Leaves_of_Binary_Tree.py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, x): 4 | # self.val = x 5 | # self.left = None 6 | # self.right = None 7 | 8 | class Solution: 9 | def findLeaves(self, root): 10 | """ 11 | :type root: TreeNode 12 | :rtype: List[List[int]] 13 | """ 14 | results = [] 15 | 16 | def distance_to_leave(node): 17 | nonlocal results 18 | 19 | if node is None: 20 | return 0 21 | 22 | distance = 1 + max(distance_to_leave(node.left), 23 | distance_to_leave(node.right)) 24 | 25 | # update the resulting array 26 | if distance > len(results): 27 | results.append([node.val]) 28 | else: 29 | results[distance-1].append(node.val) 30 | 31 | # return the maximum distance to any of the leaves 32 | return distance 33 | 34 | distance_to_leave(root) 35 | 36 | return results 37 | -------------------------------------------------------------------------------- /python/367_valid_perfect_square.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Given a positive integer num, write a function which returns True if num is a perfect square else False. 3 | 4 | Note: Do not use any built-in library function such as sqrt. 5 | 6 | Example 1: 7 | Input: 16 8 | Output: true 9 | 10 | Example 2: 11 | 12 | Input: 14 13 | Output: false 14 | ''' 15 | 16 | class Solution: 17 | def isPerfectSquare(self, num: int) -> bool: 18 | ''' 19 | Binary search on the proper number 20 | ''' 21 | low, high = 0, num 22 | 23 | # tricky to set the conditions: 24 | while low <= high: # condition 1). low <= high 25 | pivot = (low + high) // 2 26 | product = pivot * pivot 27 | if product == num: 28 | return True 29 | elif product < num: 30 | low = pivot + 1 # condition 2). move the pivot 31 | else: 32 | high = pivot - 1 # condition 3). move the pivot 33 | 34 | return False 35 | -------------------------------------------------------------------------------- /python/370_range_addition.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def getModifiedArray(self, length: int, updates: List[List[int]]) -> List[int]: 4 | 5 | array = [0] * length 6 | 7 | # update the boundaries with delta values 8 | for start, end, delta in updates: 9 | array[start] += delta 10 | if end < length - 1: 11 | array[end + 1] -= delta 12 | 13 | # calculate the cumulative sum / prefix_sum 14 | prefix_sum = 0 15 | for index in range(length): 16 | array[index] += prefix_sum 17 | prefix_sum = array[index] 18 | 19 | return array 20 | -------------------------------------------------------------------------------- /python/375_Guess_Higher_or_Lower_Number_II.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def getMoneyAmount(self, n: int) -> int: 3 | 4 | @lru_cache(maxsize=None) 5 | def cost(low, high): 6 | """ minmax algorithm: 7 | the minimal values among all possible (worst) scenarios 8 | """ 9 | if low >= high: 10 | return 0 11 | 12 | min_cost = float('inf') 13 | for pivot in range(low, high): 14 | worst_scenario = pivot + max(cost(low, pivot-1), cost(pivot+1, high)) 15 | min_cost = min(min_cost, worst_scenario) 16 | 17 | return min_cost 18 | 19 | return cost(0, n) 20 | 21 | 22 | class SolutionOptimized: 23 | def getMoneyAmount(self, n: int) -> int: 24 | 25 | @lru_cache(maxsize=None) 26 | def cost(low, high): 27 | if low >= high: 28 | return 0 29 | 30 | min_cost = float('inf') 31 | # only consider using the right half of the elments as pivots, 32 | # as they are the worst scenarios. 33 | for pivot in range((low+high)//2, high): 34 | worst_scenario = pivot + max(cost(low, pivot-1), cost(pivot+1, high)) 35 | min_cost = min(min_cost, worst_scenario) 36 | 37 | return min_cost 38 | 39 | return cost(1, n) -------------------------------------------------------------------------------- /python/377_combination_sum_IV.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def combinationSum4(self, nums: List[int], target: int) -> int: 3 | 4 | """ 5 | dynamic programming: 6 | comb[target] = sum(comb[target-nums[i]]) 7 | 8 | - treat each candidate number as the last number in the final combination. 9 | - append the candidate number to the combinations of (target - nums[i]), i.e. comb[target-nums[i]] 10 | """ 11 | 12 | #import functools 13 | @functools.lru_cache(maxsize = None) 14 | def combinationSum4(remain): 15 | if remain == 0: 16 | return 1 17 | 18 | result = 0 19 | for num in nums: 20 | if remain - num >= 0: 21 | result += combinationSum4(remain - num) 22 | return result 23 | 24 | return combinationSum4(target) 25 | -------------------------------------------------------------------------------- /python/383_ransom_note.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def canConstruct(self, ransomNote: str, magazine: str) -> bool: 4 | 5 | def count_letter(word): 6 | letter_cnt = defaultdict(int) 7 | for letter in word: 8 | letter_cnt[letter] += 1 9 | return letter_cnt 10 | 11 | 12 | ransom_dict = count_letter(ransomNote) 13 | magazine_dict = count_letter(magazine) 14 | 15 | for letter, cnt in ransom_dict.items(): 16 | if letter not in magazine_dict: 17 | return False 18 | if cnt > magazine_dict[letter]: 19 | return False 20 | 21 | return True 22 | 23 | 24 | def canConstruct_OneHashmap(self, ransomNote: str, magazine: str) -> bool: 25 | 26 | def count_letter(word): 27 | letter_cnt = defaultdict(int) 28 | for letter in word: 29 | letter_cnt[letter] += 1 30 | return letter_cnt 31 | 32 | magazine_dict = count_letter(magazine) 33 | 34 | for letter in ransomNote: 35 | if letter not in magazine_dict: 36 | return False 37 | magazine_dict[letter] -= 1 38 | if magazine_dict[letter] == 0: 39 | del magazine_dict[letter] 40 | 41 | return True 42 | -------------------------------------------------------------------------------- /python/384_Shuffle_Array.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | 3 | def __init__(self, nums: List[int]): 4 | self.original = nums 5 | self.perm = list(self.original) 6 | 7 | 8 | def reset(self) -> List[int]: 9 | """ 10 | Resets the array to its original configuration and return it. 11 | """ 12 | # restore the current state of permutation to its original state 13 | self.perm = list(self.original) 14 | return self.perm 15 | 16 | 17 | def shuffle(self) -> List[int]: 18 | """ 19 | Returns a random shuffling of the array. 20 | """ 21 | # apparently this is so-call Fisher-Yates algorithm. 22 | for i in range(len(self.perm)): 23 | #randi = random.randrange(i, len(self.perm)) 24 | randi = i + random.randint(0, len(self.perm)-i-1) 25 | self.perm[i], self.perm[randi] = self.perm[randi], self.perm[i] 26 | 27 | return self.perm 28 | 29 | 30 | # Your Solution object will be instantiated and called as such: 31 | # obj = Solution(nums) 32 | # param_1 = obj.reset() 33 | # param_2 = obj.shuffle() -------------------------------------------------------------------------------- /python/392_Is_Subsequence.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def isSubsequence(self, s: str, t: str) -> bool: 3 | 4 | LEFT_BOUND = len(s) 5 | RIGHT_BOUND = len(t) 6 | 7 | @lru_cache(None) 8 | def rec_isSubsequence(left_index, right_index): 9 | if left_index == LEFT_BOUND: 10 | return True 11 | elif right_index == RIGHT_BOUND: 12 | return False 13 | 14 | if s[left_index] == t[right_index]: 15 | left_index += 1 16 | right_index += 1 17 | else: 18 | right_index += 1 19 | 20 | return rec_isSubsequence(left_index, right_index) 21 | 22 | 23 | return rec_isSubsequence(0, 0) -------------------------------------------------------------------------------- /python/404_sum_of_left_leaves.py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def sumOfLeftLeaves(self, root: TreeNode) -> int: 9 | 10 | leaf_sum = 0 11 | def dfs(node, is_left=None): 12 | nonlocal leaf_sum 13 | if node is None: 14 | return 15 | 16 | if node.left is None and node.right is None and is_left: 17 | leaf_sum += node.val 18 | else: 19 | dfs(node.left, True) 20 | dfs(node.right, False) 21 | 22 | dfs(root) 23 | return leaf_sum 24 | -------------------------------------------------------------------------------- /python/415_add_strings.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def addStrings(self, num1: str, num2: str) -> str: 3 | 4 | p1, p2 = len(num1) - 1, len(num2) - 1 5 | output = [] 6 | carry = 0 7 | 8 | while p1 >= 0 or p2 >= 0: 9 | 10 | bit_sum = 0 11 | 12 | if p1 >= 0: 13 | bit_sum += int(num1[p1]) 14 | p1 -= 1 15 | 16 | if p2 >= 0: 17 | bit_sum += int(num2[p2]) 18 | p2 -= 1 19 | 20 | bit_sum += carry 21 | carry = bit_sum // 10 22 | curr_bit = bit_sum % 10 23 | 24 | output.append(str(curr_bit)) 25 | 26 | if carry > 0: 27 | output.append(str(carry)) 28 | 29 | return "".join(reversed(output)) -------------------------------------------------------------------------------- /python/416_partition_equal_subset_sum.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def canPartition(self, nums: List[int]) -> bool: 3 | 4 | total_sum = sum(nums) 5 | if total_sum % 2 != 0: 6 | return False 7 | 8 | target = total_sum // 2 9 | 10 | @lru_cache(maxsize=None) 11 | def backtrack(curr, curr_sum): 12 | 13 | if curr_sum == target: 14 | return True 15 | elif curr_sum > target or curr == len(nums): 16 | return False 17 | 18 | if backtrack(curr+1, curr_sum + nums[curr]): 19 | return True 20 | 21 | if backtrack(curr+1, curr_sum): 22 | return True 23 | 24 | return backtrack(0, 0) 25 | -------------------------------------------------------------------------------- /python/42_trapping_rain_water.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def trap(self, height: List[int]) -> int: 3 | 4 | left_max, right_max = 0, 0 5 | left, right = 0, len(height) - 1 6 | 7 | total_sum = 0 8 | while left < right: 9 | left_max = max(left_max, height[left]) 10 | right_max = max(right_max, height[right]) 11 | 12 | if left_max < right_max: 13 | total_sum += left_max - height[left] 14 | left += 1 15 | else: 16 | total_sum += right_max - height[right] 17 | right -= 1 18 | 19 | return total_sum 20 | -------------------------------------------------------------------------------- /python/435_non_overlapping_intervals.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int: 4 | 5 | intervals.sort(key = lambda x: x[1]) 6 | 7 | eliminate_count = 0 8 | 9 | curr_end = intervals[0][1] 10 | 11 | # Eliminate the intervals at early as possible greedily 12 | for (start, end) in intervals[1:]: 13 | if start < curr_end: 14 | # eliminate the current overlapped interval, 15 | # since if we keep it, it would have a greater chance to overlap with the following intervals 16 | eliminate_count += 1 17 | else: 18 | curr_end = end 19 | 20 | return eliminate_count -------------------------------------------------------------------------------- /python/442_find_duplicates_in_an_array.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def findDuplicates(self, nums: List[int]) -> List[int]: 3 | ret = [] 4 | for x in nums: 5 | if nums[abs(x) - 1] < 0: 6 | ret.append(abs(x)) 7 | else: 8 | nums[abs(x) - 1] *= -1 9 | 10 | return ret -------------------------------------------------------------------------------- /python/450_delete_node_in_BST.py: -------------------------------------------------------------------------------- 1 | 2 | # Definition for a binary tree node. 3 | # class TreeNode: 4 | # def __init__(self, val=0, left=None, right=None): 5 | # self.val = val 6 | # self.left = left 7 | # self.right = right 8 | class Solution: 9 | def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]: 10 | 11 | if not root: 12 | return root 13 | 14 | def find_least_node(node): 15 | while node.left: 16 | node = node.left 17 | return node 18 | 19 | if key < root.val: 20 | root.left = self.deleteNode(root.left, key) 21 | elif key > root.val: 22 | root.right = self.deleteNode(root.right, key) 23 | else: # key == root.val 24 | # replace it with either of non-empty child node 25 | if not root.left: 26 | return root.right 27 | elif not root.right: 28 | return root.left 29 | else: 30 | # delete the current node by replacing it with the left most value on its right child 31 | left_most_node = find_least_node(root.right) 32 | root.val = left_most_node.val 33 | root.right = self.deleteNode(root.right, root.val) 34 | 35 | return root 36 | -------------------------------------------------------------------------------- /python/452_minimal_number_of_arrows_to_burst_ballons.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Solution: 4 | def findMinArrowShots(self, points: List[List[int]]) -> int: 5 | 6 | points.sort(key = lambda x:x[1]) 7 | 8 | # minimal arrows 9 | arrow_count = 1 10 | 11 | # starting from the first ballon 12 | curr_end = points[0][1] 13 | 14 | for (start, end) in points: 15 | # Use one arrow to burst all ballons in an overlapped interval 16 | if curr_end < start: 17 | arrow_count += 1 18 | curr_end = end 19 | 20 | return arrow_count -------------------------------------------------------------------------------- /python/458_poor_pigs.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def poorPigs(self, buckets: int, minutesToDie: int, minutesToTest: int) -> int: 3 | 4 | bit_states = minutesToTest / minutesToDie + 1 5 | 6 | # a mathematical solution 7 | # information contained in buckets: log(buckets) 8 | # information contained for a single pig (possible states): log(states) 9 | #return math.ceil(math.log(buckets) / math.log(bit_states)) 10 | 11 | num_bits = 0 # number of pigs as well 12 | while bit_states ** num_bits < buckets: 13 | num_bits += 1 14 | 15 | return num_bits 16 | 17 | -------------------------------------------------------------------------------- /python/461_hamming_distance.py: -------------------------------------------------------------------------------- 1 | """ 2 | The Hamming distance between two integers is the number of positions at which the corresponding bits are different. 3 | 4 | Given two integers x and y, calculate the Hamming distance. 5 | 6 | Note: 7 | 0 ≤ x, y < 231. 8 | 9 | Example: 10 | Input: x = 1, y = 4 11 | 12 | Output: 2 13 | 14 | Explanation: 15 | 1 (0 0 0 1) 16 | 4 (0 1 0 0) 17 | ↑ ↑ 18 | 19 | The above arrows point to positions where the corresponding bits are different. 20 | 21 | """ 22 | class BrianKernighanSolution: 23 | def hammingDistance(self, x: int, y: int) -> int: 24 | diff = x ^ y 25 | count = 0 26 | while diff: 27 | count += 1 28 | # turn off rightmost 1-bit 29 | diff = diff & (diff - 1) 30 | return count 31 | 32 | class Solution(object): 33 | def hammingDistance(self, x, y): 34 | """ 35 | :type x: int 36 | :type y: int 37 | :rtype: int 38 | """ 39 | distance = 0 40 | 41 | while x != 0 or y != 0: 42 | if (x % 2) != (y % 2): 43 | distance += 1 44 | 45 | x = x >> 1 46 | y = y >> 1 47 | 48 | return distance -------------------------------------------------------------------------------- /python/463_Island_Perimeter.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def islandPerimeter(self, grid: List[List[int]]) -> int: 4 | 5 | ROWS, COLS = len(grid), len(grid[0]) 6 | 7 | def shoreCount(row, col): 8 | count = 0 9 | directions = [(-1, 0), (0, 1), (1, 0), (0, -1)] 10 | for rowOffset, colOffset in directions: 11 | newRow, newCol = row+rowOffset, col+colOffset 12 | if newRow < 0 or newRow >= ROWS or newCol < 0 or newCol >= COLS: 13 | count += 1 14 | elif grid[newRow][newCol] == 0: 15 | count += 1 16 | return count 17 | 18 | perimeter = 0 19 | for row in range(ROWS): 20 | for col in range(COLS): 21 | if grid[row][col] == 1: 22 | perimeter += shoreCount(row, col) 23 | 24 | return perimeter 25 | -------------------------------------------------------------------------------- /python/46_permutations.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def permute(self, nums: List[int]) -> List[List[int]]: 4 | 5 | output = [] 6 | 7 | def swap(array, a, b): 8 | if a != b: 9 | array[a], array[b] = array[b], array[a] 10 | 11 | def backtrack(start, array): 12 | nonlocal output 13 | """ 14 | Generate permutation for the subarray[start:] 15 | 16 | when we reach the last subarray, the permutation is formed inside the array. 17 | """ 18 | if start == len(array): 19 | output.append(list(array)) 20 | return 21 | 22 | for next_index in range(start, len(array)): 23 | swap(array, next_index, start) 24 | 25 | # move on the subarray, i.e. array[start+1:] 26 | backtrack(start + 1, array) 27 | 28 | swap(array, next_index, start) 29 | 30 | backtrack(0, nums) 31 | return output 32 | 33 | 34 | -------------------------------------------------------------------------------- /python/470_implement_rand10_using_rand7.py: -------------------------------------------------------------------------------- 1 | # The rand7() API is already defined for you. 2 | # def rand7(): 3 | # @return a random integer in the range 1 to 7 4 | 5 | class Solution: 6 | def rand10(self): 7 | """ 8 | :rtype: int 9 | 10 | Two-phase generation, decision-tree alike approach 11 | 12 | [1, 2, 3] [5, 6, 7] 13 | | | 14 | | | 15 | [1, 2, 3, 4, 5] [6, 7, 8, 9, 10] 16 | 17 | """ 18 | while True: 19 | first_round = rand7() 20 | if first_round != 4: 21 | while True: 22 | second_round = rand7() 23 | if second_round <= 5: 24 | return second_round + (first_round < 4) * 5 25 | 26 | 27 | def rand10_rejection_sampling(self): 28 | """ 29 | :rtype: int 30 | """ 31 | while True: 32 | row, col = rand7(), rand7() 33 | index = (row - 1) * 7 + col 34 | if index <= 40: 35 | return (index - 1) % 10 + 1 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /python/480_sliding_window_median.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def medianSlidingWindow(self, nums: List[int], k: int) -> List[float]: 3 | 4 | window, medians = [], [] 5 | for i, num in enumerate(nums): 6 | 7 | # insert an element into sorted list 8 | # the resulting list would be still in order 9 | bisect.insort(window, num) 10 | 11 | if i >= k: 12 | # remove the element that is just out of scope 13 | # window.remove(nums[i-k]) 14 | # apply binary search to locate the element to remove 15 | # but it won't make much difference, since eventually 16 | # we need to shift the elements 17 | window.pop(bisect.bisect(window, nums[i-k]) - 1) 18 | 19 | if i >= k - 1: 20 | if k % 2 == 0: 21 | median = (window[k//2] + window[k//2 - 1]) / 2 22 | else: 23 | median = window[k//2] 24 | 25 | medians.append(median) 26 | 27 | 28 | return medians -------------------------------------------------------------------------------- /python/497_random_point_in_non-overlapping_rectangles.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | class Solution: 4 | 5 | def __init__(self, rects: List[List[int]]): 6 | self.prefix_sums = [] 7 | self.total_area = 0 8 | for rect in rects: 9 | x1, y1, x2, y2 = rect 10 | # the area could be of lines 11 | area = (x2-x1 + 1) * (y2-y1 + 1) 12 | self.total_area += area 13 | self.prefix_sums.append(self.total_area) 14 | self.rects = rects 15 | 16 | def pick(self) -> List[int]: 17 | 18 | pick = int(random.random() * self.total_area) 19 | 20 | # binary search to locate the chosen rectangle 21 | low, high = 0, len(self.prefix_sums) 22 | while low < high: 23 | mid = int((high - low) / 2 + low) 24 | if pick >= self.prefix_sums[mid]: 25 | low = mid + 1 26 | else: 27 | high = mid 28 | 29 | picked_rect = self.rects[low] 30 | x1, y1, x2, y2 = picked_rect 31 | 32 | return [random.randrange(x1, x2+1), random.randrange(y1, y2+1)] 33 | 34 | 35 | # Your Solution object will be instantiated and called as such: 36 | # obj = Solution(rects) 37 | # param_1 = obj.pick() -------------------------------------------------------------------------------- /python/513_find_bottom_left_tree_value.py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def findBottomLeftValue(self, root: TreeNode) -> int: 9 | leaves_list = [] 10 | 11 | def dfs(node, row, col): 12 | if node is None: 13 | return 14 | 15 | if node.left is None and node.right is None: 16 | leaves_list.append((-row, col, node.val)) 17 | else: 18 | dfs(node.left, row+1, col-1) 19 | dfs(node.right, row+1, col+1) 20 | 21 | dfs(root, 0, 0) 22 | 23 | # order the leaves nodes by its (row, col) index 24 | leaves_list.sort() 25 | 26 | return leaves_list[0][2] 27 | -------------------------------------------------------------------------------- /python/516_longest_palindromic_subsequence.py: -------------------------------------------------------------------------------- 1 | class SolutionTLE: 2 | def longestPalindromeSubseq(self, s: str) -> int: 3 | 4 | max_len = 0 5 | str_len = len(s) 6 | 7 | def dfs(index, subsequence): 8 | nonlocal max_len 9 | 10 | if subsequence == subsequence[::-1]: 11 | max_len = max(max_len, len(subsequence)) 12 | 13 | if index == str_len: 14 | return 15 | 16 | dfs(index+1, subsequence) 17 | dfs(index+1, subsequence + s[index]) 18 | 19 | 20 | dfs(0, "") 21 | 22 | return max_len 23 | 24 | 25 | class Solution: 26 | def longestPalindromeSubseq(self, s: str) -> int: 27 | 28 | """ 29 | optimized break-down of the problem, 30 | with less memory consumption, as well as less computation. 31 | """ 32 | @functools.lru_cache(maxsize=None) 33 | def dp(left, right): 34 | 35 | if left == right: 36 | return 1 37 | elif left > right: 38 | return 0 39 | 40 | if s[left] == s[right]: 41 | return 2 + dp(left+1, right-1) 42 | else: 43 | return max(dp(left+1, right), dp(left, right-1)) 44 | 45 | return dp(0, len(s)-1) -------------------------------------------------------------------------------- /python/523_Continuous_Subarray_Sum.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def checkSubarraySum(self, nums: List[int], k: int) -> bool: 3 | 4 | # the earliest index with the same module remaider of k 5 | prefix_sum_indices = {} 6 | 7 | # a virtual prefix sum index. 8 | # for the test case of [0, 0] k = 0 9 | prefix_sum = 0 10 | prefix_sum_indices[0] = -1 11 | 12 | for index, num in enumerate(nums): 13 | 14 | prefix_sum += num 15 | 16 | # group the prefix sums with modulo 17 | if k != 0: 18 | prefix_sum %= k # normalize the sum 19 | 20 | if prefix_sum in prefix_sum_indices: 21 | if index - prefix_sum_indices[prefix_sum] > 1: 22 | return True 23 | else: 24 | prefix_sum_indices[prefix_sum] = index 25 | 26 | return False 27 | -------------------------------------------------------------------------------- /python/526_beautiful_arrangement.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def countArrangement(self, N: int) -> int: 3 | 4 | nums = [i+1 for i in range(N)] 5 | comb_count = 0 6 | def backtrack(start, comb): 7 | nonlocal comb_count 8 | 9 | if start == N: 10 | comb_count += 1 11 | return 12 | 13 | for index in range(start, N): 14 | 15 | if (start + 1) % nums[index] == 0 \ 16 | or nums[index] % (start + 1) == 0: 17 | # mark the choice 18 | comb[start], comb[index] = comb[index], comb[start] 19 | backtrack(start+1, comb) 20 | # revert the choice 21 | comb[start], comb[index] = comb[index], comb[start] 22 | 23 | backtrack(0, nums) 24 | 25 | return comb_count 26 | -------------------------------------------------------------------------------- /python/528_random_pick_with_weight.py: -------------------------------------------------------------------------------- 1 | class Solution(object): 2 | 3 | def __init__(self, w): 4 | """ 5 | :type w: List[int] 6 | """ 7 | self.prefix_sum_array = [] 8 | 9 | prefix_sum = 0 10 | for weight in w: 11 | prefix_sum += weight 12 | self.prefix_sum_array.append(prefix_sum) 13 | self.total_sum = prefix_sum 14 | 15 | def pickIndex(self): 16 | """ 17 | :rtype: int 18 | """ 19 | from bisect import bisect_left 20 | 21 | prefix_sum = self.total_sum * random.random() 22 | return bisect_left(self.prefix_sum_array, prefix_sum) 23 | 24 | 25 | # Your Solution object will be instantiated and called as such: 26 | # obj = Solution(w) 27 | # param_1 = obj.pickIndex() -------------------------------------------------------------------------------- /python/530_min_absolute_difference_in_BST.py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def getMinimumDifference(self, root: TreeNode) -> int: 9 | 10 | min_diff = float('inf') 11 | 12 | def dfs(node): 13 | nonlocal min_diff 14 | if not node: 15 | return (-1, -1) 16 | 17 | l_min_val, l_max_val = dfs(node.left) 18 | r_min_val, r_max_val = dfs(node.right) 19 | 20 | if l_max_val != -1: 21 | min_diff = min(min_diff, abs(l_max_val - node.val)) 22 | else: 23 | l_min_val = node.val 24 | 25 | if r_min_val != -1: 26 | min_diff = min(min_diff, abs(r_min_val - node.val)) 27 | else: 28 | r_max_val = node.val 29 | 30 | return min(l_min_val, node.val), max(node.val, r_max_val) 31 | 32 | dfs(root) 33 | 34 | return min_diff 35 | -------------------------------------------------------------------------------- /python/539_minimum_time_difference.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def findMinDifference(self, timePoints: List[str]) -> int: 3 | 4 | def to_minutes(time_str): 5 | return int(time_str[0:2]) * 60 + int(time_str[3:]) 6 | 7 | timestamps = [to_minutes(time_str) for time_str in timePoints] 8 | 9 | timestamps.sort() 10 | 11 | min_gap = float('inf') 12 | for index in range(1, len(timestamps)): 13 | min_gap = min(min_gap, timestamps[index] - timestamps[index-1]) 14 | 15 | tail_to_head = 24 * 60 - timestamps[-1] + timestamps[0] 16 | min_gap = min(min_gap, tail_to_head) 17 | 18 | return min_gap -------------------------------------------------------------------------------- /python/542_01_matrix.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def updateMatrix(self, mat: List[List[int]]) -> List[List[int]]: 4 | 5 | ROWS = len(mat) 6 | COLS = len(mat[0]) 7 | 8 | def bfs(pos): 9 | visited = set([pos]) 10 | queue = deque([(pos, 0)]) 11 | 12 | while queue: 13 | (row, col), distance = queue.popleft() 14 | 15 | for ro, co in [(0, 1), (1, 0), (0, -1), (-1, 0)]: 16 | new_row, new_col = row + ro, col + co 17 | if 0 > new_row or new_row == ROWS or 0 > new_col or new_col == COLS: 18 | continue 19 | 20 | if (new_row, new_col) in visited: 21 | continue 22 | 23 | if mat[new_row][new_col] == 0: 24 | return distance + 1 25 | else: # == 1 26 | visited.add((new_row, new_col)) 27 | queue.append(((new_row, new_col), distance+1)) 28 | 29 | # should never have arrived here. 30 | return distance 31 | 32 | res = [[0]*COLS for _ in range(ROWS)] 33 | 34 | for row in range(ROWS): 35 | for col in range(COLS): 36 | if mat[row][col] == 1: 37 | res[row][col] = bfs((row, col)) 38 | 39 | return res 40 | 41 | 42 | -------------------------------------------------------------------------------- /python/547_number_of_provinces.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def findCircleNum(self, isConnected: List[List[int]]) -> int: 3 | 4 | def isExplored(node, visited): 5 | """ 6 | To explore the province starting from the current node. 7 | Mark all connected cities as visited. 8 | 9 | return: True if the current city is visited already. 10 | otherwise false. 11 | """ 12 | if visited[node]: 13 | return True 14 | 15 | visited[node] = True 16 | is_connected = isConnected[node] 17 | for neighbor in range(0, city_num): 18 | if is_connected[neighbor] == 1 and not visited[neighbor]: 19 | isExplored(neighbor, visited) 20 | 21 | return False 22 | 23 | city_num = len(isConnected) 24 | visited = [False for _ in range(city_num)] 25 | 26 | province_cnt = 0 27 | for city in range(city_num): 28 | if not isExplored(city, visited): 29 | province_cnt += 1 30 | 31 | return province_cnt 32 | -------------------------------------------------------------------------------- /python/559_maximum_depth_N-ary_tree.py: -------------------------------------------------------------------------------- 1 | """ 2 | # Definition for a Node. 3 | class Node: 4 | def __init__(self, val=None, children=None): 5 | self.val = val 6 | self.children = children if children else [] 7 | """ 8 | 9 | class Solution: 10 | def maxDepth(self, root: 'Node') -> int: 11 | 12 | max_depth = 0 13 | 14 | def dfs(node, curr_depth): 15 | nonlocal max_depth 16 | 17 | if node is None: 18 | return 19 | 20 | if len(node.children) == 0: 21 | max_depth = max(max_depth, curr_depth) 22 | else: 23 | for child in node.children: 24 | dfs(child, curr_depth+1) 25 | 26 | dfs(root, 1) 27 | 28 | return max_depth 29 | -------------------------------------------------------------------------------- /python/565_Nesting_Array.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def arrayNesting(self, nums: List[int]) -> int: 3 | 4 | max_length = -1 5 | visited = [False] * len(nums) 6 | 7 | for i in range(0, len(nums)): 8 | if visited[i]: 9 | continue 10 | start, count = nums[i], 0 11 | visited[i] = True 12 | # form the cycle 13 | while True: 14 | start = nums[start] 15 | visited[start] = True 16 | count += 1 17 | if start == nums[i]: 18 | break 19 | 20 | max_length = max(max_length, count) 21 | 22 | return max_length -------------------------------------------------------------------------------- /python/56_merge_intervals.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def merge(self, intervals: List[List[int]]) -> List[List[int]]: 4 | 5 | intervals.sort(key = lambda x: x[0]) 6 | 7 | merged = [] 8 | for interval in intervals: 9 | if len(merged) == 0 or merged[-1][1] < interval[0]: 10 | # add a new interval 11 | merged.append(interval) 12 | else: 13 | # update the previous interval 14 | merged[-1][1] = max(merged[-1][1], interval[1]) 15 | 16 | return merged 17 | 18 | -------------------------------------------------------------------------------- /python/572_subtree_of_another_tree.py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def isSubtree(self, s: TreeNode, t: TreeNode) -> bool: 9 | 10 | def isIdentical(a, b): 11 | if a is None or b is None: 12 | return (a is None) and (b is None) 13 | if a.val != b.val: 14 | return False 15 | if not isIdentical(a.left, b.left): 16 | return False 17 | return isIdentical(a.right, b.right) 18 | 19 | if s is None: 20 | return False 21 | 22 | if s.val == t.val: 23 | if isIdentical(s, t): 24 | return True 25 | 26 | if self.isSubtree(s.left, t): 27 | return True 28 | else: 29 | return self.isSubtree(s.right, t) 30 | -------------------------------------------------------------------------------- /python/593_valid_square.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def validSquare(self, p1: List[int], p2: List[int], p3: List[int], p4: List[int]) -> bool: 3 | 4 | points = sorted([tuple(p1), tuple(p2), tuple(p3), tuple(p4)]) 5 | 6 | diag1 = (points[3][0] - points[0][0], 7 | points[3][1] - points[0][1]) 8 | 9 | diag2 = (points[2][0] - points[1][0], 10 | points[2][1] - points[1][1]) 11 | 12 | mid1 = ((points[3][0] + points[0][0]) / 2, 13 | (points[3][1] + points[0][1]) / 2) 14 | mid2 = ((points[2][0] + points[1][0]) / 2, 15 | (points[2][1] + points[1][1]) / 2) 16 | 17 | # vectors of the same length 18 | # orthogonal vectors 19 | if mid1 != mid2 or \ 20 | (diag1[0] ** 2 + diag1[1] ** 2) == 0 or \ 21 | (diag1[0] * diag2[0] + diag1[1] * diag2[1]) != 0.0 or \ 22 | (diag1[0] ** 2 + diag1[1] ** 2) != (diag2[0] ** 2 + diag2[1] ** 2): 23 | return False 24 | 25 | return True -------------------------------------------------------------------------------- /python/611_valid_triangle_number.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | """ 3 | Time limit exceeded 4 | """ 5 | def triangleNumber(self, nums: List[int]) -> int: 6 | 7 | triangle_cnt = 0 8 | nums.sort() 9 | 10 | def backtrack(start, comb): 11 | nonlocal triangle_cnt 12 | if len(comb) == 3: 13 | if comb[0] + comb[1] > comb[2] and comb[2] - comb[1] < comb[0]: 14 | triangle_cnt += 1 15 | return 16 | 17 | for i in range(start, len(nums)): 18 | comb.append(nums[i]) 19 | backtrack(i + 1, comb) 20 | comb.pop() 21 | 22 | backtrack(0, []) 23 | 24 | return triangle_cnt 25 | 26 | 27 | class SolutionThreePointers: 28 | def triangleNumber(self, nums: List[int]) -> int: 29 | n = len(nums) 30 | 31 | triangle_cnt = 0 32 | nums.sort() 33 | 34 | for third in range(n-1, 1, -1): 35 | # fixing on the third side 36 | first = 0 37 | second = third - 1 38 | while first < second: 39 | if nums[first] + nums[second] > nums[third]: 40 | triangle_cnt += second - first 41 | # try another candidate for the second side 42 | second -= 1 43 | else: 44 | # the first side is too small 45 | first += 1 46 | 47 | return triangle_cnt -------------------------------------------------------------------------------- /python/621_task_scheduler.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def leastInterval(self, tasks: List[str], n: int) -> int: 3 | 4 | task_queue = [] 5 | 6 | task_count = defaultdict(int) 7 | for task in tasks: 8 | task_count[task] += 1 9 | 10 | for task, count in task_count.items(): 11 | task_queue.append((-count, task)) 12 | 13 | heapq.heapify(task_queue) 14 | 15 | total_slots = 0 16 | 17 | while task_queue: 18 | slots_window = n + 1 19 | 20 | # populate one interval of slots 21 | slot_arrange = [] 22 | while task_queue and slots_window > 0: 23 | count, task = heapq.heappop(task_queue) 24 | slot_arrange.append((task, count+1)) 25 | slots_window -= 1 26 | total_slots += 1 27 | 28 | # need to re-insert the remain tasks 29 | for task, count in slot_arrange: 30 | if count != 0: 31 | heapq.heappush(task_queue, (count, task)) 32 | 33 | if len(task_queue) == 0: 34 | break 35 | 36 | # padding with idle slots 37 | total_slots += slots_window 38 | 39 | return total_slots 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /python/628_maximum_product_of_three_numbers.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def maximumProduct(self, nums: List[int]) -> int: 4 | 5 | nums.sort() 6 | 7 | # the maximum value for the product of three numbers can only be the following two cases: 8 | # - top 3 numbers 9 | # - top 1 number * bottom 2 numbers 10 | return max(nums[-1] * nums[-2] * nums[-3], 11 | nums[0] * nums[1] * nums[-1]) 12 | -------------------------------------------------------------------------------- /python/637_average_of_levels_in_binary_tree.py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def averageOfLevels(self, root: TreeNode) -> List[float]: 9 | if root is None: 10 | return [] 11 | 12 | queue = deque([root]) 13 | avg_list = [] 14 | while queue: 15 | level_size = len(queue) 16 | 17 | level_sum = 0 18 | for i in range(level_size): 19 | node = queue.popleft() 20 | level_sum += node.val 21 | 22 | for next_node in [node.left, node.right]: 23 | if next_node is not None: 24 | queue.append(next_node) 25 | 26 | avg_list.append(level_sum / level_size) 27 | 28 | return avg_list 29 | -------------------------------------------------------------------------------- /python/647_palindromic_substrings.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def countSubstrings(self, s: str) -> int: 4 | 5 | @functools.lru_cache(maxsize=None) 6 | def isPalindrome(start, end): 7 | if start >= end: 8 | return True 9 | 10 | if s[start] != s[end]: 11 | return False 12 | else: 13 | return isPalindrome(start+1, end-1) 14 | 15 | substr_cnt = 0 16 | 17 | for end in range(0, len(s)): 18 | for start in range(0, end+1): 19 | if isPalindrome(start, end): 20 | substr_cnt += 1 21 | 22 | return substr_cnt 23 | 24 | 25 | class Solution: 26 | def countSubstrings(self, s: str) -> int: 27 | 28 | str_len = len(s) 29 | 30 | def countPalindromicSubstr(start, end): 31 | cnt = 0 32 | # expand towards two ends 33 | while start >= 0 and end < str_len: 34 | if s[start] != s[end]: 35 | break 36 | start -= 1 37 | end += 1 38 | cnt += 1 39 | 40 | return cnt 41 | 42 | total_cnt = 0 43 | for center in range(0, str_len): 44 | total_cnt += countPalindromicSubstr(center, center) 45 | total_cnt += countPalindromicSubstr(center, center+1) 46 | 47 | return total_cnt 48 | -------------------------------------------------------------------------------- /python/648_replace_words.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def replaceWords(self, dictionary: List[str], sentence: str) -> str: 3 | 4 | trie = {} 5 | 6 | def update_trie(word, trie): 7 | for letter in word: 8 | if letter not in trie: 9 | trie[letter] = {} 10 | trie = trie[letter] 11 | trie["$"] = word 12 | 13 | # building a Trie data structure for all words 14 | for word in dictionary: 15 | update_trie(word, trie) 16 | 17 | def find_min_prefix(word, trie): 18 | """ 19 | Find the shortest common prefix 20 | """ 21 | for letter in word: 22 | if letter not in trie: 23 | return "" 24 | trie = trie[letter] 25 | if "$" in trie: 26 | return trie["$"] 27 | return "" 28 | 29 | 30 | output = [] 31 | words = sentence.split() 32 | for word in words: 33 | prefix = find_min_prefix(word, trie) 34 | if prefix == "": 35 | output.append(word) 36 | else: 37 | output.append(prefix) 38 | 39 | return " ".join(output) -------------------------------------------------------------------------------- /python/664_Strange_Printer.py: -------------------------------------------------------------------------------- 1 | """ 2 | Reference: https://leetcode.com/problems/strange-printer/discuss/106810/Java-O(n3)-DP-Solution-with-Explanation-and-Simple-Optimization 3 | """ 4 | class Solution: 5 | def strangePrinter(self, s: str) -> int: 6 | str_size = len(s) 7 | if str_size == 0: 8 | return 0 9 | 10 | # init with the value 'n', size of the square 11 | dp = [[str_size]*str_size for i in range(str_size)] 12 | for i in range(str_size): 13 | # reset the diagnoal values 14 | dp[i][i] = 1 15 | 16 | # complexity O(N^3) 17 | for end in range(0, str_size): 18 | for start in range(end, -1, -1): 19 | for mid in range(start, end): 20 | default_print_num = dp[start][mid] + dp[mid+1][end] 21 | if s[mid] == s[end]: 22 | default_print_num -= 1 23 | dp[start][end] = min(dp[start][end], default_print_num) 24 | 25 | 26 | return dp[0][str_size-1] -------------------------------------------------------------------------------- /python/667_Beautiful_Arrangements_II.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def constructArray(self, n: int, k: int) -> List[int]: 3 | 4 | # prefix 5 | nums = [i for i in range(1, n-k)] 6 | 7 | # postfix pattern: 8 | # 1, n, 2, n-1, 3, n-2, .... 9 | for i in range(k+1): 10 | if i % 2 == 0: 11 | nums.append(n-k + i // 2) 12 | else: 13 | nums.append(n - i // 2) 14 | 15 | return nums 16 | -------------------------------------------------------------------------------- /python/669_Trim_Binary_Search_Tree.py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def trimBST(self, root: TreeNode, low: int, high: int) -> TreeNode: 9 | 10 | if root is None: 11 | return root 12 | 13 | left_child = self.trimBST(root.left, low, high) 14 | right_child = self.trimBST(root.right, low, high) 15 | 16 | if root.val < low or root.val > high: 17 | # trim the current node 18 | root = left_child or right_child 19 | else: 20 | root.left = left_child 21 | root.right = right_child 22 | 23 | return root -------------------------------------------------------------------------------- /python/696_count_binary_substrings.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def countBinarySubstrings(self, s: str) -> int: 3 | 4 | # count the groups of identical bits 5 | # e.g. [1110011] -> [3, 2, 2] 6 | groups = [1] 7 | for i in range(1, len(s)): 8 | if s[i] != s[i-1]: 9 | groups.append(1) 10 | else: 11 | groups[-1] += 1 12 | 13 | ans = 0 14 | for gi in range(1, len(groups)): 15 | ans += min(groups[gi], groups[gi-1]) 16 | 17 | return ans 18 | -------------------------------------------------------------------------------- /python/697_degree_of_array.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def findShortestSubArray(self, nums: List[int]) -> int: 3 | 4 | num_range_dict = {} 5 | max_count = 0 6 | 7 | for index, num in enumerate(nums): 8 | if num in num_range_dict: 9 | start, end, count = num_range_dict[num] 10 | new_count = count + 1 11 | num_range_dict[num] = (start, index, new_count) 12 | else: 13 | new_count = 1 14 | num_range_dict[num] = (index, index, new_count) 15 | 16 | if new_count > max_count: 17 | max_count = new_count 18 | 19 | min_range = float("inf") 20 | for _, value in num_range_dict.items(): 21 | start, end, count = value 22 | if count == max_count: 23 | min_range = min(min_range, end-start+1) 24 | 25 | return min_range 26 | 27 | 28 | -------------------------------------------------------------------------------- /python/698_Partition_To_K_Equal_Sum_Subsets.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def canPartitionKSubsets(self, nums: List[int], k: int) -> bool: 3 | """ Time Limit Exceed solution """ 4 | 5 | total_sum = sum(nums) 6 | if total_sum % k != 0: 7 | return False 8 | target = total_sum // k 9 | 10 | @lru_cache(maxsize=None) 11 | def backtrack(curr, comb): 12 | if curr == len(nums): 13 | return all([s == target for s in comb]) 14 | 15 | comb_list = list(comb) 16 | for index, subset_sum in enumerate(comb_list): 17 | if subset_sum + nums[curr] > target: 18 | continue 19 | 20 | comb_list[index] += nums[curr] 21 | if backtrack(curr+1, tuple(sorted(comb_list, reverse=True))): 22 | return True 23 | comb_list[index] -= nums[curr] 24 | 25 | return False 26 | 27 | nums.sort() 28 | if nums[-1] > target: return False 29 | 30 | comb = [0] * k 31 | return backtrack(0, tuple(comb)) 32 | -------------------------------------------------------------------------------- /python/701_Insert_into_a_Binary_Search_Tree.py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode: 9 | 10 | def dfs(node, value): 11 | next_node = None 12 | if value < node.val: 13 | if node.left is None: 14 | # insert as the new left child 15 | node.left = TreeNode(value) 16 | return 17 | else: 18 | next_node = node.left 19 | else: # value > node.val 20 | if node.right is None: 21 | node.right = TreeNode(value) 22 | return 23 | else: 24 | next_node = node.right 25 | # continue the search 26 | dfs(next_node, value) 27 | 28 | if root is None: 29 | return TreeNode(val) 30 | else: 31 | dfs(root, val) 32 | return root 33 | -------------------------------------------------------------------------------- /python/713_Subarray_Product_Less_Than_K.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int: 3 | 4 | if k <= 1: return 0 5 | 6 | window_product = 1 7 | left, count = 0, 0 8 | 9 | for right, val in enumerate(nums): 10 | 11 | window_product *= val 12 | 13 | while window_product >= k: 14 | window_product //= nums[left] 15 | left += 1 16 | 17 | count += right - left + 1 18 | 19 | return count 20 | -------------------------------------------------------------------------------- /python/71_simplify_path.py: -------------------------------------------------------------------------------- 1 | class SolutionRefined: 2 | def simplifyPath(self, path: str) -> str: 3 | 4 | folders = path.split('/') 5 | 6 | canonical = deque([]) 7 | for i, folder in enumerate(folders): 8 | if folder == "" or folder == ".": 9 | # skip 10 | continue 11 | elif folder == "..": 12 | # we might go beyond the root folder 13 | if canonical: 14 | canonical.pop() 15 | else: 16 | canonical.append(folder) 17 | 18 | return "/" + "/".join(canonical) 19 | 20 | 21 | 22 | class Solution: 23 | def simplifyPath(self, path: str) -> str: 24 | 25 | folders = path.split('/') 26 | 27 | canonical = deque([]) 28 | for i, folder in enumerate(folders): 29 | if folder == "" and i > 0: 30 | # skip 31 | continue 32 | elif folder == "..": 33 | # we might go beyond the root folder 34 | if canonical: 35 | canonical.pop() 36 | elif folder == ".": 37 | continue 38 | else: 39 | canonical.append(folder) 40 | 41 | if len(canonical) == 0 or canonical[0] != "": 42 | canonical.appendleft("") 43 | 44 | if len(canonical) == 1: 45 | return "/" 46 | else: 47 | return "/".join(canonical) 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /python/735_asteroids_collision.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def asteroidCollision(self, asteroids: List[int]) -> List[int]: 3 | 4 | stack = [] 5 | 6 | for asteroid in asteroids: 7 | if len(stack) == 0: 8 | stack.append(asteroid) 9 | continue 10 | 11 | to_add = True 12 | 13 | while stack: 14 | tail = stack[-1] 15 | if (tail * asteroid > 0) or (tail < 0 and asteroid > 0): 16 | stack.append(asteroid) 17 | to_add = False 18 | break 19 | else: 20 | # collision 21 | if abs(tail) > abs(asteroid): 22 | # current asteroid is destroyed 23 | to_add = False 24 | break 25 | elif abs(tail) == abs(asteroid): 26 | # both asteroid is destroyed 27 | stack.pop() 28 | to_add = False 29 | break 30 | else: 31 | # previous asteroid is destroyed 32 | stack.pop() 33 | if to_add: 34 | # we still need to add the current asteroid into the stack 35 | stack.append(asteroid) 36 | 37 | return stack -------------------------------------------------------------------------------- /python/739_daily_temperatures.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def dailyTemperatures(self, T: List[int]) -> List[int]: 4 | 5 | mono_stack = [] 6 | answer = [0] * len(T) 7 | 8 | for index, number in enumerate(T): 9 | 10 | while mono_stack: 11 | top_index, top_number = mono_stack[-1] 12 | if top_number < number: 13 | mono_stack.pop() 14 | answer[top_index] = index - top_index 15 | else: 16 | break 17 | 18 | mono_stack.append((index, number)) 19 | 20 | return answer -------------------------------------------------------------------------------- /python/73_set_matrix_zeroes.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def setZeroes(self, matrix: List[List[int]]) -> None: 3 | """ 4 | Do not return anything, modify matrix in-place instead. 5 | """ 6 | 7 | column_header = matrix[0] 8 | col_size = len(column_header) 9 | 10 | # whether to reset the header later 11 | reset_header = False 12 | for header in column_header: 13 | if header == 0: 14 | reset_header = True 15 | 16 | # use the first row as the indicator to reset columns 17 | for row in range(1, len(matrix)): 18 | reset_row = False 19 | for col in range(col_size): 20 | if matrix[row][col] == 0: 21 | column_header[col] = 0 22 | reset_row = True 23 | 24 | if reset_row: 25 | matrix[row] = [0] * col_size 26 | 27 | # reset columns 28 | for col in range(col_size): 29 | if column_header[col] == 0: 30 | for row in range(len(matrix)): 31 | matrix[row][col] = 0 32 | 33 | # reset the column indicator if necessary 34 | if reset_header: 35 | matrix[0] = [0] * col_size 36 | 37 | -------------------------------------------------------------------------------- /python/783_min_difference_between_BST_nodes.py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def minDiffInBST(self, root: TreeNode) -> int: 9 | 10 | min_diff = float('inf') 11 | 12 | def dfs(node): 13 | nonlocal min_diff 14 | 15 | if not node: 16 | return (None, None) 17 | 18 | l_min_val, l_max_val = dfs(node.left) 19 | r_min_val, r_max_val = dfs(node.right) 20 | 21 | if l_max_val: 22 | min_diff = min(min_diff, abs(node.val - l_max_val)) 23 | else: 24 | l_min_val = node.val 25 | 26 | if r_min_val: 27 | min_diff = min(min_diff, abs(node.val - r_min_val)) 28 | else: 29 | r_max_val = node.val 30 | 31 | return min(l_min_val, node.val), max(r_max_val, node.val) 32 | 33 | dfs(root) 34 | 35 | return min_diff 36 | -------------------------------------------------------------------------------- /python/785_is_graph_bipartite.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def isBipartite(self, graph: List[List[int]]) -> bool: 4 | """ 5 | Run a BFS to see if the graph can be colored with two different codes, 6 | i.e. parent and child nodes should have different colors. 7 | """ 8 | colors = {} 9 | nodes_num = len(graph) 10 | 11 | for node in range(nodes_num): 12 | if node in colors: 13 | continue 14 | 15 | colors[node] = 1 16 | queue = deque([node]) 17 | while queue: 18 | curr = queue.popleft() 19 | for child in graph[curr]: 20 | if child not in colors: 21 | colors[child] = - colors[curr] 22 | queue.append(child) 23 | elif colors[child] == colors[curr]: 24 | return False 25 | 26 | return True 27 | -------------------------------------------------------------------------------- /python/791_custom_sort_string.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def customSortString(self, order: str, s: str) -> str: 3 | 4 | # using order to define the priority/order of each letter 5 | letter_priority = {} 6 | for index, letter in enumerate(order): 7 | letter_priority[letter] = index 8 | 9 | # apply the priority for each letter 10 | # Note: by default, for letter with unknown priority, we put them in the front 11 | heap = [] 12 | heapq.heapify(heap) 13 | for letter in s: 14 | priority = -1 15 | if letter in letter_priority: 16 | priority = letter_priority[letter] 17 | heapq.heappush(heap, (priority, letter)) 18 | 19 | output = [] 20 | while heap: 21 | priority, letter = heapq.heappop(heap) 22 | output.append(letter) 23 | 24 | return "".join(output) -------------------------------------------------------------------------------- /python/792_number_of_matching_subsequence.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Solution: 4 | def numMatchingSubseq(self, s: str, words: List[str]) -> int: 5 | 6 | letter_indices = defaultdict(list) 7 | for index, letter in enumerate(s): 8 | letter_indices[letter].append(index) 9 | 10 | def isSubsequence(word): 11 | """ 12 | Greedy matching which ensures to find a solution if there exists one. 13 | 14 | XYZ --> ****X****Y*****Z*** 15 | """ 16 | cursor = -1 17 | for letter in word: 18 | if letter not in letter_indices: 19 | return False 20 | 21 | indices = letter_indices[letter] 22 | first_match = bisect.bisect_right(indices, cursor) 23 | 24 | if first_match == len(indices): 25 | # no match 26 | return False 27 | else: 28 | return True 29 | 30 | count = 0 31 | for word in words: 32 | if isSubsequence(word): 33 | count += 1 34 | return count 35 | 36 | -------------------------------------------------------------------------------- /python/796_ratate_string.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def rotateString(self, s: str, goal: str) -> bool: 3 | if len(s) == 0 and len(goal) == 0: 4 | return True 5 | elif len(s) != len(goal): 6 | return False 7 | 8 | for split_index in range(len(s)): 9 | shift_str = s[split_index:] + s[0:split_index] 10 | if shift_str == goal: 11 | return True 12 | 13 | return False 14 | 15 | 16 | def rotateString_ABAB_BA(self, s: str, goal: str) -> bool: 17 | if len(s) == 0 and len(goal) == 0: 18 | return True 19 | elif len(s) != len(goal): 20 | return False 21 | 22 | return goal in (s+s) 23 | 24 | -------------------------------------------------------------------------------- /python/826_most_profit_assign_work.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def maxProfitAssignment(self, difficulty: List[int], profit: List[int], worker: List[int]) -> int: 4 | """ 5 | Similar problem as finding the intersection between two sorted list 6 | """ 7 | jobs = [(d, p) for (d, p) in zip(difficulty, profit)] 8 | jobs.sort(key = lambda x: x[0]) 9 | 10 | worker.sort() 11 | job_p, worker_p = 0, 0 12 | 13 | max_profit = 0 14 | total_profit = 0 15 | 16 | while worker_p < len(worker): 17 | 18 | # move the job pointer to find the most profitable job based on the current difficulty 19 | while job_p < len(jobs) and jobs[job_p][0] <= worker[worker_p]: 20 | max_profit = max(max_profit, jobs[job_p][1]) 21 | job_p += 1 22 | 23 | # either we take a new job, or the previously most profitable job 24 | total_profit += max_profit 25 | worker_p += 1 26 | 27 | return total_profit -------------------------------------------------------------------------------- /python/835_Image_Overlap.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def largestOverlap(self, A: List[List[int]], B: List[List[int]]) -> int: 3 | 4 | dim = len(A) 5 | 6 | def shift_and_count(x_shift, y_shift, M, R): 7 | """ M: matrix to be moved 8 | R: matrix for reference 9 | moving one matrix up and left is equivalent to 10 | moving the other matrix down and right 11 | """ 12 | count = 0 13 | for r_row, m_row in enumerate(range(y_shift, dim)): 14 | for r_col, m_col in enumerate(range(x_shift, dim)): 15 | if M[m_row][m_col] == 1 and M[m_row][m_col] == R[r_row][r_col]: 16 | count += 1 17 | return count 18 | 19 | max_overlaps = 0 20 | # move one of the matrice up and left and vice versa. 21 | # (equivalent to move the other matrix down and right) 22 | for y_shift in range(0, dim): 23 | for x_shift in range(0, dim): 24 | max_overlaps = max(max_overlaps, shift_and_count(x_shift, y_shift, A, B)) 25 | max_overlaps = max(max_overlaps, shift_and_count(x_shift, y_shift, B, A)) 26 | 27 | return max_overlaps -------------------------------------------------------------------------------- /python/841_rooms_and_keys.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def canVisitAllRooms(self, rooms: List[List[int]]) -> bool: 3 | 4 | visited = set() 5 | 6 | def dfs(curr): 7 | nonlocal visited 8 | 9 | visited.add(curr) 10 | for next_room in rooms[curr]: 11 | if next_room not in visited: 12 | dfs(next_room) 13 | 14 | # kick off the traversal 15 | dfs(0) 16 | 17 | return len(visited) == len(rooms) 18 | -------------------------------------------------------------------------------- /python/844_backspace_string_compare.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def backspaceCompare(self, s: str, t: str) -> bool: 3 | 4 | def simulate(string): 5 | """ 6 | Simulate the input with backspace key (i.e. "#") 7 | return the remaining string 8 | """ 9 | str_stack = [] 10 | for letter in string: 11 | if letter == "#": 12 | if str_stack: 13 | str_stack.pop() 14 | else: 15 | str_stack.append(letter) 16 | 17 | return "".join(str_stack) 18 | 19 | 20 | return simulate(s) == simulate(t) -------------------------------------------------------------------------------- /python/867_transpose_matrix.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def transpose(self, A: List[List[int]]) -> List[List[int]]: 3 | TA = [[None] * len(A) for _ in range(len(A[0]))] 4 | 5 | # This initializaiton wont work!!! The elements are essentially references. 6 | # TA = [[0] * len(A)] * len(A[0]) 7 | 8 | for row in range(len(A)): 9 | for col, num in enumerate(A[row]): 10 | TA[col][row] = num 11 | 12 | return TA -------------------------------------------------------------------------------- /python/89_gray_code.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def grayCode(self, n: int) -> List[int]: 3 | 4 | res = [] 5 | 6 | if n == 0: 7 | return [0] 8 | 9 | res.append(0) 10 | res.append(1) 11 | 12 | shift = 1 13 | while shift < n: 14 | 15 | # obtain the symmetric samples of the current results 16 | size = len(res) 17 | for i in range(size-1, -1, -1): 18 | new_value = res[i] | (1 << shift) 19 | res.append(new_value) 20 | 21 | shift += 1 22 | 23 | return res 24 | -------------------------------------------------------------------------------- /python/905_sort_by_parity.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def sortArrayByParity(self, A: List[int]) -> List[int]: 3 | A.sort(key=lambda x:x%2) 4 | return A 5 | 6 | 7 | class SolutionTwoPointers: 8 | def sortArrayByParity(self, A: List[int]) -> List[int]: 9 | 10 | p_odd, p_even = 0, len(A)-1 11 | while p_odd < p_even: 12 | 13 | if A[p_odd] % 2 == 0: 14 | p_odd += 1 15 | elif A[p_even] % 2 == 1: 16 | p_even -= 1 17 | else: 18 | A[p_odd], A[p_even] = A[p_even], A[p_odd] 19 | 20 | return A -------------------------------------------------------------------------------- /python/91_decode_ways.py: -------------------------------------------------------------------------------- 1 | """ 2 | A message containing letters from A-Z is being encoded to numbers using the following mapping: 3 | 4 | 'A' -> 1 5 | 'B' -> 2 6 | ... 7 | 'Z' -> 26 8 | Given a non-empty string containing only digits, determine the total number of ways to decode it. 9 | 10 | Example 1: 11 | 12 | Input: "12" 13 | Output: 2 14 | Explanation: It could be decoded as "AB" (1 2) or "L" (12). 15 | 16 | 17 | Note: 18 | there are a few "exceptional" test cases, such as '0', '101' and '01' that one should takes into account. 19 | 20 | """ 21 | 22 | from functools import lru_cache 23 | 24 | 25 | class Solution: 26 | def numDecodings(self, s: str) -> int: 27 | return self.count(s) 28 | 29 | @lru_cache(maxsize=None) 30 | def count(self, s): 31 | """ recursion with memoization 32 | """ 33 | if len(s) == 0: 34 | return 1 35 | 36 | if s[0] == '0': 37 | return 0 38 | 39 | if len(s) == 1: 40 | return self.count(s[1:]) 41 | if len(s) >= 2: 42 | if int(s[0:2]) < 27: 43 | return self.count(s[1:]) + self.count(s[2:]) 44 | else: 45 | return self.count(s[1:]) 46 | 47 | -------------------------------------------------------------------------------- /python/921_minimal_add_to_make_parentheses_valid.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def minAddToMakeValid(self, s: str) -> int: 3 | stack = [] 4 | 5 | missing_count = 0 6 | for bracket in s: 7 | if bracket == "(": 8 | stack.append(bracket) 9 | else: # ")" 10 | if len(stack) == 0: 11 | missing_count += 1 12 | else: 13 | stack.pop() 14 | 15 | return missing_count + len(stack) 16 | -------------------------------------------------------------------------------- /python/925_long_pressed_name.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def isLongPressedName(self, name: str, typed: str) -> bool: 3 | 4 | # two pointers to the "name" and "typed" string respectively 5 | np, tp = 0, 0 6 | 7 | # advance two pointers, until we exhaust one of the strings 8 | while np < len(name) and tp < len(typed): 9 | if name[np] == typed[tp]: 10 | np += 1 11 | tp += 1 12 | elif tp >= 1 and typed[tp] == typed[tp-1]: 13 | tp += 1 14 | else: 15 | return False 16 | 17 | # if there is still some characters left *unmatched* in the origin string, 18 | # then we don't have a match. 19 | # e.g. name = "abc" typed = "aabb" 20 | if np != len(name): 21 | return False 22 | else: 23 | # In the case that there are some redundant characters left in typed 24 | # we could still have a match. 25 | # e.g. name = "abc" typed = "abccccc" 26 | while tp < len(typed): 27 | if typed[tp] != typed[tp-1]: 28 | return False 29 | tp += 1 30 | 31 | return True -------------------------------------------------------------------------------- /python/933_number_of_recent_calls.py: -------------------------------------------------------------------------------- 1 | class RecentCounter: 2 | 3 | def __init__(self): 4 | self.slide_window = deque() 5 | 6 | def ping(self, t: int) -> int: 7 | self.slide_window.append(t) 8 | 9 | # invalidate the outdated pings 10 | while self.slide_window: 11 | if self.slide_window[0] < t - 3000: 12 | self.slide_window.popleft() 13 | else: 14 | break 15 | 16 | return len(self.slide_window) 17 | 18 | 19 | # Your RecentCounter object will be instantiated and called as such: 20 | # obj = RecentCounter() 21 | # param_1 = obj.ping(t) -------------------------------------------------------------------------------- /python/938_range_sum_of_BST.py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def rangeSumBST(self, root: TreeNode, L: int, R: int) -> int: 9 | 10 | value_sum = 0 11 | def dfs(node): 12 | nonlocal value_sum 13 | if not node: 14 | return 15 | if L <= node.val <= R: 16 | value_sum += node.val 17 | 18 | if node.val >= L: 19 | dfs(node.left) 20 | if node.val <= R: 21 | dfs(node.right) 22 | 23 | dfs(root) 24 | 25 | return value_sum -------------------------------------------------------------------------------- /python/946_validate_stack_sequences.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def validateStackSequences(self, pushed: List[int], popped: List[int]) -> bool: 3 | 4 | if len(pushed) == 0: 5 | return len(popped) == 0 6 | 7 | stack = list() 8 | push_curr, pop_curr = 1, 0 9 | stack.append(pushed[0]) 10 | 11 | while pop_curr < len(popped): 12 | 13 | if len(stack) > 0 and popped[pop_curr] == stack[-1]: 14 | stack.pop() 15 | pop_curr += 1 16 | elif push_curr < len(pushed): 17 | stack.append(pushed[push_curr]) 18 | push_curr += 1 19 | else: 20 | return False 21 | 22 | return pop_curr == len(popped) -------------------------------------------------------------------------------- /python/949_Lagest_Time_for_Given_Digits.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def largestTimeFromDigits(self, A: List[int]) -> str: 3 | 4 | max_time = -1 5 | # enumerate all possiblities, with the permutation() func 6 | for h, i, j, k in itertools.permutations(A): 7 | hour = h*10 + i 8 | minute = j*10 + k 9 | if hour < 24 and minute < 60: 10 | max_time = max(max_time, hour * 60 + minute) 11 | 12 | if max_time == -1: 13 | return "" 14 | else: 15 | # string formatting with leading zero padding 16 | return "{:02d}:{:02d}".format(max_time // 60, max_time % 60) 17 | -------------------------------------------------------------------------------- /python/94_binary_tree_inorder_traversal.py: -------------------------------------------------------------------------------- 1 | # Definition for a binary tree node. 2 | # class TreeNode: 3 | # def __init__(self, val=0, left=None, right=None): 4 | # self.val = val 5 | # self.left = left 6 | # self.right = right 7 | class Solution: 8 | def inorderTraversal(self, root: TreeNode) -> List[int]: 9 | 10 | def inorderDFS(node, results): 11 | if not node: 12 | return 13 | 14 | inorderDFS(node.left, results) 15 | results.append(node.val) 16 | inorderDFS(node.right, results) 17 | 18 | 19 | results = [] 20 | inorderDFS(root, results) 21 | 22 | return results 23 | -------------------------------------------------------------------------------- /python/953_verifying_an_alien_dictionary.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def isAlienSorted(self, words: List[str], order: str) -> bool: 3 | 4 | # handle the edge case, as well as a cheap optimization 5 | if len(words) <= 1: 6 | return True 7 | 8 | letter_order = {} 9 | for rank, letter in enumerate(order): 10 | letter_order[letter] = rank 11 | 12 | def is_less_than(word1, word2): 13 | bound = min(len(word1), len(word2)) 14 | index = 0 15 | while index < bound: 16 | if letter_order[word1[index]] < letter_order[word2[index]]: 17 | return True 18 | elif letter_order[word1[index]] > letter_order[word2[index]]: 19 | return False 20 | index += 1 21 | 22 | return True if len(word1) <= len(word2) else False 23 | 24 | 25 | prev_word = words[0] 26 | for next_word in words[1:]: 27 | if not is_less_than(prev_word, next_word): 28 | return False 29 | prev_word = next_word 30 | 31 | 32 | return True 33 | 34 | 35 | -------------------------------------------------------------------------------- /python/956_tallest_billboard.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def tallestBillboard(self, rods: List[int]) -> int: 3 | 4 | # knapsack problem 5 | # each rod to be placed in either left or right bin, or discarded 6 | # find the maximum sum of postive numbers, with the total sum of zero 7 | dp = {} 8 | # dp[sum] = max_sum_of_postive_number (left bin) 9 | dp[0] = 0 10 | for rod in rods: 11 | 12 | newDP = defaultdict(int) 13 | for rodSum in dp: 14 | newDP[rodSum] = max(dp[rodSum], newDP[rodSum]) 15 | newDP[rodSum + rod] = max(dp[rodSum] + rod, newDP[rodSum + rod]) 16 | newDP[rodSum - rod] = max(dp[rodSum], newDP[rodSum - rod]) 17 | 18 | dp = newDP 19 | 20 | return dp[0] 21 | -------------------------------------------------------------------------------- /python/957_Prison_Cells_After_N_Days.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def prisonAfterNDays(self, cells: List[int], N: int) -> List[int]: 3 | 4 | seen = defaultdict(int) 5 | is_fast_forwarded = False 6 | 7 | while N > 0: 8 | if not is_fast_forwarded: 9 | state_key = tuple(cells) 10 | last_seen_index = seen[state_key] 11 | if last_seen_index != 0: 12 | N %= seen[state_key] - N 13 | is_fast_forwarded = True 14 | else: 15 | seen[state_key] = N 16 | 17 | if N > 0: 18 | N -= 1 19 | next_day_cells = self.nextDay(cells) 20 | cells = next_day_cells 21 | 22 | return cells 23 | 24 | 25 | def nextDay(self, cells: List[int]): 26 | ret = [] 27 | for i in range(len(cells)): 28 | if i > 0 and i < 7 and cells[i-1] == cells[i+1]: 29 | ret.append(1) 30 | else: 31 | ret.append(0) 32 | return ret 33 | -------------------------------------------------------------------------------- /python/967_Numbers_With_Same_Consecutive_Differences.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def numsSameConsecDiff(self, N: int, K: int) -> List[int]: 3 | 4 | if N == 1: 5 | return [i for i in range(10)] 6 | 7 | ans = [] 8 | 9 | def DFS(N, num): 10 | if N == 1: 11 | return ans.append(num) 12 | 13 | tail_digit = num % 10 14 | next_digits = set([tail_digit + K, tail_digit - K]) 15 | for next_digit in next_digits: 16 | if 0 <= next_digit < 10: 17 | new_num = num * 10 + next_digit 18 | DFS(N-1, new_num) 19 | 20 | for num in range(1, 10): 21 | DFS(N, num) 22 | 23 | return list(ans) 24 | 25 | -------------------------------------------------------------------------------- /python/973_k_closest_points_to_origin.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]: 3 | 4 | distance_queue = [] 5 | 6 | for point in points: 7 | x, y = point 8 | distance = x ** 2 + y ** 2 9 | distance_queue.append((distance, [x, y])) 10 | 11 | heapq.heapify(distance_queue) 12 | output = [] 13 | while k: 14 | distance, point = heapq.heappop(distance_queue) 15 | output.append(point) 16 | k -= 1 17 | 18 | return output -------------------------------------------------------------------------------- /python/974_Subarray_Sums_Divisable_by_K.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def subarraysDivByK(self, A: List[int], K: int) -> int: 3 | 4 | prefix_sum_group = defaultdict(int) 5 | 6 | prefix_sum = 0 7 | count = 0 8 | 9 | for num in A: 10 | prefix_sum += num 11 | prefix_sum %= K 12 | prefix_sum_group[prefix_sum] += 1 13 | 14 | for group_key, group_size in prefix_sum_group.items(): 15 | if group_key == 0: 16 | # number of solutions constructed by modulo of K 17 | count += (group_size * (group_size -1))/2 + group_size 18 | elif group_size > 1: 19 | # number of solutions constructed by non-modulo of K 20 | count += group_size * (group_size -1) / 2 21 | 22 | return int(count) 23 | -------------------------------------------------------------------------------- /python/977_squares_of_a_sorted_array.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def sortedSquares(self, A: List[int]) -> List[int]: 3 | 4 | result = [0] * len(A) 5 | 6 | # pointers to left and right end of the array. 7 | lp, rp = 0, len(A)-1 8 | sp = len(A) - 1 # pointer for the result list 9 | 10 | while sp >= 0: 11 | if abs(A[lp]) > abs(A[rp]): 12 | result[sp] = A[lp] * A[lp] 13 | lp += 1 14 | else: 15 | result[sp] = A[rp] * A[rp] 16 | rp -= 1 17 | sp -= 1 18 | 19 | return result 20 | -------------------------------------------------------------------------------- /python/983_minimum_costs_for_tickets.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def mincostTickets(self, days: List[int], costs: List[int]) -> int: 4 | 5 | """ 6 | Dynamic programming in the bottom-up approach 7 | """ 8 | N = len(days) 9 | 10 | # dp[i] the minimum cost if the travel is of the postfix: days[i:] 11 | dp = [0] * (N+1) 12 | 13 | # should be the 1-day pass, if the input is designed properly 14 | # i.e. one day should be the least expensive of all. 15 | # the minimal cost of ticket if the travel plan consists of one day 16 | dp[N-1] = min(costs) 17 | 18 | for index in range(N-2, -1, -1): 19 | curr_day = days[index] 20 | min_cost = float('inf') 21 | 22 | # Try different plans from the current day till the end 23 | for ci, duration in enumerate([1, 7, 30]): 24 | next_day_index = index 25 | while next_day_index < N and days[next_day_index] < curr_day + duration : 26 | next_day_index += 1 27 | 28 | min_cost = min(min_cost, costs[ci] + dp[next_day_index]) 29 | # fill the best plan for the postfix plan, days[index:] 30 | dp[index] = min_cost 31 | 32 | return dp[0] 33 | -------------------------------------------------------------------------------- /python/insert_digit_5.py: -------------------------------------------------------------------------------- 1 | """ 2 | Insert the digit '5' to an integer to make the resulting integer as big as possible 3 | 4 | e.g. 5 | 234 -> 5234 6 | 679 -> 6795 7 | -23 -> -235 8 | """ 9 | 10 | from collections import deque 11 | 12 | def solution(N): 13 | 14 | positive = True if N >= 0 else False 15 | 16 | if not positive: 17 | N = - N 18 | 19 | # split the integer number into a list of digits 20 | nums = deque([]) 21 | remainder = N 22 | while remainder > 0: 23 | nums.appendleft(remainder % 10) 24 | remainder = remainder // 10 25 | 26 | new_nums = [] 27 | inserted = False 28 | for digit in nums: 29 | if positive: 30 | if 5 >= digit and not inserted: 31 | new_nums.append(5) 32 | inserted = True 33 | new_nums.append(digit) 34 | else: 35 | if 5 <= digit and not inserted: 36 | new_nums.append(5) 37 | inserted = True 38 | new_nums.append(digit) 39 | 40 | if not inserted: 41 | new_nums.append(5) 42 | 43 | # aggregate a list of digits into an integer 44 | power = 1 45 | result = 0 46 | for digit in reversed(new_nums): 47 | result += power * digit 48 | power = power * 10 49 | 50 | if positive: 51 | return result 52 | else: 53 | return -result 54 | -------------------------------------------------------------------------------- /python/minimal_coin_changes.py: -------------------------------------------------------------------------------- 1 | from functools import lru_cache 2 | 3 | 4 | """ 5 | Given an amount, return the combination of coin changes, 6 | so that the total number of coins is minimal. 7 | The available coins are [25, 10, 5, 1] 8 | """ 9 | 10 | def make_change(amount): 11 | 12 | min_comb = (float("inf"), 0, 0, 0) 13 | 14 | @lru_cache(maxsize=None) 15 | def dp(sub_amount, comb): 16 | nonlocal min_comb 17 | #print(sub_amount, comb) 18 | 19 | if sub_amount < 0: 20 | return 21 | elif sub_amount == 0: 22 | if sum(comb) < sum(min_comb): 23 | min_comb = comb 24 | return 25 | 26 | for index, coin in enumerate([25, 10, 5, 1]): 27 | if sub_amount >= coin: 28 | next_comb = list(comb) 29 | next_comb[index] += 1 30 | dp(sub_amount - coin, tuple(next_comb)) 31 | 32 | init_comb = (0, 0, 0, 0) 33 | 34 | dp(amount, init_comb) 35 | return min_comb 36 | --------------------------------------------------------------------------------