├── C practice files ├── enums.c ├── enums.exe ├── memory_allocation.c ├── pointer.c ├── pointer1.c ├── pointer1.exe ├── pointer10.c ├── pointer10.exe ├── pointer11.c ├── pointer11.exe ├── pointer12.c ├── pointer12.exe ├── pointer13.c ├── pointer13.exe ├── pointer14.exe ├── pointer2.c ├── pointer2.exe ├── pointer3.c ├── pointer3.exe ├── pointer4.c ├── pointer4.exe ├── pointer5.c ├── pointer5.exe ├── pointer6.c ├── pointer6.exe ├── pointer7.c ├── pointer7.exe ├── pointer8.c ├── pointer8.exe ├── pointer9.c ├── pointer9.exe ├── pointers_intro.c ├── pointers_intro.exe ├── structs.c ├── typedefinition.c ├── typedefinition.exe ├── unions.c └── unions.exe ├── Game-playing ├── combinatorial-game-playing.MD ├── minimax_game_playing.py └── minimax_tic_tac_toe.py ├── General_tips.MD ├── MO's_algorithm_sqrt_decomposition.py ├── README.md ├── Recursion_trick_CP.py ├── Time_complexity.MD ├── advanced ds ├── avl_tree.py ├── graphs.py ├── red_black_tree.py ├── splay_tree_bottom_up.py └── splay_tree_top_down.py ├── arrays ├── Book_allocation_problem.py ├── DNF_three_way_partitioning.py ├── H-index.py ├── Interview_patterns.MD ├── Longest_consecutive_ones.py ├── Maximum_consecutive_onesIII.py ├── Median_sliding_window.py ├── aggresive_cows.py ├── arrange_biggest_number.py ├── array_pair_target_sum.py ├── array_quadrapulet.py ├── array_queue.py ├── array_rotate.py ├── best_buy_sell_stocks.py ├── bit_arrays.py ├── candy_distribution.py ├── check_if_rectangles_overlap.py ├── check_jump_game.py ├── check_monotonic_array.py ├── check_strictly_increasing.py ├── close_nums_palintir.py ├── closest_pair_sorted.py ├── closest_pair_sorted_UNSORTED_arrays.py ├── coding_blocks_scholarships.py ├── combinations_generate.py ├── combinations_sum.py ├── count_LED_changes.py ├── count_subarrays_XOR_K.py ├── equilibrium_index_arrays.py ├── find_k_closest_elements.py ├── find_odd_occurence_number.py ├── find_rotation_count.py ├── first_last_occurence.py ├── first_least_and_last_greater_element.py ├── first_negative_integer_every_subarrays.py ├── first_positive_missing_number.py ├── fisher_yates_random_sattolo_algo.py ├── generate_power_set.py ├── guess_number_bin_search.py ├── gym_lockers.py ├── is_palindrome_number.py ├── jump_hopes.py ├── k_closest_points_origin.py ├── kth_permutation_sequence.py ├── largest_subarray_zero_sum.py ├── largest_sum_non_adjacent.py ├── least_capacity_within_D_days.py ├── longest_consecutive_subsequence.py ├── longest_subarray_sum_k.py ├── longest_switching_subarrays.py ├── majority_element_array.py ├── max_length_biotonic_subarray.py ├── max_product_subarrays.py ├── max_sum_product_subarrays_window_sliding.py ├── maximum6_and9.py ├── maximum_character_between_two_same_chars.py ├── maximum_contiguous_circular_sum.py ├── maximum_every_subarray_k_window_sliding.py ├── maximum_of_minimum_window_sizes.py ├── maximum_subarray_kadane.py ├── maximum_sum_biotonic_subarray.py ├── maximum_sum_k_subarray.py ├── merge_overlapping_intervals.py ├── merge_two_sorted_arrays.py ├── min_jump_game.py ├── min_operations_N.py ├── missing_number_list.py ├── moving_zeros.py ├── next_permutation_find.py ├── painters_partition_problem.py ├── pancake_sorting.py ├── pascal.py ├── peak_element.py ├── pivot_sorted_rotated_array.py ├── player.py ├── prefix_sum.py ├── product_except_self.py ├── reverse_digits_numbers.py ├── reverse_word_by_word.py ├── rockpaperscissorgame.py ├── roman_to_integer.py ├── search_rotated_sorted_array.py ├── segregate_zeroes_ones_efficient.py ├── smallest_subarrays_sum_greater_equal_k.py ├── sort_transformed_array_or_square.py ├── sorted_negative_integers.py ├── staright_line_check.py ├── streak.py ├── subarray_sum.py ├── subset_generation.py ├── sum_n_divisible.py ├── sum_of_digits_using_arrays.py ├── triplet_target_array.py ├── ultra_fast_strings.py └── union_intersection_arrays.py ├── binary search trees ├── BST_iterator.py ├── Searching_BST.py ├── binary_search_tree_intro.py ├── check_BST.py ├── counting_unique_BST_n_nodes.py ├── deletion_BST.py ├── flatten_BST.py └── insertion_BST.py ├── binary trees ├── Interview_patterns.MD ├── LCA.py ├── Max_path_sum_any_node.py ├── binary_level_by_level_print.py ├── binary_trees_intro.py ├── binary_values_sum.py ├── bottom_left_value_tree.py ├── bottom_view_tree.py ├── build_array_preorder_postorder.py ├── build_balanced_tree_array.py ├── check_Height_balanced_binary_tree.py ├── check_identical_trees.py ├── clone_binary_tree.py ├── connect_nodes_at_savelevel.py ├── convert_binary_tree_doublylinkedlist.py ├── convert_binary_tree_linkedlist.py ├── count_and_sum_nodes.py ├── count_sum_leaf_nodes.py ├── count_sum_left_leaves.py ├── counting_labelled_unlabelled_trees.py ├── deepest_node_binary_tree.py ├── deletion_binary_tree.py ├── diameter_of_tree.py ├── flatten_binary_tree.py ├── height_of_tree.py ├── inorder_traversal_DFS.py ├── insertion_binary_tree.py ├── invert_binary_tree.py ├── level_order_spiral_traversal.py ├── level_order_traversal_BFS.py ├── max_path_leaf_to_leaf.py ├── max_path_leaf_tree_dp.py ├── max_path_node_tree_dp.py ├── morris_traversal.py ├── path_from_root_to_node.py ├── postorder_traversal_DFS.py ├── preorder_traversal_DFS.py ├── print_all_root_leaf_paths.py ├── print_k_distance_target_node.py ├── print_kth_level_of_tree.py ├── print_left_view_tree.py ├── right_view_binary_tree.py ├── serialize_deserialize_trees.py ├── shortest_path_nodes.py ├── sum_replacement.py ├── symmetric_tree.py ├── vertex_cover_problem.py └── vertical_traversal_level.py ├── bitwise ├── add_using_bitwise_operators.py ├── bit_trick_sum.py ├── bits_magic.py ├── compare_without_operators.py ├── compliment_number.py ├── count_1_set_bits.py ├── count_set_bits_range_query.py ├── decimal_to_binary.py ├── fast_exponentiation_binary_algo_power.py ├── find_random_added_string_XOR.py ├── find_right_most_setbit.py ├── find_two_unique_numbers.py ├── find_unique_element_array.py ├── find_unique_repeated_thrice.py ├── get_min_max_without_operators.py ├── odd_even.py └── reverse_bits_int.py ├── cc.JPG ├── concurrency_leetcode ├── H20_barrier.py ├── bounded_blocking_queue.py ├── dining_philosophers.py ├── fizzBuzz.py ├── html_parser_multithreaded.py ├── light_controlled_intersection.py ├── print_even_odd.py ├── print_foobar.py └── print_order.py ├── cp_template.md ├── cs50 ├── pset1.py ├── pset2.py └── pset2_2.py ├── custom data structures ├── Construct_hash_map.py ├── Design_linked_list.py ├── LRU_cache.py ├── conversion_data_structure_units.py ├── efficient_findMin_findMax_heaps.py ├── efficient_insert_delete_search_random.py ├── hit_counter_design.py └── peeking_iterator.py ├── divide and conquer ├── DAC_patterns.MD ├── Matrix_product_optimized_straseen_algo.py ├── closest_pair.py ├── count_inversions.py ├── merge_sort.py ├── product_integers_karatsuba_algo.py └── quick_sort.py ├── dynamic programming ├── 0-1_knapsack.py ├── 01_matrix_shortest_dist.py ├── Coin_Change_ways.py ├── Interview_tricks.MD ├── LCS_three_strings.py ├── Longest_common_subsequence.py ├── bell_numbers.py ├── boolean_parenthisization.py ├── box_stacking.py ├── catalan_numbers.py ├── coin_minimum_change.py ├── count_construct_target_string.py ├── count_form_target_string.py ├── count_of_subset_sum.py ├── count_subset_sum_diff.py ├── count_target_sums.py ├── count_unique_paths_grids.py ├── decode_ways_string_dp.py ├── diameter_tree_dp_general_syntax.py ├── egg_dropping_puzzle.py ├── equal_sum_partition_problem.py ├── fibonacci.py ├── largest_square_matrix_ones.py ├── longest_common_substring.py ├── longest_increasing_subsequences.py ├── longest_palindrome_subsequence.py ├── longest_palindromic_substring.py ├── longest_path_matrix.py ├── longest_repeating_subsequence.py ├── matrix_chain_multiplication.py ├── max_longest_chain_pairs.py ├── max_path_sum_leaf_to_leaf.py ├── max_path_sum_node_to_node.py ├── maximize_cut_segments.py ├── maximum_subarray_kadane.py ├── maximum_sum_increasing_subsequence.py ├── maximum_sum_rectangle_2d_matrix.py ├── maximum_sum_rectangle_2d_matrix0.py ├── min_path_cost.py ├── min_path_sum_mod.py ├── minimum_cost_climb.py ├── minimum_deletions_palindrome.py ├── minimum_insertion_deletions_transform.py ├── minimum_insertions_palindrome.py ├── minimum_maximum_value_exp.py ├── minimum_subset_sum_difference.py ├── number_of_ways_coins.py ├── optimal_game_strategy-1.py ├── painters-partition.py ├── palindromic_partitioning.py ├── print_LCS.py ├── print_knapsack.py ├── print_shortest_common_supersequence.py ├── rod_cutting_problem.py ├── rotten_oranges_dp.py ├── scrambled_string_check.py ├── shortest_common_supersequence.py ├── staircase.py ├── string_edit_levenstein_dist.py ├── subset_sum.py ├── ugly_numbers.py ├── unbounded_fractional_problem.py ├── vertex_cover_DP.py └── word_break.py ├── fast_IO_template_PY3.py ├── fast_IO_template_pypy2.py ├── file _handling_threading_requests_practice ├── file practice1.py ├── file practice2.py ├── request_practice.py └── threading_programs.py ├── graphs ├── BFS_matrix.py ├── BFS_traversals.py ├── DFS_matrix.py ├── DFS_traversals.py ├── Number_of_islands.py ├── Reconstruct_itinerary.py ├── Sum_of_degree_nodes.py ├── alien_dictionary_order.py ├── array_strings_chaining_check.py ├── articulation_points_graphs.py ├── bellman_ford.py ├── bidirectional_BFS.py ├── check_bipartite_graphs.py ├── clone_undirected_graph.py ├── compute_bridges_tarjans_graphs.py ├── connected_components.py ├── count_all_cycles_graph.py ├── count_degree_cycle_graphs.py ├── course_schedule.py ├── critical_connections.py ├── detect_cycle.py ├── detect_cycle_with_degree_nodes.py ├── dijkstra_algo_adj_list.py ├── dijkstra_algo_adj_matrix.py ├── disjoint_sets.py ├── dislikes_bipartite_graphs.py ├── euler_graphs.py ├── find_diameter_cycles_edges.py ├── flood_fill.py ├── floyd_warshall_algo.py ├── friendship_circles.py ├── graphs_functions.py ├── graphs_traversals.py ├── graphs_unweighted.py ├── graphs_weighted.py ├── jump_game_check.py ├── kruskal_MST.py ├── min_jump_game.py ├── minimum_days_N_oranges_graphs.py ├── minimum_ops_network_connected.py ├── minimum_swaps_graphs.py ├── network_delay_graphs.py ├── prims_MST.py ├── print_cycles_undirected_graph.py ├── print_euler_path_fleury_algo.py ├── rotten_oranges.py ├── shortest_path_BFS.py ├── solving_maze_DFS.py ├── strongly_connected_components.py ├── topological_sorting.py ├── word_boogle.py ├── word_ladder.py └── word_ladder_2.py ├── greedy ├── activity_selection_greedy.py ├── denominations.py ├── find_rooms_for_meeting.py ├── fractional_knapsack.py ├── graph_colouring.py ├── huffman_coding.py ├── job_sequencing.py ├── max_sum_three_stacks.py └── meeting_time_intervals.py ├── hashing ├── hash_map.py └── hashmap.py ├── heaps_and_priorityQueues ├── Interview_patterns.MD ├── check_binary_heap.py ├── heap_build_from_array.py ├── heap_built_in.py ├── heap_sort.py ├── heaps.py ├── heaps_decrease_key_mod.py ├── kth_smallest_ele_sorted_matrix.py ├── kth_smallest_element.py ├── median_stream.py ├── merge_k_sorted_arrays.py ├── merge_k_sorted_different_sizes.py ├── min_join_ropes_length.py ├── nearly_sorted_array.py ├── priority_queues_built_in.py ├── priority_queues_modified.py ├── priority_queues_scratch.py ├── skyline_problem.py └── top_k_frequent_elements.py ├── linked lists ├── Interview_patterns.MD ├── check_palindrome_linked_list.py ├── circular_linked_lists.py ├── clone_random_ptr_linked_list.py ├── delete_node_linkedlist.py ├── delete_node_trick.py ├── delete_val_from_linkedlist.py ├── detect_loop_and_removal.py ├── detect_loop_list.py ├── doubly_linked_lists.py ├── find_intersection_point.py ├── flattening_multi_level_linked_list.py ├── linked_list_reverse.py ├── linked_lists.py ├── linked_lists_add_numbers.py ├── linked_lists_add_numbers_non_trivial.py ├── merge_sort_linked_lists.py ├── middle_linked_list.py ├── nth_node_from_the_end.py ├── reorder_linked_list.py └── swap_nodes.py ├── matrix specific ├── Design_snake_game.py ├── MATRIX_interview_patterns.MD ├── anti-spiral_matrix_traversal.py ├── check_if_tictactoe_won_valid.py ├── check_valid_sudoku.py ├── count_negative_sorted_mat.py ├── diagonal_matrix_traversal.py ├── island_perimeter.py ├── lucky_numbers.py ├── magical_park.py ├── matrix_multiplication_naive.py ├── matrix_sorted_search.py ├── maximum_sum_rectangle_2d_matrix.py ├── maximum_sum_submatrix_sorted_matrix.py ├── multiplication_table_search.py ├── robot_origin_check.py ├── rotate_image_array.py ├── spiral_traversal_matrix.py ├── submatrix_generate.py ├── sum_all_submatrices.py ├── sum_submatrix_range_queries.py └── wave_and_snake_print.py ├── number theory ├── bell_numbers.py ├── catalan_numbers.py ├── check_perfect_square.py ├── count_prime_numbers_seive.py ├── counting_divisors_seive.py ├── desktop.ini ├── efficient_fact_binomial.py ├── fast_exponentiation_binary_algo.py ├── fibonacci_efficient.py ├── finding_first_positive_monotonic_function.py ├── gcd.py ├── gcd_extended.py ├── gcd_lcm_efficient.py ├── kth_root.py ├── linear_diphonatine_equations.py ├── mathemagica │ └── mathemagica.py ├── mod_exponentiation.py ├── mod_inverse.py ├── prime_factors.py ├── seive_prime_factors.py ├── seive_prime_numbers.py └── sqrt_finding_binary_search.py ├── pattern print ├── command_prompt_printing.py └── magicdiamond.py ├── prefix_cum_sum_array.py ├── random algorithms └── monte_carlo_pi.py ├── recursion and backtracking ├── N-queens.py ├── backtracking_patterns.MD ├── brackets_generation.py ├── combinations.py ├── count_all_paths_matrix.py ├── factorial.py ├── fibonacci.py ├── flood_fill.py ├── generate_n_bit_gray_codes.py ├── heap's_algo_permutate.py ├── knight_tour_problem.py ├── permutate_strings.py ├── power_exponent.py ├── print_all_paths_matrix.py ├── rat_maze_problem.py ├── recursion.md ├── recursion_patterns.MD ├── solve_sudoko.py ├── solving_cryptoarithmetic_puzzle.py ├── subsets_subsequences_generation_recursion.py ├── sum_natural_numbers.py └── word_boogle.py ├── searching algo ├── Interview_patterns.MD ├── binary_search.py ├── binary_search_bisect.py ├── exponential_search.py ├── ternary_search.py └── variants_binary_search.py ├── segment-fenwick-trees ├── XOR_range_queries.py ├── fenwick_trees_template.py └── segment_trees_template.py ├── shell_leetcode ├── AWK.sh ├── print_tenth_line.sh ├── transpose_file.sh ├── valid_phone_numbers.sh └── word_frequency.sh ├── sorting algo ├── Bead_sort.py ├── DNF_sort.py ├── SORTING_tricks_python.py ├── bubble_sort.py ├── bucket_sort.py ├── counting_sort.py ├── insertion.py ├── merge_sort.py ├── pancake_sorting.py ├── quick_sort.py ├── radix_sort.py ├── selection_sort.py ├── sort_0_1_2.py └── wave_sort.py ├── stacks, queues, dequees ├── Interview_Patterns.MD ├── __pycache__ │ ├── deques.cpython-37.pyc │ └── stacks.cpython-37.pyc ├── balanced_parenthesis.py ├── deques.py ├── generate_binary_strings.py ├── largest_area_rectangle_histogram.py ├── maximum_all_subarrays_sizek_deques.py ├── min_queues.py ├── min_stack.py ├── min_stacks_part2.py ├── queue_inbuilt.py ├── queue_using_circular_array.py ├── queues.py ├── queues_using_stacks.py ├── rain_water_trap.py ├── reverse_list_stack.py ├── reverse_stack.py ├── stack_inbuilt.py ├── stacks.py ├── stacks_min_modification.py ├── stacks_using_queues.py └── stock_span_problem.py ├── string matching algo ├── Boyer_Moore_algo.py ├── KMP_algorithm.py ├── Z_algorithm.py ├── rabin_karp.py └── string_matching_naive.py ├── strings ├── INTERVIEW_ideas.MD ├── alien_dictionary.py ├── anagram_grouping.py ├── binary_strings_addition.py ├── camel_case_pattern_matching.py ├── check_binary_codes_substring.py ├── check_if_anagram.py ├── check_string_rotations.py ├── check_valid_palindrome_string.py.py ├── check_valid_palindromes_remove_char.py ├── count_fruit_in_basket.py ├── count_non_increasing_non_decreasing_sequences.py ├── count_palindromes_swaps.py ├── count_special_palindromes.py ├── custom_split_string.py ├── decode_ways_2.py ├── decode_ways_strings.py ├── difference_between_ascii_codes.py ├── find_all_anagrams_string.py ├── find_first_and_last_occurence.py ├── first_non_repeating_char_string.py ├── generate_all_valid_ip.py ├── implement_atoi.py ├── longest_common_prefix.py ├── longest_distinct_chars.py ├── longest_palindromic_substring.py ├── longest_prefix_suffix.py ├── longest_substring_k_unique_chars.py ├── longest_substring_without_repeating_chars.py ├── max_freq_words_string.py ├── maximum_occuring_character_string.py ├── minimum_deletions_uniqueness.py ├── minimum_window_substring.py ├── print_CamelCase.py ├── remove_duplicates.py ├── remove_duplicates_adjacent.py ├── remove_spaces.py ├── reorganize_strings_no_adjacent.py ├── reverse_words_complete.py ├── second_most_repeated_char.py ├── shift_string.py ├── sort_strings_1.py ├── strength_password.py ├── string_compression.py ├── string_hashing_template.py ├── string_ignorance_alternate.py ├── string_lexographical_sort.py ├── string_palindrome_rearrange_check.py ├── string_remove_common_chars.py ├── string_reverse_indi_words.py.py ├── strings_add.py ├── strings_palindrome_checker.py ├── subsequence_matcher.py ├── substrings_subsequences.py ├── total_substrings_possible.py ├── unique_email_adds.py ├── word_search_grid.py └── words_reverse.py └── tries ├── Inserting_searching.py └── Unique_prefix_array.py /C practice files/enums.c: -------------------------------------------------------------------------------- 1 | // In this we will see uses of enum keywords in C 2 | // It is a user defined data type in C, used to mainly assign names to integral 3 | // constants, the names make a program easy to read and maintain. 4 | // enum is used to declare new declare new enumeration types, 5 | // name of enumeration is "flag" and constant are the values of the flag 6 | // constant1 = 0, constant2 = 1, ...etc. 7 | // enum flag{constant1, constant2....} 8 | // Compiler assigns values starting from 0. 9 | // All enum constants must be unique in their scope 10 | // Following fails : 11 | // enum state {working, failed}; 12 | // enum result {failed, passed}; 13 | // Macros can also be defined for assigning constant values, but enums differ and have advantages 14 | // * enums follow scope rules 15 | // * enum variables are automatically assigned values. 16 | 17 | # include 18 | 19 | enum week{Mon, Tue, Wed, Thur, Fri, Sat, Sun}; 20 | enum year{Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec}; 21 | // we can also initialize values from user defined constants : 22 | // enum week{Mon = 1, Tue, Wed, Thur, Fri, Sat, Sun}; this will start counting from 1.. 23 | 24 | 25 | 26 | int main(void){ 27 | enum week day; 28 | day = Wed; // This shows Wed is a constant defined now 29 | printf("value of wed : %d\n", day); 30 | int i = 0; 31 | printf("Value of constants Jan to Dec: \n"); 32 | for(i = Jan; i <= Dec; i++){ 33 | printf("%d ", i); 34 | } 35 | 36 | return 0; 37 | 38 | } -------------------------------------------------------------------------------- /C practice files/enums.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/souravs17031999/100dayscodingchallenge/d05966f3e6875a5ec5a8870b9d2627be570d18d9/C practice files/enums.exe -------------------------------------------------------------------------------- /C practice files/pointer.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/souravs17031999/100dayscodingchallenge/d05966f3e6875a5ec5a8870b9d2627be570d18d9/C practice files/pointer.c -------------------------------------------------------------------------------- /C practice files/pointer1.c: -------------------------------------------------------------------------------- 1 | // Program for introduction to pointers 2 | // Pointers basically are used to store the address of the variables 3 | // we can access any address of the variables using "&" operator 4 | 5 | #include 6 | 7 | int main() 8 | { 9 | int x, y, z; 10 | printf("Initial addresses : %p %p %p\n", &x, &y, &z); 11 | return 0; 12 | } 13 | 14 | -------------------------------------------------------------------------------- /C practice files/pointer1.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/souravs17031999/100dayscodingchallenge/d05966f3e6875a5ec5a8870b9d2627be570d18d9/C practice files/pointer1.exe -------------------------------------------------------------------------------- /C practice files/pointer10.c: -------------------------------------------------------------------------------- 1 | // understanding Function pointers 2 | // function pointers don't point to data, but only to code 3 | // This can be also be used in place of switch case - point 1 4 | // This can be also be passed as argument in function and returned from function - point 2 5 | // 6 | 7 | # include 8 | 9 | void fun(int a){ 10 | printf("Value of a is %d\n", a); 11 | } 12 | 13 | int main(){ 14 | void (*fun_ptr)(int) = &fun; // here fun_ptr is a pointer to function fun() 15 | 16 | (*fun_ptr)(10); // extra brackets is required to call this function 17 | 18 | // can also be written as with removing & operator and calling funtion withut * operator 19 | 20 | void (*fun_ptr1)(int) = fun; 21 | 22 | fun_ptr1(10); 23 | 24 | return 0; 25 | 26 | // point - 1 27 | 28 | // creating a array of pointers using function pointers 29 | 30 | void (*fun_ptr_arr[])(int, int) = {add, subtract, multiply}; 31 | // here add, subtract and multiply are all different functions 32 | } -------------------------------------------------------------------------------- /C practice files/pointer10.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/souravs17031999/100dayscodingchallenge/d05966f3e6875a5ec5a8870b9d2627be570d18d9/C practice files/pointer10.exe -------------------------------------------------------------------------------- /C practice files/pointer11.c: -------------------------------------------------------------------------------- 1 | // In this we will see dereference operators "*", prefix (++p) and postfix operators (p++) precedence 2 | // precedence of prefix ++ and * is same , associativity is right to left 3 | // precedence of postfix ++ is higher than * , associativity is left to right 4 | 5 | #include 6 | 7 | int main(void){ 8 | int arr[] = {10, 20}; 9 | int *p = arr; 10 | ++*p; // here compiler compiles in the following way : ++(*p) so *p is 10 and ++*p will be *p = *p + 1 => 10 + 1 => 11 11 | printf("arr[0] = %d, arr[1] = %d, *p = %d", arr[0], arr[1], *p); 12 | 13 | int s[] = {10, 20}; 14 | int *r = s; 15 | *r++; // here compiler compiles in the following way : *(r++) so r++ is r = r + 1, which will point to address of 20, so now *r will be 20 16 | printf("\ns[0] = %d, s[1] = %d, *r = %d", s[0], s[1], *r); 17 | 18 | return 0; 19 | } -------------------------------------------------------------------------------- /C practice files/pointer11.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/souravs17031999/100dayscodingchallenge/d05966f3e6875a5ec5a8870b9d2627be570d18d9/C practice files/pointer11.exe -------------------------------------------------------------------------------- /C practice files/pointer12.c: -------------------------------------------------------------------------------- 1 | // In this , we will see how pointer works on struct 2 | // Like arrays, pointer to a struct holds the memory address of the first 3 | // element in the struct 4 | // "->" arrow operator is used to access a value from the struct pointer 5 | // (*ptr).field where we first dereference the struct pointer and then 6 | // acess the field using the standard "." notation. 7 | // Acessing a field from the struct pointer is so common, the "->" operator exists to make it easier 8 | 9 | struct person{ 10 | int age; 11 | char *name; 12 | }; 13 | 14 | #include 15 | 16 | int main(void){ 17 | struct person first; 18 | struct person *ptr; 19 | first.age = 21; 20 | first.name = "sourav kumar"; 21 | ptr = &first; 22 | printf("age=%d, name=%s\n", first.age, ptr->name); 23 | return 0; 24 | } -------------------------------------------------------------------------------- /C practice files/pointer12.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/souravs17031999/100dayscodingchallenge/d05966f3e6875a5ec5a8870b9d2627be570d18d9/C practice files/pointer12.exe -------------------------------------------------------------------------------- /C practice files/pointer13.c: -------------------------------------------------------------------------------- 1 | // constructing linked list node structure using structs in C 2 | #include 3 | #include 4 | 5 | struct Node { 6 | int data; 7 | struct Node *next; 8 | }; 9 | 10 | int main(void) 11 | { 12 | struct Node *head = NULL; 13 | struct Node *second = NULL; 14 | struct Node *third = NULL; 15 | head = (struct Node*)malloc(sizeof(struct Node)); 16 | second = (struct Node*)malloc(sizeof(struct Node)); 17 | third = (struct Node*)malloc(sizeof(struct Node)); 18 | 19 | head->data = 1; 20 | head->next = second; 21 | 22 | second->data = 2; 23 | second->next = third; 24 | 25 | third->data = 3; 26 | third->next = NULL; 27 | 28 | printf("value stored in head next: %d\n", head->next); 29 | printf("addres of second %d\n", &second); 30 | printf("value stored in second next: %d\n", second->next); 31 | printf("addres of third %d\n", &third); 32 | printf("value stored in third next: %d", third->next); 33 | 34 | return 0; 35 | } -------------------------------------------------------------------------------- /C practice files/pointer13.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/souravs17031999/100dayscodingchallenge/d05966f3e6875a5ec5a8870b9d2627be570d18d9/C practice files/pointer13.exe -------------------------------------------------------------------------------- /C practice files/pointer14.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/souravs17031999/100dayscodingchallenge/d05966f3e6875a5ec5a8870b9d2627be570d18d9/C practice files/pointer14.exe -------------------------------------------------------------------------------- /C practice files/pointer2.c: -------------------------------------------------------------------------------- 1 | // Initialising the pointer with a unary pointer "*" before its name 2 | // "*" before a name makes it a pointer variable which stores address of 3 | // another variable 4 | 5 | // Another use of * operator is to dereferencing that is 6 | // getting the actual value stored in pointer. 7 | // Pointer points to the value and stores the address of that value. 8 | 9 | #include 10 | int main(){ 11 | int x = 5; 12 | int *ptr; 13 | ptr = &x; 14 | 15 | printf("value Of x = %i : and stored at = %p\n", *ptr, ptr); 16 | 17 | // we can change the value stored at the address this pointer points to 18 | *ptr = 20; 19 | printf("value Of x = %i : and stored at = %p", *ptr, ptr); 20 | return 0; 21 | } 22 | 23 | -------------------------------------------------------------------------------- /C practice files/pointer2.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/souravs17031999/100dayscodingchallenge/d05966f3e6875a5ec5a8870b9d2627be570d18d9/C practice files/pointer2.exe -------------------------------------------------------------------------------- /C practice files/pointer3.c: -------------------------------------------------------------------------------- 1 | // understanding pointer arithmetic 2 | // Pointers can be incremented, decremented , 3 | // and a integer may be added or subtracted using += , -=} 4 | 5 | #include 6 | 7 | int main(){ 8 | int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; // array declaration in c 9 | int n = sizeof(arr)/sizeof(arr[0]); // finding the size of array 10 | int i = 0; // for loop variables outside the loop 11 | int *ptr; // declaring the pointer variable 12 | ptr = arr; // pointing this will make ptr to point to first allocated space 13 | for (i = 0; i <= n - 1; i++){ // iterable loop 14 | printf("value at arr[%i] = %i at %p\n", i, *ptr, ptr); 15 | ptr += 1; // incrementing the pointer will point to next contigous location 16 | } 17 | return 0; 18 | } -------------------------------------------------------------------------------- /C practice files/pointer3.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/souravs17031999/100dayscodingchallenge/d05966f3e6875a5ec5a8870b9d2627be570d18d9/C practice files/pointer3.exe -------------------------------------------------------------------------------- /C practice files/pointer4.c: -------------------------------------------------------------------------------- 1 | // we can see from below that arrays are basically pointers in c 2 | // as declaring arrays similar to declare pointer , as we can access 3 | // the element using pointer[i] using array indexing 4 | 5 | # include 6 | 7 | int main(){ 8 | int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 9 | int *ptr; 10 | ptr = arr; 11 | int i = 0; 12 | for (i = 0; i <= 9; i++){ 13 | printf("value at arr[%i] : %i\n", i, ptr[i]); // here we use ptr[i] instead of arr[i] to access the element 14 | } 15 | } -------------------------------------------------------------------------------- /C practice files/pointer4.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/souravs17031999/100dayscodingchallenge/d05966f3e6875a5ec5a8870b9d2627be570d18d9/C practice files/pointer4.exe -------------------------------------------------------------------------------- /C practice files/pointer5.c: -------------------------------------------------------------------------------- 1 | // We can use pointers for accessing 2d arrays in the same way as for 1-d arrays 2 | // nums[i][j] is equivalent to *(*(nums + i) + j) 3 | 4 | # include 5 | 6 | int main(){ 7 | int arr[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; 8 | int i = 0; 9 | int j = 0; 10 | for(i = 0; i <= 2; i++){ 11 | for(j = 0; j <= 2; j++){ 12 | printf("value at arr[%i][%i] : %i\n", i, j, arr[i][j]); 13 | } 14 | } 15 | printf("value of first element arr[0][0] is %i\n", *(*arr)); // this is same as arr[0][0] 16 | printf("value of arr[0][1] element is %i\n", *(*arr) + 1); // this is sames as arr[0][1] 17 | printf("value of arr[1][2] element is %i\n", *(*(arr + 1) + 2)); // this is same as arr[1][2] 18 | 19 | return 0; 20 | } -------------------------------------------------------------------------------- /C practice files/pointer5.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/souravs17031999/100dayscodingchallenge/d05966f3e6875a5ec5a8870b9d2627be570d18d9/C practice files/pointer5.exe -------------------------------------------------------------------------------- /C practice files/pointer6.c: -------------------------------------------------------------------------------- 1 | // playing with 2d pointers arrays (matrices) 2 | 3 | # include 4 | 5 | int main(){ 6 | int arr[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; 7 | int i = 0; 8 | int j = 0; 9 | int *ptr1 = arr[0]; 10 | int *ptr2 = arr[1]; 11 | 12 | for(i = 0; i <= 2; i++){ 13 | for(j = 0; j <= 2; j++){ 14 | printf("value at arr[%i][%i] : %i\n", i, j, *ptr1); 15 | printf("value at arr[%i][%i] : %i\n", i, j, *ptr2); 16 | } 17 | ptr1 += 1; 18 | ptr2 += 1; 19 | } 20 | return 0; 21 | 22 | } -------------------------------------------------------------------------------- /C practice files/pointer6.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/souravs17031999/100dayscodingchallenge/d05966f3e6875a5ec5a8870b9d2627be570d18d9/C practice files/pointer6.exe -------------------------------------------------------------------------------- /C practice files/pointer7.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/souravs17031999/100dayscodingchallenge/d05966f3e6875a5ec5a8870b9d2627be570d18d9/C practice files/pointer7.exe -------------------------------------------------------------------------------- /C practice files/pointer8.c: -------------------------------------------------------------------------------- 1 | // NULL pointers 2 | // It is different from uninitialised and dangling pointer. 3 | // It's a specific valid "invalid" pointer in C standard 4 | // An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant. \ 5 | // If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.” 6 | // NULL is basically 0 but not in integer context , it's in pointer context 7 | // Sizeof() is valid and is of pointer's normal size 8 | 9 | # include 10 | 11 | int main(){ 12 | int *ptr = NULL; // it is perfectly valid to be written as int *ptr = 0; 13 | printf("value of ptr : %p\n", ptr); 14 | printf("sizeof of ptr : %i\n", sizeof(ptr)); 15 | // we can also dereference the NULL pointer 16 | printf("value of dereference : %d", *ptr); 17 | } -------------------------------------------------------------------------------- /C practice files/pointer8.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/souravs17031999/100dayscodingchallenge/d05966f3e6875a5ec5a8870b9d2627be570d18d9/C practice files/pointer8.exe -------------------------------------------------------------------------------- /C practice files/pointer9.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/souravs17031999/100dayscodingchallenge/d05966f3e6875a5ec5a8870b9d2627be570d18d9/C practice files/pointer9.exe -------------------------------------------------------------------------------- /C practice files/pointers_intro.c: -------------------------------------------------------------------------------- 1 | // Pointers are basically used to reference any type of variable by storing the address of the variable 2 | // that is it points to the specific memory address of the variable with which it is associated 3 | // "*" operator is used when declaring a pointer and when dereferencing the pointer. 4 | // dereferencing the pointer means getting the value stored at that address 5 | // Size of the operator depends on architechture, for 32 bit systems : 4 bytes or 32 bits, for 64 bit systems : 8 bytes or 64 bits 6 | // "&" operator is used to get the address of another variable , used to assign a value to a pointer 7 | // "*" operator can also be used to assign a value to pointer address 8 | // Pointers declared will be of the same type as the initialised value 9 | // we can't tell compiler to assign a pointer of type "x" to some another variable of type "y" 10 | // It should be always same type 11 | // Ex. int ival = 1; int *iptr = &ival; 12 | // float fval = 1.0f; float *fptr = &fval; 13 | // we can't do this : iptr = &fval; 14 | 15 | 16 | // Pointers to arrays 17 | // Arrays are basically valid pointers in C 18 | // When array is created, compiler allocates memory for the entire arrays and then assigns 19 | // a pointer to the arrays variable 20 | // we can do this : int *ptr = myarray where int myarrays[4] = {1, 2, 3, 4}; 21 | // We can't do this : myarray = ptr; myarray = myarray2; myarray = &myarray2[0] 22 | 23 | #include 24 | 25 | int main(void){ 26 | 27 | return 0; 28 | } -------------------------------------------------------------------------------- /C practice files/pointers_intro.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/souravs17031999/100dayscodingchallenge/d05966f3e6875a5ec5a8870b9d2627be570d18d9/C practice files/pointers_intro.exe -------------------------------------------------------------------------------- /C practice files/structs.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef struct { 4 | PyObject_HEAD; 5 | Py_ssize_t ob_size; 6 | 7 | /* Vector of pointers to list elements. list[0] is ob_item[0], etc. */ 8 | PyObject **ob_item; 9 | 10 | /* ob_item contains space for 'allocated' elements. The number 11 | * currently in use is ob_size. 12 | * Invariants: 13 | * 0 <= ob_size <= allocated 14 | * len(list) == ob_size 15 | * ob_item == NULL implies ob_size == allocated == 0 16 | */ 17 | Py_ssize_t allocated; 18 | } PyListObject; 19 | 20 | int main(){ 21 | 22 | return 0; 23 | } -------------------------------------------------------------------------------- /C practice files/typedefinition.c: -------------------------------------------------------------------------------- 1 | // typedef is used to give data type a new name. 2 | // #define in C is a derivative which is used to #define alias 3 | // typedef is limited to giving symbolic names to types only, whereas 4 | // #define can be used to define an alias for values as well , #define 3.14 PI etc. 5 | // typedef interpretation is performed by compiler where #define statements 6 | // are performed by preprocessor 7 | // #define should not be terminated with a semicolon, but typedef should be terminated with semicolon 8 | // #define will just copy-paste the definition values at the point of use, while 9 | // typedef is the actual definition of a new type 10 | // typedef follows the scope rule which means if a new type is defined in a scope 11 | // then the new type name will only be visible till the scope is there. 12 | // typedefs can be also applied to structs to avoid writing structs everytime we declare them 13 | 14 | 15 | #include 16 | 17 | typedef struct Books{ 18 | char title[50]; 19 | char author[50]; 20 | char subject[100]; 21 | int book_id; 22 | } BOOK; 23 | 24 | // After this line HYD is replaced with "Hyderabad" 25 | #define HYD "Hyderabad" 26 | 27 | // This line BYTE can be in place of unsigned char 28 | typedef unsigned char BYTE; 29 | 30 | int main(void){ 31 | BYTE b1, b2; 32 | b1 = 'c'; 33 | printf("%c ", b1); 34 | return 0; 35 | 36 | Book book; 37 | // used can be : book.title, book.author etc.. 38 | } -------------------------------------------------------------------------------- /C practice files/typedefinition.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/souravs17031999/100dayscodingchallenge/d05966f3e6875a5ec5a8870b9d2627be570d18d9/C practice files/typedefinition.exe -------------------------------------------------------------------------------- /C practice files/unions.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/souravs17031999/100dayscodingchallenge/d05966f3e6875a5ec5a8870b9d2627be570d18d9/C practice files/unions.exe -------------------------------------------------------------------------------- /General_tips.MD: -------------------------------------------------------------------------------- 1 | ## For solving problems : 2 | 3 | * First match the pattern of similar questions solved in the past (run through all the DS/ALGO interview patterns) 4 | * If above doesn't works, then it means the question may involve some thinking beyond any similar pattern : Go for explicilty writing input-output examples and clearly observe any 5 | pattern hidden in it, usually it does help to write the input and output examples explicitly and correlate it. 6 | There are tricky patterns hidden inside the problem which are only visible when written clearly explicitly. 7 | 8 | ## General tips : 9 | 10 | * Listen to question completely and carefully, then ask clarifying questions and observe input and output examples. 11 | * Start from basic naive solution, and then move upto optimal solution one by one. 12 | * Discuss the time and space tradeoffs for all the solutions. 13 | ------------------------------------------------------------------------------------------------------------------------------ 14 | * Always have a positive smile and confidence while speaking during the interview. 15 | * If interviewer interrupts, then allow him to speak completely, then only tell your opinion (don't speak over him) 16 | * Have a two way dialogue and discussion. 17 | * Think of interviewer as a friend/colleague. 18 | ``` 19 | REMEMBER INTERVIEW IS JUST SIMULATION OF REAL TIME PROBLEM SOLVING WITH YOUR COLLEAGUE IF YOU WERE ACTUALLY WORKING AT COMPANY AND SOLVING PROBLEM FOR THE CLIENT. 20 | ``` 21 | -------------------------------------------------------------------------------- /Recursion_trick_CP.py: -------------------------------------------------------------------------------- 1 | import collections,sys,threading 2 | sys.setrecursionlimit(10**9) 3 | threading.stack_size(10**8) 4 | 5 | def main(): 6 | pass 7 | 8 | threading.Thread(target=main).start() 9 | -------------------------------------------------------------------------------- /Time_complexity.MD: -------------------------------------------------------------------------------- 1 | # TIPS FOR COMPUTING TIME COMPLEXITY : 2 | * Don't depend on just loops (either for or while), 3 | * check actual loop variables whether they are incremented, decremened, multiplied, divided... 4 | * For linked list, check how much pointer points to elements ? 5 | * For stacks, queues, dequeus, how many elements are atmost procesed inside these ? 6 | * For trees and graphs : how many atmost nodes are procesed ? 7 | * For strings, arrays similar to linked list, how many times we are traversing to each one of 8 | those elements, or how much our pointer points to ? 9 | -------------------------------------------------------------------------------- /advanced ds/graphs.py: -------------------------------------------------------------------------------- 1 | class adjnode: 2 | def __init__(self, data): 3 | self.data = data 4 | self.next = None 5 | 6 | class graph: 7 | def __init__(self, vertices): 8 | self.V = vertices 9 | self.adjlist = [None]*self.V 10 | 11 | def add_edge(self, src, dst): 12 | new_node_dst = adjnode(dst) 13 | new_node_dst.next = self.adjlist[src] 14 | self.adjlist[src] = new_node_dst 15 | 16 | new_node_src = adjnode(src) 17 | new_node_src.next = self.adjlist[dst] 18 | self.adjlist[dst] = new_node_src 19 | 20 | def print_adjlist(self): 21 | for i in range(self.V): 22 | print(f'vertex {i} :', end = " ") 23 | ptr = self.adjlist[i] 24 | while(ptr): 25 | print(f'-> {ptr.data}', end = " ") 26 | ptr = ptr.next 27 | print() 28 | 29 | if __name__ == '__main__': 30 | g = graph(5) 31 | g.add_edge(0, 1) 32 | g.add_edge(0, 4) 33 | g.add_edge(1, 2) 34 | g.add_edge(1, 3) 35 | g.add_edge(1, 4) 36 | g.add_edge(2, 3) 37 | g.add_edge(3, 4) 38 | g.print_adjlist() 39 | -------------------------------------------------------------------------------- /arrays/DNF_three_way_partitioning.py: -------------------------------------------------------------------------------- 1 | # Program for three way Partitioning, such that the overall array is paritioned into three 2 | # paritions such that elements shorter than pivot (mid) will be before the pivot, 3 | # and elements greater than pivot will be greater than pivot. 4 | # In this way, we will be having three regions, such that there will be elements lesser than 5 | # pivot, some elements equal to pivot region and then elements greater than pivot. 6 | # TIME : 0(N), SPACE : 0(1). 7 | 8 | import random 9 | def Partition(arr, mid): 10 | i, j, k = 0, 0, len(arr) 11 | while j < k: 12 | if arr[j] < mid: 13 | arr[i], arr[j] = arr[j], arr[i] 14 | i += 1 15 | j += 1 16 | elif arr[j] > mid: 17 | k -= 1 18 | arr[j], arr[k] = arr[k], arr[j] 19 | else: 20 | j += 1 21 | 22 | if __name__ == '__main__': 23 | arr = [random.randint(0, 2) for _ in range(10)] 24 | print(arr) 25 | Partition(arr, 1) 26 | print(arr) 27 | -------------------------------------------------------------------------------- /arrays/Interview_patterns.MD: -------------------------------------------------------------------------------- 1 | # IN INTERVIEW, LOOK OUT FOR PATTERNS : 2 | * Two pointer technique (generally after sorting) : 3 | > CONVERGING TOWARDS THE CENTER => WHILE START < END 4 | > DIVERGING AWAY FROM CENTER (EXPANDING FROM CENTER) => WHILE START >= 0 AND END < LENGTH 5 | 6 | * Hashing : HASHMAP/HASHTABLE 7 | * Bitsmasking (XOR TRICKS) 8 | * Window sliding (MAX/MIN, SUM, PRODUCT, XOR OF K SIZED WINDOW FOR MAXIMUM, MINIMUM, LONGEST, SMALLEST) : 9 | > STATIC SIZED WINDOW SLIDING : FIXED WINDOW SIZE DURING WHOLE TRAVERSAL (USUALLY SIMULATED BY TWO POINTERS START, END) 10 | > DYNAMIC SIZED WINDOW SLIDING : VARIABLE SIZING WINDOW, EXPAND FROM RIGHT SIDE, SHRINK FROM LEFT SIDE. 11 | > DYNAMIC SIZED WINDOW + AUX. DS : VARIABLE SIZING WINDOW + SOME KIND OF HASHMAP/HASHTABLE 12 | > IMPORTANT : IF we want to answer some queries for every window : then use Deque/queue based approach (not a single answer) 13 | 14 | * Modified binary search (keep searching even if value is found by including other conditions) 15 | * Prefix cumulative sum array (for range queries) 16 | ``` 17 | prefix[i] = arr[i] + prefix[i - 1] 18 | ``` 19 | * OTHER ADVANCED TECHNIQUES FOR RANGE QUERIES : SEGMENT TREES, FENWICK TREES. 20 | -------------------------------------------------------------------------------- /arrays/Longest_consecutive_ones.py: -------------------------------------------------------------------------------- 1 | # Program to count maximum length of consecutive ones in a integer 2 | 3 | # naive solution will be to use two loops, one outer loop starting from every integer and inner loop to start from that integer to consecutive ones and count the ones. 4 | # then take the overall max count which takes 0(N*N), where N is length of binary string converted for integer N. 5 | 6 | # Efficient approach using sliding window 7 | # in 0(N), where N is same as said above. 8 | 9 | from sys import stdin, stdout 10 | n = bin(int(stdin.readline().strip()))[2:] 11 | stdout.write(n) 12 | stdout.write("\n") 13 | max_z = 0 14 | temp = 0 15 | for i in range(len(n)): 16 | if n[i] == '1': 17 | temp += 1 18 | max_z = max(max_z, temp) 19 | else: 20 | temp = 0 21 | 22 | stdout.write(str(max_z)) 23 | 24 | # ---------------------------------------------------- 25 | # Another efficient approach using bitwise operation taking O(length of max ones consecutive) 26 | 27 | stdout.write("\n") 28 | x = int(stdin.readline().strip()) 29 | count = 0 30 | while x : 31 | x = (x & (x << 1)) 32 | count += 1 33 | 34 | stdout.write(str(count)) 35 | -------------------------------------------------------------------------------- /arrays/Maximum_consecutive_onesIII.py: -------------------------------------------------------------------------------- 1 | # Program for computing longest length of consecutive ones 2 | # with atmost k flips allowed to convert 0 to 1. 3 | 4 | # Efficient solution using sliding window in O(N) 5 | 6 | class Solution: 7 | def longestOnes(self, A: List[int], K: int) -> int: 8 | start, max_z = 0, 0 9 | count = 0 10 | for end in range(len(A)): 11 | if A[end] == 0: 12 | count += 1 13 | 14 | while count > K: 15 | if A[start] == 0: 16 | count -= 1 17 | start += 1 18 | 19 | max_z = max(max_z, end - start + 1) 20 | return max_z 21 | 22 | -------------------------------------------------------------------------------- /arrays/Median_sliding_window.py: -------------------------------------------------------------------------------- 1 | # Program for computing median of sliding k-sized window in the array. 2 | # Input is array of integer arr[] and an integer K, we need to find the median of each window of size K 3 | # starting from left and moving towards the right by one position each time. 4 | 5 | # Input: arr[] = {-1, 5, 13, 8, 2, 3, 3, 1}, k = 3 6 | # Output: 5 8 8 3 3 3 7 | # 8 | # Input: arr[] = {-1, 5, 13, 8, 2, 3, 3, 1}, k = 4 9 | # Output: 6.5 6.5 5.5 3.0 2.5 10 | 11 | # ------------------------------------------------------------------------------------------------------------------- 12 | # Naive Approach: 13 | # The simplest approach to solve the problem is to traverse over every window of size K and sort the elements of 14 | # the window and find the middle element. Print the middle element of every window as the median. 15 | # Time Complexity: O(N*KlogK) 16 | # Auxiliary Space: O(K) 17 | 18 | # This is different than median in a stream where we use heaps - one min heap and one max heap. 19 | 20 | # This can be solved using GNU-policy based data structures in C++ or Treeset in Java. 21 | 22 | # In python, we can use sortedcontainers for sortedlist. 23 | -------------------------------------------------------------------------------- /arrays/arrange_biggest_number.py: -------------------------------------------------------------------------------- 1 | # Program for arranging the array, so that we get the biggest number possible. 2 | ## IDEA: logic is to sort these numbers but using custom comparator function. 3 | # Custom comparator function will be such that it will take two arguments - X, Y and we compare XY (Y appended at the end of X), 4 | # YX (X appended at the end of Y). 5 | # If XY is larger, X should come before in output, else Y should come before. 6 | # EX. X,Y = > 542, 60 7 | # X -> 54260, Y-> 60542, SINCE 60542 IS GREATER, THEN WE PUT Y FIRST. 8 | 9 | import functools 10 | 11 | def Compare(x, y): 12 | return ((int(f"{y}{x}") > int(f"{x}{y}")) - (int(f"{y}{x}") < int(f"{x}{y}"))) 13 | 14 | if __name__ == '__main__': 15 | arr = [54, 546, 548, 60] 16 | arr.sort(key=functools.cmp_to_key(Compare)) 17 | print("".join(list(map(str, arr)))) 18 | -------------------------------------------------------------------------------- /arrays/array_pair_target_sum.py: -------------------------------------------------------------------------------- 1 | # Program for finding all unqiue pairs for which sums up to some target value in a array 2 | 3 | # The idea is to understand the fact that after sorting the whole array, we can observe that if we keep two pointers one at leftmost index and other at rightmost index then, if the sum of both is greater than target, then we know that decrease our pointers otherwise increase it. 4 | def array_target_sum_main(l, target): 5 | start = 0 # leftmost starting index 6 | end = len(l) - 1 # rightmost ending index 7 | l.sort() # sorting the index 8 | while(start < end): 9 | if(l[start] + l[end] == target): # checking if we got the target 10 | print(f"{l[start]} {l[end]}") 11 | # we don't have to stop once we found the sum and try to continue checking if possibility 12 | if(l[start] + l[end] > target): # we need to decrease the right pointer because list is already sorted , so we need to decrease the sum 13 | end -= 1 14 | else: 15 | start += 1 16 | 17 | 18 | n = int(input()) 19 | l = [] 20 | for i in range(n): 21 | l.append(int(input())) 22 | target = int(input()) 23 | array_target_sum_main(l, target) 24 | -------------------------------------------------------------------------------- /arrays/close_nums_palintir.py: -------------------------------------------------------------------------------- 1 | # program to return true if there are two distinct indices such that their difference 2 | # is less than or equal to some given value of k. 3 | # idea is to use a hash map (dict) to store key as element and value as their last seen index , 4 | # in this way when we see already seen element (key already present in the dict) , then 5 | # we simply check the difference condition to be less than k , and return bool value accordingly 6 | # time : 0(N), space : 0(N) 7 | 8 | def closest(arr, k): 9 | if len(arr): 10 | count = {} 11 | flag = False 12 | for i in range(0, len(arr)): 13 | if arr[i] in count: 14 | if abs(i - count[arr[i]]) <= k: 15 | flag = True 16 | 17 | count[arr[i]] = i 18 | return flag 19 | else: 20 | return False 21 | 22 | if __name__ == '__main__': 23 | assert closest([0, 1, 2, 3, 5, 2], 3) == True 24 | assert closest([1,2,3,1], 3) == True 25 | assert closest([1,0,1,1], 1) == True 26 | assert closest([1,2,3,1,2,3], 2) == False 27 | assert closest([], 1) == False 28 | assert closest([1, 2, 3, 4, 5], 1) == False 29 | assert closest([1, 2, 2, 4, 5, 2, 5, 8, 4, 2], 5) == True 30 | -------------------------------------------------------------------------------- /arrays/closest_pair_sorted.py: -------------------------------------------------------------------------------- 1 | # Program for finding the pair whose sum is closest to x. 2 | 3 | # Input: nums = [1, 2, 3, 4, 5], target = 10 4 | # Output: [4, 5] 5 | # Input: nums= [-1, 2, 1, -4], target = 4 6 | # Output: [2, 1] 7 | # -------------------------------------------------------------------------------------------------------------------- 8 | # TIME : 0(N * LOG(N)), BECAUSE ARRAY IS NOT UNSORTED but if array is sorted, 9 | # then, TIME : 0(N) 10 | 11 | import sys 12 | def compute_closest_pair(arr, n, x): 13 | 14 | l_idx, r_idx, diff = 0, 0, sys.maxsize 15 | left, right = 0, n - 1 16 | arr.sort() 17 | while left < right: 18 | 19 | if abs(arr[left] + arr[right] - x) < diff : 20 | l_idx = left 21 | r_idx = right 22 | diff = abs(arr[left] + arr[right] - x) 23 | 24 | if arr[left] + arr[right] > x: 25 | right -= 1 26 | else: 27 | left += 1 28 | 29 | return [arr[l_idx], arr[r_idx]] 30 | 31 | if __name__ == '__main__': 32 | arr = [1, 2, 3, 4, 5] 33 | assert compute_closest_pair(arr, len(arr), 10) == [4, 5] 34 | -------------------------------------------------------------------------------- /arrays/combinations_generate.py: -------------------------------------------------------------------------------- 1 | # Program for generating and printing all combinations of size "r". 2 | # Approach will be to fix one one element at its position, and then either it will choose the current element or not choose the current element 3 | # same as the one for subset/subsequence generation. 4 | # Following is recursion tree visualized : 5 | # (0,0) 6 | # / \ 7 | # (1,1) (0,1) 8 | # / \ / \ 9 | # (2,2) (1,2) (1,1) (0,2) 10 | # /\ / \ 11 | # (3,3) (2,3) (2,3) (1,3) 12 | # /\ /\ / \ / (1,5) 13 | # (3,4) (2,4) (3,4) (2,4) (2,4) (1,4) \ (2,5) 14 | # /\ / \ / \ 15 | # (3,5) (2,5) (3,5) (2,5) (3,5) (2,5) 16 | # 17 | # ---------------------------------------------------------------------------------------------------------------------------------------------- 18 | # TIME : 0(2 ^ n) tighter upper bound whereas upper bound can be 0(nCr). 19 | 20 | arr = [1, 2, 3, 4, 5] 21 | 22 | def combinations(arr, n, r, index, temp, i): 23 | 24 | if index == r: 25 | print(temp) 26 | return 27 | 28 | if i >= n: 29 | return 30 | 31 | temp[index] = arr[i] 32 | combinations(arr, n, r, index + 1, temp, i + 1) 33 | combinations(arr, n, r, index, temp, i + 1) 34 | 35 | temp = [0] * 3 36 | combinations(arr, 5, 3, 0, temp, 0) 37 | -------------------------------------------------------------------------------- /arrays/count_LED_changes.py: -------------------------------------------------------------------------------- 1 | # Program For counting the total changes in the LED after displaying the number 2 | # of digits given as input string. 3 | # Very easy approach would be to precompute the led's lighted up as and when any 4 | # number between 0-9, is displayed. 5 | # so, let's assume the display is seven segement display. 6 | # Now, 0 : displayed by 6 led's, and similar all others. 7 | # precomputed array , LED : : [ 6, 2, 5, 5, 4, 5, 6, 3, 7, 5 ] 8 | # Now, simply we can initialize our sum as first digit value and then iterate 9 | # through the input string, and count the changes in the led as 10 | # LED[i] - LED[i - 1], as this will no. of led's that changed when we displayed 11 | # char at i transitioning from i - 1. 12 | # So, let's say, n = "082", 0 would lighten up 6 led's, and now when displayed 8 13 | # would switch on one extra light, so 6 + 1 = 7 so far. 14 | # Now, 2 would switch off 2 extra led's, and so, 7 + 2 = 9 15 | # Hence, total 9 times LED's toggled. 16 | -------------------------------------------------------------------------------- /arrays/count_subarrays_XOR_K.py: -------------------------------------------------------------------------------- 1 | # Program for counting the subarrays with given XOR value as K. 2 | # Naive solution in 0(N^3)/0(N^2) 3 | # 4 | # [4, 2, 2, 6, 4] 5 | # 6 | # 4, 7 | # 4, 2, 8 | # 4, 2, 2, 9 | # 4, 2, 2, 6, 10 | # 4, 2, 2, 6, 4, 11 | # 12 | # 2, 13 | # 2, 2, 14 | # 2, 2, 6, 15 | # 2, 2, 6, 4, 16 | # ..... 17 | # 18 | # TIME : 0(N^2), SPACE : 0(1) 19 | # 20 | # -------------------------------------------------------------------------------------------------------- 21 | # Xr(i) 22 | # -------- 23 | # [4, 2, 2, 6, 4] 24 | # ____ 25 | # (y) m=XOR given 26 | # 27 | # Xr(i) => prefix XOR till {i} 28 | # there will be many y's for which we will get m as XOR of remaining array for XOR{i} subarray. 29 | # so, we have to count those y's. 30 | # TIME : 0(N), SPACE : 0(N) 31 | # ------------------------------------------------------------------------------------------------------- 32 | 33 | def compute_largest_XOR(arr, m): 34 | 35 | n = len(arr) 36 | XOR = [0] * n 37 | count = 0 38 | map = {} 39 | 40 | for i in range(1, n): 41 | XOR[i] = XOR[i - 1] ^ arr[i] 42 | 43 | for i in range(n): 44 | Y = m ^ XOR[i] 45 | 46 | if Y in map: 47 | count += map[Y] 48 | 49 | if XOR[i] == m: 50 | count += 1 51 | 52 | map[XOR[i]] = map.get(XOR[i], 0) + 1 53 | 54 | print(count) 55 | 56 | 57 | if __name__ == '__main__': 58 | arr = [4, 2, 2, 6, 4] 59 | compute_largest_XOR(arr, 6) 60 | -------------------------------------------------------------------------------- /arrays/find_odd_occurence_number.py: -------------------------------------------------------------------------------- 1 | # program to find odd occured element , if there is only one odd occurence element 2 | # one of the optimized approach would be to create hashmap and store all the occurences of every char, and then traversing over it and 3 | # check if it is odd, then print corresponding element 4 | # Best optimized approach is to use XOR bitmasking, as same elements would vanish , as in case of even occurence, whereas 5 | # different elements would appear as in odd , final result would get us that particular element which has odd occurence. 6 | # method 1 7 | # time complexity : 0(N) 8 | # space complexity : 0(N) 9 | # def find_occ(arr): 10 | # arr_size = len(arr) 11 | # max_value = max(arr) + 1 12 | # count = [0]*max_value 13 | # for i in range(arr_size): 14 | # count[arr[i]] += 1 15 | # for i in range(len(count)): 16 | # if count[i] & 1: 17 | # return i 18 | # method 2 19 | # time complexity : 0(N) 20 | # space complexity : 0(1) 21 | def find_occ(arr): 22 | res = 0 23 | for i in range(len(arr)): 24 | res = res ^ arr[i] 25 | return res 26 | 27 | if __name__ == '__main__': 28 | assert find_occ([1, 2, 3, 2, 3, 1, 3]) == 3 29 | assert find_occ([5, 7, 2, 7, 5, 2, 5]) == 5 30 | -------------------------------------------------------------------------------- /arrays/first_least_and_last_greater_element.py: -------------------------------------------------------------------------------- 1 | # Program to find first occurence of least greater value than key given in the array and similarly last occurence of greater value than key. 2 | # Intution is modified binary search , also a follow up from previous problem. 3 | # ----------------------------------------------------------------------------------------------------- 4 | # TIME : 0(log(N)), space : 0(1) 5 | 6 | # just greater than key , so it will be first least 7 | def first_least_greater(arr, key): 8 | 9 | pos = -1 10 | start, end = 0, len(arr) - 1 11 | while start <= end: 12 | mid = start + (end - start) // 2 13 | 14 | if key < arr[mid]: 15 | pos = mid 16 | end = mid - 1 17 | elif key > arr[mid]: 18 | start = mid + 1 19 | else: 20 | start = mid + 1 21 | 22 | return pos 23 | 24 | # just less than key, so it will be last greater 25 | def last_greatest_lesser(arr, key): 26 | 27 | pos = -1 28 | start, end = 0, len(arr) - 1 29 | while start <= end: 30 | mid = start + (end - start) // 2 31 | 32 | if key < arr[mid]: 33 | end = mid - 1 34 | elif key > arr[mid]: 35 | pos = mid 36 | start = mid + 1 37 | else: 38 | end = mid - 1 39 | 40 | return pos 41 | 42 | arr = [2, 3, 3, 5, 5, 5, 6, 6] 43 | print(first_least_greater(arr, 5)) 44 | print(last_greatest_lesser(arr, 5)) 45 | -------------------------------------------------------------------------------- /arrays/gym_lockers.py: -------------------------------------------------------------------------------- 1 | # Program for computing total count of open doors (lockers) after performing following operations: 2 | # In first pass, we open all doors, 3 | # in second pass, we close all other door (second door), 4 | # in third pass, we closes all third doors, 5 | # in fourth pass, we closes all fourth doors, .......... 6 | # How many are left open in the end. 7 | 8 | # If we observe closesly, then it's just factors of the number (current pass) which is important, 9 | # ex. 10 10 | # 1 2 3 4 5 6 7 8 9 10 11 | # N = 1 ____________________ 12 | # N = 2 * * * * * 13 | # N = 3 * * * * * * * 14 | # N = 4 ...... 15 | # N = 5 16 | # N = 10 _ * * _ * * * * _ * 17 | 18 | # So, only left open lockers are the count of all perfect squares upto 10 => 1, 4, 9 (3) 19 | # Similarly, we can generalise for all given N. 20 | 21 | # Hence, count of open doors/lockers from given N => floor(sqrt(N)) 22 | -------------------------------------------------------------------------------- /arrays/is_palindrome_number.py: -------------------------------------------------------------------------------- 1 | # program to check if a given number is palindrome is not 2 | # logic is to compute the reversed part of second half only while going through the digits and compare it with the first half 3 | # another approach is to convert it into str and then simply reverse and compare it but it will take extra space for string 4 | # log(x) base 10 where x is no of digits 5 | def check(n): 6 | # if number is -ve, then it is obviously not a palindrome 7 | if n < 0: 8 | return 0 9 | # if number is zero, then it is a palindrome 10 | if not n: 11 | return 1 12 | # if n is a single digit number, then it is a palindrome 13 | if 1 < n < 9: 14 | return 1 15 | # if numbers are of form 10, 100, 200, 1000 etc.., then it can't be palindrome 16 | if not n % 10: 17 | return 0 18 | # for others 19 | result = 0 20 | # going for half of the digits and computing the reverse of those half and then comparing the first half with that reversed second half 21 | while result < n: 22 | rem = n % 10 23 | result = result * 10 + rem 24 | n = n // 10 25 | # after this, n will be contain first half and result (reversed part) will contain second half 26 | return n == result or n == result // 10 27 | 28 | if __name__ == '__main__': 29 | print(check(20)) 30 | # for i in random.sample([random.randint(0, 1000000) for i in range(100000)], 10): 31 | # print(check(i)) 32 | -------------------------------------------------------------------------------- /arrays/jump_hopes.py: -------------------------------------------------------------------------------- 1 | # arr = [2, 0, 1, 0] 2 | # # ptr -> pointing to the first index, as 3 | # # i = 0 => hop 2 position => ptr at i = 1, then hop 1 position , ptr = > at i = 3 so, correct ! 4 | # # i = 1 => hop 0 position ... 5 | # # i = 1 => hop 1 position => ptr at i = 3, so correct ! 6 | # # 7 | # # so, for every i, we are using ptr to hop to forward and check if it reaches the end ! 8 | # # 9 | # # this takes 0(N^2) in worst case 10 | # can we do better ? 11 | 12 | 13 | 14 | def canReachEnd(arr): 15 | n = len(arr) 16 | for i in range(n): 17 | ptr = i 18 | print(f"outer ptr : {ptr}") 19 | while ptr < n and arr[ptr] != 0: 20 | print(f"inner ptr : {ptr}") 21 | if ptr == n - 1: 22 | return 1 23 | if arr[ptr] == 0: 24 | return 0 25 | ptr = ptr + arr[ptr] 26 | print("inner finished ") 27 | 28 | 29 | if __name__ == '__main__': 30 | arr = [3, 2, 1, 0, 4] 31 | print(canReachEnd(arr)) 32 | -------------------------------------------------------------------------------- /arrays/maximum6_and9.py: -------------------------------------------------------------------------------- 1 | # program to return the maximum number if given some number in the range 1-10000 (excluding higher range limit), only consisting of 9's and 6's by modifying atmost one digit. 2 | 3 | # logic is very simple as it is clear that if we go from left to right , since left digits carry more weightage, then we can understand if we change one digit wherever we get first '6' and then rest leave it as it is, then it will be the largest possible (if only one digit modification allowed.) 4 | import math 5 | def maximum69Number(num): 6 | s = str(num) 7 | index = 0 8 | result = '' 9 | # getting length of number 10 | length = math.ceil(math.log(num, 10)) 11 | flag = 0 12 | while index <= length - 1: 13 | # if it's 9, then simply append it to result 14 | if s[index] == '9': 15 | result += s[index] 16 | # if it's 6, and if it's the first (which is checked by the flag) then, we append 9 17 | else: 18 | if not flag: 19 | result += '9' 20 | flag = 1 21 | # otherwise, simply append 6 22 | else: 23 | result += '6' 24 | # moving forward one at a time , left to right 25 | index += 1 26 | return int(result) 27 | 28 | print(maximum69Number(9669)) 29 | -------------------------------------------------------------------------------- /arrays/min_operations_N.py: -------------------------------------------------------------------------------- 1 | # Program for computing minimum operations required from moving 0 to N. 2 | # We are allowed to use two operations : 3 | # 1. Add 1 to the number 4 | # 2. Double the number 5 | # --------------------------------------------------------------------------- 6 | # One of the observation is that minimum operations required from 0 -> N will be same as N -> 0. 7 | # So, we can basically move from N -> 0, and divide and subtract (instead of add and multiply as from 0-> N). 8 | # Now, we can think that if the number is even, then divide by 2, but if the number is odd, then we can subtract by 1 unit. 9 | # In this way min number of operations are counted . 10 | # ---------------------------------------------------------------------------- 11 | # TIME : 0(min_operations) 12 | 13 | def compute_min_opr(N): 14 | count = 0 15 | while N: 16 | if N & 1: 17 | N -= 1 18 | else: 19 | N = N // 2 20 | count += 1 21 | return count 22 | 23 | if __name__ == '__main__': 24 | print(compute_min_opr(7)) 25 | -------------------------------------------------------------------------------- /arrays/missing_number_list.py: -------------------------------------------------------------------------------- 1 | # program to find the missing number from a list of 0-n 2 | # IDEA: logic is to take XOR for given array , and then XOR of range 0-n, and then XOR of those two results will be the missing number. 3 | # we can see this in this way : L = A1, A2, A3 ; A1, A2, A3, A4 -> XOR will be (A1^A1)^(A2^A2)^(A3^A3)^A4 will give us A4. 4 | # One more logic is to use gaussian formula for summing up natural numers as n*(n+1)/2 and then take sum of given list and return difference of both. 5 | from sys import stdin, stdout 6 | # function for finding missing number 7 | def find_missing_number(l): 8 | result = 0 9 | # XOR for given list 10 | for i in l: 11 | result = result ^ i 12 | result1 = 0 13 | # XOR for range 0 to n 14 | for i in range(0, len(l)+1): 15 | result1 = result1 ^ i 16 | # this XOR will be the result 17 | return result ^ result1 18 | if __name__ == '__main__': 19 | #input_list = list(map(int, stdin.readline().strip().split())) 20 | l = [9, 6, 4, 2, 3, 5, 7, 0, 1] 21 | stdout.write(str(find_missing_number(l))) 22 | -------------------------------------------------------------------------------- /arrays/moving_zeros.py: -------------------------------------------------------------------------------- 1 | # program for moving all zeroes to the end and all 2 | # non zero elements at the front with the same relative ordering. 3 | # Optimized approach : 4 | # Idea here is to take a counter, and then simply for every non-zero element, we can increment counter and place the element at that index of the array, in this way, all non-zero 5 | # elements will be at the front maintaining their relative order, then simply put zeroes for the remaining lenght of the array. 6 | # TIME : 0(N), SPACE : 0(1). 7 | 8 | def moveZeroes(nums) -> None: 9 | n = len(nums) 10 | ptr = 0 11 | for i in range(0, n): 12 | if nums[i] != 0: 13 | nums[ptr] = nums[i] 14 | ptr += 1 15 | 16 | for i in range(ptr, n): 17 | nums[i] = 0 18 | -------------------------------------------------------------------------------- /arrays/pascal.py: -------------------------------------------------------------------------------- 1 | # program to print pascal's traingle and return one row if prompted to do so 2 | 3 | # import from my own library for efficieny purposes 4 | from mathemagica import mathemagica as m 5 | 6 | # function to print pascal_triangle 7 | def pascal_triangle_print(n, ret): 8 | # print and return from here if only row is to be printed 9 | if (ret == 1): 10 | for i in range(n): 11 | for j in range(i + 1): 12 | if i == n - 1: 13 | print(f'{m.binom(i, j)}', end = " ") 14 | return 15 | # from here whole triangle is printed 16 | # printing traingle using each binomial coefficients calculation 17 | print('printing whole traiangle...') 18 | for i in range(n): 19 | # printing spaces 20 | for j in range(n-i-1): 21 | print(' ', end = " ") 22 | # printing digits for that row 23 | for j in range(i + 1): 24 | print(f'{m.binom(i, j)} ', end = " ") 25 | print() 26 | 27 | 28 | # main function 29 | if __name__ == '__main__': 30 | print('Wanna print the whole traiangle or just a row : ? press 0 for former and 1 for latter') 31 | t = int(input()) 32 | print('enter the number of rows to be printed:') 33 | n = int(input()) 34 | pascal_triangle_print(n, t) 35 | -------------------------------------------------------------------------------- /arrays/player.py: -------------------------------------------------------------------------------- 1 | # program to determine who wins based on bunch of conditions (simple conditions to break tie on a card game) 2 | def winning_player(winning_suit, suit1, number1, suit2, number2): 3 | if suit1 == winning_suit: 4 | if suit2 == winning_suit: 5 | if number1 > number2: 6 | return "Player 1 wins" 7 | elif number2 > number1: 8 | return "Player 2 wins" 9 | else: 10 | return "Draw" 11 | else: 12 | return "Player 1 wins" 13 | 14 | elif suit2 == winning_suit: 15 | return "Player 2 wins" 16 | else: 17 | if number1 > number2: 18 | return "Player 1 wins" 19 | elif number2 > number1: 20 | return "Player 2 wins" 21 | else: 22 | return "Draw" 23 | 24 | if __name__ == '__main__': 25 | winning_suit = 'B' 26 | n_rounds = 5 27 | game_play = [['A', 2, 'B', 1], ['A', 7, 'D', 2], ['B', 5, 'D', 13], ['B', 3, 'B', 1], ['A', 12, 'C', 12]] 28 | for i in range(0, len(game_play)): 29 | print(winning_player(winning_suit, game_play[i][0], game_play[i][1], game_play[i][2], game_play[i][3])) 30 | -------------------------------------------------------------------------------- /arrays/prefix_sum.py: -------------------------------------------------------------------------------- 1 | # program to create prefix sum array array prefixSum[] of same size such that the value of prefixSum[i] is arr[0] + arr[1] + arr[2] … arr[i]. 2 | 3 | def prefix_sum(arr): 4 | if len(arr) == 0: 5 | return 6 | if len(arr) == 1: 7 | return [arr[0]] 8 | arr_size = len(arr) 9 | prefix_array = [0] * arr_size 10 | prefix_array[0] = arr[0] 11 | for i in range(1, arr_size): 12 | prefix_array[i] = arr[i] + prefix_array[i - 1] 13 | return prefix_array 14 | 15 | # main function 16 | if __name__ == '__main__': 17 | prefix_sum([10, 20, 10, 5, 15]) == [10, 30, 40, 45, 60] 18 | prefix_sum([10, 4, 16, 20 ]) == [10, 14, 30, 50] 19 | prefix_sum([]) == None 20 | prefix_sum([10]) == [10] 21 | -------------------------------------------------------------------------------- /arrays/reverse_digits_numbers.py: -------------------------------------------------------------------------------- 1 | # program to reverse the integer keeping overflow in mind 2 | # logic is to keep a track of all remainders and use following formula like - result = result * 10 + remainder to combine the complete inverse digits 3 | # and keep reducing the number itself by taking in account of division by 10 4 | from sys import maxsize, stdin, stdout 5 | # function to reverse the integer 6 | def reverse(self, x: int) -> int: 7 | # initialize the result 8 | result = 0 9 | # we need to account for +ve and -ve numbers , so keeping +1 for +ve , -1 for -ve numbers. 10 | flag = 1 11 | if x < 0: 12 | flag = -1 13 | x = flag * x 14 | if x % 10 == x: 15 | return flag * x 16 | while(x >= 1): 17 | remainder = x % 10 18 | x = x // 10 19 | result = result * 10 + remainder 20 | return flag * result if result < maxsize else 0 21 | -------------------------------------------------------------------------------- /arrays/segregate_zeroes_ones_efficient.py: -------------------------------------------------------------------------------- 1 | # Program for zeros and ones segregatation, efficiently. 2 | # Idea is to basically keep two pointers and then, we can keep on moving left pointers (incrementing) until we hit 1, and then keep on moving 3 | # right pointers (decrementing) until we hit 0, and then, we will be able to actually stop at that position and now, 4 | # if left < right, then it means that "1" is at left pointer, "0" is at right pointer, so we need to swap these both. 5 | # In this way, all 0's will be at front, and then all 1's will be at back of the array. 6 | # This is also efficient in a way such that only one pass is required otherwise we can also simply count the number of times "1" and "0" occurs 7 | # and simply copy to the original array resulting in segregatation but that can take more than one pass. 8 | # TIME : 0(N), SPACE : 0(1) 9 | 10 | import random 11 | def segregate(arr, n): 12 | left, right = 0, n - 1 13 | while left < right: 14 | 15 | while arr[left] == 0 and left < right: 16 | left += 1 17 | 18 | while arr[right] == 1 and left < right: 19 | right -= 1 20 | 21 | if left < right: 22 | arr[left], arr[right] = 0, 1 23 | left += 1 24 | right -= 1 25 | 26 | return arr 27 | 28 | if __name__ == '__main__': 29 | arr = [random.randint(0, 1) for _ in range(10)] 30 | print(arr) 31 | segregate(arr, len(arr)) 32 | print(arr) 33 | -------------------------------------------------------------------------------- /arrays/sorted_negative_integers.py: -------------------------------------------------------------------------------- 1 | # program for sorting positive numbers , keeping negative numbers at their place. 2 | 3 | # importing my implementation of counting sort 4 | from counting_sort import counting_sort as count_sort 5 | from sys import stdin, stdout 6 | 7 | # IDEA: logic is to move all the positive integers into a new array (list) and then sort them using counting sort and then, move them back into their 8 | # respective position into the array by keeping a pointer at the new array and incrementing it whenever we move one element from that array to the main. 9 | def negative_sort(arr, n): 10 | # moving the positive integers to the new array #for i in range(n): if arr[i] >= 0: l.append(arr[i]), same as this one. 11 | l = [arr[i] for i in range(n) if arr[i] >= 0] 12 | k = len(l) 13 | # sorting the array 14 | l = count_sort(l, k) 15 | p = 0 16 | # now, replacing the old element with the correct position of sorted element 17 | for i in range(n): 18 | if arr[i] >= 0: 19 | arr[i] = l[p] 20 | p += 1 21 | return arr 22 | 23 | # main function 24 | if __name__ == '__main__': 25 | n = int(stdin.readline().strip()) 26 | arr = list(map(int, stdin.readline().strip().split())) 27 | print(negative_sort(arr, n)) 28 | -------------------------------------------------------------------------------- /arrays/staright_line_check.py: -------------------------------------------------------------------------------- 1 | # program for checking if its a straight line or not , given a set of coordinates 2 | # IDEA: logic is to calculate the slope of first two coordinates and then check slope of every other point, if all are same then we can form straight line passing through all of them otherwise not. 3 | 4 | from sys import stdin, stdout 5 | def check_straight(coordinates): 6 | # setting the intial coordinates for finding slope 7 | x1, y1, x2, y2 = coordinates[0][0], coordinates[0][1], coordinates[1][0], coordinates[1][1] 8 | for x, y in coordinates: 9 | if (y - y1)*(x2 - x1) != (y2 - y1)*(x - x1): # for calculating slope using product form of slope formula to avoid zero divison error 10 | return False 11 | return True 12 | 13 | # tests 14 | assert check_straight([[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7]]) == True 15 | assert check_straight([[1,1],[1,2],[3,4],[4,5],[5,6],[7,7]]) == False 16 | # main function 17 | if __name__ == '__main__': 18 | coordinates = [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7]] 19 | stdout.write(str(check_straight(coordinates))) 20 | -------------------------------------------------------------------------------- /arrays/streak.py: -------------------------------------------------------------------------------- 1 | # program to return the maximum streak in a array containing tosses results of some rounds as [heads, heads, tails, ......] 2 | 3 | def maximum_stream(arr): 4 | max_count_h, max_count_t = 0, 0 5 | curr_count_h, curr_count_t = 0, 0 6 | for i in range(0, len(arr)): 7 | if arr[i] == 'H': 8 | curr_count_h += 1 9 | curr_count_t = 0 10 | max_count_h = max(max_count_h, curr_count_h) 11 | else: 12 | curr_count_t += 1 13 | curr_count_h = 0 14 | max_count_t = max(max_count_t, curr_count_t) 15 | return [max_count_h, max_count_t] 16 | 17 | if __name__ == '__main__': 18 | assert maximum_stream(['H', 'T', 'T', 'T', 'H', 'H', 'T']) == [2, 3] 19 | assert maximum_stream(['H', 'H', 'H', 'T', 'T', 'H', 'H', 'H', 'H', 'T']) == [4, 2] 20 | assert maximum_stream(['H']) == [1, 0] 21 | assert maximum_stream(['T', 'T', 'T']) == [0, 3] 22 | assert maximum_stream(['H', 'H', 'H', 'H']) == [4, 0] 23 | -------------------------------------------------------------------------------- /arrays/sum_n_divisible.py: -------------------------------------------------------------------------------- 1 | # Program for finding sum of n digit numbers divisible by p 2 | 3 | # logic is to firstly find out the first and last number of that required range, like for 1 digit - it's 1 to 10, then for two digit, it's 10 to 100 , similary 100-to 1000 for three digit numbers. 4 | # Then the second thing is that can find out the first number and last number divisible by given number , and it follows from the observation of this series is A.P and then hence , we can calculate no of terms using formula for nth term, a(n) = a + (n - 1) * d and then use second formula for calculating sum of the series : s = (n/2)*[a + l] / 2 5 | def sum_div(n, p): 6 | # calculate first and second for the range 7 | first_num = pow(10, n - 1) 8 | last_num = pow(10, n) 9 | # calculate those first and last which are divisible 10 | first_dig = first_num - first_num % p + p 11 | last_dig = last_num - last_num % p 12 | # count of those all numbers in this sequence 13 | count_numbers = ((last_dig - first_dig) / p) + 1 14 | # return the sum now 15 | return ((count_numbers) * (first_dig + last_dig) / 2) 16 | 17 | print(sum_div(3, 7)) 18 | -------------------------------------------------------------------------------- /arrays/triplet_target_array.py: -------------------------------------------------------------------------------- 1 | # program to find triplet of numbers which add up to some target sum for a given array 2 | 3 | # function to find the triplet and print the triplet 4 | # idea is to first sort the array, then fix one of the element and then choose/select other two required like the same finding pair for a sum problem 5 | def triple_sum(l, target): 6 | # sort the array 7 | l.sort() 8 | # fixing one element one by one 9 | for i in range(len(l)-2): 10 | if i>0 and l[i]==l[i-1]: continue 11 | # setting first index as always the next one of choosen element 12 | start = i + 1 13 | end = len(l) - 1 14 | # now we know the target for a pair to be find 15 | k = target - l[i] 16 | # finding those other two for the fixed element 17 | while(start < end): 18 | if l[start] + l[end] == k: 19 | print(f'{l[i]}, {l[start]} and {l[end]}') 20 | # if removing duplicates, then 21 | while start < end and l[start] == l[start - 1]: start += 1 22 | while start < end and l[end] == l[end + 1]: end -= 1 23 | start += 1 24 | end -= 1 25 | if l[start] + l[end] > k: 26 | end -= 1 27 | else: 28 | start += 1 29 | 30 | # main function 31 | if __name__ == '__main__': 32 | t = int(input().strip()) 33 | l = [] 34 | for i in range(t): 35 | l.append(int(input().strip())) 36 | target = int(input().strip()) 37 | triple_sum(l, target) 38 | -------------------------------------------------------------------------------- /arrays/ultra_fast_strings.py: -------------------------------------------------------------------------------- 1 | # In his contest he gave the contestants many different pairs of numbers. Each number is made from 2 | # digits 0 or 1. The contestants should write a new number corresponding to the given pair of 3 | # numbers. The rule is simple: The i-th digit of the answer is 1 if and only if the i-th digit of 4 | # the two given numbers differ. In the other case the i-th digit of the answer is 0. 5 | 6 | # Program for giving output on the basis of above description, giving two numbers as input. 7 | 8 | def UltraString(s1, s2): 9 | for i in range(len(s1)): 10 | print(ord(s1[i]) ^ ord(s2[i]), end = "") 11 | 12 | if __name__ == '__main__': 13 | s1, s2 = "10111", "10000" 14 | UltraString(s1, s2) 15 | -------------------------------------------------------------------------------- /binary search trees/Searching_BST.py: -------------------------------------------------------------------------------- 1 | # Program for searching for key into binary search tree. 2 | # WE CAN TAKE FOLLOWING EXAMPLE FOR VISUALIZING : 3 | # ---------------------------------------------------- 4 | # 4 5 | # / \ 6 | # 2 5 7 | # / \ 8 | # 1 3 9 | # -------------------------------- 10 | class Node: 11 | 12 | def __init__(self, val): 13 | self.data = val 14 | self.left = None 15 | self.right = None 16 | 17 | 18 | def search(root, key): 19 | 20 | 21 | if root == None: 22 | return False 23 | 24 | if root.data == key: 25 | return True 26 | 27 | if key < root.data: 28 | return search(root.left, key) 29 | else: 30 | return search(root.right, key) 31 | 32 | if __name__ == '__main__': 33 | root = Node(4) 34 | root.left = Node(2) 35 | root.right = Node(5) 36 | root.left.left = Node(1) 37 | root.left.right = Node(3) 38 | print(search(root, 39)) 39 | -------------------------------------------------------------------------------- /binary search trees/counting_unique_BST_n_nodes.py: -------------------------------------------------------------------------------- 1 | # Program for counting unique BST's with 'n' number of nodes. 2 | # From counting and observing as done for binary unlabelled trees, we can observe this follows catalan numbers, 3 | # and we can directly return the value of catalan numbers. 4 | # C : 1, 1, 2, 5, 14..... 5 | 6 | # code will be similar to binary tree unlabelled counting. 7 | # Also approach can be DP one or the binom formula. 8 | -------------------------------------------------------------------------------- /binary trees/Interview_patterns.MD: -------------------------------------------------------------------------------- 1 | # FOR INTERVIEW PATTERNS : 2 | * TOP DOWN APPROACH : 3 | ## Preorder/ level order traversal/ ANY DFS ITERATIVE 4 | ## Normal DAC recursive approach 5 | ## Direct recursive relations 6 | * BOTTOM UP APPROACH : 7 | ## Postorder 8 | ## Pair logic (returning multiple values from node going bottom up) 9 | * General tips : 10 | * Complexity reduces from 0(N^2) => 0(N) by using bottom up approach. 11 | * Also, sometimes hashmaps, deque, queue can reduce Complexity. 12 | * In any recursive calls, we pass one of the args in the calls whom we don't intend to change in the every recursive call, 13 | * so, we pass it by reference by sending list[element] in the intial args. 14 | * If doing iterative approach and need to go back up, hashmaps storing back pointers (parent) is really helpful 15 | (True for trees and graphs). 16 | ## Iterative approach : 17 | * Usually this takes more space due to stack/queue/deque but this is pretty easy to think in terms of BFS approach (often coupled with None pointer delimiter logic) 18 | * We can use a queue with BFS coupled with HashMap sorting based on keys efficient for vertical order/bottom order otherwise we can also implement our own MAP as python doesn;t 19 | support inbuild dict sorted. 20 | * Use None delimiter logic in BFS 21 | * Use horizonal distance logic marking by 0, -1, 1, -2, 2...... 22 | * Use bidirectional BFS search idea 23 | -------------------------------------------------------------------------------- /binary trees/binary_trees_intro.py: -------------------------------------------------------------------------------- 1 | # Program for binary trees introduction 2 | # We can take example for follwoing tree and visualize stack call : 3 | # 1 4 | # / \ 5 | # 2 3 6 | # / \ 7 | # 4 5 8 | # -------------------------------- 9 | class Node: 10 | 11 | def __init__(self, data): 12 | self.left = None 13 | self.right = None 14 | self.data = data 15 | 16 | if __name__ == '__main__': 17 | root = Node(1) 18 | root.left = Node(2) 19 | root.right = Node(3) 20 | root.left.left = Node(4) 21 | root.left.right = Node(5) 22 | print(root.data) 23 | print(root.left.data) 24 | print(root.right.data) 25 | print(root.left.left.data) 26 | 27 | # 1 28 | # / \ 29 | # 2 3 30 | # / 31 | # 4 32 | -------------------------------------------------------------------------------- /binary trees/convert_binary_tree_doublylinkedlist.py: -------------------------------------------------------------------------------- 1 | # Program for conversion of binary tree to doubly linked list. 2 | # 1 3 | # / \ 4 | # 2 3 5 | # / \ 6 | # 4 5 7 | # -------------------------------- 8 | # For the above inorder traversal : 4 2 1 5 3 9 | # In the same way, we will create doubly linked list in inorder traversal. 10 | 11 | class Node: 12 | 13 | def __init__(self, data): 14 | self.left = None 15 | self.right = None 16 | self.data = data 17 | 18 | def convert_double(root, head, prev): 19 | 20 | if root == None: 21 | return 22 | 23 | convert_double(root.left, head, prev) 24 | if prev[0] == None: 25 | head = root 26 | print(head.data) 27 | else: 28 | root.left = prev[0] 29 | prev[0].right = root 30 | 31 | prev[0] = root 32 | convert_double(root.right, head, prev) 33 | 34 | def print_list(head): 35 | 36 | ptr = head 37 | print("HEY") 38 | while ptr: 39 | print(ptr.data, end = " ") 40 | ptr = ptr.right 41 | 42 | if __name__ == '__main__': 43 | root = Node(1) 44 | root.left = Node(2) 45 | root.right = Node(3) 46 | root.left.left = Node(4) 47 | root.left.right = Node(5) 48 | head = None 49 | prev = [None] 50 | convert_double(root, head, prev) 51 | -------------------------------------------------------------------------------- /binary trees/count_sum_leaf_nodes.py: -------------------------------------------------------------------------------- 1 | # Program to count and get sum of leaf nodes in the binary tree. 2 | # ------------------------------ 3 | # We can take example for follwoing tree and visualize stack call : 4 | # (2, 1) 1 5 | # / \ 6 | # (1, 1) 2 3 (0, 0) 7 | # / \ 8 | # (0, 0) 4 5 (0, 0) 9 | # -------------------------------- 10 | # We can actuall the following recurrence relation as we observe 11 | # count_leaf(root) = count_leaf(root.left) + count_leaf(root.right) 12 | # ----------------------------------------- 13 | # BASE CASES : 14 | # so, if root == None, return 0 15 | # so, if root.left, root.right is None, return 1 16 | # ----------------------------------------- 17 | 18 | class Node: 19 | 20 | def __init__(self, data): 21 | self.left = None 22 | self.right = None 23 | self.data = data 24 | 25 | def count_leaf(root): 26 | 27 | if root == None: 28 | return 0 29 | 30 | if root.left == None and root.right == None: 31 | return 1 32 | 33 | return count_leaf(root.left) + count_leaf(root.right) 34 | 35 | def sum_leaf(root): 36 | 37 | if root == None: 38 | return 0 39 | 40 | if root.left == None and root.right == None: 41 | return root.data 42 | 43 | return sum_leaf(root.left) + sum_leaf(root.right) 44 | 45 | if __name__ == '__main__': 46 | root = Node(1) 47 | root.left = Node(2) 48 | root.right = Node(3) 49 | root.left.left = Node(4) 50 | root.left.right = Node(5) 51 | print(count_leaf(root)) 52 | print(sum_leaf(root)) 53 | -------------------------------------------------------------------------------- /binary trees/height_of_tree.py: -------------------------------------------------------------------------------- 1 | # Program for computing height of the binary tree. 2 | # Height is simply the longest path from root to the leaf node. 3 | # IDEA: Approach is to as always break down the into recursive approach, and observing that height of the tree 4 | # is maximum of the (height of left subtree, height of right subtree) + 1. 5 | # So, in this way, as every node we compute max(left subtree, right subtree) + 1. 6 | # We can take example for follwoing tree and visualize stack call : 7 | # (3) 1 8 | # / \ 9 | # (2) 2 3 (1) 10 | # / \ 11 | # (1) 4 5 (1) 12 | # -------------------------------- 13 | 14 | class Node: 15 | 16 | def __init__(self, data): 17 | self.left = None 18 | self.right = None 19 | self.data = data 20 | 21 | # recursive function to compute the height of the tree 22 | # height(root) = 1 + max(height(root.left), height(root.right)) 23 | def height(root): 24 | 25 | if root == None: 26 | return 0 27 | 28 | return 1 + max(height(root.left), height(root.right)) 29 | 30 | if __name__ == '__main__': 31 | root = Node(1) 32 | root.left = Node(2) 33 | root.right = Node(3) 34 | root.left.left = Node(4) 35 | root.left.right = Node(5) 36 | print(height(root)) 37 | -------------------------------------------------------------------------------- /binary trees/level_order_traversal_BFS.py: -------------------------------------------------------------------------------- 1 | # Program to print level order traversal for the binary tree 2 | # APPROACH : Approach is to think that for level order, we first expand 3 | # whatever is explored first, in breadth wise manner, that is we need to 4 | # have some buffer which can store next set of elements to be explored once we 5 | # finish exploring current element. 6 | # That is we need some intermediate structure, here "queue" which uses this 7 | # property of first in first out. 8 | 9 | from collections import deque 10 | 11 | class Node: 12 | """Node structure """ 13 | def __init__(self, val): 14 | self.data = val 15 | self.left = None 16 | self.right = None 17 | 18 | # function for level order traversal for binary tree 19 | def levelOrder(root): 20 | 21 | if not root: 22 | return 23 | 24 | queue = deque([]) 25 | queue.append(root) 26 | 27 | while queue: 28 | 29 | temp = queue.popleft() 30 | print(temp.data, end = " ") 31 | 32 | if temp.left: 33 | queue.append(temp.left) 34 | 35 | if temp.right: 36 | queue.append(temp.right) 37 | 38 | # driver test function 39 | if __name__ == '__main__': 40 | root = Node(13) 41 | root.left = Node(12) 42 | root.right = Node(10) 43 | root.left.left = Node(4) 44 | root.left.right = Node(19) 45 | root.right.left = Node(16) 46 | root.right.right = Node(9) 47 | levelOrder(root) 48 | -------------------------------------------------------------------------------- /binary trees/max_path_node_tree_dp.py: -------------------------------------------------------------------------------- 1 | # Program to compute maximum path from any node to any node. 2 | # We have already seen other solutions to the problem but here, we will use DP. 3 | class Node: 4 | 5 | def __init__(self, data): 6 | self.left = None 7 | self.right = None 8 | self.data = data 9 | 10 | # DP using postorder traversal 11 | 12 | def path_sum(root, res): 13 | 14 | if root == None: 15 | return 0 16 | 17 | left = path_sum(root.left, res) 18 | right = path_sum(root.right, res) 19 | 20 | temp = max(max(left, right) + root.data, root.data) # this case is when root is not part of answer and it will be passed onto above parent who called it 21 | ans = max(temp, root.data + left + right) # this will be when root is part of answer 22 | res[0] = max(temp, ans) # we have to take max of both possible above cases 23 | 24 | return temp 25 | 26 | # res is passed as reference and will contain the final answer 27 | def compute_max_sum(root): 28 | res = [-sys.maxsize-1] 29 | path_sum(root, res) 30 | return res[0] 31 | 32 | 33 | 34 | import sys 35 | # driver test function 36 | if __name__ == '__main__': 37 | root = Node(1) 38 | root.left = Node(2) 39 | root.right = Node(3) 40 | root.left.left = Node(4) 41 | root.left.right = Node(5) 42 | print(compute_max_sum(root)) 43 | -------------------------------------------------------------------------------- /binary trees/path_from_root_to_node.py: -------------------------------------------------------------------------------- 1 | # We can take example for follwoing tree and visualize stack call : 2 | # 1 3 | # / \ 4 | # 2 3 5 | # / \ 6 | # 4 5 7 | # -------------------------------- 8 | class Node: 9 | 10 | def __init__(self, data): 11 | self.left = None 12 | self.right = None 13 | self.data = data 14 | 15 | 16 | # At every node, we check if it's the root data , if not 17 | # then, we check for left subtree and right subtree. 18 | def findPath(root, path, k): 19 | 20 | if root == None: 21 | return False 22 | 23 | 24 | path.append(root.data) 25 | if root.data == k: 26 | return True 27 | 28 | if root.left and findPath(root.left, path, k) or root.right and findPath(root.right, path, k): 29 | return True, path 30 | 31 | path.pop() 32 | return False 33 | 34 | 35 | if __name__ == '__main__': 36 | root = Node(1) 37 | root.left = Node(2) 38 | root.right = Node(3) 39 | root.left.left = Node(4) 40 | root.left.right = Node(5) 41 | print(findPath(root, [], 5)) 42 | -------------------------------------------------------------------------------- /binary trees/symmetric_tree.py: -------------------------------------------------------------------------------- 1 | # Program for checking whether given tree is symmetric tree 2 | # --------------------------------------------------------------------------- 3 | # 4 | # 1 5 | # / \ 6 | # 2 2 => True 7 | # / \ / \ 8 | # 3 4 4 3 9 | # 10 | # 11 | # 1 12 | # / \ 13 | # 2 2 => False 14 | # \ \ 15 | # 3 3 16 | # 17 | # ---------------------------------------------------------------------------- 18 | # We can observe following facts, for the symmetrical tree : 19 | # firstly, root data must be same, then root.left == root.right, 20 | # root.left.left == root.right.right, then root.left.right == root.right.left 21 | # ---------------------------------------------------------------------------- 22 | class Node: 23 | 24 | def __init__(self, data): 25 | self.left = None 26 | self.right = None 27 | self.data = data 28 | 29 | def is_symmetric(root1, root2): 30 | if root1 == None and root2 == None: 31 | return True 32 | 33 | if root1 and root2: 34 | return root1.data == root2.data and is_symmetric(root1.left, root2.right) and is_symmetric(root1.right, root2.left) 35 | 36 | return False 37 | 38 | def check_symmetric(root): 39 | return is_symmetric(root, root) 40 | 41 | if __name__ == '__main__': 42 | root = Node(1) 43 | root.left = Node(2) 44 | root.right = Node(3) 45 | root.left.left = Node(4) 46 | root.left.right = Node(5) 47 | print(check_symmetric(root)) 48 | -------------------------------------------------------------------------------- /bitwise/add_using_bitwise_operators.py: -------------------------------------------------------------------------------- 1 | # Program to add the numbers using bitwise operators 2 | # Logic is to use half adder logic, like "&" AND operator is used for getting carry - positions where both numbers have set bits because 3 | # only on those positions we can have carry 4 | # and "^" XOR is useful for getting the actual sum. 5 | # but the thing is that we actually add the carry by left shifting it by 1 in the next iteration. 6 | # Algorithm is similar to what we usually do in normal multiplication elementry way but using bits now. 7 | 8 | def add_bitwise(x, y): 9 | ''' 10 | function : to add the two numbers given 11 | x (int): first number 12 | y (int) : second number 13 | 14 | return : the result of addition of two numbers given as input to this method 15 | ''' 16 | while y != 0: 17 | # here x is actual sum maintaining running sum, and y is bascially carry left shited by 1 18 | # we are doing both in parallel 19 | x, y = x ^ y, (x & y) << 1 20 | 21 | # finally x will contain the overall sum 22 | return x 23 | 24 | 25 | if __name__ == '__main__': 26 | a, b = 15, 32 27 | print(add_bitwise(a, b)) 28 | -------------------------------------------------------------------------------- /bitwise/bit_trick_sum.py: -------------------------------------------------------------------------------- 1 | # Just one trick to observe while we sum two different digits in differen n-base systems : 2 | # Let's say we have 1->2 => 12 3 | # So, what we did above was simply following : 4 | # taking first digit and left shift by one bit and add the next digit : 5 | # 1 * 10 + 2 = 12 6 | 7 | # Similar approach could be followed for other systems such as binary : 8 | # 1->1 => 3 9 | # 1 << 1 | 1 => 3 10 | curr_number = 1 11 | curr_number = (1 << 1) | 1 = 3 12 | -------------------------------------------------------------------------------- /bitwise/compare_without_operators.py: -------------------------------------------------------------------------------- 1 | # Program to compare two numbers without using any comparison operators and string functions 2 | # # IDEA: Logic is to use XOR operators and as we know 3 | # x ^ x = 0 and x ^ y = 1 4 | 5 | def compare(x, y): 6 | 7 | return x ^ y 8 | 9 | # if use of arithmetical operators are allowed, then we need to use 10 | # subtract => (x - y) = 0 if EQUAL. 11 | if __name__ == '__main__': 12 | x, y = 30, 10 13 | if not compare(x, y): 14 | print("EQUAL") 15 | else: 16 | print("UNEQUAL !") 17 | 18 | x, y = 30, 30 19 | if not compare(x, y): 20 | print("EQUAL") 21 | else: 22 | print("UNEQUAL !") 23 | -------------------------------------------------------------------------------- /bitwise/compliment_number.py: -------------------------------------------------------------------------------- 1 | # Program to compute the compliment of number. 2 | # APPROACH 1: 3 | 4 | def bin_to_dec(n): 5 | res = 0 6 | power = 0 7 | ptr = len(n) - 1 8 | while ptr > 0: 9 | res += int(n[ptr]) * (2 ** power) 10 | power += 1 11 | ptr -= 1 12 | 13 | return res 14 | 15 | def compute_compliment(n): 16 | s = bin(n)[2:] 17 | s = list(s) 18 | for i in range(len(s)): 19 | s[i] = int(s[i]) ^ 1 20 | 21 | return bin_to_dec("".join(map(str, s))) 22 | 23 | # APPROACH 2: 24 | # Here, in this soution, we are just making another bit variable to be full of 1's upto 25 | # the length of bits in num and then simply returning the XOR of two 26 | # num = 5 = 101 27 | # bit ===== 111 28 | # Ans ==== 010 = 2 29 | # ----- bitwise ------- 30 | 31 | def compute_compliment_bitwise(n): 32 | 33 | temp1 = 0 34 | temp2 = n 35 | while temp2: 36 | temp1 = (temp1 << 1) ^ 1 37 | temp2 = temp2 >> 1 38 | 39 | return temp1 ^ n 40 | 41 | print(compute_compliment_bitwise(27)) 42 | -------------------------------------------------------------------------------- /bitwise/count_set_bits_range_query.py: -------------------------------------------------------------------------------- 1 | # Program for counting the set bits for the given range query. 2 | # Assume all range starts from 0 and goes till Num. 3 | # Input : Num (int) 4 | # 0<=n<=Num, find for all n values 5 | # We have already seen simple solutions but here we will use a trick due to number of ranges so that query takes 0(1). 6 | 7 | # We can observe one fact that if we know set bits of some "x", then we can find out set bits of "x/2" or vice-versa. 8 | # If the number is odd, and we right shift (divide by 2), then we lose "1" at its least significant digit. 9 | # so, count of set bits will also be affected. 10 | # If the number is even, and we right shift (divide by 2), then no change in set bit count as lsb contains 0. 11 | 12 | # So, let's say : x = 2 => set bits of 2 => 1, then set bits of 4 => even => 1, but if 5 => odd => 1 + (5 >> 2) => 2 13 | # and set bits of x = 7 =: 3, set bits of 14 => even => 3, and set bits of 15 => 1 + 15>>2 => 1 + 3 => 4 14 | 15 | # ------------------------------------------------------------------------------------------------------------------------ 16 | 17 | class Solution: 18 | def countBits(self, num: int) -> List[int]: 19 | ans = [0] 20 | for i in range(1, num + 1): 21 | if i & 1: 22 | ans.append(1 + ans[i >> 1]) # ans[i >> 1] is same as ans[i // 2] 23 | else: 24 | ans.append(ans[i >> 1]) 25 | 26 | return ans 27 | -------------------------------------------------------------------------------- /bitwise/decimal_to_binary.py: -------------------------------------------------------------------------------- 1 | # function to convert decimal number to binary 2 | # IDEA: logic is that first, we can get last bit by simply ANDing with 1 , and keep right shifting by 1 , but then we will get reverse of the number 3 | # so, we need to actually move from end towards start and keep combining running sum in the form of base10 calculation by multiplying every digit extracted by 4 | # correspoding power of 10 and adding it to the running sum obtained so far. 5 | 6 | def decimal_to_binary(x): 7 | ans = 0 8 | power = 1 9 | while (x > 0): 10 | ans += (x & 1) * power 11 | x = x >> 1 12 | power *= 10 13 | return ans 14 | 15 | print(decimal_to_binary(3)) 16 | -------------------------------------------------------------------------------- /bitwise/fast_exponentiation_binary_algo_power.py: -------------------------------------------------------------------------------- 1 | # Program to compute exponentiation efficiently 2 | # compute a raise to power b most efficient way using bitwise logic 3 | # IDEA: firstly, we note that if we normally multiply "a" , "b" number of times, then it takes 0(N) time which is not optimal, 4 | # so, what we use is bit level information for "b" and use it to multiply "a" only when there is set bit "1" in "b" which reduces the 5 | # computations of multiplcations at most log(N) times. 6 | # Time : 0(log(N)), SPACE : 0(1), WHERE N IS EXPONENT. 7 | 8 | def expo(a, n): 9 | ans = 1 10 | while n > 0: 11 | if n & 1: 12 | ans *= a 13 | 14 | a *= a 15 | n = n >> 1 16 | 17 | return ans 18 | 19 | # here x, n = > a, b 20 | def compute_power(x: float, n: int) -> float: 21 | if n > 0: 22 | return float('%.5f'%(expo(x, n))) 23 | else: 24 | return float('%.5f'%(1 / expo(x, -n))) 25 | 26 | if __name__ == '__main__': 27 | print(compute_power(2.00000, 2)) 28 | # assert expo(2, 4) == 16 29 | # assert expo(4, 4) == 256 30 | # assert expo(3, 5) == 243 31 | # assert expo(5, 3) == 125 32 | # assert expo(5, 1) == 5 33 | # assert expo(5, 0) == 1 34 | # 35 | -------------------------------------------------------------------------------- /bitwise/find_random_added_string_XOR.py: -------------------------------------------------------------------------------- 1 | # Problem is that there are two strings s, t and string t is generated by randomly shuffling string "s" and adding one extra letter anywhere randomly. 2 | # Now, we have to find that extra randomly added element. 3 | # Since, this is guaranteed that there is only one extra element added, then we can make use of XOR trick to find that extra element. 4 | # ex. s = "abcd", t = "abcde" 5 | # S ^ T = "e" 6 | # Time : 0(N), Space : 0(1) 7 | 8 | # function to solve above problem using XOR trick 9 | 10 | def findTheDifference(self, s: str, t: str) -> str: 11 | 12 | if not len(s) and not len(t): 13 | return 14 | 15 | # since , XOR works on numbers, hence converting to ASCII form 16 | # taking XOR of first string 17 | res = 0 18 | for i in s: 19 | res ^= ord(i) 20 | # taking XOR of second string which effectively will cancel out repeated 21 | # elements and remaining one will be the extra element 22 | for i in t: 23 | res ^= ord(i) 24 | return chr(res) 25 | -------------------------------------------------------------------------------- /bitwise/find_right_most_setbit.py: -------------------------------------------------------------------------------- 1 | # simple right shifting bit one by one until it hits 1, if it hits 1, then we check and return the count. 2 | # TIME : 0(log(n)) as max digits is log2(n). 3 | 4 | def get_right_set_bit(n): 5 | count = 1 6 | while n: 7 | if n & 1: 8 | return count 9 | n = n >> 1 10 | count += 1 11 | -------------------------------------------------------------------------------- /bitwise/odd_even.py: -------------------------------------------------------------------------------- 1 | def isEven(x): 2 | return x & 1 3 | 4 | 5 | print(isEven(2)) 6 | print(isEven(23)) 7 | print(isEven(0)) 8 | print(isEven(171)) 9 | print(isEven(200)) 10 | -------------------------------------------------------------------------------- /cc.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/souravs17031999/100dayscodingchallenge/d05966f3e6875a5ec5a8870b9d2627be570d18d9/cc.JPG -------------------------------------------------------------------------------- /concurrency_leetcode/print_foobar.py: -------------------------------------------------------------------------------- 1 | Suppose you are given the following code: 2 | 3 | class FooBar { 4 | public void foo() { 5 | for (int i = 0; i < n; i++) { 6 | print("foo"); 7 | } 8 | } 9 | 10 | public void bar() { 11 | for (int i = 0; i < n; i++) { 12 | print("bar"); 13 | } 14 | } 15 | } 16 | The same instance of FooBar will be passed to two different threads. Thread A will call foo() while thread B will call bar(). Modify the given program to output "foobar" n times. 17 | 18 | # ----------------------------------------------------------------------------------------------------- 19 | Use two locks for the threads to signal to each other when the other should run. bar_lock starts in a locked state because we always want foo to print first. 20 | 21 | import threading 22 | 23 | class FooBar: 24 | def __init__(self, n): 25 | self.n = n 26 | self.foo_lock = threading.Lock() 27 | self.bar_lock = threading.Lock() 28 | self.bar_lock.acquire() 29 | 30 | def foo(self, printFoo): 31 | for i in range(self.n): 32 | self.foo_lock.acquire() 33 | printFoo() 34 | self.bar_lock.release() 35 | 36 | def bar(self, printBar): 37 | for i in range(self.n): 38 | self.bar_lock.acquire() 39 | printBar() 40 | self.foo_lock.release() 41 | -------------------------------------------------------------------------------- /cp_template.md: -------------------------------------------------------------------------------- 1 | # Fast I/O 2 | from sys import stdin, stdout 3 | 4 | if __name__ == '__main__': 5 | first method : input_list = list(map(int, stdin.readline().strip().split())) or input_list = [x for x in stdin.readline().split()] 6 | second method : int(stdin.readline().strip()) 7 | 8 | first method = stdout.write(var) # always a string inside stdout 9 | second method = stdout.write(f"{var}\n") 10 | nested inputs list 11 | park = [] 12 | for i in range(n): 13 | park.append(list(map(int, stdin.readline().strip().split()))) 14 | 15 | third method : lines = stdin.readlines() # for more efficient I/O, load all input at once, and then loop over it. 16 | # use lambda for short hand functions ! 17 | # use in-built collections and itertools ! 18 | -------------------------------------------------------------------------------- /cs50/pset1.py: -------------------------------------------------------------------------------- 1 | def mario_block_right(n): 2 | for i in range(0, n): 3 | for j in range(0, n-i-1): # printing spaces 4 | print(" ", end = "") 5 | for k in range(0, i + 1): # printing stars 6 | print("*", end = "") 7 | print() 8 | 9 | def mario_block_left(n): 10 | for i in range(0, n): 11 | for k in range(0, i + 1): # printing stars 12 | print("*", end = "") 13 | print() 14 | 15 | 16 | def mario_tree(n): 17 | for i in range(0, n): 18 | for j in range(0, n-i-1): # printing spaces 19 | print(" ", end = "") 20 | for k in range(0, i + 1): # printing stars 21 | print("*", end = "") 22 | print(" ", end = "") 23 | for k in range(0, i + 1): # printing stars 24 | print("*", end = "") 25 | print() 26 | 27 | if __name__ == '__main__': 28 | while(1): 29 | height = int(input("Mario Tree Height: ")) 30 | if height <= 0: 31 | print("wrong value of height !") 32 | else: 33 | mario_tree(height) 34 | break 35 | -------------------------------------------------------------------------------- /cs50/pset2.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import random 3 | def cipher_text(text, key): 4 | cipher_text = "" 5 | text = text.upper() 6 | for i in range(len(text)): 7 | if text[i].isalpha(): 8 | cipher_text += chr(((ord(text[i]) + key - 65) % 26) + 65) 9 | else: 10 | cipher_text += text[i] 11 | print(f"secured text is : {cipher_text}") 12 | return text 13 | 14 | def decipher_text(text, key): 15 | decipher_text = "" 16 | for i in range(len(text)): 17 | decipher_text += chr(((ord(text[i]) - key - 65) % 26) + 65) 18 | print(f"decipher text is : {text}") 19 | 20 | if __name__ == '__main__': 21 | key = random.randint(-sys.maxsize-1, sys.maxsize) 22 | raw_text = input().strip() 23 | print(f"raw text is : {raw_text}") 24 | if len(raw_text): 25 | decipher_text(cipher_text(raw_text, key), key) 26 | else: 27 | print('Please enter something !') 28 | -------------------------------------------------------------------------------- /cs50/pset2_2.py: -------------------------------------------------------------------------------- 1 | import re 2 | import sys 3 | def cli_index(text): 4 | words = len(text.split()) 5 | letters = 0 6 | for i in text: 7 | if i.isalpha() or i.isdigit(): 8 | letters += 1 9 | sentences = len(re.findall('\w+[^?!.][?!.]', text)) 10 | print(f"your text contains {words} words, {letters} letters and {sentences} sentences") 11 | L = (letters / words) * 100 12 | S = (sentences / words) * 100 13 | return round(0.0588 * L - 0.296 * S - 15.8) 14 | if __name__ == '__main__': 15 | text = input().strip() 16 | result = cli_index(text) 17 | if result > 16: 18 | print(f"Coleman–Liau index for given text: Grade 16+") 19 | elif result < 1: 20 | print(f"Coleman–Liau index for given text: Before Grade 1") 21 | else: 22 | print(f"Coleman–Liau index for given text: Grade {result}") 23 | -------------------------------------------------------------------------------- /custom data structures/conversion_data_structure_units.py: -------------------------------------------------------------------------------- 1 | from sys import stdin, stdout 2 | class Units_conversion: 3 | def __init__(self): 4 | self.units = {'INR':0.014, 'YEN':0.0092, 'USD':1, 'GBP':1.28} 5 | def convert(self, src_unit, dst_unit, value): 6 | if src_unit in self.units: 7 | temp_usd = self.units[src_unit] * value 8 | return temp_usd / self.units[dst_unit] 9 | def add_unit(self, new_unit, new_conversion_rate): 10 | self.units[new_unit] = new_conversion_rate 11 | 12 | if __name__ == '__main__': 13 | print('WELCOME TO CURRENCY CONVERSION PORTAL !') 14 | print('write your source currency, target currency and your value for conversion - ') 15 | l = list(stdin.readline().strip().split()) 16 | unit = Units_conversion() 17 | print(f'converted value is {unit.convert(l[0], l[1], int(l[2]))}') 18 | -------------------------------------------------------------------------------- /divide and conquer/DAC_patterns.MD: -------------------------------------------------------------------------------- 1 | ## The overall approach should be if we can divide the search space recursively and compute the solution from left half 2 | ## side, compute the solution from right half space and finally combine all solutions and return it. 3 | # General template : 4 | 5 | ``` 6 | DAC(a, i, j){ 7 | 8 | if small_enough_to_be_solved(a, i, j){ 9 | return solution(a, i, j) 10 | } 11 | else{ 12 | m = divide(a, i, j) 13 | b = DAC(a, i, mid) 14 | c = DAC(a, mid + 1, j) 15 | d = combine(b, c) 16 | } 17 | return d 18 | } 19 | ``` 20 | -------------------------------------------------------------------------------- /divide and conquer/merge_sort.py: -------------------------------------------------------------------------------- 1 | def Merge(arr, l, m, r): 2 | arr1 = arr[l:m] 3 | arr2 = arr[m:r] 4 | 5 | arr1_size = len(arr1) 6 | arr2_size = len(arr2) 7 | if arr1_size == 0: 8 | return 9 | if arr2_size == 0: 10 | return 11 | # looping from back of second array 12 | for i in range(arr2_size - 1, -1, -1): 13 | # setting pointer for first array 14 | j = arr1_size - 2 15 | # saving the last element 16 | last = arr1[arr1_size - 1] 17 | # looping over the first array and shifting the elements 18 | while j >= 0 and arr2[i] < arr1[j]: 19 | arr1[j + 1] = arr1[j] 20 | j -= 1 21 | 22 | # now, the position is found, then just putting elements in appropriate positions 23 | if j != arr1_size - 2 and last > arr2[i]: 24 | arr1[j + 1] = arr2[i] 25 | # everytime last will change, as arr1 is modified 26 | arr2[i] = last 27 | 28 | 29 | def MergeSort(arr, l, r): 30 | if l < r: 31 | m = l + (r - l) // 2 32 | MergeSort(arr, l, m) 33 | MergeSort(arr, m + 1, r) 34 | 35 | Merge(arr, l, m, r) 36 | 37 | if __name__ == '__main__': 38 | arr = [1, 5, 9, 10, 15, 20, 2, 3, 8, 13] 39 | MergeSort(arr, 0, len(arr) - 1) 40 | print(arr) 41 | -------------------------------------------------------------------------------- /divide and conquer/product_integers_karatsuba_algo.py: -------------------------------------------------------------------------------- 1 | # Program for fast multiplication using karatsuba algorithm - divide and conquer approach 2 | # We keep dividing the numbers and solve the product recursively by making total 3 recursive calls to compute exact three products namely ac , bd, (a+b)(c+d), 3 | # for a given number X and Y divided as a, b and c, d respectively. 4 | # Any number can be written as X : (10^n)*ac + (10^(n/2))*(a + b)(c + d) + bd 5 | # TIME : 0(n^1.58) which is better than traditional algorithm ((0(N^2))) 6 | 7 | 8 | # Here, taking inputs in string would be a good idea but not a great idea ! 9 | import math 10 | 11 | # IMplement this split method optimized way such that it splits the number at given index 12 | # def split_at(n, mid): 13 | # temp = str(n) 14 | # print(temp, mid) 15 | # return int(temp[:mid]), int(temp[mid:]) 16 | 17 | def karatsuba(x, y): 18 | if x < 10 or y < 10: 19 | return x * y 20 | 21 | m = min(int(math.log10(x) + 1), int(math.log10(y) + 1)) 22 | mid = math.floor(m/2) 23 | 24 | a, b = split_at(x, mid) 25 | c, d = split_at(y, mid) 26 | 27 | z0 = karatsuba(b, d) 28 | z1 = karatsuba((a + b), (c + d)) 29 | z2 = karatsuba(a, c) 30 | 31 | return z2 * pow(10, mid * 2) + (z1 - z2 - z0) * pow(10, mid) + z0 32 | 33 | if __name__ == '__main__': 34 | x, y = 12345, 67899 35 | print(split_at(x, y)) 36 | -------------------------------------------------------------------------------- /divide and conquer/quick_sort.py: -------------------------------------------------------------------------------- 1 | # implementing randomized version of QuickSort 2 | import random 3 | def partition(arr,low,high): 4 | i = low - 1 5 | pivot = arr[high] 6 | for j in range(low , high): 7 | if arr[j] < pivot: 8 | i = i + 1 9 | arr[i], arr[j] = arr[j], arr[i] 10 | 11 | arr[i + 1], arr[high] = arr[high], arr[i + 1] 12 | return i + 1 13 | 14 | def partitionr(arr, low, high): 15 | randpivot = random.randrange(low, high) 16 | arr[low], arr[randpivot] = arr[randpivot], arr[low] 17 | return partition(arr, low, high) 18 | 19 | def quicksort(arr, low, high): 20 | if low < high: 21 | pi = partitionr(arr, low, high) 22 | print(pi) 23 | print("going for left sub array") 24 | quicksort(arr, low, pi - 1) 25 | print("going for right sub array") 26 | quicksort(arr, pi + 1, high) 27 | 28 | if __name__ == '__main__': 29 | arr = [10, 7, 8, 9, 1, 5] 30 | quicksort(arr, 0, 5) 31 | print(arr) 32 | -------------------------------------------------------------------------------- /dynamic programming/count_construct_target_string.py: -------------------------------------------------------------------------------- 1 | # Program for counting the construction of the target string according to given array of strings. 2 | # EX. 3 | # Input : A = ["adc", "aec", "erg"], S = "ac" 4 | # Output : 4 5 | # Target string can be formed in following ways : 6 | # 1) 1st character of "adc" and the 3rd character of "adc". 7 | # 2) 1st character of "adc" and the 3rd character of "aec". 8 | # 3) 1st character of "aec" and the 3rd character of "adc". 9 | # 4) 1st character of "aec" and the 3rd character of "aec". 10 | # 11 | # Input : A = ["afsdc", "aeeeedc", "ddegerg"], S = "ae" 12 | # Output : 12 13 | # --------------------------------------------------------------------------------------------------------- 14 | 15 | cache = {} 16 | mod = 10000007 17 | def calculate(pos, prev, s, index): 18 | 19 | if pos == len(s): 20 | return 1 21 | 22 | if (pos, prev) in cache: 23 | return cache[(pos, prev)] 24 | 25 | answer = 0 26 | for i in range(len(index)): 27 | if index[i] > prev: 28 | answer = answer + calculate(pos + 1, index[i], s, index) 29 | 30 | cache[(pos, prev)] = answer 31 | 32 | return cache[(pos, prev)] 33 | 34 | 35 | def count_target_string(arr, target): 36 | 37 | index = [[] for _ in range(26)] 38 | 39 | for i in range(len(arr)): 40 | for j in range(len(arr[i])): 41 | index[ord(arr[i][j]) - ord('a')].append(j + 1) 42 | 43 | return calculate(0, 0, target, index[0]) 44 | 45 | 46 | print(count_target_string(['abc', 'aec', 'erg'], "ac")) 47 | -------------------------------------------------------------------------------- /dynamic programming/count_form_target_string.py: -------------------------------------------------------------------------------- 1 | # Program to count all the possible number of ways to construct the target string using characters from strings 2 | # in the given array such that indices of the chars used in string construction forma strictly increasing sequence. 3 | # Multiple chars can also be sued from the same string. 4 | # Input : A = ["adc", "aec", "erg"], S = "ac" 5 | # Output : 4 6 | # Target string can be formed in following ways : 7 | # 1) 1st character of "adc" and the 3rd character of "adc". 8 | # 2) 1st character of "adc" and the 3rd character of "aec". 9 | # 3) 1st character of "aec" and the 3rd character of "adc". 10 | # 4) 1st character of "aec" and the 3rd character of "aec". 11 | # Input : A = ["afsdc", "aeeeedc", "ddegerg"], S = "ae" 12 | # Output : 12 13 | # ------------------------------------------------------------------------------------------------------- 14 | -------------------------------------------------------------------------------- /dynamic programming/count_subset_sum_diff.py: -------------------------------------------------------------------------------- 1 | # program for counting subset sum with given difference as input for the given array (set). 2 | # We need to count all such possible subsets such that difference between the sums is the given difference. 3 | # S 4 | # / \ 5 | # S1 S2 6 | # Now, given s1 - s2 = diff (1) 7 | # also, we know, 8 | # s1 + s2 = sum(arr) (2) 9 | # 10 | # adding 1 and 2, 11 | # we get, s1 = (diff + sum(arr)) / 2 where, we know diff and sum(arr), so we get s1 (sum of first subset). 12 | # Now, this problem reduces to counting all such subsets which has sum s1 which has already been discussed. 13 | -------------------------------------------------------------------------------- /dynamic programming/count_target_sums.py: -------------------------------------------------------------------------------- 1 | # Program for computing the count of all such possible assignments of "+" or "-" operators infront of every element 2 | # such that evaluation leads to target sum. 3 | # EX. 4 | # [1, 1, 2, 3], target_sum = 1 5 | # [+, -, -, +] => 1 (one such possible assignment) 6 | # basically, this problem is based on one of the main observation is that if we think in terms of sets partition : 7 | # 8 | # [1, 1, 2, 3] 9 | # / \ 10 | # [+1, +3] [-1, -2] 11 | # [4] - [3] => 1 12 | # 13 | # Now, we can think of this problem the same way as we thought of the count of subset sum difference problem, 14 | # as the problem will be now reduced to that problem. 15 | # Now, this problem is simply exactly the same as count of subset diff problem where input for difference is now the given evaluation value (here =1) 16 | -------------------------------------------------------------------------------- /dynamic programming/max_path_sum_leaf_to_leaf.py: -------------------------------------------------------------------------------- 1 | # Program to compute maximum path sum from any leaf node to any leaf node in the given binary tree. 2 | # Logic is to approach using DP => 3 | # TIME : 0(N) 4 | 5 | # Use same code as for max_path_sum for any node to any node : 6 | # With some minor variation : 7 | 8 | temp = max(left, right) + root.data 9 | 10 | if temp.left == None and temp.right == None: 11 | temp = max(temp, root.data) 12 | 13 | ans = max(temp, left + right + root.data) 14 | 15 | res = max(ans, temp) 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /dynamic programming/max_path_sum_node_to_node.py: -------------------------------------------------------------------------------- 1 | # Program for computing max-path sum from any node to any node for a given binary tree. 2 | # Logic is to solve using DP approach => 3 | # TIME : 0(N) 4 | 5 | 6 | 7 | # Definition for a binary tree node. 8 | # class TreeNode: 9 | # def __init__(self, val=0, left=None, right=None): 10 | # self.val = val 11 | # self.left = left 12 | # self.right = right 13 | import sys 14 | class Solution: 15 | 16 | def max_path(self, root, res): 17 | 18 | if root == None: 19 | return 0 20 | 21 | left = self.max_path(root.left, res) 22 | right = self.max_path(root.right, res) 23 | 24 | temp = max(max(left, right) + root.val, root.val) 25 | ans = max(temp, left + right + root.val) 26 | 27 | res[0] = max(res[0], ans) 28 | 29 | return temp 30 | 31 | 32 | def maxPathSum(self, root: TreeNode) -> int: 33 | 34 | res = [-sys.maxsize-1] 35 | self.max_path(root, res) 36 | return res[0] 37 | -------------------------------------------------------------------------------- /dynamic programming/maximum_sum_increasing_subsequence.py: -------------------------------------------------------------------------------- 1 | # Program to compute maximum sum of increasing subsequence. 2 | # This is very similar and a variation for Longest increasing subsequence (lis) problem as we need increasing subsequence 3 | # but just a slight difference is that instead of length, we need the sum. 4 | # so, just the criteria will be changed and filling of table will remain same. 5 | # -------------------------------------------------------------------------------------------------------------- 6 | # TIME : 0(N^2) 7 | 8 | # Dynamic Programming bsed Python 9 | # implementation of Maximum Sum 10 | # Increasing Subsequence (MSIS) 11 | # problem 12 | 13 | # maxSumIS() returns the maximum 14 | # sum of increasing subsequence 15 | # in arr[] of size n 16 | def maxSumIS(arr, n): 17 | res = 0 18 | msis = [0 for x in range(n)] 19 | 20 | # Initialize msis values 21 | # for all indexes 22 | for i in range(n): 23 | msis[i] = arr[i] 24 | 25 | # Compute maximum sum 26 | # values in bottom up manner 27 | for i in range(1, n): 28 | for j in range(i): 29 | if (arr[i] > arr[j]): 30 | msis[i] = max(msis[i], msis[j] + arr[i]) 31 | 32 | # Pick maximum of 33 | # all msis values 34 | for i in range(n): 35 | if res < msis[i]: 36 | res = msis[i] 37 | 38 | return res 39 | 40 | # Driver Code 41 | arr = [1, 101, 2, 3, 100, 4, 5] 42 | n = len(arr) 43 | print("Sum of maximum sum increasing " + 44 | "subsequence is " + 45 | str(maxSumIS(arr, n))) 46 | -------------------------------------------------------------------------------- /dynamic programming/minimum_cost_climb.py: -------------------------------------------------------------------------------- 1 | # Program for computing the minimum cost for climbing the stairs to the highest building. 2 | # We can either start from first and second index at every time. 3 | # EX. 4 | # INPUT : [16, 19, 10, 12, 18] 5 | # OUTPUT : 31 6 | # 19 -> 12 7 | # INPUT : [2, 5, 3, 1, 7, 3, 4] 8 | # OUTPUT : 9 9 | # 2-> 3-> 1->3 10 | # --------------------------------------------------------------------------------------------------------------------------- 11 | # We use DP to solve this. 12 | # At every state, we store the subproblem of finding the minimum cost to reach ith step. 13 | # We can clearly see the optimal substructure and overlapping subproblems. 14 | 15 | def compute_cost(cost, n): 16 | 17 | dp = [None] * n 18 | if n == 1: 19 | return cost[0] 20 | 21 | dp[0], dp[1] = cost[0], cost[1] 22 | 23 | for i in range(2, n): 24 | dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i] 25 | 26 | print(dp) 27 | return min(dp[n - 2], dp[n - 1]) 28 | 29 | 30 | a = [16, 19, 10, 12, 18 ] 31 | n = len(a) 32 | print(compute_cost(a, n)) 33 | -------------------------------------------------------------------------------- /dynamic programming/minimum_insertions_palindrome.py: -------------------------------------------------------------------------------- 1 | # Program for minimum insertions to make a string palindrome. 2 | # Now, if we carefully observe then, when we delete some chars to transform the string into palindrome. 3 | # Then, we get LPS. 4 | # Now, if we again want to make it palindrome, then we need to insert those same chars that we deleted. 5 | # So, crux of this question is as below : 6 | # TOTAL insertions = total deletions (which can eventually be solved using LPS) 7 | # EX. 8 | # abcd: Number of insertions required is 3 i.e. dcbabcd 9 | # If we see, then we need to delete 3 chars in order to get lps (here, "a") 10 | # which is also equal to number of insertions required. 11 | # TIME : 0(N ^ 2) 12 | -------------------------------------------------------------------------------- /dynamic programming/number_of_ways_coins.py: -------------------------------------------------------------------------------- 1 | # Program for number of ways to make up to some value N. 2 | # Dp state[value]i, where this value is sum upto that index value. 3 | # So, we can use 1d array of size (N + 1) 4 | # ----------------------------------------------------------------------------------------------- 5 | # TIME : 0(N * m) 6 | 7 | def count_ways(arr, m, N): 8 | 9 | count = [0 for _ in range(N + 1)] 10 | 11 | count[0] = 1 12 | 13 | for i in range(1, N + 1): 14 | for j in range(m): 15 | # if we accumulate ways i, then compute for other remaining value 'i - arr[j]' 16 | if arr[j] <= i: 17 | count[i] += count[i - arr[j]] 18 | 19 | return count[N] 20 | 21 | arr = [1, 5, 6] 22 | m = len(arr) 23 | N = 7 24 | print("Total number of ways = ", count_ways(arr, m, N)) 25 | -------------------------------------------------------------------------------- /dynamic programming/print_knapsack.py: -------------------------------------------------------------------------------- 1 | # Program to print those items which are included in the knapsack in the knapsack problem. 2 | # Below knapsack is function which gives the dp table formed after applying the algorithm. 3 | # We simply backtrack using the last index (the answer cell) till top left to form the answer in the way using two pointers row, col denoted by i, j. 4 | # ------------------------------------------------------------------------------------------------------------------------------------------------------- 5 | 6 | def print_knapsack(W, wt, val, n): 7 | 8 | dp = knapsack(W, wt, val, n) 9 | for i in range(len(dp)): 10 | for j in range(len(dp[0])): 11 | print(dp[i][j], end = " ") 12 | print() 13 | 14 | i, j = n, W 15 | res = [] 16 | while i > 0 and j > 0: 17 | 18 | if dp[i][j] == dp[i - 1][j]: 19 | i -= 1 20 | else: 21 | res.append(i) 22 | j -= dp[i][j] - dp[i - 1][j] 23 | i -= 1 24 | 25 | 26 | print(f"items selected : {res[::-1]}") 27 | 28 | -------------------------------------------------------------------------------- /file _handling_threading_requests_practice/file practice2.py: -------------------------------------------------------------------------------- 1 | # tell(), seek() will allow us to handle file pointers. 2 | # seek(offset, whence) method by default defines offset and whence as 1. 3 | # whence : 0 => from beginning of file 4 | # whence : 1 => from crrent pos 5 | # whencee : 2 => from end of file 6 | # so offset can be +, - 7 | # but this whence is only defined for "0" for normal files 8 | # but other values of whence for seeks is defined for byte modes operation. 9 | 10 | with open("C:/Users/DELL/Desktop/output.txt", 'ab') as file: 11 | print(file.tell()) 12 | file.seek(-5, 2) 13 | file.write(b'0123456789abcdef') 14 | -------------------------------------------------------------------------------- /file _handling_threading_requests_practice/request_practice.py: -------------------------------------------------------------------------------- 1 | import requests 2 | # GET REQUEST 3 | 4 | r = requests.get('https://stackoverflow.com/questions/49191883/get-number-of-bytes-during-a-request', headers={'User-Agent': 'Mozilla/5.0'}) 5 | 6 | 7 | print(r.status_code) 8 | print(r.headers['content-type']) 9 | print(r.headers) 10 | # print(r.headers['content-length']) 11 | print(len(r.content)) 12 | print(r.headers['transfer-encoding']) 13 | print(r.content.json) 14 | # if using HTTP basic auth : 15 | # pass in the get method : auth=('user', 'pass') 16 | # requests.get('https://api.github.com/user', auth=('user', 'pass')) 17 | 18 | 19 | # POST REQUEST 20 | # payload = {'key1': 'value1', 'key2': 'value2'} 21 | # >>> r = requests.post("http://httpbin.org/post", data=payload) 22 | 23 | # FOR SENDING FILES, 24 | # url = 'http://httpbin.org/post' 25 | # files = {'report.xls': open('report.xls', 'rb')} 26 | # r = requests.post(url, files=files) 27 | -------------------------------------------------------------------------------- /graphs/BFS_matrix.py: -------------------------------------------------------------------------------- 1 | # BFS traversal for matrix representation of graph 2 | from collections import deque 3 | 4 | def BFS(matrix, source): 5 | 6 | queue = deque([]) 7 | visited = set() 8 | 9 | queue.append(source) 10 | visited.add(source) 11 | 12 | while queue: 13 | curr = queue.popleft() 14 | print(curr, end = " ") 15 | 16 | for j in range(len(matrix)): 17 | if matrix[curr][j] and j not in visited: 18 | queue.append(j) 19 | visited.add(j) 20 | 21 | 22 | import random 23 | if __name__ == '__main__': 24 | N = 4 25 | # matrix = [[random.randint(0, 1)] * N for _ in range(N)] 26 | matrix = [[0, 1, 0, 0], 27 | [0, 0, 1, 0], 28 | [1, 0, 0, 1], 29 | [0, 0, 0, 1]] 30 | 31 | print(matrix) 32 | BFS(matrix, 1) 33 | -------------------------------------------------------------------------------- /graphs/DFS_matrix.py: -------------------------------------------------------------------------------- 1 | # DFS traversal for matrix representation of graph 2 | from collections import deque 3 | 4 | def DFSUtil(matrix, source, visited): 5 | 6 | visited.add(source) 7 | print(source, end = " ") 8 | 9 | for j in range(len(matrix)): 10 | if matrix[source][j] and j not in visited: 11 | DFSUtil(matrix, j, visited) 12 | 13 | def DFS(matrix, start): 14 | visited = set() 15 | DFSUtil(matrix, start, visited) 16 | 17 | import random 18 | if __name__ == '__main__': 19 | N = 4 20 | # matrix = [[random.randint(0, 1)] * N for _ in range(N)] 21 | matrix = [[0, 1, 0, 1], 22 | [0, 1, 1, 1], 23 | [1, 1, 0, 1], 24 | [1, 0, 0, 1]] 25 | 26 | print(matrix) 27 | DFS(matrix, 2) 28 | -------------------------------------------------------------------------------- /graphs/Number_of_islands.py: -------------------------------------------------------------------------------- 1 | # Finding the number of islands through DFS approach. 2 | 3 | class Solution: 4 | def dfs(self, mat, i, j): 5 | rows, cols = len(mat), len(mat[0]) 6 | if i < 0 or j < 0 or i >= rows or j >= cols or mat[i][j] == '0': 7 | return 0 8 | 9 | mat[i][j] = '0' 10 | self.dfs(mat, i + 1, j) 11 | self.dfs(mat, i - 1, j) 12 | self.dfs(mat, i, j + 1) 13 | self.dfs(mat, i, j - 1) 14 | 15 | return 1 16 | 17 | def numIslands(self, grid: List[List[str]]) -> int: 18 | if grid == None or len(grid) == 0: 19 | return 0 20 | num = 0 21 | for i in range(len(grid)): 22 | for j in range(len(grid[i])): 23 | if grid[i][j] == '1': 24 | num += self.dfs(grid, i, j) 25 | return num 26 | 27 | if __name__ == '__main__': 28 | mat = [[1, 1, 0, 0, 0], 29 | [0, 1, 0, 0, 1], 30 | [1, 0, 0, 1, 1], 31 | [0, 0, 0, 0, 0], 32 | [1, 0, 1, 0, 1]] 33 | print(num_islands(mat, 5)) 34 | -------------------------------------------------------------------------------- /graphs/Sum_of_degree_nodes.py: -------------------------------------------------------------------------------- 1 | # Program for sum of degree of nodes in the graph. 2 | # We know that if there are no edges in the graph, then sum = 0 3 | # Now, if we add one more edge, then we increase the degree by 1 for each node. 4 | # Hence, total sum increases by 2. 5 | # Thus, total sum of degrees = 2 * edges 6 | 7 | from collections import defaultdict as dd 8 | class Graph: 9 | 10 | def __init__(self, size): 11 | self.V = size 12 | self.graph = dd(list) 13 | 14 | def add_Edge(self, u, v): 15 | self.graph[u].append(v) 16 | self.graph[v].append(u) 17 | 18 | def printGraph(self): 19 | for i in range(self.V): 20 | print(i, end = "->") 21 | for j in self.graph[i]: 22 | print(j, end = " ") 23 | print() 24 | 25 | if __name__ == '__main__': 26 | 27 | graph = Graph(12) 28 | graph.add_Edge(0, 9) 29 | graph.add_Edge(9, 8) 30 | graph.add_Edge(8, 1) 31 | graph.add_Edge(8, 7) 32 | graph.add_Edge(7, 10) 33 | graph.add_Edge(7, 3) 34 | graph.add_Edge(10, 11) 35 | graph.add_Edge(11, 7) 36 | graph.add_Edge(3, 2) 37 | graph.add_Edge(3, 4) 38 | graph.add_Edge(3, 5) 39 | graph.add_Edge(5, 6) 40 | graph.add_Edge(6, 7) 41 | graph.printGraph() 42 | -------------------------------------------------------------------------------- /graphs/clone_undirected_graph.py: -------------------------------------------------------------------------------- 1 | # Like we have done in singly linked list copying with random pointers, likewise we are going to use hashmaps to apply the 2 | # same algorithm. 3 | # Traversing BFS + HashMap to indicate which nodes have been visited/cloned and also to keep mark of all 4 | # adjacent nodes/neighbours in the adjacency list. 5 | # TIME : 0(E + V) 6 | 7 | 8 | """ 9 | # Definition for a Node. 10 | class Node: 11 | def __init__(self, val = 0, neighbors = None): 12 | self.val = val 13 | self.neighbors = neighbors if neighbors is not None else [] 14 | """ 15 | from collections import deque 16 | class Solution: 17 | def cloneGraph(self, node: 'Node') -> 'Node': 18 | 19 | queue = deque() 20 | map = {} 21 | 22 | if not node: 23 | return node 24 | 25 | queue.append(node) 26 | new_src = Node(node.val) 27 | map[node] = new_src 28 | 29 | while queue: 30 | 31 | curr = queue.popleft() 32 | 33 | for i in curr.neighbors: 34 | 35 | if i not in map: 36 | new_node = Node(i.val) 37 | map[i] = new_node 38 | queue.append(i) 39 | 40 | map[curr].neighbors.append(map[i]) 41 | 42 | return map[node] 43 | -------------------------------------------------------------------------------- /graphs/count_all_cycles_graph.py: -------------------------------------------------------------------------------- 1 | # Given an undirected graph consisting of N vertices numbered [0, N-1] and E edges, the task is to count the number of cycles such that any 2 | # subset of vertices of a cycle does not form another cycle. 3 | # Since V vertices require V edges to form 1 cycle, these the number of required cycles can be expressed using the formula: (Edges - Vertices) + 1 4 | -------------------------------------------------------------------------------- /graphs/count_degree_cycle_graphs.py: -------------------------------------------------------------------------------- 1 | # Given the number of vertices in a Cycle Graph. The task is to find the Degree and the number of Edges of the cycle graph. 2 | # Degree: Degree of any vertex is defined as the number of edge Incident on it. 3 | # Cycle Graph: In graph theory, a graph that consists of single cycle is called a cycle graph or circular graph. 4 | # The cycle graph with n vertices is called Cn. 5 | # It is a Connected Graph. 6 | # A Cycle Graph or Circular Graph is a graph that consists of a single cycle. 7 | # In a Cycle Graph number of vertices is equal to number of edges. 8 | # A Cycle Graph is 2-edge colorable or 2-vertex colorable, if and only if it has an even number of vertices. 9 | # A Cycle Graph is 3-edge colorable or 3-edge colorable, if and only if it has an odd number of vertices. 10 | # In a Cycle Graph, Degree of each vertx in a graph is two. 11 | # The degree of a Cycle graph is 2 times the number of vertices. As each edge is counted twice. 12 | -------------------------------------------------------------------------------- /graphs/find_diameter_cycles_edges.py: -------------------------------------------------------------------------------- 1 | # Program to find the diameter, cycles and edges of a Wheel Graph 2 | # Wheel Graph: A Wheel graph is a graph formed by connecting a single universal vertex to all vertices of a cycle. Properties:- 3 | 4 | # Wheel graphs are Planar graphs. 5 | # There is always a Hamiltonian cycle in the Wheel graph. 6 | # Chromatic Number is 3 and 4, if n is odd and even respectively. 7 | 8 | # Problem Statement: Given the Number of Vertices in a Wheel Graph. The task is to find: 9 | # 10 | # The Number of Cycles in the Wheel Graph. 11 | # Number of edges in Wheel Graph. 12 | # The diameter of a Wheel Graph. 13 | Number of Cycle = (vertices * vertices) - (3 * vertices) + 3 14 | Number of edge = 2 * (vertices - 1) 15 | Diameter = if vertices = 4, Diameter = 1 16 | if vertices > 4, Diameter = 2 17 | -------------------------------------------------------------------------------- /graphs/graphs_weighted.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict as dd 2 | class Graph: 3 | 4 | def __init__(self, size): 5 | self.V = size 6 | self.graph = dd(list) 7 | 8 | def add_Edge(self, u, v, w): 9 | self.graph[u].append((v, w)) 10 | 11 | def printGraph(self): 12 | for i in range(self.V): 13 | print(i, end = "->") 14 | for j in self.graph[i]: 15 | print(j, end = " ") 16 | print() 17 | 18 | if __name__ == '__main__': 19 | 20 | graph = Graph(12) 21 | graph.add_Edge(0, 9, 1) 22 | graph.add_Edge(9, 8, 2) 23 | graph.add_Edge(8, 1, 3) 24 | graph.add_Edge(8, 7, 4) 25 | graph.add_Edge(7, 10, 5) 26 | graph.add_Edge(7, 3, 6) 27 | graph.add_Edge(10, 11, 7) 28 | graph.add_Edge(11, 7, 8) 29 | graph.add_Edge(3, 2, 9) 30 | graph.add_Edge(3, 4, 10) 31 | graph.add_Edge(3, 5, 11) 32 | graph.add_Edge(5, 6, 12) 33 | graph.add_Edge(6, 7, 12) 34 | graph.printGraph() 35 | -------------------------------------------------------------------------------- /graphs/solving_maze_DFS.py: -------------------------------------------------------------------------------- 1 | # DFS traversal for MAZE representation of graph 2 | # here, we use matrix representation 3 | 4 | from collections import deque 5 | 6 | def DFSUtil(matrix, source, visited, goal): 7 | 8 | visited.add(source) 9 | if source == goal: 10 | print("GOAL NODE REACHED !") 11 | return 12 | 13 | for j in range(len(matrix)): 14 | if matrix[source][j] and j not in visited: 15 | DFSUtil(matrix, j, visited, goal) 16 | 17 | def DFS(matrix, start, goal): 18 | visited = set() 19 | DFSUtil(matrix, start, visited, goal) 20 | 21 | def pretty_print(matrix): 22 | for i in range(len(matrix)): 23 | for j in range(len(matrix)): 24 | print(matrix[i][j], end = " ") 25 | print() 26 | 27 | import random 28 | if __name__ == '__main__': 29 | N = 6 30 | # matrix = [[random.randint(0, 1) for _ in range(N)] for _ in range(N)] 31 | matrix = [[1, 1, 1, 1, 1, 1], 32 | [0, 0, 1, 0, 1, 0], 33 | [0, 0, 1, 0, 0, 0], 34 | [0, 1, 0, 1, 1, 1], 35 | [1, 1, 0, 0, 0, 0], 36 | [1, 0, 0, 0, 1, 1]] 37 | pretty_print(matrix) 38 | DFS(matrix, 2, 2) 39 | -------------------------------------------------------------------------------- /greedy/denominations.py: -------------------------------------------------------------------------------- 1 | # Given denominations : [1, 2, 5, 10, 20, 50, 100, 500, 1000] 2 | # We need to minimize the number of coins possible to make upto some value V. 3 | # ------------------------------------------------------------------------------ 4 | # From our intuition, we can apply our greedy approach for firstly choosing 5 | # coins of maximum denominations as much possible. 6 | # But this algorithm only works for indian denominations for specific cases. 7 | # For general denominations, we need to use DP. 8 | # ------------------------------------------------------------------------------ 9 | # TIME : 0(N*log(N)) 10 | # ------------------------------------------------------------------------------ 11 | 12 | def compute_min_den(V): 13 | 14 | 15 | deno = [1, 2, 5, 10, 20, 50, 100, 500, 1000] 16 | ans = [] 17 | for i in range(8, -1, -1): 18 | 19 | while V >= deno[i]: 20 | V -= deno[i] 21 | ans.append(deno[i]) 22 | 23 | print(ans) 24 | 25 | if __name__ == '__main__': 26 | n = 93 27 | print(compute_min_den(n)) 28 | -------------------------------------------------------------------------------- /greedy/find_rooms_for_meeting.py: -------------------------------------------------------------------------------- 1 | # 1. Program for computing required rooms for conducting meetings such that no two meetings overlap over each other and also each room can conduct only one meeting at a time. 2 | 3 | # meeting time intervals = [[0, 30],[5, 10],[15, 20]] 4 | 5 | # after sorting on finish : [[5, 10], [15, 20], [0, 30]] 6 | 7 | # => [0, 5, 15] 8 | # => [10, 20, 30] 9 | 10 | # rooms = 2 11 | 12 | # TIME : 0(N* Log(N)), SPACE : 0(N) 13 | 14 | def compute_conf_room(arr): 15 | 16 | start = [x[0] for x in arr] 17 | end = [x[1] for x in arr] 18 | 19 | start.sort() 20 | end.sort() 21 | rooms, end_i = 0, 0 22 | for i in range(len(arr)): 23 | if start[i] < end[end_i]: 24 | rooms += 1 25 | else: 26 | end_i += 1 27 | 28 | return rooms 29 | 30 | # # 2. Program for computing if given number of queries can be accomodated in the given calendar days and given number of rooms. 31 | # Input: calendar = [[1, 2], [4, 5], [8, 10]], rooms = 1, queries = [[2, 3], [3, 4]] 32 | # Output: [true, true] 33 | 34 | calendar = [[1, 2], [4, 5], [8, 10]] 35 | res = [None] * len(calendar) 36 | for i in range(len(calendar)): 37 | calendar.append(queries[i]) 38 | if compute_conf_room(arr) <= rooms: 39 | res[i] = True 40 | calendar.remove(len(calendar) - 1) 41 | 42 | return res 43 | 44 | 45 | if __name__ == '__main__': 46 | 47 | arr = [[0, 30],[5, 10],[15, 20]] 48 | print(compute_conf_room(arr)) 49 | -------------------------------------------------------------------------------- /greedy/fractional_knapsack.py: -------------------------------------------------------------------------------- 1 | # The problem statement says that we have to basically maximize the profit collected in the knapsack of 2 | # weight of atmost W, given some array containing items weights and its values. 3 | # Array : {{60, 10}, {100, 20}, {120, 30}} 4 | # (value, weight) pairs. 5 | # ----------------------------------------------------------------------------- 6 | # Actually, we can't be able to actually get any kind of greedy approach based on just weights, values 7 | # so, we can get the ratio: value/weight for the items and considering constraints, we can get the items in the knapsack, 8 | # and if at any point , we can't pack more items, then we can simply compute fraction of items and get the remaining 9 | # knapsack filled till W. 10 | # ----------------------------------------------------------------------------- 11 | # TIME : 0(N*log(N)) 12 | 13 | def compute_knapsack(arr, n, W): 14 | 15 | arr.sort(key=lambda x : x[0]/x[1], reverse=True) 16 | curr_w = 0 17 | result = 0 18 | for i in range(n): 19 | 20 | if curr_w + arr[i][1] <= W: 21 | curr_w += arr[i][1] 22 | result += arr[i][0] 23 | 24 | else: 25 | remain = W - curr_w 26 | result += arr[i][0] * (remain / arr[i][1]) 27 | break 28 | 29 | print(result) 30 | 31 | if __name__ == '__main__': 32 | 33 | arr = [[60, 10], [100, 20], [120, 30]] 34 | compute_knapsack(arr, 3, 50) 35 | -------------------------------------------------------------------------------- /greedy/huffman_coding.py: -------------------------------------------------------------------------------- 1 | # Program for huffman coding algorithm using greedy approach 2 | # We have been given arr containing data, now frequency given associated with data 3 | # and size is total length of array. 4 | # TIME : 0(N*log(N)) 5 | # NOTE : given array is not sorted. 6 | # If array is sorted, then we can do this in linear time (can get away with heaps !) 7 | 8 | from heapq import heapify, heappush, heappop 9 | class MinHeapNode: 10 | 11 | def __init__(self, val, freq): 12 | self.data = val 13 | self.freq = freq 14 | self.left = None 15 | self.right = None 16 | 17 | def build_huffman_tree(arr, freq, size): 18 | 19 | # build all the unique data and push into min heap 20 | heap = [] 21 | for i in range(size): 22 | heappush(heap, (freq[i], MinHeapNode(arr[i], freq[i]))) 23 | 24 | # for all heap, in the tuple : Ist index : freq, 2nd index : node address 25 | while len(heap) > 1: 26 | 27 | 28 | left = heappop(heap) 29 | 30 | right = heappop(heap) 31 | 32 | new_root = MinHeapNode(left[0] + right[0], '$') 33 | 34 | new_root.left = left[0] 35 | new_root.right = right[0] 36 | 37 | heappush(heap, (left[0] + right[0], new_root)) 38 | 39 | print(heap) 40 | 41 | 42 | if __name__ == '__main__': 43 | arr = ['a', 'b', 'c', 'd', 'e', 'f'] 44 | freq = [5, 9, 12, 13, 16, 45] 45 | size = 6 46 | build_huffman_tree(arr, freq, size) 47 | -------------------------------------------------------------------------------- /greedy/meeting_time_intervals.py: -------------------------------------------------------------------------------- 1 | # Given an array of meeting time intervals consisting of start and end times [[s1,e1],[s2,e2],...] (si < ei), determine if a person could attend all meetings. 2 | # We use here greedy approach similar to activity selection problem. 3 | 4 | # meeting time intervals = [[0, 30],[5, 10],[15, 20]] 5 | 6 | # after sorting on finish : [[5, 10], [15, 20], [0, 30]] 7 | # TIME : 0(N * log(N)), SPACE : 0(1) 8 | 9 | def compute_meeting_room(arr): 10 | 11 | arr.sort(key = lambda x : x[1]) 12 | for i in range(len(arr)): 13 | if arr[i][1] > arr[i + 1][0]: 14 | return False 15 | 16 | return True 17 | 18 | if __name__ == '__main__': 19 | 20 | arr = [[0, 30],[5, 10],[15, 20]] 21 | print(compute_meeting_room(arr)) 22 | -------------------------------------------------------------------------------- /heaps_and_priorityQueues/Interview_patterns.MD: -------------------------------------------------------------------------------- 1 | # Important trick for Heaps/Priority queues : 2 | * Try to build heap from array/list if all the elements are known beforehand 3 | which will be going inside heaps as it will take linear time 0(N) otherwise 4 | going for one by one would take 0(N*log(N)) and it's inefficient. 5 | * Convert elements into tuples by using : 6 | ```[() for _ .....]``` and then pass into heapify directly. 7 | 8 | * More examples (Priority queues): 9 | > Let's say we have hashmaps using Counter/ dict 10 | ```count = dict()/Counter()``` 11 | ```ele = [(count.get(i), i) for _ in count]``` 12 | ```heapify(ele)``` 13 | > Let's say we used array for hashmaps 14 | ```count = [2, 1, 3, 4, 0, 0, 1, 0, 0, 1]``` 15 | ```ele = [(count[i], i) for i in range(len(count)) if count[i]]``` 16 | ```heapify(ele)``` 17 | > For max heaps (upper was using min heaps): 18 | ```ele = [(-count[i], i) for i in range(len(count)) if count[i]]``` 19 | ```heapify(ele)``` 20 | ``` 21 | * If using Priority Queue and update is required in value, since there is no function for "increase_key" operation, 22 | * so, we need to know exact index at which the elements is so that we can change that index value. 23 | * We Should use hashmaps for mapping the index to the elements and keep updating it in any operation (helpful for increase_key) 24 | -------------------------------------------------------------------------------- /heaps_and_priorityQueues/check_binary_heap.py: -------------------------------------------------------------------------------- 1 | # Program for checking if given array forms binary heap or not. 2 | # idea is to check from last internal node that is at n//2 - 1 , to check 3 | # if at any node up the tree, we have if any voilation of heapify depending on 4 | # whether we are checking min-heap, or max-heap. 5 | # TIME : 0(N), space : 0(1). 6 | 7 | def left(i): 8 | return 2 * i + 1 9 | 10 | def right(i): 11 | return 2 * i + 1 12 | 13 | def Check_Heap(arr): 14 | n = len(arr) 15 | for i in range(n//2 - 1, -1, -1): 16 | 17 | if arr[i] < arr[left(i)]: 18 | return False 19 | 20 | if arr[i] < arr[right(i)]: 21 | return False 22 | 23 | return True 24 | 25 | 26 | if __name__ == '__main__': 27 | arr = [90, 15, 10, 7, 12, 2] 28 | print(Check_Heap(arr)) 29 | arr1 = [90, 15, 10, 7, 12, 2, 7, 3] 30 | print(Check_Heap(arr1)) 31 | -------------------------------------------------------------------------------- /heaps_and_priorityQueues/heaps_decrease_key_mod.py: -------------------------------------------------------------------------------- 1 | from heapq import heappush, heappop 2 | 3 | class PriorityQueue: 4 | 5 | def __init__(self): 6 | 7 | self.heap = [] 8 | 9 | 10 | # helper function for getting parent of any node at index i 11 | def parent(self, i): 12 | return i // 2 13 | 14 | # function for pushing the element onto heap 15 | def heappush(self, priority, val): 16 | 17 | heappush(self.heap, (priority, val)) 18 | 19 | # function for popping from the heap 20 | def heappop(self): 21 | 22 | return heappop(self.heap) 23 | 24 | 25 | def decrease_key(self, i, new_val): 26 | self.heap[i] = (new_val, i) 27 | while i != 0 and self.heap[self.parent(i)][0] > self.heap[i][0]: 28 | self.heap[i], self.heap[self.parent(i)] = self.heap[self.parent(i)], self.heap[i] 29 | 30 | def pretty_print(self): 31 | print(self.heap) 32 | 33 | if __name__ == '__main__': 34 | queue = PriorityQueue() 35 | queue.heappush(0, 0) 36 | queue.heappush(100000, 1) 37 | queue.heappush(100000, 2) 38 | queue.heappush(100000, 3) 39 | temp = queue.heappop() 40 | queue.pretty_print() 41 | queue.decrease_key(1, 90000) 42 | queue.pretty_print() 43 | -------------------------------------------------------------------------------- /heaps_and_priorityQueues/merge_k_sorted_different_sizes.py: -------------------------------------------------------------------------------- 1 | # Program to merge k sorted arrays but now of different sizes. 2 | # Earlier when sizes were same, then we could have used DAC approach to break down the problem and 3 | # merge into groups of 2 by recursive method. 4 | # But now since sizes are different, it is not feasible to exactly break these in groups of 2. 5 | # Hence, for this only one optimized solution could be there - to use binary min heap of size k. 6 | # TIME : 0(N*lg(k)), SPACE : 0(K) 7 | 8 | 9 | from heapq import heappush, heappop, heapify 10 | 11 | # OPTMIZED SOLUTION : 12 | def MergeKSortedarrays(arr, k): 13 | # build and initalize the heap of size k 14 | heap = [] 15 | heapify(heap) 16 | for i in range(k): 17 | heappush(heap, (arr[i][0], i, 0)) 18 | 19 | # heap.print_heap() 20 | res = [] 21 | # heap.print_heap() 22 | # Iterate while heap is not empty 23 | while heap: 24 | # get the top element along with i, j entry of the array 25 | element, x, y = heap[0] 26 | # took off the root element 27 | heappop(heap) 28 | # push to the result 29 | res.append(element) 30 | # we check whether our current row of array exhausts, if yes then we do nothing 31 | if (y + 1 < len(arr[x])): 32 | heappush(heap, (arr[x][y + 1], x, y + 1)) 33 | # heap.print_heap() 34 | return res 35 | 36 | 37 | 38 | if __name__ == '__main__': 39 | arr1 = [[2, 6, 12], [1, 9], [23, 34, 90, 2000]] 40 | k = 3 41 | print(MergeKSortedarrays(arr1, k)) 42 | -------------------------------------------------------------------------------- /heaps_and_priorityQueues/min_join_ropes_length.py: -------------------------------------------------------------------------------- 1 | # Program for getting the minimum cost of joining the given n number of ropes efficiently. 2 | # Cost is defined by the lengths of the ropes given to us as input. 3 | # We can observe, that if we follow greedy approach such that we always choose next minimum element to 4 | # be added to the already made rope so far, this would lead to overall minimum cost. 5 | # For tracking current minimum and next minimum element, a binary heap is optimized version of maintaining this. 6 | # So, everytime we extract two minimum from the min heap and then add them to the cost so far, and then push back the the 7 | # popped element cost again in the heap as we also want to keep track of old cost to be added in the next iteration. 8 | # This goes on till we have atleast two elements in the heap. 9 | # The idea is to use greedy approach to minimise the overall cost so at eveyr point, we get two minimum ropes to be added to the overall rope and update the cost along with it. 10 | # TIME : 0(N*lg(N)), SPACE : 0(N). 11 | 12 | 13 | from heapq import heappush, heappop, heapify 14 | 15 | def MinJoin(arr): 16 | heapify(arr) 17 | cost = 0 18 | while len(arr) > 1: 19 | a, b = heappop(arr), heappop(arr) 20 | cost += (a + b) 21 | heappush(arr, a + b) 22 | 23 | return cost 24 | 25 | 26 | if __name__ == '__main__': 27 | arr = [4, 3, 2, 6] 28 | print(MinJoin(arr)) 29 | -------------------------------------------------------------------------------- /heaps_and_priorityQueues/priority_queues_built_in.py: -------------------------------------------------------------------------------- 1 | # Program for implementing PriorityQueue using built-in modules 2 | # we can use two approach as shown 3 | 4 | from heapq import heappush, heappop, heapify 5 | from queue import PriorityQueue 6 | 7 | heap = [] 8 | heapify(heap) 9 | 10 | 11 | 12 | heappush(heap, (4, 'elon')) 13 | heappush(heap, (1, 'newtech')) 14 | heappush(heap, (3, 'shakti')) 15 | heappush(heap, (2, 'sourav')) 16 | print("PriorityQueue using heapq :") 17 | 18 | print(heap) 19 | 20 | q = PriorityQueue() 21 | q.put((2, 'sourav')) 22 | q.put((4, 'elon')) 23 | q.put((1, 'shakti')) 24 | q.put((3, 'shakti')) 25 | 26 | 27 | print("\nPriorityQueue using Queue module, PriorityQueue class :") 28 | # indexing in PriorityQueue is not possible 29 | while not q.empty(): 30 | print(q.get()) 31 | -------------------------------------------------------------------------------- /heaps_and_priorityQueues/top_k_frequent_elements.py: -------------------------------------------------------------------------------- 1 | # Program for printing top k frequent elements from the given input array 2 | # Naive approach is to keep a count of all numbers in the array in a extra array in a tupled way 3 | # (element, count) and then sort it on the basis of their frequencies and return the elements for 4 | # top k frequencies and this would take atleast 0(N*lg(N)). 5 | # Can we do better than this ? 6 | # So, we can observe since we need count, so we will use collections.Counter for better performance 7 | # and also we can use Counter.most_common() which inbuilt uses heap when given some "k" as input. 8 | # So, overall we are building the array to sort it and return it k times by popping from root. 9 | # So, either we can build a heap and pop it k times or we can use directly above said functions. 10 | # In both case, max bound will be 0(k * (lg(N))) and space : 0(N) for building the heap. 11 | # Can we do more better than this ? 12 | 13 | # [0(N) + 0(K) + N*lg(N)] = bounded by N * lg(N) since K < N solution : 14 | from typing import List 15 | from collections import Counter 16 | def topKFrequent(nums: List[int], k: int) -> List[int]: 17 | count = Counter(nums) 18 | count = count.most_common(k) 19 | res = [] 20 | for i in range(k): 21 | res.append(count[i][0]) 22 | return res 23 | 24 | 25 | if __name__ == '__main__': 26 | arr, k = [1, 1, 1, 2, 2, 3], 2 27 | print(topKFrequent(arr, k)) 28 | -------------------------------------------------------------------------------- /linked lists/Interview_patterns.MD: -------------------------------------------------------------------------------- 1 | # FOR INTERVIEW : 2 | * Fast ptr and Slow ptr logic 3 | * Visited Flag logic (modification in basic structure if allowed) or if not allowed, then some kind of Hashing. 4 | * Keeping a track of tail pointer along with head. 5 | * Any other manipulation of pointer tricks. 6 | * Construct dummy node and some manipulation. 7 | -------------------------------------------------------------------------------- /linked lists/delete_node_linkedlist.py: -------------------------------------------------------------------------------- 1 | # program for deleting the node given only it's reference, not any head ref 2 | 3 | # importing all linkedlist main functions 4 | from linkedlist import LList, Node 5 | 6 | # functions for deleting given node 7 | 8 | # logic is to simply swap the given node pointer data with next node data, and then delete the next node which contains now that original data by pointing and adjusting pointer by skipping the deleted node. 9 | def delete_val(node): 10 | if not node: 11 | return 12 | # swapping the current node and next node 13 | node.next.data, node.data = node.data, node.next.data 14 | # deleting the node by skipping the connection between current node and swapped node 15 | node.next = node.next.next 16 | 17 | # main function 18 | if __name__ == '__main__': 19 | l = LList() 20 | head = Node(4) 21 | first_node = Node(5) 22 | second_node = Node(1) 23 | third_node = Node(9) 24 | head.next, first_node.next, second_node.next, third_node.next = first_node, second_node, third_node, None # 4->5->1->9 25 | delete_val(first_node) # deletes 5 26 | ptr = head 27 | while(ptr): 28 | print(ptr.data, end = " ") 29 | ptr = ptr.next 30 | # prints 4->1->9 31 | -------------------------------------------------------------------------------- /linked lists/detect_loop_list.py: -------------------------------------------------------------------------------- 1 | # Program to check if linked list contains a cycle / loop or not 2 | # Idea : First logic is that we can use a extra field in node structure named as "visited" , and mark it while traversing along the linked list 3 | # then, if we encounter a already visited node, then obvisoulsy, we have found a loop. 4 | # Optimized version is called floyd's cycle detection algorithm which uses two pointers technique also called hare and tortoise algorithm 5 | # in which one is called slow_ptr which moves one step at a time, and another known as fast_ptr which moves two steps at a a time. 6 | # and then since the cycle length is n, they will defintely meet at some time if there is a loop. 7 | # Time : 0(N), Space : extra space whatever is required by using two extra pointers. 8 | 9 | def contains_cycle(first_node): 10 | 11 | # Check if the linked list contains a cycle 12 | slowptr = fastptr = first_node 13 | while slowptr and fastptr and fastptr.next: 14 | slowptr = slowptr.next 15 | fastptr = fastptr.next.next 16 | if slowptr == fastptr: 17 | return True 18 | 19 | return False 20 | -------------------------------------------------------------------------------- /linked lists/middle_linked_list.py: -------------------------------------------------------------------------------- 1 | # program to find and return middle element of linked lists 2 | 3 | # importing all functions from main linked list 4 | from linkedlist import LList, Node 5 | 6 | # function for middle list return 7 | def middle_list(head): 8 | # if head is none 9 | if head == None: 10 | return 11 | # if only one element is present 12 | if not head.next: 13 | return head 14 | # if only two element is present 15 | if not head.next.next: 16 | return head.next 17 | # otherwise, use two pointers trick, so move one pointer twice as fast as the slow pointer which moves one step at a time and when fast one will reach the end of list, slow one will be at the middle of the list. 18 | slow_ptr = fast_ptr = head 19 | while(fast_ptr and fast_ptr.next): 20 | fast_ptr = fast_ptr.next.next 21 | slow_ptr = slow_ptr.next 22 | return slow_ptr 23 | 24 | # main function 25 | if __name__ == '__main__': 26 | l = LList() 27 | head = Node(1) 28 | l.add_node_end(head, 2) 29 | l.add_node_end(head, 3) 30 | l.add_node_end(head, 4) 31 | l.add_node_end(head, 5) 32 | l.add_node_end(head, 6) 33 | l.print_list(head) 34 | print(middle_list(head).data) 35 | -------------------------------------------------------------------------------- /linked lists/swap_nodes.py: -------------------------------------------------------------------------------- 1 | # Program for swapping the nodes pairwise - 2 | # Here, we are just swapping the nodes values of both adjacent nodes 3 | # We can also actually change pointers / links if swapping values are not allowed. 4 | # TIME : 0(N), SPACE : 0(1) 5 | 6 | # Node structure class 7 | class Node: 8 | 9 | def __init__(self, x): 10 | self.data = x 11 | self.next = None 12 | 13 | # LinkedList class 14 | class LinkedList: 15 | 16 | def swap_nodes(self, head): 17 | ptr = head 18 | while ptr and ptr.next : 19 | ptr.data, ptr.next.data = ptr.next.data, ptr.data 20 | ptr = ptr.next.next 21 | 22 | return head 23 | 24 | def pretty_print(self, head): 25 | ptr = head 26 | while ptr: 27 | print(ptr.data, end = " -> ") 28 | ptr = ptr.next 29 | 30 | 31 | # driver testing function 32 | if __name__ == '__main__': 33 | llist = LinkedList() 34 | head = Node(1) 35 | second1 = Node(2) 36 | third1 = Node(3) 37 | fourth1 = Node(4) 38 | fifth1 = Node(5) 39 | sixth1 = Node(6) 40 | seventh1 = Node(7) 41 | head.next = second1 42 | second1.next = third1 43 | third1.next = fourth1 44 | fourth1.next = fifth1 45 | fifth1.next = sixth1 46 | sixth1.next = seventh1 47 | 48 | llist.pretty_print(head) 49 | new_head = llist.swap_nodes(head) 50 | print() 51 | llist.pretty_print(new_head) 52 | -------------------------------------------------------------------------------- /matrix specific/MATRIX_interview_patterns.MD: -------------------------------------------------------------------------------- 1 | # WHEN IN INTERVIEW, LOOK OUT FOR PATTERNS: 2 | * Converting between matrix and array is useful : 3 | ``` 4 | 2d[i][j] = 1d[i * n + j], n is cols , (i, j) is index in matrix. 5 | ``` 6 | * Converting between array and matrix : 7 | ``` 8 | index of array => idx, so row_idx, col_idx = idx / cols, idx % cols, mat[row_idx][col_idx] 9 | ``` 10 | * Look for staircase algorithm for sorted matrices. 11 | * Elements indices on the same diagonals sum up to same values. 12 | * Transposing is like swapping either in upper half or lower half. 13 | * Transpose and rotation is related. 14 | * Look for boundary algorithm (keeping four pointers for setting boundaries) for different types of traversals. 15 | * Computing prefix sum matrix and suffix sum matrix can be really helpful. 16 | ## while computing prefix or suffix, we compute first by row and then col, or vice-versa. 17 | ## Do row wise addition prefix sum 18 | ## Then, do column wise addition prefix sum 19 | # Computing subqueries for sum would be like : 20 | ``` 21 | Sum (i, j) -> (x, y) => P[x][y] + P[i - 1][j - 1] - P[i - 1][y] - P[x][j - 1] 22 | ``` 23 | 24 | # Kadane's algo for 2-d matrix 25 | -------------------------------------------------------------------------------- /matrix specific/matrix_multiplication_naive.py: -------------------------------------------------------------------------------- 1 | # Program to naively multiply two matrices 2 | # TIME : 0(M*N*K) AND SPACE : 0(K^2) , WHERE M , N , K ARE DIMENSIONS OF FIRST MAT, SECOND MAT AND RESULTANT PRODUCT MATRIX RESPECTIVELY. 3 | # WE CAN IMPROVE THIS BY USING DIVIDE AND CONQUER APPROACH BY USING STRASEEN ALGORITHM OR KARATSUBA ALGORITHM (DISCUSSED IN DAC SECTION). 4 | 5 | def product(mat1, mat2): 6 | rows1, cols1 = len(mat1), len(mat1[0]) 7 | rows2, cols2 = len(mat2), len(mat2[0]) 8 | if cols1 != rows1: 9 | raise Exception (f"Dimensions ERROR , mat1 : [{rows1} x {cols1}] mat2 : [{rows2} x {cols2}]") 10 | 11 | res = [[0] * cols2 for _ in range(rows1)] 12 | print(res) 13 | for i in range(rows1): 14 | for j in range(cols2): 15 | for k in range(len(res)): 16 | res[i][j] += mat1[i][k] * mat2[k][j] 17 | 18 | print(res) 19 | 20 | 21 | # DRIVER TEST FUNCTION 22 | if __name__ == '__main__': 23 | 24 | M = [[1, 1, 1, 1], 25 | [2, 2, 2, 2], 26 | [3, 3, 3, 3], 27 | [4, 4, 4, 4]] 28 | 29 | N = [[1, 1, 1, 1], 30 | [2, 2, 2, 2], 31 | [3, 3, 3, 3], 32 | [4, 4, 4, 4]] 33 | 34 | product(M, N) 35 | -------------------------------------------------------------------------------- /number theory/check_perfect_square.py: -------------------------------------------------------------------------------- 1 | # program to check if the number is isPerfectSquare or not without use of math.sqrt 2 | # logic is to loop over all the numbers from 1 to sqrt(n) so, it will take much lesses time than looping over n. 3 | 4 | def isPerfectSquare(num: int) -> bool: 5 | # if num is 0 or 1, then it is true 6 | if num == 0 or num == 1: 7 | return 1 8 | i = 1 9 | # otherwise, loop till it becomes true (sqrt(n)) 10 | while i * i <= num: 11 | if i * i == num: 12 | return True 13 | i += 1 14 | return False 15 | print(isPerfectSquare(14)) 16 | print(isPerfectSquare(493658625628)) 17 | -------------------------------------------------------------------------------- /number theory/desktop.ini: -------------------------------------------------------------------------------- 1 | [LocalizedFileNames] 2 | fast_exponentiation_binary_algo.py=@fast_exponentiation_binary_algo,0 3 | -------------------------------------------------------------------------------- /number theory/efficient_fact_binomial.py: -------------------------------------------------------------------------------- 1 | # Program for efficient computation of binomial coefficient using factorials. 2 | # We have already seen the efficient method of fact computation using DP, and that we are going to use it for binomial computations. 3 | # The MAX_VALUES Depends on the contraints given in the problem. 4 | 5 | MAX_INT = 100 6 | import sys 7 | def get_fact(): 8 | 9 | fact = [0] * MAX_INT 10 | fact[0] = 1 11 | for i in range(1, len(fact)): 12 | fact[i] = (fact[i - 1] * i) % sys.maxsize 13 | 14 | return fact 15 | 16 | def get_binom(n, k, factorial): 17 | return factorial[n] // (factorial[k] * factorial[n - k]) 18 | 19 | factorial = get_fact() 20 | print(get_binom(40, 2, factorial)) 21 | 22 | # Use pascal's triangle to generate DP bottom up table. 23 | -------------------------------------------------------------------------------- /number theory/fast_exponentiation_binary_algo.py: -------------------------------------------------------------------------------- 1 | # Program to compute exponentiation efficiently 2 | # compute a raise to power b most efficient way using bitwise logic 3 | # IDEA: firstly, we note that if we normally multiply "a" , "b" number of times, then it takes 0(N) time which is not optimal, 4 | # so, what we use is bit level information for "b" and use it to multiply "a" only when there is set bit "1" in "b" which reduces the 5 | # computations of multiplcations at most log(N) times. 6 | # Time : 0(log(N)), SPACE : 0(1) 7 | 8 | def expo(a, n): 9 | ans = 1 10 | while n > 0: 11 | if n & 1: 12 | ans *= a 13 | 14 | a *= a 15 | n = n >> 1 16 | 17 | return ans 18 | 19 | if __name__ == '__main__': 20 | assert expo(2, 4) == 16 21 | assert expo(4, 4) == 256 22 | assert expo(3, 5) == 243 23 | assert expo(5, 3) == 125 24 | assert expo(5, 1) == 5 25 | assert expo(5, 0) == 1 26 | -------------------------------------------------------------------------------- /number theory/fibonacci_efficient.py: -------------------------------------------------------------------------------- 1 | # program to sum up even fibonacci terms, one of the trick is to use golden ration to calculate fibonacci terms using the formula used below and then second trick is to only calculate even value terms without checking every number in the range , due to the fact that every third number in the series is already known even value, so keep and counter and increment it by 3 and then sum it up. 2 | # here, we are only required to sum up the terms until that sum exceed 4000000 3 | 4 | import math 5 | # function to calculate fibonacci nth term 6 | def fib(n): 7 | numr = pow((math.sqrt(5) + 1) / 2, n) 8 | return round(numr / math.sqrt(5)) 9 | 10 | # function to sum up even valued fib terms 11 | def sum_even_fib(): 12 | n = 0 13 | sum = 0 14 | while(fib(n) < 4000001): 15 | sum = sum + fib(n) 16 | n = n + 3 17 | return sum 18 | 19 | # main function 20 | if __name__ == '__main__': 21 | print(sum_even_fib()) 22 | -------------------------------------------------------------------------------- /number theory/gcd_extended.py: -------------------------------------------------------------------------------- 1 | # Program for finding gcd for array of numbers 2 | # gcd(a, b, c) = gcd(a, gcd(b, c)) = gcd(gcd(a, b), c) = gcd(gcd(a, c), b) 3 | 4 | def gcd_extend(a, b): 5 | return a if b == 0 else gcd_extend(b, a % b) 6 | 7 | 8 | if __name__ == '__main__': 9 | arr = [2, 4, 6, 8, 16, 40] 10 | gcd = gcd_extend(arr[0], arr[1]) 11 | for i in arr: 12 | gcd = gcd_extend(gcd, i) 13 | 14 | print(gcd) 15 | -------------------------------------------------------------------------------- /number theory/gcd_lcm_efficient.py: -------------------------------------------------------------------------------- 1 | # program to compute gcd and lcm efficiently 2 | # logic is to use euclid's algorithm , ex. 10, 20 -> here we take a as 10 and b as 20. 3 | # now, first divide 20 by 10, take remainder and then again divide now by this remainder to 10 that is 0, and so on now since here, it comes as zero in only one iteration, then stop (it's stopping criteria) otherwise keep dividing by the remainder of calculate prev division. 4 | # function to return the gcd of numbers 5 | def gcd(x, y): 6 | if x == 0: 7 | return y 8 | else: 9 | return gcd(y % x, x) 10 | 11 | # function to return the lcm of numbers 12 | # here, we know that gcd * lcm = a * b (product of two numbers) 13 | def lcm(x, y): 14 | if x == 0 and y == 0: 15 | return 0 16 | g = gcd(x, y) 17 | return x * y // g 18 | 19 | # main function 20 | if __name__ == '__main__': 21 | assert lcm(10, 20) == 20 22 | assert lcm(0, 10) == 0 23 | assert lcm(10, 0) == 0 24 | assert lcm(0, 0) == 0 25 | assert lcm(1, 1) == 1 26 | print(lcm(90, 2)) == 90 27 | -------------------------------------------------------------------------------- /number theory/kth_root.py: -------------------------------------------------------------------------------- 1 | # Program to compute kth root of given number n. 2 | # So, we have to find the greatest integer x, for which x^k <= n is true. 3 | # # IDEA: naive approach is to simply go on linear searching from 1 to n, and stop whenever we find 4 | # the above condition to be satified, this way we get biggest integer satifying above condition. 5 | # But that can take n*log(k) for computing this integer and for range queries, this may take up to: 6 | # t*n*log(k). 7 | # Now if we observe carefully, we can see search space from 1 to n is monotonic (sorted ordering in ascending manner) 8 | # We can apply binary search and this reduces time complexity to log(n)*log(k). 9 | 10 | # this computes and checks power in log(k) time. 11 | def isPow(n, k, x): 12 | return x ** k <= n 13 | 14 | # function to apply binary search in log(N) 15 | def compute_pow(n, k): 16 | start, end = 1, n 17 | ans = 0 18 | while start <= end: 19 | mid = (start + end) // 2 20 | if isPow(n, k, mid): 21 | ans = mid 22 | start = mid + 1 23 | else: 24 | end = mid - 1 25 | 26 | return mid 27 | 28 | # driver test function 29 | if __name__ == '__main__': 30 | n, k = 1000000000000, 10 31 | print(compute_pow(n, k)) 32 | -------------------------------------------------------------------------------- /number theory/linear_diphonatine_equations.py: -------------------------------------------------------------------------------- 1 | # Program to check if solutions of linear diophantine equations possible or not ? 2 | # Linear diophantine equations are linear polynomials in the form of a*x + b*y = c and x, y can take only integral solutions. 3 | # Equations can be of more than two unknowns also. 4 | # Firstly we check if solutions exist or not , if yes then what are those integers for which it holds. 5 | 6 | from math import gcd 7 | from sys import stdin, stdout 8 | 9 | # Function to check if solutions exist for given values of a, b, c 10 | # IDEA: logic is to get the gcd of a, b so that we can get a number which divides a, and b evenly and then since x and y are integers, 11 | # so overall a*x + b*y gets divided evenly and since a*x + b*y = c, so c also should get divided evenly by this gcd. 12 | 13 | def check_diophantine(a, b, c): 14 | return (c % gcd(a, b)) == 0 15 | 16 | # function to find exact one of the integral solutions if the solution exists 17 | def find_diophantine(a, b, c): 18 | if check_diophantine(a, b, c): 19 | i = 0 20 | while i * a <= c: 21 | if (c - (a * i)) % b == 0: 22 | print("Found solution -") 23 | print(f"x : {i}, y : {int((c - (i * a)) / b)}") 24 | break 25 | i += 1 26 | else: 27 | print("No solution possible !") 28 | 29 | # main driver program 30 | if __name__ == '__main__': 31 | print("Enter values of a, b, c (spaced): ") 32 | a, b, c = list(map(int, stdin.readline().strip().split())) 33 | find_diophantine(a, b, c) 34 | -------------------------------------------------------------------------------- /number theory/mod_exponentiation.py: -------------------------------------------------------------------------------- 1 | # Short idea for modular exponentiation, logic is same for bitwise exponentiation. 2 | # IDEA: approach is same as already done for fast exponentiation but here we will also include modulus for avoiding overflows 3 | # TIME : 0(log(B)) 4 | 5 | def mod_exp(a, b, m): 6 | 7 | ans = 1 8 | a = a % m 9 | 10 | if a == 0: 11 | return 0 12 | 13 | while b > 0: 14 | if b & 1: 15 | ans = (ans * a) % m 16 | a = (a * a) % m 17 | b = b // 2 18 | return ans 19 | 20 | 21 | print(mod_exp(2, 10, 1000000000)) 22 | -------------------------------------------------------------------------------- /number theory/mod_inverse.py: -------------------------------------------------------------------------------- 1 | # Program to compute modular inverse for a given number such that a*x + b*y = 1, find x. 2 | # computation for (a.b) % m = 1, where m is in range : [1, m-1] 3 | # This is basically application of modular exponentiation 4 | # TIME : 0(log(M)) 5 | 6 | def mod_exp(a, b, m): 7 | 8 | ans = 1 9 | a = a % m 10 | 11 | if a == 0: 12 | return 0 13 | 14 | while b > 0: 15 | if b & 1: 16 | ans = (ans * a) % m 17 | a = (a * a) % m 18 | b = b // 2 19 | return ans 20 | 21 | 22 | def mod_inverse(a, n): 23 | return mod_exp(a, m-2, m) 24 | -------------------------------------------------------------------------------- /number theory/prime_factors.py: -------------------------------------------------------------------------------- 1 | # Program to compute prime factors in most efficient way 2 | # IDEA: Naive solution takes 0(N) time, One optimization to naive solution is that we can actually loop and iterate over sqrt(N) instaead of N because 3 | # divisors occur in pairs, for the same reason as was in computing prime numbers. 4 | # Time complexity then reduces to 0(Sqrt(N)) 5 | # But for range of queries, we can optimize it further using prime seives. 6 | 7 | import math 8 | 9 | def prime_factor(n): 10 | l = [] # list storing prime factors 11 | # iterating till sqrt(N) 12 | for i in range(2, int(math.sqrt(n)) + 1, 1): 13 | # reducing number by keep dividing by the same number till it exhausts 14 | while (n % i == 0): 15 | l.append(i) 16 | n = n / i 17 | 18 | # handling prime number situation 19 | if n > 2: 20 | l.append(n) 21 | 22 | return l 23 | 24 | 25 | # One more optimization is that, we can first exploit its composite parts by keep dividing by 2 and then once it's done, then start with 3 and then go over 26 | # by jumping 2 each time, like 3, 5, 7, .... because whatever is left is odd number now onwards. 27 | # def prime_factor3(n): 28 | # l = [] 29 | # while n % 2 == 0: 30 | # l.append(2) 31 | # n = n / 2 32 | # 33 | # for i in range(3, int(math.sqrt(n)) + 1, 2): 34 | # while (n % i == 0): 35 | # l.append(i) 36 | # n = n / i 37 | # if n > 2: 38 | # l.append(n) 39 | # 40 | 41 | if __name__ == '__main__': 42 | print(prime_factor(60)) 43 | -------------------------------------------------------------------------------- /pattern print/command_prompt_printing.py: -------------------------------------------------------------------------------- 1 | # prints breadth of s 2 | print('---------------', end = " ") 3 | print('--------------------------', end = " ") 4 | print(' -', end = " ") 5 | print(' {}'.format(('-')*20)) 6 | j = 0 7 | for i in range(1, 16): 8 | print(" "*i, end = "") 9 | # prints bottom part of t 10 | print('\\', end = " "*(30 - i)) 11 | # prints diagonal part of s 12 | print('|', end = " "*(25 - i)) 13 | 14 | print('/', end = " "*(i + j)) 15 | j += 1 16 | 17 | print('\\', end = " "*(30 - i)) 18 | 19 | print('|') 20 | 21 | 22 | print('---------------') 23 | # prints t 24 | 25 | input() 26 | -------------------------------------------------------------------------------- /pattern print/magicdiamond.py: -------------------------------------------------------------------------------- 1 | # Python program for generating diamond pattern in python 3.7+ 2 | 3 | # Function to print upper half of diamond (pyramid) 4 | def floyd(n): 5 | ''' 6 | Parameters: 7 | n : size of pattern 8 | ''' 9 | for i in range(0, n): 10 | for j in range(0, n-i-1): # printing spaces 11 | print(" ", end = "") 12 | for k in range(0, i + 1): # printing stars 13 | print("* ", end = "") 14 | print() 15 | 16 | 17 | # Function to print lower half of diamond (pyramid) 18 | def reverse_floyd(n): 19 | ''' 20 | Parameters: 21 | n : size of pattern 22 | ''' 23 | for i in range(n, 0, -1): 24 | for j in range(i, 0, -1): # printing stars 25 | print("* ", end = "") 26 | print() 27 | for k in range(n-i+1, 0, -1): # printing spaces 28 | print(" ", end = "") 29 | 30 | # Function to print complete diamond pattern of "*" 31 | def pretty_print(n): 32 | ''' 33 | Parameters: 34 | n : size of pattern 35 | ''' 36 | if n <= 0: 37 | print(" ... .... nothing printing :(") 38 | return 39 | floyd(n) # upper half 40 | reverse_floyd(n) # lower half 41 | 42 | 43 | if __name__ == "__main__": 44 | print("| /\ | |- | |- |--| |\ /| |-") 45 | print("|/ \| |- |_ |_ |__| | \/ | |_") 46 | K = 1 47 | while(K): 48 | user_number = int(input("enter the number and , and see the magic : ")) 49 | print() 50 | pretty_print(user_number) 51 | K = int(input("press 0 to exit... and 1 to continue...")) 52 | 53 | print("Good Bye...") 54 | -------------------------------------------------------------------------------- /prefix_cum_sum_array.py: -------------------------------------------------------------------------------- 1 | # Program to compute prefix sum or cummulative sum array so that at every point, we have value equal to sum of all number from 0 to till that index. 2 | # TIME : 0(N), SPACE : 0(N), WHERE N IS SIZE OF INPUT ARRAY 3 | # Powerful for range sum queries (if static, that is no updates) 4 | 5 | def prefix_sum(arr): 6 | if not len(arr): 7 | return [] 8 | cum_sum = [0 for _ in range(len(arr))] 9 | cum_sum[0] = arr[0] 10 | for i in range(1, len(arr)): 11 | cum_sum[i] = cum_sum[i - 1] + arr[i] 12 | 13 | return cum_sum 14 | 15 | if __name__ == '__main__': 16 | arr =[10, 4, 16, 20] 17 | print(prefix_sum(arr)) 18 | -------------------------------------------------------------------------------- /random algorithms/monte_carlo_pi.py: -------------------------------------------------------------------------------- 1 | # Program to estimate value of pi using monte-carlo method. 2 | # Monte-carlo method is one of the randomized algorithms which uses some kind of randomness 3 | # in computational algorithms. It relies on random sampling to obtain numerical results. 4 | 5 | import random 6 | 7 | INTERVAL = 5000 8 | 9 | circle_points, square_points = 0, 0 10 | 11 | for i in range(INTERVAL * 2): 12 | 13 | range_x = random.uniform(-1, 1) 14 | range_y = random.uniform(-1, 1) 15 | 16 | origin_dist = range_x ** 2 + range_y ** 2 17 | if origin_dist <= 1: 18 | circle_points += 1 19 | 20 | square_points += 1 21 | 22 | if i % 50 == 0: 23 | print(f"Value of pi after {i} iterations: {(4 * circle_points) / square_points}") 24 | 25 | 26 | print(f"Final value of pi : {(4 * circle_points) / square_points}") 27 | -------------------------------------------------------------------------------- /recursion and backtracking/backtracking_patterns.MD: -------------------------------------------------------------------------------- 1 | # Three things to keep in mind always solving backtracking problems : 2 | ## * CHOICE : what do we do , we place item/ we remove the item / anything like in sudoku, n-queens etc...., 3 | ## searching exhaustively for all possibilities. 4 | ## * CONSTRAINTS : what is the way to validate the solution if it is correct or not. How do we tell if it is correct placement ? 5 | ## * GOAL : Base cases which decide we have reached our solution , or if we are out of bound. 6 | 7 | # General template : 8 | ``` 9 | 10 | def is_safe(board, row, col...){ 11 | // conditions to check the valid placement 12 | return condition1 and condition2 and ..... 13 | } 14 | 15 | 16 | def solve(row, col): 17 | if col == board[row].length{ 18 | base cases check and return answer 19 | } 20 | 21 | for val from 1 : 9 => { 22 | board[row][col] = value 23 | if is_safe(row, col....){ 24 | if solve(row, col + 1, board){ 25 | return true 26 | } 27 | board[row][col] = 0 28 | } 29 | } 30 | return False 31 | 32 | ``` 33 | 34 | # So, we keep placing, validation and undoing the wrong path and recurse for other path. 35 | -------------------------------------------------------------------------------- /recursion and backtracking/brackets_generation.py: -------------------------------------------------------------------------------- 1 | # Program for generating balanced set of brackets possible for given n. 2 | # In this also, we can use recursion and check some conditions based on number of opening 3 | # of opening and closing brackets. 4 | # Also, we know that in a balanced brackets, opening and closing will be same and will 5 | # be equal to given n, which can help in our base case. 6 | # ALso, we knwo string length generated will be maximum 2 * n. 7 | # TIME: (2^N) , EXPONENTIAL COMPLEXITY, SPACE : 0(N) 8 | 9 | def printBrackets(s, pos, open, close, n): 10 | if open == close == n: 11 | for i in s: 12 | print(i, end = " ") 13 | print() 14 | return 15 | 16 | else: 17 | if open > close: 18 | s[pos] = '}' 19 | printBrackets(s, pos + 1, open, close + 1, n) 20 | 21 | if open < n: 22 | s[pos] = '{' 23 | printBrackets(s, pos + 1, open + 1, close, n) 24 | 25 | 26 | n = 3 27 | s = [""] * 2 * n 28 | printBrackets(s, 0, 0, 0, n) 29 | -------------------------------------------------------------------------------- /recursion and backtracking/combinations.py: -------------------------------------------------------------------------------- 1 | # Program to generate all combinations using recursion + backtracking. 2 | 3 | class Solution: 4 | 5 | def combinations(self, subset, index, n, k, output): 6 | if len(subset) == k: 7 | output.append(subset.copy()) 8 | return 9 | 10 | for i in range(index, n + 1): 11 | subset.append(i) 12 | self.combinations(subset, i + 1, n, k, output) 13 | subset.pop() 14 | 15 | 16 | def combine(self, n: int, k: int) -> List[List[int]]: 17 | output = [] 18 | self.combinations([], 1, n, k, output) 19 | return output 20 | -------------------------------------------------------------------------------- /recursion and backtracking/count_all_paths_matrix.py: -------------------------------------------------------------------------------- 1 | # recursive implementation for counting all the paths possible in a matrix 2 | # given some starting point (assuming to be (0, 0) - leftmost corner and end point is at 3 | # rightmost end corner) 4 | 5 | def CountPath(rows, cols): 6 | if rows == 1 or cols == 1: 7 | return 1 8 | 9 | return CountPath(rows - 1, cols) + CountPath(rows, cols - 1) 10 | 11 | 12 | print(CountPath(10, 10)) 13 | -------------------------------------------------------------------------------- /recursion and backtracking/factorial.py: -------------------------------------------------------------------------------- 1 | # recursive implementation for computing the value of nth factorial 2 | 3 | def fact(n): 4 | if n == 1: 5 | return 1 6 | 7 | return fact(n - 1) * n 8 | 9 | if __name__ == '__main__': 10 | print(fact(5)) 11 | -------------------------------------------------------------------------------- /recursion and backtracking/fibonacci.py: -------------------------------------------------------------------------------- 1 | # recursive implementation for computing nth fibonacci number in the sequence 2 | 3 | def fib(n): 4 | if n <= 0: 5 | return 0 6 | if n == 1: 7 | return 1 8 | 9 | return fib(n - 1) + fib(n - 2) 10 | 11 | if __name__ == '__main__': 12 | print(fib(9)) 13 | -------------------------------------------------------------------------------- /recursion and backtracking/heap's_algo_permutate.py: -------------------------------------------------------------------------------- 1 | # heap's algorithm to find all the permutations of the given array / string . 2 | # At each step, on k initial elements of the collection. 3 | # Initially k == n, and thereafter k < n, each step generates k! permutations that end with same 4 | # n - k final elements. 5 | # It does this by calling itself once with the kth element unaltered and then k - 1 times with the 6 | # kth element exchanged for each of the initial k - 1 final elements. 7 | # The recursive calls modify the initial k - 1 elements and a rule is needed at each iteration 8 | # to select which will be exchanged with the last. 9 | # This is done by parity of the number of elements operated on at this step, if k is even, 10 | # final element is iteratively exchanged with each element index. 11 | # if k is odd, then final element is always exchanged with the first. 12 | # TIME : 0(N!) since N! permutations in all , 13 | # SPACE : 0(N) , each permutation requires amortised 0(1) time. 14 | 15 | def printArr(arr, n): 16 | for i in range(n): 17 | print(arr[i], end = " ") 18 | print() 19 | 20 | def heapPermutations(arr, size, n): 21 | if size == 1: 22 | printArr(arr, n) 23 | return 24 | 25 | for i in range(size): 26 | heapPermutations(arr, size - 1, n) 27 | 28 | if size & 1: 29 | arr[0], arr[size - 1] = arr[size - 1], arr[0] 30 | else: 31 | arr[i], arr[size - 1] = arr[size - 1], arr[i] 32 | 33 | if __name__ == '__main__': 34 | arr = [1, 2, 3] 35 | n = len(arr) 36 | heapPermutations(arr, n, n) 37 | -------------------------------------------------------------------------------- /recursion and backtracking/power_exponent.py: -------------------------------------------------------------------------------- 1 | # recursive implementation for computing the power of a raised to b. 2 | 3 | def power(a, b): 4 | if b == 0: 5 | return 1 6 | 7 | return power(a, b - 1) * a 8 | 9 | 10 | print(power(2, 4)) 11 | -------------------------------------------------------------------------------- /recursion and backtracking/print_all_paths_matrix.py: -------------------------------------------------------------------------------- 1 | # pROGRAM FOR PRINITNG ALL PATHS FROM A GIVEN STARTING CORNER (0, 0) TO END CORNER RIGHTMOST OF A matrix 2 | 3 | def PrintAllPath(mat, hash, i, j, res=[]): 4 | 5 | # print("current hash :") 6 | # pretty_print(hash) 7 | # print(f"current result : {res}\n") 8 | rows, cols = len(mat), len(mat[0]) 9 | # base case 10 | if i < 0 or i >= rows or j < 0 or j >= cols or hash[i][j] == 1: 11 | return 12 | 13 | # we know we have reached the destination 14 | if i == rows - 1 and j == cols - 1: 15 | 16 | res.append(mat[i][j]) 17 | # print the elements 18 | for i in range(len(res)): 19 | print(res[i], end = " ") 20 | print() 21 | return 22 | 23 | hash[i][j] = 1 24 | res.append(mat[i][j]) 25 | 26 | PrintAllPath(mat, hash, i, j + 1, res) 27 | PrintAllPath(mat, hash, i + 1, j, res) 28 | PrintAllPath(mat, hash, i - 1, j, res) 29 | PrintAllPath(mat, hash, i, j - 1, res) 30 | 31 | res.pop() 32 | hash[i][j] = 0 33 | 34 | 35 | def pretty_print(mat): 36 | for i in range(len(mat)): 37 | for j in range(len(mat[0])): 38 | print(mat[i][j], end = " ") 39 | print() 40 | 41 | if __name__ == '__main__': 42 | mat = [ 43 | [1, 2, 3], 44 | [4, 5, 6] 45 | ] 46 | rows, cols = len(mat), len(mat[0]) 47 | 48 | hash = [[0] * cols for _ in range(rows)] 49 | PrintAllPath(mat, hash, 0, 0) 50 | -------------------------------------------------------------------------------- /recursion and backtracking/recursion.md: -------------------------------------------------------------------------------- 1 | # Recursive approach : 2 | * Find out base case : which can be computed simply (to exit the recursion stack) 3 | * Find out examples and relation between them , build up "leap of faith" recursion 4 | * Finally, assuming leap of faith, write generalized equations for recursive calls. 5 | 6 | ### In this way, we can tackle most of the problems of recursion, assuming something that we have already implemented or it is avaiable to us, we just need to apply recursion whenever we can find problems to be broken down into smaller subproblems of same type. 7 | -------------------------------------------------------------------------------- /recursion and backtracking/recursion_patterns.MD: -------------------------------------------------------------------------------- 1 | # Interview patterns Recursion 2 | * Iterating recursive structures like liked list, tree, graph... 3 | * Subproblems : dynamic programming, direct recurrence relations 4 | * Selection : backtracking 5 | * Permuations : ordering a list, strings etc... 6 | * Divide and Conquer (DAC) approach : splitting problem space and then combining those results 7 | * Depth first search 8 | -------------------------------------------------------------------------------- /recursion and backtracking/solving_cryptoarithmetic_puzzle.py: -------------------------------------------------------------------------------- 1 | # Program for solving cryptoarithmetic puzzle where the Input consists of some combinations of 2 | # alphabets and we need to verify the given equation by substituting some number (0-9) assigned to 3 | # all alphabets uniquely to them and none of them can be same. 4 | # SEND 5 | # + MORE 6 | # -------- 7 | # MONEY 8 | # -------- 9 | # -------------------------------------------------------------------------------------------------------- 10 | # We will use backtracking to solve this problem and try every combinations of digits possible. 11 | bool ExhaustiveSolve(puzzleT puzzle, string lettersToAssign) 12 | { 13 | if (lettersToAssign.empty()) // no more choices to make 14 | return PuzzleSolved(puzzle); // checks arithmetic to see if works 15 | for (int digit = 0; digit <= 9; digit++) // try all digits 16 | { 17 | if (AssignLetterToDigit(lettersToAssign[0], digit)) 18 | { 19 | if (ExhaustiveSolve(puzzle, lettersToAssign.substr(1))) 20 | return true; 21 | UnassignLetterFromDigit(lettersToAssign[0], digit); 22 | } 23 | } 24 | return false; // nothing worked, need to backtrack 25 | } 26 | -------------------------------------------------------------------------------- /recursion and backtracking/sum_natural_numbers.py: -------------------------------------------------------------------------------- 1 | # recursive implementation for summing up the natural numbers to n 2 | 3 | def sum_natural(x): 4 | if x == 1: 5 | return 1 6 | 7 | return sum_natural(x - 1) + x 8 | 9 | if __name__ == '__main__': 10 | print(sum_natural(5)) 11 | -------------------------------------------------------------------------------- /searching algo/Interview_patterns.MD: -------------------------------------------------------------------------------- 1 | # Common Interview Patterns : 2 | * Apply binary search : Look for monotonic sequences like strictly increasing or decreasing / sorted order. 3 | * Look for exact range (maybe infinite), find start value and end value. 4 | * If it looks like sorted series like start = 10, end = 30, and we can try every possible value from 10...11...12...13.........30, then we need to apply binary 5 | search to optimize it as this series is itself sorted. This is also called modified binary search pattern. 6 | * That means it's not necessary that array will be given, we need to explicitly, find range in which our answer can lie and then find if sequence is sorted from start....end. 7 | * Sometimes, the given array will not be sorted but then also, we can apply modified binary search using concept of "promising search space", that means find some criterior which 8 | can reduce the search space to either half side by proving any one of the side is can give promising answer in that range meaning we don;t know exaclty, but with some conditions, 9 | we are having probability of finding the answer in that half space. 10 | -------------------------------------------------------------------------------- /searching algo/binary_search.py: -------------------------------------------------------------------------------- 1 | def binary_search(l, start, end, key): 2 | while(start <= end): 3 | middle = (start + end) // 2 # Caution : for larger inputs , typically more than 2^30 - 1, in order to arithmetic overflow, 4 | # use middle = start + (end - start) // 2 5 | # source : https://ai.googleblog.com/2006/06/extra-extra-read-all-about-it-nearly.html 6 | if key < l[middle]: 7 | end = middle - 1 8 | elif key > l[middle]: 9 | start = middle + 1 10 | else: 11 | return 1 12 | return 0 13 | 14 | 15 | def sort(l): 16 | return l.sort() 17 | 18 | def search_main(l, key): 19 | start = 0 20 | end = len(l) - 1 21 | l.sort() 22 | if binary_search(l, start, end, key): 23 | print("element found :)") 24 | else: 25 | print("element not found :(") 26 | 27 | if __name__ == '__main__': 28 | print("List the elements of list: ") 29 | u_list = list(map(int, input().strip().split())) 30 | key = int(input().strip()) 31 | print(f"your list : {u_list}") 32 | search_main(u_list, key) 33 | -------------------------------------------------------------------------------- /searching algo/binary_search_bisect.py: -------------------------------------------------------------------------------- 1 | from bisect import bisect_left 2 | 3 | def bin_search(arr, key, start, end): 4 | index = bisect_left(arr, key, start, end) 5 | if index < end and arr[index] == key: 6 | return index 7 | else: 8 | return -1 9 | 10 | if __name__ == '__main__': 11 | arr = [1, 3, 5, 10, 100] # the list must be already sorted before passing it to bisect. 12 | print(bin_search(arr, 10, 0, 4)) # 3 13 | print(bin_search(arr, 95, 0, 4)) # -1 14 | -------------------------------------------------------------------------------- /shell_leetcode/print_tenth_line.sh: -------------------------------------------------------------------------------- 1 | ''' 2 | Given a text file file.txt, print just the 10th line of the file. 3 | 4 | Example: 5 | 6 | Assume that file.txt has the following content: 7 | 8 | Line 1 9 | Line 2 10 | Line 3 11 | Line 4 12 | Line 5 13 | Line 6 14 | Line 7 15 | Line 8 16 | Line 9 17 | Line 10 18 | Your script should output the tenth line, which is: 19 | 20 | Line 10 21 | ''' 22 | 23 | awk 'FNR == 10 {print }' file.txt 24 | 25 | awk 'FNR == 10 {print }' file.txt 26 | 27 | sed -n 10p file.txt 28 | 29 | tail -n+10 file.txt|head -1 30 | -------------------------------------------------------------------------------- /shell_leetcode/transpose_file.sh: -------------------------------------------------------------------------------- 1 | ''' 2 | Given a text file file.txt, transpose its content. 3 | 4 | You may assume that each row has the same number of columns and each field is separated by the ' ' character. 5 | 6 | Example: 7 | 8 | If file.txt has the following content: 9 | 10 | name age 11 | alice 21 12 | ryan 30 13 | Output the following: 14 | 15 | name alice ryan 16 | age 21 30 17 | ''' 18 | 19 | awk ' 20 | { 21 | for (i = 1; i <= NF; i++) { 22 | if(NR == 1) { 23 | s[i] = $i; 24 | } else { 25 | s[i] = s[i] " " $i; 26 | } 27 | } 28 | } 29 | END { 30 | for (i = 1; s[i] != ""; i++) { 31 | print s[i]; 32 | } 33 | }' file.txt 34 | 35 | ''' 36 | Writing C style code using AWK 37 | Check with manual with AWK.sh 38 | ''' 39 | -------------------------------------------------------------------------------- /shell_leetcode/valid_phone_numbers.sh: -------------------------------------------------------------------------------- 1 | ''' 2 | Given a text file file.txt that contains list of phone numbers (one per line), write a one liner bash script to print all valid phone numbers. 3 | 4 | You may assume that a valid phone number must appear in one of the following two formats: (xxx) xxx-xxxx or xxx-xxx-xxxx. (x means a digit) 5 | 6 | You may also assume each line in the text file must not contain leading or trailing white spaces. 7 | 8 | Example: 9 | 10 | Assume that file.txt has the following content: 11 | 12 | 987-123-4567 13 | 123 456 7890 14 | (123) 456-7890 15 | Your script should output the following valid phone numbers: 16 | 17 | 987-123-4567 18 | (123) 456-7890 19 | ''' 20 | grep -P '^(\d{3}-|\(\d{3}\) )\d{3}-\d{4}$' file.txt 21 | 22 | ''' 23 | We use regex pattern for phone numbers to match using grep command. 24 | ''' 25 | -------------------------------------------------------------------------------- /shell_leetcode/word_frequency.sh: -------------------------------------------------------------------------------- 1 | ''' 2 | Write a bash script to calculate the frequency of each word in a text file words.txt. 3 | 4 | For simplicity sake, you may assume: 5 | 6 | words.txt contains only lowercase characters and space ' ' characters. 7 | Each word must consist of lowercase characters only. 8 | Words are separated by one or more whitespace characters. 9 | Example: 10 | 11 | Assume that words.txt has the following content: 12 | 13 | the day is sunny the the 14 | the sunny is is 15 | Your script should output the following, sorted by descending frequency: 16 | 17 | the 4 18 | is 3 19 | sunny 2 20 | day 1 21 | ''' 22 | cat words.txt | tr -s ' ' '\n' | sort | uniq -c | sort -r | awk '{ print $2, $1 }' 23 | 24 | ''' 25 | explanation: 26 | ➜ Desktop cat Words.txt 27 | the day is sunny the the 28 | the sunny is is 29 | ➜ Desktop cat Words.txt| tr -s ' ' '\n' 30 | the 31 | day 32 | is 33 | sunny 34 | the 35 | the 36 | the 37 | sunny 38 | is 39 | is 40 | ➜ Desktop cat Words.txt| tr -s ' ' '\n' | sort 41 | day 42 | is 43 | is 44 | is 45 | sunny 46 | sunny 47 | the 48 | the 49 | the 50 | the 51 | ➜ Desktop cat Words.txt| tr -s ' ' '\n' | sort | uniq -c 52 | 1 day 53 | 3 is 54 | 2 sunny 55 | 4 the 56 | ➜ Desktop cat Words.txt| tr -s ' ' '\n' | sort | uniq -c | sort -r 57 | 4 the 58 | 3 is 59 | 2 sunny 60 | 1 day 61 | ➜ Desktop cat Words.txt| tr -s ' ' '\n' | sort | uniq -c | sort -r | awk '{print $2, $1}' 62 | the 4 63 | is 3 64 | sunny 2 65 | day 1 66 | 67 | ''' 68 | -------------------------------------------------------------------------------- /sorting algo/Bead_sort.py: -------------------------------------------------------------------------------- 1 | """ 2 | Bead sort only works for sequences of nonegative integers. 3 | https://en.wikipedia.org/wiki/Bead_sort 4 | """ 5 | 6 | 7 | def bead_sort(sequence: list) -> list: 8 | """ 9 | >>> bead_sort([6, 11, 12, 4, 1, 5]) 10 | [1, 4, 5, 6, 11, 12] 11 | >>> bead_sort([9, 8, 7, 6, 5, 4 ,3, 2, 1]) 12 | [1, 2, 3, 4, 5, 6, 7, 8, 9] 13 | >>> bead_sort([5, 0, 4, 3]) 14 | [0, 3, 4, 5] 15 | >>> bead_sort([8, 2, 1]) 16 | [1, 2, 8] 17 | >>> bead_sort([1, .9, 0.0, 0, -1, -.9]) 18 | Traceback (most recent call last): 19 | ... 20 | TypeError: Sequence must be list of nonnegative integers 21 | >>> bead_sort("Hello world") 22 | Traceback (most recent call last): 23 | ... 24 | TypeError: Sequence must be list of nonnegative integers 25 | """ 26 | if any(not isinstance(x, int) or x < 0 for x in sequence): 27 | raise TypeError("Sequence must be list of nonnegative integers") 28 | for _ in range(len(sequence)): 29 | for i, (rod_upper, rod_lower) in enumerate(zip(sequence, sequence[1:])): 30 | if rod_upper > rod_lower: 31 | sequence[i] -= rod_upper - rod_lower 32 | sequence[i + 1] += rod_upper - rod_lower 33 | return sequence 34 | 35 | 36 | if __name__ == "__main__": 37 | assert bead_sort([5, 4, 3, 2, 1]) == [1, 2, 3, 4, 5] 38 | assert bead_sort([7, 9, 4, 3, 5]) == [3, 4, 5, 7, 9] 39 | -------------------------------------------------------------------------------- /sorting algo/DNF_sort.py: -------------------------------------------------------------------------------- 1 | # program to sort 0's, 1's and 2's in order 2 | 3 | def dnf_sort(arr): 4 | if not len(arr): 5 | return [] 6 | n = len(arr) 7 | count_1, count_2, count_3 = 0, 0, 0 8 | for i in arr: 9 | if i == 0: 10 | count_1 += 1 11 | elif i == 1: 12 | count_2 += 1 13 | else: 14 | count_3 += 1 15 | 16 | i = 0 17 | while count_1: 18 | arr[i] = 0 19 | i += 1 20 | count_1 -= 1 21 | 22 | while count_2: 23 | arr[i] = 1 24 | i += 1 25 | count_2 -= 1 26 | 27 | while count_3: 28 | arr[i] = 2 29 | i += 1 30 | count_3 -= 1 31 | return arr 32 | 33 | if __name__ == '__main__': 34 | arr = [0, 1, 1, 0, 1, 2, 1, 2, 0, 0, 0, 1] 35 | print(dnf_sort(arr)) 36 | -------------------------------------------------------------------------------- /sorting algo/bubble_sort.py: -------------------------------------------------------------------------------- 1 | from sys import stdin, stdout 2 | def bubble_sort(arr, n): 3 | for i in range(n): 4 | flag = False 5 | for j in range(n - i - 1): 6 | if arr[j] > arr[j + 1]: 7 | arr[j], arr[j + 1] = arr[j + 1], arr[j] 8 | flag = True 9 | 10 | if not flag: 11 | break 12 | for i in arr: 13 | stdout.write(f"{i}\n") 14 | 15 | if __name__ == '__main__': 16 | arr = [-90, 12, -60, 900, 0] 17 | bubble_sort(arr, len(arr)) 18 | -------------------------------------------------------------------------------- /sorting algo/bucket_sort.py: -------------------------------------------------------------------------------- 1 | import random 2 | def insertion_sort(arr): 3 | for i in range(len(arr)): 4 | j = i 5 | while j > 0 and arr[j] < arr[j - 1]: 6 | arr[j], arr[j - 1] = arr[j - 1], arr[j] 7 | j -= 1 8 | return arr 9 | 10 | def bucket_sort(x): 11 | # create buckets and initialize 12 | slot_num = 10 13 | arr = [[] for _ in range(slot_num)] 14 | # putting items into correct buckets 15 | for j in x: 16 | index_b = int(slot_num * j) 17 | arr[index_b].append(j) 18 | 19 | # sorting each buckets individually 20 | for i in range(len(arr)): 21 | arr[i] = insertion_sort(arr[i]) 22 | # putting all the elements from buckets back into original array in sorted order 23 | k = 0 24 | for i in range(slot_num): 25 | for j in range(len(arr[i])): 26 | x[k] = arr[i][j] 27 | return x 28 | 29 | 30 | if __name__ == '__main__': 31 | arr = [0.897, 0.565, 0.656, 0.1234, 0.665, 0.3434] 32 | print(f"unsorted array : {arr}") 33 | print(bucket_sort(f"sorted array : {bucket_sort(arr)}")) 34 | -------------------------------------------------------------------------------- /sorting algo/insertion.py: -------------------------------------------------------------------------------- 1 | import random 2 | def insertion_sort(arr): 3 | for i in range(len(arr)): 4 | j = i 5 | while j > 0 and arr[j] < arr[j - 1]: 6 | arr[j], arr[j - 1] = arr[j - 1], arr[j] 7 | j -= 1 8 | return arr 9 | 10 | if __name__ == '__main__': 11 | insertion_sort([random.randint(1, 100) for x in range(100)]) 12 | -------------------------------------------------------------------------------- /sorting algo/merge_sort.py: -------------------------------------------------------------------------------- 1 | def Merge(arr, left, mid, right): 2 | temp_arr = [0] * (right - left + 1) 3 | print(temp_arr) 4 | i, j = left, mid + 1 5 | k = left 6 | # looping over the both the arrays 7 | while(i <= mid and j <= right): 8 | # if first array element is smaller 9 | if arr[i] < arr[j]: 10 | temp_arr[k] = arr[i] 11 | i += 1 12 | # if second array element is smaller 13 | else: 14 | temp_arr[k] = arr[j] 15 | j += 1 16 | k += 1 17 | # executes copying if remaining elements are from array 1 18 | while i <= mid: 19 | temp_arr[k] = arr[i] 20 | i += 1 21 | k += 1 22 | # executes copying if remaining elements are from array 2 23 | while j <= right: 24 | temp_arr[k] = temp_arr[j] 25 | k += 1 26 | j += 1 27 | 28 | for i in range(left, right + 1): 29 | arr[i] = temp_arr[i] 30 | 31 | 32 | def MergeSort(arr, l, r): 33 | if l < r: 34 | m = l + (r - l) // 2 35 | MergeSort(arr, l, m) 36 | MergeSort(arr, m + 1, r) 37 | 38 | Merge(arr, l, m, r) 39 | 40 | if __name__ == '__main__': 41 | arr = [1, 5, 9, 10, 15, 20, 2, 3, 8, 13] 42 | MergeSort(arr, 0, len(arr) - 1) 43 | print(arr) 44 | -------------------------------------------------------------------------------- /sorting algo/quick_sort.py: -------------------------------------------------------------------------------- 1 | # implementing randomized version of QuickSort 2 | import random 3 | def partition(arr,low,high): 4 | i = low - 1 5 | pivot = arr[high] 6 | for j in range(low , high): 7 | if arr[j] < pivot: 8 | i = i + 1 9 | arr[i], arr[j] = arr[j], arr[i] 10 | 11 | arr[i + 1], arr[high] = arr[high], arr[i + 1] 12 | return i + 1 13 | 14 | def partitionr(arr, low, high): 15 | randpivot = random.randrange(low, high) 16 | arr[low], arr[randpivot] = arr[randpivot], arr[low] 17 | return partition(arr, low, high) 18 | 19 | def quicksort(arr, low, high): 20 | if low < high: 21 | pi = partitionr(arr, low, high) 22 | print(pi) 23 | print("going for left sub array") 24 | quicksort(arr, low, pi - 1) 25 | print("going for right sub array") 26 | quicksort(arr, pi + 1, high) 27 | 28 | if __name__ == '__main__': 29 | arr = [10, 7, 8, 9, 1, 5] 30 | quicksort(arr, 0, 5) 31 | print(arr) 32 | -------------------------------------------------------------------------------- /sorting algo/radix_sort.py: -------------------------------------------------------------------------------- 1 | import random 2 | def counting_sort(arr, exp): 3 | d = 10 # since we know only 0-9 digits can be possible at any nth place of digit 4 | output = [0 for _ in range(len(arr))] 5 | count = [0 for _ in range(d)] 6 | for i in range(len(arr)): 7 | index = arr[i] // exp # this extracts the required part of digit 8 | count[index % 10] += 1 # index % 10 retreives last digit 9 | 10 | for i in range(1, d): 11 | count[i] += count[i - 1] 12 | 13 | for i in range(len(arr)-1, -1, -1): 14 | index = arr[i] // exp 15 | output[count[index % 10] - 1] = arr[i] 16 | count[index % 10] -= 1 17 | 18 | for i in range(len(arr)): 19 | arr[i] = output[i] 20 | 21 | def radix_sort(arr): 22 | # we go for dividing and taking modulus starting from 1, 10, 100, 1000 till we hit 0 23 | exp = 1 24 | m = max(arr) 25 | while m/exp > 0: 26 | counting_sort(arr, exp) 27 | exp *= 10 28 | return arr 29 | 30 | if __name__ == '__main__': 31 | arr = [random.randint(0, 10000) for _ in range(10)] 32 | print(f"unsorted array : {arr}") 33 | print(f"sorted array : {radix_sort(arr)}") 34 | -------------------------------------------------------------------------------- /sorting algo/selection_sort.py: -------------------------------------------------------------------------------- 1 | def selection_sort(arr, n): 2 | for i in range(n): 3 | min_idx = i 4 | for j in range(i + 1, n): 5 | if arr[j] < arr[min_idx]: 6 | min_idx = j 7 | 8 | if i != min_idx: 9 | arr[i], arr[min_idx] = arr[min_idx], arr[i] 10 | 11 | return arr 12 | 13 | if __name__ == '__main__': 14 | arr = [-90, 121, 0, -60, 1000] 15 | selection_sort(arr, 5) 16 | print(arr) 17 | -------------------------------------------------------------------------------- /sorting algo/sort_0_1_2.py: -------------------------------------------------------------------------------- 1 | # Program to efficietly sort the array containing only 0's, 1's, 2's. 2 | # We simply take two variables and in one pass count all the 0's and 1's. 3 | # Then, already we have size, so we know size - (count1 + count2) will be count for 2's. 4 | # Now, we copy in the array directly one by one that many number of times stored in count1, count2 and size - (count1 + count2). 5 | 6 | def sort_linear(arr, n): 7 | count1, count2 = 0, 0 8 | for i in arr: 9 | if i == 0: 10 | count1 += 1 11 | elif i == 1: 12 | count2 += 1 13 | 14 | k = 0 15 | for i in range(count1): 16 | arr[k] = 0 17 | k += 1 18 | 19 | for i in range(count2): 20 | arr[k] = 1 21 | k += 1 22 | 23 | for i in range(n - (count1 + count2)): 24 | arr[k] = 2 25 | k += 1 26 | 27 | 28 | if __name__ == '__main__': 29 | arr = [0, 1, 2, 1, 2] 30 | sort_linear(arr, 5) 31 | print(arr) 32 | -------------------------------------------------------------------------------- /sorting algo/wave_sort.py: -------------------------------------------------------------------------------- 1 | # program to generate wave type of sort known as wave sort, which satisfies following condition : 2 | # arr[0] >= arr[1] <= arr[2] >= arr[3] <= arr[4] >= ….. 3 | # IDEA: Logic is to fix all the even indices such that it gets bigger than its either adjacent elements (both sides) except first and last, 4 | # the way to do is to traverse over the array and check if current even index element is smaller than previous one, then swap these both and 5 | # similarly if it is smaller than its next element then, swap those, all other elements are taken care of. 6 | # Time : 0(N), space: 0(1) 7 | 8 | # function for sorting the array in wave form : B-S-B-S-B.... 9 | def wave_sort(arr): 10 | n = len(arr) 11 | for i in range(0, n, 2): 12 | 13 | if i > 0 and arr[i] < arr[i - 1]: 14 | arr[i], arr[i - 1] = arr[i - 1], arr[i] 15 | if i < n - 1 and arr[i] < arr[i + 1]: 16 | arr[i], arr[i + 1] = arr[i + 1], arr[i] 17 | 18 | return arr 19 | 20 | if __name__ == '__main__': 21 | assert wave_sort(arr = [1, 3, 4, 2, 7, 5]) == [3, 1, 4, 2, 7, 5] 22 | assert wave_sort(arr = [10, 90, 49, 2, 1, 5, 23]) == [90, 10, 49, 1, 5, 2, 23] 23 | -------------------------------------------------------------------------------- /stacks, queues, dequees/__pycache__/deques.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/souravs17031999/100dayscodingchallenge/d05966f3e6875a5ec5a8870b9d2627be570d18d9/stacks, queues, dequees/__pycache__/deques.cpython-37.pyc -------------------------------------------------------------------------------- /stacks, queues, dequees/__pycache__/stacks.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/souravs17031999/100dayscodingchallenge/d05966f3e6875a5ec5a8870b9d2627be570d18d9/stacks, queues, dequees/__pycache__/stacks.cpython-37.pyc -------------------------------------------------------------------------------- /stacks, queues, dequees/generate_binary_strings.py: -------------------------------------------------------------------------------- 1 | # Program for generating binary strings of given value of "n". 2 | # --------------------------------------------------------------------------------------------------------------------------- 3 | # Examples: 4 | # 5 | # Input: n = 2 6 | # Output: 1, 10 7 | # 8 | # Input: n = 5 9 | # Output: 1, 10, 11, 100, 101 10 | # --------------------------------------------------------------------------------------------------------------------------- 11 | # Now, we can check for this using simply bin() function in python but we should not use any in-built function here. 12 | # So, we are going to use queue here. 13 | # --------------------------------------------------------------------------------------------------------------------------- 14 | from collections import deque 15 | def generate_binary(n): 16 | 17 | queue = deque() 18 | queue.append("1") 19 | 20 | while n > 0: 21 | 22 | n -= 1 23 | curr = queue.popleft() 24 | print(curr, end = " ") 25 | queue.append(curr + "0") 26 | queue.append(curr + "1") 27 | 28 | generate_binary(5) 29 | -------------------------------------------------------------------------------- /stacks, queues, dequees/queue_inbuilt.py: -------------------------------------------------------------------------------- 1 | # Program for using inbuilt function for building queue 2 | # deque is preferable for default use as it's compiler optimized 3 | 4 | from collections import deque 5 | from queue import Queue as Q 6 | from multiprocessing import Queue as MPQ 7 | 8 | def queue1(): 9 | q = deque() 10 | q.append(0) 11 | q.append(1) 12 | q.append(2) 13 | q.pop() 14 | 15 | def queue2(): 16 | q = Q() 17 | q.put(0) 18 | q.put(1) 19 | q.put(2) 20 | q.get() 21 | 22 | def queue3(): 23 | q = MPQ() 24 | q.put(0) 25 | q.put(1) 26 | q.put(2) 27 | q.get() 28 | 29 | if __name__ == '__main__': 30 | queue1() 31 | queue2() 32 | queue3() 33 | -------------------------------------------------------------------------------- /stacks, queues, dequees/reverse_stack.py: -------------------------------------------------------------------------------- 1 | # Program to reverse the stack using one extra stack 2 | # IDEA: logic is to use temporary stack and for each element time, one by one we can take out a top element from stack 3 | # and then append other elements left to another temporary stack and then , push the element into original stack 4 | # Now, we can again put back all other elements from temporary stack to original stack 5 | # We can keep doing it until we have done it for all the elements once 6 | # TIME : 0(N^2), N IS LENGTH OF STACK SIZE, SPACE : 0(N) 7 | 8 | from collections import deque 9 | 10 | def reverse_stack(stack): 11 | n = len(stack) 12 | temp = deque() 13 | for i in range(0, n): 14 | 15 | top = stack.pop() 16 | 17 | for j in range(0, n - i - 1): 18 | temp.append(stack.pop()) 19 | 20 | stack.append(top) 21 | 22 | for j in range(0, n - i - 1): 23 | stack.append(temp.pop()) 24 | 25 | if __name__ == '__main__': 26 | 27 | stack = deque() 28 | stack.append('a') 29 | stack.append('b') 30 | stack.append('c') 31 | stack.append('d') 32 | reverse_stack(stack) 33 | print(stack) 34 | -------------------------------------------------------------------------------- /stacks, queues, dequees/stack_inbuilt.py: -------------------------------------------------------------------------------- 1 | # Program for using inbuilt function for building stack 2 | # deque is preferable for default use as it's compiler optimized 3 | 4 | from collections import deque 5 | from queue import LifoQueue 6 | 7 | def stack1(): 8 | stack = deque() 9 | stack.append(0) 10 | stack.append(1) 11 | stack.pop() 12 | 13 | def stack2(): 14 | stack = LifoQueue() 15 | stack.put(0) 16 | stack.put(1) 17 | stack.put(2) 18 | stack.get() 19 | 20 | if __name__ == '__main__': 21 | stack1() 22 | stack2() 23 | -------------------------------------------------------------------------------- /strings/anagram_grouping.py: -------------------------------------------------------------------------------- 1 | # program to group same type of anagrams together 2 | # logic is to get tuple of all the words in a list in sorted order and 3 | # add it to the list. 4 | # like we get tuple for one of the word : ('e', 'a', 't') 5 | # and then other words will be appended to the same order of 6 | # lists like "ate" will be also ('e', 'a', 't'). 7 | 8 | # ------------------------------------------------------------------------------------------- 9 | # TIME : 0(M*N*log(N)) 10 | 11 | # Using Trie can improve time complexity with extra cost of space. 12 | 13 | import collections 14 | def main(strs): 15 | ans = collections.defaultdict(list) 16 | for s in strs: 17 | ans[tuple(sorted(s))].append(s) 18 | return ans.values() 19 | 20 | if __name__ == '__main__': 21 | l = ['eat', 'tea', 'tan', 'ate', 'nat', 'bat'] 22 | main(l) 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /strings/binary_strings_addition.py: -------------------------------------------------------------------------------- 1 | # Program for adding two binary strings together without using any int() conversion 2 | # Idea is same as was done for digit sum using arrays , 3 | # We add one by one from last index of both string, and take divison and modulo as our carry, result respectively. 4 | 5 | def binary_add(s1, s2): 6 | 7 | max_len = max(len(s1), len(s2)) 8 | s1 = s1.zfill(max_len) 9 | s2 = s2.zfill(max_len) 10 | 11 | result = '' 12 | carry = 0 13 | for i in range(max_len - 1, -1, -1): 14 | r = carry 15 | r += 1 if s1[i] == '1' else 0 16 | r += 1 if s2[i] == '1' else 0 17 | result = ('1' if r % 2 == 1 else '0') + result 18 | carry = 0 if r < 2 else 1 19 | 20 | if carry !=0 : result = '1' + result 21 | return result.zfill(max_len) 22 | 23 | 24 | print(binary_add('1101', '100')) 25 | -------------------------------------------------------------------------------- /strings/camel_case_pattern_matching.py: -------------------------------------------------------------------------------- 1 | # Program to match the words in the dictionery matching the Camel Case pattern. 2 | # Input: 3 | # 2 4 | # 8 5 | # Hi Hello HelloWorld HiTech HiGeek HiTechWorld HiTechCity HiTechLab 6 | # HA 7 | # 3 8 | # WelcomeGeek WelcomeToGeeksForGeeks GeeksForGeeks 9 | # WTG 10 | # --------------------------------------------------------------------------------------------------- 11 | 12 | #code 13 | 14 | from sys import stdin, stdout 15 | 16 | def camel_case_match(word, n, pattern): 17 | 18 | count = {} 19 | 20 | for i in range(n): 21 | 22 | res = [] 23 | for j in range(len(word[i])): 24 | 25 | if ord(word[i][j]) >= 65 and ord(word[i][j]) <= 90: 26 | res.append(word[i][j]) 27 | temp = "".join(res) 28 | if temp not in count: 29 | count[temp] = [word[i]] 30 | else: 31 | count[temp].append(word[i]) 32 | 33 | res = [] 34 | for key, value in count.items(): 35 | if key == pattern: 36 | for i in value: 37 | res.append(i) 38 | 39 | print(count) 40 | return " ".join(res) 41 | 42 | 43 | if __name__ == '__main__': 44 | 45 | print(camel_case_match(['Hi', 'Hello', 'HelloWorld', 'HiTech', 'HiGeek', 'HiTechWorld', 'HiTechCity', 'HiTechLab'], 8, 'HT')) 46 | -------------------------------------------------------------------------------- /strings/check_valid_palindrome_string.py.py: -------------------------------------------------------------------------------- 1 | # Program to check whether string is a valid palindrome, ignoring any other character than alphanumeric, and ignoring cases. 2 | # Logic is to simply use two pointer technique, ignoring any non valid character , only comparing alnum chars , and returning true or false. 3 | # Time complexity is 0(N) and space complexity is 0(1) 4 | def valid_palindrome(s): 5 | # assuming empty string is valid palindrome 6 | if len(s) == 0: 7 | return True 8 | # assinging two pointers for start and end variables 9 | start = 0 10 | end = len(s) - 1 11 | # traversing the string from both end 12 | while start < end: 13 | # ignore the invalid char from start 14 | if not s[start].isalnum(): 15 | start += 1 16 | # ignore the invalid char from end 17 | elif not s[end].isalnum(): 18 | end -= 1 19 | # now compare valid chars 20 | else: 21 | if s[start].lower() != s[end].lower(): 22 | return False 23 | else: 24 | start += 1 25 | end -= 1 26 | # every valid char is same, from both sides, thereby returning True at this point 27 | return True 28 | 29 | # main function 30 | if __name__ == '__main__': 31 | assert valid_palindrome("race a car") == False 32 | assert valid_palindrome("A man, a plan, a canal: Panama") == True 33 | assert valid_palindrome("") == True 34 | assert valid_palindrome("A") == True 35 | -------------------------------------------------------------------------------- /strings/difference_between_ascii_codes.py: -------------------------------------------------------------------------------- 1 | # Program to insert ascii-codes for every pair of chars for a given string. 2 | 3 | def DiffASCII(s): 4 | print(s[0], end = "") 5 | for i in range(1, len(s)): 6 | print(ord(s[i]) - ord(s[i - 1]), end = "") 7 | print(s[i], end = "") 8 | 9 | if __name__ == '__main__': 10 | s = "acb" 11 | DiffASCII(s) 12 | -------------------------------------------------------------------------------- /strings/generate_all_valid_ip.py: -------------------------------------------------------------------------------- 1 | # Program for generating all the valid IP's from the given input string. 2 | # Here, important thing is to we can use recursion + backtracking or iterative, both will take almost same complexity but main 3 | # point is that overall complexity will be constant 0(1) as total ip addresses generated will be constant 0(2^32) ip addresses. 4 | # ---------------------------------------------------------------------------------------------------------------------------------- 5 | 6 | def is_valid(s): 7 | 8 | s = s.split(".") 9 | for i in s: 10 | if len(i) > 3 or int(i) < 0 or int(i) > 255: 11 | return False 12 | if len(i) > 1 and int(i) == 0: 13 | return False 14 | if len(i) > 1 and int(i) != 0 and i[0] == '0': 15 | return False 16 | return True 17 | 18 | def convert(s): 19 | 20 | n = len(s) 21 | if n > 12: 22 | return [] 23 | 24 | s_new = s 25 | res = [] 26 | for i in range(1, n - 2): 27 | for j in range(i + 1, n - 1): 28 | for k in range(j + 1, n): 29 | s_new = s_new[:k] + '.' + s_new[k:] 30 | s_new = s_new[:j] + '.' + s_new[j:] 31 | s_new = s_new[:i] + '.' + s_new[i:] 32 | 33 | if is_valid(s_new): 34 | res.append(s_new) 35 | 36 | s_new = s 37 | return res 38 | 39 | A = "25525511135" 40 | # B = "25505011535" 41 | print(convert(A)) 42 | # print(convert(B)) 43 | -------------------------------------------------------------------------------- /strings/implement_atoi.py: -------------------------------------------------------------------------------- 1 | # Program for conversion of string to integer. 2 | # NOTE : 3 | # If no valid conversion could be performed, a zero value is returned. 4 | # 5 | # Note: 6 | # 7 | # Only the space character ' ' is considered as whitespace character. 8 | # Assume we are dealing with an environment which could only store integers within the 32-bit signed integer range: [−231, 231 − 1]. 9 | # If the numerical value is out of the range of representable values, INT_MAX (231 − 1) or INT_MIN (−231) is returned. 10 | # TIME : 0(N), SPACE : 0(1) 11 | 12 | class Solution: 13 | def myAtoi(self, str: str) -> int: 14 | i = 0 15 | sign = 1 16 | result = 0 17 | if len(str) == 0: return 0 18 | 19 | while i < len(str) and str[i] == ' ': 20 | i += 1 21 | 22 | if i < len(str) and (str[i] == '+' or str[i] == '-'): 23 | if str[i] == '+': 24 | sign = 1 25 | else: 26 | sign = -1 27 | i += 1 28 | 29 | while i < len(str) and str[i].isnumeric(): 30 | result = result * 10 + (ord(str[i]) - ord('0')) 31 | i += 1 32 | 33 | return max(-2**31, min(sign * result,2**31-1)) 34 | -------------------------------------------------------------------------------- /strings/longest_prefix_suffix.py: -------------------------------------------------------------------------------- 1 | # Program for computing longest prefix suffix lps array building for the given string. 2 | # Often used as KMP preprocessing part for substring search. 3 | # -------------------------------------------------------------------------------------- 4 | # In Below algorithm, i denotes longest suffix, and j denotes prefix matching with longest suffix. 5 | # We assume pi[i] = 0, and start our algorithm with i = 1, and j = 0. 6 | # And whenever s[i] and s[j] matches, we move both i, and j and set lps[i] = j + 1 as till j it has been matched : substring 0...j - 1 7 | # but when they don't match, we set j at the last longest found prefix as till that has already matched, so we again start from there, 8 | # so for going back to last matched substring, we use lps[j - 1] which tells the exactly same thing. 9 | # TIME : 0(N), N IS LENGTh OF GIVEN STRING, SPACE : 0(N). 10 | # --------------------------------------------------------------------------------------- 11 | 12 | def build_lps(s): 13 | 14 | i, j = 1, 0 15 | n = len(s) 16 | lps = [0] * n 17 | while i < n: 18 | if s[i] == s[j]: 19 | lps[i] = j + 1 20 | j += 1 21 | i += 1 22 | else: 23 | if j != 0: 24 | j = lps[j - 1] 25 | else: 26 | lps[i] = 0 27 | i += 1 28 | 29 | print(lps) 30 | return max(lps) 31 | 32 | if __name__ == '__main__': 33 | print(build_lps("aaaa")) 34 | -------------------------------------------------------------------------------- /strings/max_freq_words_string.py: -------------------------------------------------------------------------------- 1 | # Program to find the most occuring word in the given string array. 2 | # If same frequency, then print in order of input (last string having max same frequency should be printed) 3 | # EX. 4 | # Input: 5 | # 3 6 | # 3 7 | # geeks for geeks 8 | # 2 9 | # hello world 10 | # 3 11 | # world wide fund 12 | # 13 | # Output: 14 | # geeks 15 | # world 16 | # fund 17 | # -------------------------------------------------------------------------------------------------------- 18 | 19 | from sys import stdin, stdout 20 | import sys 21 | from collections import OrderedDict 22 | 23 | def count_max_freq(arr, n): 24 | 25 | count = OrderedDict() 26 | for i in range(n): 27 | if arr[i] in count: 28 | count[arr[i]] += 1 29 | else: 30 | count[arr[i]] = 1 31 | 32 | max_ele, max_idx = None, 0 33 | 34 | for i, j in count.items(): 35 | if j >= max_idx: 36 | max_idx = j 37 | max_ele = i 38 | 39 | return max_ele 40 | 41 | if __name__ == '__main__': 42 | print(count_max_freq(['world', 'wide', 'fund'], 3)) 43 | -------------------------------------------------------------------------------- /strings/print_CamelCase.py: -------------------------------------------------------------------------------- 1 | # Print the camelcase individual words for a given string. 2 | 3 | def CamelPrint(s): 4 | count = 0 5 | for i in range(len(s)): 6 | if ord(s[i]) >= 65 and ord(s[i]) <= 90: 7 | print() 8 | print(s[i], end = "") 9 | else: 10 | print(s[i], end = "") 11 | 12 | if __name__ == '__main__': 13 | s = "IAmACompetitiveProgrammer" 14 | CamelPrint(s) 15 | -------------------------------------------------------------------------------- /strings/remove_duplicates_adjacent.py: -------------------------------------------------------------------------------- 1 | # Program to remove adjacent duplicates in the given string. 2 | # We have to completely remove everything that is in a stream. 3 | # EX. 4 | # "geeksforgeek" => "gksforgk" 5 | # "acaaabbbacdddd" => "acac" 6 | # ------------------------------------------------------------------------------------------------------------- 7 | class Solution: 8 | def removeDuplicates(self, S: str) -> str: 9 | 10 | if not S: 11 | return 12 | 13 | stack = [S[0]] 14 | for i in range(1, len(S)): 15 | if stack and S[i] == stack[-1]: 16 | stack.pop() 17 | else: 18 | stack.append(S[i]) 19 | 20 | return "".join(stack) 21 | 22 | -------------------------------------------------------------------------------- /strings/remove_spaces.py: -------------------------------------------------------------------------------- 1 | # Program to remove spaces from the given string and return the modified string. 2 | 3 | #code 4 | from sys import stdin, stdout 5 | def remove_space(s, n): 6 | 7 | if n == 0: 8 | return "" 9 | 10 | ptr = 0 11 | while s[ptr] == " ": 12 | ptr += 1 13 | 14 | res = "" 15 | for i in range(ptr, n): 16 | if s[i] == " ": 17 | continue 18 | else: 19 | res += s[i] # more efficient could be : res = "".join(res, s[i]) 20 | 21 | return res 22 | 23 | 24 | if __name__ == '__main__': 25 | t = int(stdin.readline().strip()) 26 | for i in range(t): 27 | s = stdin.readline().strip() 28 | n = len(s) 29 | stdout.write(remove_space(s, n)) 30 | stdout.write("\n") 31 | 32 | -------------------------------------------------------------------------------- /strings/reverse_words_complete.py: -------------------------------------------------------------------------------- 1 | # TIME : 0(N), SPACE : 0(1), 2 | 3 | class Solution: 4 | def reverseWords(self, s: str) -> str: 5 | l = s.split()[::-1] 6 | return " ".join(l) 7 | -------------------------------------------------------------------------------- /strings/second_most_repeated_char.py: -------------------------------------------------------------------------------- 1 | # Program for printing the second most repeated word in the given list of strings. 2 | # Input: 3 | # 2 4 | # 6 5 | # aaa bbb ccc bbb aaa aaa 6 | # 6 7 | # geeks for geeks for geeks aaa 8 | # 9 | # Output: 10 | # bbb 11 | # for 12 | # ---------------------------------------------------------------------------------------- 13 | 14 | #code 15 | from sys import stdin, stdout 16 | 17 | from heapq import heapify, heappop, heappush 18 | 19 | def compute_second_repeated(arr, n): 20 | count = {} 21 | for i in arr: 22 | if i in count: 23 | count[i] += 1 24 | else: 25 | count[i] = 1 26 | 27 | heap = [(-count[i], i) for i in count] 28 | heapify(heap) 29 | 30 | heappop(heap) 31 | return heappop(heap)[1] 32 | 33 | 34 | if __name__ == '__main__': 35 | t = int(stdin.readline().strip()) 36 | for i in range(t): 37 | n = int(stdin.readline().strip()) 38 | s = stdin.readline().strip().split() 39 | stdout.write(compute_second_repeated(s, n)) 40 | stdout.write('\n') 41 | -------------------------------------------------------------------------------- /strings/shift_string.py: -------------------------------------------------------------------------------- 1 | # program to shift strings according to given shift matrix containing direction and steps needed to shift 2 | # 0 means shift left, 1 means right shift and first index of each row of matrix gives direction 3 | # and second index gives steps 4 | # since the constrains are small, so 0(n*n) can be passed here. 5 | 6 | def reverse(s, start, end): 7 | l = list(s) 8 | while(start < end): 9 | l[start], l[end] = l[end], l[start] 10 | start += 1 11 | end -= 1 12 | return "".join(l) 13 | 14 | def left_rotate_reverse(s, k): 15 | print("left roatet") 16 | n = len(s) 17 | k = k % n 18 | s = reverse(s, 0, k-1) 19 | s = reverse(s, k, n-1) 20 | s = reverse(s, 0, n-1) 21 | return s 22 | 23 | def rotate_right_reverse(s, k): 24 | print("right torate") 25 | n = len(s) 26 | k = k % n 27 | s = reverse(s, 0, n - k - 1) 28 | s = reverse(s, n - k, n - 1) 29 | s = reverse(s, 0, n - 1) 30 | return s 31 | 32 | def shift_string(s, shift): 33 | for i in shift: 34 | dir, steps = i[0], i[1] 35 | if not dir: 36 | s = left_rotate_reverse(s, steps) 37 | else: 38 | s = rotate_right_reverse(s, steps) 39 | print(s) 40 | 41 | return s 42 | 43 | # main driver function 44 | if __name__ == '__main__': 45 | shift = [[0, 1], [1, 2]] 46 | s = "abc" 47 | assert shift_string(s, shift) == "cab" 48 | -------------------------------------------------------------------------------- /strings/sort_strings_1.py: -------------------------------------------------------------------------------- 1 | # Program to sort the tuples given first in the order of their second index values. 2 | # Then, sort lexographically according to first index. 3 | # Ex. First index in the tuple here contains "names", second index in the tuple 4 | # here contains "values(salaries)". 5 | 6 | def sort_string(arr, n, x): 7 | arr.sort(key = lambda x : (x[1], x[0]), reverse = True) 8 | print(arr) 9 | 10 | if __name__ == '__main__': 11 | arr = [['Eve', 78], ['Bob', 99], ['Suzy', 86], ['Alice', 86]] 12 | n = len(arr) 13 | x = 79 14 | sort_string(arr, n, x) 15 | -------------------------------------------------------------------------------- /strings/strength_password.py: -------------------------------------------------------------------------------- 1 | # Program to check the strength of password depending on the weights assingned in cicular order from 0-25 inclusive where we know the 2 | # weight_a and then we need to return the strength as the sum of the weights 3 | 4 | def get_strength(s, weight_a): 5 | strength = 0 6 | for i in s: 7 | temp = (ord(i) - ord('a')) + weight_a 8 | if temp > 25: 9 | temp = (temp % 25) - 1 10 | strength += temp 11 | return strength 12 | 13 | if __name__ == '__main__': 14 | assert get_strength("hellomrz", 2) == 91 15 | assert get_strength("aaaaa", 1) == 5 16 | assert get_strength("hackerrank", 10) == 128 17 | assert get_strength("aaaaa", 0) == 0 18 | assert get_strength("abcde", 0) == 10 19 | -------------------------------------------------------------------------------- /strings/string_compression.py: -------------------------------------------------------------------------------- 1 | # program to compress the string in a particular fashion, instead of printing all the repeated chars in a string, 2 | # we need to take only the count of the repeated char and a count of just that char. 3 | 4 | def compress_string(s): 5 | result, count = "", 1 6 | prev = s[0] 7 | for i in range(1, len(s)): 8 | if s[i] == prev: 9 | count += 1 10 | else: 11 | result = f"{result}{str(count)}{prev}" 12 | #result += prev 13 | prev = s[i] 14 | count = 1 15 | 16 | result = f"{result}{str(count)}{prev}" 17 | print(result) 18 | 19 | if __name__ == '__main__': 20 | s = "aaabbccds" 21 | compress_string(s) 22 | -------------------------------------------------------------------------------- /strings/string_hashing_template.py: -------------------------------------------------------------------------------- 1 | # There can really huge number of cases where string hashing is really powerful. 2 | 3 | # polynomial rolling hash function 4 | # hash(s) = sigma(s[i] * p^i) % m 5 | def get_hash(s): 6 | p = 31 # if we are using both ascii lower and upper case letters, then use p = 53. 7 | m = 10 ** 9 + 9 8 | hash_value = 0 9 | power = 1 10 | for i in range(len(s)): 11 | hash_value += (ord(s[i]) * power) % m 12 | power = (power * p) % m 13 | 14 | return hash_value 15 | 16 | # this is a simple hash function 17 | def get_hash(key): 18 | sum = 0 19 | for i in key: 20 | sum += ord(i) 21 | return sum % 10 22 | 23 | if __name__ == '__main__': 24 | print(get_hash("souravkumar")) 25 | -------------------------------------------------------------------------------- /strings/string_ignorance_alternate.py: -------------------------------------------------------------------------------- 1 | # Program to print the string ignoring alternate occuring characters. 2 | # Meaning first time we see the char, we print it, next time we see it, we don't print it. 3 | # Again we print it, and then not print it and keep going on when seeing the same char. 4 | # Input: 5 | # 2 6 | # It is a long day dear. 7 | # Geeks for geeks 8 | # Output: 9 | # It sa longdy ear. 10 | # Geks fore 11 | # --------------------------------------------------------------------------------------------------------------------- 12 | 13 | 14 | #code 15 | from sys import stdin, stdout 16 | 17 | def remove_string(s): 18 | res = [] 19 | count = [0] * 256 20 | 21 | for i in s: 22 | if count[ord(i.lower())]: 23 | count[ord(i.lower())] = 0 24 | else: 25 | count[ord(i.lower())] = 1 26 | res.append(i) 27 | 28 | return "".join(res) 29 | 30 | 31 | if __name__ == '__main__': 32 | 33 | t = int(stdin.readline().strip()) 34 | for i in range(t): 35 | l = stdin.readline().strip() 36 | stdout.write(remove_string(l)) 37 | stdout.write('\n') 38 | -------------------------------------------------------------------------------- /strings/string_lexographical_sort.py: -------------------------------------------------------------------------------- 1 | # Program for sorting the strings lexographically and also we want that if lexographically similar string exist, 2 | # then longer strings should come first before shorter one. 3 | # We may have used simply sort which will sort the strings lexographically, but then we also want to maintain the invariant that longer 4 | # string should come before shorter string if they are lexographically smaller. 5 | # So, we need to make our custom sort function. 6 | # So, we simply apply two for loops (on logic of bubble sort), then while traversing, we can check the above invariant. 7 | 8 | 9 | # to check if swap is necessary and required, by maintaining the invariat that longer string should come before shorter one if they 10 | # are lexographically similar. 11 | def IsSwap(s1, s2): 12 | i = 0 13 | while i < len(s1) and i < len(s2): 14 | if s1[i] > s2[i]: 15 | return 1 16 | elif s1[i] < s2[i]: 17 | return -1 18 | i += 1 19 | 20 | if len(s1) < len(s2): 21 | return 1 22 | else: 23 | return -1 24 | 25 | # simple two for loops for comparison passes similar to bubble sort 26 | def sort_string(arr, n): 27 | 28 | for i in range(n): 29 | for j in range(n - i - 1): 30 | if IsSwap(arr[j], arr[j + 1]) > 0: 31 | arr[j], arr[j + 1] = arr[j + 1], arr[j] 32 | 33 | 34 | if __name__ == '__main__': 35 | arr = ['bat', 'apple', 'batman'] 36 | n = 3 37 | sort_string(arr, n) 38 | print(arr) 39 | -------------------------------------------------------------------------------- /strings/string_remove_common_chars.py: -------------------------------------------------------------------------------- 1 | # Program to remove common charactcers and return the concatenation of two strings with all 2 | # uncommon charactcers. 3 | #Input: 4 | # 2 5 | # aacdb 6 | # gafd 7 | # abcs 8 | # cxzca 9 | # 10 | # Output: 11 | # cbgf 12 | # bsxz 13 | # 14 | # Explanation: 15 | # Testcase 1: 16 | # The common characters of s1 and s2 are: a, d. 17 | # The uncommon characters of s1 and s2 are: c, b, g and f. 18 | # Thus the modified string with uncommon characters concatenated is: cbgf 19 | # ----------------------------------------------------------------------------------- 20 | 21 | #code 22 | from sys import stdin, stdout 23 | 24 | def remove_common(s1, s2): 25 | s = set(s1) 26 | res = [] 27 | common = set() 28 | for i in s2: 29 | if i in s: 30 | common.add(i) 31 | 32 | for i in s1: 33 | if i not in common: 34 | res.append(i) 35 | 36 | for i in s2: 37 | if i not in common: 38 | res.append(i) 39 | 40 | if len(res) == 0: 41 | return str(-1) 42 | 43 | return "".join(res) 44 | 45 | if __name__ == '__main__': 46 | t = int(stdin.readline().strip()) 47 | for i in range(t): 48 | s1 = stdin.readline().strip() 49 | s2 = stdin.readline().strip() 50 | stdout.write(remove_common(s1, s2)) 51 | stdout.write('\n') 52 | -------------------------------------------------------------------------------- /strings/strings_add.py: -------------------------------------------------------------------------------- 1 | # function to calculate sum of digits of both arrays after ensuring that first array is always bigger in size than the other one 2 | # Idea is similar to the array digit sum, just a variation will be in getting exact number from the string without int() conversion, 3 | # and that will be done by using ASCII values using ord() , ord('i') - ord('0') will get us the number i. 4 | 5 | def sum_array(s1, s2, m, n): 6 | i = m - 1 7 | j = n - 1 8 | carry = 0 9 | ans = "" 10 | # adding the digits of both arrays till the lower array index exhausts 11 | while(j >= 0): 12 | temp = (ord(s1[i]) - ord('0')) + (ord(s2[j]) - ord('0')) + carry 13 | ans = f"{ans}{temp%10}" 14 | carry = temp // 10 15 | 16 | j -= 1 17 | i -= 1 18 | 19 | # adding the remaining digits of bigger array and carry 20 | while(i >= 0): 21 | temp = (ord(s1[i]) - ord('0')) + carry 22 | ans = f"{ans}{temp%10}" 23 | carry = temp // 10 24 | i -= 1 25 | 26 | # if there is remaining carry at the last , simply add it to the answer(sum) 27 | if (carry): 28 | ans = f"{ans}{carry}" 29 | 30 | return ans[::-1] 31 | 32 | if __name__ == '__main__': 33 | s1 = '3333311111111111' 34 | s2 = '44422222221111' 35 | m, n = len(s1), len(s2) 36 | if m > n: 37 | print(sum_array(s1, s2, m, n)) 38 | else: 39 | print(sum_array(s2, s1, n, m)) 40 | -------------------------------------------------------------------------------- /strings/subsequence_matcher.py: -------------------------------------------------------------------------------- 1 | # Program to check if one string is an subsequence of other string. 2 | # We simply move from rightmost char to leftmost char or vice-versa, by keeping two pointers to last chars of both strings. 3 | # If both chars match, then we simply move both (decrement). 4 | # If it doesn't match, then we simply move the pointer for second string. 5 | # --------------------------------------------------------------------------------------------- 6 | # TIME : 0(N), N is length of string2. 7 | 8 | def is_subsequence(s1, s2): 9 | 10 | m, n = len(s1), len(s2) 11 | i, j = 0, 0 12 | while i < m and j < n: 13 | if s1[i] == s2[j]: 14 | i += 1 15 | j += 1 16 | 17 | 18 | return i == m 19 | 20 | str1 = "gksrek" 21 | str2 = "geeksforgeeks" 22 | 23 | print("Yes") if is_subsequence(str1,str2) else ("No") 24 | -------------------------------------------------------------------------------- /strings/total_substrings_possible.py: -------------------------------------------------------------------------------- 1 | # So, given a string, compute total possible substrings in it. 2 | # So, we can see for all substrings possible, 3 | # length can be decreased one by one , then to 1, 4 | # n + (n - 1) + (n - 2) + (n - 3) ...... + 2 + 1 5 | # n * (n + 1) // 2 => total substrings possible. 6 | # also, if empty substring is also valid, then 7 | # (n * (n + 1) // 2) + 1 8 | --------------------------------------------------------------------------------