Repository: akgmage/data-structures-and-algorithms Branch: main Commit: a9d5f0c556b7 Files: 894 Total size: 2.3 MB Directory structure: gitextract_p9w8rb_6/ ├── .github/ │ └── ISSUE_TEMPLATE/ │ └── feature_request.md ├── .gitignore ├── 2D Arrays (Matrix)/ │ ├── 2D_sorted.go │ ├── 2D_sorted_array.java │ ├── binary_search.cpp │ ├── count_negatives_in_sorted_matrix.cpp │ ├── matrix_diagonal_sum.cpp │ ├── matrix_wave_print.cpp │ ├── rotate_matrix.cpp │ ├── rotate_matrix.go │ ├── rotate_matrix.java │ ├── rotate_matrix.js │ ├── rotate_matrix.py │ ├── search_element.cpp │ ├── searching_in_sorted_array.cpp │ ├── searching_in_sorted_array.java │ ├── searching_in_sorted_array.js │ ├── searching_in_sorted_array.py │ ├── set_matrix_0.cpp │ ├── set_matrix_0.go │ ├── set_matrix_0.java │ ├── spiral_traverse.cpp │ ├── spiral_traverse.go │ ├── spiral_traverse.java │ ├── spiral_traverse.py │ ├── zigzag_traversal.cpp │ ├── zigzag_traversal.go │ ├── zigzag_traversal.java │ ├── zigzag_traversal.js │ └── zigzag_traversal.py ├── Arrays/ │ ├── Jobassign.cpp │ ├── Maximum_freq.py │ ├── Rotatedarr.cpp │ ├── array_of_products.cpp │ ├── array_of_products.go │ ├── array_of_products.java │ ├── array_of_products.js │ ├── array_of_products.py │ ├── ceaser_cipher.cpp │ ├── ceaser_cipher.go │ ├── ceaser_cipher.java │ ├── ceaser_cipher.js │ ├── ceaser_cipher.py │ ├── dutch_national_flag.cpp │ ├── dutch_national_flag.go │ ├── dutch_national_flag.java │ ├── dutch_national_flag.js │ ├── dutch_national_flag.py │ ├── find_three_largest_integers.js │ ├── find_three_largest_number.cpp │ ├── find_three_largest_numbers.go │ ├── find_three_largest_numbers.java │ ├── find_three_largest_numbers.py │ ├── first_duplicate_value.cpp │ ├── first_duplicate_value.go │ ├── first_duplicate_value.java │ ├── first_duplicate_value.js │ ├── first_duplicate_value.py │ ├── four_sum.py │ ├── insert_interval.cpp │ ├── insert_interval.go │ ├── insert_interval.java │ ├── insert_interval.js │ ├── insert_interval.py │ ├── insert_intervals.cpp │ ├── insert_intervals.go │ ├── is_monotonic.cpp │ ├── is_monotonic.go │ ├── is_monotonic.java │ ├── is_monotonic.js │ ├── is_monotonic.py │ ├── longest_peak.cpp │ ├── longest_peak.go │ ├── longest_peak.java │ ├── longest_peak.js │ ├── longest_peak.py │ ├── majority_element.go │ ├── majority_element.java │ ├── maximum_subarray_sum.cpp │ ├── maximum_subarray_sum.go │ ├── maximum_subarray_sum.py │ ├── merge_intervals.cpp │ ├── merge_intervals.go │ ├── merge_intervals.java │ ├── merge_intervals.js │ ├── merge_intervals.py │ ├── merge_sorted_array.cpp │ ├── merge_sorted_array.java │ ├── merge_sorted_arrays.py │ ├── minimum_size_subarray_sum.java │ ├── move_element_to_end.cpp │ ├── move_element_to_end.go │ ├── move_element_to_end.java │ ├── move_element_to_end.js │ ├── move_element_to_end.py │ ├── non-overlapping intervals.cpp │ ├── pallindromic_permutations.cpp │ ├── sign_of_the_product_of_an_array.cpp │ ├── smallest_difference.cpp │ ├── smallest_difference.go │ ├── smallest_difference.java │ ├── smallest_difference.js │ ├── smallest_difference.py │ ├── sorted_square_array,js │ ├── sorted_square_array.cpp │ ├── sorted_square_array.go │ ├── sorted_square_array.java │ ├── sorted_square_array.py │ ├── string_halves.cpp │ ├── three_largest_no.py │ ├── total_hamming_distance.cpp │ ├── tournament_winner.go │ ├── triplet_sum.cpp │ ├── triplet_sum.go │ ├── triplet_sum.java │ ├── triplet_sum.js │ ├── triplet_sum.py │ └── urlify.go ├── Backtracking/ │ ├── Generate_Parentheses.py │ ├── geenrate_parentheses.go │ ├── n_queen.cpp │ ├── n_queens.java │ └── sudoko_solver.java ├── Binary Search/ │ ├── BinarySearchRecursive.java │ ├── binary_search.cpp │ ├── binary_search.js │ ├── binary_search.py │ ├── binary_search_iterative.go │ ├── binary_search_recursive.go │ ├── binary_serach_first_and_last_occurence.py │ ├── first_and_last_pos.js │ ├── first_and_last_pos_of_element.cpp │ ├── first_and_last_position.java │ ├── first_last_pos.java │ ├── first_occurance.go │ ├── first_occurence.java │ ├── first_true.cpp │ ├── first_true.go │ ├── first_true.java │ ├── first_true.js │ ├── first_true.py │ ├── floor_of_target.java │ ├── index_position.java │ ├── infinity_array.java │ ├── last_occurance.go │ ├── median_of_two_sorted_arrays.cpp │ ├── median_of_two_sorted_arrays.js │ ├── minimum_in_rotated_sorted_array.cpp │ ├── perfect_square.java │ ├── search_in_rotated_sorted_array.cpp │ ├── search_in_sorted_rotated_array.cpp │ ├── search_in_sorted_rotated_array.go │ ├── search_in_sorted_rotated_array.java │ ├── search_in_sorted_rotated_array.py │ ├── search_insert_position.js │ └── square_root.java ├── Bit Manipulation/ │ ├── bloom_filter.cpp │ ├── bloom_filter.py │ ├── count_bits.go │ ├── interesting_array.java │ ├── mod_array.java │ ├── number_of_1_bits.java │ ├── parity_of_a_word.go │ ├── power_of_2.cpp │ ├── reduce_to_zero.cpp │ ├── setbits.cpp │ ├── single_number.java │ └── subarrays_with_bitwise_OR_1.java ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dynamic Programming/ │ ├── best_time_to_buy_and_sell_stock.cpp │ ├── best_time_to_buy_and_sell_stock.go │ ├── best_time_to_buy_and_sell_stock.java │ ├── best_time_to_buy_and_sell_stock.js │ ├── best_time_to_buy_and_sell_stock.py │ ├── climb_stairs.cpp │ ├── climb_stairs.go │ ├── climb_stairs.java │ ├── climb_stairs.js │ ├── climb_stairs.py │ ├── coin_change.cpp │ ├── coin_change.go │ ├── coin_change.java │ ├── coin_change.js │ ├── coin_change.py │ ├── dice_throws.go │ ├── disk_stacking.go │ ├── disk_stacking.py │ ├── distance_of_nearest_0.cpp │ ├── distance_of_nearest_0.py │ ├── distane_of_nearest_0.go │ ├── distane_of_nearest_0.java │ ├── distane_of_nearest_0.js │ ├── edit_distance_dp.cpp │ ├── edit_distance_dp.go │ ├── edit_distance_dp.java │ ├── edit_distance_dp.js │ ├── edit_distance_dp.py │ ├── edit_distance_memoized.cpp │ ├── edit_distance_recursive.cpp │ ├── house_robber.cpp │ ├── juice_bottling.cpp │ ├── juice_bottling.go │ ├── juice_bottling.java │ ├── juice_bottling.js │ ├── juice_bottling.py │ ├── knapsack.cpp │ ├── knapsack.go │ ├── knapsack.java │ ├── knapsack.js │ ├── knapsack.py │ ├── knight_probability_chessboard.cpp │ ├── knight_probability_chessboard.go │ ├── knight_probability_chessboard.java │ ├── knight_probability_chessboard.js │ ├── knight_probability_chessboard.py │ ├── kth_closest_point_to origin.java │ ├── largest_rectangle.java │ ├── largest_rectangle.js │ ├── largest_rectangle.py │ ├── longest_common_subsequence_dp.cpp │ ├── longest_common_subsequence_memoization.cpp │ ├── longest_common_subsequence_recursive.cpp │ ├── longest_increasing_subsequence.cpp │ ├── longest_pallindromic_substring.cpp │ ├── longest_pallindromic_substring.go │ ├── longest_pallindromic_substring.java │ ├── longest_pallindromic_substring.js │ ├── longest_pallindromic_substring.py │ ├── max_path_sum.go │ ├── max_sum_increasing_subsequence.cpp │ ├── max_sum_increasing_subsequence.go │ ├── max_sum_increasing_subsequence.java │ ├── max_sum_increasing_subsequence.js │ ├── max_sum_increasing_subsequence.py │ ├── maximal_sqaure.cpp │ ├── maximal_sqaure.go │ ├── maximal_sqaure.js │ ├── maximal_sqaure.py │ ├── maximal_square.java │ ├── min_cost_travel_in_a_grid.cpp │ ├── min_insertion_steps_for_string_palindrome.java │ ├── min_number_of_jumps.cpp │ ├── min_number_of_jumps.go │ ├── min_number_of_jumps.java │ ├── min_number_of_jumps.js │ ├── min_number_of_jumps.py │ ├── min_steps_to_make_string_palindrome.go │ ├── min_steps_to_make_string_palindrome.java │ ├── min_steps_to_make_string_palindrome.py │ ├── min_steps_to_make_string_pallindrome.cpp │ ├── min_steps_to_make_string_pallindrome.js │ ├── min_steps_to_reduce_a_number_to_one.cpp │ ├── num_ways_to_make_change.cpp │ ├── num_ways_to_make_change.go │ ├── num_ways_to_make_change.java │ ├── num_ways_to_make_change.js │ ├── num_ways_to_make_change.py │ ├── num_ways_to_traverse_graph.cpp │ ├── num_ways_to_traverse_graph.go │ ├── num_ways_to_traverse_graph.js │ ├── num_ways_to_traverse_graph.py │ ├── numbers_in_pi.go │ ├── reconstruct_bst_in_python.py │ ├── rod_cutting_problem_dp.cpp │ ├── rod_cutting_problem_memoized.cpp │ ├── rod_cutting_problem_recursive.cpp │ ├── trapping_rain_water.cpp │ ├── unique_paths_with_obstacles.cpp │ ├── wine_selling_problem_dp.cpp │ ├── wine_selling_problem_memoized.cpp │ └── wine_selling_problem_recursive.cpp ├── Famous Algorithms/ │ ├── N_queen.js │ ├── euclidean_algorithm.java │ ├── euclidean_algorithm.js │ ├── euclidean_algorithm.py │ ├── kadanes_algorithm.c++ │ ├── kadanes_algorithm.go │ ├── kadanes_algorithm.java │ ├── kadanes_algorithm.py │ ├── kadenes_algorithm.js │ ├── kmp.java │ ├── kmp.js │ └── kmp.py ├── Fast and Slow Pointers/ │ ├── happy_number.go │ ├── linked_list_compute_midpoint.cpp │ ├── linked_list_compute_midpoint.java │ ├── linked_list_find_middle.py │ ├── linked_list_floyds_cycle_detection.cpp │ └── linked_list_floyds_cycle_detection.py ├── Graphs/ │ ├── Diljstra.go │ ├── GraphBFS.java │ ├── Graph_Dijstra.java │ ├── Graphs_Dijkstras.py │ ├── Graphs_Ford_Fulkerson.cpp │ ├── Graphs_bfs.cpp │ ├── Graphs_bfs.js │ ├── Graphs_bfs.py │ ├── Graphs_bfs_sssp.cpp │ ├── Graphs_cycle_detection_bfs.cpp │ ├── Graphs_cycle_detection_dfs.cpp │ ├── Graphs_dfs.cpp │ ├── Graphs_dfs.java │ ├── Graphs_dfs.js │ ├── Graphs_dfs.py │ ├── Graphs_dfs_connected_components.cpp │ ├── Graphs_dijkstras.js │ ├── Graphs_flood_fill.cpp │ ├── Graphs_kill_process.cpp │ ├── Graphs_kruskals_algo.cpp │ ├── Graphs_kruskals_algorithm.js │ ├── Graphs_topological_sort_bfs.cpp │ ├── Graphs_topological_sort_dfs.cpp │ ├── a_star_algorithm.py │ ├── adjacency_list.java │ ├── adjacency_matrix.go │ ├── adjacency_matrix.java │ ├── dijkstras.cpp │ ├── dijkstras.go │ ├── dijkstras.java │ ├── dijkstras.py │ ├── dijkstras_heap_based.go │ ├── ford_fulkerson.cpp │ ├── graphs_adjacency_list.cpp │ ├── graphs_adjacency_list_generic.cpp │ ├── graphs_bfs.go │ ├── graphs_dfs.go │ ├── kruskals_algorithm.java │ ├── kruskals_algorithm.py │ ├── remove_island.go │ ├── river_sizes.cpp │ ├── river_sizes.go │ ├── river_sizes.java │ ├── river_sizes.js │ ├── river_sizes.py │ ├── single_cycle_check.cpp │ ├── single_cycle_check.go │ ├── single_cycle_check.java │ ├── single_cycle_check.js │ ├── single_cycle_check.py │ ├── snack_ladders.cpp │ ├── snack_ladders.js │ ├── snack_ladders.py │ ├── snakes_ladders.java │ ├── topological_sort.cpp │ ├── topological_sort.go │ ├── topological_sort.java │ ├── topological_sort.js │ ├── topological_sort.py │ ├── two_colorable.cpp │ ├── two_colorable.go │ ├── two_colorable.java │ ├── two_colorable.js │ ├── two_colorable.py │ ├── union_find.cpp │ ├── union_find.go │ ├── union_find.java │ ├── union_find.js │ ├── union_find.py │ ├── validate_bst.cpp │ ├── validate_bst.go │ ├── validate_bst.java │ ├── validate_bst.js │ ├── validate_bst.py │ ├── youngest_common_ancestor.cpp │ ├── youngest_common_ancestor.go │ ├── youngest_common_ancestor.java │ ├── youngest_common_ancestor.js │ └── youngest_common_ancestor.py ├── Greedy/ │ ├── coin_change.go │ ├── coin_change.java │ ├── coin_change.js │ ├── coin_change.py │ ├── task_assignment.cpp │ ├── task_assignment.go │ ├── task_assignment.java │ ├── task_assignment.js │ └── task_assignment.py ├── Hash Table/ │ ├── Bloomfilter.cpp │ ├── Convert an array to reduced form using hashing.cpp │ ├── Count_Pairs_of_Points_With_Distance_k.py │ ├── HashTable.java │ ├── Longest_substring_without_repeating_characters.py │ ├── Partition.java │ ├── Partition_string.py │ ├── add_first_missing_positive.cpp │ ├── add_first_missing_positive.java │ ├── find_optimal_partition_of_string.cpp │ ├── find_optimal_partition_of_string.go │ ├── first_duplicate_value.go │ ├── first_missing_positve.go │ ├── first_missing_positve.js │ ├── first_missing_positve.py │ ├── first_non_repeated_character.go │ ├── first_repeated_character.go │ ├── four_number_sum.java │ ├── frequency_of_elements.java │ ├── group_anagrams.cpp │ ├── group_anagrams.go │ ├── group_anagrams.java │ ├── group_anagrams.js │ ├── group_anagrams.py │ ├── integer_to_roman.cpp │ ├── integer_to_roman.go │ ├── integer_to_roman.java │ ├── integer_to_roman.js │ ├── integer_to_roman.py │ ├── remove_duplicates.go │ ├── roman_to_integer.cpp │ ├── roman_to_integer.go │ ├── roman_to_integer.java │ ├── roman_to_integer.js │ ├── roman_to_integer.py │ ├── sum_of_unique_elements.java │ ├── three_number_sum.java │ ├── two_sum.cpp │ ├── two_sum.go │ ├── two_sum.java │ ├── two_sum.js │ ├── two_sum.py │ ├── zero_sum_subarray.cpp │ ├── zero_sum_subarray.go │ ├── zero_sum_subarray.java │ ├── zero_sum_subarray.js │ └── zero_sum_subarray.py ├── Heaps/ │ ├── heap.cpp │ ├── heap.go │ ├── heap.java │ ├── heap.js │ ├── heap.py │ └── k_closest.py ├── LICENSE ├── Linked List/ │ ├── Add_two_numbers.py │ ├── Intersection_LL.cpp │ ├── LFU_Cache.cpp │ ├── Linked_List_Component.java │ ├── MiddleOfLinkedList.java │ ├── MiddleofLL.py │ ├── RemoveKthNodeFromEnd.java │ ├── Remove_nth_node_from_end.java │ ├── add_two_numbers.cpp │ ├── add_two_numbers.js │ ├── delete_kth_node.js │ ├── double_linked_list.go │ ├── doubly_linked_list.cpp │ ├── doubly_linked_list.java │ ├── doubly_linked_list.js │ ├── floyds_cycle_detection.cpp │ ├── floyds_cycle_detection.go │ ├── floyds_cycle_detection.java │ ├── intersection_of_two_linked_lists.cpp │ ├── liniked_list_sort_list.cpp │ ├── linked_list.go │ ├── linked_list.js │ ├── linked_list.py │ ├── linked_list_add_two_numbers.py │ ├── linked_list_compute_middle.cpp │ ├── linked_list_compute_middle.java │ ├── linked_list_delete_at_any_pos.cpp │ ├── linked_list_delete_at_head.cpp │ ├── linked_list_delete_at_tail.cpp │ ├── linked_list_delete_node.go │ ├── linked_list_even_or_odd.go │ ├── linked_list_find_length.cpp │ ├── linked_list_insert_at_any_pos.cpp │ ├── linked_list_insert_at_head.cpp │ ├── linked_list_insert_at_tail.cpp │ ├── linked_list_kth_from_end.go │ ├── linked_list_kth_node_from_end.cpp │ ├── linked_list_linear_search.cpp │ ├── linked_list_merge_k_sorted_lists.cpp │ ├── linked_list_merge_two_sorted_linked_list.cpp │ ├── linked_list_mergesort_an_unsorted_list.cpp │ ├── linked_list_middle.go │ ├── linked_list_middle.js │ ├── linked_list_odd_even.cpp │ ├── linked_list_pallindrome.cpp │ ├── linked_list_recursive_search.cpp │ ├── linked_list_remove_dups.cpp │ ├── linked_list_remove_kth_node_from_end.py │ ├── linked_list_remove_nth_node_from_end.cpp │ ├── linked_list_remove_nth_node_from_end.py │ ├── linked_list_reverse.cpp │ ├── linked_list_reverse.js │ ├── linked_list_reverse_recursive.cpp │ ├── linked_list_sum_lists.cpp │ ├── linked_list_swap_nodes_in_pair.cpp │ ├── linked_list_swap_nodes_in_pair_iterative.cpp │ ├── linked_list_take_input.cpp │ ├── linked_list_take_input_as_array_operator_overloading.cpp │ ├── linked_list_take_input_operator_overloading.cpp │ ├── recursive and iterative in singly linkedlist.py │ ├── reverse_linked_list.go │ ├── reverse_linked_list.java │ ├── singly_linked_list.cpp │ ├── singly_linked_list.go │ ├── singly_linked_list.java │ ├── singly_linked_list.py │ ├── sll.go │ └── sort_linked_list.cpp ├── Math/ │ ├── Basic_operations.py │ ├── Count.go │ ├── Factorial.cpp │ ├── Factorial.go │ ├── Factorial.java │ ├── Factorial.py │ ├── Hamming_distance.py │ ├── Hammingdistance.cpp │ ├── K_closest.cpp │ ├── K_closest_points_to_origin.java │ ├── K_closest_points_to_origin.py │ ├── Number_of_Substrings_With_Only_1s.cpp │ ├── PowXn.go │ ├── PowXn.java │ ├── PowXn.py │ ├── Reverse_Integer.cpp │ ├── Sum_four.cpp │ ├── circle.cpp │ ├── count_numbers_with_unique_digits.java │ ├── count_primes.cpp │ ├── count_primes.java │ ├── count_unique_digits.cpp │ ├── count_unique_digits.go │ ├── factorial.py │ ├── factorial_iterative.js │ ├── find_longest_increasing_subsequence.cpp │ ├── hamming_distance.cpp │ ├── is_power_of_two.js │ ├── is_prime.js │ ├── k_closest_points_to_origin.cpp │ ├── missing_number.py │ ├── modular_expo_itera.cpp │ ├── modular_expo_recursive.cpp │ ├── num_points_inside_a_circle.cpp │ ├── num_points_inside_a_circle.js │ ├── num_points_inside_a_circle.py │ ├── num_steps_reduce_to_zero.Go │ ├── num_steps_reduce_to_zero.cpp │ ├── num_steps_reduce_to_zero.java │ ├── num_steps_reduce_to_zero.js │ ├── num_steps_reduce_to_zero.py │ ├── number_of_substrings_with_only_1s.js │ ├── op.js │ ├── palindrome_number.java │ ├── pallindrome_number.cpp │ ├── pallindrome_number.py │ ├── powXn.cpp │ ├── powXn.js │ ├── prime_factorization.cpp │ ├── shuffle_an_array.cpp │ ├── shuffle_an_array.py │ ├── sieve_of_eratosthenes.cpp │ ├── substrings_with_1s.py │ ├── unique_digits.cpp │ ├── unique_digits.java │ ├── unique_digits.py │ └── unique_integers_that_sum_up_to_0.cpp ├── Misc/ │ └── tictactoe.java ├── Patterns/ │ ├── CompleteSquare.java │ ├── HollowPattern.cpp │ ├── HollowPattern.go │ ├── HollowPattern.java │ ├── HollowPattern.py │ ├── Ladder_Pattern.java │ ├── ReverseRightTrianglePattern.java │ ├── RightPascalTriangle.java │ ├── StarDiamond.java │ ├── diamond_pattern.cpp │ ├── diamond_pattern.go │ ├── diamond_pattern.java │ ├── diamond_pattern.py │ ├── numerical_pattern.java │ ├── triangular_pattern.java │ └── triangular_pattern.py ├── Priority Queues/ │ ├── In_Place_HeapSort.cpp │ ├── buy_the_ticket.cpp │ ├── check_max_heap.cpp │ ├── kth_largest_element.cpp │ ├── kth_smallest_element.cpp │ ├── merge_k_sorted_arrays.cpp │ └── running_median.cpp ├── Queue/ │ ├── queue.cpp │ ├── queue.go │ ├── queue.java │ ├── queue.js │ ├── queue.py │ ├── queue_using_stack.js │ ├── queue_using_stacks.go │ ├── queues_using_stacks.py │ └── stack_using_queue.cpp ├── README.md ├── Recursion/ │ ├── calculatextopowern.cpp │ ├── count_digits.cpp │ ├── count_digits.go │ ├── count_zeros.cpp │ ├── count_zeros.go │ ├── factorial.cpp │ ├── factorial.go │ ├── factorial.js │ ├── fibonacci.cpp │ ├── fibonacci.go │ ├── fibonacci_memoization.cpp │ ├── fibonacci_memoization.go │ ├── geometric_sum.cpp │ ├── is_array_sorted.cpp │ ├── is_array_sorted.go │ ├── is_element_present.cpp │ ├── modular_exponentiation.cpp │ ├── multiplication.cpp │ ├── powerset.cpp │ ├── powerset.go │ ├── powerset.java │ ├── powerset.js │ ├── powerset.py │ ├── print_numbers.cpp │ ├── recursive_bubble_sort.cpp │ ├── reverse_print.go │ ├── tower_of_hannoi.cpp │ ├── tower_of_hannoi.java │ ├── tower_of_hannoi.js │ ├── tower_of_hannoi.py │ ├── tower_of_hanoi.go │ └── valid_palindrome_2.py ├── SECURITY.md ├── Scheduling/ │ ├── fcfs.c │ ├── sjf.c │ └── srtf.c ├── Scheduling Algortihms/ │ ├── fcfs.c │ ├── sjf.c │ └── srtf.c ├── Searching/ │ ├── first_duplicate_value.js │ ├── linear_search_string.cpp │ ├── semordnilap.go │ ├── separate_0s_and_1s.go │ └── separate_even_odd.go ├── Sliding Window/ │ ├── find_max.go │ ├── fruits_into_basket.cpp │ ├── fruits_into_basket.go │ ├── fruits_into_basket.java │ ├── fruits_into_basket.js │ ├── fruits_into_basket.py │ ├── longest_repeated_character_replacement.cpp │ ├── longest_repeated_character_replacement.go │ ├── longest_repeated_character_replacement.java │ ├── longest_repeated_character_replacement.js │ ├── longest_repeated_character_replacement.py │ ├── longest_substring_with_k_distinct_chars.go │ ├── longest_substring_with_k_distinct_chars.java │ ├── longest_substring_with_k_distinct_chars.js │ ├── longest_substring_with_k_distinct_chars.py │ ├── longest_substring_without_repeating_characters.go │ ├── max_eraser_value.go │ ├── max_eraser_value.java │ ├── sliding_window_max.java │ ├── sliding_window_max.js │ ├── sliding_window_max.py │ ├── subaray_sum_equals_k.cpp │ ├── subaray_sum_equals_k.go │ ├── subaray_sum_equals_k.java │ ├── subaray_sum_equals_k.js │ ├── subaray_sum_equals_k.py │ ├── subarray_product_less_than_k,js │ ├── subarray_product_less_than_k.cpp │ ├── subarray_product_less_than_k.go │ ├── subarray_product_less_than_k.java │ └── subarray_product_less_than_k.py ├── Stacks/ │ ├── Stack_with_max_API.cpp │ ├── Stacks_using_queues.py │ ├── balanced_parenthesis.go │ ├── is_palindrome.go │ ├── next_greater_element.c++ │ ├── next_greater_element.go │ ├── next_greater_element.java │ ├── next_greater_element.js │ ├── next_greater_element.py │ ├── queue using stack.go │ ├── queue using stack.java │ ├── queue using stack.js │ ├── queue using stack.py │ ├── queue_using_stacks.cpp │ ├── reverse_polish_notation.cpp │ ├── stack.cpp │ ├── stack.go │ ├── stack.java │ ├── stack.js │ ├── stack.py │ ├── stack_array_based.go │ ├── stack_dynamic_array.go │ ├── stack_linked_list.go │ ├── stack_using_queue.cpp │ ├── stack_using_queue.go │ ├── stack_using_queue.java │ ├── stack_using_queue.js │ ├── stack_using_queue.py │ ├── stacks_API.cpp │ ├── stacks_using_queues.java │ ├── stacks_with_queues.cpp │ ├── stacks_with_queues.py │ └── valid_parentheses.cpp ├── Strings/ │ ├── Dp_plaindrome.py │ ├── KMP.go │ ├── Longest_palindromic_substring.py │ ├── MaxConcatenatedstr.java │ ├── Min_palindrome.js │ ├── Valid_palindrome.py │ ├── case_specific_sorting_of_strings.cpp │ ├── check panagram.java │ ├── check_anagrams.java │ ├── check_palindrome.cpp │ ├── check_permutations.cpp │ ├── count_occurances.java │ ├── group_anagrams.cpp │ ├── group_anagrams.go │ ├── group_anagrams.java │ ├── group_anagrams.js │ ├── group_anagrams.py │ ├── is_pallindrome.cpp │ ├── is_pallindrome.go │ ├── is_pallindrome.java │ ├── is_pallindrome.js │ ├── is_pallindrome.py │ ├── is_unique.cpp │ ├── is_unique.go │ ├── is_unique.java │ ├── is_unique.js │ ├── is_unique.py │ ├── length_of_longest_substring.java │ ├── longest palindromic substring.java │ ├── longest_common_prefix.cpp │ ├── longest_string.cpp │ ├── one_edit.cpp │ ├── one_edit.go │ ├── one_edit.java │ ├── one_edit.js │ ├── one_edit.py │ ├── plaindrome_str.cpp │ ├── reverse_string.go │ ├── reverse_words_in_a_string.cpp │ ├── reverse_words_in_a_string.go │ ├── reverse_words_in_a_string.js │ ├── reverse_words_in_string.java │ ├── valid_palindrome.js │ ├── valid_pallindrome2.cpp │ ├── valid_pallindrome2.go │ ├── valid_pallindrome2.java │ ├── valid_pallindrome2.js │ ├── valid_pallindrome2.py │ ├── well_formed_parentheses.cpp │ ├── well_formed_parentheses.java │ └── zigzag_conversion.cpp ├── Trees/ │ ├── AVL/ │ │ └── avl.go │ ├── Binary Search Trees/ │ │ ├── Kth_Largest_Value_In_BST.py │ │ ├── Kth_largest_BST.cpp │ │ ├── Kth_largest_BST.java │ │ ├── Kth_largest_BST.js │ │ ├── Validate_BST.cpp │ │ ├── Validate_BST.java │ │ ├── Validate_BST.js │ │ ├── Validate_BST.py │ │ ├── bst.go │ │ ├── find_closest_value.cpp │ │ ├── find_closest_value.go │ │ ├── find_closest_value.js │ │ ├── find_closest_value.py │ │ ├── insert_into_bst.cpp │ │ ├── kth_largest.go │ │ ├── min_height_BST.cpp │ │ ├── min_height_BST.go │ │ ├── min_height_BST.java │ │ ├── min_height_BST.js │ │ ├── min_height_BST.py │ │ ├── reconstruct_bst.cpp │ │ ├── reconstruct_bst.go │ │ ├── reconstruct_bst.java │ │ ├── reconstruct_bst.js │ │ ├── reconstruct_bst.py │ │ ├── search.cpp │ │ └── validate_bst.go │ ├── Binary Trees/ │ │ ├── Trie.js │ │ ├── bfs.cpp │ │ ├── bfs.go │ │ ├── binary_tree.go │ │ ├── branch_sum.go │ │ ├── build_tree_preorder.cpp │ │ ├── calculate_size.go │ │ ├── count_nodes.cpp │ │ ├── delete.go │ │ ├── dfs.cpp │ │ ├── dfs.go │ │ ├── dfs.java │ │ ├── dfs.js │ │ ├── dfs.py │ │ ├── diameter.cpp │ │ ├── diameter.go │ │ ├── diameter.java │ │ ├── diameter.js │ │ ├── diameter.py │ │ ├── find_branch_sum.go │ │ ├── find_max.go │ │ ├── height.cpp │ │ ├── height.go │ │ ├── height_balanced_binary_tree.cpp │ │ ├── height_balanced_binary_tree.go │ │ ├── height_balanced_binary_tree.java │ │ ├── height_balanced_binary_tree.js │ │ ├── height_balanced_binary_tree.py │ │ ├── inorder_traversal.cpp │ │ ├── inorder_traversal.go │ │ ├── inorder_traversal.java │ │ ├── inorder_traversal.js │ │ ├── inorder_traversal.py │ │ ├── invert.cpp │ │ ├── invert.go │ │ ├── invert.java │ │ ├── invert.js │ │ ├── invert.py │ │ ├── is_symmetric.cpp │ │ ├── is_symmetric.go │ │ ├── is_symmetric.java │ │ ├── is_symmetric.js │ │ ├── is_symmetric.py │ │ ├── level_by_level.cpp │ │ ├── level_order_traversal.cpp │ │ ├── level_order_traversal.go │ │ ├── node_depth.go │ │ ├── postorder_traversal.cpp │ │ ├── preorder_traversal.cpp │ │ ├── remove_leaf_nodes.go │ │ ├── search_an_element.go │ │ └── sum_of_all_nodes.cpp │ ├── Implement_Trie.cpp │ ├── Implement_Trie.py │ ├── MaxpathBinaryTree.cpp │ ├── tree.go │ ├── trie.cpp │ ├── trie.go │ └── trie.java ├── Tries/ │ ├── pattern_matching.cpp │ ├── search_in_tries.cpp │ └── trie_node_class.cpp └── sorting/ ├── Cyclic_Sort.java ├── bubble_sort.cpp ├── bubble_sort.go ├── bubble_sort.java ├── bubble_sort.js ├── bubble_sort.py ├── bucket-sort.js ├── bucket_sort.cpp ├── bucket_sort.go ├── bucket_sort.java ├── bucket_sort.js ├── count_sort.cpp ├── count_sort.java ├── count_sort.js ├── count_sort.py ├── dnf.cpp ├── dnf.go ├── dnf.java ├── dnf.js ├── dnf.py ├── heap_sort.cpp ├── heap_sort.java ├── heap_sort.js ├── heap_sort.py ├── insertion_sort.cpp ├── insertion_sort.go ├── insertion_sort.java ├── insertion_sort.js ├── insertion_sort.py ├── merge_sort.cpp ├── merge_sort.go ├── merge_sort.java ├── merge_sort.js ├── merge_sort.py ├── quick_sort.cpp ├── quick_sort.go ├── quick_sort.java ├── quick_sort.js ├── quick_sort.py ├── radix_sort.cpp ├── radix_sort.go ├── radix_sort.java ├── radix_sort.js ├── radix_sort.py ├── selection_sort.cpp ├── selection_sort.go ├── selection_sort.java ├── selection_sort.js ├── selection_sort.py ├── tim_sort.cpp ├── tim_sort.go ├── tim_sort.java ├── tim_sort.py └── wave_sort.cpp ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest an idea for this project title: '' labels: '' assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. ================================================ FILE: .gitignore ================================================ .vscode .idea *.exe ================================================ FILE: 2D Arrays (Matrix)/2D_sorted.go ================================================ func searchMatrix(matrix [][]int, target int) bool { if len(matrix) == 0 || len(matrix[0]) == 0 { return false } rows, cols := len(matrix), len(matrix[0]) left, right := 0, rows*cols-1 for left <= right { mid := left + (right-left)/2 // Convert the 1D index back to 2D coordinates row, col := mid/cols, mid%cols midValue := matrix[row][col] if midValue == target { return true } else if midValue < target { left = mid + 1 } else { right = mid - 1 } } return false } ================================================ FILE: 2D Arrays (Matrix)/2D_sorted_array.java ================================================ /* The algorithm starts by initializing two pointers, left and right, which represent the start and end indices of the search range. The range spans from the first element (matrix[0][0]) to the last element (matrix[m - 1][n - 1]) in the matrix, where m is the number of rows and n is the number of columns. The function then enters a while loop that continues as long as left is less than or equal to right. In each iteration, it calculates the middle index mid using the formula mid = left + (right - left) / 2. This ensures that mid is always rounded down to the lower integer if the range is odd. The middle index mid is then mapped to its corresponding row and column indices in the matrix using the formulas row = mid / n and col = mid % n. The number num at the middle position (matrix[row][col]) is retrieved and compared with the target. If num is equal to the target, the function returns true as the target is found in the matrix. If num is less than the target, the left pointer is updated to mid + 1 to search in the upper half of the range. If num is greater than the target, the right pointer is updated to mid - 1 to search in the lower half of the range. If the while loop terminates without finding the target, the function returns false. The algorithm achieves a time complexity of O(log(m * n)), where m is the number of rows and n is the number of columns in the matrix, as it uses binary search to efficiently search within the given range. */ #include class Solution { public: /** * Searches for a target integer in a matrix with specific properties. * * @param matrix The input matrix. * @param target The target integer to search for. * @return True if the target is found in the matrix, false otherwise. */ bool searchMatrix(std::vector>& matrix, int target) { int m = matrix.size(); int n = matrix[0].size(); int left = 0; // Start index of the search range int right = m * n - 1; // End index of the search range while (left <= right) { int mid = left + (right - left) / 2; // Calculate middle index int row = mid / n; // Calculate row index int col = mid % n; // Calculate column index int num = matrix[row][col]; // Get the number at the middle position if (num == target) { return true; // Target found in the matrix } else if (num < target) { left = mid + 1; // Search in the upper half of the range } else { right = mid - 1; // Search in the lower half of the range } } return false; // Target not found in the matrix } }; ================================================ FILE: 2D Arrays (Matrix)/binary_search.cpp ================================================ /* You are given an m x n integer matrix matrix with the following two properties: Each row is sorted in non-decreasing order. The first integer of each row is greater than the last integer of the previous row. Given an integer target, return true if target is in matrix or false otherwise. You must write a solution in O(log(m * n)) time complexity. 1.Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3 Output: true 2.Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13 Output: false */ class Solution { bool costumBinarySearch(const vector>& matrix, int target, int low, int high){ int size = matrix[0].size(); while(high - low > 1){ int mid = (high + low) / 2; if(matrix[mid / size][mid % size] < target) { low = mid + 1; } else { high = mid; } } if(matrix[low / size][low % size] == target || matrix[high / size][high % size] == target){ return true; } return false; } public: bool searchMatrix(vector>& matrix, int target) { return costumBinarySearch(matrix, target, 0, matrix.size() * matrix[0].size() - 1); } }; ================================================ FILE: 2D Arrays (Matrix)/count_negatives_in_sorted_matrix.cpp ================================================ /* Given a m x n matrix grid which is sorted in non-increasing order both row-wise and column-wise, return the number of negative numbers in grid. Example 1: Input: grid = [[4,3,2,-1],[3,2,1,-1],[1,1,-1,-2],[-1,-1,-2,-3]] Output: 8 Explanation: There are 8 negatives number in the matrix. Example 2: Input: grid = [[3,2],[1,0]] Output: 0 Constraints: m == grid.length n == grid[i].length 1 <= m, n <= 100 -100 <= grid[i][j] <= 100 Follow up: Could you find an O(n + m) solution? */ class Solution { public: int countNegatives(vector>& grid) { int len = grid.size(), m = grid[0].size(); int ans = 0; int j = 0, i = len - 1; while(j < m && i >= 0){ if(grid[i][j] < 0){ ans += (m - j); i--; } else{ j++; } } return ans; } }; ================================================ FILE: 2D Arrays (Matrix)/matrix_diagonal_sum.cpp ================================================ /* Given a square matrix mat, return the sum of the matrix diagonals. Only include the sum of all the elements on the primary diagonal and all the elements on the secondary diagonal that are not part of the primary diagonal. Example 1: Input: mat = [[1,2,3], [4,5,6], [7,8,9]] Output: 25 Explanation: Diagonals sum: 1 + 5 + 9 + 3 + 7 = 25 Notice that element mat[1][1] = 5 is counted only once. Example 2: Input: mat = [[1,1,1,1], [1,1,1,1], [1,1,1,1], [1,1,1,1]] Output: 8 Example 3: Input: mat = [[5]] Output: 5 Constraints: n == mat.length == mat[i].length 1 <= n <= 100 1 <= mat[i][j] <= 100 */ class Solution { public: int diagonalSum(vector>& mat) { int len = mat.size(), sum = 0; for(int i = 0; i < len; i++){ sum += mat[i][i] + mat[i][len - 1 - i]; } // if length is odd then subtract mid element, because its added twice if(len & 1){ sum -= mat[len / 2][len / 2]; } return sum; } }; ================================================ FILE: 2D Arrays (Matrix)/matrix_wave_print.cpp ================================================ // Prints a matrix in wave form #include using namespace std; void wave_print(int Mat[][10], int R, int C){ for(int j = 0; j < C; j++){ if(j & 1){ for(int i = R - 1; i >= 0; i--){ cout << Mat[i][j] << " "; } } else{ for(int i = 0; i < R; i++){ cout << Mat[i][j] << " "; } } } } int main(){ int Mat[10][10], R, C; cin >> R >> C; for(int i = 0; i < R; i++){ for(int j = 0; j < C; j++){ cin >> Mat[i][j]; } } for(int i = 0; i < R; i++){ for(int j = 0; j < C; j++){ cout << Mat[i][j] << " "; } cout << endl; } wave_print(Mat, R, C); } ================================================ FILE: 2D Arrays (Matrix)/rotate_matrix.cpp ================================================ // Rotate clockwise and anti-clockwise /* This implementation first defines two functions, rotateClockwise and rotateCounterclockwise, to rotate the image by 90 degrees clockwise and counterclockwise, respectively. Each function takes a 2D vector image as input and modifies it in place. The printImage function is used to print the image for demonstration purposes. Finally, the main function initializes an example image, prints it, rotates it by 90 degrees clockwise, prints it again, rotates it by 90 degrees counterclockwise, and prints it one last time to verify that the rotations worked as expected. */ #include #include using namespace std; // Function to rotate the image by 90 degrees clockwise void rotateClockwise(vector>& image) { int n = image.size(); for (int i = 0; i < n / 2; i++) { for (int j = i; j < n - i - 1; j++) { int temp = image[i][j]; image[i][j] = image[n - j - 1][i]; image[n - j - 1][i] = image[n - i - 1][n - j - 1]; image[n - i - 1][n - j - 1] = image[j][n - i - 1]; image[j][n - i - 1] = temp; } } } // Function to rotate the image by 90 degrees counterclockwise void rotateCounterclockwise(vector>& image) { int n = image.size(); for (int i = 0; i < n / 2; i++) { for (int j = i; j < n - i - 1; j++) { int temp = image[i][j]; image[i][j] = image[j][n - i - 1]; image[j][n - i - 1] = image[n - i - 1][n - j - 1]; image[n - i - 1][n - j - 1] = image[n - j - 1][i]; image[n - j - 1][i] = temp; } } } // Function to print the image void printImage(vector>& image) { for (auto row : image) { for (auto pixel : row) { cout << pixel << " "; } cout << endl; } } // Driver code int main() { vector> image = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; cout << "Original Image:" << endl; printImage(image); rotateClockwise(image); cout << "Image rotated by 90 degrees clockwise:" << endl; printImage(image); rotateCounterclockwise(image); cout << "Image rotated by 90 degrees counterclockwise:" << endl; printImage(image); return 0; } ================================================ FILE: 2D Arrays (Matrix)/rotate_matrix.go ================================================ // Rotate clockwise and anti-clockwise /* The rotateClockwise function takes an image array as input and returns the image rotated by 90 degrees clockwise. It creates a new rotated array with the same dimensions as the original image, and then iterates over each element of the image, assigning it to a new position in the rotated array. The rotateCounterClockwise function works similarly, but it rotates the image counterclockwise instead. The n-j-1 and n-i-1 indices are used to swap the rows and columns, respectively. In the main function, we create an example image, and then call the rotateClockwise and rotateCounterClockwise functions to rotate the image by 90 degrees in each direction. Finally, we print the rotated images. */ package main import "fmt" func rotateClockwise(image [][]int) [][]int { n := len(image) rotated := make([][]int, n) for i := 0; i < n; i++ { rotated[i] = make([]int, n) for j := 0; j < n; j++ { rotated[i][j] = image[n-j-1][i] } } return rotated } func rotateCounterClockwise(image [][]int) [][]int { n := len(image) rotated := make([][]int, n) for i := 0; i < n; i++ { rotated[i] = make([]int, n) for j := 0; j < n; j++ { rotated[i][j] = image[j][n-i-1] } } return rotated } func main() { // example image image := [][]int{ {1, 2, 3}, {4, 5, 6}, {7, 8, 9}, } // rotate clockwise rotatedClockwise := rotateClockwise(image) fmt.Println("Rotated Clockwise:") for _, row := range rotatedClockwise { fmt.Println(row) } // rotate counterclockwise rotatedCounterClockwise := rotateCounterClockwise(image) fmt.Println("Rotated Counterclockwise:") for _, row := range rotatedCounterClockwise { fmt.Println(row) } } ================================================ FILE: 2D Arrays (Matrix)/rotate_matrix.java ================================================ // Rotate clockwise and anti-clockwise /* This program takes a 2D array (matrix) and performs two types of 90-degree rotations: clockwise and anti-clockwise. The rotateClockwise method takes a matrix and returns a new matrix with its elements rotated 90 degrees clockwise. The rotateAntiClockwise method takes a matrix and returns a new matrix with its elements rotated 90 degrees anti-clockwise. The printMatrix method is used to print the matrix elements in a readable format. Finally, the main method initializes a test matrix and performs the two rotations on it. */ public class RotateMatrix { // Rotate matrix by 90 degrees clockwise public static int[][] rotateClockwise(int[][] matrix) { int n = matrix.length; int[][] result = new int[n][n]; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { result[i][j] = matrix[n - j - 1][i]; } } return result; } // Rotate matrix by 90 degrees anti-clockwise public static int[][] rotateAntiClockwise(int[][] matrix) { int n = matrix.length; int[][] result = new int[n][n]; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { result[i][j] = matrix[j][n - i - 1]; } } return result; } // Print matrix public static void printMatrix(int[][] matrix) { for (int i = 0; i < matrix.length; i++) { for (int j = 0; j < matrix[i].length; j++) { System.out.print(matrix[i][j] + " "); } System.out.println(); } } // Test program public static void main(String[] args) { int[][] matrix = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} }; System.out.println("Original Matrix:"); printMatrix(matrix); int[][] rotatedClockwise = rotateClockwise(matrix); System.out.println("Matrix after 90 degree clockwise rotation:"); printMatrix(rotatedClockwise); int[][] rotatedAntiClockwise = rotateAntiClockwise(matrix); System.out.println("Matrix after 90 degree anti-clockwise rotation:"); printMatrix(rotatedAntiClockwise); } } ================================================ FILE: 2D Arrays (Matrix)/rotate_matrix.js ================================================ // Rotate clockwise and anti-clockwise /* Here, we first get the number of rows and columns in the matrix. Then, for rotating the matrix clockwise, we iterate over the columns in reverse order and create a new row in the rotated matrix by iterating over each row in the original matrix and adding the corresponding element to the new row. Finally, we add the new row to the rotated matrix. For rotating the matrix anti-clockwise, we again iterate over the columns but this time in the forward order and create a new row in the rotated matrix by iterating over each row in the original matrix in reverse order and adding the corresponding element to the new row. Finally, we add the new row to the rotated matrix. */ function rotateClockwise(matrix) { // Get the number of rows and columns in the matrix const rows = matrix.length; const cols = matrix[0].length; // Create a new matrix to store the rotated matrix const rotated = []; // Iterate over the columns in reverse order and create a new row in the rotated matrix for (let j = cols - 1; j >= 0; j--) { const newRow = []; // Iterate over each row in the matrix and add the corresponding element to the new row for (let i = 0; i < rows; i++) { newRow.push(matrix[i][j]); } // Add the new row to the rotated matrix rotated.push(newRow); } // Return the rotated matrix return rotated; } function rotateAntiClockwise(matrix) { // Get the number of rows and columns in the matrix const rows = matrix.length; const cols = matrix[0].length; // Create a new matrix to store the rotated matrix const rotated = []; // Iterate over the columns in reverse order and create a new row in the rotated matrix for (let j = 0; j < cols; j++) { const newRow = []; // Iterate over each row in the matrix in reverse order and add the corresponding element to the new row for (let i = rows - 1; i >= 0; i--) { newRow.push(matrix[i][j]); } // Add the new row to the rotated matrix rotated.push(newRow); } // Return the rotated matrix return rotated; } ================================================ FILE: 2D Arrays (Matrix)/rotate_matrix.py ================================================ # Rotate clockwise and anti-clockwise ''' The rotate_clockwise() function takes a matrix as input and returns the matrix rotated by 90 degrees clockwise. It does this by first transposing the matrix (swapping the elements across the diagonal), and then reversing each row of the transposed matrix. The rotate_counterclockwise() function takes a matrix as input and returns the matrix rotated by 90 degrees counterclockwise. It also transposes the matrix first, and then reverses each column of the transposed matrix. Both functions use two nested loops to iterate through the matrix and perform the required operations. The n variable represents the size of the matrix, and is used to control the range of the loops. ''' def rotate_clockwise(matrix): """ Function to rotate the given matrix by 90 degrees clockwise """ n = len(matrix) # Transpose the matrix for i in range(n): for j in range(i, n): matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j] # Reverse each row to get the final rotated matrix for i in range(n): matrix[i] = matrix[i][::-1] return matrix def rotate_counterclockwise(matrix): """ Function to rotate the given matrix by 90 degrees counterclockwise """ n = len(matrix) # Transpose the matrix for i in range(n): for j in range(i, n): matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j] # Reverse each column to get the final rotated matrix for i in range(n//2): for j in range(n): matrix[j][i], matrix[j][n-i-1] = matrix[j][n-i-1], matrix[j][i] return matrix ================================================ FILE: 2D Arrays (Matrix)/search_element.cpp ================================================ /* You are given an m x n integer matrix matrix with the following two properties: Each row is sorted in non-decreasing order. The first integer of each row is greater than the last integer of the previous row. Given an integer target, return true if target is in matrix or false otherwise. You must write a solution in O(log(m * n)) time complexity. 1.Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3 Output: true 2.Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13 Output: false */ class Solution { public: bool searchMatrix(vector> &matrix,int target) { int i = 0; int j = matrix[0].size() - 1; while(i < matrix.size() && j >= 0) { if(target == matrix[i][j]) return true; else if(target < matrix[i][j]) j--; else i++; } return false; } }; ================================================ FILE: 2D Arrays (Matrix)/searching_in_sorted_array.cpp ================================================ /* Name : Rajeev Kumar Github username : Tonystart121 Repository name : data-structures-and-algorithms Problem : Searching in 2D sorted array in C++ Issue Number : #273 Problem statement : Given a sorted matrix mat[n][m] and an element ‘x’. Find the position of x in the matrix if it is present, else print -1. Sample testcases: Testcase 1 --> Input: number of rows(n) and column(m).Let n=3,m=3 and value to search x = 20 . arr[n][m] = { {1, 5, 9}, {14, 20, 21}, {30, 34, 43} } Output: found at (1,2); Testcase 2 --> Input: number of rows(n) and column(m).Let n=3,m=4 and value to search x = 43 arr[n][m] = { {1, 5, 9, 11}, {14, 20, 21, 26}, {30, 34, 43, 50} } Output: Found at (2,3); Time Complexity = O(n+m) Space Complexity = O(n+m) Explanation: This code asks the user to enter the number of rows and column in the array and element to find in the array, and then prompts them to enter each element of the array one at a time. Once the array is complete, the code applies the linear search/mapping algorithm to find the element within the array, and then prints the position of that element to the console. Start at the top left corner of the matrix. Compare the target element to the element at the current position. If the target element is equal to the element at the current position, then return the current position. If the target element is less than the element at the current position, then move down one row. If the target element is greater than the element at the current position, then move right one column. Repeat steps 2-5 until the target element is found or the entire matrix has been searched. */ // ----------------------------------------------------------------------------- code begins now! #include using namespace std; int main(){ // enter array rows and column int n,m; cin>>n>>m; // taking input array. int arr[n][m]; for(int i=0;i>arr[i][j]; } } // taking input value to search. int key; cin>>key; // initializing rightmost element as current element. int cr_row = 0, cr_col=m-1; bool ans = false; while(cr_row=0){ // if key==curr output its position if(arr[cr_row][cr_col]==key){ cout<arr[cr_row][cr_col]){ cr_row++; } // if key less than array elements, col decreasing. else if(key>In this code, we have the Solution class with the searchMatrix method that takes a 2D matrix and a target value as parameters. >>It iterates through each element in the matrix and checks if the current element is equal to the target value. >>If a match is found, it returns true. If no match is found after checking all elements, it returns false. >>In the main function, we create an instance of the Solution class and define a sample matrix and target value. >>We then call the searchMatrix method with the provided matrix and target, and store the result in the found variable. >>Finally, we print whether the target was found or not. >>In this example, the output will be "Target found: true" since the target value 5 exists in the matrix. */ class Solution { public boolean searchMatrix(int[][] matrix, int target) { int m = matrix.length; int i = 0; for (i = 0; i < m; i++) { for (int j = 0; j < matrix[i].length; j++) { if (matrix[i][j] == target) return true; } } return false; } public static void main(String[] args) { Solution solution = new Solution(); int[][] matrix = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} }; int target = 5; boolean found = solution.searchMatrix(matrix, target); System.out.println("Target found: " + found); } } ================================================ FILE: 2D Arrays (Matrix)/searching_in_sorted_array.js ================================================ /*Name : Abhinav kumar Github username : Abhinavcode13 Repository name : data-structures-and-algorithms Problem : Search in 2D sorted array in Javascript Issue Number : #272 Problem statement : Explanation of the below Javascript code : The function searchMatrix takes in a matrix (matrix) and a target integer (target) as parameters. It returns true if the target is found in the matrix and false otherwise. The function first checks for edge cases where the matrix is empty or the rows are empty, and immediately returns false in such cases. It then initializes variables for the number of rows (rows) and columns (cols) in the matrix, and sets the left and right indices for the binary search. The binary search is performed using a while loop, with the left and right indices as the condition. Inside the loop, the middle index (mid) is calculated using Math.floor((left + right) / 2), and the corresponding row and column indices are derived from the mid index. The element at the mid index is compared with the target, and based on the comparison, the search space is narrowed down by updating the left and right indices accordingly. If the target is found, the function returns true. If the loop completes without finding the target, the function returns false. The time complexity of this JavaScript solution is O(log(m * n)), as it performs a binary search on a list of size m * n. */ -------------------------------------------------------------------------//Javascript code begins here----------------------------------------------------------------------- /** * Searches for a target integer in a matrix. * @param {number[][]} matrix - The matrix to search in. * @param {number} target - The target integer to search for. * @return {boolean} - True if the target is found, false otherwise. */ function searchMatrix(matrix, target) { // Check for empty matrix or empty rows if (!matrix || matrix.length === 0 || matrix[0].length === 0) { return false; } const rows = matrix.length; const cols = matrix[0].length; let left = 0; let right = rows * cols - 1; // Perform binary search on the matrix while (left <= right) { const mid = Math.floor((left + right) / 2); const row = Math.floor(mid / cols); const col = mid % cols; if (matrix[row][col] === target) { // Target found return true; } else if (matrix[row][col] < target) { // Target is in the right half of the matrix left = mid + 1; } else { // Target is in the left half of the matrix right = mid - 1; } } // Target not found return false; } ================================================ FILE: 2D Arrays (Matrix)/searching_in_sorted_array.py ================================================ ''' Date:28/6/23 About:Search in 2D sorted array in Python Input: You are given an m x n integer matrix matrix with the following two properties: Each row is sorted in non-decreasing order. The first integer of each row is greater than the last integer of the previous row. Given an integer target, return true if target is in matrix or false otherwise. Time Complexity: You must write a solution in O(log(m * n)) time complexity. Space Complexity:O(1) //Example 1: Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3 Output: true //Example 2: Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13 Output: false //Explanation The method takes two parameters: matrix, which represents the sorted matrix, and target, which is the value we want to find in the matrix. The code initializes variables nRows and nCols to store the number of rows and columns in the matrix, respectively. The starting position for the search is set to the bottom-left corner of the matrix (row = nRows - 1, col = 0). The code enters a while loop that continues as long as the current row index (row) is within the bounds of the matrix (0 to nRows - 1 and the current column index (col) is within the bounds of the matrix (0 to nCols - 1). Inside the loop, the code retrieves the value at the current position in the matrix (val = matrix[row][col]). If the current value (val) == value, the method returns True, indicating that the target is found in the matrix. If the current value (val) < target value, it means the target can only be found in the rows above the current row. Therefore, the column index (col) is incremented to move to the next column. If the current value (val) >target value, it means the target can only be found in the columns to the left of the current column. Therefore, the row index (row) is decremented to move to the previous row. If the loop completes without finding the target value, the method returns False. ''' class Solution(object): def searchMatrix(self, matrix, target): nRows = len(matrix) nCols = len(matrix[0]) row = nRows - 1 col = 0 while 0 <= row < nRows and 0 <= col < nCols: val = matrix[row][col] if val == target: return True elif val < target: col += 1 else: row -= 1 return False # Create an instance of the Solution class solution = Solution() # Define the matrix and target value matrix = [[1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34, 60]] target = 3 # Call the searchMatrix method and print the result result = solution.searchMatrix(matrix, target) print(result) ================================================ FILE: 2D Arrays (Matrix)/set_matrix_0.cpp ================================================ /* Problem: You are given a 2D matrix, and the task is to modify it such that if an element in the matrix is zero, you need to set all the elements in the corresponding row and column to zero as well. Solution: To solve this problem, you can follow the steps below: 1. Create two sets, rows and columns, to keep track of the rows and columns that need to be set to zero. 2. Iterate through the matrix row by row, and for each element, if it is zero, add its row index to the rows set and its column index to the columns set. 3. Iterate through the matrix again, and for each element, check if its row index or column index exists in the respective sets (rows or columns). If either index exists, set the element to zero. 4. Finally, iterate through the matrix one more time, and for each row or column index in the rows or columns sets, set all the elements in that row or column to zero. 5. Return the modified matrix. Complexity: Let's assume the matrix has dimensions MxN. Step 2: In this step, we iterate through the matrix once, which takes O(M*N) time. Step 3: In this step, we again iterate through the matrix once, which takes O(M*N) time. Step 4: In this step, we iterate through the rows and columns sets, which contain at most M+N elements. Therefore, this step takes O(M+N) time. Overall, the time complexity of the solution is O(MN + M + N), which can be simplified to O(MN). The space complexity is O(M+N) since we are using two sets to store the rows and columns that need to be set to zero. */ #include #include #include using namespace std; void setZeroes(vector>& matrix) { unordered_set rows; unordered_set columns; // Step 1: Find the rows and columns that need to be set to zero for (int i = 0; i < matrix.size(); i++) { for (int j = 0; j < matrix[0].size(); j++) { if (matrix[i][j] == 0) { rows.insert(i); columns.insert(j); } } } // Step 2: Set the corresponding rows and columns to zero for (int i = 0; i < matrix.size(); i++) { for (int j = 0; j < matrix[0].size(); j++) { if (rows.count(i) || columns.count(j)) { matrix[i][j] = 0; } } } } void printMatrix(const vector>& matrix) { for (const auto& row : matrix) { for (int num : row) { cout << num << " "; } cout << endl; } } int main() { vector> matrix = { {1, 1, 1}, {1, 0, 1}, {1, 1, 1} }; cout << "Original Matrix:" << endl; printMatrix(matrix); setZeroes(matrix); cout << "Modified Matrix:" << endl; printMatrix(matrix); return 0; } ================================================ FILE: 2D Arrays (Matrix)/set_matrix_0.go ================================================ /* Problem: You are given a 2D matrix, and the task is to modify it such that if an element in the matrix is zero, you need to set all the elements in the corresponding row and column to zero as well. Solution: To solve this problem, you can follow the steps below: 1. Create two sets, rows and columns, to keep track of the rows and columns that need to be set to zero. 2. Iterate through the matrix row by row, and for each element, if it is zero, add its row index to the rows set and its column index to the columns set. 3. Iterate through the matrix again, and for each element, check if its row index or column index exists in the respective sets (rows or columns). If either index exists, set the element to zero. 4. Finally, iterate through the matrix one more time, and for each row or column index in the rows or columns sets, set all the elements in that row or column to zero. 5. Return the modified matrix. Complexity: Let's assume the matrix has dimensions MxN. Step 2: In this step, we iterate through the matrix once, which takes O(M*N) time. Step 3: In this step, we again iterate through the matrix once, which takes O(M*N) time. Step 4: In this step, we iterate through the rows and columns sets, which contain at most M+N elements. Therefore, this step takes O(M+N) time. Overall, the time complexity of the solution is O(MN + M + N), which can be simplified to O(MN). The space complexity is O(M+N) since we are using two sets to store the rows and columns that need to be set to zero. */ package main import ( "fmt" ) func setZeroes(matrix [][]int) { rows := make(map[int]bool) columns := make(map[int]bool) // Step 1: Find the rows and columns that need to be set to zero for i := 0; i < len(matrix); i++ { for j := 0; j < len(matrix[0]); j++ { if matrix[i][j] == 0 { rows[i] = true columns[j] = true } } } // Step 2: Set the corresponding rows and columns to zero for i := 0; i < len(matrix); i++ { for j := 0; j < len(matrix[0]); j++ { if rows[i] || columns[j] { matrix[i][j] = 0 } } } } func main() { matrix := [][]int{ {1, 1, 1}, {1, 0, 1}, {1, 1, 1}, } fmt.Println("Original Matrix:") printMatrix(matrix) setZeroes(matrix) fmt.Println("Modified Matrix:") printMatrix(matrix) } func printMatrix(matrix [][]int) { for i := 0; i < len(matrix); i++ { for j := 0; j < len(matrix[0]); j++ { fmt.Print(matrix[i][j], " ") } fmt.Println() } } ================================================ FILE: 2D Arrays (Matrix)/set_matrix_0.java ================================================ /* SET MATRIX ZERO - JAVA LANGUAGE Problem Link1 : https://leetcode.com/problems/set-matrix-zeroes/ Problem Link2 : https://www.codingninjas.com/codestudio/problems/zero-matrix_1171153 Problem: You are given a 2D matrix, and the task is to modify it such that if an element in the matrix is zero, you need to set all the elements in the corresponding row and column to zero as well. E.g- Input: | 1 1 1 | | 1 0 1 | | 1 1 1 | Output: | 1 0 1 | | 0 0 0 | | 1 0 1 | METHOD 1] Brute Force Approach for Set Matrix Zeroes Step 1. Create an array answer of size (n X m) and initialize every element as 1. Step 2. Traverse the matrix array row-wise and set the current row as 0 in answer array if the current row contains an element equals to 0. Step 3. Traverse the matrix array column-wise and set the current column as 0 in answer array if the current column contains an element equals to 0. Step 4. Now traverse the answer array, if the current element is 0, then set this element as 0 in a matrix array. Step 5. Return matrix array Complexity Analysis: Time Complexity = O(n * m) Space Complexity = O(n * m) where n is the number of rows in the matrix and m is the number of columns in the matrix. METHOD 2] Optimal Approach for Set Matrix Zeroes If we assume that -1, do not occur in the matrix array, then Step 1. Traverse the matrix array row-wise and set all the elements of current row which are not 0 as -1, if the current row contains an element equals to 0. Step 2. Traverse the matrix array column-wise and set all the elements of the current column which are not 0 as -1, if the current column contains an element equals to 0. Step 3. Again traverse the matrix and set all the elements that are -1 to 0. Step 4. Return matrix array. Complexity Analysis: Time Complexity = O(n * m) Space Complexity = O(1) where n is the number of rows in the matrix and m is the number of columns in the matrix. */ public class setMatrixZero { // Method 1] Brute Force Approach private static void setZeroes_bruteForce(int[][] matrix, int n, int m) { int answer[][] = new int[n][m]; // Step 1 // Set all elements of answer array as 1 for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { answer[i][j] = 1; // making each element as 1 } } // Traverse row wise --> Step 2 for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { if (matrix[i][j] == 0) { // Set this row as zero in answer array for (int k = 0; k < m; k++) { answer[i][k] = 0; } break; } } } // Traverse column wise --> Step 3 for (int j = 0; j < m; j++) { for (int i = 0; i < n; i++) { if (matrix[i][j] == 0) { // Set this column as 0 in answer array for (int k = 0; k < n; k++) { answer[k][j] = 0; } } } } // Update the elements in matrix array --> Step 4 for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { if (answer[i][j] == 0) { matrix[i][j] = 0; } } } } // Method 2] Optimal Approach private static void setZeroes_optimalMethod(int[][] matrix, int n, int m) { // Traverse row wise for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { if (matrix[i][j] == 0) { // Set all the elements that are not zero as -1 for (int k = 0; k < m; k++) { if (matrix[i][k] != 0) { matrix[i][k] = -1; } } } } } // Traverse column wise for (int j = 0; j < m; j++) { for (int i = 0; i < n; i++) { if (matrix[i][j] == 0) { // Set all the elements that are not zero as -1 for (int k = 0; k < n; k++) { if (matrix[k][j] != 0) { matrix[k][j] = -1; } } } } } // Update all -1 as 0 for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { if (matrix[i][j] == -1) { matrix[i][j] = 0; } } } } public static void main(String[] args) { // Example using Method 1 - Brute Force int[][] matrix1 = new int[][] {{1, 1, 1}, {1, 0, 1}, {1, 1, 1}}; // Defining Matrix int n = matrix1.length; int m = matrix1[0].length; setZeroes_bruteForce(matrix1, n, m); for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { System.out.print(matrix1[i][j] + " "); // Printing Matrix } System.out.println(); } System.out.println("-----------------"); // Example using Method 2 - Optimal int[][] matrix2 = new int[][] {{0, 0, 6, 0}, {1, 4, 9, 0}, {1, 8, 1, 8}}; // Defining Matrix n = matrix2.length; m = matrix2[0].length; setZeroes_optimalMethod(matrix2, n, m); for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { System.out.print(matrix2[i][j] + " "); // Printing Matrix } System.out.println(); } } } ================================================ FILE: 2D Arrays (Matrix)/spiral_traverse.cpp ================================================ /* Write a function that takes in an n x m two-dimensional array (that can be square-shaped when n == m) and returns a one-dimensional array of all the array's elements in spiral order. Spiral order starts at the top left corner of the two-dimensional array, goes to the right, and proceeds in a spiral pattern all the way until every element has been visited. Explanation: The SpiralTraverse function takes a 2D integer array array and returns a 1D integer slice that contains the elements of array traversed in a spiral order, starting from the top-left corner and moving clockwise. The function first initializes an empty slice result to hold the elements of the spiral traversal. If the input array is empty, the function immediately returns the empty result. Next, the function initializes variables startRow, endRow, startCol, and endCol to keep track of the boundaries of the matrix. These variables will be updated as the function traverses the matrix. The function then enters a loop that traverses the matrix in a spiral order. The loop continues as long as startRow <= endRow and startCol <= endCol, which means that there are still elements in the matrix to be traversed. The first step in the loop is to traverse the top row of the matrix from left to right, and append each element to the result slice. The next step is to traverse the rightmost column of the matrix from top to bottom, and append each element to the result slice. If there is more than one row in the matrix, the function then traverses the bottom row of the matrix from right to left, and appends each element to the result slice. If there is only one row left, the loop is broken to avoid duplicating the elements. Finally, if there is more than one column in the matrix, the function traverses the left O(n) time | O(n) space - where n is the total number of elements in the array */ #include using namespace std; #include using namespace std; vector SpiralTraverse(vector> array) { vector result; // vector to store the spiral traversal int rows = array.size(); // number of rows in the input array int cols = array[0].size(); // number of columns in the input array int startRow = 0, endRow = rows - 1; // indices for the start and end row of the current subarray int startCol = 0, endCol = cols - 1; // indices for the start and end column of the current subarray // loop until the entire input array is traversed while (startRow <= endRow && startCol <= endCol) { // traverse the top row from left to right for (int col = startCol; col <= endCol; col++) { result.push_back(array[startRow][col]); } // traverse the right column from top to bottom for (int row = startRow + 1; row <= endRow; row++) { result.push_back(array[row][endCol]); } // traverse the bottom row from right to left for (int col = endCol - 1; col >= startCol; col--) { // check if there is only one row in the subarray if (startRow == endRow) { break; } result.push_back(array[endRow][col]); } // traverse the left column from bottom to top for (int row = endRow - 1; row > startRow; row--) { // check if there is only one column in the subarray if (startCol == endCol) { break; } result.push_back(array[row][startCol]); } // update the indices for the next subarray to be traversed startRow++; endRow--; startCol++; endCol--; } return result; } int main() { vector> array = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; vector result = SpiralTraverse(array); cout << "Spiral traversal: "; for (int i = 0; i < result.size(); i++) { cout << result[i] << " "; } cout << endl; return 0; } ================================================ FILE: 2D Arrays (Matrix)/spiral_traverse.go ================================================ /* Write a function that takes in an n x m two-dimensional array (that can be square-shaped when n == m) and returns a one-dimensional array of all the array's elements in spiral order. Spiral order starts at the top left corner of the two-dimensional array, goes to the right, and proceeds in a spiral pattern all the way until every element has been visited. Explanation: The SpiralTraverse function takes a 2D integer array array and returns a 1D integer slice that contains the elements of array traversed in a spiral order, starting from the top-left corner and moving clockwise. The function first initializes an empty slice result to hold the elements of the spiral traversal. If the input array is empty, the function immediately returns the empty result. Next, the function initializes variables startRow, endRow, startCol, and endCol to keep track of the boundaries of the matrix. These variables will be updated as the function traverses the matrix. The function then enters a loop that traverses the matrix in a spiral order. The loop continues as long as startRow <= endRow and startCol <= endCol, which means that there are still elements in the matrix to be traversed. The first step in the loop is to traverse the top row of the matrix from left to right, and append each element to the result slice. The next step is to traverse the rightmost column of the matrix from top to bottom, and append each element to the result slice. If there is more than one row in the matrix, the function then traverses the bottom row of the matrix from right to left, and appends each element to the result slice. If there is only one row left, the loop is broken to avoid duplicating the elements. Finally, if there is more than one column in the matrix, the function traverses the left O(n) time | O(n) space - where n is the total number of elements in the array */ package main import "fmt" func SpiralTraverse(array [][]int) []int { // Initialize an empty slice to hold the result result := []int{} // If the input array is empty, return the empty result if len(array) == 0 { return result } // Initialize variables to keep track of the boundaries of the matrix startRow, endRow := 0, len(array)-1 startCol, endCol := 0, len(array[0])-1 // Traverse the matrix in a spiral order for startRow <= endRow && startCol <= endCol { // Traverse the top row from left to right for col := startCol; col <= endCol; col++ { result = append(result, array[startRow][col]) } // Traverse the rightmost column from top to bottom for row := startRow + 1; row <= endRow; row++ { result = append(result, array[row][endCol]) } // Traverse the bottom row from right to left, if there is more than one row for col := endCol - 1; col >= startCol; col-- { // If there is only one row left, break the loop to avoid duplicating the elements if startRow == endRow { break } result = append(result, array[endRow][col]) } // Traverse the leftmost column from bottom to top, if there is more than one column for row := endRow - 1; row > startRow; row-- { // If there is only one column left, break the loop to avoid duplicating the elements if startCol == endCol { break } result = append(result, array[row][startCol]) } // Update the boundaries of the matrix startRow++ endRow-- startCol++ endCol-- } // Return the result slice return result } func main() { // Example 2D array array := [][]int{ {1, 2, 3, 4}, {10, 11, 12, 5}, {9, 8, 7, 6}, } // Call SpiralTraverse function on array result := SpiralTraverse(array) // Print the result to console fmt.Println(result) } ================================================ FILE: 2D Arrays (Matrix)/spiral_traverse.java ================================================ /* Write a function that takes in an n x m two-dimensional array (that can be square-shaped when n == m) and returns a one-dimensional array of all the array's elements in spiral order. Spiral order starts at the top left corner of the two-dimensional array, goes to the right, and proceeds in a spiral pattern all the way until every element has been visited. Explanation: The SpiralTraverse function takes a 2D integer array array and returns a 1D integer slice that contains the elements of array traversed in a spiral order, starting from the top-left corner and moving clockwise. The function first initializes an empty slice result to hold the elements of the spiral traversal. If the input array is empty, the function immediately returns the empty result. Next, the function initializes variables startRow, endRow, startCol, and endCol to keep track of the boundaries of the matrix. These variables will be updated as the function traverses the matrix. The function then enters a loop that traverses the matrix in a spiral order. The loop continues as long as startRow <= endRow and startCol <= endCol, which means that there are still elements in the matrix to be traversed. The first step in the loop is to traverse the top row of the matrix from left to right, and append each element to the result slice. The next step is to traverse the rightmost column of the matrix from top to bottom, and append each element to the result slice. If there is more than one row in the matrix, the function then traverses the bottom row of the matrix from right to left, and appends each element to the result slice. If there is only one row left, the loop is broken to avoid duplicating the elements. Finally, if there is more than one column in the matrix, the function traverses the left O(n) time | O(n) space - where n is the total number of elements in the array */ import java.util.*; public class SpiralTraverse { public static void main(String[] args) { int[][] array = { {1, 2, 3}, {12, 13, 4}, {11, 14, 5}, {10, 15, 6}, {9, 8, 7} }; System.out.println(spiralFillUsingLoop(array)); // spiralFillUsingRecursion(array, 0, array.length - 1, 0, array[0].length - 1, result); } public static List spiralFillUsingLoop(int[][] array) { // O(n) time | O(n) space; if(array.length == 0) return new ArrayList<>(); var result = new ArrayList(); var startRow = 0; var endRow = array.length - 1; var startCol = 0; var endCol = array[0].length - 1; while(startRow <= endRow && startCol <= endCol) { for(int col = startCol; col <= endCol; col++) result.add(array[startRow][col]); for(int row = startRow + 1; row <= endRow; row++) result.add(array[row][endCol]); for(int col = endCol - 1; col >= startCol; col--) { /** * Handle the edge case when there's a single row * in the middle of the matrix, In this case. we don't * want to double-count the values in this row, which * we've already counted in the first for loop above. * Test case for this edge case : * [ * [1, 2, 3, 4], * [10, 11, 12, 5], * [9, 8, 7, 6] * ] * * */ if(startRow == endRow) break; result.add(array[endRow][col]); } for(int row = endRow - 1; row > startRow; row--) { /** * Handle the edge case when there's a single column * in the middle of the matrix, In this case. we don't * want to double-count the values in this row, which * we've already counted in the first for loop above. * Test case for this edge case : * [ * [1, 2, 3], * [12, 13, 4], * [11, 14, 5], * [10, 15, 6], * ] * */ if(startCol == endCol) break; result.add(array[row][startCol]); } startRow++; endRow--; startCol++; endCol--; } return result; } public static void spiralFillUsingRecursion( int[][] array, int startRow, int endRow, int startCol, int endCol, ArrayList result) { // O(n) time | O(n) space; if(startRow > endRow || startCol > endCol) return; for(int col = startCol; col <= endCol; col++) result.add(array[startRow][col]); for(int row = startRow + 1; row <= endRow; row++) result.add(array[row][endCol]); for(int col = endCol - 1; col >= startCol; col--) { if(startRow == endRow) break; result.add(array[endRow][col]); } for(int row = endRow - 1; row > startRow; row--) { if(startCol == endCol) break; result.add(array[row][startCol]); } spiralFillUsingRecursion(array, startRow + 1, endRow -1, startCol + 1, endCol - 1, result); } } ================================================ FILE: 2D Arrays (Matrix)/spiral_traverse.py ================================================ ##### LEETCODE 54. Spiral Matrix #### ##### INPUT OUTPUT ##### # Given an m x n matrix, return all elements of the matrix in spiral order. # Input: matrix = [[1,2,3],[4,5,6],[7,8,9]] # Output: [1,2,3,6,9,8,7,4,5] # Input: matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]] # Output: [1,2,3,4,8,12,11,10,9,5,6,7] # Write python code along with explanation and comments use meaningful variables ##### EXPLAINATION ##### # The given problem statement is to return all elements of the matrix in spiral order. We can start by defining the four boundaries of the matrix - top, bottom, left and right. We can then traverse the matrix in a spiral order by following these four steps: # Traverse from left to right along the top boundary # Traverse from top to bottom along the right boundary # Traverse from right to left along the bottom boundary # Traverse from bottom to top along the left boundary # After each traversal, we need to update the corresponding boundary and change the direction of traversal. # We can implement this algorithm using a while loop that runs as long as the top boundary is less than or equal to the bottom boundary and the left boundary is less than or equal to the right boundary. Within the while loop, we can use an if-else ladder to check the current direction of traversal and perform the corresponding traversal along the boundary. # Finally, we can return the result list containing all the spiral order elements of the matrix. #### DRY RUN: #### # Initially, we have the matrix: # 1 2 3 # 4 5 6 # 7 8 9 # We initialize the variables top, bottom, left, and right to 0, 2, 0, and 2 respectively. We also set the direction variable to 0. # Now, we enter the while loop since top<=bottom and left<=right. # In the first iteration of the while loop, direction=0 means we need to traverse from left to right along the top boundary. # We iterate the for loop from left=0 to right=2 and append the elements 1, 2, 3 to the result list. After this, we increment top by 1 to mark that the top boundary is now done. # The result list now contains [1, 2, 3]. # In the second iteration of the while loop, direction=1 means we need to traverse from top to bottom along the right boundary. # We iterate the for loop from top=1 to bottom=2 and append the elements 6, 9 to the result list. After this, we decrement right by 1 to mark that the right boundary is now done. # The result list now contains [1, 2, 3, 6, 9]. # In the third iteration of the while loop, direction=2 means we need to traverse from right to left along the bottom boundary. # We iterate the for loop from right=1 to left=0 and append the elements 8, 7 to the result list. After this, we decrement bottom by 1 to mark that the bottom boundary is now done. # The result list now contains [1, 2, 3, 6, 9, 8, 7]. # In the fourth iteration of the while loop, direction=3 means we need to traverse from bottom to top along the left boundary. # We iterate the for loop from bottom=1 to top=1 and append the element 4 and 5 to the result list. After this, we increment left by 1 to mark that the left boundary is now done. # The result list now contains [1, 2, 3, 6, 9, 8, 7, 4, 5]. # Now, we have completed one full spiral traversal of the matrix. The direction variable is updated as (0+1)%4=1 which sets it to 1 for the next iteration. # We continue with the while loop since top<=bottom and left<=right. In the second iteration, the process continues in the same way as described above until all elements of the matrix are visited. # Finally, the function returns the result list containing all the spiral order elements of the matrix. def spiralOrder(matrix): # Initialize variables to keep track of indices and boundaries top, bottom = 0, len(matrix) - 1 left, right = 0, len(matrix[0]) - 1 direction = 0 # 0 = right, 1 = down, 2 = left, 3 = up # Initialize an empty result list to store the spiral order elements result = [] while top <= bottom and left <= right: if direction == 0: # Traverse right for i in range(left, right+1): result.append(matrix[top][i]) top += 1 elif direction == 1: # Traverse down for i in range(top, bottom+1): result.append(matrix[i][right]) right -= 1 elif direction == 2: # Traverse left for i in range(right, left-1, -1): result.append(matrix[bottom][i]) bottom -= 1 else: # Traverse up for i in range(bottom, top-1, -1): result.append(matrix[i][left]) left += 1 # Update direction after completing one full traversal direction = (direction + 1) % 4 return result matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] print(spiralOrder(matrix)) ================================================ FILE: 2D Arrays (Matrix)/zigzag_traversal.cpp ================================================ /* Write a function that takes in an n x m two-dimensional array (that can be square-shaped when n == m) and returns a one-dimensional array of all the array's elements in zigzag order. Sample Input:= [ [1, 3, 4, 10], [2, 5, 9, 11], [6, 8, 12, 15], [7, 13, 14, 16], ] Output: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] Explanation: The given code snippet implements the ZigzagTraverse algorithm, which traverses a 2D array in a zigzag pattern and returns the elements in a 1D array. Here's an explanation of the code: 1. `ZigzagTraverse`: This is the main function that takes a 2D array `array` as input and returns a 1D array containing the elements traversed in zigzag order. 2. `height` and `width`: These variables store the height and width of the 2D array, respectively. The `height` represents the number of rows (minus 1 as it is 0-based indexing), and the `width` represents the number of columns (minus 1 as it is 0-based indexing). 3. `row` and `col`: These variables keep track of the current position while traversing the 2D array. 4. `goingDown`: This boolean variable determines the direction of traversal. When `goingDown` is `true`, the traversal is in the downward direction; otherwise, it is in the upward direction. 5. `result`: This array stores the elements of the 2D array in zigzag order, which will be returned as the final result. 6. The main loop: The loop runs until the current position is within bounds (not out of the 2D array). 7. Append element to result: The code appends the current element at position `(row, col)` to the `result` array. 8. Traversal logic: The algorithm decides the next position for traversal based on the current position and the `goingDown` flag. If `goingDown` is `true`, it will traverse diagonally downwards (towards the bottom-right or the bottom-left corner, depending on the position). Otherwise, it will traverse diagonally upwards (towards the top-right or the top-left corner, depending on the position). 9. `isOutOfBounds`: This is a helper function that checks if the current position `(row, col)` is out of bounds of the 2D array (i.e., if `row` or `col` is less than 0 or greater than the height or width, respectively). 10. Return result: After the traversal is complete, the function returns the `result` array, which contains the elements of the 2D array in zigzag order. The ZigzagTraverse algorithm efficiently zigzags through the 2D array by changing the direction of traversal whenever it reaches the boundary or the corners of the array, allowing it to cover all elements in zigzag order. O(n) time | O(n) space - where n is the total number of elements in the two-dimensional array */ #include using namespace std; vector ZigzagTraverse(vector>& array) { // Get the height (number of rows) and width (number of columns) of the array. int height = array.size(); int width = array[0].size(); // Initialize the row and column pointers to start from the top-left element (0,0). int row = 0; int col = 0; // Initialize a flag to track the direction of traversal (goingDown). // true means moving down, false means moving up. bool goingDown = true; // Initialize a vector to store the elements in zigzag order. vector result; // Loop until the current position is within the bounds of the array. while (!isOutOfBounds(row, col, height, width)) { // Append the current element to the result vector. result.push_back(array[row][col]); // If moving down, check if we reached the bottom row or leftmost column. if (goingDown) { if (col == 0 || row == height - 1) { // Change direction if we reached the bottom row or leftmost column. goingDown = false; if (row == height - 1) { // Move right if we reached the bottom row. col++; } else { // Move down if we reached the leftmost column. row++; } } else { // Move diagonally down-left. row++; col--; } } else { // If moving up, check if we reached the top row or rightmost column. if (row == 0 || col == width - 1) { // Change direction if we reached the top row or rightmost column. goingDown = true; if (col == width - 1) { // Move down if we reached the rightmost column. row++; } else { // Move right if we reached the top row. col++; } } else { // Move diagonally up-right. row--; col++; } } } // Return the vector containing the elements in zigzag order. return result; } bool isOutOfBounds(int row, int col, int height, int width) { // Check if the current position is outside the bounds of the array. return row < 0 || col < 0 || row >= height || col >= width; } ================================================ FILE: 2D Arrays (Matrix)/zigzag_traversal.go ================================================ /* Write a function that takes in an n x m two-dimensional array (that can be square-shaped when n == m) and returns a one-dimensional array of all the array's elements in zigzag order. Sample Input:= [ [1, 3, 4, 10], [2, 5, 9, 11], [6, 8, 12, 15], [7, 13, 14, 16], ] Output: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] Explanation: The given code snippet implements the ZigzagTraverse algorithm, which traverses a 2D array in a zigzag pattern and returns the elements in a 1D array. Here's an explanation of the code: 1. `ZigzagTraverse`: This is the main function that takes a 2D array `array` as input and returns a 1D array containing the elements traversed in zigzag order. 2. `height` and `width`: These variables store the height and width of the 2D array, respectively. The `height` represents the number of rows (minus 1 as it is 0-based indexing), and the `width` represents the number of columns (minus 1 as it is 0-based indexing). 3. `row` and `col`: These variables keep track of the current position while traversing the 2D array. 4. `goingDown`: This boolean variable determines the direction of traversal. When `goingDown` is `true`, the traversal is in the downward direction; otherwise, it is in the upward direction. 5. `result`: This array stores the elements of the 2D array in zigzag order, which will be returned as the final result. 6. The main loop: The loop runs until the current position is within bounds (not out of the 2D array). 7. Append element to result: The code appends the current element at position `(row, col)` to the `result` array. 8. Traversal logic: The algorithm decides the next position for traversal based on the current position and the `goingDown` flag. If `goingDown` is `true`, it will traverse diagonally downwards (towards the bottom-right or the bottom-left corner, depending on the position). Otherwise, it will traverse diagonally upwards (towards the top-right or the top-left corner, depending on the position). 9. `isOutOfBounds`: This is a helper function that checks if the current position `(row, col)` is out of bounds of the 2D array (i.e., if `row` or `col` is less than 0 or greater than the height or width, respectively). 10. Return result: After the traversal is complete, the function returns the `result` array, which contains the elements of the 2D array in zigzag order. The ZigzagTraverse algorithm efficiently zigzags through the 2D array by changing the direction of traversal whenever it reaches the boundary or the corners of the array, allowing it to cover all elements in zigzag order. O(n) time | O(n) space - where n is the total number of elements in the two-dimensional array */ package main // ZigzagTraverse traverses a 2D array in a zigzag pattern and returns the elements in a 1D array. func ZigzagTraverse(array [][]int) []int { // Get the height and width of the 2D array. height := len(array) - 1 width := len(array[0]) - 1 // Initialize variables to keep track of the current position while traversing. row, col := 0, 0 // Initialize a boolean variable to determine the direction of traversal. goingDown := true // Initialize an array to store the elements traversed in zigzag order. result := []int{} // The main loop runs until the current position is within bounds (not out of the 2D array). for !isOutOfBounds(row, col, height, width) { // Append the current element at position (row, col) to the result array. result = append(result, array[row][col]) // Traversal logic: Decide the next position for traversal based on the current position and the goingDown flag. if goingDown { if col == 0 || row == height { // Change direction to upward if at the top-left or bottom-right corner. goingDown = false // Decide the next position based on whether we are at the bottom or right boundary. if row == height { col++ } else { row++ } } else { // Continue diagonally downward. row++ col-- } } else { if row == 0 || col == width { // Change direction to downward if at the top-right or bottom-left corner. goingDown = true // Decide the next position based on whether we are at the top or right boundary. if col == width { row++ } else { col++ } } else { // Continue diagonally upward. row-- col++ } } } // Return the final result, which contains the elements of the 2D array in zigzag order. return result } // isOutOfBounds checks if the current position (row, col) is out of bounds of the 2D array. func isOutOfBounds(row, col, height, width int) bool { return row < 0 || col < 0 || row > height || col > width } ================================================ FILE: 2D Arrays (Matrix)/zigzag_traversal.java ================================================ /* Write a function that takes in an n x m two-dimensional array (that can be square-shaped when n == m) and returns a one-dimensional array of all the array's elements in zigzag order. Sample Input:= [ [1, 3, 4, 10], [2, 5, 9, 11], [6, 8, 12, 15], [7, 13, 14, 16], ] Output: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] Explanation: The given code snippet implements the ZigzagTraverse algorithm, which traverses a 2D array in a zigzag pattern and returns the elements in a 1D array. Here's an explanation of the code: 1. `ZigzagTraverse`: This is the main function that takes a 2D array `array` as input and returns a 1D array containing the elements traversed in zigzag order. 2. `height` and `width`: These variables store the height and width of the 2D array, respectively. The `height` represents the number of rows (minus 1 as it is 0-based indexing), and the `width` represents the number of columns (minus 1 as it is 0-based indexing). 3. `row` and `col`: These variables keep track of the current position while traversing the 2D array. 4. `goingDown`: This boolean variable determines the direction of traversal. When `goingDown` is `true`, the traversal is in the downward direction; otherwise, it is in the upward direction. 5. `result`: This array stores the elements of the 2D array in zigzag order, which will be returned as the final result. 6. The main loop: The loop runs until the current position is within bounds (not out of the 2D array). 7. Append element to result: The code appends the current element at position `(row, col)` to the `result` array. 8. Traversal logic: The algorithm decides the next position for traversal based on the current position and the `goingDown` flag. If `goingDown` is `true`, it will traverse diagonally downwards (towards the bottom-right or the bottom-left corner, depending on the position). Otherwise, it will traverse diagonally upwards (towards the top-right or the top-left corner, depending on the position). 9. `isOutOfBounds`: This is a helper function that checks if the current position `(row, col)` is out of bounds of the 2D array (i.e., if `row` or `col` is less than 0 or greater than the height or width, respectively). 10. Return result: After the traversal is complete, the function returns the `result` array, which contains the elements of the 2D array in zigzag order. The ZigzagTraverse algorithm efficiently zigzags through the 2D array by changing the direction of traversal whenever it reaches the boundary or the corners of the array, allowing it to cover all elements in zigzag order. O(n) time | O(n) space - where n is the total number of elements in the two-dimensional array */ import java.util.ArrayList; import java.util.List; public class Main { public static List ZigzagTraverse(int[][] array) { // Get the height (number of rows) and width (number of columns) of the array. int height = array.length; int width = array[0].length; // Initialize the row and column pointers to start from the top-left element (0,0). int row = 0; int col = 0; // Initialize a flag to track the direction of traversal (goingDown). // true means moving down, false means moving up. boolean goingDown = true; // Initialize a list to store the elements in zigzag order. List result = new ArrayList<>(); // Loop until the current position is within the bounds of the array. while (!isOutOfBounds(row, col, height, width)) { // Append the current element to the result list. result.add(array[row][col]); // If moving down, check if we reached the bottom row or leftmost column. if (goingDown) { if (col == 0 || row == height - 1) { // Change direction if we reached the bottom row or leftmost column. goingDown = false; if (row == height - 1) { // Move right if we reached the bottom row. col++; } else { // Move down if we reached the leftmost column. row++; } } else { // Move diagonally down-left. row++; col--; } } else { // If moving up, check if we reached the top row or rightmost column. if (row == 0 || col == width - 1) { // Change direction if we reached the top row or rightmost column. goingDown = true; if (col == width - 1) { // Move down if we reached the rightmost column. row++; } else { // Move right if we reached the top row. col++; } } else { // Move diagonally up-right. row--; col++; } } } // Return the list containing the elements in zigzag order. return result; } public static boolean isOutOfBounds(int row, int col, int height, int width) { // Check if the current position is outside the bounds of the array. return row < 0 || col < 0 || row >= height || col >= width; } public static void main(String[] args) { int[][] array = { {1, 3, 4, 10}, {2, 5, 9, 11}, {6, 8, 12, 15}, {7, 13, 14, 16} }; List result = ZigzagTraverse(array); System.out.println(result); } } ================================================ FILE: 2D Arrays (Matrix)/zigzag_traversal.js ================================================ /* Write a function that takes in an n x m two-dimensional array (that can be square-shaped when n == m) and returns a one-dimensional array of all the array's elements in zigzag order. Sample Input:= [ [1, 3, 4, 10], [2, 5, 9, 11], [6, 8, 12, 15], [7, 13, 14, 16], ] Output: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] Explanation: The given code snippet implements the ZigzagTraverse algorithm, which traverses a 2D array in a zigzag pattern and returns the elements in a 1D array. Here's an explanation of the code: 1. `ZigzagTraverse`: This is the main function that takes a 2D array `array` as input and returns a 1D array containing the elements traversed in zigzag order. 2. `height` and `width`: These variables store the height and width of the 2D array, respectively. The `height` represents the number of rows (minus 1 as it is 0-based indexing), and the `width` represents the number of columns (minus 1 as it is 0-based indexing). 3. `row` and `col`: These variables keep track of the current position while traversing the 2D array. 4. `goingDown`: This boolean variable determines the direction of traversal. When `goingDown` is `true`, the traversal is in the downward direction; otherwise, it is in the upward direction. 5. `result`: This array stores the elements of the 2D array in zigzag order, which will be returned as the final result. 6. The main loop: The loop runs until the current position is within bounds (not out of the 2D array). 7. Append element to result: The code appends the current element at position `(row, col)` to the `result` array. 8. Traversal logic: The algorithm decides the next position for traversal based on the current position and the `goingDown` flag. If `goingDown` is `true`, it will traverse diagonally downwards (towards the bottom-right or the bottom-left corner, depending on the position). Otherwise, it will traverse diagonally upwards (towards the top-right or the top-left corner, depending on the position). 9. `isOutOfBounds`: This is a helper function that checks if the current position `(row, col)` is out of bounds of the 2D array (i.e., if `row` or `col` is less than 0 or greater than the height or width, respectively). 10. Return result: After the traversal is complete, the function returns the `result` array, which contains the elements of the 2D array in zigzag order. The ZigzagTraverse algorithm efficiently zigzags through the 2D array by changing the direction of traversal whenever it reaches the boundary or the corners of the array, allowing it to cover all elements in zigzag order. O(n) time | O(n) space - where n is the total number of elements in the two-dimensional array */ function ZigzagTraverse(array) { // Get the height (number of rows) and width (number of columns) of the array. const height = array.length; const width = array[0].length; // Initialize the row and column pointers to start from the top-left element (0,0). let row = 0; let col = 0; // Initialize a flag to track the direction of traversal (goingDown). // true means moving down, false means moving up. let goingDown = true; // Initialize an array to store the elements in zigzag order. const result = []; // Loop until the current position is within the bounds of the array. while (!isOutOfBounds(row, col, height, width)) { // Append the current element to the result array. result.push(array[row][col]); // If moving down, check if we reached the bottom row or leftmost column. if (goingDown) { if (col === 0 || row === height - 1) { // Change direction if we reached the bottom row or leftmost column. goingDown = false; if (row === height - 1) { // Move right if we reached the bottom row. col++; } else { // Move down if we reached the leftmost column. row++; } } else { // Move diagonally down-left. row++; col--; } } else { // If moving up, check if we reached the top row or rightmost column. if (row === 0 || col === width - 1) { // Change direction if we reached the top row or rightmost column. goingDown = true; if (col === width - 1) { // Move down if we reached the rightmost column. row++; } else { // Move right if we reached the top row. col++; } } else { // Move diagonally up-right. row--; col++; } } } // Return the array containing the elements in zigzag order. return result; } function isOutOfBounds(row, col, height, width) { // Check if the current position is outside the bounds of the array. return row < 0 || col < 0 || row >= height || col >= width; } ================================================ FILE: 2D Arrays (Matrix)/zigzag_traversal.py ================================================ ''' Write a function that takes in an n x m two-dimensional array (that can be square-shaped when n == m) and returns a one-dimensional array of all the array's elements in zigzag order. Sample Input:= [ [1, 3, 4, 10], [2, 5, 9, 11], [6, 8, 12, 15], [7, 13, 14, 16], ] Output: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] Explanation: The given code snippet implements the ZigzagTraverse algorithm, which traverses a 2D array in a zigzag pattern and returns the elements in a 1D array. Here's an explanation of the code: 1. `ZigzagTraverse`: This is the main function that takes a 2D array `array` as input and returns a 1D array containing the elements traversed in zigzag order. 2. `height` and `width`: These variables store the height and width of the 2D array, respectively. The `height` represents the number of rows (minus 1 as it is 0-based indexing), and the `width` represents the number of columns (minus 1 as it is 0-based indexing). 3. `row` and `col`: These variables keep track of the current position while traversing the 2D array. 4. `goingDown`: This boolean variable determines the direction of traversal. When `goingDown` is `true`, the traversal is in the downward direction; otherwise, it is in the upward direction. 5. `result`: This array stores the elements of the 2D array in zigzag order, which will be returned as the final result. 6. The main loop: The loop runs until the current position is within bounds (not out of the 2D array). 7. Append element to result: The code appends the current element at position `(row, col)` to the `result` array. 8. Traversal logic: The algorithm decides the next position for traversal based on the current position and the `goingDown` flag. If `goingDown` is `true`, it will traverse diagonally downwards (towards the bottom-right or the bottom-left corner, depending on the position). Otherwise, it will traverse diagonally upwards (towards the top-right or the top-left corner, depending on the position). 9. `isOutOfBounds`: This is a helper function that checks if the current position `(row, col)` is out of bounds of the 2D array (i.e., if `row` or `col` is less than 0 or greater than the height or width, respectively). 10. Return result: After the traversal is complete, the function returns the `result` array, which contains the elements of the 2D array in zigzag order. The ZigzagTraverse algorithm efficiently zigzags through the 2D array by changing the direction of traversal whenever it reaches the boundary or the corners of the array, allowing it to cover all elements in zigzag order. O(n) time | O(n) space - where n is the total number of elements in the two-dimensional array ''' def ZigzagTraverse(array): # Get the height (number of rows) and width (number of columns) of the array. height = len(array) width = len(array[0]) # Initialize the row and column pointers to start from the top-left element (0,0). row, col = 0, 0 # Initialize a flag to track the direction of traversal (goingDown). # True means moving down, False means moving up. goingDown = True # Initialize a list to store the elements in zigzag order. result = [] # Loop until the current position is within the bounds of the array. while not isOutOfBounds(row, col, height, width): # Append the current element to the result list. result.append(array[row][col]) # If moving down, check if we reached the bottom row or leftmost column. if goingDown: if col == 0 or row == height - 1: # Change direction if we reached the bottom row or leftmost column. goingDown = False if row == height - 1: # Move right if we reached the bottom row. col += 1 else: # Move down if we reached the leftmost column. row += 1 else: # Move diagonally down-left. row += 1 col -= 1 else: # If moving up, check if we reached the top row or rightmost column. if row == 0 or col == width - 1: # Change direction if we reached the top row or rightmost column. goingDown = True if col == width - 1: # Move down if we reached the rightmost column. row += 1 else: # Move right if we reached the top row. col += 1 else: # Move diagonally up-right. row -= 1 col += 1 # Return the list containing the elements in zigzag order. return result def isOutOfBounds(row, col, height, width): # Check if the current position is outside the bounds of the array. return row < 0 or col < 0 or row >= height or col >= width ================================================ FILE: Arrays/Jobassign.cpp ================================================ #include #include #define N 4 // Number of workers and tasks void hungarianAlgorithm(int costMatrix[N][N]); int main() { int costMatrix[N][N] = { {9, 2, 7, 8}, {6, 4, 3, 7}, {5, 8, 1, 8}, {7, 6, 9, 4} }; hungarianAlgorithm(costMatrix); return 0; } void hungarianAlgorithm(int costMatrix[N][N]) { int i, j; int numWorkers = N, numTasks = N; int minCost, minCostIdx; int rowCover[N] = {0}; int colCover[N] = {0}; int assignment[N][2] = {0}; // Stores the assignment // Step 1: Subtract the smallest value in each row from all elements in that row for (i = 0; i < numWorkers; i++) { minCost = INT_MAX; for (j = 0; j < numTasks; j++) { if (costMatrix[i][j] < minCost) { minCost = costMatrix[i][j]; } } for (j = 0; j < numTasks; j++) { costMatrix[i][j] -= minCost; } } // Step 2: Find a zero in the cost matrix and mark the row and column for (i = 0; i < numWorkers; i++) { for (j = 0; j < numTasks; j++) { if (costMatrix[i][j] == 0 && !rowCover[i] && !colCover[j]) { assignment[i][0] = i; assignment[i][1] = j; rowCover[i] = 1; colCover[j] = 1; } } } // Step 3: Check if all rows are covered int rowCoveredCount = 0; for (i = 0; i < numWorkers; i++) { rowCoveredCount += rowCover[i]; } if (rowCoveredCount == numWorkers) { // All rows are covered, we have the optimal assignment printf("Optimal Assignment:\n"); for (i = 0; i < numWorkers; i++) { printf("Worker %d -> Task %d\n", assignment[i][0] + 1, assignment[i][1] + 1); } return; } else { // Proceed to step 4 } // Step 4: Find the minimum uncovered value (minCost) in the cost matrix minCost = INT_MAX; for (i = 0; i < numWorkers; i++) { for (j = 0; j < numTasks; j++) { if (!rowCover[i] && !colCover[j] && costMatrix[i][j] < minCost) { minCost = costMatrix[i][j]; } } } // Step 5: Subtract minCost from all uncovered elements and add it to all elements at the intersection of covering lines for (i = 0; i < numWorkers; i++) { for (j = 0; j < numTasks; j++) { if (!rowCover[i] && !colCover[j]) { costMatrix[i][j] -= minCost; } else if (rowCover[i] && colCover[j]) { costMatrix[i][j] += minCost; } } } // Continue to step 3 hungarianAlgorithm(costMatrix); } ================================================ FILE: Arrays/Maximum_freq.py ================================================ class Solution { public int majorityElement(int[] nums) { int candidate = nums[0]; // Initialize the candidate as the first element int count = 1; // Initialize the count of the current candidate as 1 // Iterate through the array starting from the second element for (int i = 1; i < nums.length; i++) { if (nums[i] == candidate) { // If the current element is the same as the candidate, increment the count count++; } else if (count > 0) { // If the current element is different from the candidate and count is positive, // decrement the count since we have a matching pair (candidate vs. current element) count--; } else { // If count becomes zero, update the candidate to the current element and set count to 1 candidate = nums[i]; count = 1; } } // At the end, the candidate will be the majority element return candidate; } } ================================================ FILE: Arrays/Rotatedarr.cpp ================================================ class Solution { public: int search(std::vector& nums, int target) { int low = 0, high = nums.size() - 1; while (low <= high) { int mid = (low + high) / 2; if (nums[mid] == target) { return mid; } if (nums[low] <= nums[mid]) { if (nums[low] <= target && target < nums[mid]) { high = mid - 1; } else { low = mid + 1; } } else { if (nums[mid] < target && target <= nums[high]) { low = mid + 1; } else { high = mid - 1; } } } return -1; } }; ================================================ FILE: Arrays/array_of_products.cpp ================================================ /* Given an array of integers A, find and return the product array of the same size where the ith element of the product array will be equal to the product of all the elements divided by the ith element of the array. Note: It is always possible to form the product array with integer (32 bit) values. Solve it without using the division operator. Input: [1, 2, 3, 4, 5] Output : [120, 60, 40, 30, 24] Explanation: The code snippet provides a function called `ArrayOfProducts` that takes an array of integers as input and returns another array where each element is the product of all the integers in the input array except for the one at that index. Here's how the code works: 1. It initializes an empty result array with the same length as the input array to store the final products. 2. It uses a left-to-right approach to compute the running product of all elements to the left of each index. 3. It initializes a variable `leftRunningProduct` to keep track of the running product of elements on the left side. 4. It iterates over the input array from left to right using a `for` loop. 5. For each index `i`, it stores the current `leftRunningProduct` in the result array at index `i` and then updates the `leftRunningProduct` by multiplying it with the corresponding element in the input array. 6. After the loop, the result array will contain the product of all elements to the left of each index. 7. It uses a right-to-left approach to compute the running product of all elements to the right of each index and multiply it with the corresponding left product in the result array. 8. It initializes a variable `rightRunningProduct` to keep track of the running product of elements on the right side. 9. It iterates over the input array from right to left using a `for` loop. 10. For each index `i`, it multiplies the `rightRunningProduct` with the corresponding left product in the result array and updates the `rightRunningProduct` by multiplying it with the corresponding element in the input array. 11. After the loop, the result array will contain the product of all elements to the left and right of each index, except for the element at that index. 12. Finally, it returns the result array. This algorithm avoids using division and solves the problem in linear time complexity, making two passes over the input array. The space complexity is also linear, as it uses an additional array to store the products. */ #include #include using namespace std; // Given an array of integers, returns an array where each element // is the product of all the integers in the input array except for the one at that index. vector ArrayOfProducts(vector& array) { int n = array.size(); vector result(n, 1); // Compute the running product of all elements to the left of each index // and store it in the result array. int leftRunningProduct = 1; for (int i = 0; i < n; i++) { result[i] = leftRunningProduct; // Store left product in the result array leftRunningProduct *= array[i]; // Update left product } // Compute the running product of all elements to the right of each index // and multiply it with the corresponding left product in the result array. int rightRunningProduct = 1; for (int i = n - 1; i >= 0; i--) { result[i] *= rightRunningProduct; // Multiply the right product with the corresponding left product rightRunningProduct *= array[i]; // Update right product } return result; } int main() { // Example usage vector input = {1, 2, 3, 4, 5}; vector output = ArrayOfProducts(input); // Print the result for (int num : output) { cout << num << " "; } cout << endl; return 0; } ================================================ FILE: Arrays/array_of_products.go ================================================ /* Given an array of integers A, find and return the product array of the same size where the ith element of the product array will be equal to the product of all the elements divided by the ith element of the array. Note: It is always possible to form the product array with integer (32 bit) values. Solve it without using the division operator. Input: [1, 2, 3, 4, 5] Output : [120, 60, 40, 30, 24] Explanation: The code snippet provides a function called `ArrayOfProducts` that takes an array of integers as input and returns another array where each element is the product of all the integers in the input array except for the one at that index. Here's how the code works: 1. It initializes an empty result array with the same length as the input array to store the final products. 2. It uses a left-to-right approach to compute the running product of all elements to the left of each index. 3. It initializes a variable `leftRunningProduct` to keep track of the running product of elements on the left side. 4. It iterates over the input array from left to right using a `for` loop. 5. For each index `i`, it stores the current `leftRunningProduct` in the result array at index `i` and then updates the `leftRunningProduct` by multiplying it with the corresponding element in the input array. 6. After the loop, the result array will contain the product of all elements to the left of each index. 7. It uses a right-to-left approach to compute the running product of all elements to the right of each index and multiply it with the corresponding left product in the result array. 8. It initializes a variable `rightRunningProduct` to keep track of the running product of elements on the right side. 9. It iterates over the input array from right to left using a `for` loop. 10. For each index `i`, it multiplies the `rightRunningProduct` with the corresponding left product in the result array and updates the `rightRunningProduct` by multiplying it with the corresponding element in the input array. 11. After the loop, the result array will contain the product of all elements to the left and right of each index, except for the element at that index. 12. Finally, it returns the result array. This algorithm avoids using division and solves the problem in linear time complexity, making two passes over the input array. The space complexity is also linear, as it uses an additional array to store the products. */ package main // Given an array of integers, returns an array where each element // is the product of all the integers in the input array except for the one at that index. func ArrayOfProducts(array []int) []int { result := make([]int, len(array)) // Compute the running product of all elements to the left of each index // and store it in the result array. leftRunningProduct := 1 for i := 0; i < len(array); i++ { result[i] = leftRunningProduct // Store left product in the result array leftRunningProduct *= array[i] // Update left product } // Compute the running product of all elements to the right of each index // and multiply it with the corresponding left product in the result array. rightRunningProduct := 1 for i := len(array) - 1; i >= 0; i-- { result[i] = rightRunningProduct * result[i] // Multiply the right product with the corresponding left product rightRunningProduct *= array[i] // Update right product } return result } ================================================ FILE: Arrays/array_of_products.java ================================================ /* Given an array of integers A, find and return the product array of the same size where the ith element of the product array will be equal to the product of all the elements divided by the ith element of the array. Note: It is always possible to form the product array with integer (32 bit) values. Solve it without using the division operator. Input: [1, 2, 3, 4, 5] Output : [120, 60, 40, 30, 24] Explanation: The code snippet provides a function called `ArrayOfProducts` that takes an array of integers as input and returns another array where each element is the product of all the integers in the input array except for the one at that index. Here's how the code works: 1. It initializes an empty result array with the same length as the input array to store the final products. 2. It uses a left-to-right approach to compute the running product of all elements to the left of each index. 3. It initializes a variable `leftRunningProduct` to keep track of the running product of elements on the left side. 4. It iterates over the input array from left to right using a `for` loop. 5. For each index `i`, it stores the current `leftRunningProduct` in the result array at index `i` and then updates the `leftRunningProduct` by multiplying it with the corresponding element in the input array. 6. After the loop, the result array will contain the product of all elements to the left of each index. 7. It uses a right-to-left approach to compute the running product of all elements to the right of each index and multiply it with the corresponding left product in the result array. 8. It initializes a variable `rightRunningProduct` to keep track of the running product of elements on the right side. 9. It iterates over the input array from right to left using a `for` loop. 10. For each index `i`, it multiplies the `rightRunningProduct` with the corresponding left product in the result array and updates the `rightRunningProduct` by multiplying it with the corresponding element in the input array. 11. After the loop, the result array will contain the product of all elements to the left and right of each index, except for the element at that index. 12. Finally, it returns the result array. This algorithm avoids using division and solves the problem in linear time complexity, making two passes over the input array. The space complexity is also linear, as it uses an additional array to store the products. */ import java.util.Arrays; public class ArrayOfProducts { public static int[] arrayOfProducts(int[] array) { int n = array.length; int[] result = new int[n]; // Compute the running product of all elements to the left of each index // and store it in the result array. int leftRunningProduct = 1; for (int i = 0; i < n; i++) { result[i] = leftRunningProduct; // Store left product in the result array leftRunningProduct *= array[i]; // Update left product } // Compute the running product of all elements to the right of each index // and multiply it with the corresponding left product in the result array. int rightRunningProduct = 1; for (int i = n - 1; i >= 0; i--) { result[i] *= rightRunningProduct; // Multiply the right product with the corresponding left product rightRunningProduct *= array[i]; // Update right product } return result; } public static void main(String[] args) { // Example usage int[] input = {1, 2, 3, 4, 5}; int[] output = arrayOfProducts(input); // Print the result System.out.println(Arrays.toString(output)); } } ================================================ FILE: Arrays/array_of_products.js ================================================ /* Given an array of integers A, find and return the product array of the same size where the ith element of the product array will be equal to the product of all the elements divided by the ith element of the array. Note: It is always possible to form the product array with integer (32 bit) values. Solve it without using the division operator. Input: [1, 2, 3, 4, 5] Output : [120, 60, 40, 30, 24] Explanation: The code snippet provides a function called `ArrayOfProducts` that takes an array of integers as input and returns another array where each element is the product of all the integers in the input array except for the one at that index. Here's how the code works: 1. It initializes an empty result array with the same length as the input array to store the final products. 2. It uses a left-to-right approach to compute the running product of all elements to the left of each index. 3. It initializes a variable `leftRunningProduct` to keep track of the running product of elements on the left side. 4. It iterates over the input array from left to right using a `for` loop. 5. For each index `i`, it stores the current `leftRunningProduct` in the result array at index `i` and then updates the `leftRunningProduct` by multiplying it with the corresponding element in the input array. 6. After the loop, the result array will contain the product of all elements to the left of each index. 7. It uses a right-to-left approach to compute the running product of all elements to the right of each index and multiply it with the corresponding left product in the result array. 8. It initializes a variable `rightRunningProduct` to keep track of the running product of elements on the right side. 9. It iterates over the input array from right to left using a `for` loop. 10. For each index `i`, it multiplies the `rightRunningProduct` with the corresponding left product in the result array and updates the `rightRunningProduct` by multiplying it with the corresponding element in the input array. 11. After the loop, the result array will contain the product of all elements to the left and right of each index, except for the element at that index. 12. Finally, it returns the result array. This algorithm avoids using division and solves the problem in linear time complexity, making two passes over the input array. The space complexity is also linear, as it uses an additional array to store the products. */ function arrayOfProducts(array) { const n = array.length; const result = new Array(n).fill(1); // Compute the running product of all elements to the left of each index // and store it in the result array. let leftRunningProduct = 1; for (let i = 0; i < n; i++) { result[i] = leftRunningProduct; // Store left product in the result array leftRunningProduct *= array[i]; // Update left product } // Compute the running product of all elements to the right of each index // and multiply it with the corresponding left product in the result array. let rightRunningProduct = 1; for (let i = n - 1; i >= 0; i--) { result[i] *= rightRunningProduct; // Multiply the right product with the corresponding left product rightRunningProduct *= array[i]; // Update right product } return result; } // Example usage const inputArray = [1, 2, 3, 4, 5]; const outputArray = arrayOfProducts(inputArray); // Print the result console.log(outputArray); ================================================ FILE: Arrays/array_of_products.py ================================================ ''' Given an array of integers A, find and return the product array of the same size where the ith element of the product array will be equal to the product of all the elements divided by the ith element of the array. Note: It is always possible to form the product array with integer (32 bit) values. Solve it without using the division operator. Input: [1, 2, 3, 4, 5] Output : [120, 60, 40, 30, 24] Explanation: The code snippet provides a function called `ArrayOfProducts` that takes an array of integers as input and returns another array where each element is the product of all the integers in the input array except for the one at that index. Here's how the code works: 1. It initializes an empty result array with the same length as the input array to store the final products. 2. It uses a left-to-right approach to compute the running product of all elements to the left of each index. 3. It initializes a variable `leftRunningProduct` to keep track of the running product of elements on the left side. 4. It iterates over the input array from left to right using a `for` loop. 5. For each index `i`, it stores the current `leftRunningProduct` in the result array at index `i` and then updates the `leftRunningProduct` by multiplying it with the corresponding element in the input array. 6. After the loop, the result array will contain the product of all elements to the left of each index. 7. It uses a right-to-left approach to compute the running product of all elements to the right of each index and multiply it with the corresponding left product in the result array. 8. It initializes a variable `rightRunningProduct` to keep track of the running product of elements on the right side. 9. It iterates over the input array from right to left using a `for` loop. 10. For each index `i`, it multiplies the `rightRunningProduct` with the corresponding left product in the result array and updates the `rightRunningProduct` by multiplying it with the corresponding element in the input array. 11. After the loop, the result array will contain the product of all elements to the left and right of each index, except for the element at that index. 12. Finally, it returns the result array. This algorithm avoids using division and solves the problem in linear time complexity, making two passes over the input array. The space complexity is also linear, as it uses an additional array to store the products. ''' def array_of_products(array): n = len(array) result = [1] * n # Compute the running product of all elements to the left of each index # and store it in the result array. left_running_product = 1 for i in range(n): result[i] = left_running_product # Store left product in the result array left_running_product *= array[i] # Update left product # Compute the running product of all elements to the right of each index # and multiply it with the corresponding left product in the result array. right_running_product = 1 for i in range(n - 1, -1, -1): result[i] *= right_running_product # Multiply the right product with the corresponding left product right_running_product *= array[i] # Update right product return result # Example usage input_array = [1, 2, 3, 4, 5] output_array = array_of_products(input_array) # Print the result print(output_array) ================================================ FILE: Arrays/ceaser_cipher.cpp ================================================ /* Given a non-empty string of lowercase letters and a non-negative integer representing a key, write a function that returns a new string obtained by shifting every letter in the input string by k positions in the alphabet, where k is the key. Note that letters should "wrap" around the alphabet; in other words, the letter z shifted by one returns the letter a Sample Input : abz key: 3 Output: dec Explanation: The CaesarCipherEncryptor function takes a string and a key (integer) as input and returns a new string obtained by shifting each character of the input string by the key number of positions to the right in the alphabet, wrapping around if necessary. The function first calculates the shift amount and offset value. The shift amount is calculated by taking the key modulo 26, which ensures that the shift amount is always within the range of 0 to 25. The offset value is calculated by taking 26, which is the number of letters in the alphabet. The function then converts the input string to a rune slice (for ease of manipulation). A rune is a single character in a programming language. The rune type is used to represent characters in Go. The function then iterates over each character in the rune slice. For each character, the function checks if the character is a lowercase letter and if shifting it will still be within the lowercase range. If the character is a lowercase letter and shifting it will still be within the lowercase range, the function simply adds the shift amount to the character. If the character is outside of the lowercase range after shifting, the function wraps it around by adding the shift amount - offset to the character. The function then updates the character in the rune slice. The function then converts the resulting rune slice back to a string and returns it. The time complexity of the CaesarCipherEncryptor function is O(n), where n is the length of the input string. This is because the function iterates over each character in the input string, performing a constant amount of work for each character. The space complexity of the CaesarCipherEncryptor function is O(n), where n is the length of the input string. This is because the function creates a new rune slice to store the encrypted string. The rune slice is the same size as the input string, so the space complexity is O(n). */ // C++ #include #include using namespace std; string CaesarCipherEncryptor(string str, int key) { // Calculate the shift amount and offset value int shift = key % 26; int offset = 26; // Convert the input string to a vector of characters vector chars(str.begin(), str.end()); // Iterate over each character in the vector for (int i = 0; i < chars.size(); i++) { // If the character is a lowercase letter and shifting it will still be within the lowercase range if (chars[i] >= 'a' && chars[i] + shift <= 'z') { chars[i] += shift; } else { // If the character is outside of the lowercase range after shifting, wrap it around chars[i] += shift - offset; } } // Convert the resulting vector of characters back to a string and return it return string(chars.begin(), chars.end()); } int main() { // Get the input string and key from the user string str; cout << "Enter a string: "; cin >> str; int key; cout << "Enter a key: "; cin >> key; // Encrypt the string using the Caesar cipher string encrypted_str = CaesarCipherEncryptor(str, key); // Print the encrypted string cout << "Encrypted string: " << encrypted_str << endl; return 0; } ================================================ FILE: Arrays/ceaser_cipher.go ================================================ /* Given a non-empty string of lowercase letters and a non-negative integer representing a key, write a function that returns a new string obtained by shifting every letter in the input string by k positions in the alphabet, where k is the key. Note that letters should "wrap" around the alphabet; in other words, the letter z shifted by one returns the letter a Sample Input : abz key: 3 Output: dec Explanation: The CaesarCipherEncryptor function takes a string and a key (integer) as input and returns a new string obtained by shifting each character of the input string by the key number of positions to the right in the alphabet, wrapping around if necessary. The function first calculates the shift amount and offset value. The shift amount is calculated by taking the key modulo 26, which ensures that the shift amount is always within the range of 0 to 25. The offset value is calculated by taking 26, which is the number of letters in the alphabet. The function then converts the input string to a rune slice (for ease of manipulation). A rune is a single character in a programming language. The rune type is used to represent characters in Go. The function then iterates over each character in the rune slice. For each character, the function checks if the character is a lowercase letter and if shifting it will still be within the lowercase range. If the character is a lowercase letter and shifting it will still be within the lowercase range, the function simply adds the shift amount to the character. If the character is outside of the lowercase range after shifting, the function wraps it around by adding the shift amount - offset to the character. The function then updates the character in the rune slice. The function then converts the resulting rune slice back to a string and returns it. The time complexity of the CaesarCipherEncryptor function is O(n), where n is the length of the input string. This is because the function iterates over each character in the input string, performing a constant amount of work for each character. The space complexity of the CaesarCipherEncryptor function is O(n), where n is the length of the input string. This is because the function creates a new rune slice to store the encrypted string. The rune slice is the same size as the input string, so the space complexity is O(n). */ package main // CaesarCipherEncryptor takes a string and a key (integer) as input and returns // a new string obtained by shifting each character of the input string by the // key number of positions to the right in the alphabet, wrapping around if necessary. func CaesarCipherEncryptor(str string, key int) string { // Calculate the shift amount and offset value shift, offset := rune(key % 26), rune(26) // Convert the input string to a rune slice (for ease of manipulation) runes := []rune(str) // Iterate over each character in the rune slice for i, char := range runes { // If the character is a lowercase letter and shifting it will still be within the lowercase range if char >= 'a' && char + shift <= 'z' { char += shift } else { // If the character is outside of the lowercase range after shifting, wrap it around char += shift - offset } // Update the character in the rune slice runes[i] = char } // Convert the resulting rune slice back to a string and return it return string(runes) } ================================================ FILE: Arrays/ceaser_cipher.java ================================================ /* Given a non-empty string of lowercase letters and a non-negative integer representing a key, write a function that returns a new string obtained by shifting every letter in the input string by k positions in the alphabet, where k is the key. Note that letters should "wrap" around the alphabet; in other words, the letter z shifted by one returns the letter a Sample Input : abz key: 3 Output: dec Explanation: The CaesarCipherEncryptor function takes a string and a key (integer) as input and returns a new string obtained by shifting each character of the input string by the key number of positions to the right in the alphabet, wrapping around if necessary. The function first calculates the shift amount and offset value. The shift amount is calculated by taking the key modulo 26, which ensures that the shift amount is always within the range of 0 to 25. The offset value is calculated by taking 26, which is the number of letters in the alphabet. The function then converts the input string to a rune slice (for ease of manipulation). A rune is a single character in a programming language. The rune type is used to represent characters in Go. The function then iterates over each character in the rune slice. For each character, the function checks if the character is a lowercase letter and if shifting it will still be within the lowercase range. If the character is a lowercase letter and shifting it will still be within the lowercase range, the function simply adds the shift amount to the character. If the character is outside of the lowercase range after shifting, the function wraps it around by adding the shift amount - offset to the character. The function then updates the character in the rune slice. The function then converts the resulting rune slice back to a string and returns it. The time complexity of the CaesarCipherEncryptor function is O(n), where n is the length of the input string. This is because the function iterates over each character in the input string, performing a constant amount of work for each character. The space complexity of the CaesarCipherEncryptor function is O(n), where n is the length of the input string. This is because the function creates a new rune slice to store the encrypted string. The rune slice is the same size as the input string, so the space complexity is O(n). */ // Java import java.util.Scanner; public class CaesarCipherEncryptor { public static String encrypt(String str, int key) { // Calculate the shift amount and offset value int shift = key % 26; int offset = 26; // Convert the input string to a char array char[] chars = str.toCharArray(); // Iterate over each character in the char array for (int i = 0; i < chars.length; i++) { // If the character is a lowercase letter and shifting it will still be within the lowercase range if (chars[i] >= 'a' && chars[i] + shift <= 'z') { chars[i] += shift; } else { // If the character is outside of the lowercase range after shifting, wrap it around chars[i] += shift - offset; } } // Convert the resulting char array back to a string and return it return new String(chars); } public static void main(String[] args) { // Get the input string and key from the user Scanner scanner = new Scanner(System.in); System.out.println("Enter a string: "); String str = scanner.nextLine(); System.out.println("Enter a key: "); int key = scanner.nextInt(); // Encrypt the string using the Caesar cipher String encrypted_str = encrypt(str, key); // Print the encrypted string System.out.println("Encrypted string: " + encrypted_str); } } ================================================ FILE: Arrays/ceaser_cipher.js ================================================ /* Given a non-empty string of lowercase letters and a non-negative integer representing a key, write a function that returns a new string obtained by shifting every letter in the input string by k positions in the alphabet, where k is the key. Note that letters should "wrap" around the alphabet; in other words, the letter z shifted by one returns the letter a Sample Input : abz key: 3 Output: dec Explanation: The CaesarCipherEncryptor function takes a string and a key (integer) as input and returns a new string obtained by shifting each character of the input string by the key number of positions to the right in the alphabet, wrapping around if necessary. The function first calculates the shift amount and offset value. The shift amount is calculated by taking the key modulo 26, which ensures that the shift amount is always within the range of 0 to 25. The offset value is calculated by taking 26, which is the number of letters in the alphabet. The function then converts the input string to a rune slice (for ease of manipulation). A rune is a single character in a programming language. The rune type is used to represent characters in Go. The function then iterates over each character in the rune slice. For each character, the function checks if the character is a lowercase letter and if shifting it will still be within the lowercase range. If the character is a lowercase letter and shifting it will still be within the lowercase range, the function simply adds the shift amount to the character. If the character is outside of the lowercase range after shifting, the function wraps it around by adding the shift amount - offset to the character. The function then updates the character in the rune slice. The function then converts the resulting rune slice back to a string and returns it. The time complexity of the CaesarCipherEncryptor function is O(n), where n is the length of the input string. This is because the function iterates over each character in the input string, performing a constant amount of work for each character. The space complexity of the CaesarCipherEncryptor function is O(n), where n is the length of the input string. This is because the function creates a new rune slice to store the encrypted string. The rune slice is the same size as the input string, so the space complexity is O(n). */ function caesarCipherEncryptor(str, key) { // Calculate the shift amount and offset value let shift = key % 26; let offset = 26; // Convert the input string to a lowercase string str = str.toLowerCase(); // Iterate over each character in the string for (let i = 0; i < str.length; i++) { // If the character is a letter and shifting it will still be within the lowercase range if (str[i] >= "a" && str[i] + shift <= "z") { str[i] += shift; } else { // If the character is outside of the lowercase range after shifting, wrap it around str[i] += shift - offset; } } // Return the encrypted string return str; } // Example usage let str = "Hello, world!"; let key = 3; let encryptedStr = caesarCipherEncryptor(str, key); console.log(encryptedStr); // Output: "Khoor, zloor!" ================================================ FILE: Arrays/ceaser_cipher.py ================================================ ''' Given a non-empty string of lowercase letters and a non-negative integer representing a key, write a function that returns a new string obtained by shifting every letter in the input string by k positions in the alphabet, where k is the key. Note that letters should "wrap" around the alphabet; in other words, the letter z shifted by one returns the letter a Sample Input : abz key: 3 Output: dec Explanation: The CaesarCipherEncryptor function takes a string and a key (integer) as input and returns a new string obtained by shifting each character of the input string by the key number of positions to the right in the alphabet, wrapping around if necessary. The function first calculates the shift amount and offset value. The shift amount is calculated by taking the key modulo 26, which ensures that the shift amount is always within the range of 0 to 25. The offset value is calculated by taking 26, which is the number of letters in the alphabet. The function then converts the input string to a rune slice (for ease of manipulation). A rune is a single character in a programming language. The rune type is used to represent characters in Go. The function then iterates over each character in the rune slice. For each character, the function checks if the character is a lowercase letter and if shifting it will still be within the lowercase range. If the character is a lowercase letter and shifting it will still be within the lowercase range, the function simply adds the shift amount to the character. If the character is outside of the lowercase range after shifting, the function wraps it around by adding the shift amount - offset to the character. The function then updates the character in the rune slice. The function then converts the resulting rune slice back to a string and returns it. The time complexity of the CaesarCipherEncryptor function is O(n), where n is the length of the input string. This is because the function iterates over each character in the input string, performing a constant amount of work for each character. The space complexity of the CaesarCipherEncryptor function is O(n), where n is the length of the input string. This is because the function creates a new rune slice to store the encrypted string. The rune slice is the same size as the input string, so the space complexity is O(n). ''' # Python def caesar_cipher_encryptor(str, key): # Calculate the shift amount and offset value shift = key % 26 offset = 26 # Convert the input string to a list of characters chars = list(str) # Iterate over each character in the list for i in range(len(chars)): # If the character is a lowercase letter and shifting it will still be within the lowercase range if chars[i] >= 'a' and chars[i] + shift <= 'z': chars[i] += shift else: # If the character is outside of the lowercase range after shifting, wrap it around chars[i] += shift - offset # Convert the resulting list of characters back to a string and return it return ''.join(chars) ================================================ FILE: Arrays/dutch_national_flag.cpp ================================================ /* Given an array nums with n objects colored red, white, or blue, sort them in-place so that objects of the same color are adjacent, with the colors in the order red, white, and blue. We will use the integers 0, 1, and 2 to represent the color red, white, and blue, respectively. Input: nums = [2,0,2,1,1,0] Output: [0,0,1,1,2,2] Explanation: The algorithm partitions the array into three sections: elements with the value 0, elements with the value 1, and elements with the value 2. It uses three pointers, `start`, `low`, and `end`, to keep track of the boundaries between these sections. The algorithm iterates through the array using the `low` pointer. Here's how it works: 1. If the element at `low` is 0, it means it should be in the first section. In this case, the algorithm swaps the element with the element at the `start` position, increments both `start` and `low` pointers, and moves the `low` pointer to the next element to process. 2. If the element at `low` is 1, it means it should be in the second section. In this case, the algorithm simply increments the `low` pointer and moves to the next element to process. 3. If the element at `low` is 2, it means it should be in the third section. In this case, the algorithm swaps the element with the element at the `end` position, decrements the `end` pointer, and moves the `low` pointer to the next element to process. The reason for moving the `low` pointer to the next element is to recheck the value after the swap, as the swapped element could be 0 or 1. The algorithm continues these steps until the `low` pointer surpasses the `end` pointer, indicating that all elements have been processed. The "Dutch National Flag" algorithm has a time complexity of O(n), where n is the size of the input array. It performs a single pass through the array, ensuring that all elements are correctly placed in their respective sections. Note that the code assumes the input vector `nums` contains only values 0, 1, and 2, and it modifies the vector in-place to achieve the sorted order. Time complexity: O(n) Space complexity: O(1) */ #include using namespace std; void sortColors(vector &nums) { // created 3 variables start , low and end which are pointing start and low which are pointing to first index , end is pointing to last index . int start = 0, low = 0, end = nums.size() - 1; while (low <= end) { if (nums[low] == 0) // checking if element of low is 0 . If yes then swap to start and low . { swap(nums[low], nums[start]); start++, low++; } else if (nums[low] == 1) // checking if element at low index is 1 , If yes then increase the index by 1 . { low++; } else // else swap the element of low index to end . { swap(nums[low], nums[end]); end--; } } } int main() { vector nums{2, 0, 2, 1, 1, 0}; sortColors(nums); // Printing array's elements .. for (auto i : nums) { cout << i << " "; } } ================================================ FILE: Arrays/dutch_national_flag.go ================================================ /* Given an array nums with n objects colored red, white, or blue, sort them in-place so that objects of the same color are adjacent, with the colors in the order red, white, and blue. We will use the integers 0, 1, and 2 to represent the color red, white, and blue, respectively. Input: nums = [2,0,2,1,1,0] Output: [0,0,1,1,2,2] Explanation: The algorithm partitions the array into three sections: elements with the value 0, elements with the value 1, and elements with the value 2. It uses three pointers, `start`, `low`, and `end`, to keep track of the boundaries between these sections. The algorithm iterates through the array using the `low` pointer. Here's how it works: 1. If the element at `low` is 0, it means it should be in the first section. In this case, the algorithm swaps the element with the element at the `start` position, increments both `start` and `low` pointers, and moves the `low` pointer to the next element to process. 2. If the element at `low` is 1, it means it should be in the second section. In this case, the algorithm simply increments the `low` pointer and moves to the next element to process. 3. If the element at `low` is 2, it means it should be in the third section. In this case, the algorithm swaps the element with the element at the `end` position, decrements the `end` pointer, and moves the `low` pointer to the next element to process. The reason for moving the `low` pointer to the next element is to recheck the value after the swap, as the swapped element could be 0 or 1. The algorithm continues these steps until the `low` pointer surpasses the `end` pointer, indicating that all elements have been processed. The "Dutch National Flag" algorithm has a time complexity of O(n), where n is the size of the input array. It performs a single pass through the array, ensuring that all elements are correctly placed in their respective sections. Note that the code assumes the input vector `nums` contains only values 0, 1, and 2, and it modifies the vector in-place to achieve the sorted order. Time complexity: O(n) Space complexity: O(1) */ package main import "fmt" func sortColors(nums []int) { start := 0 low := 0 end := len(nums) - 1 for low <= end { if nums[low] == 0 { // Swap the element at low with the element at start nums[low], nums[start] = nums[start], nums[low] start++ low++ } else if nums[low] == 1 { // Move to the next element low++ } else { // Swap the element at low with the element at end nums[low], nums[end] = nums[end], nums[low] end-- } } } func main() { nums := []int{2, 0, 2, 1, 1, 0} sortColors(nums) fmt.Println(nums) } ================================================ FILE: Arrays/dutch_national_flag.java ================================================ /* Given an array nums with n objects colored red, white, or blue, sort them in-place so that objects of the same color are adjacent, with the colors in the order red, white, and blue. We will use the integers 0, 1, and 2 to represent the color red, white, and blue, respectively. Input: nums = [2,0,2,1,1,0] Output: [0,0,1,1,2,2] Explanation: The algorithm partitions the array into three sections: elements with the value 0, elements with the value 1, and elements with the value 2. It uses three pointers, `start`, `low`, and `end`, to keep track of the boundaries between these sections. The algorithm iterates through the array using the `low` pointer. Here's how it works: 1. If the element at `low` is 0, it means it should be in the first section. In this case, the algorithm swaps the element with the element at the `start` position, increments both `start` and `low` pointers, and moves the `low` pointer to the next element to process. 2. If the element at `low` is 1, it means it should be in the second section. In this case, the algorithm simply increments the `low` pointer and moves to the next element to process. 3. If the element at `low` is 2, it means it should be in the third section. In this case, the algorithm swaps the element with the element at the `end` position, decrements the `end` pointer, and moves the `low` pointer to the next element to process. The reason for moving the `low` pointer to the next element is to recheck the value after the swap, as the swapped element could be 0 or 1. The algorithm continues these steps until the `low` pointer surpasses the `end` pointer, indicating that all elements have been processed. The "Dutch National Flag" algorithm has a time complexity of O(n), where n is the size of the input array. It performs a single pass through the array, ensuring that all elements are correctly placed in their respective sections. Note that the code assumes the input vector `nums` contains only values 0, 1, and 2, and it modifies the vector in-place to achieve the sorted order. Time complexity: O(n) Space complexity: O(1) */ public class Main { public static void sortColors(int[] nums) { int start = 0; int low = 0; int end = nums.length - 1; while (low <= end) { if (nums[low] == 0) { // Swap the element at low with the element at start int temp = nums[low]; nums[low] = nums[start]; nums[start] = temp; start++; low++; } else if (nums[low] == 1) { // Move to the next element low++; } else { // Swap the element at low with the element at end int temp = nums[low]; nums[low] = nums[end]; nums[end] = temp; end--; } } } public static void main(String[] args) { int[] nums = {2, 0, 2, 1, 1, 0}; sortColors(nums); for (int num : nums) { System.out.print(num + " "); } } } ================================================ FILE: Arrays/dutch_national_flag.js ================================================ /* Given an array nums with n objects colored red, white, or blue, sort them in-place so that objects of the same color are adjacent, with the colors in the order red, white, and blue. We will use the integers 0, 1, and 2 to represent the color red, white, and blue, respectively. Input: nums = [2,0,2,1,1,0] Output: [0,0,1,1,2,2] Explanation: The algorithm partitions the array into three sections: elements with the value 0, elements with the value 1, and elements with the value 2. It uses three pointers, `start`, `low`, and `end`, to keep track of the boundaries between these sections. The algorithm iterates through the array using the `low` pointer. Here's how it works: 1. If the element at `low` is 0, it means it should be in the first section. In this case, the algorithm swaps the element with the element at the `start` position, increments both `start` and `low` pointers, and moves the `low` pointer to the next element to process. 2. If the element at `low` is 1, it means it should be in the second section. In this case, the algorithm simply increments the `low` pointer and moves to the next element to process. 3. If the element at `low` is 2, it means it should be in the third section. In this case, the algorithm swaps the element with the element at the `end` position, decrements the `end` pointer, and moves the `low` pointer to the next element to process. The reason for moving the `low` pointer to the next element is to recheck the value after the swap, as the swapped element could be 0 or 1. The algorithm continues these steps until the `low` pointer surpasses the `end` pointer, indicating that all elements have been processed. The "Dutch National Flag" algorithm has a time complexity of O(n), where n is the size of the input array. It performs a single pass through the array, ensuring that all elements are correctly placed in their respective sections. Note that the code assumes the input vector `nums` contains only values 0, 1, and 2, and it modifies the vector in-place to achieve the sorted order. Time complexity: O(n) Space complexity: O(1) */ function sortColors(nums) { let start = 0; let low = 0; let end = nums.length - 1; while (low <= end) { if (nums[low] === 0) { // Swap the element at low with the element at start [nums[low], nums[start]] = [nums[start], nums[low]]; start++; low++; } else if (nums[low] === 1) { // Move to the next element low++; } else { // Swap the element at low with the element at end [nums[low], nums[end]] = [nums[end], nums[low]]; end--; } } } // Example usage: const nums = [2, 0, 2, 1, 1, 0]; sortColors(nums); console.log(nums); ================================================ FILE: Arrays/dutch_national_flag.py ================================================ ''' Given an array nums with n objects colored red, white, or blue, sort them in-place so that objects of the same color are adjacent, with the colors in the order red, white, and blue. We will use the integers 0, 1, and 2 to represent the color red, white, and blue, respectively. Input: nums = [2,0,2,1,1,0] Output: [0,0,1,1,2,2] Explanation: The algorithm partitions the array into three sections: elements with the value 0, elements with the value 1, and elements with the value 2. It uses three pointers, `start`, `low`, and `end`, to keep track of the boundaries between these sections. The algorithm iterates through the array using the `low` pointer. Here's how it works: 1. If the element at `low` is 0, it means it should be in the first section. In this case, the algorithm swaps the element with the element at the `start` position, increments both `start` and `low` pointers, and moves the `low` pointer to the next element to process. 2. If the element at `low` is 1, it means it should be in the second section. In this case, the algorithm simply increments the `low` pointer and moves to the next element to process. 3. If the element at `low` is 2, it means it should be in the third section. In this case, the algorithm swaps the element with the element at the `end` position, decrements the `end` pointer, and moves the `low` pointer to the next element to process. The reason for moving the `low` pointer to the next element is to recheck the value after the swap, as the swapped element could be 0 or 1. The algorithm continues these steps until the `low` pointer surpasses the `end` pointer, indicating that all elements have been processed. The "Dutch National Flag" algorithm has a time complexity of O(n), where n is the size of the input array. It performs a single pass through the array, ensuring that all elements are correctly placed in their respective sections. Note that the code assumes the input vector `nums` contains only values 0, 1, and 2, and it modifies the vector in-place to achieve the sorted order. Time complexity: O(n) Space complexity: O(1) ''' def sortColors(nums): start = 0 low = 0 end = len(nums) - 1 while low <= end: if nums[low] == 0: # Swap the element at low with the element at start nums[low], nums[start] = nums[start], nums[low] start += 1 low += 1 elif nums[low] == 1: # Move to the next element low += 1 else: # Swap the element at low with the element at end nums[low], nums[end] = nums[end], nums[low] end -= 1 # Example usage: nums = [2, 0, 2, 1, 1, 0] sortColors(nums) print(nums) ================================================ FILE: Arrays/find_three_largest_integers.js ================================================ /* Write a function that takes in an array of at least three integers and, without sorting the input array, returns a sorted array of the three largest integers in the input array. Explanation: This code defines a function called `FindThreeLargestNumbers` that takes an array of integers as input and returns an array of the three largest integers in the input array. The `triplets` array is initialized with three smallest possible values. Then, the function iterates through the input array using a `for` loop and calls the `updateLargest` function to update the `triplets` array with the current number if it is larger than one of the values in the array. The `updateLargest` function takes two arguments: `triplets` and `num`. It first checks if `num` is greater than the third value in the `triplets` array. If so, it calls the `shiftAndUpdate` function to update the `triplets` array with the current number at the third index. If `num` is not greater than the third value in the `triplets` array, it checks if `num` is greater than the second value in the `triplets` array. If so, it calls the `shiftAndUpdate` function to update the `triplets` array with the current number at the second index. Finally, if `num` is not greater than either the third or second value in the `triplets` array, it checks if `num` is greater than the first value in the `triplets` array. If so, it calls the `shiftAndUpdate` function to update the `triplets` array with the current number at the first index. The `shiftAndUpdate` function takes three arguments: `triplets`, `num`, and `idx`. It iterates through the `triplets` array using a `for` loop and shifts each value to the left by one position until it reaches the `idx` index. Then it updates the value at the `idx` index with the current number `num`. Time and Space complexity : O(n) time | O(1) space - where n is the length of the input array */ function findThreeLargestNumbers(array) { // Initialize an array to hold the three largest numbers, starting with negative infinity let triplets = [-Infinity, -Infinity, -Infinity]; // Iterate through each number in the input array for (let num of array) { // Call the updateLargest function to determine if the number should be included in the triplet updateLargest(triplets, num); } // Return the array containing the three largest numbers return triplets; } function updateLargest(triplets, num) { // If the number is larger than the third-largest element in the triplet if (num > triplets[2]) { // Shift the other elements to make room and add the number as the new third-largest element shiftAndUpdate(triplets, num, 2); } // Otherwise, if the number is larger than the second-largest element else if (num > triplets[1]) { // Shift and update the triplet accordingly shiftAndUpdate(triplets, num, 1); } // Otherwise, if the number is larger than the first-largest element else if (num > triplets[0]) { // Shift and update the triplet accordingly shiftAndUpdate(triplets, num, 0); } } function shiftAndUpdate(triplets, num, idx) { // Iterate through the elements of the triplet for (let i = 0; i <= idx; i++) { // If the loop reaches the specified index, add the new number to the triplet if (i === idx) { triplets[i] = num; } // Otherwise, shift the elements to the right else { triplets[i] = triplets[i + 1]; } } } // Test the findThreeLargestNumbers function const array = [1, 5, 2, 9, 10, 3]; const result = findThreeLargestNumbers(array); console.log(result); ================================================ FILE: Arrays/find_three_largest_number.cpp ================================================ /* Write a function that takes in an array of at least three integers and, without sorting the input array, returns a sorted array of the three largest integers in the input array. */ /* Explanation This code defines a function called `FindThreeLargestNumbers` that takes an array of integers as input and returns an array of the three largest integers in the input array. The `triplets` array is initialized with three smallest possible values. Then, the function iterates through the input array using a `for` loop and calls the `updateLargest` function to update the `triplets` array with the current number if it is larger than one of the values in the array. The `updateLargest` function takes two arguments: `triplets` and `num`. It first checks if `num` is greater than the third value in the `triplets` array. If so, it calls the `shiftAndUpdate` function to update the `triplets` array with the current number at the third index. If `num` is not greater than the third value in the `triplets` array, it checks if `num` is greater than the second value in the `triplets` array. If so, it calls the `shiftAndUpdate` function to update the `triplets` array with the current number at the second index. Finally, if `num` is not greater than either the third or second value in the `triplets` array, it checks if `num` is greater than the first value in the `triplets` array. If so, it calls the `shiftAndUpdate` function to update the `triplets` array with the current number at the first index. The `shiftAndUpdate` function takes three arguments: `triplets`, `num`, and `idx`. It iterates through the `triplets` array using a `for` loop and shifts each value to the left by one position until it reaches the `idx` index. Then it updates the value at the `idx` index with the current number `num`. Time and Space complexity : O(n) time | O(1) space - where n is the length of the input array */ #include #include #include #include using namespace std; // Function to update the triplet if the input number is larger than any of its elements. void updateLargest(vector& triplets, int num); // Function to shift the elements of the triplet to make room for a new number and add the number to the specified index. void shiftAndUpdate(vector& triplets, int num, int idx); // Function to find the three largest integers in the input array in descending order. vector findThreeLargestNumbers(vector& array); int main() { // Create an input array. vector array = {141, 1, 17, -7, -17, -27, 18, 541, 8, 7, 7}; // Find the three largest integers in the input array. vector result = findThreeLargestNumbers(array); // Output the three largest integers in descending order. for (int num : result) { cout << num << " "; } cout << endl; return 0; } vector findThreeLargestNumbers(vector& array) { // Initialize a vector to hold the three largest integers, starting with negative infinity. vector triplets = {INT_MIN, INT_MIN, INT_MIN}; // Iterate over each number in the input array and call the updateLargest function to determine if it should be included in the triplet. for (int num : array) { updateLargest(triplets, num); } // Return the vector containing the three largest integers in descending order. return triplets; } void updateLargest(vector& triplets, int num) { // If the number is larger than the third-largest element in the triplet, shift the other elements to make room and add the number. if (num > triplets[2]) { shiftAndUpdate(triplets, num, 2); // Otherwise, if the number is larger than the second-largest element, shift and update the triplet accordingly. } else if (num > triplets[1]) { shiftAndUpdate(triplets, num, 1); // Otherwise, if the number is larger than the first-largest element, shift and update the triplet accordingly. } else if (num > triplets[0]) { shiftAndUpdate(triplets, num, 0); } } void shiftAndUpdate(vector& triplets, int num, int idx) { // Shift the elements of the triplet to the right starting at the specified index, and add the new number to the specified index. for (int i = 0; i < idx + 1; i++) { // If the loop reaches the specified index, add the new number to the triplet. if (i == idx) { triplets[i] = num; // Otherwise, shift the elements to the right. } else { triplets[i] = triplets[i + 1]; } } } ================================================ FILE: Arrays/find_three_largest_numbers.go ================================================ /* Write a function that takes in an array of at least three integers and, without sorting the input array, returns a sorted array of the three largest integers in the input array. Explanation: This code defines a function called `FindThreeLargestNumbers` that takes an array of integers as input and returns an array of the three largest integers in the input array. The `triplets` array is initialized with three smallest possible values. Then, the function iterates through the input array using a `for` loop and calls the `updateLargest` function to update the `triplets` array with the current number if it is larger than one of the values in the array. The `updateLargest` function takes two arguments: `triplets` and `num`. It first checks if `num` is greater than the third value in the `triplets` array. If so, it calls the `shiftAndUpdate` function to update the `triplets` array with the current number at the third index. If `num` is not greater than the third value in the `triplets` array, it checks if `num` is greater than the second value in the `triplets` array. If so, it calls the `shiftAndUpdate` function to update the `triplets` array with the current number at the second index. Finally, if `num` is not greater than either the third or second value in the `triplets` array, it checks if `num` is greater than the first value in the `triplets` array. If so, it calls the `shiftAndUpdate` function to update the `triplets` array with the current number at the first index. The `shiftAndUpdate` function takes three arguments: `triplets`, `num`, and `idx`. It iterates through the `triplets` array using a `for` loop and shifts each value to the left by one position until it reaches the `idx` index. Then it updates the value at the `idx` index with the current number `num`. Time and Space complexity : O(n) time | O(1) space - where n is the length of the input array */ package main import "math" // FindThreeLargestNumbers returns the three largest integers in the input array in descending order. func FindThreeLargestNumbers(array []int) []int { // Initialize a slice to hold the three largest integers, starting with negative infinity. triplets := []int{math.MinInt32, math.MinInt32, math.MinInt32} for _, num := range array { // For each number in the array, call the updateLargest function to determine if it should be included in the triplet. updateLargest(triplets, num) } return triplets } // updateLargest updates the triplet if the input number is larger than any of its elements. func updateLargest(triplets []int, num int) { // If the number is larger than the third-largest element in the triplet, shift the other elements to make room and add the number. if num > triplets[2] { shiftAndUpdate(triplets, num, 2) // Otherwise, if the number is larger than the second-largest element, shift and update the triplet accordingly. } else if num > triplets[1] { shiftAndUpdate(triplets, num, 1) // Otherwise, if the number is larger than the first-largest element, shift and update the triplet accordingly. } else if num > triplets[0] { shiftAndUpdate(triplets, num, 0) } } // shiftAndUpdate shifts the elements of the triplet to make room for a new number and adds the number to the specified index. func shiftAndUpdate(triplets []int, num int, idx int) { for i := 0; i < idx+1; i++ { // If the loop reaches the specified index, add the new number to the triplet. if i == idx { triplets[i] = num // Otherwise, shift the elements to the right. } else { triplets[i] = triplets[i+1] } } } ================================================ FILE: Arrays/find_three_largest_numbers.java ================================================ /* Write a function that takes in an array of at least three integers and, without sorting the input array, returns a sorted array of the three largest integers in the input array. Explanation: This code defines a function called `FindThreeLargestNumbers` that takes an array of integers as input and returns an array of the three largest integers in the input array. The `triplets` array is initialized with three smallest possible values. Then, the function iterates through the input array using a `for` loop and calls the `updateLargest` function to update the `triplets` array with the current number if it is larger than one of the values in the array. The `updateLargest` function takes two arguments: `triplets` and `num`. It first checks if `num` is greater than the third value in the `triplets` array. If so, it calls the `shiftAndUpdate` function to update the `triplets` array with the current number at the third index. If `num` is not greater than the third value in the `triplets` array, it checks if `num` is greater than the second value in the `triplets` array. If so, it calls the `shiftAndUpdate` function to update the `triplets` array with the current number at the second index. Finally, if `num` is not greater than either the third or second value in the `triplets` array, it checks if `num` is greater than the first value in the `triplets` array. If so, it calls the `shiftAndUpdate` function to update the `triplets` array with the current number at the first index. The `shiftAndUpdate` function takes three arguments: `triplets`, `num`, and `idx`. It iterates through the `triplets` array using a `for` loop and shifts each value to the left by one position until it reaches the `idx` index. Then it updates the value at the `idx` index with the current number `num`. Time and Space complexity : O(n) time | O(1) space - where n is the length of the input array */ import java.util.Arrays; public class Main { // Function to find the three largest numbers in the input array public static int[] findThreeLargestNumbers(int[] array) { // Initialize an array to hold the three largest numbers, starting with negative infinity int[] triplets = new int[]{Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE}; // Iterate through each number in the input array for (int num : array) { // Call the updateLargest function to determine if the number should be included in the triplet updateLargest(triplets, num); } // Return the array containing the three largest numbers return triplets; } // Function to update the triplet if the input number is larger than any of its elements private static void updateLargest(int[] triplets, int num) { // If the number is larger than the third-largest element in the triplet if (num > triplets[2]) { // Shift the other elements to make room and add the number as the new third-largest element shiftAndUpdate(triplets, num, 2); } // Otherwise, if the number is larger than the second-largest element else if (num > triplets[1]) { // Shift and update the triplet accordingly shiftAndUpdate(triplets, num, 1); } // Otherwise, if the number is larger than the first-largest element else if (num > triplets[0]) { // Shift and update the triplet accordingly shiftAndUpdate(triplets, num, 0); } } // Function to shift the elements of the triplet and add the new number to the specified index private static void shiftAndUpdate(int[] triplets, int num, int idx) { // Iterate through the elements of the triplet for (int i = 0; i < idx + 1; i++) { // If the loop reaches the specified index, add the new number to the triplet if (i == idx) { triplets[i] = num; } // Otherwise, shift the elements to the right else { triplets[i] = triplets[i + 1]; } } } // Main function to test the findThreeLargestNumbers function public static void main(String[] args) { int[] array = {1, 5, 2, 9, 10, 3}; int[] result = findThreeLargestNumbers(array); System.out.println(Arrays.toString(result)); } } ================================================ FILE: Arrays/find_three_largest_numbers.py ================================================ ''' Write a function that takes in an array of at least three integers and, without sorting the input array, returns a sorted array of the three largest integers in the input array. Explanation: This code defines a function called `FindThreeLargestNumbers` that takes an array of integers as input and returns an array of the three largest integers in the input array. The `triplets` array is initialized with three smallest possible values. Then, the function iterates through the input array using a `for` loop and calls the `updateLargest` function to update the `triplets` array with the current number if it is larger than one of the values in the array. The `updateLargest` function takes two arguments: `triplets` and `num`. It first checks if `num` is greater than the third value in the `triplets` array. If so, it calls the `shiftAndUpdate` function to update the `triplets` array with the current number at the third index. If `num` is not greater than the third value in the `triplets` array, it checks if `num` is greater than the second value in the `triplets` array. If so, it calls the `shiftAndUpdate` function to update the `triplets` array with the current number at the second index. Finally, if `num` is not greater than either the third or second value in the `triplets` array, it checks if `num` is greater than the first value in the `triplets` array. If so, it calls the `shiftAndUpdate` function to update the `triplets` array with the current number at the first index. The `shiftAndUpdate` function takes three arguments: `triplets`, `num`, and `idx`. It iterates through the `triplets` array using a `for` loop and shifts each value to the left by one position until it reaches the `idx` index. Then it updates the value at the `idx` index with the current number `num`. Time and Space complexity : O(n) time | O(1) space - where n is the length of the input array ''' def find_three_largest_numbers(array): # Initialize a list to hold the three largest numbers, starting with negative infinity triplets = [float('-inf'), float('-inf'), float('-inf')] # Iterate through each number in the input array for num in array: # Call the update_largest function to determine if the number should be included in the triplet update_largest(triplets, num) # Return the list containing the three largest numbers return triplets def update_largest(triplets, num): # If the number is larger than the third-largest element in the triplet if num > triplets[2]: # Shift the other elements to make room and add the number as the new third-largest element shift_and_update(triplets, num, 2) # Otherwise, if the number is larger than the second-largest element elif num > triplets[1]: # Shift and update the triplet accordingly shift_and_update(triplets, num, 1) # Otherwise, if the number is larger than the first-largest element elif num > triplets[0]: # Shift and update the triplet accordingly shift_and_update(triplets, num, 0) def shift_and_update(triplets, num, idx): # Iterate through the elements of the triplet for i in range(idx + 1): # If the loop reaches the specified index, add the new number to the triplet if i == idx: triplets[i] = num # Otherwise, shift the elements to the right else: triplets[i] = triplets[i + 1] # Test the find_three_largest_numbers function array = [1, 5, 2, 9, 10, 3] result = find_three_largest_numbers(array) print(result) ================================================ FILE: Arrays/first_duplicate_value.cpp ================================================ /* Given an array of integers between 1 and n, inclusive, where n is the length of the array, write a function that returns the first integer that appears more than once (when the array is read from left to right). Sample Input = [2, 1, 5, 2, 3, 3, 4] Output : 2 Please provide O(n) time and O(1) space solution along with O(n) time and O(n) space solution */ #include using namespace std; class Solution{ public: // O(N) time complexity and O(N) Space Complexity Solution int findDuplicate1(vector& nums) { int N=nums.size(); // Use Vector Instead of Unordered Set or Map for O(1) extraction time complexity vector trk(N,false); for(int i=0;i& nums) { int N=nums.size(); for(int i=0;i visited = new HashSet<>(); // iterate through the array for (int num : nums) { // check if the current element has already been visited if (visited.contains(num)) { // if it has, return the current element return num; } // otherwise, add it to the set of visited elements visited.add(num); } // if no duplicate is found, return -1 return -1; } ================================================ FILE: Arrays/first_duplicate_value.js ================================================ /* Given an array of integers between 1 and n, inclusive, where n is the length of the array, write a function that returns the first integer that appears more than once (when the array is read from left to right). Sample Input = [2, 1, 5, 2, 3, 3, 4] Output : 2 */ // O(n) time and O(1) space solution. // Approach: The approach utilizes the fact that the array contains integers between 1 and n, // where n is the length of the array. By negating the values at specific indices, // we can track which numbers have appeared before. If a number has appeared before, // its corresponding index will have a negative value. // This allows us to identify the first duplicate encountered during the iteration. function findFirstDuplicate(nums){ let n=nums.length; for(let i=0;i>The fourSum function takes in a list of integers (nums) and a target value (target). >>The code sorts the nums list in ascending order. >>It initializes an empty list res to store the resulting quadruplets. >>Code uses two nested loops to iterate over combinations of four numbers from the nums list. >>It avoids duplicates by skipping iterations when the current element is the same as the previous element in both the outer and inner loops. >>Inside nested loops, code uses two pointers (lo and hi) to find pairs of elements that sum up to the remaining target value. >>It compares the sum of the four elements with the target value and takes appropriate actions: . If sum equals the target, it adds the quadruplet to the result list res and skips any duplicate elements by moving the pointers accordingly. . If sum is less than the target, it increments the lo pointer to try larger values. . If sum is greater than the target, it decrements the hi pointer to try smaller values. >>After the nested loops, the function returns the resulting list of quadruplets res. ''' from typing import List class Solution: def fourSum(self, nums: List[int], target: int) -> List[List[int]]: n = len(nums) nums.sort() res = [] for i in range(n-3): # avoid the duplicates while moving i if i > 0 and nums[i] == nums[i - 1]: continue for j in range(i+1, n-2): # avoid the duplicates while moving j if j > i + 1 and nums[j] == nums[j - 1]: continue lo = j + 1 hi = n - 1 while lo < hi: temp = nums[i] + nums[j] + nums[lo] + nums[hi] if temp == target: res += [nums[i], nums[j], nums[lo], nums[hi]], # skip duplicates while lo < hi and nums[lo] == nums[lo + 1]: lo += 1 lo += 1 while lo < hi and nums[hi] == nums[hi - 1]: hi -= 1 hi -= 1 elif temp < target: lo += 1 else: hi -= 1 return res # Test case nums = [1, 0, -1, 0, -2, 2] target = 0 solution = Solution() result = solution.fourSum(nums, target) print(result) ================================================ FILE: Arrays/insert_interval.cpp ================================================ /* Insert Interval In this implementation, the `Interval` struct represents an interval with a start and end value. The `insert` function takes a sorted list of intervals and a new interval as input and returns a new list of intervals after merging the new interval with the existing intervals. Here's how the `insert` function works: 1. It initializes an empty `result` slice to store the merged intervals and sets the index `i` to 0. 2. It iterates over the existing intervals and adds intervals that end before the new interval starts to the `result` slice. 3. It merges intervals that overlap with the new interval by updating the start and end values of the new interval accordingly. 4. It adds the merged new interval to the `result` slice. 5. It adds any remaining intervals from the original list to the `result` slice. 6. Finally, it returns the `result` slice containing the merged intervals. The `min` and `max` functions are helper functions to find the minimum and maximum of two integers. In the `main` function, an example input is provided with a list of intervals and a new interval. The `insert` function is called with these inputs, and the result is printed to the console. Time Complexity: The time complexity is O(n), where n is the number of intervals in the input list. This is because we need to iterate through each interval in the list to merge and insert the new interval. In the worst case, we may need to traverse all intervals in the list. Space Complexity: The space complexity is O(n), where n is the number of intervals in the input list. This is because we create a new result slice to store the merged intervals, which can potentially contain all the intervals from the input list plus the merged new interval. Therefore, the space required is proportional to the number of intervals in the input list. Overall, the algorithm has a linear time complexity and linear space complexity with respect to the number of intervals in the input list. */ #include #include using namespace std; // Interval represents a closed interval [start, end]. struct Interval { int start; int end; Interval(int s, int e) : start(s), end(e) {} }; vector insertInterval(vector& intervals, Interval newInterval) { vector result; // Traverse through each interval in the input list // and perform the necessary merging and inserting. for (const auto& interval : intervals) { // If the current interval ends before the new interval starts, // add it to the result as it does not overlap. if (interval.end < newInterval.start) { result.push_back(interval); } // If the current interval starts after the new interval ends, // add the new interval and update it to the current interval // as there won't be any more overlap with subsequent intervals. else if (interval.start > newInterval.end) { result.push_back(newInterval); newInterval = interval; } // If there is an overlap between the current interval and the new interval, // merge them by updating the new interval's start and end. else { newInterval.start = min(interval.start, newInterval.start); newInterval.end = max(interval.end, newInterval.end); } } // Add the final merged or inserted interval to the result. result.push_back(newInterval); return result; } // Utility function to print the intervals. void printIntervals(const vector& intervals) { for (const auto& interval : intervals) { cout << "[" << interval.start << ", " << interval.end << "] "; } cout << endl; } int main() { // Example usage vector intervals = {Interval(1, 3), Interval(6, 9)}; Interval newInterval(2, 5); cout << "Original intervals: "; printIntervals(intervals); vector mergedIntervals = insertInterval(intervals, newInterval); cout << "Merged intervals: "; printIntervals(mergedIntervals); return 0; } ================================================ FILE: Arrays/insert_interval.go ================================================ /* Insert Interval In this implementation, the `Interval` struct represents an interval with a start and end value. The `insert` function takes a sorted list of intervals and a new interval as input and returns a new list of intervals after merging the new interval with the existing intervals. Here's how the `insert` function works: 1. It initializes an empty `result` slice to store the merged intervals and sets the index `i` to 0. 2. It iterates over the existing intervals and adds intervals that end before the new interval starts to the `result` slice. 3. It merges intervals that overlap with the new interval by updating the start and end values of the new interval accordingly. 4. It adds the merged new interval to the `result` slice. 5. It adds any remaining intervals from the original list to the `result` slice. 6. Finally, it returns the `result` slice containing the merged intervals. The `min` and `max` functions are helper functions to find the minimum and maximum of two integers. In the `main` function, an example input is provided with a list of intervals and a new interval. The `insert` function is called with these inputs, and the result is printed to the console. Time Complexity: The time complexity is O(n), where n is the number of intervals in the input list. This is because we need to iterate through each interval in the list to merge and insert the new interval. In the worst case, we may need to traverse all intervals in the list. Space Complexity: The space complexity is O(n), where n is the number of intervals in the input list. This is because we create a new result slice to store the merged intervals, which can potentially contain all the intervals from the input list plus the merged new interval. Therefore, the space required is proportional to the number of intervals in the input list. Overall, the algorithm has a linear time complexity and linear space complexity with respect to the number of intervals in the input list. */ package main import ( "fmt" ) // Interval represents an interval with a start and end value. type Interval struct { Start int End int } // insert merges a new interval into a sorted list of intervals. func insert(intervals []Interval, newInterval Interval) []Interval { result := make([]Interval, 0) i := 0 // Add intervals that end before the new interval starts for i < len(intervals) && intervals[i].End < newInterval.Start { result = append(result, intervals[i]) i++ } // Merge intervals that overlap with the new interval for i < len(intervals) && intervals[i].Start <= newInterval.End { newInterval.Start = min(newInterval.Start, intervals[i].Start) newInterval.End = max(newInterval.End, intervals[i].End) i++ } // Add the merged new interval result = append(result, newInterval) // Add remaining intervals for i < len(intervals) { result = append(result, intervals[i]) i++ } return result } // min returns the minimum of two integers. func min(a, b int) int { if a < b { return a } return b } // max returns the maximum of two integers. func max(a, b int) int { if a > b { return a } return b } func main() { intervals := []Interval{ {1, 3}, {6, 9}, } newInterval := Interval{2, 5} result := insert(intervals, newInterval) fmt.Println("Merged Intervals:", result) } ================================================ FILE: Arrays/insert_interval.java ================================================ /* Insert Interval In this implementation, the `Interval` struct represents an interval with a start and end value. The `insert` function takes a sorted list of intervals and a new interval as input and returns a new list of intervals after merging the new interval with the existing intervals. Here's how the `insert` function works: 1. It initializes an empty `result` slice to store the merged intervals and sets the index `i` to 0. 2. It iterates over the existing intervals and adds intervals that end before the new interval starts to the `result` slice. 3. It merges intervals that overlap with the new interval by updating the start and end values of the new interval accordingly. 4. It adds the merged new interval to the `result` slice. 5. It adds any remaining intervals from the original list to the `result` slice. 6. Finally, it returns the `result` slice containing the merged intervals. The `min` and `max` functions are helper functions to find the minimum and maximum of two integers. In the `main` function, an example input is provided with a list of intervals and a new interval. The `insert` function is called with these inputs, and the result is printed to the console. Time Complexity: The time complexity is O(n), where n is the number of intervals in the input list. This is because we need to iterate through each interval in the list to merge and insert the new interval. In the worst case, we may need to traverse all intervals in the list. Space Complexity: The space complexity is O(n), where n is the number of intervals in the input list. This is because we create a new result slice to store the merged intervals, which can potentially contain all the intervals from the input list plus the merged new interval. Therefore, the space required is proportional to the number of intervals in the input list. Overall, the algorithm has a linear time complexity and linear space complexity with respect to the number of intervals in the input list. */ import java.util.ArrayList; import java.util.List; class Interval { int start; int end; Interval(int start, int end) { this.start = start; this.end = end; } } public class InsertInterval { public static List insertInterval(List intervals, Interval newInterval) { List result = new ArrayList<>(); // Traverse through each interval in the input list // and perform the necessary merging and inserting. for (Interval interval : intervals) { // If the current interval ends before the new interval starts, // add it to the result as it does not overlap. if (interval.end < newInterval.start) { result.add(interval); } // If the current interval starts after the new interval ends, // add the new interval and update it to the current interval // as there won't be any more overlap with subsequent intervals. else if (interval.start > newInterval.end) { result.add(newInterval); newInterval = interval; } // If there is an overlap between the current interval and the new interval, // merge them by updating the new interval's start and end. else { newInterval.start = Math.min(interval.start, newInterval.start); newInterval.end = Math.max(interval.end, newInterval.end); } } // Add the final merged or inserted interval to the result. result.add(newInterval); return result; } // Utility function to print the intervals. public static void printIntervals(List intervals) { for (Interval interval : intervals) { System.out.print("[" + interval.start + ", " + interval.end + "] "); } System.out.println(); } public static void main(String[] args) { // Example usage List intervals = new ArrayList<>(); intervals.add(new Interval(1, 3)); intervals.add(new Interval(6, 9)); Interval newInterval = new Interval(2, 5); System.out.print("Original intervals: "); printIntervals(intervals); List mergedIntervals = insertInterval(intervals, newInterval); System.out.print("Merged intervals: "); printIntervals(mergedIntervals); } } ================================================ FILE: Arrays/insert_interval.js ================================================ /* Insert Interval In this implementation, the `Interval` struct represents an interval with a start and end value. The `insert` function takes a sorted list of intervals and a new interval as input and returns a new list of intervals after merging the new interval with the existing intervals. Here's how the `insert` function works: 1. It initializes an empty `result` slice to store the merged intervals and sets the index `i` to 0. 2. It iterates over the existing intervals and adds intervals that end before the new interval starts to the `result` slice. 3. It merges intervals that overlap with the new interval by updating the start and end values of the new interval accordingly. 4. It adds the merged new interval to the `result` slice. 5. It adds any remaining intervals from the original list to the `result` slice. 6. Finally, it returns the `result` slice containing the merged intervals. The `min` and `max` functions are helper functions to find the minimum and maximum of two integers. In the `main` function, an example input is provided with a list of intervals and a new interval. The `insert` function is called with these inputs, and the result is printed to the console. Time Complexity: The time complexity is O(n), where n is the number of intervals in the input list. This is because we need to iterate through each interval in the list to merge and insert the new interval. In the worst case, we may need to traverse all intervals in the list. Space Complexity: The space complexity is O(n), where n is the number of intervals in the input list. This is because we create a new result slice to store the merged intervals, which can potentially contain all the intervals from the input list plus the merged new interval. Therefore, the space required is proportional to the number of intervals in the input list. Overall, the algorithm has a linear time complexity and linear space complexity with respect to the number of intervals in the input list. */ class Interval { constructor(start, end) { this.start = start; this.end = end; } } function insertInterval(intervals, newInterval) { const mergedIntervals = []; let i = 0; // Skip all intervals that end before the new interval starts while (i < intervals.length && intervals[i].end < newInterval.start) { mergedIntervals.push(intervals[i]); i++; } // Merge intervals that overlap with the new interval while (i < intervals.length && intervals[i].start <= newInterval.end) { newInterval.start = Math.min(intervals[i].start, newInterval.start); newInterval.end = Math.max(intervals[i].end, newInterval.end); i++; } mergedIntervals.push(newInterval); // Add the remaining intervals to the merged intervals list while (i < intervals.length) { mergedIntervals.push(intervals[i]); i++; } return mergedIntervals; } // Example usage const intervals = [new Interval(1, 3), new Interval(6, 9)]; const newInterval = new Interval(2, 5); console.log("Original intervals:", intervals); const mergedIntervals = insertInterval(intervals, newInterval); console.log("Merged intervals:", mergedIntervals); ================================================ FILE: Arrays/insert_interval.py ================================================ ''' Insert Interval In this implementation, the `Interval` struct represents an interval with a start and end value. The `insert` function takes a sorted list of intervals and a new interval as input and returns a new list of intervals after merging the new interval with the existing intervals. Here's how the `insert` function works: 1. It initializes an empty `result` slice to store the merged intervals and sets the index `i` to 0. 2. It iterates over the existing intervals and adds intervals that end before the new interval starts to the `result` slice. 3. It merges intervals that overlap with the new interval by updating the start and end values of the new interval accordingly. 4. It adds the merged new interval to the `result` slice. 5. It adds any remaining intervals from the original list to the `result` slice. 6. Finally, it returns the `result` slice containing the merged intervals. The `min` and `max` functions are helper functions to find the minimum and maximum of two integers. In the `main` function, an example input is provided with a list of intervals and a new interval. The `insert` function is called with these inputs, and the result is printed to the console. Time Complexity: The time complexity is O(n), where n is the number of intervals in the input list. This is because we need to iterate through each interval in the list to merge and insert the new interval. In the worst case, we may need to traverse all intervals in the list. Space Complexity: The space complexity is O(n), where n is the number of intervals in the input list. This is because we create a new result slice to store the merged intervals, which can potentially contain all the intervals from the input list plus the merged new interval. Therefore, the space required is proportional to the number of intervals in the input list. Overall, the algorithm has a linear time complexity and linear space complexity with respect to the number of intervals in the input list. ''' class Interval: def __init__(self, start, end): self.start = start self.end = end def insertInterval(intervals, newInterval): mergedIntervals = [] i = 0 # Skip all intervals that end before the new interval starts while i < len(intervals) and intervals[i].end < newInterval.start: mergedIntervals.append(intervals[i]) i += 1 # Merge intervals that overlap with the new interval while i < len(intervals) and intervals[i].start <= newInterval.end: newInterval.start = min(intervals[i].start, newInterval.start) newInterval.end = max(intervals[i].end, newInterval.end) i += 1 mergedIntervals.append(newInterval) # Add the remaining intervals to the merged intervals list while i < len(intervals): mergedIntervals.append(intervals[i]) i += 1 return mergedIntervals # Example usage intervals = [ Interval(1, 3), Interval(6, 9) ] newInterval = Interval(2, 5) print('Original intervals:', [(i.start, i.end) for i in intervals]) mergedIntervals = insertInterval(intervals, newInterval) print('Merged intervals:', [(i.start, i.end) for i in mergedIntervals]) ================================================ FILE: Arrays/insert_intervals.cpp ================================================ /* You are given an array of non-overlapping intervals intervals where intervals[i] = [starti, endi] represent the start and the end of the ith interval and intervals is sorted in ascending order by starti. You are also given an interval newInterval = [start, end] that represents the start and end of another interval. Insert newInterval into intervals such that intervals is still sorted in ascending order by starti and intervals still does not have any overlapping intervals (merge overlapping intervals if necessary). Return intervals after the insertion. Example 1: Input: intervals = [[1,3],[6,9]], newInterval = [2,5] Output: [[1,5],[6,9]] */ class Solution { public: vector> insert(vector>& intervals, vector& newInterval) { // get the number of intervals and initialize an index variable int n = intervals.size(), i = 0; // initialize a vector to store the result vector> res; // loop through the intervals until the end or until the end of the first interval that comes after the new interval while(i < n && intervals[i][1] < newInterval[0]){ // add the current interval to the result res.push_back(intervals[i]); i++; } // loop through the intervals that overlap with the new interval while(i < n && newInterval[1] >= intervals[i][0]){ // update the start and end of the new interval to include the current interval newInterval[0] = min(newInterval[0], intervals[i][0]); newInterval[1] = max(newInterval[1], intervals[i][1]); i++; } // add the new interval to the result res.push_back(newInterval); // add the remaining intervals to the result while(i < n){ res.push_back(intervals[i]); i++; } // return the result return res; } }; ================================================ FILE: Arrays/insert_intervals.go ================================================ // 57. Insert Interval // You are given an array of non-overlapping intervals intervals where intervals[i] = [starti, endi] represent the start and the end of the ith interval and intervals is sorted in ascending order by starti. You are also given an interval newInterval = [start, end] that represents the start and end of another interval. // Insert newInterval into intervals such that intervals is still sorted in ascending order by starti and intervals still does not have any overlapping intervals (merge overlapping intervals if necessary). // Return intervals after the insertion. // Example 1: // Input: intervals = [[1,3],[6,9]], newInterval = [2,5] // Output: [[1,5],[6,9]] // Example 2: // Input: intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8] // Output: [[1,2],[3,10],[12,16]] // Explanation: Because the new interval [4,8] overlaps with [3,5],[6,7],[8,10]. // CODE EXPLAINATION WITH DRY RUN: // The insert function takes in two inputs: an array of non-overlapping intervals represented as an array of arrays of integers (intervals), and a single interval represented as an array of integers (newInterval). The function returns the updated intervals after inserting the new interval such that the intervals remain sorted by the start value and do not overlap. // The function first initializes some variables: // n: the new interval // j: the array of existing intervals // i: a counter variable initialized to 0 // m: the starting point of the new interval; initially set to the starting point of n // m1: the ending point of the new interval; initially set to the ending point of n // The function then enters a loop that iterates through each interval in the array of existing intervals j. For each iteration, it checks if the new interval overlaps with the current interval. An overlap occurs if any of the following conditions are met: // The starting point of the new interval is between the starting and ending points of the current interval // The ending point of the new interval is between the starting and ending points of the current interval // The new interval completely engulfs the current interval // If an overlap is detected, the function updates the starting point and ending point of the new interval (m and m1) based on the starting and ending points of the current interval (j[i][0] and j[i][1], respectively). It then removes the current interval from the array of existing intervals j using the append function with a slice operation that excludes the current element (append(j[:i], j[i+1:]...)) and decrements the counter variable i so that the loop will consider the next interval after removing the current one. // After iterating through all intervals in j, the function appends the new interval represented by [m, m1] to the end of the j array using the append function. Finally, the intervals in the array are sorted based on their starting points using two nested loops that swap elements if they are not in order. // Now, let's do a dry run of the example input and output: // Input: // intervals = [[1,3],[6,9]] // newInterval = [2,5] // The initial values of variables are: // n = [2,5] // j = [[1,3],[6,9]] // i = 0 // m = 2 // m1 = 5 // In the first iteration of the loop, the current interval is [1,3]. Since n overlaps with this interval, m is updated to 1 and m1 is updated to 5. The current interval is removed from j, which becomes [[6,9]]. The counter variable i is decremented to account for the removal, making its value -1. In the next iteration of the loop, i is incremented to 0, so the current interval is [6,9]. Since there is no overlap between n and this interval, nothing happens. The loop exits, and the updated j array is [[1,5],[6,9]], which matches the expected output. // Thus, the function correctly inserts the new interval and sorts the resulting array. package main import "fmt" func insert(intervals [][]int, newInterval []int) [][]int { n := newInterval j := intervals i := 0 m := n[0] // Initialize starting point of the interval to be inserted m1 := n[1] // Initialize ending point of the interval to be inserted for i < len(j) { if n[0] >= j[i][0] && n[0] <= j[i][1] || n[1] >= j[i][0] && n[1] <= j[i][1] || n[0] <= j[i][0] && n[1] >= j[i][1] { if j[i][0] < m { m = j[i][0] } if j[i][1] > m1 { m1 = j[i][1] } j = append(j[:i], j[i+1:]...) i-- } i++ } s1 := []int{m, m1} j = append(j, s1) for i := 0; i < len(j); i++ { for k := i + 1; k < len(j); k++ { if j[i][0] > j[k][0] { j[i], j[k] = j[k], j[i] } } } return j } func main() { intervals := [][]int{{1, 3}, {6, 9}} newInterval := []int{2, 5} result := insert(intervals, newInterval) fmt.Println(result) } ================================================ FILE: Arrays/is_monotonic.cpp ================================================ /* An array is said to be monotonic in nature if it is either continuously increasing or continuously decreasing. Mathematically, An array A is continuously increasing if for all i <= j, A[i] <= A[j]. The IsMonotonic function takes an array of integers and returns a boolean value indicating whether the array is monotonic or not. A monotonic array is one in which the elements are either non-increasing or non-decreasing. The function works by initializing two boolean variables, isNonDecreasing and isNonIncreasing, to true. It then iterates over the array from the first element to the second-to-last element, comparing each element to the next one. If the current element is less than the next element, it sets isNonDecreasing to false, indicating that the array is not non-decreasing. If the current element is greater than the next element, it sets isNonIncreasing to false, indicating that the array is not non-increasing. At the end of the loop, the function returns true if either isNonDecreasing or isNonIncreasing is still true, indicating that the array is monotonic. Otherwise, it returns false. O(n) time | O(1) space - where n is the length of the array */ #include #include using namespace std; bool IsMonotonic(vector& array) { bool isNonDecreasing = true; // flag to track if array is non-decreasing bool isNonIncreasing = true; // flag to track if array is non-increasing // iterate through the array starting at index 1 for (int i = 1; i < array.size(); i++) { if (array[i] < array[i - 1]) { // if the current element is less than the previous element, array is not non-decreasing isNonDecreasing = false; } if (array[i] > array[i - 1]) { // if the current element is greater than the previous element, array is not non-increasing isNonIncreasing = false; } } // if either flag is true, return true indicating that array is monotonic return isNonDecreasing || isNonIncreasing; } int main() { // example usage vector arr = {1, 2, 3, 3, 4, 5}; bool isMonotonic = IsMonotonic(arr); cout << "Array is monotonic: " << isMonotonic << endl; return 0; } ================================================ FILE: Arrays/is_monotonic.go ================================================ /* An array is said to be monotonic in nature if it is either continuously increasing or continuously decreasing. Mathematically, An array A is continuously increasing if for all i <= j, A[i] <= A[j]. The IsMonotonic function takes an array of integers and returns a boolean value indicating whether the array is monotonic or not. A monotonic array is one in which the elements are either non-increasing or non-decreasing. The function works by initializing two boolean variables, isNonDecreasing and isNonIncreasing, to true. It then iterates over the array from the first element to the second-to-last element, comparing each element to the next one. If the current element is less than the next element, it sets isNonDecreasing to false, indicating that the array is not non-decreasing. If the current element is greater than the next element, it sets isNonIncreasing to false, indicating that the array is not non-increasing. At the end of the loop, the function returns true if either isNonDecreasing or isNonIncreasing is still true, indicating that the array is monotonic. Otherwise, it returns false. O(n) time | O(1) space - where n is the length of the array */ package main import "fmt" func IsMonotonic(array []int) bool { // assume the array is non-decreasing until we find a decreasing element isNonDecreasing := true // assume the array is non-increasing until we find an increasing element isNonIncreasing := true for i := 1; i < len(array); i++ { if array[i] < array[i - 1] { // if the current element is less than the previous element, the array is not non-decreasing isNonDecreasing = false } if array[i] > array[i - 1] { // if the current element is greater than the previous element, the array is not non-increasing isNonIncreasing = false } } // return true if the array is either non-decreasing or non-increasing return isNonDecreasing || isNonIncreasing } func main() { Arr := []int{1, 2, 3, 4, 5} Arr2 := []int{5, 4, 3, 2, 1} fmt.Println(IsMonotonic(Arr)) fmt.Println(IsMonotonic(Arr2)) } ================================================ FILE: Arrays/is_monotonic.java ================================================ /* An array is said to be monotonic in nature if it is either continuously increasing or continuously decreasing. Mathematically, An array A is continuously increasing if for all i <= j, A[i] <= A[j]. The IsMonotonic function takes an array of integers and returns a boolean value indicating whether the array is monotonic or not. A monotonic array is one in which the elements are either non-increasing or non-decreasing. The function works by initializing two boolean variables, isNonDecreasing and isNonIncreasing, to true. It then iterates over the array from the first element to the second-to-last element, comparing each element to the next one. If the current element is less than the next element, it sets isNonDecreasing to false, indicating that the array is not non-decreasing. If the current element is greater than the next element, it sets isNonIncreasing to false, indicating that the array is not non-increasing. At the end of the loop, the function returns true if either isNonDecreasing or isNonIncreasing is still true, indicating that the array is monotonic. Otherwise, it returns false. O(n) time | O(1) space - where n is the length of the array */ public class Main { public static boolean isMonotonic(int[] array) { boolean isNonDecreasing = true; // Assume the array is non-decreasing until we find a decreasing element boolean isNonIncreasing = true; // Assume the array is non-increasing until we find an increasing element for (int i = 1; i < array.length; i++) { if (array[i] < array[i - 1]) { // If the current element is less than the previous element, the array is not non-decreasing isNonDecreasing = false; } if (array[i] > array[i - 1]) { // If the current element is greater than the previous element, the array is not non-increasing isNonIncreasing = false; } } // Return true if the array is either non-decreasing or non-increasing return isNonDecreasing || isNonIncreasing; } public static void main(String[] args) { int[] arr = { 1, 2, 3, 4, 5 }; int[] arr2 = { 5, 4, 3, 2, 1 }; System.out.println(isMonotonic(arr)); System.out.println(isMonotonic(arr2)); } } ================================================ FILE: Arrays/is_monotonic.js ================================================ /* An array is said to be monotonic in nature if it is either continuously increasing or continuously decreasing. Mathematically, An array A is continuously increasing if for all i <= j, A[i] <= A[j]. The IsMonotonic function takes an array of integers and returns a boolean value indicating whether the array is monotonic or not. A monotonic array is one in which the elements are either non-increasing or non-decreasing. The function works by initializing two boolean variables, isNonDecreasing and isNonIncreasing, to true. It then iterates over the array from the first element to the second-to-last element, comparing each element to the next one. If the current element is less than the next element, it sets isNonDecreasing to false, indicating that the array is not non-decreasing. If the current element is greater than the next element, it sets isNonIncreasing to false, indicating that the array is not non-increasing. At the end of the loop, the function returns true if either isNonDecreasing or isNonIncreasing is still true, indicating that the array is monotonic. Otherwise, it returns false. O(n) time | O(1) space - where n is the length of the array */ function isMonotonic(array) { let isNonDecreasing = true; // Assume the array is non-decreasing until we find a decreasing element let isNonIncreasing = true; // Assume the array is non-increasing until we find an increasing element for (let i = 1; i < array.length; i++) { if (array[i] < array[i - 1]) { // If the current element is less than the previous element, the array is not non-decreasing isNonDecreasing = false; } if (array[i] > array[i - 1]) { // If the current element is greater than the previous element, the array is not non-increasing isNonIncreasing = false; } } // Return true if the array is either non-decreasing or non-increasing return isNonDecreasing || isNonIncreasing; } const arr = [1, 2, 3, 4, 5]; const arr2 = [ ================================================ FILE: Arrays/is_monotonic.py ================================================ ''' An array is said to be monotonic in nature if it is either continuously increasing or continuously decreasing. Mathematically, An array A is continuously increasing if for all i <= j, A[i] <= A[j]. The IsMonotonic function takes an array of integers and returns a boolean value indicating whether the array is monotonic or not. A monotonic array is one in which the elements are either non-increasing or non-decreasing. The function works by initializing two boolean variables, isNonDecreasing and isNonIncreasing, to true. It then iterates over the array from the first element to the second-to-last element, comparing each element to the next one. If the current element is less than the next element, it sets isNonDecreasing to false, indicating that the array is not non-decreasing. If the current element is greater than the next element, it sets isNonIncreasing to false, indicating that the array is not non-increasing. At the end of the loop, the function returns true if either isNonDecreasing or isNonIncreasing is still true, indicating that the array is monotonic. Otherwise, it returns false. O(n) time | O(1) space - where n is the length of the array ''' def is_monotonic(array): is_non_decreasing = True # Assume the array is non-decreasing until we find a decreasing element is_non_increasing = True # Assume the array is non-increasing until we find an increasing element for i in range(1, len(array)): if array[i] < array[i - 1]: # If the current element is less than the previous element, the array is not non-decreasing is_non_decreasing = False if array[i] > array[i - 1]: # If the current element is greater than the previous element, the array is not non-increasing is_non_increasing = False # Return true if the array is either non-decreasing or non-increasing return is_non_decreasing or is_non_increasing arr = [1, 2, 3, 4, 5] arr2 = [5, 4, 3, 2, 1] print(is_monotonic(arr)) print(is_monotonic(arr2)) ================================================ FILE: Arrays/longest_peak.cpp ================================================ /* Write a function that takes in an array of integers and returns the length of the longest peak in the array. A peak is defined as adjacent integers in the array that are strictly increasing until they reach a tip (the highest value in the peak), at which point they become strictly decreasing. At least three integers are required to form a peak. The code defines a function named LongestPeak that takes an array of integers as an argument and returns an integer representing the length of the longest "peak" in the array. A "peak" is defined as a sequence of integers in the array that begins with an increasing sequence of integers, reaches a maximum value (the "peak"), and ends with a decreasing sequence of integers. The function first initializes a variable longestPeak to 0, which will be used to store the length of the longest peak found so far. It then initializes a variable i to 1, which will be used to iterate over the elements of the array. The function then enters a loop that continues until i is less than len(array) - 1. Inside the loop, the function checks whether the current element at i is a peak, by comparing it to its neighboring elements. If it is not a peak, the loop continues by incrementing i. If the current element at i is a peak, the function searches to the left and right of the peak to find the beginning and end of the peak. It does this by iterating left and right from the peak until it finds a decreasing sequence of integers, using the variables leftIndex and rightIndex. Once the function has found the beginning and end of the peak, it calculates the length of the peak using the formula rightIndex - leftIndex - 1. If the length of the current peak is greater than the current longest peak, it updates longestPeak to the length of the current peak. Finally, the function updates the value of i to be the end of the peak (rightIndex), so that the loop will skip over the entire peak and continue iterating from the end of the peak. The function returns the value of longestPeak once it has finished iterating over the array. The time complexity of the LongestPeak function is O(n), where n is the length of the input array, because it iterates through the array only once. The space complexity of the function is O(1), because it uses a constant amount of extra space, regardless of the size of the input array. */ #include int LongestPeak(std::vector& array) { int longestPeak = 0; int i = 1; while (i < array.size() - 1) { // Check if i is a peak (i.e., it's greater than its neighbors) bool isPeak = array[i - 1] < array[i] && array[i] > array[i + 1]; if (!isPeak) { // If i is not a peak, move to the next element i += 1; continue; } // Search left of i to find the beginning of the peak int leftIndex = i - 2; while (leftIndex >= 0 && array[leftIndex] < array[leftIndex + 1]) { leftIndex--; } // Search right of i to find the end of the peak int rightIndex = i + 2; while (rightIndex < array.size() && array[rightIndex] < array[rightIndex - 1]) { rightIndex++; } // Calculate the length of the current peak int currentPeak = rightIndex - leftIndex - 1; // Update longestPeak if currentPeak is longer if (currentPeak > longestPeak) { longestPeak = currentPeak; } // Move i to the end of the current peak i = rightIndex; } return longestPeak; } ================================================ FILE: Arrays/longest_peak.go ================================================ /* Write a function that takes in an array of integers and returns the length of the longest peak in the array. A peak is defined as adjacent integers in the array that are strictly increasing until they reach a tip (the highest value in the peak), at which point they become strictly decreasing. At least three integers are required to form a peak. The code defines a function named LongestPeak that takes an array of integers as an argument and returns an integer representing the length of the longest "peak" in the array. A "peak" is defined as a sequence of integers in the array that begins with an increasing sequence of integers, reaches a maximum value (the "peak"), and ends with a decreasing sequence of integers. The function first initializes a variable longestPeak to 0, which will be used to store the length of the longest peak found so far. It then initializes a variable i to 1, which will be used to iterate over the elements of the array. The function then enters a loop that continues until i is less than len(array) - 1. Inside the loop, the function checks whether the current element at i is a peak, by comparing it to its neighboring elements. If it is not a peak, the loop continues by incrementing i. If the current element at i is a peak, the function searches to the left and right of the peak to find the beginning and end of the peak. It does this by iterating left and right from the peak until it finds a decreasing sequence of integers, using the variables leftIndex and rightIndex. Once the function has found the beginning and end of the peak, it calculates the length of the peak using the formula rightIndex - leftIndex - 1. If the length of the current peak is greater than the current longest peak, it updates longestPeak to the length of the current peak. Finally, the function updates the value of i to be the end of the peak (rightIndex), so that the loop will skip over the entire peak and continue iterating from the end of the peak. The function returns the value of longestPeak once it has finished iterating over the array. The time complexity of the LongestPeak function is O(n), where n is the length of the input array, because it iterates through the array only once. The space complexity of the function is O(1), because it uses a constant amount of extra space, regardless of the size of the input array. */ package main import "fmt" // LongestPeak function takes an integer array and returns the length of the longest peak in the array. func LongestPeak(array []int) int { longestPeak := 0 i := 1 for i < len(array)-1 { // check if i is a peak (i.e., it's greater than its neighbors) isPeak := array[i-1] < array[i] && array[i] > array[i+1] if !isPeak { // if i is not a peak, move to the next element i += 1 continue } // search left of i to find the beginning of the peak leftIndex := i - 2 for leftIndex >= 0 && array[leftIndex] < array[leftIndex+1] { leftIndex-- } // search right of i to find the end of the peak rightIndex := i + 2 for rightIndex < len(array) && array[rightIndex] < array[rightIndex-1] { rightIndex++ } // calculate the length of the current peak currentPeak := rightIndex - leftIndex - 1 // update longestPeak if currentPeak is longer if currentPeak > longestPeak { longestPeak = currentPeak } // move i to the end of the current peak i = rightIndex } return longestPeak } func main() { array := []int{1, 2, 3, 4, 5, 4, 3, 2, 1} longestPeak := LongestPeak(array) fmt.Println(longestPeak) // Output: 9 } ================================================ FILE: Arrays/longest_peak.java ================================================ /* Write a function that takes in an array of integers and returns the length of the longest peak in the array. A peak is defined as adjacent integers in the array that are strictly increasing until they reach a tip (the highest value in the peak), at which point they become strictly decreasing. At least three integers are required to form a peak. The code defines a function named LongestPeak that takes an array of integers as an argument and returns an integer representing the length of the longest "peak" in the array. A "peak" is defined as a sequence of integers in the array that begins with an increasing sequence of integers, reaches a maximum value (the "peak"), and ends with a decreasing sequence of integers. The function first initializes a variable longestPeak to 0, which will be used to store the length of the longest peak found so far. It then initializes a variable i to 1, which will be used to iterate over the elements of the array. The function then enters a loop that continues until i is less than len(array) - 1. Inside the loop, the function checks whether the current element at i is a peak, by comparing it to its neighboring elements. If it is not a peak, the loop continues by incrementing i. If the current element at i is a peak, the function searches to the left and right of the peak to find the beginning and end of the peak. It does this by iterating left and right from the peak until it finds a decreasing sequence of integers, using the variables leftIndex and rightIndex. Once the function has found the beginning and end of the peak, it calculates the length of the peak using the formula rightIndex - leftIndex - 1. If the length of the current peak is greater than the current longest peak, it updates longestPeak to the length of the current peak. Finally, the function updates the value of i to be the end of the peak (rightIndex), so that the loop will skip over the entire peak and continue iterating from the end of the peak. The function returns the value of longestPeak once it has finished iterating over the array. The time complexity of the LongestPeak function is O(n), where n is the length of the input array, because it iterates through the array only once. The space complexity of the function is O(1), because it uses a constant amount of extra space, regardless of the size of the input array. */ ================================================ FILE: Arrays/longest_peak.js ================================================ /* Write a function that takes in an array of integers and returns the length of the longest peak in the array. A peak is defined as adjacent integers in the array that are strictly increasing until they reach a tip (the highest value in the peak), at which point they become strictly decreasing. At least three integers are required to form a peak. The code defines a function named LongestPeak that takes an array of integers as an argument and returns an integer representing the length of the longest "peak" in the array. A "peak" is defined as a sequence of integers in the array that begins with an increasing sequence of integers, reaches a maximum value (the "peak"), and ends with a decreasing sequence of integers. The function first initializes a variable longestPeak to 0, which will be used to store the length of the longest peak found so far. It then initializes a variable i to 1, which will be used to iterate over the elements of the array. The function then enters a loop that continues until i is less than len(array) - 1. Inside the loop, the function checks whether the current element at i is a peak, by comparing it to its neighboring elements. If it is not a peak, the loop continues by incrementing i. If the current element at i is a peak, the function searches to the left and right of the peak to find the beginning and end of the peak. It does this by iterating left and right from the peak until it finds a decreasing sequence of integers, using the variables leftIndex and rightIndex. Once the function has found the beginning and end of the peak, it calculates the length of the peak using the formula rightIndex - leftIndex - 1. If the length of the current peak is greater than the current longest peak, it updates longestPeak to the length of the current peak. Finally, the function updates the value of i to be the end of the peak (rightIndex), so that the loop will skip over the entire peak and continue iterating from the end of the peak. The function returns the value of longestPeak once it has finished iterating over the array. The time complexity of the LongestPeak function is O(n), where n is the length of the input array, because it iterates through the array only once. The space complexity of the function is O(1), because it uses a constant amount of extra space, regardless of the size of the input array. */ function longestPeak(array) { let longestPeak = 0; let i = 1; while (i < array.length - 1) { // Check if i is a peak (i.e., it's greater than its neighbors) const isPeak = array[i - 1] < array[i] && array[i] > array[i + 1]; if (!isPeak) { // If i is not a peak, move to the next element i += 1; continue; } // Search left of i to find the beginning of the peak let leftIndex = i - 2; while (leftIndex >= 0 && array[leftIndex] < array[leftIndex + 1]) { leftIndex--; } // Search right of i to find the end of the peak let rightIndex = i + 2; while ( rightIndex < array.length && array[rightIndex] < array[rightIndex - 1] ) { rightIndex++; } // Calculate the length of the current peak const currentPeak = rightIndex - leftIndex - 1; // Update longestPeak if currentPeak is longer if (currentPeak > longestPeak) { longestPeak = currentPeak; } // Move i to the end of the current peak i = rightIndex; } return longestPeak; } // Test the function const array = [1, 3, 2, 1, 4, 7, 3, 2, 1]; const result = longestPeak(array); console.log(result); // Output: 6 ================================================ FILE: Arrays/longest_peak.py ================================================ ''' Write a function that takes in an array of integers and returns the length of the longest peak in the array. A peak is defined as adjacent integers in the array that are strictly increasing until they reach a tip (the highest value in the peak), at which point they become strictly decreasing. At least three integers are required to form a peak. The code defines a function named LongestPeak that takes an array of integers as an argument and returns an integer representing the length of the longest "peak" in the array. A "peak" is defined as a sequence of integers in the array that begins with an increasing sequence of integers, reaches a maximum value (the "peak"), and ends with a decreasing sequence of integers. The function first initializes a variable longestPeak to 0, which will be used to store the length of the longest peak found so far. It then initializes a variable i to 1, which will be used to iterate over the elements of the array. The function then enters a loop that continues until i is less than len(array) - 1. Inside the loop, the function checks whether the current element at i is a peak, by comparing it to its neighboring elements. If it is not a peak, the loop continues by incrementing i. If the current element at i is a peak, the function searches to the left and right of the peak to find the beginning and end of the peak. It does this by iterating left and right from the peak until it finds a decreasing sequence of integers, using the variables leftIndex and rightIndex. Once the function has found the beginning and end of the peak, it calculates the length of the peak using the formula rightIndex - leftIndex - 1. If the length of the current peak is greater than the current longest peak, it updates longestPeak to the length of the current peak. Finally, the function updates the value of i to be the end of the peak (rightIndex), so that the loop will skip over the entire peak and continue iterating from the end of the peak. The function returns the value of longestPeak once it has finished iterating over the array. The time complexity of the LongestPeak function is O(n), where n is the length of the input array, because it iterates through the array only once. The space complexity of the function is O(1), because it uses a constant amount of extra space, regardless of the size of the input array. ''' def longest_peak(array): longest_peak = 0 i = 1 while i < len(array) - 1: # Check if i is a peak (i.e., it's greater than its neighbors) is_peak = array[i - 1] < array[i] > array[i + 1] if not is_peak: # If i is not a peak, move to the next element i += 1 continue # Search left of i to find the beginning of the peak left_index = i - 2 while left_index >= 0 and array[left_index] < array[left_index + 1]: left_index -= 1 # Search right of i to find the end of the peak right_index = i + 2 while right_index < len(array) and array[right_index] < array[right_index - 1]: right_index += 1 # Calculate the length of the current peak current_peak = right_index - left_index - 1 # Update longest_peak if current_peak is longer if current_peak > longest_peak: longest_peak = current_peak # Move i to the end of the current peak i = right_index return longest_peak # Test the function array = [1, 3, 2, 1, 4, 7, 3, 2, 1] result = longest_peak(array) print(result) # Output: 6 arr=[1, 2, 3, 3, 4, 0, 10, 6, 5, -1, -3, 2, 3] def longestPeak(arr: list) -> int: ans = 0 # iterate through the array from index 1 to len(arr) - 1 for indx in range(1, len(arr) - 1): # check if the current element is a peak if arr[indx - 1] < arr[indx] > arr[indx + 1]: # if it is a peak, then find the length of the peak uphill_start = downhill_ends = indx # go to the uphill start while uphill_start > 0 and arr[uphill_start] > arr[uphill_start - 1]: uphill_start -= 1 # go to the downhill end while downhill_ends + 1 < len(arr) and arr[downhill_ends] > arr[downhill_ends + 1]: downhill_ends += 1 # update the ans ans = max(ans, (downhill_ends - uphill_start + 1)) return ans print(longestPeak(arr)) # output: 6 ================================================ FILE: Arrays/majority_element.go ================================================ /* Given an array nums of size n, return the majority element. The majority element is the element that appears more than ⌊n / 2⌋ times. You may assume that the majority element always exists in the array. Example 1: Input: nums = [3,2,3] Output: 3 Example 2: Input: nums = [2,2,1,1,1,2,2] Output: 2 Constraints: n == nums.length 1 <= n <= 5 * 104 -109 <= nums[i] <= 109 Follow-up: Could you solve the problem in linear time and in O(1) space? */ package main import "fmt" //Boyer-Moore Voting Algorithm /* Source(https://leetcode.com/problems/majority-element/solutions/127412/majority-element/) Intuition If we had some way of counting instances of the majority element as +1+1+1 and instances of any other element as −1-1−1, summing them would make it obvious that the majority element is indeed the majority element. Algorithm Essentially, what Boyer-Moore does is look for a suffix sufsufsuf of nums where suf[0]suf[0]suf[0] is the majority element in that suffix. To do this, we maintain a count, which is incremented whenever we see an instance of our current candidate for majority element and decremented whenever we see anything else. Whenever count equals 0, we effectively forget about everything in nums up to the current index and consider the current number as the candidate for majority element. It is not immediately obvious why we can get away with forgetting prefixes of nums - consider the following examples (pipes are inserted to separate runs of nonzero count). [7, 7, 5, 7, 5, 1 | 5, 7 | 5, 5, 7, 7 | 7, 7, 7, 7] Here, the 7 at index 0 is selected to be the first candidate for majority element. count will eventually reach 0 after index 5 is processed, so the 5 at index 6 will be the next candidate. In this case, 7 is the true majority element, so by disregarding this prefix, we are ignoring an equal number of majority and minority elements - therefore, 7 will still be the majority element in the suffix formed by throwing away the first prefix. [7, 7, 5, 7, 5, 1 | 5, 7 | 5, 5, 7, 7 | 5, 5, 5, 5] Now, the majority element is 5 (we changed the last run of the array from 7s to 5s), but our first candidate is still 7. In this case, our candidate is not the true majority element, but we still cannot discard more majority elements than minority elements (this would imply that count could reach -1 before we reassign candidate, which is obviously false). Therefore, given that it is impossible (in both cases) to discard more majority elements than minority elements, we are safe in discarding the prefix and attempting to recursively solve the majority element problem for the suffix. Eventually, a suffix will be found for which count does not hit 0, and the majority element of that suffix will necessarily be the same as the majority element of the overall array. */ func MajorityElement(nums []int) int { count, value := 0, 0 for i := range nums { if count == 0 { value = nums[i] } if value == nums[i] { count++ } else { count-- } } return value } func main() { Arr := []int{2, 2, 2, 1, 1, 1, 3, 3, 3, 3, 2, 3} fmt.Println(MajorityElement(Arr)) } ================================================ FILE: Arrays/majority_element.java ================================================ /* Majority Element Given an array nums of size n, return the majority element. The majority element is the element that appears more than ⌊n / 2⌋ times. You may assume that the majority element always exists in the array. Example 1: Input: nums = [3,2,3] Output: 3 Example 2: Input: nums = [2,2,1,1,1,2,2] Output: 2 */ class Solution { public int majorityElement(int[] nums) { int count = 0; int target = nums[0]; for(int i=0; i #include using namespace std; // Function to find the maximum subarray sum using Kadane's algorithm int maxSubArraySum(vector& nums) { int maxSum = INT_MIN; int currSum = 0; // Iterate over each element in the array for (int i = 0; i < nums.size(); i++) { // Add the current element to the current sum currSum += nums[i]; // Update the maximum sum seen so far if (currSum > maxSum) { maxSum = currSum; } // If the current sum is negative, we reset it to zero if (currSum < 0) { currSum = 0; } } // Return the maximum sum return maxSum; } // Function to calculate maximum subarray sum using brute force approach int maxSubarraySumBruteForce(vector& nums) { int maxSum = INT_MIN; // Initialize maximum sum to smallest integer value // Consider all subarrays starting from i to j and calculate their sum for (int i = 0; i < nums.size(); i++) { int sum = 0; // Initialize sum for each subarray for (int j = i; j < nums.size(); j++) { sum += nums[j]; // Add element to sum // Update maxSum if sum is greater if (sum > maxSum) { maxSum = sum; } } } return maxSum; // Return maximum sum } // Driver code to test the function int main() { // Example usage vector nums = {-2, 1, -3, 4, -1, 2, 1, -5, 4}; int maxSum = maxSubArraySum(nums); cout << "Maximum subarray sum: " << maxSum << endl; maxSum = maxSubarraySumBruteForce(nums); cout << "Maximum subarray sum using brute force: " << maxSum << endl; return 0; } ================================================ FILE: Arrays/maximum_subarray_sum.go ================================================ // Maximum Subarray /* The maxSubarraySum function takes an integer slice arr as input and returns the maximum subarray sum as an integer. The maxSoFar variable is initialized to the smallest possible integer value, since any valid subarray sum must be greater than or equal to this value. The maxEndingHere variable is initialized to 0, since an empty subarray has a sum of 0. The function then iterates through the elements of arr, updating maxEndingHere and maxSoFar as necessary. At each iteration, the maximum ending here is updated by adding the current element to it. If the maximum ending here becomes negative, it is reset to 0, since any subarray that includes a negative sum will not be the maximum subarray. If the maximum ending here is greater than the maximum subarray sum so far, maxSoFar is updated to the new maximum. Finally, the function returns maxSoFar. In the main function, an example input array is defined and passed to maxSubarraySum. The resulting maximum subarray sum is printed to the console. The time complexity of the above implementation of Kadane's algorithm for finding the maximum subarray sum is O(n), where n is the length of the input array. This is because we are iterating over each element of the array only once. The space complexity of the implementation is O(1), as we are only using a constant amount of extra space for storing the maximum subarray sum and the current subarray sum. Example Input: arr = [-2, 1, -3, 4, -1, 2, 1, -5, 4] Example Output: 6 Explanation: The maximum subarray sum is [4, -1, 2, 1] which adds up to 6. */ package main import ( "fmt" "math" ) // Kadanes algorithm // This function returns the maximum subarray sum in a given slice of integers. // It takes an integer slice as input and returns the maximum subarray sum as an integer. func maxSubarraySum(arr []int) int { maxSoFar := math.MinInt32 // Initialize the maximum subarray sum to the smallest possible integer value maxEndingHere := 0 // Initialize the maximum ending here to 0 for _, num := range arr { // Update the maximum ending here maxEndingHere += num // If the maximum ending here is negative, we reset it to 0 if maxEndingHere < 0 { maxEndingHere = 0 } // If the maximum ending here is greater than the maximum subarray sum so far, // we update the maximum subarray sum so far if maxEndingHere > maxSoFar { maxSoFar = maxEndingHere } } return maxSoFar } // Brute Force Solution func maxSubarraySumBruteForce(nums []int) int { maxSum := math.MinInt32 // Initialize the maximum sum to the smallest possible integer n := len(nums) // Consider all possible subarrays and keep track of the maximum sum for i := 0; i < n; i++ { currSum := 0 // Initialize the current sum to 0 // Consider all subarrays starting from i and ending at j for j := i; j < n; j++ { currSum += nums[j] // Add the jth element to the current sum // Update the maximum sum if the current sum is greater if currSum > maxSum { maxSum = currSum } } } return maxSum } func main() { arr := []int{-2, 1, -3, 4, -1, 2, 1, -5, 4} maxSum := maxSubarraySum(arr) fmt.Println("Maximum subarray sum:", maxSum) maxSum = maxSubarraySumBruteForce(arr) fmt.Println("Maximum subarray sum using brute force:", maxSum) } ================================================ FILE: Arrays/maximum_subarray_sum.py ================================================ ''' The max_subarray_sum function takes an array arr as input and returns the maximum sum of a contiguous subarray in the given array. The function initializes two variables curr_sum and max_sum to the first element of the array. It then loops through the rest of the array starting from the second element. Inside the loop, the function updates the curr_sum variable by adding the current element to it. If the current sum is less than the current element, it means that starting a new subarray at this point will result in a greater sum, so the function starts a new subarray with the current element. The max_sum variable is then updated with the maximum value of max_sum and curr_sum. Once the loop is finished, the function returns the max_sum variable as the maximum sum of a contiguous subarray in the given array. The time complexity of this algorithm is O(n), where n is the length of the input array, since we only loop through the array once. The space complexity is O(1), since we only use a constant amount of extra space to store the current sum and maximum sum variables. Example Input: arr = [-2, 1, -3, 4, -1, 2, 1, -5, 4] Example Output: 6 Explanation: The maximum subarray sum is [4, -1, 2, 1] which adds up to 6. ''' def max_subarray_sum(arr): """ Returns the maximum sum of a contiguous subarray in the given array. Parameters: arr (list): A list of integers. Returns: int: The maximum sum of a contiguous subarray. """ # Initialize variables to keep track of the current sum and maximum sum curr_sum = max_sum = arr[0] # Loop through the array starting from the second element for i in range(1, len(arr)): # Update the current sum by adding the current element curr_sum += arr[i] # If the current sum is less than the current element, start a new subarray curr_sum = max(curr_sum, arr[i]) # Update the maximum sum if the current sum is greater max_sum = max(max_sum, curr_sum) # Return the maximum sum return max_sum arr = [-2, 1, -3, 4, -1, 2, 1, -5, 4] print(max_subarray_sum(arr)) ================================================ FILE: Arrays/merge_intervals.cpp ================================================ /* Array: Merge intervals in C++ #1113 Given an array of intervals where intervals[i] = [starti, endi], merge all overlapping intervals, and return an array of the non-overlapping intervals that cover all the intervals in the input. Example 1: Input: intervals = [[1,3],[2,6],[8,10],[15,18]] Output: [[1,6],[8,10],[15,18]] Explanation: Since intervals [1,3] and [2,6] overlap, merge them into [1,6]. */ class Solution { public: vector> merge(vector>& intervals) { vector>v; // Create an empty vector of vectors to store the merged intervals sort(intervals.begin(), intervals.end()); // Sort the intervals based on their start times vectorv1=intervals[0]; // Initialize the first merged interval to be the first interval in the sorted list for(int i=1; i") // get the end time of the current interval and the start and end time of the next interval currentIntervalEnd := currentInterval[1] nextIntervalStart, nextIntervalEnd := nextInterval[0], nextInterval[1] // if the end time of the current interval is greater than or equal to the start time of the next interval, // then the two intervals overlap and should be merged if currentIntervalEnd >= nextIntervalStart { // set the end time of the current interval to the maximum of its current end time and the end time of the next interval currentInterval[1] = max(currentIntervalEnd, nextIntervalEnd) } else { // if the two intervals do not overlap, then the next interval becomes the new current interval and is added to the merged intervals list currentInterval = nextInterval mergedIntervals = append(mergedIntervals, currentInterval) } } // return the list of merged intervals return mergedIntervals } // function to return the maximum of two integers func max(a, b int) int { if a > b { return a } return b } ================================================ FILE: Arrays/merge_intervals.java ================================================ /* Given an array of intervals where intervals[i] = [starti, endi], merge all overlapping intervals, and return an array of the non-overlapping intervals that cover all the intervals in the input. Example 1: Input: intervals = [[1,3],[2,6],[8,10],[15,18]] Output: [[1,6],[8,10],[15,18]] Explanation: Since intervals [1,3] and [2,6] overlap, merge them into [1,6]. */ int len = intervals.length; // Get the length of the input intervals array int i = 0; // Initialize a variable to keep track of the current interval being merged List result = new ArrayList<>(); // Create an empty ArrayList to store the merged intervals Arrays.sort(intervals, Comparator.comparingInt(a -> a[0])); // Sort the intervals based on their start times using a lambda expression int first = intervals[i][0]; // Initialize the start time of the first merged interval to be the start time of the first interval in the sorted list int second = intervals[i][1]; // Initialize the end time of the first merged interval to be the end time of the first interval in the sorted list while (i < len) { // Iterate through the intervals until all have been merged if (intervals[i][0] <= second) { // If the start time of the current interval is less than or equal to the end time of the current merged interval, they overlap second = Math.max(second, intervals[i][1]); // Update the end time of the current merged interval to be the maximum of the two end times } else { // If the current interval does not overlap with the current merged interval result.add(new int[]{first, second}); // Add the current merged interval to the output list first = intervals[i][0]; // Update the start time of the current merged interval to be the start time of the current interval second = intervals[i][1]; // Update the end time of the current merged interval to be the end time of the current interval } i++; // Move on to the next interval } result.add(new int[]{first, second}); // Add the last merged interval to the output list return result.toArray(new int[0][]); // Convert the output ArrayList to an array and return it ================================================ FILE: Arrays/merge_intervals.js ================================================ /* Problem definition: Given an array of intervals where intervals[i] = [starti, endi], merge all overlapping intervals, and return an array of the non-overlapping intervals that cover all the intervals in the input. Approach: The approach is to first sort the intervals by their start times. Then we initialize a new merged intervals array and loop through all the intervals. For each interval, if the merged array is empty or the current interval does not overlap with the previous interval, then we add the current interval to the merged array. Otherwise, we merge the current interval with the previous interval by updating the end time of the previous interval to the maximum of its original end time and the end time of the current interval. Finally, we return the merged array. Complexity: The time complexity of this solution is O(n log n) due to the initial sorting step. The space complexity is O(n) to store the merged intervals array. Sample input/outputs: Example 1: Input: [[1,4],[4,5]] Output: [[1,5]] Example 2: Input: [[1,3],[2,6],[8,10],[9,12]] Output: [[1,6],[8,12]] Example 3: Input: [[1,4],[0,4]] Output: [[0,4]] */ function merge(intervals) { // Sort the intervals by their start times intervals.sort((a, b) => a[0] - b[0]); // Initialize a new merged intervals array const merged = []; // Loop through all the intervals for (let i = 0; i < intervals.length; i++) { let interval = intervals[i]; // If the merged array is empty or the current interval does not overlap with the previous interval // then add the current interval to the merged array if (merged.length === 0 || interval[0] > merged[merged.length - 1][1]) { merged.push(interval); } else { // Otherwise, merge the current interval with the previous interval merged[merged.length - 1][1] = Math.max(merged[merged.length - 1][1], interval[1]); } } // Return the merged array return merged; } // Example usage: const intervals = [[1,3],[2,6],[8,10],[15,18]]; console.log(merge(intervals)); // [[1,6],[8,10],[15,18]] ================================================ FILE: Arrays/merge_intervals.py ================================================ #Program Author : TheCodeVenturer [Niraj Modi] ''' Problem definition: Given an array of intervals where intervals[i] = [starti, endi], merge all overlapping intervals, and return an array of the non-overlapping intervals that cover all the intervals in the input. Approach: The Approach to this problem is that it will be more easier if we start merging the time linear increasing fashion To Make It in Linear Increasing Fashion We will Sort the Array Then will initialise a new mergeIntervals Array. Here, sol then will pick the first starting and ending point and will iterate through the array .Here, start,end and insert it if the new start time is Greater then previous one(end) or will merge it with previous Start Time,end Time(start,end). and at finally when the loop is over again will insert the start and end for the final Interval then Will the mergedIntervals Array Complexity: Time Complexity: O(n logn) for sorting the array and additional O(n) for the complete iteration All Over It will be O(n logn) Space Complexity: O(1) as we are not using any extra Spaces Space to Store Solution is not counted in Space complexity Sample input/outputs: Example 1: Input: [[1,9],[4,5]] Output: [[1,9]] Example 2: Input: [[1,3],[2,6],[8,10],[9,12]] Output: [[1,6],[8,12]] Example 3: Input: [[1,4],[0,2]] Output: [[0,4]] ''' class Solution(object): def mergeIntervals(self, Intervals): Intervals.sort() # Sorting the Intervals to make the Array in Linear Increasing Fashion sol = [] # MergedIntervals Array Initialisation start = Intervals[0][0] end = Intervals[0][1] #setting up the first start and end Point for [l, u] in Intervals: # if l (current start) is Greater then previous end then will add it to mergedIntervals Array # else will update the end with the greater end that is max(end,u) if (l > end): sol.append([start, end]) start, end = l, u else: end = max(end, u) #finally adding the final start and end point to sol sol.append([start, end]) return sol if __name__ == "__main__": Intervals = [[1,3],[2,6],[8,10],[9,12]] sol = Solution() print(sol.mergeIntervals(Intervals)) ================================================ FILE: Arrays/merge_sorted_array.cpp ================================================ /* Problem Statement :- You are given two integer arrays nums1 and nums2, sorted in non-decreasing order, and two integers m and n, representing the number of elements in nums1 and nums2 respectively. Merge nums1 and nums2 into a single array sorted in non-decreasing order. The final sorted array should not be returned by the function, but instead be stored inside the array nums1. To accommodate this, nums1 has a length of m + n, where the first m elements denote the elements that should be merged, and the last n elements are set to 0 and should be ignored. nums2 has a length of n. Example 1 :- Input: nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3 Output: [1,2,2,3,5,6] Explanation: The arrays we are merging are [1,2,3] and [2,5,6]. The result of the merge is [1,2,2,3,5,6] with the underlined elements coming from nums1. Example 2 :- Input: nums1 = [1], m = 1, nums2 = [], n = 0 Output: [1] Explanation: The arrays we are merging are [1] and []. The result of the merge is [1]. Example 3 :- Input: nums1 = [0], m = 0, nums2 = [1], n = 1 Output: [1] Explanation: The arrays we are merging are [] and [1]. The result of the merge is [1]. Note that because m = 0, there are no elements in nums1. The 0 is only there to ensure the merge result can fit in nums1. */ // Two Pointer Approch From index 0; void merge( vector < int > & nums1 , int m , vector< int > & nums2 , int n ) { int i , j = 0 , u = 0 ; vector< int > temp ; for( i = 0 ; i < m ; i++ ) temp.push_back( nums1[i] ) ; // Copying the element form num1 that is needed to merged i = 0 ; while( i < m && j < n ){ if( temp[i] > nums2[j] ) nums1[ u++ ] = nums2[ j++ ] ; else nums1[ u++ ] = temp[ i++ ] ; } while( i < m ) nums1[ u++ ] = temp[ i++ ] ; while( j < n ) nums1[ u++ ] = nums2[ j++ ] ; } // Two Pointer Approch From index last void merge( vector< int > & nums1 , int m , vector< int > & nums2 , int n ) { int j = n-1 , i = m-1 , k = m+n-1 ; while( j >= 0 ){ if( i >= 0 && nums1[ i ] > nums2[ j ] ) nums1[ k-- ] = nums1[ i-- ] ; else nums1[ k-- ] = nums2[ j-- ] ; } } /* Tip :- Dry Run this code on Copy to Understand the key factors. */ ================================================ FILE: Arrays/merge_sorted_array.java ================================================ //You are given two integer arrays nums1 and nums2, sorted in non-decreasing order, and two integers // m and n, representing the number of elements in nums1 and nums2 respectively. //Merge nums1 and nums2 into a single array sorted in non-decreasing order. //The final sorted array should not be returned by the function, but instead be stored inside the array nums1. // To accommodate this, nums1 has a length of m + n, where the first m elements denote the elements that // should be merged, and the last n elements are set to 0 and should be ignored. nums2 has a length of n. import java.util.Arrays; class merge_sorted_array{ public void merge(int[] nums1, int m, int[] nums2, int n){ int[] merged=new int[m+n]; //new merged array //copying the first m elements from nums1 to the merged array if (m >= 0) System.arraycopy(nums1, 0, merged, 0, m); //copying the first n elements from nums2 to the merged array if (n >= 0) System.arraycopy(nums2, 0, merged, m + 0, n); //sorting the merged array Arrays.sort(merged); //copying the merged array to the nums1 array System.arraycopy(merged, 0, nums1, 0, nums1.length); } public static void main(String[] args){ int[] nums1 = {1, 2, 3, 0, 0, 0}; int m = 3, n = 3; int[] nums2 ={2, 5, 6}; merge_sorted_array is = new merge_sorted_array(); is.merge(nums1, m, nums2, n); for (int j : nums1) { System.out.print(j + " "); } } } ================================================ FILE: Arrays/merge_sorted_arrays.py ================================================ class Solution: def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None: """ Do not return anything, modify nums1 in-place instead. """ #Time Complexity - O(M+N) #Space Complexity - O(1) #Using similar to merge sort algorithm implementation. #Traversing in reverse order and comparing m,n values. #Placing the highest element last and continuing with the algorithm. i, j, k = m - 1, n - 1, m + n - 1 while(i >= 0 and j >= 0): if(nums1[i] > nums2[j]): nums1[k] = nums1[i] i -= 1 k -= 1 else: nums1[k] = nums2[j] j -= 1 k -= 1 #placing left over elements from num1 to nums1 while(i >= 0): nums1[k] = nums1[i] i -= 1 k -= 1 #placing left over elements from num2 to num1 while(j >= 0): nums1[k] = nums2[j] j -= 1 k -= 1 ================================================ FILE: Arrays/minimum_size_subarray_sum.java ================================================ /** * Given an array of positive integers nums and a positive integer target, return the minimal length of a subarray whose sum is greater than or equal to target. If there is no such subarray, return 0 instead. * * * * Example 1: * * Input: target = 7, nums = [2,3,1,2,4,3] * Output: 2 * Explanation: The subarray [4,3] has the minimal length under the problem constraint. * Example 2: * * Input: target = 4, nums = [1,4,4] * Output: 1 * Example 3: * * Input: target = 11, nums = [1,1,1,1,1,1,1,1] * Output: 0 * * * Constraints: * * 1 <= target <= 109 * 1 <= nums.length <= 105 * 1 <= nums[i] <= 104 * * https://leetcode.com/problems/minimum-size-subarray-sum/ */ public class MinimumSizeSubarraySum { public static void main(String[] args) { int[] nums = {2, 3, 1, 2, 4, 3}; int target = 7; int ans = solve(nums, target); System.out.println(ans); } public static int solve(int[] nums, int target) { // O(N) time | O(1) space // Sliding Window int leftIdx = 0, rightIdx = 0, currentSum = 0, minIdx = Integer.MAX_VALUE, len = nums.length; // Variable size sliding window: 2 - pointer while (rightIdx < len) { int currentNum = nums[rightIdx]; currentSum += currentNum; if (currentSum >= target) { // Check id currentSum >= target //Skip all left elements until currentSum < target (To find the smallest window) while (currentSum >= target) { currentSum -= nums[leftIdx++]; } int currentWindowSize = rightIdx - leftIdx + 1 + 1; // including leftIdx - 1 idx and update smallest window size. minIdx = Math.min(minIdx, currentWindowSize); } rightIdx++; } return minIdx == Integer.MAX_VALUE ? 0 : minIdx; // O(N^2) time | O(1) space // int ans = Integer.MAX_VALUE, len = nums.length; // for (int i = 0; i < len; i++) {; // int sum = 0; // for (int j = i; j < len; j++) { // int currentNum = nums[j]; // sum += currentNum; // if (sum >= target) { // ans = Math.min(ans, j - i + 1); // } // } // } // return ans == Integer.MAX_VALUE ? 0 : ans; } } ================================================ FILE: Arrays/move_element_to_end.cpp ================================================ /* Move Element to end Sample Input : [1, 0, 3, 0, 0, 5] To move: 0 Output : [1, 3, 5, 0, 0, 0] This is a function called MoveElementToEnd that takes an array of integers array and an integer toMove as input, and returns a modified array with all instances of toMove moved to the end of the array. The function first initializes an integer variable index to 0, which will keep track of the index of the first element in the array that is not equal to toMove. Then, it loops through the array using a for loop, and if the current element is not equal to toMove, it replaces the element at the index position with the current element and increments the index variable by 1. This effectively shifts all elements that are not equal to toMove to the beginning of the array. Next, the function loops through the remaining elements of the array (i.e., those that were not overwritten in the previous loop), and sets their value to toMove. This effectively moves all instances of toMove to the end of the array. Finally, the modified array is returned. O(n) time | O(1) space - where n is the length of the array */ #include std::vector MoveElementToEnd(std::vector& array, int toMove) { int index = 0; // initialize a variable to keep track of the index where elements should be moved to for (int i = 0; i < array.size(); i++) { // loop through the entire array if (array[i] != toMove) { // check if the current element is not equal to the element to be moved array[index] = array[i]; // move the current element to the left side of the array by replacing the element at the current index (index) with the current element (array[i]) index++; // increment the index variable by 1 to keep track of the index where the next non-target element should be moved } } for (int i = index; i < array.size(); i++) { // loop through the remaining elements in the array from index to the end array[i] = toMove; // set each element to be the target element } return array; // return the modified array } ================================================ FILE: Arrays/move_element_to_end.go ================================================ /* Move Element to end Sample Input : [1, 0, 3, 0, 0, 5] To move: 0 Output : [1, 3, 5, 0, 0, 0] This is a function called MoveElementToEnd that takes an array of integers array and an integer toMove as input, and returns a modified array with all instances of toMove moved to the end of the array. The function first initializes an integer variable index to 0, which will keep track of the index of the first element in the array that is not equal to toMove. Then, it loops through the array using a for loop, and if the current element is not equal to toMove, it replaces the element at the index position with the current element and increments the index variable by 1. This effectively shifts all elements that are not equal to toMove to the beginning of the array. Next, the function loops through the remaining elements of the array (i.e., those that were not overwritten in the previous loop), and sets their value to toMove. This effectively moves all instances of toMove to the end of the array. Finally, the modified array is returned. O(n) time | O(1) space - where n is the length of the array */ package main func MoveElementToEnd(array []int, toMove int) []int { index := 0 // initialize a variable to keep track of the index where elements should be moved to for i := 0; i < len(array); i++ { // loop through the entire array if array[i] != toMove { // check if the current element is not equal to the element to be moved array[index] = array[i] // move the current element to the left side of the array by replacing the element at the current index (index) with the current element (array[i]) index++ // increment the index variable by 1 to keep track of the index where the next non-target element should be moved } } for i := index; i < len(array); i++ { // loop through the remaining elements in the array from index to the end array[i] = toMove // set each element to be the target element } return array // return the modified array } ================================================ FILE: Arrays/move_element_to_end.java ================================================ /* Move Element to end Sample Input : [1, 0, 3, 0, 0, 5] To move: 0 Output : [1, 3, 5, 0, 0, 0] This is a function called MoveElementToEnd that takes an array of integers array and an integer toMove as input, and returns a modified array with all instances of toMove moved to the end of the array. The function first initializes an integer variable index to 0, which will keep track of the index of the first element in the array that is not equal to toMove. Then, it loops through the array using a for loop, and if the current element is not equal to toMove, it replaces the element at the index position with the current element and increments the index variable by 1. This effectively shifts all elements that are not equal to toMove to the beginning of the array. Next, the function loops through the remaining elements of the array (i.e., those that were not overwritten in the previous loop), and sets their value to toMove. This effectively moves all instances of toMove to the end of the array. Finally, the modified array is returned. O(n) time | O(1) space - where n is the length of the array */ import java.util.List; public class MoveElementToEnd { public static List moveElementToEnd(List array, int toMove) { int index = 0; // initialize a variable to keep track of the index where elements should be moved to for (int i = 0; i < array.size(); i++) { // loop through the entire array if (array.get(i) != toMove) { // check if the current element is not equal to the element to be moved array.set(index, array.get(i)); // move the current element to the left side of the array by replacing the element at the current index (index) with the current element (array.get(i)) index++; // increment the index variable by 1 to keep track of the index where the next non-target element should be moved } } for (int i = index; i < array.size(); i++) { // loop through the remaining elements in the array from index to the end array.set(i, toMove); // set each element to be the target element } return array; // return the modified array } } ================================================ FILE: Arrays/move_element_to_end.js ================================================ /* Move Element to end Sample Input : [1, 0, 3, 0, 0, 5] To move: 0 Output : [1, 3, 5, 0, 0, 0] This is a function called MoveElementToEnd that takes an array of integers array and an integer toMove as input, and returns a modified array with all instances of toMove moved to the end of the array. The function first initializes an integer variable index to 0, which will keep track of the index of the first element in the array that is not equal to toMove. Then, it loops through the array using a for loop, and if the current element is not equal to toMove, it replaces the element at the index position with the current element and increments the index variable by 1. This effectively shifts all elements that are not equal to toMove to the beginning of the array. Next, the function loops through the remaining elements of the array (i.e., those that were not overwritten in the previous loop), and sets their value to toMove. This effectively moves all instances of toMove to the end of the array. Finally, the modified array is returned. O(n) time | O(1) space - where n is the length of the array */ function moveElementToEnd(array, toMove) { let index = 0; // initialize a variable to keep track of the index where elements should be moved to for (let i = 0; i < array.length; i++) { // loop through the entire array if (array[i] !== toMove) { // check if the current element is not equal to the element to be moved array[index] = array[i]; // move the current element to the left side of the array by replacing the element at the current index (index) with the current element (array[i]) index++; // increment the index variable by 1 to keep track of the index where the next non-target element should be moved } } for (let i = index; i < array.length; i++) { // loop through the remaining elements in the array from index to the end array[i] = toMove; // set each element to be the target element } return array; // return the modified array } ================================================ FILE: Arrays/move_element_to_end.py ================================================ ''' Move 0's to end Sample Input : [1, 0, 3, 0, 0, 5] Output : [1, 3, 5, 0, 0, 0] This is a function called MoveElementToEnd that takes an array of integers array and an integer toMove as input, and returns a modified array with all instances of toMove moved to the end of the array. The function first initializes an integer variable index to 0, which will keep track of the index of the first element in the array that is not equal to toMove. Then, it loops through the array using a for loop, and if the current element is not equal to toMove, it replaces the element at the index position with the current element and increments the index variable by 1. This effectively shifts all elements that are not equal to toMove to the beginning of the array. Next, the function loops through the remaining elements of the array (i.e., those that were not overwritten in the previous loop), and sets their value to toMove. This effectively moves all instances of toMove to the end of the array. Finally, the modified array is returned. O(n) time | O(1) space - where n is the length of the array ''' def move_element_to_end(array, to_move): index = 0 # initialize a variable to keep track of the index where elements should be moved to for i in range(len(array)): # loop through the entire array if array[i] != to_move: # check if the current element is not equal to the element to be moved array[index] = array[i] # move the current element to the left side of the array by replacing the element at the current index (index) with the current element (array[i]) index += 1 # increment the index variable by 1 to keep track of the index where the next non-target element should be moved for i in range(index, len(array)): # loop through the remaining elements in the array from index to the end array[i] = to_move # set each element to be the target element return array # return the modified array ================================================ FILE: Arrays/non-overlapping intervals.cpp ================================================ // Given an array of intervals intervals where intervals[i] = [starti, endi], return the minimum number of intervals you need to remove to make the rest of the intervals non-overlapping. // Example 1: // Input: intervals = [[1,2],[2,3],[3,4],[1,3]] // Output: 1 // Explanation: [1,3] can be removed and the rest of the intervals are non-overlapping. // Example 2: // Input: intervals = [[1,2],[1,2],[1,2]] // Output: 2 // Explanation: You need to remove two [1,2] to make the rest of the intervals non-overlapping. // Example 3: // Input: intervals = [[1,2],[2,3]] // Output: 0 // Explanation: You don't need to remove any of the intervals since they're already non-overlapping. // Constraints: // 1 <= intervals.length <= 105 // intervals[i].length == 2 // -5 * 104 <= starti < endi <= 5 * 104 // CODE: class Solution { public: int eraseOverlapIntervals(vector>& intervals) { sort(intervals.begin(),intervals.end()); int previous = 0; int n = intervals.size(); int ans = 0; for(int current = 1;current using namespace std; // TC O(n) n is length of string bool is_permutation_pallindrome(string s){ // we built a hash table to count how many times each character appears // we then iterate through hashtable to find out no more than one character has // odd count vector table(26, 0); bool found_odd = false; for(int i = 0; i < s.length(); i++){ table[s[i]-'a']++; } for(int x : table){ if(x&1){ if(found_odd){ return false; } found_odd = true; } } return true; } bool is_permutation_pallindrome_improved(string s){ // instead of checking oddcounts at end we can check // as we traverse the chars in string, as soon as we reach end // we have out answer. vector table(26, 0); int count_odd = 0; for(int i = 0; i < s.length(); i++){ int x = s[i]-'a'; table[x]++; if(table[x] & 1){ count_odd++; } else{ count_odd--; } } return count_odd <= 1; } void print_ans(bool ans){ if(ans){ cout << "YES\n"; } else{ cout << "NO\n"; } } int main(){ is_permutation_pallindrome("aaabdddcba") ? print_ans(true) : print_ans(false); is_permutation_pallindrome("abab") ? print_ans(true) : print_ans(false); is_permutation_pallindrome("ABA") ? print_ans(true) : print_ans(false); is_permutation_pallindrome("abcddeab") ? print_ans(true) : print_ans(false); cout << "**********IMPROVED*********\n"; is_permutation_pallindrome_improved("a") ? print_ans(true) : print_ans(false); is_permutation_pallindrome_improved("ab") ? print_ans(true) : print_ans(false); is_permutation_pallindrome_improved("ABA") ? print_ans(true) : print_ans(false); is_permutation_pallindrome_improved("abcddeab") ? print_ans(true) : print_ans(false); return 0; } ================================================ FILE: Arrays/sign_of_the_product_of_an_array.cpp ================================================ // Leetcode 1822 : Sign of the Product of an Array /* QUESTION There is a function signFunc(x) that returns: 1 if x is positive. -1 if x is negative. 0 if x is equal to 0. You are given an integer array nums. Let product be the product of all values in the array nums. Return signFunc(product). Example 1: Input: nums = [-1,-2,-3,-4,3,2,1] Output: 1 Explanation: The product of all values in the array is 144, and signFunc(144) = 1 Example 2: Input: nums = [1,5,0,2,-3] Output: 0 Explanation: The product of all values in the array is 0, and signFunc(0) = 0 Example 3: Input: nums = [-1,1,-1,1,-1] Output: -1 Explanation: The product of all values in the array is -1, and signFunc(-1) = -1 Constraints: 1 <= nums.length <= 1000 -100 <= nums[i] <= 100 CODE EXPLANATION WITH DRY RUN Suppose we have the following input vector: {-1, -2, -3, -4, 3, 2, 1} We want to find the sign of the product of all elements in the vector. Here's how the code will execute: 1-We define the input vector nums in the main function. 2-We call the arraySign function with nums as the argument. 3-The arraySign function initializes the sign variable to 1. 4-The function iterates over each element of the nums vector using a range-based for loop. 5-On the first iteration, num is -1. The element is negative, so the sign variable is multiplied by -1, changing its value to -1. 6-On the second iteration, num is -2. The element is negative, so the sign variable is multiplied by -1 again, changing its value to 1. 7-On the third iteration, num is -3. The element is negative, so the sign variable is multiplied by -1, changing its value to -1. 8-On the fourth iteration, num is -4. The element is negative, so the sign variable is multiplied by -1 again, changing its value to 1. 9-On the fifth iteration, num is 3. The element is positive, so the sign variable is not changed and remains 1. 10-On the sixth iteration, num is 2. The element is positive, so the sign variable is not changed and remains 1. 11-On the seventh iteration, num is 1. The element is positive, so the sign variable is not changed and remains 1. 12-The arraySign function returns sign, which has a value of 1. 13-In the main function, we check if sign is equal to 1. It is, so we print "Product is Positive" to the console. Therefore, the output of the program will be: "Product is Positive" */ #include #include using namespace std; // Function to find the sign of the product of all elements in the input vector int arraySign(vector &nums) { int ans = 1; // Initialize answer to 1 // Iterate over each element of the vector for (int i = 0; i < nums.size(); i++) { // If element is negative, change answer to -1 if (nums[i] < 0) { ans *= -1; } // If element is zero, answer is 0 and loop is exited else if (nums[i] == 0) { ans = 0; break; } } return ans; // Return answer } int main() { // Example usage vector nums = {-1, -2, -3, -4, 3, 2, 1}; int ans = arraySign(nums); // Print the sign of the product of the vector if (ans == 1) { cout << "Product is Positive"; } else if (ans == -1) { cout << "Product is Negative"; } else { cout << "Product is Zero"; } return 0; } ================================================ FILE: Arrays/smallest_difference.cpp ================================================ /* Write a function that takes in two non-empty arrays of integers, finds the pair of numbers (one from each array) whose absolute difference is closest to zero, and returns an array containing these two numbers, with the number from the first array in the first position. Note that the absolute difference of two integers is the distance between them on the real number line. For example, the absolute difference of -5 and 5 is 10, and the absolute difference of -5 and -4 is 1. You can assume that there will only be one pair of numbers with the smallest difference. Sample Input Array1 = [-1, 5, 10, 20, 28, 3] Sample Input Array2 = [26, 134, 135, 15, 17] Sample Output = [28, 26] The code finds the pair of elements from two arrays, array1 and array2, whose absolute difference is the smallest among all possible pairs. It does this by first sorting both arrays and then using two pointers (idx1 and idx2) to iterate through the arrays. At each iteration, it compares the current element of array1 and array2, and updates smallest (the smallest absolute difference so far) and result (the pair of elements that give the smallest absolute difference) if the absolute difference between the current elements is smaller than the current smallest. The function returns the result at the end. Time complexity: O(nlog(n) + mlog(m)), where n and m are the lengths of array1 and array2, respectively Space complexity: O(1) */ #include #include #include #include using namespace std; // SmallestDifference takes in two Vector of integers and returns a pair of integers, one from each Vector, such that their // absolute difference is as close to zero as possible. If multiple pairs have the same absolute difference, SmallestDifference // returns the pair whose elements come first in the respective slices. vector SmallestDifference(vector array1, vector array2) { // Initialize variables to track the smallest absolute difference seen so far and the current absolute difference int current = INT_MAX, smallest = INT_MAX; // Sort the input arrays in ascending order to enable efficient searching for the pair with the smallest absolute difference sort(array1.begin(), array1.end()); sort(array2.begin(), array2.end()); // Initialize variables to track the current index in each array int idx1 = 0, idx2 = 0; // Initialize an empty vector to store the pair of integers with the smallest absolute difference vector result; // Loop through both arrays until the end of at least one of them is reached while (idx1 < array1.size() && idx2 < array2.size()) { // Get the current elements from both arrays int first = array1[idx1], second = array2[idx2]; // Compute the absolute difference between the current elements if (first < second) { current = second - first; idx1++; } else if (second < first) { current = first - second; idx2++; } else { // If the current elements are equal, we have found a pair with an absolute difference of 0 and can return it return vector{first, second}; } // If the current absolute difference is smaller than the smallest seen so far, update the smallest difference and the result vector if (smallest > current) { smallest = current; result = vector{first, second}; } } // Return the pair of integers with the smallest absolute difference return result; } int main() { vector array1 = {1, 3, 15, 11, 2}; vector array2 = {23, 127, 235, 19, 8}; vector result = SmallestDifference(array1, array2); cout << "Smallest difference pair: [" << result[0] << ", " << result[1] << "]" << endl; return 0; } ================================================ FILE: Arrays/smallest_difference.go ================================================ /* Write a function that takes in two non-empty arrays of integers, finds the pair of numbers (one from each array) whose absolute difference is closest to zero, and returns an array containing these two numbers, with the number from the first array in the first position. Note that the absolute difference of two integers is the distance between them on the real number line. For example, the absolute difference of -5 and 5 is 10, and the absolute difference of -5 and -4 is 1. You can assume that there will only be one pair of numbers with the smallest difference. Sample Input Array1 = [-1, 5, 10, 20, 28, 3] Sample Input Array2 = [26, 134, 135, 15, 17] Sample Output = [28, 26] This code implements the Smallest Difference problem which takes two arrays of integers as input and returns a pair of integers, one from each array, with the smallest absolute difference between them. The function first initializes two variables current and smallest to the maximum integer value. It then sorts both input arrays in ascending order using the sort.Ints function from the sort package. The function then iterates through both arrays using two pointers, idx1 and idx2, initialized to 0. Inside the loop, it compares the elements at the current indices of the two arrays, first and second, and calculates the absolute difference between them in the current variable. If current is smaller than the smallest variable, it updates smallest to current and assigns the current pair of integers to the result variable. The function returns the result variable, which contains the pair of integers with the smallest absolute difference. If there are identical integers in the two input arrays, the function will return them immediately, without any further comparisons. O(nlog(n) + mlog(m)) time | O(1) space - where n is the length of the first input array and m is the length of the second input array */ package main import ( "math" "sort" ) // SmallestDifference takes two integer slices as input and returns a slice with two integers. // The two integers in the returned slice have the smallest absolute difference among all pairs // of integers from the two input slices. func SmallestDifference(array1, array2 []int) []int { // Initialize variables for the smallest difference and the current difference being calculated current, smallest := math.MaxInt32, math.MaxInt32 // Sort the input slices sort.Ints(array1) sort.Ints(array2) // Initialize variables for the indices for the two slices idx1, idx2 := 0, 0 // Initialize an empty slice for the result result := []int{} // Loop through the two slices until we reach the end of one of the slices for idx1 < len(array1) && idx2 < len(array2) { // Get the values at the current indices for the two slices first, second := array1[idx1], array2[idx2] // Calculate the current difference between the two values if first < second { current = second - first idx1++ } else if second < first { current = first - second idx2++ } else { // If the two values are equal, we can return the pair return []int{first, second} } // Update the smallest difference and result slice if the current difference is smaller if smallest > current { smallest = current result = []int{first, second} } } // Return the pair with the smallest absolute difference return result } ================================================ FILE: Arrays/smallest_difference.java ================================================ /* Write a function that takes in two non-empty arrays of integers, finds the pair of numbers (one from each array) whose absolute difference is closest to zero, and returns an array containing these two numbers, with the number from the first array in the first position. Note that the absolute difference of two integers is the distance between them on the real number line. For example, the absolute difference of -5 and 5 is 10, and the absolute difference of -5 and -4 is 1. You can assume that there will only be one pair of numbers with the smallest difference. Sample Input Array1 = [-1, 5, 10, 20, 28, 3] Sample Input Array2 = [26, 134, 135, 15, 17] Sample Output = [28, 26] This code implements the Smallest Difference problem which takes two arrays of integers as input and returns a pair of integers, one from each array, with the smallest absolute difference between them. The function first initializes two variables current and smallest to the maximum integer value. It then sorts both input arrays in ascending order using the sort.Ints function from the sort package. The function then iterates through both arrays using two pointers, idx1 and idx2, initialized to 0. Inside the loop, it compares the elements at the current indices of the two arrays, first and second, and calculates the absolute difference between them in the current variable. If current is smaller than the smallest variable, it updates smallest to current and assigns the current pair of integers to the result variable. The function returns the result variable, which contains the pair of integers with the smallest absolute difference. If there are identical integers in the two input arrays, the function will return them immediately, without any further comparisons. O(nlog(n) + mlog(m)) time | O(1) space - where n is the length of the first input array and m is the length of the second input array */ import java.util.Arrays; public class Main { public static int[] smallestDifference(int[] array1, int[] array2) { // Initialize variables for the smallest difference and the current difference being calculated int current = Integer.MAX_VALUE; int smallest = Integer.MAX_VALUE; // Sort the input arrays Arrays.sort(array1); Arrays.sort(array2); // Initialize variables for the indices for the two arrays int idx1 = 0; int idx2 = 0; // Initialize an empty array for the result int[] result = new int[2]; // Loop through the two arrays until we reach the end of one of the arrays while (idx1 < array1.length && idx2 < array2.length) { // Get the values at the current indices for the two arrays int first = array1[idx1]; int second = array2[idx2]; // Calculate the current difference between the two values if (first < second) { current = second - first; idx1++; } else if (second < first) { current = first - second; idx2++; } else { // If the two values are equal, we can return the pair return new int[]{first, second}; } // Update the smallest difference and result array if the current difference is smaller if (smallest > current) { smallest = current; result[0] = first; result[1] = second; } } // Return the pair with the smallest absolute difference return result; } public static void main(String[] args) { int[] array1 = {1, 3, 5, 23, 11, 2}; int[] array2 = {8, 19, 3, 15, 9}; int[] result = smallestDifference(array1, array2); System.out.println(Arrays.toString(result)); // Output: [3, 3] } } ================================================ FILE: Arrays/smallest_difference.js ================================================ /* Write a function that takes in two non-empty arrays of integers, finds the pair of numbers (one from each array) whose absolute difference is closest to zero, and returns an array containing these two numbers, with the number from the first array in the first position. Note that the absolute difference of two integers is the distance between them on the real number line. For example, the absolute difference of -5 and 5 is 10, and the absolute difference of -5 and -4 is 1. You can assume that there will only be one pair of numbers with the smallest difference. Sample Input Array1 = [-1, 5, 10, 20, 28, 3] Sample Input Array2 = [26, 134, 135, 15, 17] Sample Output = [28, 26] The smallestDifference function takes in two arrays of integers and finds the pair of values (one from each array) that have the smallest difference. It does this by sorting the two arrays in ascending order, and then iterating through both arrays using two index variables. At each step, the function calculates the difference between the values pointed to by the index variables, updates the current difference, and moves the index of the array with the smaller value to the right. If the values are equal, the function returns the pair as an array. The function keeps track of the smallest difference seen so far, and updates the result array accordingly. Finally, the function returns the result array containing the pair with the smallest difference. O(nlog(n) + mlog(m)) time | O(1) space - where n is the length of the first input array and m is the length of the second input array */ /** * Given two arrays of integers, finds a pair of values (one value from each array) * that have the smallest difference. Returns the pair as an array. * @param {number[]} array1 - The first array of integers * @param {number[]} array2 - The second array of integers * @returns {number[]} - The pair of values that have the smallest difference */ function smallestDifference(array1, array2) { let currentDiff = Infinity; // initialize current difference to a large number let smallestDiff = Infinity; // initialize smallest difference to a large number let result = []; // initialize result array array1.sort((a, b) => a - b); // sort the first array in ascending order array2.sort((a, b) => a - b); // sort the second array in ascending order let idx1 = 0; // initialize index for first array let idx2 = 0; // initialize index for second array while (idx1 < array1.length && idx2 < array2.length) { const first = array1[idx1]; const second = array2[idx2]; if (first < second) { // if the value from first array is smaller currentDiff = second - first; // update the current difference idx1++; // move the index of first array to the right } else if (second < first) { // if the value from second array is smaller currentDiff = first - second; // update the current difference idx2++; // move the index of second array to the right } else { // if the values are equal, the smallest difference is 0 return [first, second]; } if (smallestDiff > currentDiff) { // if the current difference is smaller than the smallest difference so far smallestDiff = currentDiff; // update the smallest difference result = [first, second]; // update the result array } } return result; } ================================================ FILE: Arrays/smallest_difference.py ================================================ ''' Write a function that takes in two non-empty arrays of integers, finds the pair of numbers (one from each array) whose absolute difference is closest to zero, and returns an array containing these two numbers, with the number from the first array in the first position. Note that the absolute difference of two integers is the distance between them on the real number line. For example, the absolute difference of -5 and 5 is 10, and the absolute difference of -5 and -4 is 1. You can assume that there will only be one pair of numbers with the smallest difference. Sample Input Array1 = [-1, 5, 10, 20, 28, 3] Sample Input Array2 = [26, 134, 135, 15, 17] Sample Output = [28, 26] The smallest_difference function takes two arrays of integers as input and returns a list of two integers, where the first integer is from the first array and the second is from the second array, and the absolute difference between them is the smallest among all such pairs. It achieves this by sorting the two arrays and then iterating through them in parallel, keeping track of the current smallest difference and the pair of integers that produced it. The function returns the pair with the smallest difference. O(nlog(n) + mlog(m)) time | O(1) space - where n is the length of the first input array and m is the length of the second input array ''' from typing import List import math def smallest_difference(array1: List[int], array2: List[int]) -> List[int]: """ Finds the pair of integers, one from each of the two given arrays, with the smallest difference between them. Args: array1: A list of integers. array2: A list of integers. Returns: A list of two integers, where the first integer is from array1 and the second is from array2, and the absolute difference between them is the smallest among all such pairs. Example: >>> smallest_difference([1, 3, 15, 11, 2], [23, 127, 235, 19, 8]) [11, 8] """ current = math.inf smallest = math.inf result = [] # Sort both input arrays in ascending order array1.sort() array2.sort() # Initialize two pointers, one for each array idx1, idx2 = 0, 0 # Loop through both arrays while the pointers are still within bounds while idx1 < len(array1) and idx2 < len(array2): first, second = array1[idx1], array2[idx2] # Calculate the absolute difference between the two current numbers current = abs(first - second) # Update the smallest difference and the result if the current difference is smaller if current < smallest: smallest = current result = [first, second] # If the first number is smaller than the second number, increment the pointer # for the first array, otherwise increment the pointer for the second array. if first < second: idx1 += 1 else: idx2 += 1 # Return the result return result def main(): array1 = [1, 3, 15, 11, 2] array2 = [23, 127, 235, 19, 8] result = smallest_difference(array1, array2) print("Array 1:", array1) print("Array 2:", array2) print("Smallest Difference Pair:", result) if __name__ == "__main__": main() ================================================ FILE: Arrays/sorted_square_array,js ================================================ /* Write a function that takes in a non-empty array of integers that are sorted in ascending order and returns a new array of the same length with the squares of the original integers also sorted in ascending order. Sample Input: [-6, 1, 2, 3, 4] Output: [1, 4, 6, 16, 36] Explanation: The `SortedSquaredArray` function takes an integer array as input and returns a new array where each element is the square of the corresponding element in the input array. The new array is sorted in non-decreasing order. Here's a step-by-step explanation of the code: 1. Initialize an empty result array of the same length as the original array: `result := make([]int, len(array))`. 2. Initialize variables to mark the start and end positions of the array, as well as the end_pos, which is the index of the last element in the result array: `end_pos := len(array) - 1`, `start := 0`, `end := len(array) - 1`. 3. Inside the loop, calculate the square of the absolute value for the elements at the current start and end positions: `sq1 = array[start] * array[start]`, `sq2 = array[end] * array[end]`. 4. Compare `sq1` and `sq2` to determine which one is greater. If `sq1` is greater, assign it to the `end_pos` index of the result array and increment the start position by 1: `result[end_pos] = sq1`, `start++`. This ensures that the greater squared value is placed at the end of the result array. 5. If `sq2` is greater, assign it to the `end_pos` index of the result array and decrement the end position by 1: `result[end_pos] = sq2`, `end--`. This ensures that the greater squared value is still placed at the end of the result array. 6. Decrement the `end_pos` variable to move to the previous index in the result array: `end_pos--`. 7. Repeat the process until the start and end positions cross each other. 8. Finally, return the result array, which contains the squared values of the input array elements in non-decreasing order. The code effectively uses a two-pointer approach to compare the squared values of the elements at the start and end positions and places the greater squared value at the end of the result array. This ensures that the result array is sorted in non-decreasing order. The time complexity of the `SortedSquaredArray` function is O(n), where n is the length of the input array. This is because the function performs a single pass through the array to calculate the squares and populate the result array. The space complexity of the function is O(n) as well. This is because it creates a new result array of the same length as the input array to store the squared values. Therefore, the space required is proportional to the size of the input array. Overall, the function has a linear time complexity and linear space complexity. */ function sortedSquaredArray(array) { // Initialize an empty result array of the same length as the original array const result = Array(array.length).fill(0); // Set the start and end positions and the end_pos let end_pos = array.length - 1; let start = 0; let sq1 = 0; let sq2 = 0; let end = array.length - 1; // Using the two-pointer approach, calculate the square of the absolute value and add the greatest value to the end of the result array while (start <= end) { sq1 = array[start] * array[start]; sq2 = array[end] * array[end]; if (sq1 > sq2) { result[end_pos] = sq1; start++; // Square of the start pointer is greater, so increment start by 1 } else { result[end_pos] = sq2; end--; // Square of the end pointer is greater, so decrement end by 1 } end_pos--; } return result; } const arr = [-6, 1, 2, 3, 4, 5]; const result = sortedSquaredArray(arr); console.log(result); ================================================ FILE: Arrays/sorted_square_array.cpp ================================================ /* Write a function that takes in a non-empty array of integers that are sorted in ascending order and returns a new array of the same length with the squares of the original integers also sorted in ascending order. Sample Input: [-6, 1, 2, 3, 4] Output: [1, 4, 6, 16, 36] Explanation: The `SortedSquaredArray` function takes an integer array as input and returns a new array where each element is the square of the corresponding element in the input array. The new array is sorted in non-decreasing order. Here's a step-by-step explanation of the code: 1. Initialize an empty result array of the same length as the original array: `result := make([]int, len(array))`. 2. Initialize variables to mark the start and end positions of the array, as well as the end_pos, which is the index of the last element in the result array: `end_pos := len(array) - 1`, `start := 0`, `end := len(array) - 1`. 3. Inside the loop, calculate the square of the absolute value for the elements at the current start and end positions: `sq1 = array[start] * array[start]`, `sq2 = array[end] * array[end]`. 4. Compare `sq1` and `sq2` to determine which one is greater. If `sq1` is greater, assign it to the `end_pos` index of the result array and increment the start position by 1: `result[end_pos] = sq1`, `start++`. This ensures that the greater squared value is placed at the end of the result array. 5. If `sq2` is greater, assign it to the `end_pos` index of the result array and decrement the end position by 1: `result[end_pos] = sq2`, `end--`. This ensures that the greater squared value is still placed at the end of the result array. 6. Decrement the `end_pos` variable to move to the previous index in the result array: `end_pos--`. 7. Repeat the process until the start and end positions cross each other. 8. Finally, return the result array, which contains the squared values of the input array elements in non-decreasing order. The code effectively uses a two-pointer approach to compare the squared values of the elements at the start and end positions and places the greater squared value at the end of the result array. This ensures that the result array is sorted in non-decreasing order. The time complexity of the `SortedSquaredArray` function is O(n), where n is the length of the input array. This is because the function performs a single pass through the array to calculate the squares and populate the result array. The space complexity of the function is O(n) as well. This is because it creates a new result array of the same length as the input array to store the squared values. Therefore, the space required is proportional to the size of the input array. Overall, the function has a linear time complexity and linear space complexity. */ #include #include std::vector sortedSquaredArray(std::vector& array) { // Initialize an empty result vector of the same length as the original array std::vector result(array.size()); // Set the start and end positions and the end_pos int end_pos = array.size() - 1; int start = 0; int sq1 = 0; int sq2 = 0; int end = array.size() - 1; // Using the two-pointer approach, calculate the square of the absolute value and add the greatest value to the end of the result vector while (start <= end) { sq1 = array[start] * array[start]; sq2 = array[end] * array[end]; if (sq1 > sq2) { result[end_pos] = sq1; start++; // Square of the start pointer is greater, so increment start by 1 } else { result[end_pos] = sq2; end--; // Square of the end pointer is greater, so decrement end by 1 } end_pos--; } return result; } int main() { std::vector arr = {-6, 1, 2, 3, 4, 5}; std::vector result = sortedSquaredArray(arr); for (int num : result) { std::cout << num << " "; } std::cout << std::endl; return 0; } ================================================ FILE: Arrays/sorted_square_array.go ================================================ /* Write a function that takes in a non-empty array of integers that are sorted in ascending order and returns a new array of the same length with the squares of the original integers also sorted in ascending order. Sample Input: [-6, 1, 2, 3, 4] Output: [1, 4, 6, 16, 36] Explanation: The `SortedSquaredArray` function takes an integer array as input and returns a new array where each element is the square of the corresponding element in the input array. The new array is sorted in non-decreasing order. Here's a step-by-step explanation of the code: 1. Initialize an empty result array of the same length as the original array: `result := make([]int, len(array))`. 2. Initialize variables to mark the start and end positions of the array, as well as the end_pos, which is the index of the last element in the result array: `end_pos := len(array) - 1`, `start := 0`, `end := len(array) - 1`. 3. Inside the loop, calculate the square of the absolute value for the elements at the current start and end positions: `sq1 = array[start] * array[start]`, `sq2 = array[end] * array[end]`. 4. Compare `sq1` and `sq2` to determine which one is greater. If `sq1` is greater, assign it to the `end_pos` index of the result array and increment the start position by 1: `result[end_pos] = sq1`, `start++`. This ensures that the greater squared value is placed at the end of the result array. 5. If `sq2` is greater, assign it to the `end_pos` index of the result array and decrement the end position by 1: `result[end_pos] = sq2`, `end--`. This ensures that the greater squared value is still placed at the end of the result array. 6. Decrement the `end_pos` variable to move to the previous index in the result array: `end_pos--`. 7. Repeat the process until the start and end positions cross each other. 8. Finally, return the result array, which contains the squared values of the input array elements in non-decreasing order. The code effectively uses a two-pointer approach to compare the squared values of the elements at the start and end positions and places the greater squared value at the end of the result array. This ensures that the result array is sorted in non-decreasing order. The time complexity of the `SortedSquaredArray` function is O(n), where n is the length of the input array. This is because the function performs a single pass through the array to calculate the squares and populate the result array. The space complexity of the function is O(n) as well. This is because it creates a new result array of the same length as the input array to store the squared values. Therefore, the space required is proportional to the size of the input array. Overall, the function has a linear time complexity and linear space complexity. */ package main import "fmt" func SortedSquaredArray(array []int) []int { // initialize empty result array of same length as original array result := make([]int, len(array)) // mark start and end, and end_pos end_pos := len(array) - 1 start := 0 sq1 := 0 sq2 := 0 end := len(array) - 1 // using two pointer appraoch take the absolute value's square // add the greatest at end of output array for start <= end { sq1 = array[start] * array[start] sq2 = array[end] * array[end] if sq1 > sq2 { result[end_pos] = sq1 start++ // square of start pointer is greater so increment start by 1 } else { result[end_pos] = sq2 end-- // // square of end pointer is greater so decrement end by 1 } end_pos-- } return result } func main() { arr := []int{-6, 1, 2, 3, 4, 5} msg := SortedSquaredArray(arr) fmt.Println(msg) } ================================================ FILE: Arrays/sorted_square_array.java ================================================ /* Write a function that takes in a non-empty array of integers that are sorted in ascending order and returns a new array of the same length with the squares of the original integers also sorted in ascending order. Sample Input: [-6, 1, 2, 3, 4] Output: [1, 4, 6, 16, 36] Explanation: The `SortedSquaredArray` function takes an integer array as input and returns a new array where each element is the square of the corresponding element in the input array. The new array is sorted in non-decreasing order. Here's a step-by-step explanation of the code: 1. Initialize an empty result array of the same length as the original array: `result := make([]int, len(array))`. 2. Initialize variables to mark the start and end positions of the array, as well as the end_pos, which is the index of the last element in the result array: `end_pos := len(array) - 1`, `start := 0`, `end := len(array) - 1`. 3. Inside the loop, calculate the square of the absolute value for the elements at the current start and end positions: `sq1 = array[start] * array[start]`, `sq2 = array[end] * array[end]`. 4. Compare `sq1` and `sq2` to determine which one is greater. If `sq1` is greater, assign it to the `end_pos` index of the result array and increment the start position by 1: `result[end_pos] = sq1`, `start++`. This ensures that the greater squared value is placed at the end of the result array. 5. If `sq2` is greater, assign it to the `end_pos` index of the result array and decrement the end position by 1: `result[end_pos] = sq2`, `end--`. This ensures that the greater squared value is still placed at the end of the result array. 6. Decrement the `end_pos` variable to move to the previous index in the result array: `end_pos--`. 7. Repeat the process until the start and end positions cross each other. 8. Finally, return the result array, which contains the squared values of the input array elements in non-decreasing order. The code effectively uses a two-pointer approach to compare the squared values of the elements at the start and end positions and places the greater squared value at the end of the result array. This ensures that the result array is sorted in non-decreasing order. The time complexity of the `SortedSquaredArray` function is O(n), where n is the length of the input array. This is because the function performs a single pass through the array to calculate the squares and populate the result array. The space complexity of the function is O(n) as well. This is because it creates a new result array of the same length as the input array to store the squared values. Therefore, the space required is proportional to the size of the input array. Overall, the function has a linear time complexity and linear space complexity. */ import java.util.Arrays; public class Main { public static int[] sortedSquaredArray(int[] array) { // Initialize an empty result array of the same length as the original array int[] result = new int[array.length]; // Set the start and end positions and the end_pos int end_pos = array.length - 1; int start = 0; int sq1 = 0; int sq2 = 0; int end = array.length - 1; // Using the two-pointer approach, calculate the square of the absolute value and add the greatest value to the end of the result array while (start <= end) { sq1 = array[start] * array[start]; sq2 = array[end] * array[end]; if (sq1 > sq2) { result[end_pos] = sq1; start++; // Square of the start pointer is greater, so increment start by 1 } else { result[end_pos] = sq2; end--; // Square of the end pointer is greater, so decrement end by 1 } end_pos--; } return result; } public static void main(String[] args) { int[] arr = {-6, 1, 2, 3, 4, 5}; int[] result = sortedSquaredArray(arr); for (int num : result) { System.out.print(num + " "); } System.out.println(); } } ================================================ FILE: Arrays/sorted_square_array.py ================================================ ''' Write a function that takes in a non-empty array of integers that are sorted in ascending order and returns a new array of the same length with the squares of the original integers also sorted in ascending order. Sample Input: [-6, 1, 2, 3, 4] Output: [1, 4, 6, 16, 36] Explanation: The `SortedSquaredArray` function takes an integer array as input and returns a new array where each element is the square of the corresponding element in the input array. The new array is sorted in non-decreasing order. Here's a step-by-step explanation of the code: 1. Initialize an empty result array of the same length as the original array: `result := make([]int, len(array))`. 2. Initialize variables to mark the start and end positions of the array, as well as the end_pos, which is the index of the last element in the result array: `end_pos := len(array) - 1`, `start := 0`, `end := len(array) - 1`. 3. Inside the loop, calculate the square of the absolute value for the elements at the current start and end positions: `sq1 = array[start] * array[start]`, `sq2 = array[end] * array[end]`. 4. Compare `sq1` and `sq2` to determine which one is greater. If `sq1` is greater, assign it to the `end_pos` index of the result array and increment the start position by 1: `result[end_pos] = sq1`, `start++`. This ensures that the greater squared value is placed at the end of the result array. 5. If `sq2` is greater, assign it to the `end_pos` index of the result array and decrement the end position by 1: `result[end_pos] = sq2`, `end--`. This ensures that the greater squared value is still placed at the end of the result array. 6. Decrement the `end_pos` variable to move to the previous index in the result array: `end_pos--`. 7. Repeat the process until the start and end positions cross each other. 8. Finally, return the result array, which contains the squared values of the input array elements in non-decreasing order. The code effectively uses a two-pointer approach to compare the squared values of the elements at the start and end positions and places the greater squared value at the end of the result array. This ensures that the result array is sorted in non-decreasing order. The time complexity of the `SortedSquaredArray` function is O(n), where n is the length of the input array. This is because the function performs a single pass through the array to calculate the squares and populate the result array. The space complexity of the function is O(n) as well. This is because it creates a new result array of the same length as the input array to store the squared values. Therefore, the space required is proportional to the size of the input array. Overall, the function has a linear time complexity and linear space complexity. ''' def sortedSquaredArray(array): # Initialize an empty result array of the same length as the original array result = [0] * len(array) # Set the start and end positions and the end_pos end_pos = len(array) - 1 start = 0 sq1 = 0 sq2 = 0 end = len(array) - 1 # Using the two-pointer approach, calculate the square of the absolute value and add the greatest value to the end of the result array while start <= end: sq1 = array[start] * array[start] sq2 = array[end] * array[end] if sq1 > sq2: result[end_pos] = sq1 start += 1 # Square of the start pointer is greater, so increment start by 1 else: result[end_pos] = sq2 end -= 1 # Square of the end pointer is greater, so decrement end by 1 end_pos -= 1 return result arr = [-6, 1, 2, 3, 4, 5] result = sortedSquaredArray(arr) print(result) ================================================ FILE: Arrays/string_halves.cpp ================================================ /* You are given a string s of even length. Split this string into two halves of equal lengths, and let a be the first half and b be the second half. Two strings are alike if they have the same number of vowels ('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'). Notice that s contains uppercase and lowercase letters. Return true if a and b are alike. Otherwise, return false. Example 1: Input: s = "book" Output: true Explanation: a = "bo" and b = "ok". a has 1 vowel and b has 1 vowel. Therefore, they are alike. Example 2: Input: s = "textbook" Output: false Explanation: a = "text" and b = "book". a has 1 vowel whereas b has 2. Therefore, they are not alike. Notice that the vowel o is counted twice. Constraints: 2 <= s.length <= 1000 s.length is even. s consists of uppercase and lowercase letters. */ #include class Solution { public: bool halvesAreAlike(string s) { set X {'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'}; int half = s.size() / 2; int c = 0, d = 0; for(int i = 0; i < half; i++){ if(X.find(s[i]) != X.end()) c++; } for(int i = half; i < s.size(); i++){ if(X.find(s[i]) != X.end()){ d++; } } return c == d; } }; ================================================ FILE: Arrays/three_largest_no.py ================================================ """ Write a function that takes in an array of at least three integers and, without sorting the input array, returns a sorted array of the three largest integers in the input array. # Approach - We will use three variables to store the three max values and initialize them with int_minimum - We will iterate through the array and compare the values with the three variables - If the value is greater than the first variable, we will update the variables - If the value is greater than the second variable, we will update the variables - If the value is greater than the third variable, we will update the variables - Return the three variables as a sorted array """ def three_max_no(arr:list)-> list: # give int minvalue to a,b, max_1,max_2,max_3= -9999,-9999,-9999 # we will iterate through the array and compare the values for i in arr: if i > max_1: # if the value is greater than the first variable, we will update the variables max_1,max_2,max_3 = i, max_1, max_2 elif i > max_2: # if the value is greater than the second variable, we will update the variables max_2, max_3 = i, max_2 elif i > max_3: # if the value is greater than the third variable, we will update the variables max_3 = i # return the three max values as a sorted array return [max_1, max_2, max_3] # example-1 arr=[141,1,17,-7,-17,-27,18,541,8,7,7] print(three_max_no(arr)) # sample output [541, 141, 18] ================================================ FILE: Arrays/total_hamming_distance.cpp ================================================ /* Introduction: This documentation provides a detailed explanation of the problem statement, algorithm, and implementation of calculating the sum of Hamming distances between all pairs of integers in an integer array. Hamming distance measures the number of positions at which corresponding bits are different between two numbers. The algorithm aims to find the sum of Hamming distances for all possible pairs of integers in the given array. Problem Statement: Given an integer array nums, the task is to calculate the sum of Hamming distances between all the pairs of integers in nums. The goal is to determine the total count of differing bits at each position for all possible pairs of integers in the array. Hamming Distance : Hamming distance is a metric for comparing two binary data strings. While comparing two binary strings of equal length, Hamming distance is the number of bit positions in which the two bits are different. Sample Input : [4,14,2] expected Output : 6 Explaination : In binary representation, the 4 is 0100, 14 is 1110, and 2 is 0010 (just showing the four bits relevant in this case). Total Hamming distance = HammingDistance(4, 14) + HammingDistance(4, 2) + HammingDistance(14, 2) = 2 + 2 + 2 = 6. Method 1 : (Brute Force Method) We can iterate over the whole array and find all pairs calculate Hamming distance and add all the distances. Time complexity : O(n^2) Space Complexity : O(1) Method 2 : 1. Initialize a variable `totalDistance` to store the sum of Hamming distances. 2. Iterate over each bit position from 0 to 31 (assuming integers are 32-bit). 3. For each bit position `i`, initialize two variables: `countZeros` and `countOnes` to keep track of the count of zeros and ones at that position. 4. Iterate over each element `num` in `nums`. a. Right shift `num` by `i` positions and perform a bitwise AND with 1 to check the value at bit position `i`. b. If the result is 0, increment `countZeros` by 1; otherwise, increment `countOnes` by 1. 5. Add `countZeros * countOnes` to `totalDistance`. This calculates the Hamming distance for the current bit position and adds it to the running total. 6. Repeat steps 3-5 for all bit positions. 7. Return `totalDistance` as the sum of Hamming distances between all pairs of integers in `nums`. Time Complexity : O(k*n) where , k = maximum number of bits required to represent a number in the array n = size of array Space Complexity : O(1) Explaination : Number ==> Binary Representation 4 0 1 0 0 14 1 1 1 0 2 0 0 1 0 The idea is to count differences at individual bit positions. We traverse from 0 to 31 and count numbers with i’th bit set. Let this count be ‘c'. There would be “n-c” numbers with i’th bit not set. So count of differences at i’th bit would be “count * (n-count)”, the reason for this formula is as every pair having one element which has set bit at i’th position and second element having unset bit at i’th position contributes exactly 1 to sum. 1st bit = 2*1 = 2 2nd bit = 2*1 = 2 3rd bit = 2*1 = 2 4th bit = 3*0 = 0 -------------------------- Total = 6 */ // CODE : // #include #include #include using namespace std; int totalHammingDistance(vector& nums) { int totalDistance=0; int n=nums.size(); // Here we are assuing that the 32 bits are sufficient to represent all the numbers in the array for(int i=0;i<32;i++){ int c=0;// c represents number of set bits of a particular position in the whole array. for(int j=0;j> n; vector nums(n); for(int i=0;i> nums[i]; } int total_hamming_distance = totalHammingDistance(nums); cout << total_hamming_distance << endl; return 0; } ================================================ FILE: Arrays/tournament_winner.go ================================================ /* There's an algorithms tournament taking place in which teams of programmers compete against each other to solve algorithmic problems as fast as possible. Teams compete in a round robin, where each team faces off against all other teams. Only two teams compete against each other at a time, and for each competition, one team is designated the home team, while the other team is the away team. In each competition there's always one winner and one loser; there are no ties. A team receives 3 points if it wins and 0 points if it loses. The winner of the tournament is the team that receives the most amount of points. Given an array of pairs representing the teams that have competed against each other and an array containing the results of each competition, write a function that returns the winner of the tournament. The input arrays are named competitions and results, respectively. The competitions array has elements in the form of [homeTeam, awayTeam], where each team is a string of at most 30 characters representing the name of the team. The array contains information about the winner of each corresponding competition in the competitions array. Specifically, results[i] denotes the winner of competitions[i] , where a 1 in the results array means that the home team in the corresponding competition won and a 0 means that the away team won. It's guaranteed that exactly one team will win the tournament and that each team will compete against all other teams exactly once. It's also guaranteed that the tournament will always have at least two teams. Sample Input : { "competitions": [ ["HTML", "C#"], ["C#", "Python"], ["Python", "HTML"] ], "results": [0, 0, 1] } Output: Python O(n) time | O(k) space - where n is the number of competitions and k is the number of teams */ package main import "fmt" const HOME_TEAM_WON = 1 const AWAY_TEAM_WON = 0 func TournamentWinner(competitions [][]string, results []int) string { // construct a hashmap which will keep track of team with number of matches won // if name already exist in hashmap then add 1 to it // if it doesn't then add new entry mp := make(map[string]int) winner := "" max := 0 for i := 0; i < len(results); i++ { if results[i] == HOME_TEAM_WON { mp[competitions[i][0]]++ if mp[competitions[i][0]] > max { // keep track of max value and winner max = mp[competitions[i][0]] winner = competitions[i][0] } } else { mp[competitions[i][1]]++ // keep track of max value and winner if mp[competitions[i][1]] > max { max = mp[competitions[i][1]] winner = competitions[i][1] } } } return winner } func main() { competitions := [][]string{{"HTML", "C#"}, {"C#", "Python"}, {"Python", "HTML"}} result := []int{0, 0, 1} msg := TournamentWinner(competitions, result) fmt.Println(msg) competitions = [][]string{{"HTML", "Java"},{"Java", "Python"},{"Python", "HTML"},{"C#", "Python"},{"Java", "C#"},{"C#", "HTML"}} result = []int{0, 1, 1, 1, 0, 1} msg = TournamentWinner(competitions, result) fmt.Println(msg) } ================================================ FILE: Arrays/triplet_sum.cpp ================================================ /* Given an array of integers, nums, and an integer value, target, determine if there are any three integers in nums whose sum equals the target. Return TRUE if three such integers are found in the array. Otherwise, return FALSE. */ #include #include #include /* This implementation uses the two pointer technique to find all triplets in the input array that sum up to the target sum. We first sort the input array in non-decreasing order to simplify the process of finding triplets. We then loop through the array and use two pointers, one starting from the left and one starting from the right, to find triplets that sum up to the target sum. For each iteration of the loop, we set the left pointer to the index immediately to the right of the current index, and the right pointer to the index of the last element in the array. We then move the left and right pointers towards the center, checking at each step whether the triplet formed by the current indices sums up to the target sum. If it does, we add the triplet to the result vector and continue searching for other triplets. If the sum is less than the target sum, we move the left pointer towards the center to find larger numbers. If the sum is greater than the target sum, we move the right pointer towards the center to find smaller numbers. */ std::vector> threeNumberSum(std::vector& nums, int targetSum) { // Sort the input array in non-decreasing order std::sort(nums.begin(), nums.end()); std::vector> triplets; // Loop through the array for (int i = 0; i < nums.size() - 2; i++) { int left = i + 1; int right = nums.size() - 1; // While the left index is less than the right index while (left < right) { int currentSum = nums[i] + nums[left] + nums[right]; if (currentSum == targetSum) { // If the current triplet sums up to the target sum, add it to the result vector triplets.push_back({ nums[i], nums[left], nums[right] }); // Move the left and right indices towards the center to find other triplets left++; right--; } else if (currentSum < targetSum) { // If the current triplet sums up to less than the target sum, move the left index towards the center to find larger numbers left++; } else { // If the current triplet sums up to more than the target sum, move the right index towards the center to find smaller numbers right--; } } } return triplets; } int main() { // Example usage std::vector nums = { 12, 3, 1, 2, -6, 5, -8, 6 }; int targetSum = 0; std::vector> triplets = threeNumberSum(nums, targetSum); for (std::vector& triplet : triplets) { std::cout << "["; for (int i = 0; i < triplet.size(); i++) { std::cout << triplet[i]; if (i < triplet.size() - 1) { std::cout << ", "; } } std::cout << "]" << std::endl; } return 0; } ================================================ FILE: Arrays/triplet_sum.go ================================================ /* Given an array of integers, nums, and an integer value, target, determine if there are any three integers in nums whose sum equals the target. Return TRUE if three such integers are found in the array. Otherwise, return FALSE. */ package main import ( "fmt" "sort" "strings" ) // Time complexity : Sorting the array O(n log(n)) and Nested loop to find triplet O(n^{2}) which can be simplified to O(n^{2}) // Space complexity is O(1) since we use a fixed amount of extra space in memory. // FindSumOfThree is our challenge function func findSumOfThree(nums []int, target int) bool { // Sorting the input vector sort.Sort(sort.IntSlice(nums)) // We create two pointers to track our indices and, variable to store our triple sum low, high, triple := 0, 0, 0 // Fix one element at a time and find the other two for i := 0; i < len(nums) - 2; i++ { // Set the indices of the two pointers // Index of the first of the remaining elements low = i + 1 // Last index high = len(nums) - 1 for low < high { // Check if the sum of the triple is equal to the sum triple = nums[i] + nums[low] + nums[high] // Found a triple whose sum equals the target if triple == target { return true // Move low pointer forward if the triple sum is less than the required sum } else if triple < target { low += 1 } else { // Move the high pointer backwards if the triple sum is greater than the required sum high -= 1 } } } return false } // Driver code func main() { numsLists := [][]int { {3, 7, 1, 2, 8, 4, 5}, {-1, 2, 1, -4, 5, -3}, {2, 3, 4, 1, 7, 9}, {1, -1, 0}, {2, 4, 2, 7, 6, 3, 1}, } testLists := [][]int { {10, 20, 21}, {-8, 0, 7}, {8, 10, 20}, {1, -1, 0}, {8, 11, 15}, } for i, nList := range numsLists { fmt.Printf("%d. Input array: %s\n", i + 1, strings.Replace(fmt.Sprint(nList), " ", ", ", -1)) for _, tList := range testLists[i] { if findSumOfThree(nList, tList) { fmt.Printf(" Sum for %d exists\n", tList) } else { fmt.Printf(" Sum for %d does not exist\n", tList) } } fmt.Printf("%s\n", strings.Repeat("-", 100)) } } ================================================ FILE: Arrays/triplet_sum.java ================================================ /* Given an array of integers, nums, and an integer value, target, determine if there are any three integers in nums whose sum equals the target. Return TRUE if three such integers are found in the array. Otherwise, return FALSE. */ import java.util.ArrayList; import java.util.Arrays; import java.util.List; /* We sort the input array in non-decreasing order using the Arrays.sort() method, and then loop through the array using a for loop. We use two pointers, one starting from the left and one starting from the right, to find triplets that sum up to the target sum. For each iteration of the loop, we set the left pointer to the index immediately to the right of the current index, and the right pointer to the index of the last element in the array. We then move the left and right pointers towards the center, checking at each step whether the triplet formed by the current indices sums up to the target sum. If it does, we add the triplet to the result list and continue searching for other triplets. If the sum is less than the target sum, we move the left pointer towards the center to find larger numbers. If the sum is greater than the target sum, we move the right pointer towards the center to find smaller numbers. Finally, we print out the result list using a nested for loop to iterate through each triplet and each element in each triplet. */ public class ThreeNumberSum { public static List> threeNumberSum(int[] nums, int targetSum) { Arrays.sort(nums); // Sort the input array in non-decreasing order List> triplets = new ArrayList<>(); // Loop through the array for (int i = 0; i < nums.length - 2; i++) { int left = i + 1; int right = nums.length - 1; // While the left index is less than the right index while (left < right) { int currentSum = nums[i] + nums[left] + nums[right]; if (currentSum == targetSum) { // If the current triplet sums up to the target sum, add it to the result list triplets.add(Arrays.asList(nums[i], nums[left], nums[right])); // Move the left and right indices towards the center to find other triplets left++; right--; } else if (currentSum < targetSum) { // If the current triplet sums up to less than the target sum, move the left index towards the center to find larger numbers left++; } else { // If the current triplet sums up to more than the target sum, move the right index towards the center to find smaller numbers right--; } } } return triplets; } public static void main(String[] args) { // Example usage int[] nums = { 12, 3, 1, 2, -6, 5, -8, 6 }; int targetSum = 0; List> triplets = threeNumberSum(nums, targetSum); for (List triplet : triplets) { System.out.print("["); for (int i = 0; i < triplet.size(); i++) { System.out.print(triplet.get(i)); if (i < triplet.size() - 1) { System.out.print(", "); } } System.out.println("]"); } } } ================================================ FILE: Arrays/triplet_sum.js ================================================ /* Given an array of integers, nums, and an integer value, target, determine if there are any three integers in nums whose sum equals the target. Return TRUE if three such integers are found in the array. Otherwise, return FALSE. Sample Input : 3, 7, 1, 2, 8, 4, 5 Target : 18 Output : True Sample Input : 0 -1 1 Target : 2 Output : False APPROACH: 1) We need to sort the array for our strategy to work. 2) Iterating from index 0, we check three numbers: - value at index i (starting at 0) - low: value at index i + 1 - high: value at index nums.length - 1 (the last value, also the highest) 3) Take the sum of these three values. - If sum equals target, return true - If sum is less than the target, increment "low" and run this step again - If sum is higher than the target, decrement "high" and run this step again 4) If low meets high, we ran out of numbers to try. Back to step 2. 5) Return false at the end of everything because we found no matching sums For a similar problem: https://leetcode.com/problems/3sum/ */ const sumOfThree = (nums, target) => { // function only applies to arrays with at least 3 values if (nums.length < 3) return false // Sort the array nums.sort() let low, high // Iterate through each value (up to 3rd from last) for (let i = 0; i < nums.length - 2; i++) { //Assign pointers and check the sum low = i + 1 high = nums.length - 1 while (low < high) { let sum = nums[i] + nums[low] + nums[high] // match found, return true if (sum === target) { return true // sum is too low, increase low pointer } else if (sum < target) { low++ // sum is too high, decrease high pointer } else { high-- } } } return false } console.log(sumOfThree([3, 7, 1, 2, 8, 4, 5], 18)) // true console.log(sumOfThree([3, 7, 1, 2, 8, 4, 5], 50)) // false console.log(sumOfThree([0, -1, 1], 2)) // false console.log(sumOfThree([0, -1, 1], 0)) // true ================================================ FILE: Arrays/triplet_sum.py ================================================ ''' Given an array of integers, nums, and an integer value, target, determine if there are any three integers in nums whose sum equals the target. Return TRUE if three such integers are found in the array. Otherwise, return FALSE. ''' def find3Numbers(A, arr_size, sum): # Sort the elements A.sort() # Now fix the first element # one by one and find the # other two elements for i in range(0, arr_size-2): # index of the first element # in the remaining elements l = i + 1 # index of the last element r = arr_size-1 while (l < r): if( A[i] + A[l] + A[r] == sum): return True elif (A[i] + A[l] + A[r] < sum): l += 1 else: # A[i] + A[l] + A[r] > sum r -= 1 # If we reach here, then # no triplet was found return False # Driver program to test above function A = [1, 2, 3, 4, 5, 6] sum = 9 arr_size = len(A) print(find3Numbers(A, arr_size, sum)) ================================================ FILE: Arrays/urlify.go ================================================ // Implement an algorithm to replace all spaces with %20, assumew we have extra buffer at the end. // Input : Mr Ashish Lala Output: Mr%20Ashish%20Lala // Program Author : Abhisek Kumar Gupta // Time complexity : O(n) Space complexity O(n) package main import "fmt" func Urlify(s string, length int) string { spaceCount := 0 r := []rune{} // compute spaces, for i := 0; i < length; i++ { if s[i] == ' ' { spaceCount++ } } // string to rune for _, v := range s { r = append(r, v) } // triple space count and find out how many extra characters will be there in final string index := length + (spaceCount * 3) // actual length of final string for i := length - 1; i >= 0; i-- { // If u encounter space then replace with %20 if r[i] == ' ' { r[index - 1] = '0' r[index - 2] = '2' r[index - 3] = '%' index -= 3 } else { // If there is no space then copy the original character r[index - 1] = r[i] index-- } } return string(r[index:]) } func main() { s := "Mr John Smith "; length := 13 msg := Urlify(s, length); fmt.Println(msg) } ================================================ FILE: Backtracking/Generate_Parentheses.py ================================================ ''' Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses. Example 1: Input: n = 3 Output: ["((()))","(()())","(())()","()(())","()()()"] Example 2: Input: n = 1 Output: ["()"] Constraints: 1 <= n <= 8 ''' class Solution: #reference: GeeksforGeeks for comments '''To form all the sequences of balanced bracket subsequences with n pairs. So there are n opening brackets and n closing brackets. So the subsequence will be of length 2*n. There is a simple idea, the i’th character can be ‘{‘ if and only if the count of ‘{‘ till i’th is less than n and i’th character can be ‘}’ if and only if the count of ‘{‘ is greater than the count of ‘}’ till index i. If these two cases are followed then the resulting subsequence will always be balanced''' def generate(self,n,open,close,s,final_arr): if(open==n and close==n): final_arr.append(s) return if(open List[str]: final_arr = [] Solution.generate(self,n,0,0,"",final_arr) return final_arr ================================================ FILE: Backtracking/geenrate_parentheses.go ================================================ // Implementation of generating all combinations of well-formed parentheses /* The generateParenthesis function takes an integer n as input, which represents the number of pairs of parentheses to generate. It initializes an empty array result to store the valid combinations and calls the backtrack helper function to generate all the combinations. The backtrack function takes four arguments: a pointer to the result array to add the valid combinations the current string cur the number of open brackets open the number of closed brackets close the maximum number of pairs of parentheses max The function first checks if the current string has reached the maximum length, i.e., len(cur) == max*2. If it has, the function appends the current string to the result array and returns. If there are still open brackets left to add, i.e., open < max, the function appends an open bracket to the current string and recursively calls backtrack with open+1 and close. This represents adding an open bracket to the current combination. If there are more closed brackets than open brackets, i.e., close < open, the function appends a closed bracket to the current string and recursively calls backtrack with open and close+1. This represents adding a closed bracket to the current combination. In the main function, we call generateParenthesis with n=3 and print the resulting array. This will output all possible combinations of three pairs of well-formed parentheses. Input: n = 3 Output: ["((()))","(()())","(())()","()(())","()()()"] Explanation: There are 5 valid combinations of well-formed parentheses with 3 pairs of parentheses. The output lists all of them. The time complexity of the above program is O(4^n / sqrt(n)), where n is the number of parentheses. This is because there are a total of 2n positions in the output string, and at each position, we have two choices - either to put an opening or closing parenthesis. Therefore, the total number of possible combinations is 2^(2n). However, not all of these combinations are valid, as some may violate the well-formedness condition. We can use Catalan number to determine the actual number of valid combinations. The nth Catalan number is given by (2n)! / (n+1)!n!, which is approximately equal to 4^n / sqrt(n*pi). Thus, the time complexity is O(4^n / sqrt(n)). */ package main import "fmt" func generateParenthesis(n int) []string { var result []string backtrack(&result, "", 0, 0, n) return result } // helper function to backtrack and generate all valid combinations func backtrack(result *[]string, cur string, open int, close int, max int) { // base case: if the current string has reached the maximum length, add it to the result if len(cur) == max*2 { *result = append(*result, cur) return } // if there are still open brackets left to add, add one and recurse if open < max { backtrack(result, cur+"(", open+1, close, max) } // if there are more closed brackets than open brackets, add a closed bracket and recurse if close < open { backtrack(result, cur+")", open, close+1, max) } } func main() { result := generateParenthesis(3) fmt.Println(result) } ================================================ FILE: Backtracking/n_queen.cpp ================================================ #include #include using namespace std; // Function to check if it's safe to place a queen at board[row][col] bool isSafe(vector& board, int row, int col, int N) { // Check the row on the left side for (int i = 0; i < col; ++i) { if (board[row][i] == 'Q') { return false; } } // Check upper diagonal on the left side for (int i = row, j = col; i >= 0 && j >= 0; --i, --j) { if (board[i][j] == 'Q') { return false; } } // Check lower diagonal on the left side for (int i = row, j = col; i < N && j >= 0; ++i, --j) { if (board[i][j] == 'Q') { return false; } } return true; } // Recursive function to solve N-Queens problem bool solveNQueens(vector& board, int col, int N) { if (col == N) { // All queens are placed successfully return true; } for (int i = 0; i < N; ++i) { if (isSafe(board, i, col, N)) { // Place queen board[i][col] = 'Q'; // Recur to place the rest of the queens if (solveNQueens(board, col + 1, N)) { return true; } // If placing queen in board[i][col] doesn't lead to a solution, backtrack board[i][col] = '.'; } } // If no queen can be placed in this column, return false return false; } // Function to solve N-Queens problem and print the solution void solveNQueens(int N) { vector board(N, string(N, '.')); if (solveNQueens(board, 0, N)) { // Print the solution for (int i = 0; i < N; ++i) { cout << board[i] << endl; } } else { cout << "No solution exists." << endl; } } int main() { int N; cout << "Enter the number of queens (N): "; cin >> N; solveNQueens(N); return 0; } ================================================ FILE: Backtracking/n_queens.java ================================================ /** * Problem :- N-Queens https://leetcode.com/problems/n-queens/description/ Approach:- The approach uses backtracking to generate all possible configurations of queens on the board and checks the validity of each configuration. It maintains a boolean board to represent the placement of queens, where `true` indicates the presence of a queen in a particular cell. The algorithm starts by placing a queen in the first row and proceeds recursively to the next row, checking all possible column positions for the queen. If a valid position is found, the algorithm moves to the next row and repeats the process. If all N queens are placed on the board, a valid solution is found and added to the list of solutions. To check the validity of a queen's position, the algorithm verifies three conditions: 1. Vertical Check: It checks the columns of the previous rows to ensure that there are no queens in the same column. 2. Left Diagonal Check: It checks the diagonal elements on the left side of the current position to ensure that there are no queens present. 3. Right Diagonal Check: It checks the diagonal elements on the right side of the current position to ensure that there are no queens present. The algorithm continues this process, backtracking whenever it encounters an invalid position or explores all possibilities. Once all valid configurations are found, they are converted into a list of strings representing the board configurations, and the list of solutions is returned. Time Complexity: O(N!) In the worst case, the backtracking algorithm explores all possible configurations,. However, with pruning techniques, the actual runtime is significantly lower. Space Complexity: O(N^2) because the boolean board of size NxN is used to represent the placement of queens, and the list of solutions also occupies additional space. */ class Solution { public List> solveNQueens(int n) { List> list = new ArrayList<>(); // If the board size is 2 or 3, no solution is possible, so return an empty list if (n == 2 || n == 3) { return list; } // If the board size is 1, there is only one solution with a single queen in the only cell if (n == 1) { String ans = "Q"; ArrayList a = new ArrayList<>(); a.add(ans); list.add(a); return list; } boolean arr[][] = new boolean[n][n]; // Create a boolean board to represent the placement of queens queens(arr, 0, list); // Solve the N-queens problem recursively return list; // Return the list of solutions } // Recursive function to solve the N-queens problem static void queens(boolean board[][], int row, List> list) { if (row == board.length) { ArrayList arr = new ArrayList<>(); // Convert the boolean board to a list of strings representing the board configuration for (int j = 0; j < board.length; j++) { String ans = ""; for (int i = 0; i < board[j].length; i++) { if (board[j][i] == false) { ans += "."; } else { ans += "Q"; } } arr.add(ans); } list.add(arr); // Add the board configuration to the list of solutions return; } for (int col = 0; col < board.length; col++) { if (isSafe(board, row, col)) { board[row][col] = true; // Place a queen in the current position queens(board, row + 1, list); // Recursively solve the problem for the next row board[row][col] = false; // Backtrack and remove the queen from the current position } } } // Function to check if it is safe to place a queen in a given position private static boolean isSafe(boolean[][] board, int row, int col) { // Check the vertical column for any previously placed queens for (int i = 1; i <= row; i++) { if (board[row - i][col]) { return false; } } // Check the left diagonal for any previously placed queens int max = Math.min(row, col); for (int i = 1; i <= max; i++) { if (board[row - i][col - i]) { return false; } } // Check the right diagonal for any previously placed queens int maxtimes = Math.min(row, board.length - col - 1); for (int i = 1; i <= maxtimes; i++) { if (board[row - i][col + i]) { return false; } } return true; // It is safe to place a queen in the given position } } ================================================ FILE: Backtracking/sudoko_solver.java ================================================ /*Write a program to solve a Sudoku puzzle by filling the empty cells. A sudoku solution must satisfy all of the following rules: Each of the digits 1-9 must occur exactly once in each row. Each of the digits 1-9 must occur exactly once in each column. Each of the digits 1-9 must occur exactly once in each of the 9 3x3 sub-boxes of the grid. The '.' character indicates empty cells. Example 1: Input: board = [["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]] Output: [["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]] Explanation: The input board is shown above and the only valid solution is shown below: */ package Backtracking; public class sudokoSolver { public void solveSudoku(char[][] board) { Solver(board, 0); } static boolean Solver(char[][] board , int total) { if(total==81) { return true; } int sr = total/9; int sc = total%9; if(board[sr][sc]!='.') { return Solver(board, total+1); } for(int i=1 ; i<=9 ; i++) { char curr = (char) (i+'0'); if(canElementBePlaced(board , sr , sc , curr)) { board[sr][sc]=curr; if (Solver(board , total+1)) { return true; } board[sr][sc]='.'; } } return false; } static void print(char[][] board) { for(int i=0; i<9 ;i++) { for(int j=0 ; j<9 ; j++) { System.out.print(board[i][j]+", "); } System.out.println(); } } static boolean canElementBePlaced(char[][] board , int r , int c , char k) { for(int i=0 ; i<9 ; i++) { if(board[r][i]==k) { return false; } } for(int j =0 ; j<9 ; j++) { if(board[j][c]==k) { return false; } } int sr = r-(r%3); int sc = c-(c%3); for(int i=sr; i<=sr+2;i++) { for(int j=sc ; j<=sc+2; j++) { if (board[i][j]==k) { return false; } } } return true; } } ================================================ FILE: Binary Search/BinarySearchRecursive.java ================================================ /* * Problem : Implement Binary Search using Recursion. * * RECURSION : Function that calls itself is called recursion. * * Algorithm: * 1) Get an sorted array and a target to be found in the array. * 2) Find the middle index by dividing the first and last element. * 3) If the middle element is equal to target , return the mid index. * 4) If the middle element is greater than the target , in the next recursive call make last element as mid-1 * 5) If the middle element is smaller than the target , in the next recursive call make first element as mid+1 * 6) If start is less than end ,ie,if the element not found , return -1. * * Time complexity: worst case: O(logN) , N - size of the array. * * Space Complexity: O(logN) (since we use stack for function calls) * */ public class BinarySearchRecursive { public static void main(String[] args) { int[] arr = {1,2,5,89,99,101}; int target = 1; System.out.println(binarySearch(arr , target,0 , arr.length-1)); } static int binarySearch(int[] arr ,int target, int start , int end) { int mid = (start + end)/2; if (start > end) { return -1; } if (arr[mid] == target) { return mid; } else if (arr[mid] > target) { return binarySearch(arr , target , start , mid-1); } return binarySearch(arr ,target , mid+1, end); } } ================================================ FILE: Binary Search/binary_search.cpp ================================================ /* The binarySearch function takes in a sorted vector of integers arr and a target value target to search for. The function initializes the left and right pointers to the first and last indices of the array respectively. The function uses a while loop to keep searching until the left and right pointers meet. Inside the loop, the function calculates the middle index by adding the left and right indices and dividing by 2. If the middle element is equal to the target, the function returns the middle index. If the target is greater than the middle element, the function discards the left half of the array and moves the left pointer to the middle + 1 index. If the target is smaller than the middle element, the function discards the right half of the array and moves the right pointer to the middle - 1 index. If the target is not found in the array, the function returns -1. The driver code initializes a sorted array and a target value, and calls the binarySearch function to search for the target value. The function returns the index of the target value, or -1 if it is not found. The driver code prints the result of the search to the console. */ #include #include using namespace std; // Binary search function // Takes in a sorted vector of integers and a target value to search for // Returns the index of the target value if found, -1 otherwise int binarySearch(vector arr, int target) { int left = 0; int right = arr.size() - 1; // Loop until left and right pointers meet while (left <= right) { int mid = left + (right - left) / 2; // If the target is found, return the index if (arr[mid] == target) { return mid; } // If target is greater than the middle element, discard the left half else if (arr[mid] < target) { left = mid + 1; } // If target is smaller than the middle element, discard the right half else { right = mid - 1; } } // Target not found return -1; } // Driver code to test binary search function int main() { vector arr = {2, 4, 6, 8, 10, 12, 14, 16, 18, 20}; int target = 12; int result = binarySearch(arr, target); if (result == -1) { cout << "Element not found" << endl; } else { cout << "Element found at index " << result << endl; } return 0; } ================================================ FILE: Binary Search/binary_search.js ================================================ /** * Finds the first and last occurrence of a target value in a sorted array. * * @param {number[]} N - The sorted array of numbers. * @param {number} T - The target value to search for. * @returns {number[]} An array containing the first and last index of the target value, or [-1,-1] if not found. */ const searchRange = function(N, T) { // Helper function to perform binary search on the array const find = (target, arr, left=0, right=arr.length) => { while (left <= right) { // Calculate the middle index let mid = left + right >> 1; // If the middle element is less than the target, move the left pointer to mid + 1 if (arr[mid] < target) left = mid + 1; // If the middle element is greater than or equal to the target, move the right pointer to mid - 1 else right = mid - 1; } // Return the left pointer, which will be the index of the target or the insertion point if not found return left; }; // Find the leftmost index of the target value let Tleft = find(T, N); // If the target value is not found in the array, return [-1,-1] if (N[Tleft] !== T) return [-1,-1]; // Find the rightmost index of the target value return [Tleft, find(T+1, N, Tleft) - 1]; }; ================================================ FILE: Binary Search/binary_search.py ================================================ """ Intuition: If you have to guess a magic number from 1-100, the best first attempt would be to guess '50' or in other words, the middle. If I tell you that the magic number is higher, you now don't need to consider all numbers 1-50, and if it is lower you wouldn't need to consider numbers 50-100!! In Binary Search, we follow the same idea, 1. Compar the target with the middle element. 2. If the target is higher, then the target can only lie in the right (greater) subarray. We re-calculate mid and repeat step 1. 3. If the target is lower, the target can only lie in the left (lower) half. We re-calculate mid and repeat step 1. Binary search can only operate on a sorted array. Further reading: https://en.wikipedia.org/wiki/Binary_search_algorithm """ import math def binary_search(lst, target): if not lst: return -1 lo = 0 hi = len(lst)-1 while lo <= hi: mid = math.floor(lo + (hi - lo) / 2) # Find mid. math.floor is used to round floats down. if lst[mid] < target: # Element in mid is lower than target. lo = mid + 1 # Our low (start) becomes the element after mid. elif lst[mid] > target: # Element in mid is higher than target. hi = mid - 1 # Our high (end) becomes the element before mid. elif lst[mid] == target: print(f"Found {target} at index {mid}.") return mid print(f"Target {target} not found.") return -1 arr = [10, 20, 30, 50, 60, 80, 110, 130, 140, 170] binary_search(arr, 80) binary_search(arr, 10) binary_search(arr, 110) binary_search(arr, 20) binary_search(arr, 140) binary_search(arr, 2) binary_search(arr, 1) ================================================ FILE: Binary Search/binary_search_iterative.go ================================================ /* Write a function that takes in a sorted array of integers as well as a target integer. The function should use the Binary Search algorithm to determine if the target integer is contained in the array and should return its index if it is, otherwise -1 Sample Input : [0, 1, 44, 66, 77] target = 66 Output : 3 Sample Input : [0, 1, 44, 66, 77] target = 101 Output : -1 */ package main import "fmt" func BinarySearch(array []int, target int) int { // mark start and end start := 0 end := len(array) - 1 // idea behind bs is, since array is already sorted we can use it to our advantage // 1 compute mid // 2 if mid value is less than target then we only need to search index less than mid // 3 if mid value is greater than target then we only need to search index greater than mid // and keep repeating from step 1 // make sure start and end dont cross each other for start <= end { // compute mid, prevent overflow of integer mid := start + (end - start) / 2 if array[mid] == target { return mid } else if array[mid] > target { // Element in mid is higher than target. end = mid - 1 // # Our (end) becomes the element before mid. } else if array[mid] < target { // Element in mid is lower than target. start = mid + 1 // # Our (start) becomes the element after mid. } } return -1 } func main() { arr := []int{-2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 33, 44, 55} target := 7 msg := BinarySearch(arr, target) fmt.Println(msg) msg = BinarySearch(arr, 700) fmt.Println(msg) } ================================================ FILE: Binary Search/binary_search_recursive.go ================================================ /* Implelemtation of Recursive Binary Search In computer science, binary search, also known as half-interval search, logarithmic search, or binary chop, is a search algorithm that finds the position of a target value within a sorted array. Binary search compares the target value to the middle element of the array. If they are not equal, the half in which the target cannot lie is eliminated and the search continues on the remaining half, again taking the middle element to compare to the target value, and repeating this until the target value is found. If the search ends with the remaining half being empty, the target is not in the array. Source(https://en.wikipedia.org/wiki/Binary_search_algorithm) */ package main import "fmt" func BinarySearchRecursive(Arr []int, key int) bool { low := 0 high := len(Arr) - 1 if low <= high { mid := low + (high - low) / 2 // prevent overflow if Arr[mid] > key { // Element in mid is higher than target. return BinarySearchRecursive(Arr[:mid], key) // search from start to mid } else if Arr[mid] < key { // Element in mid is lower than target. return BinarySearchRecursive(Arr[mid + 1:], key) // search from mid + 1 to last } else { return true } } return false } func main() { Arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} fmt.Println(BinarySearchRecursive(Arr, 5)) fmt.Println(BinarySearchRecursive(Arr, 6)) fmt.Println(BinarySearchRecursive(Arr, -1)) fmt.Println(BinarySearchRecursive(Arr, -10)) fmt.Println(BinarySearchRecursive(Arr, 70)) } ================================================ FILE: Binary Search/binary_serach_first_and_last_occurence.py ================================================ class Solution: def __init__(self): self.start_index=-1 self.end_index=-1 ''' Time complexity - O(logn), Space complexity - O(1)'''' '''Once the binary Search is implemented, to find the start_index, we store the current index where we find the target and implement search again on the left side of the array''' def binarySearchFirst(self,arr,low,high,target): if(low>high): return mid=(low+high)//2 if(arr[mid]>target): Solution.binarySearchFirst(self,arr,low,mid-1,target) elif(arr[mid]high): return mid=(low+high)//2 if(arr[mid]>target): Solution.binarySearchLast(self,arr,low,mid-1,target) elif(arr[mid] List[int]: #implementing binarySearchLast to populate the self.end_index Solution.binarySearchLast(self,nums,0,len(nums)-1,target) #implementing binarySearchFirst to populate the self.start_index Solution.binarySearchFirst(self,nums,0,len(nums)-1,target) return [self.start_index, self.end_index] ================================================ FILE: Binary Search/first_and_last_pos.js ================================================ /* -----------QUESTION-------------- Given an array of integers nums sorted in non-decreasing order, find the starting and ending position of a given target value. If target is not found in the array, return [-1, -1]. Devise an algorithm with O(log n) runtime complexity. Example 1: Input: nums = [5,7,7,8,8,10], target = 8 Output: [3,4] Example 2: Input: nums = [5,7,7,8,8,10], target = 6 Output: [-1,-1] Example 3: Input: nums = [], target = 0 Output: [-1,-1] Constraints: 0 <= nums.length <= 105 -109 <= nums[i] <= 109 nums is a non-decreasing array (increasing order) -109 <= target <= 109 */ // ------------SOLUTION------------- /* An intuitive Binary Search algorithm is used to find target value indices of occurence since it is given an increasing array. We perform two searches one for first occurence and second for last occurence. Two pointer start and end for 0th and (N-1)th index to iteratively find the mid of array. First follow the common left-based binary search to find the target value first occurence. If found we proceed further check in left subarray as target may still be presnt either to left or right of mid. To find last occurence index we again iterate to right subarray from start as the current value but reset last to N-1. (start does not require to begin again from 0th index as in first pass we have covered upto start index. This does not affect overall complexity but reduces number of comparisions.) */ /* Time Complexity: O(log N) - Each time the sub-array reduces to half as N/2 -> N/4 -> N/8 ... after k iterations this results to O(1) N/ 2^k = 1 N = 2^k (take a log) k = log(N) (the complexity is defined by how many times the loop executes which is k times) T.C = O(log N) Space Complexity: O(1) - Constant space for storing variables */ var searchRange = function(nums, target) { let ans = [-1, -1]; // array of resulting indices pre-initialised with -1 in case the target is not found let start = 0, end = nums.length - 1, mid; //First Occurence while (start <= end) { mid = Math.floor((start + end) / 2); if (nums[mid] === target) { ans[0] = mid; end = mid - 1; //we continue to search in left subarray to find the first occurence position of target value } else if (nums[mid] > target) { end = mid - 1; } else { start = mid + 1; } } //Last Occurence end = nums.length - 1; while (start <= end) { mid = Math.floor((start + end) / 2); if (nums[mid] === target) { ans[1] = mid; start = mid + 1; //for last occurence we search the subarray right to the middle index } else if (nums[mid] > target) { end = mid - 1; } else { start = mid + 1; } } return ans; }; ================================================ FILE: Binary Search/first_and_last_pos_of_element.cpp ================================================ /* Given an array of integers nums sorted in non-decreasing order, find the starting and ending position of a given target value. If target is not found in the array, return [-1, -1]. You must write an algorithm with O(log n) runtime complexity. Example 1: Input: nums = [5,7,7,8,8,10], target = 8 Output: [3,4] Example 2: Input: nums = [5,7,7,8,8,10], target = 6 Output: [-1,-1] Example 3: Input: nums = [], target = 0 Output: [-1,-1] Constraints: 0 <= nums.length <= 105 -109 <= nums[i] <= 109 nums is a non-decreasing array. -109 <= target <= 109 */ #include class Solution { public: int get_index(vector& nums, int target, bool found){ int ans = -1; int start = 0, end = nums.size() - 1; while(start <= end){ int mid = start + (end - start) / 2; if(nums[mid] == target){ ans = mid; if(found){ end = mid - 1; // search in left part } else{ start = mid + 1; // search in right part } } else if(nums[mid] > target){ end = mid - 1; } else{ start = mid + 1; } } return ans; } vector searchRange(vector& nums, int target) { vector ans(2, -1); int first = get_index(nums, target, true); if(first == -1) return ans; int last = get_index(nums, target, false); //ans.push_back(first); //ans.push_back(last); ans[0] = first; ans[1] = last; return ans; } }; ================================================ FILE: Binary Search/first_and_last_position.java ================================================ /* Given an array of integers nums sorted in non-decreasing order, find the starting and ending position of a given target value If target is not found in the array, return [-1, -1]. You must write an algorithm with O(log n) runtime complexity. Example 1: Input: nums = [5,7,7,8,8,10], target = 8 Output: [3,4] Example 2: Input: nums = [5,7,7,8,8,10], target = 6 Output: [-1,-1] How this Code Works? The firstPos method will give the starting position of the target element. We will find the mid and check if the mid element is equals to the target we will check if previous element is equal to target or not if it is then place low to mid-1 if not just update the high to mid-1. Next condition is if the element is greater then target it means we have to search in the left side of array hence high=mid-1.And last condition if the element is less than the target do low=mid-1. If this method returns Integer.MAX_VALUE if means element is not present. Hence return {-1,-1} as answer. The lastPost method works same way as the above method. Passed extra paramter of low because it's now already clear that from where we have to start as we already got the first index. */ class FirstandLastPosition { public int[] searchRange(int[] nums, int target) { if(nums.length==0) return new int[]{-1,-1}; int a=firstPos(nums, target); int b=0; if(a!=Integer.MAX_VALUE){ b=lastPos(nums,target,a); }else{ return new int[]{-1,-1}; } return new int[]{a,b}; } private int firstPos(int[] arr,int target){ int ans=Integer.MAX_VALUE,low=0,high=arr.length-1; while(low<=high){ int mid=low+(high-low)/2; if(arr[mid]==target){ ans=Math.min(ans,mid); if(mid>0 && arr[mid-1]==target) low=mid-1; high=mid-1; }else if(arr[mid]>target){ high=mid-1; }else if(arr[mid]target){ high=mid-1; }else{ low=mid+1; } } return ans; } } ================================================ FILE: Binary Search/first_last_pos.java ================================================ /* Given a sorted array arr containing n elements with possibly duplicate elements, the task is to find indexes of first and last occurrences of an element x in the given array. Example 1: Input: arr [] = [ 1, 3, 5, 5, 5, 5, 67, 123, 125 ] , target = 5 OUTPUT: [2 5] Explanation: First occurrence of 5 is at index 2 and last occurrence of 5 is at index 5. Example 2: Input: arr[] = [1,2,3,,5,6,7,8],target = 4 OUTPUT: [-1,-1] APPROACH: #We will separately write methods for first occurence and last occurence of the element # Follows the standard binary search algorithm with little moddification for first and last occurence # For the first occurence , return the start index at the end of the loop # For the last occurence , return the end index at the end of loop*/ import java.util.*; public class FirstLastPos{ public static int firstOccurence(int[] arr,int target){ int start = 0,end=arr.length-1; while(start <= end){ int mid = start + (end-start)/2; if (arr[mid]== target){ end = mid -1; } else if(arr[mid] > target){ end = mid -1; } else if(arr[mid] < target){ start = mid + 1; } } if(start < arr.length && arr[start] == target){ return start; } else{ return -1; } } public static int lastOccurence(int[] arr,int target){ int start = 0,end=arr.length-1; while(start <= end){ int mid = start + (end-start)/2; if (arr[mid]== target){ start = mid + 1; } else if(arr[mid] > target){ end = mid -1; } else if(arr[mid] < target){ start = mid + 1; } } if(end >= 0 && arr[end] == target){ return end; } else{ return -1; } } public static void main(String[] args){ int[] arr = {1,5,7,7,7,9,11,14}; int target = 7; int[] result = new int[2]; result[0] = firstOccurence(arr,target); result[1] = lastOccurence(arr,target); System.out.println(Arrays.toString(result)); } } ================================================ FILE: Binary Search/first_occurance.go ================================================ // Search of first occurance of element using Binary search // Sample Input {0, 1, 1, 4, 5} key 1 // Output 1 // Time conplexity O(log n) package main import "fmt" func FirstOccurance(Arr []int, key int) int { start, end, result := 0, len(Arr)-1, -1 for start <= end { mid := start + (end-start)/2 if Arr[mid] > key { end = mid - 1 } else if Arr[mid] < key { start = mid + 1 } else { result = mid // update result end = mid - 1 // move to left side to search for occurance } } return result } func main() { Arr := []int{0, 1, 1, 4, 5} fmt.Println(FirstOccurance(Arr, 1)) } ================================================ FILE: Binary Search/first_occurence.java ================================================ /* Finding the index of the first occurence of a number in a sorted array: Given a sorted array and a number x,find the first occurence of x in the array. If the element is not found , return -1. Solve it in O(logN) complexity Example : INPUT : nums = [1,5,7,7,7,9,11,14], target = 7 OUTPUT: 2 INPUT : nums = [1,2,4,6,45,55], target = 20 OUTPUT: -1 Approach: # Approach will be similar to other binary search problems, but there is a need to find first occurence, So ,if the element found is equal to target, make the end pointer point before of the mid value. # Once the condition violates and loop terminates ,check if the target value is equal to the start pointer value. if it is equal, return the start pointer. if it is not return -1; */ public class FirstOccurence{ public static int firstOccurence(int[] arr,int target){ int start = 0,end=arr.length-1; while(start <= end){ int mid = start + (end-start)/2; if (arr[mid]== target){ end = mid -1; } else if(arr[mid] > target){ end = mid -1; } else if(arr[mid] < target){ start = mid + 1; } } if(start < arr.length && arr[start] == target){ return start; } else{ return -1; } } public static void main(String[] args){ int[] arr = {1,5,7,7,7,9,11,14}; int target = 7; System.out.print(firstOccurence(arr,target)); } } ================================================ FILE: Binary Search/first_true.cpp ================================================ /* An array of boolean values is divided into two sections; the left section consists of all false and the right section consists of all true. Find the First True in a Sorted Boolean Array of the right section, i.e. the index of the first true element. If there is no true element, return -1. Input: arr = [false, false, true, true, true] Output: 2 Explanation: first true's index is 2. */ #include #include int findBoundary(std::vector& arr) { int low = 0; // Initialize the low pointer to the beginning of the vector. int high = arr.size() - 1; // Initialize the high pointer to the end of the vector. int bv = -1; // Initialize bv (boundary value) to -1. while (low <= high) { int mid = low + (high - low) / 2; // Calculate the middle index. if (!arr[mid]) { // If the element at the middle index is 'false', // it means that the last 'true' value should be on the right side. low = mid + 1; // Move the low pointer to the right of mid. } else { // If the element at the middle index is 'true', // update bv to the current middle index and continue searching on the left side. bv = mid; // Update bv to the current middle index. high = mid - 1; // Move the high pointer to the left of mid. } } // The loop ends when low > high, indicating that the search is complete. // bv contains the index of the last 'true' value encountered. return bv; } int main() { std::vector arr = {false, false, false, true, true, true, true}; int boundary = findBoundary(arr); std::cout << "Boundary Index: " << boundary << std::endl; return 0; } ================================================ FILE: Binary Search/first_true.go ================================================ /* An array of boolean values is divided into two sections; the left section consists of all false and the right section consists of all true. Find the First True in a Sorted Boolean Array of the right section, i.e. the index of the first true element. If there is no true element, return -1. Input: arr = [false, false, true, true, true] Output: 2 Explanation: first true's index is 2. */ package main import ( "fmt" ) func findBoundary(arr []bool) int { low := 0 // Initialize the low pointer to the beginning of the slice. high := len(arr) - 1 // Initialize the high pointer to the end of the slice. bv := -1 // Initialize bv (boundary value) to -1. for low <= high { mid := low + (high - low) / 2 // Calculate the middle index. if !arr[mid] { // If the element at the middle index is 'false', // it means that the last 'true' value should be on the right side. low = mid + 1 // Move the low pointer to the right of mid. } else { // If the element at the middle index is 'true', // update bv to the current middle index and continue searching on the left side. bv = mid // Update bv to the current middle index. high = mid - 1 // Move the high pointer to the left of mid. } } // The loop ends when low > high, indicating that the search is complete. // bv contains the index of the last 'true' value encountered. return bv } func main() { arr := []bool{false, false, false, true, true, true, true} boundary := findBoundary(arr) fmt.Println("Boundary Index:", boundary) } ================================================ FILE: Binary Search/first_true.java ================================================ /* An array of boolean values is divided into two sections; the left section consists of all false and the right section consists of all true. Find the First True in a Sorted Boolean Array of the right section, i.e. the index of the first true element. If there is no true element, return -1. Input: arr = [false, false, true, true, true] Output: 2 Explanation: first true's index is 2. */ public import java.util.Arrays; import java.util.List; import java.util.Scanner; import java.util.stream.Collectors; class Solution { public static int findBoundary(List arr) { int low = 0; // Initialize the low pointer to the beginning of the list. int high = arr.size() - 1; // Initialize the high pointer to the end of the list. int bv = -1; // Initialize bv (boundary value) to -1. while (low <= high) { int mid = low + (high - low) / 2; // Calculate the middle index. if (arr.get(mid) == false) { // If the element at the middle index is 'false', // it means that the last 'true' value should be on the right side. low = mid + 1; // Move the low pointer to the right of mid. } else { // If the element at the middle index is 'true', // update bv to the current middle index and continue searching on the left side. bv = mid; // Update bv to the current middle index. high = mid - 1; // Move the high pointer to the left of mid. } } // The loop ends when low > high, indicating that the search is complete. // bv contains the index of the last 'true' value encountered. return bv; } } ================================================ FILE: Binary Search/first_true.js ================================================ /* An array of boolean values is divided into two sections; the left section consists of all false and the right section consists of all true. Find the First True in a Sorted Boolean Array of the right section, i.e. the index of the first true element. If there is no true element, return -1. Input: arr = [false, false, true, true, true] Output: 2 Explanation: first true's index is 2. */ function findBoundary(arr) { let low = 0; // Initialize the low pointer to the beginning of the array. let high = arr.length - 1; // Initialize the high pointer to the end of the array. let bv = -1; // Initialize bv (boundary value) to -1. while (low <= high) { let mid = low + Math.floor((high - low) / 2); // Calculate the middle index. if (!arr[mid]) { // If the element at the middle index is 'false', // it means that the last 'true' value should be on the right side. low = mid + 1; // Move the low pointer to the right of mid. } else { // If the element at the middle index is 'true', // update bv to the current middle index and continue searching on the left side. bv = mid; // Update bv to the current middle index. high = mid - 1; // Move the high pointer to the left of mid. } } // The loop ends when low > high, indicating that the search is complete. // bv contains the index of the last 'true' value encountered. return bv; } const arr = [false, false, false, true, true, true, true]; const boundary = findBoundary(arr); console.log("Boundary Index:", boundary); ================================================ FILE: Binary Search/first_true.py ================================================ ''' An array of boolean values is divided into two sections; the left section consists of all false and the right section consists of all true. Find the First True in a Sorted Boolean Array of the right section, i.e. the index of the first true element. If there is no true element, return -1. Input: arr = [false, false, true, true, true] Output: 2 Explanation: first true's index is 2. ''' def findBoundary(arr): low = 0 # Initialize the low pointer to the beginning of the list. high = len(arr) - 1 # Initialize the high pointer to the end of the list. bv = -1 # Initialize bv (boundary value) to -1. while low <= high: mid = low + (high - low) // 2 # Calculate the middle index. if not arr[mid]: # If the element at the middle index is 'false', # it means that the last 'true' value should be on the right side. low = mid + 1 # Move the low pointer to the right of mid. else: # If the element at the middle index is 'true', # update bv to the current middle index and continue searching on the left side. bv = mid # Update bv to the current middle index. high = mid - 1 # Move the high pointer to the left of mid. # The loop ends when low > high, indicating that the search is complete. # bv contains the index of the last 'true' value encountered. return bv arr = [False, False, False, True, True, True, True] boundary = findBoundary(arr) print("Boundary Index:", boundary) ================================================ FILE: Binary Search/floor_of_target.java ================================================ /* Floor of an element in a sorted array You are given a sorted array nums and a target . Find the index of the greatest element that is less than or equal to target EXAMPLES: INPUT : nums = [2,4,5,7,9,11,18,25], target = 18 OUTPUT: 6 INPUT : nums = [2,4,5,7,9,11,18,25], target = 10 OUTPUT: 4 APPROACH : We will implement this problem using BinarySearch since the array is sorted and will be similar to ceil but with slight modification */ public class FloorOfTarget{ public static int search_floor(int[] nums,int target){ int start = 0,end = nums.length-1; while(start <=end){ int mid = start +(end-start)/2; if(nums[mid]==target){ return mid; // returns the target } else if(nums[mid] > target){ end = mid-1; } else{ start = mid +1; } } return end; // returns the nearest element to target if the target is not found } public static void main(String[] args){ int[] nums = {2,4,5,7,9,11,18,25}; int target = 10; // output will be 4 in this case. System.out.print(search_floor(nums,target)); } } ================================================ FILE: Binary Search/index_position.java ================================================ /* BINARY SEARCH Problem Statement: Given an array of integers nums sorted in ascending order and a target , You need to search the target in the array nums. If the target is found ,return the index position,else return -1 Time complexity: WorstCase: O(log N) N--> length of the array BestCase: O(1) Example : Input: nums = [-5,-2,0,2,5,30], target = 2 Output: 3 Explanation : target 2 is found at index 3 */ public class IndexPosition { // method for implementation of binary search public static int search(int[] nums, int target) { int start=0,end=nums.length-1; while(start<=end){ int mid = start + (end-start)/2; // if middle value is equal to target return the mid index if(nums[mid] == target){ return mid; } // if mid value is greater than the target, search the left sub array else if(nums[mid] > target){ end = mid - 1; } // if mid value is lesser than the target, search the right sub array else if(nums[mid] < target){ start = mid + 1; } } return -1; } public static void main(String[] args){ //get input array and target value // you can also get input dynamically using Scanner class int [] array = {-5,-2,0,2,5,30}; int target = 2; System.out.print(search(array,target)); } } ================================================ FILE: Binary Search/infinity_array.java ================================================ /*Binary Search in an Infinite array in Java * Given an array whose size is not fixed,find the index of the target element in the array. * EXAMPLE: [5,15,34,56,77,87...] (array can be of any size) * Target = 56; * Output: 3; * * APPROACH: * The approach will be similar to normal binary search but in reverse order * ie; For the start and end value, find the range which will be taking the target in the range. * We will not specify the array length since the array size is infinity. * Send the start and end value to the search function and return the position of the element. * */ public class InfinityArray { public static void main(String[] args){ int[] nums = {2,5,7,9,14,45,56,77,89,101};// can be of any size int target = 56; System.out.println(range(nums,target)); } public static int range(int[] nums,int target){ int start=0,end=1; while(target > nums[end]){ int temp = end +1; end = end + (end-start)*2; start = temp; } return search(nums,target,start,end); } public static int search(int[] nums, int target,int start,int end) { while(start<=end){ int mid = start + (end-start)/2; // if middle value is equal to target return the mid index if(nums[mid] == target){ return mid; } // if mid value is greater than the target, search the left sub array else if(nums[mid] > target){ end = mid - 1; } // if mid value is lesser than the target, search the right sub array else if(nums[mid] < target){ start = mid + 1; } } return -1; } } ================================================ FILE: Binary Search/last_occurance.go ================================================ // Search of last occurance of element using Binary search // Sample Input {0, 1, 1, 4, 5} key 1 // Output 1 // Time conplexity O(log n) package main import "fmt" func LastOccurance(Arr []int, key int) int { start, end, result := 0, len(Arr)-1, -1 for start <= end { mid := start + (end-start)/2 if Arr[mid] > key { end = mid - 1 } else if Arr[mid] < key { start = mid + 1 } else { result = mid // update result start = mid + 1 // move to RIGHT side to search for occurance } } return result } func main() { Arr := []int{0, 1, 1, 1, 2, 2, 3, 3, 4, 5} fmt.Println(LastOccurance(Arr, 1)) // 3 fmt.Println(LastOccurance(Arr, 2)) // 5 fmt.Println(LastOccurance(Arr, 3)) // 7 } ================================================ FILE: Binary Search/median_of_two_sorted_arrays.cpp ================================================ /* Approach: let nums1 = [1, 3, 4, 7, 10, 12], nums2 = [2, 3, 6, 15] In order to find the median, we need a single sorted array. So a naive approach is that, just merge the 2 sorted arrays and find the median of that array. This will have a time Complexity of O(n1 + n2), Space Complexity of O(n1 + n2) Now, lets optimise it. So, the sorted form of the given array is arr = [1, 2, 3, 3, 4, 6, 7, 10, 12, 15]. To find the median of the array, we need to select the 2 mid elements and average it out. If we observe the sorted array carefully, then we notice that the 2 middle elements are arr[4] = 4 and arr[5] = 6, => arr[4] <= arr[5]. Thought process: Now, since the arrays are sorted, so the binary searh may be able to solve the problem. Observation 1: If we partition the sorted array arr into 2 halves, then for sure we know that there would be 5 elements on left half and 5 elements on right half. Now, we can select 5 elements for right half from nums1 and nums2 combinedly and similarly the rest of the elements for the left half. Example: 1. left => [1, 3, 4, 7, 2], right => [10, 12, 3, 6, 15] 2. left => [1, 3, 4, 2, 3], right => [7, 10, 12, 6, 15] 3. left => [1, 3, 2, 3, 6], right => [4, 7, 10, 12, 15] Observation 2: All the elements on left half is lesser than all the elements on the right half. Now, according to the observation, I have to check that all the elements in the left <= all the elements in the right. This can be done by just comparing the maximum of left half <= minimum of right half. Hence, the problem boils down to a searching problem; but how to identify the binary search approach?? Suppose, we partition the element as example 1, then the max of left[] = 7 and min of right[] = 3, but according to the 2nd observation, it is not valid. So, in order to have a correct max in left, I need to reduce its value and consequestly, the min of right[] should be increased. That means, I have to move leftwards in nums1 to have a correct max value in left half. */ // Leetcode link: https://leetcode.com/problems/median-of-two-sorted-arrays/ #include class Solution { public: double findMedianSortedArrays(std::vector& nums1, std::vector& nums2) { int n1 = nums1.size(); // stores the length of nums1 array int n2 = nums2.size(); // stores the length of nums2 array if(n2 > n1) return findMedianSortedArrays(nums2, nums1); // according to approach described above, I am applying binary search on nums1 array int lo = 0, hi = n1; while(lo <= hi){ int mid1 = (lo+hi)/2; // mid of nums1 int mid2 = (n1 + n2 + 1)/2 - mid1; std::pair maxleft, minright; maxleft.first = mid1 == 0 ? INT_MIN : nums1[mid1-1]; maxleft.second = mid2 == 0 ? INT_MIN : nums2[mid2-1]; minright.first = mid1 == n1 ? INT_MAX : nums1[mid1]; minright.second = mid2 == n2 ? INT_MAX : nums2[mid2]; if(maxleft.first <= minright.second and maxleft.second <= minright.first){ if((n1+n2)%2 == 1){ return std::max(maxleft.first, maxleft.second); } else { return (std::max(maxleft.first, maxleft.second) + std::min(minright.first, minright.second))/2.0; } } else if (maxleft.first > minright.second){ hi = mid1-1; } else { lo = mid1+1; } } } }; int main(int argc, char const *argv[]) { int n; std::cin>>n; std::vector arr1(n, 0); for(int i=0;i>arr1[i]; } std::cin>>n; std::vector arr2(n, 0); for(int i=0;i>arr2[i]; } Solution sol = Solution(); double res = sol.findMedianSortedArrays(arr1, arr2); std::cout< a - b); const n = merged.length; if (n % 2 === 0) { const middle = n / 2; return (merged[middle - 1] + merged[middle]) / 2; } else { const middle = Math.floor(n / 2); return merged[middle]; } } // Approach 2: Binary Search function findMedianSortedArrays(arr1, arr2) { // If arr1 is longer than arr2, swap them to ensure arr1 is shorter if (arr1.length > arr2.length) { [arr1, arr2] = [arr2, arr1]; } const m = arr1.length; const n = arr2.length; let left = 0; let right = m; while (left <= right) { // Partition arr1 and arr2 const partition1 = Math.floor((left + right) / 2); const partition2 = Math.floor((m + n + 1) / 2) - partition1; // Calculate the max and min elements of the left and right partitions const maxLeft1 = partition1 === 0 ? -Infinity : arr1[partition1 - 1]; const minRight1 = partition1 === m ? Infinity : arr1[partition1]; const maxLeft2 = partition2 === 0 ? -Infinity : arr2[partition2 - 1]; const minRight2 = partition2 === n ? Infinity : arr2[partition2]; // If the partitions are correctly balanced, return the median if (maxLeft1 <= minRight2 && maxLeft2 <= minRight1) { if ((m + n) % 2 === 0) { return ( (Math.max(maxLeft1, maxLeft2) + Math.min(minRight1, minRight2)) / 2 ); } else { return Math.max(maxLeft1, maxLeft2); } } else if (maxLeft1 > minRight2) { // If maxLeft1 is too big, move partition1 to the left right = partition1 - 1; } else { // If maxLeft2 is too big, move partition1 to the right left = partition1 + 1; } } } ================================================ FILE: Binary Search/minimum_in_rotated_sorted_array.cpp ================================================ /* Suppose an array of length n sorted in ascending order is rotated between 1 and n times. For example, the array nums = [0,1,2,4,5,6,7] might become: [4,5,6,7,0,1,2] if it was rotated 4 times. [0,1,2,4,5,6,7] if it was rotated 7 times. Notice that rotating an array [a[0], a[1], a[2], ..., a[n-1]] 1 time results in the array [a[n-1], a[0], a[1], a[2], ..., a[n-2]]. Given the sorted rotated array nums of unique elements, return the minimum element of this array. You must write an algorithm that runs in O(log n) time. Example 1: Input: nums = [3,4,5,1,2] Output: 1 Explanation: The original array was [1,2,3,4,5] rotated 3 times. Example 2: Input: nums = [4,5,6,7,0,1,2] Output: 0 Explanation: The original array was [0,1,2,4,5,6,7] and it was rotated 4 times. Example 3: Input: nums = [11,13,15,17] Output: 11 Explanation: The original array was [11,13,15,17] and it was rotated 4 times. Constraints: n == nums.length 1 <= n <= 5000 -5000 <= nums[i] <= 5000 All the integers of nums are unique. nums is sorted and rotated between 1 and n times. */ class Solution { public: int findMin(vector& nums) { int n = nums.size(); int start = 0, end = n - 1; while(start <= end){ int mid = start + (end - start) / 2; int next = (mid + 1) % n; int prev = (mid - 1 + n) % n; if(nums[mid] <= nums[prev] && nums[mid] <= nums[next]) return nums[mid]; if(nums[mid] <= nums[end]){ end = mid - 1; } else if(nums[mid] >= nums[start]){ start = mid + 1; } } return -1; } }; ================================================ FILE: Binary Search/perfect_square.java ================================================ /* finding the perfect square Given a positive integer num, return true if num is a perfect square or false otherwise. A perfect square is an integer that is the square of an integer. In other words, it is the product of some integer with itself. Examples: Input: num = 25 Output: true Explanation: We return true because 5 * 5 = 25 and 5 is an integer. Input: num = 14 Output: false Explanation: We return false because 3.742 * 3.742 = 14 and 3.742 is not an integer. APPROACH: As we know square of a squareroot of a number is that number itself. for example if the number is 36 and the square root of 36 is 6 , we can say that 6*6 =36 (our number). We use this logic with binary search algorithm */ class PerfectSquare { public static boolean isPerfectSquare(int num) { // Binary Search long start =1,end=num; while(start<=end){ long mid = start + (end-start)/2; if(mid*mid == num){ return true; } else if(mid*mid > num){ end = mid-1; } else if(mid*mid < num){ start = mid+1; } } return false; } public static void main(String[] args){ // you can also get input dynamically using Scanner class int number = 100; System.out.print(isPerfectSquare(number)); }} ================================================ FILE: Binary Search/search_in_rotated_sorted_array.cpp ================================================ /* Given the array nums after the possible rotation and an integer target, return the index of target if it is in nums, or -1 if it is not in nums. You must write an algorithm with O(log n) runtime complexity. Input: nums = [4,5,6,7,0,1,2], target = 0 Output: 4 Input: nums = [4,5,6,7,0,1,2], target = 3 Output: -1 Input: nums = [1], target = 0 Output: -1 Constraints: > 1 <= nums.length <= 5000 > -104 <= nums[i] <= 104 > All values of nums are unique. > nums is an ascending array that is possibly rotated. > -104 <= target <= 104 APPROACH 1. Find the index at which the array has been rotated with the help of Binary Search. 2. Store the index of rotation as INDX and find the range where target might be found using the following comparision: RANGE = {INDX+1, HIGH} if TARGET < NUMS[LOW] RANGE = {LOW, INDX-1} if TARGET > NUMS{HIGH] 3. Perform Binary Search for TARGET in the required range. 4. If target is not found return -1. TIME COMPLEXITY : O(NlogN) SPACE COMPLEXITY: O(1) */ class Solution { public: int search(vector& nums, int target) { // Initialize Variable for later Usage int n=nums.size(); int low=0; int high=n-1; //Find the Rotation Point in the Rotated Array while(low<=high){ int mid=(low+high)/2; if(mid==0 || mid==n-1){ low=mid; break; } if(nums[mid-1]>nums[mid] && nums[mid+1]>nums[mid]){ low=mid; break; } else if(nums[mid]>nums[low] && nums[mid]>nums[high]){ low=mid+1; } else{ high=mid-1; } } // Re-initialize Variables Needed int indx=low; low=0, high=n-1; if(target==nums[indx]){ return indx; } else if(target>nums[high]){ high=indx-1; } else if(targetnums[mid]){ low=mid+1; } else{ high=mid-1; } } // If target not found return -1 return -1; } }; ================================================ FILE: Binary Search/search_in_sorted_rotated_array.cpp ================================================ /* There is an integer array nums sorted in ascending order (with distinct values). Prior to being passed to your function, nums is possibly rotated at an unknown pivot index k (1 <= k < nums.length) such that the resulting array is [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]] (0-indexed). For example, [0,1,2,4,5,6,7] might be rotated at pivot index 3 and become [4,5,6,7,0,1,2]. Given the array nums after the possible rotation and an integer target, return the index of target if it is in nums, or -1 if it is not in nums. You must write an algorithm with O(log n) runtime complexity. Example 1: Input: nums = [4,5,6,7,0,1,2], target = 0 Output: 4 Example 2: Input: nums = [4,5,6,7,0,1,2], target = 3 Output: -1 Example 3: Input: nums = [1], target = 0 Output: -1 Constraints: 1 <= nums.length <= 5000 -104 <= nums[i] <= 104 All values of nums are unique. nums is an ascending array that is possibly rotated. -104 <= target <= 104 */ class Solution { public: int search(vector& nums, int target) { int start = 0, end = nums.size() - 1; int ans = -1; while(start <= end){ int mid = start + (end - start) / 2; if(target == nums[mid]){ return mid; } if(nums[start] <= nums[mid]){ if(target >= nums[start] && target <= nums[mid]){ end = mid - 1; } else{ start = mid + 1; } } else{ if(target >= nums[mid] && target <= nums[end]){ start = mid + 1; } else { end = mid - 1; } } } return ans; } }; ================================================ FILE: Binary Search/search_in_sorted_rotated_array.go ================================================ /* The code snippet represents a function `SearchInSortedMatrix` that searches for a target value in a sorted matrix and returns the position (row and column) of the target if found, or [-1, -1] if not found. Here's how the code works: 1. Initialize the starting position at the top-right corner of the matrix, i.e., `row = 0` and `col = len(matrix[0]) - 1`. 2. Enter a loop that continues as long as the current position is within the matrix bounds, i.e., `row < len(matrix)` and `col >= 0`. 3. Inside the loop, compare the value at the current position (`matrix[row][col]`) with the target value: - If the value is greater than the target, it means the target value must be in a lower column, so decrement the column index (`col--`). - If the value is less than the target, it means the target value must be in a higher row, so increment the row index (`row++`). - If the value is equal to the target, it means the target value is found. Return the position as [row, col]. 4. If the loop completes without finding the target value, it means the target is not present in the matrix. Return [-1, -1] to indicate that the target was not found. The code takes advantage of the sorted nature of the matrix to perform an efficient search. By starting at the top-right corner and comparing the current value with the target, it eliminates rows and columns in each iteration, narrowing down the search space until the target is found or the entire matrix is traversed. Overall, the code has a time complexity of O(N + M), where N is the number of rows in the matrix and M is the number of columns, as it performs a linear search through the matrix. Time complexity: O(n + m) time where n is the length of the matrix's rows and m is the length of the matrix's columns Space complexity: O(1) */ package main import "fmt" func SearchInSortedMatrix(matrix [][]int, target int) []int { // Initialize the starting position at the top-right corner of the matrix row, col := 0, len(matrix[0])-1 // Continue the loop as long as the current position is within the matrix bounds for row < len(matrix) && col >= 0 { // Compare the value at the current position with the target value if matrix[row][col] > target { // If the value is greater than the target, move to the left column col-- } else if matrix[row][col] < target { // If the value is less than the target, move to the next row row++ } else { // If the value is equal to the target, return the position [row, col] return []int{row, col} } } // If the loop completes without finding the target, return [-1, -1] return []int{-1, -1} } func main() { matrix := [][]int{ {1, 4, 7, 12, 15, 1000}, {2, 5, 19, 31, 32, 1001}, {3, 8, 24, 33, 35, 1002}, {40, 41, 42, 44, 45, 1003}, {99, 100, 103, 106, 128, 1004}, } target := 33 result := SearchInSortedMatrix(matrix, target) if result[0] == -1 && result[1] == -1 { fmt.Println("Target not found in the matrix") } else { fmt.Println("Target found at position:", result) } } ================================================ FILE: Binary Search/search_in_sorted_rotated_array.java ================================================ /*There is an integer array nums sorted in ascending order (with distinct values). Prior to being passed to your function, nums is possibly rotated at an unknown pivot index k (1 <= k < nums.length) such that the resulting array is [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]] (0-indexed). For example, [0,1,2,4,5,6,7] might be rotated at pivot index 3 and become [4,5,6,7,0,1,2]. Given the array nums after the possible rotation and an integer target, return the index of target if it is in nums, or -1 if it is not in nums. You must write an algorithm with O(log n) runtime complexity. Example 1: Input: nums = [4,5,6,7,0,1,2], target = 0 Output: 4 Example 2: Input: nums = [4,5,6,7,0,1,2], target = 3 Output: -1 Example 3: Input: nums = [1], target = 0 Output: -1 */ public class searchInRotatedArray { //Brute force solution public static int searchTarget(int nums[] , int target) { int result = -1; for(int i=0 ; i=target) { end = mid-1; } else { start =mid+1; } } else { if(nums[mid]<=target && targethigh): return -1 mid = (low+high)//2 if(nums[mid]target): return Solution.binarySearch(nums,low,mid-1,target) else: return mid def search(self, nums: List[int], target: int) -> int: #Finding the index where the array was rotated and binary searching the first and second parts for the target value. rotated_index = -1 for i in range(len(nums)-1): if(nums[i]>nums[i+1]): rotated_index = i break return Solution.binarySearch(nums,0,rotated_index,target) & Solution.binarySearch(nums,rotated_index+1,len(nums)-1,target) ================================================ FILE: Binary Search/search_insert_position.js ================================================ // https://leetcode.com/problems/search-insert-position/description/ /* Given a sorted array of distinct integers and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order. You must write an algorithm with O(log n) runtime complexity. Example 1: Input: nums = [1,3,5,6], target = 5 Output: 2 Example 2: Input: nums = [1,3,5,6], target = 2 Output: 1 Example 3: Input: nums = [1,3,5,6], target = 7 Output: 4 Explanation: In a given sorted array this code uses binary search where we divide the array into two parts and check if the number is present then we return the index of element otherwise we return what should be index of that target in the given array. The loops iterates until the start index and end index of array is same. We calculate the middle index on each iteration of loop as start index / end index might change on the basis of last iteration result. If the middle index element is the target element then we return middle index otherwise we loop through the entire array until last one or two elements left. On the basis of last one or two elements we decide what is going to be the index of target in the array. */ function findSearchPosition(nums, target) { let startIndex = 0; let endIndex = nums.length - 1; while (startIndex <= endIndex) { const midIndex = startIndex + Math.floor((endIndex - startIndex) / 2); // Base case if middle index element is our target then we will return the middle index if (nums[midIndex] === target) { return midIndex; } // In case if element is not present in the given array then in the final iteration when only last one or two elements are remaining we will check what should be our target number index in the given array if (startIndex === endIndex || startIndex + 1 === endIndex) { // If element at start index is greater then target then target then start index will be assigned to target if (nums[startIndex] > target) { return startIndex; // if target is greater then end index element then end index + 1 will be assigned to target } else if (nums[endIndex] < target) { return endIndex + 1; } else { return startIndex + 1; } } // if the target element is greater then the middle index element then we will change the start index next to middle index otherwise we will change last index to middle index - 1 if (nums[midIndex] < target) { startIndex = midIndex + 1; } else { endIndex = midIndex - 1; } } } //driver code var nums = [1, 3, 5, 6]; console.log(findSearchPosition(nums, 8)); //Input: nums = [1,3,5,6], target = 8 // Output: 4 ================================================ FILE: Binary Search/square_root.java ================================================ /* Square root of a number Given a non-negative integer x, return the square root of x rounded down to the nearest integer. The returned integer should be non-negative as well. Examples: Input: x = 36 Output: 6 Explanation: The square root of 36 is 6, so we return 6. Input: x = 8 Output: 2 Explanation: The square root of 8 is 2.82842..., and since we round it down to the nearest integer, 2 is returned. HOW TO SOLVE: This is similar to finding perfect square of number but to return the square root. We use Binary Search for this problem. */ public class SquareRoot{ public static int sqrt(int x){ int start = 1, end=x; while(start<=end){ int mid = (start+end)/2; if(x/mid==mid){ return mid; } else if(mid< x/mid){ start = mid+1; } else if(mid > x/mid){ end = mid-1; } } return end; } public static void main(String[] args){ // you can also get input dynamically using Scanner class int number = 36; System.out.print(sqrt(number)); } } ================================================ FILE: Bit Manipulation/bloom_filter.cpp ================================================ // Bloom Filter Implementation // Question: How does the Bloom filter work, and how is it implemented in the C++ // Approach and Explanation // 1.Hash Functions: The Bloom filter uses four hash functions (`h1`, `h2`, `h3`, `h4`) to map input strings to positions in the bit array. These hash functions generate different indexes based on the input string and the size of the bit array. // 2. Bit Array: The Bloom filter uses a `std::bitset<1000000>` called `bitarray` to represent the filter. It is a fixed-size bit array where each bit represents a position that can be set (1) or unset (0). // 3. Lookup Operation: The `lookup` function takes a reference to the `bitarray` and a string `s` as input. It calculates the hash values for `s` using the four hash functions and checks if the corresponding positions in the `bitarray` are all set (1). If all positions are set, it returns `true`, indicating that the element is possibly present in the filter. Otherwise, it returns `false`. // 4. Insert Operation: The `insert` function takes a reference to the `bitarray` and a string `s` as input. It first checks if the element is already present in the filter by calling the `lookup` function. If the element is already present, it prints a message indicating that it is probably already present. Otherwise, it calculates the hash values for `s` using the four hash functions and sets the corresponding positions in the `bitarray` to 1, indicating the insertion of the element. // 5. Main Function: In the `main` function, a `bitarray` of size 1000000 is created. A set of strings (`sarray`) is defined, representing the elements to be added to the filter. For each string `s` in `sarray`, it checks if `s` is already present in the filter using the `lookup` function. If it is present, it prints a message indicating that it is already present. Otherwise, it inserts `s` into the filter using the `insert` function. // The Bloom filter provides a probabilistic way to test whether an element is possibly in a set. It may return false positives, indicating that an element is possibly in the set when it's not, but it will never give false negatives. The accuracy of the filter depends on the size of the bit array, the number of hash functions used, and the number of elements inserted. // In this implementation, the size of the bit array is fixed at 1000000, and four hash functions are used. The elements are inserted by setting the corresponding positions in the bit array to 1. The `lookup` function checks if all the positions for an element are set (1), indicating its possible presence in the filter. // Time Complexity-> O(1) on average, O(n) in the worst case // Space Complexity-> O(n) #include #include #include #define ll long long // Hash function 1 // This function calculates the hash value of a string using a simple additive approach. int h1(std::string s, int arrSize) { ll int hash = 0; for (int i = 0; i < s.size(); i++) { hash = (hash + ((int)s[i])); // Add the ASCII value of each character to the hash hash = hash % arrSize; // Take the modulus to ensure the hash value is within the array size } return hash; } // Hash function 2 // This function calculates the hash value of a string using an exponential approach. int h2(std::string s, int arrSize) { ll int hash = 1; for (int i = 0; i < s.size(); i++) { hash = hash + pow(19, i) * s[i]; // Multiply each character by a power of 19 and add to the hash hash = hash % arrSize; // Take the modulus to ensure the hash value is within the array size } return hash % arrSize; } // Hash function 3 // This function calculates the hash value of a string using a polynomial approach. int h3(std::string s, int arrSize) { ll int hash = 7; for (int i = 0; i < s.size(); i++) { hash = (hash * 31 + s[i]) % arrSize; // Multiply the hash by 31, add the character, and take the modulus } return hash % arrSize; } // Hash function 4 // This function calculates the hash value of a string using a combination of multiplication and exponentiation. int h4(std::string s, int arrSize) { ll int hash = 3; int p = 7; for (int i = 0; i < s.size(); i++) { hash += hash * 7 + s[i] * pow(p, i); // Multiply the hash by 7, add the character multiplied by a power of 7 hash = hash % arrSize; // Take the modulus to ensure the hash value is within the array size } return hash; } // Lookup operation // This function checks if a given string is present in the bit array. bool lookup(std::bitset<1000000> &bitarray, std::string s) { int a = h1(s, bitarray.size()); // Calculate hash using h1 int b = h2(s, bitarray.size()); // Calculate hash using h2 int c = h3(s, bitarray.size()); // Calculate hash using h3 int d = h4(s, bitarray.size()); // Calculate hash using h4 return bitarray[a] && bitarray[b] && bitarray[c] && bitarray[d]; // Check if all corresponding bits are set in the bit array } // Insert operation // This function inserts a string into the bit array. void insert(std::bitset<1000000> &bitarray, std::string s) { // Check if the element is already present or not if (lookup(bitarray, s)) { std::cout << s << " is probably already present" << std::endl; } else { int a = h1(s, bitarray.size()); // Calculate hash using h1 int b = h2(s, bitarray.size()); // Calculate hash using h2 int c = h3(s, bitarray.size()); // Calculate hash using h3 int d = h4(s, bitarray.size()); // Calculate hash using h4 bitarray[a] = true; // Set corresponding bit in the bit array for hash a bitarray[b] = true; // Set corresponding bit in the bit array for hash b bitarray[c] = true; // Set corresponding bit in the bit array for hash c bitarray[d] = true; // Set corresponding bit in the bit array for hash d std::cout << s << " inserted" << std::endl; } } int main() { std::bitset<1000000> bitarray; std::string sarray[] = {"apple", "banana", "cherry", "orange", "grape", "kiwi"}; for (const auto &s : sarray) { if (lookup(bitarray, s)) { std::cout << s << " is already present" << std::endl; } else { insert(bitarray, s); } } return 0; } ================================================ FILE: Bit Manipulation/bloom_filter.py ================================================ # Bloom Filter Implementation # Question: How can I implement a Bloom filter in Python? # explanation of the approach: # 1. The `BloomFilter` class is defined to encapsulate the functionality of the Bloom filter. It takes two parameters during initialization: `capacity` (expected number of elements to be added to the filter) and `false_positive_rate` (desired false positive rate). # 2. The `calculate_size()` method is used to calculate the size of the bit array based on the capacity and false positive rate. It uses the formula `- (capacity * log(false_positive_rate)) / (log(2) ** 2)` to determine the size. # 3. The `calculate_num_hashes()` method calculates the number of hash functions to be used based on the size of the bit array and the capacity. It uses the formula `(size / capacity) * log(2)` to determine the number of hashes. # 4. The `bit_array` attribute is created using the `bitarray` library and initialized with the calculated size. All bits in the array are initially set to 0. # 5. The `add()` method is used to add an item to the Bloom filter. It iterates over the range of `num_hashes` and calculates the index for each hash function using the `mmh3.hash()` function. The index is obtained by taking the modulo of the hash value with the size of the bit array. The corresponding bit in the bit array is set to 1. # 6. The `contains()` method is used to check if an item is present in the Bloom filter. It follows a similar process as the `add()` method, iterating over the range of `num_hashes` and calculating the index for each hash function. If any of the corresponding bits in the bit array are 0, it indicates that the item is not present and False is returned. If all the bits are 1, it means the item might be present (false positive) and True is returned. # 7. In the example usage, a `BloomFilter` object is created with a capacity of 1000 and a false positive rate of 0.01. # 8. Three items ("apple", "banana", "cherry") are added to the Bloom filter using the `add()` method. # 9. The `contains()` method is used to check if certain items ("apple", "banana", "cherry", "orange") are present in the Bloom filter. The result is printed for each item. # The Bloom filter uses the MurmurHash3 hash function (`mmh3.hash()`) to generate hash values for the items. The number of hash functions and the size of the bit array are calculated based on the desired false positive rate and capacity. The Bloom filter provides a probabilistic check for membership, with a possibility of false positives but no false negatives. import math import mmh3 from bitarray import bitarray class BloomFilter: def __init__(self, capacity, false_positive_rate): self.capacity = capacity self.false_positive_rate = false_positive_rate self.size = self.calculate_size() self.num_hashes = self.calculate_num_hashes() self.bit_array = bitarray(self.size) self.bit_array.setall(0) def calculate_size(self): # Calculate the size of the bit array based on the capacity and false positive rate size = - (self.capacity * math.log(self.false_positive_rate) ) / (math.log(2) ** 2) return int(size) def calculate_num_hashes(self): # Calculate the number of hash functions based on the size and capacity num_hashes = (self.size / self.capacity) * math.log(2) return int(num_hashes) def add(self, item): # Add an item to the Bloom filter for seed in range(self.num_hashes): # Generate a hash value using the MurmurHash3 algorithm with different seeds index = mmh3.hash(item, seed) % self.size self.bit_array[index] = 1 def contains(self, item): # Check if an item is possibly in the Bloom filter for seed in range(self.num_hashes): # Generate a hash value using the MurmurHash3 algorithm with different seeds index = mmh3.hash(item, seed) % self.size if self.bit_array[index] == 0: return False return True # Create a Bloom filter with a capacity of 1000 items and a false positive rate of 0.01 bloom_filter = BloomFilter(capacity=1000, false_positive_rate=0.01) # Add items to the filter bloom_filter.add("apple") bloom_filter.add("banana") bloom_filter.add("cherry") # Check if items are in the filter print(bloom_filter.contains("apple")) # Expected output: True print(bloom_filter.contains("banana")) # Expected output: True print(bloom_filter.contains("cherry")) # Expected output: True print(bloom_filter.contains("orange")) # Expected output: False ================================================ FILE: Bit Manipulation/count_bits.go ================================================ // Program to count the number of bits that are set to 1 in an integer // The following program tests bits one at a time starting with the least-significant bit. // Since we perform O(1) computation per bit, the time complexity is O(n) where n is number of bits in the integer // Best case time complexity is O(1), if the input io 0 package main import ( "errors" "fmt" ) func CountBits(x int) (int, error) { if x < 0 { return x, errors.New("Negative number") } numBits := 0 for x > 0{ numBits += x & 1 x >>= 1 } return numBits, nil } func main() { res, err := CountBits(7) fmt.Println(res) fmt.Println(err) } ================================================ FILE: Bit Manipulation/interesting_array.java ================================================ package BitManipulation; import com.google.common.base.Stopwatch; public class InterestingArray { public static void main(String[] args) { Stopwatch timer = Stopwatch.createStarted(); int[] array = {9, 14, 27, 81, 197, 0, 1}; String ans = solve(array); System.out.println(ans); System.out.println("Runtime " + timer); } public static String solve(int[] array) { // O(N) time | O(1) space int oddCount = 0; for (var num : array) if (num % 2 != 0) oddCount += 1; return oddCount % 2 != 0 ? "No" : "Yes"; } } ================================================ FILE: Bit Manipulation/mod_array.java ================================================ /** * You are given a large number in the form of a array A of size N where each element denotes a digit of the number. * You are also given a number B. You have to find out the value of A % B and return it. * * * * Problem Constraints * 1 <= N <= 105 * 0 <= Ai <= 9 * 1 <= B <= 109 * * * Input Format * The first argument is an integer array A. * The second argument is an integer B. * * * Output Format * Return a single integer denoting the value of A % B. * * * Example Input * Input 1: * A = [1, 4, 3] * B = 2 * Input 2: * * A = [4, 3, 5, 3, 5, 3, 2, 1] * B = 47 * * * Example Output * Output 1: * 1 * Output 2: * * 20 * * * Example Explanation * Explanation 1: * 143 is an odd number so 143 % 2 = 1. * Explanation 2: * * 43535321 % 47 = 20 */ /** * You are given a large number in the form of a array A of size N where each element denotes a digit of the number. * You are also given a number B. You have to find out the value of A % B and return it. * * * * Problem Constraints * 1 <= N <= 105 * 0 <= Ai <= 9 * 1 <= B <= 109 * * * Input Format * The first argument is an integer array A. * The second argument is an integer B. * * * Output Format * Return a single integer denoting the value of A % B. * * * Example Input * Input 1: * A = [1, 4, 3] * B = 2 * Input 2: * * A = [4, 3, 5, 3, 5, 3, 2, 1] * B = 47 * * * Example Output * Output 1: * 1 * Output 2: * * 20 * * * Example Explanation * Explanation 1: * 143 is an odd number so 143 % 2 = 1. * Explanation 2: * * 43535321 % 47 = 20 */ package ModularArithmetic; public class ModArray { public static void main(String[] args) { int[] array = {4, 3, 5, 3, 5, 3, 2, 1}; int divisor = 47; int ans = solve(array, divisor); System.out.println(ans); } public static int solve(int[] array, int divisor) { // O(N) time | O(1) space long res = 0; int len = array.length - 1; long POWER_10 = 1; long MOD = divisor; for (int i = len; i > -1; i--) { long currentDigit = array[i]; long currentValue = (currentDigit * POWER_10) % MOD; res = (res + currentValue) % MOD; POWER_10 = (POWER_10 * 10) % MOD; } return (int) res; } } ================================================ FILE: Bit Manipulation/number_of_1_bits.java ================================================ /** * Write a function that takes an integer and returns the number of 1 bits it has. * * * Problem Constraints * 1 <= A <= 109 * * * Input Format * First and only argument contains integer A * * * Output Format * Return an integer as the answer * * * Example Input * Input 1: * 11 * Input 2: * 6 * * * Example Output * Output 1: * 3 * Output 2: * 2 * * * Example Explanation * Explaination 1: * 11 is represented as 1011 in binary. * Explaination 2: * 6 is represented as 110 in binary. */ package BitManipulation; public class NumberOf1Bits { public static void main(String[] args) { int num = 11; int ans = solve(num); System.out.println(ans); } public static int solve(int num) { // O(Log(N)) tine | O(1) space int currentNum = num, count = 0; while (currentNum != 0) { if ( (currentNum & 1) == 1) count++; currentNum = currentNum >> 1; } return count; } } ================================================ FILE: Bit Manipulation/parity_of_a_word.go ================================================ // Computing the parity of a word // The parity of a binary word is 1 if the number of 1s in the word is odd // The parity of a binary word is 0 if the number of 1s in the word is even // Input : 11 // Output : 1 // Input : 12 // Output : 0 package main import "fmt" // This algorithm iteratively tests the value of each bit while // tacking the number of 1's seen so far (Brute Force) func Parity(x int) int { result := 0 for x > 0 { result ^= (x & 1) x >>= 1 } return result } func ParityImproved(x int) int { result := 0 for x > 0 { result ^= 1 // This can be used to improve the performance in the best and average cases. x &= (x - 1) // erase lowest set bit in a word in a single operation } return result } func main() { fmt.Println("*********Brute Force*********") msg := Parity(11) fmt.Println(msg) msg = Parity(12) fmt.Println(msg) fmt.Println("*********Improved*********") msg = ParityImproved(11) fmt.Println(msg) msg = ParityImproved(12) fmt.Println(msg) } // The time complexity is O(n) where n is word size ================================================ FILE: Bit Manipulation/power_of_2.cpp ================================================ /* Given an integer n, return true if it is a power of two. Otherwise, return false. An integer n is a power of two, if there exists an integer x such that n == 2x. Example 1: Input: n = 1 Output: true Explanation: 20 = 1 Example 2: Input: n = 16 Output: true Explanation: 24 = 16 Example 3: Input: n = 3 Output: false Constraints: -231 <= n <= 231 - 1 Follow up: Could you solve it without loops/recursion? */ class Solution { public: bool isPowerOfTwo(int n) { return n > 0 && !(n &(n - 1)); } }; ================================================ FILE: Bit Manipulation/reduce_to_zero.cpp ================================================ /* Given an integer num, return the number of steps to reduce it to zero. In one step, if the current number is even, you have to divide it by 2, otherwise, you have to subtract 1 from it. Example 1: Input: num = 14 Output: 6 Explanation: Step 1) 14 is even; divide by 2 and obtain 7. Step 2) 7 is odd; subtract 1 and obtain 6. Step 3) 6 is even; divide by 2 and obtain 3. Step 4) 3 is odd; subtract 1 and obtain 2. Step 5) 2 is even; divide by 2 and obtain 1. Step 6) 1 is odd; subtract 1 and obtain 0. Example 2: Input: num = 8 Output: 4 Explanation: Step 1) 8 is even; divide by 2 and obtain 4. Step 2) 4 is even; divide by 2 and obtain 2. Step 3) 2 is even; divide by 2 and obtain 1. Step 4) 1 is odd; subtract 1 and obtain 0. Example 3: Input: num = 123 Output: 12 Constraints: 0 <= num <= 106 */ #include class Solution { public: int numberOfSteps (int num) { int count = 0; while(num){ if(!(num & 1)){ num = num >> 1; } else{ num--; } count++; } return count; } }; ================================================ FILE: Bit Manipulation/setbits.cpp ================================================ // Program to count the number of bits that are set to 1 in an integer // The following program tests bits one at a time starting with the least-significant bit. // Since we perform O(1) computation per bit, the time complexity is O(n) where n is number of bits in the integer // Best case time complexity is O(1), if the input io 0 #include using namespace std; int find_set_bits(int n){ int set_bits = 0; while(n){ set_bits += (n & 1); n >>= 1; } return set_bits; } int main(){ cout << find_set_bits(4) << endl; return 0; } ================================================ FILE: Bit Manipulation/single_number.java ================================================ /** * Given an array of integers A, every element appears twice except for one. Find that integer that occurs once. * * NOTE: Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory? * * * * Problem Constraints * 1 <= |A| <= 2000000 * * 0 <= A[i] <= INTMAX * * * * Input Format * The first and only argument of input contains an integer array A. * * * * Output Format * Return a single integer denoting the single element. * * * * Example Input * Input 1: * * A = [1, 2, 2, 3, 1] * Input 2: * * A = [1, 2, 2] * * * Example Output * Output 1: * * 3 * Output 2: * * 1 * * * Example Explanation * Explanation 1: * * 3 occurs once. * Explanation 2: * * 1 occurs once. */ package BitManipulation; import com.google.common.base.Stopwatch; public class SingleNumber { public static void main(String[] args) { Stopwatch timer = Stopwatch.createStarted(); int[] arr = {1, 2, 2, 3, 1}; int ans = solve(arr); System.out.println(ans); System.out.println("Runtime " + timer); } public static int solve(int[] array) { // O(N) time | O(1) space int ans = 0; for (int num : array) ans ^= num; return ans; } } ================================================ FILE: Bit Manipulation/subarrays_with_bitwise_OR_1.java ================================================ /** * Problem Description * Given an array B of length A with elements 1 or 0. Find the number of subarrays such that the bitwise OR of all the elements present in the subarray is 1. * * * Problem Constraints * 1 <= A <= 105 * * * Input Format * The first argument is a single integer A. * The second argument is an integer array B. * * * Output Format * Return the number of subarrays with bitwise array 1. * * * Example Input * Input 1: * A = 3 * B = [1, 0, 1] * Input 2: * A = 2 * B = [1, 0] * * * Example Output * Output 1: * 5 * Output2: * 2 * * * Example Explanation * Explanation 1: * The subarrays are :- [1], [0], [1], [1, 0], [0, 1], [1, 0, 1] * Except the subarray [0] all the other subarrays has a Bitwise OR = 1 * Explanation 2: * The subarrays are :- [1], [0], [1, 0] * Except the subarray [0] all the other subarrays has a Bitwise OR = 1 */ package BitManipulation; public class subArrayWithBitwiseOR1 { public static void main(String[] args) { int[] array = {1, 0, 1}; int n = 3; int ans = solve(array, n); System.out.println(ans); } public static int solve(int[] array, int len) { // O(N) time | O(1) space int possibleSubarraysWithThisIdx = 0; int ans = 0; for (int i = 0; i < len; i++) { int currentNum = array[i]; if (currentNum == 1) possibleSubarraysWithThisIdx = i + 1; ans += possibleSubarraysWithThisIdx; } return ans; } } ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Conflict resolution When multiple contributors disagree on the direction for a particular patch or the general direction of the project, the conflict should be resolved by communication. The people who disagree should get together, try to understand each other's points of view, and work to find a design that addresses everyone's concerns. This is usually sufficient to resolve issues. If you cannot come to an agreement, ask for the advice of a more senior member of the project. Be wary of agreement by attrition, where one person argues a point repeatedly until other participants give up in the interests of moving on. This is not conflict resolution, as it does not address everyone's concerns. Be wary of agreement by compromise, where two good competing solutions are merged into one mediocre solution. A conflict is addressed when the participants agree that the final solution is better than all the conflicting proposals. Sometimes the solution is more work than either of the proposals. Embrace the yak shave. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: - Demonstrating empathy and kindness toward other people - Being respectful of differing opinions, viewpoints, and experiences - Giving and gracefully accepting constructive feedback - Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience - Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: - The use of sexualized language or imagery, and sexual attention or advances of any kind - Trolling, insulting or derogatory comments, and personal or political attacks - Public or private harassment - Publishing others' private information, such as a physical or email address, without their explicit permission - Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at . All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. ================================================ FILE: CONTRIBUTING.md ================================================ ## Contributing ## Fork this repository Get involved! Fork this repository by clicking on the fork button on the top of this page. This will create a copy of this repository in your account. - [How to Fork a repo](https://docs.github.com/en/get-started/quickstart/fork-a-repo) Before you get started, we encourage you to read these documents which describe some of our community norms: Our [code of conduct](https://github.com/akgmage/data-structures-and-algorithms/blob/main/CODE_OF_CONDUCT.md), which stipulates explicitly that everyone must be gracious, respectful, and professional. This also documents our conflict resolution policy and encourages people to ask questions. Contributions are always welcome! - Pick any good first issue and add comment on it (Example: "I'll take this up"), or Add classic DSA problem which is currently not present in this repo - Read description on the issue (Mostly it will be the link to the question) - Add question on top of file - Add sample input and output - Explain approach with comments - Add Time and Space complexity - Take care of Readability (Code is written once and read multiple times, so keep this in mind) - Provide link for further reading (optional) - Send a Pull Request (PR) against main branch and mention issue number in the request format (#ISSUENUMBER) # Example program ```go /* Write a function that takes in two non-empty arrays of integers, finds the pair of numbers (one from each array) whose absolute difference is closest to zero, and returns an array containing these two numbers, with the number from the first array in the first position. Note that the absolute difference of two integers is the distance between them on the real number line. For example, the absolute difference of -5 and 5 is 10, and the absolute difference of -5 and -4 is 1. You can assume that there will only be one pair of numbers with the smallest difference. Sample Input Array1 = [-1, 5, 10, 20, 28, 3] Sample Input Array2 = [26, 134, 135, 15, 17] Sample Output = [28, 26] Explanation : This code implements the Smallest Difference problem which takes two arrays of integers as input and returns a pair of integers, one from each array, with the smallest absolute difference between them. The function first initializes two variables current and smallest to the maximum integer value. It then sorts both input arrays in ascending order using the sort.Ints function from the sort package. The function then iterates through both arrays using two pointers, idx1 and idx2, initialized to 0. Inside the loop, it compares the elements at the current indices of the two arrays, first and second, and calculates the absolute difference between them in the current variable. If current is smaller than the smallest variable, it updates smallest to current and assigns the current pair of integers to the result variable. The function returns the result variable, which contains the pair of integers with the smallest absolute difference. If there are identical integers in the two input arrays, the function will return them immediately, without any further comparisons. O(nlog(n) + mlog(m)) time | O(1) space - where n is the length of the first input array and m is the length of the second input array */ package main import ( "math" "sort" ) // SmallestDifference takes two integer slices as input and returns a slice with two integers. // The two integers in the returned slice have the smallest absolute difference among all pairs // of integers from the two input slices. func SmallestDifference(array1, array2 []int) []int { // Initialize variables for the smallest difference and the current difference being calculated current, smallest := math.MaxInt32, math.MaxInt32 // Sort the input slices sort.Ints(array1) sort.Ints(array2) // Initialize variables for the indices for the two slices idx1, idx2 := 0, 0 // Initialize an empty slice for the result result := []int{} // Loop through the two slices until we reach the end of one of the slices for idx1 < len(array1) && idx2 < len(array2) { // Get the values at the current indices for the two slices first, second := array1[idx1], array2[idx2] // Calculate the current difference between the two values if first < second { current = second - first idx1++ } else if second < first { current = first - second idx2++ } else { // If the two values are equal, we can return the pair return []int{first, second} } // Update the smallest difference and result slice if the current difference is smaller if smallest > current { smallest = current result = []int{first, second} } } // Return the pair with the smallest absolute difference return result } ``` ================================================ FILE: Dynamic Programming/best_time_to_buy_and_sell_stock.cpp ================================================ // Best Time to buy and sell stock /* Explanation: We start by initializing the minimum price to the maximum integer value and the maximum profit to 0. We loop through the prices array, and for each price: If the price is less than the current minimum price, we update the minimum price. Otherwise, if the difference between the price and the minimum price is greater than the current maximum profit, we update the maximum profit. Finally, we return the maximum profit Sample Input [7, 1, 5, 3, 6, 4] Output: 5 buy at 1 sell at 6 Time Complexity: O(n), where n is the length of the prices array. Space Complexity: O(1), as we are only using two variables to keep track of the minimum price and maximum profit */ #include #include #include using namespace std; int maxProfit(vector& prices) { int minPrice = INT_MAX; // initialize to maximum value to start with int maxProfit = 0;// initialize to 0 for (int price : prices) { minPrice = min(minPrice, price); // update minimum price seen so far maxProfit = max(maxProfit, price - minPrice); // update maximum profit seen so far } return maxProfit; } int main() { // example usage vector prices {7, 1, 5, 3, 6, 4}; int max_profit = maxProfit(prices); cout << "Max profit: " << max_profit << endl; return 0; } ================================================ FILE: Dynamic Programming/best_time_to_buy_and_sell_stock.go ================================================ // Best Time to buy and sell stock /* Explanation: We start by initializing the minimum price to the maximum integer value and the maximum profit to 0. We loop through the prices array, and for each price: If the price is less than the current minimum price, we update the minimum price. Otherwise, if the difference between the price and the minimum price is greater than the current maximum profit, we update the maximum profit. Finally, we return the maximum profit Sample Input [7, 1, 5, 3, 6, 4] Output: 5 buy at 1 sell at 6 Time Complexity: O(n), where n is the length of the prices array. Space Complexity: O(1), as we are only using two variables to keep track of the minimum price and maximum profit */ package main import ( "fmt" "math" ) func maxProfit(prices []int) int { minPrice := math.MaxInt32 // Initialize minimum price to maximum integer value maxProfit := 0 // Initialize maximum profit to 0 for _, price := range prices { if price < minPrice { minPrice = price // Update minimum price } else if price-minPrice > maxProfit { maxProfit = price - minPrice // Update maximum profit } } return maxProfit // Return maximum profit } func main() { prices := []int{7, 1, 5, 3, 6, 4} fmt.Println(maxProfit(prices)) // Output: 5 } ================================================ FILE: Dynamic Programming/best_time_to_buy_and_sell_stock.java ================================================ // Best Time to buy and sell stock /* Explanation: We start by initializing the minimum price to the maximum integer value and the maximum profit to 0. We loop through the prices array, and for each price: If the price is less than the current minimum price, we update the minimum price. Otherwise, if the difference between the price and the minimum price is greater than the current maximum profit, we update the maximum profit. Finally, we return the maximum profit Sample Input [7, 1, 5, 3, 6, 4] Output: 5 buy at 1 sell at 6 Time Complexity: O(n), where n is the length of the prices array. Space Complexity: O(1), as we are only using two variables to keep track of the minimum price and maximum profit */ public class Solution { public int maxProfit(int[] prices) { // Initialize variables to track the minimum price seen so far and the maximum profit int minPrice = Integer.MAX_VALUE; int maxProfit = 0; // Loop through the prices array for (int i = 0; i < prices.length; i++) { // If the current price is less than the minimum price seen so far, update the minimum price if (prices[i] < minPrice) { minPrice = prices[i]; } // If the difference between the current price and the minimum price is greater than the maximum profit seen so far, update the maximum profit else if (prices[i] - minPrice > maxProfit) { maxProfit = prices[i] - minPrice; } } return maxProfit; // Return the maximum profit } public static void main(String[] args) { int[] prices = {7, 1, 5, 3, 6, 4}; int maxProfit = maxProfit(prices); System.out.println("Max Profit: " + maxProfit); } } ================================================ FILE: Dynamic Programming/best_time_to_buy_and_sell_stock.js ================================================ // Best Time to buy and sell stock /* Explanation: We start by initializing the minimum price to the maximum integer value and the maximum profit to 0. We loop through the prices array, and for each price: If the price is less than the current minimum price, we update the minimum price. Otherwise, if the difference between the price and the minimum price is greater than the current maximum profit, we update the maximum profit. Finally, we return the maximum profit Sample Input [7, 1, 5, 3, 6, 4] Output: 5 buy at 1 sell at 6 Time Complexity: O(n), where n is the length of the prices array. Space Complexity: O(1), as we are only using two variables to keep track of the minimum price and maximum profit */ /** * @param {number[]} prices * @return {number} */ var maxProfit = function (prices) { let minPrice = Infinity; // keep track of minimum price seen so far let maxProfit = 0; // keep track of maximum profit seen so far for (let i = 0; i < prices.length; i++) { if (prices[i] < minPrice) { minPrice = prices[i]; // update minimum price seen so far } else if (prices[i] - minPrice > maxProfit) { maxProfit = prices[i] - minPrice; // update maximum profit seen so far } } return maxProfit; }; const prices = [7, 1, 5, 3, 6, 4]; console.log(maxProfit(prices)); // Output: 5 ================================================ FILE: Dynamic Programming/best_time_to_buy_and_sell_stock.py ================================================ # Best Time to buy and sell stock ''' The function maxProfit takes a list of integers prices and returns the maximum profit that can be made from buying and selling the stock represented by prices. The function works by initializing the minimum price to a very large number (sys.maxsize) and the maximum profit to 0. It then loops through the prices, updating the minimum price if the current price is less than the current minimum price, and updating the maximum profit if the difference between the current price and the minimum price is greater than the current maximum profit. Finally, it returns the maximum profit. Sample Input [7, 1, 5, 3, 6, 4] Output: 5 buy at 1 sell at 6 Time Complexity: O(n), where n is the length of the prices array. Space Complexity: O(1), as we are only using two variables to keep track of the minimum price and maximum profit ''' def maxProfit(prices: List[int]) -> int: # initialize the minimum price as maximum integer value min_price = sys.maxsize # initialize the maximum profit as 0 max_profit = 0 # loop through the prices for price in prices: # if the current price is less than the minimum price, update the minimum price if price < min_price: min_price = price # else if the difference between the current price and the minimum price is greater than the maximum profit, # update the maximum profit elif price - min_price > max_profit: max_profit = price - min_price # return the maximum profit return max_profit prices = [7, 1, 5, 3, 6, 4] profit = maxProfit(prices) print(profit) # Output: 5 ================================================ FILE: Dynamic Programming/climb_stairs.cpp ================================================ // A child is climbing a stair case. It takes n steps to reach to the top. Each time child can either climb 1 // or 2 steps. In how many distinct ways can the child climb to the top? #include using namespace std; // ClimbStairs: returns the number of ways in which a child can climb stairs // Approach: Number of ways to reach kth stair = Number of ways to reach k − 1th stair + Number of ways to reach k − 2th stair // ClimbStairs(k) = ClimbStairs(k-1) + ClimbStairs(k-2) int climbStairs(int n) { // Base case if (n < 3) { return n; } int cache[n]; // Initialize initial 2 values cache[0] = 1; cache[1] = 2; for (int i = 2; i < n; i++) { // Add previous 2 values cache[i] = cache[i - 1] + cache[i - 2]; } return cache[n - 1]; } // Variation: A child is climbing up a staircase with n steps and can hop either 1 step, 2 steps, or 3 steps at a time. // Implement a method to count how many possible ways the child can jump up the stairs. // Approach similar to the above problem int climbStairsVariation(int n) { // Base case if (n < 3) { return n; } int cache[n]; // Initialize initial 3 values cache[0] = 1; cache[1] = 2; cache[2] = 4; for (int i = 3; i < n; i++) { // Add previous 3 values cache[i] = cache[i - 1] + cache[i - 2] + cache[i - 3]; } return cache[n - 1]; } int main() { cout << climbStairs(5) << endl; cout << climbStairsVariation(5) << endl; return 0; } ================================================ FILE: Dynamic Programming/climb_stairs.go ================================================ package main // A child is climbing a stair case. It takes n steps to reach to the top. Each time child can either climb 1 // or 2 steps. In how many distinct ways can the child climb to the top? import "fmt" // ClimbStairs: returns the number of ways in which a child can climb stairs // Approach: Number of ways to reach kth stair = Number of ways to reach k − 1th stair + Number of ways to reach k − 2th stair // ClimbStairs(k) = ClimbStairs(k-1) + ClimbStairs(k-2) func ClimbStairs(n int) int { // base case if n < 3 { return n } cache := make([]int, n) // initialize initial 2 values cache[0], cache[1] = 1, 2 for i := 2; i < n; i++ { // add previous 2 values cache[i] = cache[i-1] + cache[i-2] } return cache[n-1] } // Variatiom: A child is climbing up a staircase with 􀝊 steps, and can hop either 1 step, 2 steps, or 3 steps at a time. // Implement a method to count how many possible ways the child can jump up the stairs. // Approach similar to above problem func ClimbStairs2(n int) int { // base case if n < 3 { return n } cache := make([]int, n) // initialize initial 3 values cache[0], cache[1], cache[2] = 1, 2, 4 for i := 3; i < n; i++ { // add previous 3 values cache[i] = cache[i-1] + cache[i-2] + cache[i - 3] } return cache[n-1] } func main() { fmt.Println(ClimbStairs(5)) fmt.Println(ClimbStairs2(5)) } ================================================ FILE: Dynamic Programming/climb_stairs.java ================================================ // A child is climbing a stair case. It takes n steps to reach to the top. Each time child can either climb 1 // or 2 steps. In how many distinct ways can the child climb to the top? public class StairClimbing { // ClimbStairs: returns the number of ways in which a child can climb stairs // Approach: Number of ways to reach kth stair = Number of ways to reach k − 1th stair + Number of ways to reach k − 2th stair // ClimbStairs(k) = ClimbStairs(k-1) + ClimbStairs(k-2) public static int climbStairs(int n) { // Base case if (n < 3) { return n; } int[] cache = new int[n]; // Initialize initial 2 values cache[0] = 1; cache[1] = 2; for (int i = 2; i < n; i++) { // Add previous 2 values cache[i] = cache[i - 1] + cache[i - 2]; } return cache[n - 1]; } // Variatiom: A child is climbing up a staircase with n steps and can hop either 1 step, 2 steps, or 3 steps at a time. // Implement a method to count how many possible ways the child can jump up the stairs. // Approach similar to the above problem public static int climbStairsVariation(int n) { // Base case if (n < 3) { return n; } int[] cache = new int[n]; // Initialize initial 3 values cache[0] = 1; cache[1] = 2; cache[2] = 4; for (int i = 3; i < n; i++) { // Add previous 3 values cache[i] = cache[i - 1] + cache[i - 2] + cache[i - 3]; } return cache[n - 1]; } public static void main(String[] args) { System.out.println(climbStairs(5)); System.out.println(climbStairsVariation(5)); } } ================================================ FILE: Dynamic Programming/climb_stairs.js ================================================ // A child is climbing a stair case. It takes n steps to reach to the top. Each time child can either climb 1 // or 2 steps. In how many distinct ways can the child climb to the top? // ClimbStairs: returns the number of ways in which a child can climb stairs // Approach: Number of ways to reach kth stair = Number of ways to reach k − 1th stair + Number of ways to reach k − 2th stair // ClimbStairs(k) = ClimbStairs(k-1) + ClimbStairs(k-2) function climbStairs(n) { // Base case if (n < 3) { return n; } const cache = new Array(n); // Initialize initial 2 values cache[0] = 1; cache[1] = 2; for (let i = 2; i < n; i++) { // Add previous 2 values cache[i] = cache[i - 1] + cache[i - 2]; } return cache[n - 1]; } // Variation: A child is climbing up a staircase with n steps and can hop either 1 step, 2 steps, or 3 steps at a time. // Implement a method to count how many possible ways the child can jump up the stairs. // Approach similar to the above problem function climbStairsVariation(n) { // Base case if (n < 3) { return n; } const cache = new Array(n); // Initialize initial 3 values cache[0] = 1; cache[1] = 2; cache[2] = 4; for (let i = 3; i < n; i++) { // Add previous 3 values cache[i] = cache[i - 1] + cache[i - 2] + cache[i - 3]; } return cache[n - 1]; } console.log(climbStairs(5)); console.log(climbStairsVariation(5)); ================================================ FILE: Dynamic Programming/climb_stairs.py ================================================ ''' A child is climbing a stair case. It takes n steps to reach to the top. Each time child can either climb 1 \ or 2 steps. In how many distinct ways can the child climb to the top? ''' # ClimbStairs: returns the number of ways in which a child can climb stairs # Approach: Number of ways to reach kth stair = Number of ways to reach k − 1th stair + Number of ways to reach k − 2th stair # ClimbStairs(k) = ClimbStairs(k-1) + ClimbStairs(k-2) def climb_stairs(n): # Base case if n < 3: return n cache = [0] * n # Initialize initial 2 values cache[0], cache[1] = 1, 2 for i in range(2, n): # Add previous 2 values cache[i] = cache[i - 1] + cache[i - 2] return cache[n - 1] # Variation: A child is climbing up a staircase with n steps and can hop either 1 step, 2 steps, or 3 steps at a time. # Implement a method to count how many possible ways the child can jump up the stairs. # Approach similar to the above problem def climb_stairs_variation(n): # Base case if n < 3: return n cache = [0] * n # Initialize initial 3 values cache[0], cache[1], cache[2] = 1, 2, 4 for i in range(3, n): # Add previous 3 values cache[i] = cache[i - 1] + cache[i - 2] + cache[i - 3] return cache[n - 1] if __name__ == "__main__": print(climb_stairs(5)) print(climb_stairs_variation(5)) ================================================ FILE: Dynamic Programming/coin_change.cpp ================================================ /* Coin Change Problem You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount of money.Return the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.You may assume that you have an infinite number of each kind of coin. The code uses a dynamic programming approach to solve the coin change problem. The dp vector is used to store the minimum number of coins needed to make each amount from 0 to amount. The minimum number of coins needed to make an amount of 0 is 0. Then, for each coin, the code iterates through each amount from the coin value to the target amount. If the current amount minus the coin value is a valid amount, the code updates the minimum number of coins needed to make that amount by taking the minimum of the current value and the value of dp[i - coin] + 1. Finally, if the minimum number of coins needed to make the target amount is still INT_MAX, it is not possible to make the amount and the code returns -1. Otherwise, the code returns the minimum number of coins needed to make the target amount. Sample Input : [1, 2, 5] target : 11 Output 3 (5, 5, 1) The time complexity is O(n * V), where n is the number of coins and V is the value we want to make change for. The space complexity is also O(n * V) as we need to store the minimum number of coins required to make change for every value up to V for every coin. In the worst case, when we have a large number of coins and a large value V, the time and space complexity can become quite large. However, this approach can efficiently handle a wide range of input values and is guaranteed to give the optimal solution. */ #include #include #include using namespace std; int coinChange(vector& coins, int amount) { // Create a vector to store the minimum number of coins needed to make each amount from 0 to amount vector dp(amount + 1, INT_MAX); // The minimum number of coins needed to make an amount of 0 is 0 dp[0] = 0; // Iterate through each coin for (int coin : coins) { // Iterate through each amount from the coin value to the target amount for (int i = coin; i <= amount; i++) { // If the current amount minus the coin value is a valid amount, update the minimum number of coins needed if (dp[i - coin] != INT_MAX) { dp[i] = min(dp[i], dp[i - coin] + 1); } } } // If the minimum number of coins needed to make the target amount is still INT_MAX, it is not possible to make the amount if (dp[amount] == INT_MAX) { return -1; } // Return the minimum number of coins needed to make the target amount return dp[amount]; } int main() { vector coins = {1, 2, 5}; int amount = 11; cout << "Minimum number of coins needed: " << coinChange(coins, amount) << endl; return 0; } ================================================ FILE: Dynamic Programming/coin_change.go ================================================ /* Coin Change Problem You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount of money.Return the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.You may assume that you have an infinite number of each kind of coin. This implementation uses a bottom-up approach to fill in a 2D table of minimum coin counts for each amount up to the target amount. The table is initialized with the base cases (0 coins for an amount of 0, infinity for an amount greater than 0) and then filled in using the recurrence relation: dp[i][j] = min(dp[i-1][j], dp[i][j-coins[i-1]]+1) where dp[i][j] is the minimum number of coins needed to make an amount of j using the first i coins. If the current coin value coins[i-1] is greater than the current amount j, then we can't use that coin, so we take the minimum number of coins we need to make the amount using only the first i-1 coins (dp[i-1][j]). Otherwise, we can use the current coin, so we take the minimum of the number of coins we need to make the amount using only the first i-1 coins (dp[i-1][j]) and the number of coins we need to make the amount minus the value of the current coin, plus one (dp[i][j-coins[i-1]]+1). The final result is dp[len(coins)][amount], which gives us the minimum number of coins needed to make the target amount. If this value is infinity, then it's not possible to make the amount using the given coins, so we return -1. Sample Input : [1, 2, 5] target : 11 Output 3 (5, 5, 1) The time complexity of this implementation is O(nm), where n is the number of coins and m is the target amount. The space complexity is also O(nm) because we're storing a 2D table of size (n+1) x (m+1). */ package main import ( "fmt" "math" ) func coinChange(coins []int, amount int) int { // create a 2D slice to store the minimum number of coins needed for each subproblem dp := make([][]int, len(coins)+1) for i := range dp { dp[i] = make([]int, amount+1) } // initialize the first row to infinity and the first column to 0 for j := 1; j <= amount; j++ { dp[0][j] = math.MaxInt32 } for i := 0; i <= len(coins); i++ { dp[i][0] = 0 } // fill in the rest of the table for i := 1; i <= len(coins); i++ { for j := 1; j <= amount; j++ { if j < coins[i-1] { dp[i][j] = dp[i-1][j] } else { dp[i][j] = min(dp[i-1][j], dp[i][j-coins[i-1]]+1) } } } // return the result if dp[len(coins)][amount] == math.MaxInt32 { return -1 } return dp[len(coins)][amount] } func min(a, b int) int { if a < b { return a } return b } func main() { coins := []int{1, 2, 5} amount := 11 fmt.Println(coinChange(coins, amount)) } ================================================ FILE: Dynamic Programming/coin_change.java ================================================ /* Coin Change Problem You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount of money.Return the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.You may assume that you have an infinite number of each kind of coin. Example 1: Input: coins = [1,2,5], amount = 11 Output: 3 Explanation: 11 = 5 + 5 + 1 Example 2: Input: coins = [2], amount = 3 Output: -1 Example 3: Input: coins = [1], amount = 0 Output: 0 Constraints: 1 <= coins.length <= 12 1 <= coins[i] <= 231 - 1 0 <= amount <= 104 */ //SOLUTION //EXPLANATION OF CODE /*Using unbounded knapsack in Dynamic programming * The minimal number of coins required to make up a specific sum using a certain number of coins is stored in a 2D array. * The array's dimensions are n+1 and amount+1, where n is the length of the coins array. * Initialisation 1 * t[i][j] is set to Integer when i = 0.MAX_VALUE-1 indicates that the amount j cannot be calculated using 0 coins. For j = 0, * t[i][j] is set to 0, suggesting that the amount 0 can be made up of any number of coins. * Initialisation 2 * If the amount j is a multiple of the first coin denomination, then the minimum number of coins required to make up the amount j is j/coins[0]. * Otherwise, it is not possible to make up the amount j using only the first coin denomination, so the value of t[1][j] is set to * Integer.MAX_VALUE-1. * * main code * The third loop fills in the t array for all other cases. If the current coin denomination coins[i-1] is less than or equal to the current amount * j, then we can either include or exclude the current coin denomination to make up the amount j. If we include the current coin denomination, * then the minimum number of coins required is 1 + t[i][j-coins[i-1]]. If we exclude the current coin denomination, then the minimum number of * coins required is t[i-1][j]. We take the minimum of these two values to get the minimum number of coins required to make up the amount j using * the first i coin denominations. Finally, if the value of t[n][amount] is still Integer.MAX_VALUE-1, then it is not possible to make up the amount amount using the coin denominations in coins, so we return -1. Otherwise, we return the value of t[n][amount], which represents the minimum number of coins required to make up the amount amount using all the coin denominations in coins. */ //Code: class Solution { public int coinChange(int[] coins, int amount) { int n=coins.length; int t[][]= new int[n+1][amount+1]; for(int i=0; i -1 { // If the result is already stored, return it to avoid redundant calculations. return storedResults[numDice][target] } // Recursive calculation: Find the number of ways to reach the target sum using numDice dice. numWaysToReachTarget := 0 for currentTarget := max(0, target-numSides); currentTarget < target; currentTarget++ { // Recursively calculate the number of ways to reach the currentTarget using numDice-1 dice. numWaysToReachTarget += diceThrowsHelper(numDice-1, numSides, currentTarget, storedResults) } // Store the result in storedResults for future use. storedResults[numDice][target] = numWaysToReachTarget // Return the total number of ways to reach the target sum. return numWaysToReachTarget } // max is a helper function to find the maximum of two integers. func max(a, b int) int { if a > b { return a } return b } ================================================ FILE: Dynamic Programming/disk_stacking.go ================================================ /* You're given a non-empty array of arrays where each subarray holds three integers and represents a disk. These integers denote each disk's width, depth, and height, respectively. Your goal is to stack up the disks and to maximize the total height of the stack. A disk must have a strictly smaller width, depth, and height than any other disk below it. Write a function that returns an array of the disks in the final stack, starting with the top disk and ending with the bottom disk. Note that you can't rotate disks; in other words, the integers in each subarray must represent [width, depth, height] at all times Sample input : = [[2, 1, 2], [3, 2, 3], [2, 2, 8], [2, 3, 4], [1, 3, 1], [4, 4, 5]] Output: [[2, 1, 2], [3, 2, 3], [4, 4, 5]] // 10 (2 + 3 + 5) is the tallest height we can get by Explanation: This code snippet implements the "Disk Stacking" problem, which is a classic dynamic programming problem. The goal is to find the maximum height that can be achieved by stacking disks on top of each other while adhering to certain conditions. The problem is defined as follows: Given a list of disks represented by their dimensions (width, depth, height), you need to find a stack of disks with the maximum height. You can only stack a disk on top of another if its width, depth, and height are all strictly smaller than those of the disk below it. Now, let's go through the code step by step: 1. `Disk` and `Disks` types: These are custom types defined to simplify the code and make it more readable. `Disk` represents a single disk's dimensions, and `Disks` is a slice of `Disk`, representing a collection of disks. 2. Implementing sort interface for `Disks`: The `Disks` type is provided with three methods - `Len`, `Swap`, and `Less`. These methods are part of the `sort.Interface`, which allows us to sort the disks based on their height (the third dimension). 3. `DiskStacking` function: This is the main function that solves the disk stacking problem. It takes a 2D slice `input`, representing the dimensions of the disks, and returns a 2D slice representing the sequence of disks to stack to achieve the maximum height. 4. Creating `disks` slice and sorting: The function first converts the input to a `Disks` slice and then sorts it based on the third dimension (height) in increasing order. Sorting will allow us to consider disks in a specific order while building the stack. 5. Initializing `heights` and `sequences` slices: The function initializes two slices, `heights` and `sequences`, both with the same length as the number of disks. `heights` will keep track of the maximum height achievable with the current disk at each position, and `sequences` will store the index of the previous disk that contributes to the current disk's maximum height. 6. Dynamic programming loop: The function iterates over each disk and calculates the maximum height that can be achieved by considering the current disk and all the disks before it. It checks if the conditions (`areValidDimensions`) for placing the current disk on top of another are met, and if so, it updates the maximum height and the contributing disk index in `heights` and `sequences`. 7. Finding the maximum height and the sequence: After the dynamic programming loop, it finds the index with the maximum height in the `heights` slice. This index represents the topmost disk in the stack, which contributes to the maximum height. 8. Building the sequence: The function calls the `buildSequence` function to create the sequence of disks contributing to the maximum height. It starts from the topmost disk (found in the previous step) and follows the sequence of disks using the `sequences` slice. 9. Reversing the sequence: The sequence is built in reverse order, starting from the topmost disk. Since the problem requires the disks to be stacked from bottom to top, the `reverse` function is used to reverse the order of elements in the sequence. 10. Returning the result: Finally, the function returns the sequence of disks that should be stacked to achieve the maximum height. O(n^2) time | O(n) space - where n is the number of disks */ package main import "sort" // Custom type for representing a single disk's dimensions (width, depth, height) type Disk []int // Custom type for representing a collection of disks type Disks []Disk // Implementing sort.Interface for Disks to allow sorting based on height (third dimension) func (disks Disks) Len() int { return len(disks) } func (disks Disks) Swap(i, j int) { disks[i], disks[j] = disks[j], disks[i] } func (disks Disks) Less(i, j int) bool { return disks[i][2] < disks[j][2] } // Main function to solve the Disk Stacking problem func DiskStacking(input [][]int) [][]int { // Convert input to Disks slice and sort it based on height in increasing order disks := make(Disks, len(input)) for i, disk := range input { disks[i] = disk } sort.Sort(disks) // Initialize slices to store the maximum height and the sequence of disks heights := make([]int, len(disks)) sequences := make([]int, len(disks)) for i := range disks { heights[i] = disks[i][2] sequences[i] = -1 } // Dynamic programming loop to find the maximum height and contributing disks for i := 1; i < len(disks); i++ { currentDisk := disks[i] for j := 0; j < i; j++ { other := disks[j] // Check if conditions are met to stack currentDisk on top of other if areValidDimensions(other, currentDisk) { // Update maximum height and contributing disk index if needed if heights[i] <= currentDisk[2]+heights[j] { heights[i] = currentDisk[2] + heights[j] sequences[i] = j } } } } // Find the index with the maximum height (topmost disk in the stack) maxIndex := 0 for i, height := range heights { if height > heights[maxIndex] { maxIndex = i } } // Build the sequence of disks contributing to the maximum height sequence := buildSequence(disks, sequences, maxIndex) // Return the sequence of disks that should be stacked to achieve the maximum height return sequence } // Function to check if the conditions are met for placing current disk on top of other func areValidDimensions(o Disk, c Disk) bool { return o[0] < c[0] && o[1] < c[1] && o[2] < c[2] } // Function to build the sequence of disks contributing to the maximum height func buildSequence(disks []Disk, sequences []int, index int) [][]int { sequence := [][]int{} for index != -1 { sequence = append(sequence, disks[index]) index = sequences[index] } // Since the sequence is built in reverse order (top to bottom), reverse it to get correct order reverse(sequence) return sequence } // Function to reverse the order of elements in a 2D slice func reverse(numbers [][]int) { for i, j := 0, len(numbers)-1; i < j; i, j = i+1, j-1 { numbers[i], numbers[j] = numbers[j], numbers[i] } } ================================================ FILE: Dynamic Programming/disk_stacking.py ================================================ ''' You're given a non-empty array of arrays where each subarray holds three integers and represents a disk. These integers denote each disk's width, depth, and height, respectively. Your goal is to stack up the disks and to maximize the total height of the stack. A disk must have a strictly smaller width, depth, and height than any other disk below it. Write a function that returns an array of the disks in the final stack, starting with the top disk and ending with the bottom disk. Note that you can't rotate disks; in other words, the integers in each subarray must represent [width, depth, height] at all times Sample input : = [[2, 1, 2], [3, 2, 3], [2, 2, 8], [2, 3, 4], [1, 3, 1], [4, 4, 5]] Output: [[2, 1, 2], [3, 2, 3], [4, 4, 5]] // 10 (2 + 3 + 5) is the tallest height we can get by Explanation: This code snippet implements the "Disk Stacking" problem, which is a classic dynamic programming problem. The goal is to find the maximum height that can be achieved by stacking disks on top of each other while adhering to certain conditions. The problem is defined as follows: Given a list of disks represented by their dimensions (width, depth, height), you need to find a stack of disks with the maximum height. You can only stack a disk on top of another if its width, depth, and height are all strictly smaller than those of the disk below it. Now, let's go through the code step by step: 1. `Disk` and `Disks` types: These are custom types defined to simplify the code and make it more readable. `Disk` represents a single disk's dimensions, and `Disks` is a slice of `Disk`, representing a collection of disks. 2. Implementing sort interface for `Disks`: The `Disks` type is provided with three methods - `Len`, `Swap`, and `Less`. These methods are part of the `sort.Interface`, which allows us to sort the disks based on their height (the third dimension). 3. `DiskStacking` function: This is the main function that solves the disk stacking problem. It takes a 2D slice `input`, representing the dimensions of the disks, and returns a 2D slice representing the sequence of disks to stack to achieve the maximum height. 4. Creating `disks` slice and sorting: The function first converts the input to a `Disks` slice and then sorts it based on the third dimension (height) in increasing order. Sorting will allow us to consider disks in a specific order while building the stack. 5. Initializing `heights` and `sequences` slices: The function initializes two slices, `heights` and `sequences`, both with the same length as the number of disks. `heights` will keep track of the maximum height achievable with the current disk at each position, and `sequences` will store the index of the previous disk that contributes to the current disk's maximum height. 6. Dynamic programming loop: The function iterates over each disk and calculates the maximum height that can be achieved by considering the current disk and all the disks before it. It checks if the conditions (`areValidDimensions`) for placing the current disk on top of another are met, and if so, it updates the maximum height and the contributing disk index in `heights` and `sequences`. 7. Finding the maximum height and the sequence: After the dynamic programming loop, it finds the index with the maximum height in the `heights` slice. This index represents the topmost disk in the stack, which contributes to the maximum height. 8. Building the sequence: The function calls the `buildSequence` function to create the sequence of disks contributing to the maximum height. It starts from the topmost disk (found in the previous step) and follows the sequence of disks using the `sequences` slice. 9. Reversing the sequence: The sequence is built in reverse order, starting from the topmost disk. Since the problem requires the disks to be stacked from bottom to top, the `reverse` function is used to reverse the order of elements in the sequence. 10. Returning the result: Finally, the function returns the sequence of disks that should be stacked to achieve the maximum height. O(n^2) time | O(n) space - where n is the number of disks ''' def DiskStacking(disks): # Sort the disks based on their height disks.sort(key=lambda x: x[2]) # Initialize arrays to store heights and sequences heights = [disk[2] for disk in disks] # Heights of each disk sequences = [-1 for _ in disks] # Index sequence for each disk # Loop through each disk and calculate the maximum height for i in range(1, len(disks)): current_disk = disks[i] for j in range(i): other_disk = disks[j] # Check if the dimensions of the other_disk are valid for stacking if are_valid_dimensions(other_disk, current_disk): # Update the height and sequence if the condition is met if heights[i] <= current_disk[2] + heights[j]: heights[i] = current_disk[2] + heights[j] sequences[i] = j # Find the index of the maximum height max_index = heights.index(max(heights)) # Build and return the sequence of disks for the maximum height sequence = build_sequence(disks, sequences, max_index) return sequence def are_valid_dimensions(other_disk, current_disk): # Check if the dimensions of other_disk allow current_disk to be stacked on top return other_disk[0] < current_disk[0] and other_disk[1] < current_disk[1] and other_disk[2] < current_disk[2] def build_sequence(disks, sequences, index): sequence = [] # Build the sequence of disks for the maximum height while index != -1: sequence.append(disks[index]) index = sequences[index] sequence.reverse() # Reverse the sequence to maintain the correct order return sequence ================================================ FILE: Dynamic Programming/distance_of_nearest_0.cpp ================================================ /*Given an m x n binary matrix mat, return the distance of the nearest 0 for each cell. The distance between two adjacent cells is 1. Example 1: Input: mat = [[0,0,0],[0,1,0],[0,0,0]] Output: [[0,0,0],[0,1,0],[0,0,0]] Example 2: Input: mat = [[0,0,0],[0,1,0],[1,1,1]] Output: [[0,0,0],[0,1,0],[1,2,1]] Constraints: m == mat.length n == mat[i].length 1 <= m, n <= 10^4 1 <= m * n <= 10^4 mat[i][j] is either 0 or 1. There is at least one 0 in mat. explanation: stepwise explanation of the code: 1. We start by initializing the dimensions of the input matrix mat as m (number of rows) and n (number of columns). This will be used later in the code. 2. We create a result matrix called result of size m x n to store the distance of the nearest 0 for each cell. Initially, we set all the values in result to a large value (in this case, INT_MAX) to represent an unreachable distance. 3. We create a queue called q to store the cell indices that need to be processed during the breadth-first search. 4. We iterate through the input matrix mat and enqueue the indices of all the cells with value 0 into the queue. Additionally, we mark the distance of these cells in the result matrix as 0. This initialization step ensures that the cells with value 0 are considered as starting points for the breadth-first search. 5. Now, we perform the breadth-first search using the queue q. While the queue is not empty, we process the cells in a breadth-first manner. 6. For each cell (row, col) popped from the queue, we check its neighboring cells in four directions: up, down, left, and right. We define the directions as a vector of pairs called directions, where each pair represents the change in row and column indices to move in a specific direction. 7. If the neighboring cell indices (newRow, newCol) are within the valid range of the matrix and the current distance in the result matrix for (newRow, newCol) is greater than the distance of the current cell plus 1, we update the distance in the result matrix and enqueue the neighboring cell (newRow, newCol) into the queue for further processing. 8. Once the breadth-first search is completed, the result matrix will contain the distance of the nearest 0 for each cell in the input matrix. 9. Finally, we return the result matrix. */ #include #include #include using namespace std; class Solution { public: vector> updateMatrix(vector> &mat) { int m = mat.size(); int n = mat[0].size(); // Create a result matrix and initialize with large values vector> result(m, vector(n, INT_MAX)); // Create a queue to store cell indices queue> q; // Initialize the queue with 0 cells and mark their distance as 0 for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (mat[i][j] == 0) { result[i][j] = 0; q.push({i, j}); } } } // Perform breadth-first search vector> directions{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; while (!q.empty()) { int row = q.front().first; int col = q.front().second; q.pop(); for (const auto &dir : directions) { int newRow = row + dir.first; int newCol = col + dir.second; if (newRow >= 0 && newRow < m && newCol >= 0 && newCol < n) { if (result[newRow][newCol] > result[row][col] + 1) { result[newRow][newCol] = result[row][col] + 1; q.push({newRow, newCol}); } } } } return result; } }; int main() { // Example usage vector> mat = {{0, 0, 0}, {0, 1, 0}, {0, 0, 0}}; Solution s; vector> result = s.updateMatrix(mat); // Print the result for (const auto &row : result) { for (int val : row) { cout << val << " "; } cout << endl; } return 0; } ================================================ FILE: Dynamic Programming/distance_of_nearest_0.py ================================================ ''' Given an m x n binary matrix mat, return the distance of the nearest 0 for each cell. The distance between two adjacent cells is 1. Example 1: Input: mat = [[0,0,0],[0,1,0],[0,0,0]] Output: [[0,0,0],[0,1,0],[0,0,0]] Example 2: Input: mat = [[0,0,0],[0,1,0],[1,1,1]] Output: [[0,0,0],[0,1,0],[1,2,1]] Constraints: m == mat.length n == mat[i].length 1 <= m, n <= 104 1 <= m * n <= 104 mat[i][j] is either 0 or 1. There is at least one 0 in mat. Intuition The goal is to get the nearest 0 for each 1. So how can we get the nearest 0 for each 1? Lets use Multisource BFS and Dynamic Programming. Approach To get the nearest 0 for each 1, we should treat every 0 as if they are on the same level, make a BFS on them and keep track of the distance as we go further. Since we are finding the nearest zero, we may need to use pre-calculated distances for the current 1 when all of its neighbours are all 1. Lets see this with example! Here is the given grid. 0 0 0 0 1 0 1 1 1 Then by taking all zeros and making a BFS on them, the nearest distance becomes 1 for all ones other than the back ticked 1 found on the third row. 0 0 0 0 1 0 1 `1` 1 The back ticked '1' is not adjacent to 0, so it should use the precalculated distance of the nearest 1 it get. So, in here we are using precalculated values to get the nearest distance. This technique is called Dynamic programming. Then we will get this grid by using the values we calculated for its adjacent 1 0 0 0 0 1 0 1 2 1 Complexity Time complexity: O(N) Space complexity: O(V + E) Code sample input and output: 1. Input: mat = [[0,0,0],[0,1,0],[0,0,0]] Output: [[0,0,0],[0,1,0],[0,0,0]] ''' class Solution: def updateMatrix(self, mat: List[List[int]]) -> List[List[int]]: # it a Multi-source BFS like 994. Rotting Oranges # treat every 0 as if they are on the same level and make BFS on them def isvalid(row, col): if row >= len(mat) or row < 0 or col >= len(mat[0]) or col < 0: return False return True # get every zero and add them to a queue q, visited = deque(), set() for row in range( len(mat)): for col in range( len(mat[0])): if mat[row][col] == 0: q.append([row, col]) visited.add((row, col)) level = 1 # the distance to the nearest zero starts from 1 while q: size = len(q) for _ in range( size ): row, col =q.popleft() for r, c in [ [1, 0], [-1, 0], [0, 1], [0, -1]]: newRow, newCol = row + r, col + c if (newRow, newCol) not in visited and isvalid(newRow, newCol): mat[newRow][newCol] = level q.append([newRow, newCol]) visited.add( (newRow, newCol)) level += 1 return mat ================================================ FILE: Dynamic Programming/distane_of_nearest_0.go ================================================ // Given an m x n binary matrix mat, return the distance of the nearest 0 for each cell in Java // Solution // // This solution uses a breadth-first search (BFS) approach to calculate the distance of the nearest 0 for each cell in the matrix. // // The idea is to initialize a distances matrix with all values set to the maximum integer value, except for the cells that contain 0s, // // which are set to 0 and added to a queue. We then perform a BFS on the queue, updating the distances of neighboring cells as we go. // // Finally, we return the updated distances matrix. // Time Complexity: // We traverse the entire matrix in the worst case to fill the distances matrix with initial values, which takes O(m * n) time. // We use Breadth-First Search (BFS) to update the distances matrix, which in the worst case can visit each cell once, taking O(m * n) time. // Therefore, the total time complexity of this solution is O(m * n). // Space Complexity: // We store the distances matrix, which requires O(m * n) space. // We use a queue to implement the BFS algorithm, which can store at most m * n cells in the worst case, taking O(m * n) space. // Therefore, the total space complexity of this solution is O(m * n). package main import ( "container/list" "math" ) var directions = [][2]int{{-1, 0}, {0, 1}, {1, 0}, {0, -1}} func updateMatrix(mat [][]int) [][]int { m := len(mat) n := len(mat[0]) distances := make([][]int, m) // Initialize a distances matrix // Initialize distances to math.MaxInt32 except for cells with 0 for i := range distances { distances[i] = make([]int, n) for j := range distances[i] { distances[i][j] = math.MaxInt32 if mat[i][j] == 0 { distances[i][j] = 0 } } } queue := list.New() // Initialize a queue for BFS // Perform BFS for i := 0; i < m; i++ { for j := 0; j < n; j++ { if mat[i][j] == 0 { queue.PushBack([2]int{i, j}) // Add the cell to the queue } } } for queue.Len() > 0 { element := queue.Front() queue.Remove(element) cell := element.Value.([2]int) i, j := cell[0], cell[1] for _, direction := range directions { x, y := i+direction[0], j+direction[1] // Check if cell is out of bounds if x < 0 || x >= m || y < 0 || y >= n { continue } // Check if distance is already smaller if distances[x][y] <= distances[i][j]+1 { continue } distances[x][y] = distances[i][j] + 1 // Update the distance queue.PushBack([2]int{x, y}) // Add the cell to the queue } } return distances // Return the updated distances matrix } ================================================ FILE: Dynamic Programming/distane_of_nearest_0.java ================================================ // Given an m x n binary matrix mat, return the distance of the nearest 0 for each cell in Java // Solution // // This solution uses a breadth-first search (BFS) approach to calculate the distance of the nearest 0 for each cell in the matrix. // // The idea is to initialize a distances matrix with all values set to the maximum integer value, except for the cells that contain 0s, // // which are set to 0 and added to a queue. We then perform a BFS on the queue, updating the distances of neighboring cells as we go. // // Finally, we return the updated distances matrix. // Time Complexity: // We traverse the entire matrix in the worst case to fill the distances matrix with initial values, which takes O(m * n) time. // We use Breadth-First Search (BFS) to update the distances matrix, which in the worst case can visit each cell once, taking O(m * n) time. // Therefore, the total time complexity of this solution is O(m * n). // Space Complexity: // We store the distances matrix, which requires O(m * n) space. // We use a queue to implement the BFS algorithm, which can store at most m * n cells in the worst case, taking O(m * n) space. // Therefore, the total space complexity of this solution is O(m * n). class Solution { private static final int[][] DIRECTIONS = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}; public int[][] updateMatrix(int[][] mat) { int m = mat.length; int n = mat[0].length; int[][] distances = new int[m][n]; // Initialize a distances matrix Queue queue = new LinkedList<>(); // Initialize a queue for BFS // Loop through the matrix and set distances to MAX_VALUE except for cells with 0 for (int i = 0; i < m; i++) { Arrays.fill(distances[i], Integer.MAX_VALUE); for (int j = 0; j < n; j++) { if (mat[i][j] == 0) { distances[i][j] = 0; queue.offer(new int[]{i, j}); // Add the cell to the queue } } } // Perform BFS while (!queue.isEmpty()) { int[] cell = queue.poll(); int i = cell[0]; int j = cell[1]; for (int[] direction : DIRECTIONS) { int x = i + direction[0]; int y = j + direction[1]; if (x < 0 || x >= m || y < 0 || y >= n) { // Check if cell is out of bounds continue; } if (distances[x][y] <= distances[i][j] + 1) {// Check if distance is already smaller continue; } distances[x][y] = distances[i][j] + 1; // Update the distance queue.offer(new int[]{x, y});// Add the cell to the queue } } return distances; // Return the updated distances matrix } } //Input // mat = // [[0,0,0],[0,1,0],[0,0,0]] // Output // [[0,0,0],[0,1,0],[0,0,0]] ================================================ FILE: Dynamic Programming/distane_of_nearest_0.js ================================================ // Given an m x n binary matrix mat, return the distance of the nearest 0 for each cell in Java // Solution // // This solution uses a breadth-first search (BFS) approach to calculate the distance of the nearest 0 for each cell in the matrix. // // The idea is to initialize a distances matrix with all values set to the maximum integer value, except for the cells that contain 0s, // // which are set to 0 and added to a queue. We then perform a BFS on the queue, updating the distances of neighboring cells as we go. // // Finally, we return the updated distances matrix. // Time Complexity: // We traverse the entire matrix in the worst case to fill the distances matrix with initial values, which takes O(m * n) time. // We use Breadth-First Search (BFS) to update the distances matrix, which in the worst case can visit each cell once, taking O(m * n) time. // Therefore, the total time complexity of this solution is O(m * n). // Space Complexity: // We store the distances matrix, which requires O(m * n) space. // We use a queue to implement the BFS algorithm, which can store at most m * n cells in the worst case, taking O(m * n) space. // Therefore, the total space complexity of this solution is O(m * n). function updateMatrix(mat) { const m = mat.length; const n = mat[0].length; const queue = []; // Initialize distance matrix with maximum possible values const dist = new Array(m).fill().map(() => new Array(n).fill(Number.MAX_VALUE)); // Initialize the queue with all cells containing 0 for (let i = 0; i < m; i++) { for (let j = 0; j < n; j++) { if (mat[i][j] === 0) { dist[i][j] = 0; queue.push([i, j]); } } } // Perform a BFS starting from the cells containing 0 while (queue.length > 0) { const [i, j] = queue.shift(); // Check the neighbors of the current cell const neighbors = [[i - 1, j], [i + 1, j], [i, j - 1], [i, j + 1]]; for (const [ni, nj] of neighbors) { // Check if the neighbor is within bounds if (ni >= 0 && ni < m && nj >= 0 && nj < n) { // If the distance to the neighbor can be updated if (dist[ni][nj] > dist[i][j] + 1) { dist[ni][nj] = dist[i][j] + 1; queue.push([ni, nj]); } } } } return dist; } The updateMatrix function takes a binary matrix mat as input and returns a matrix dist with the distance of the nearest 0 for each cell. The algorithm first initializes the dist matrix with maximum possible values and adds all cells containing 0 to a queue. Then, it performs a BFS starting from the cells in the queue and updates the distances of the neighboring cells if they can be improved. Finally, it returns the dist matrix with the updated distances. ================================================ FILE: Dynamic Programming/edit_distance_dp.cpp ================================================ /* Given two strings str1 and str2 and following three operations that can performed on str1. 1) Insert 2) Remove 3) Replace Find minimum number of operations required to convert ‘str1’ into ‘str2’. For example if input strings are CAT AND CAR the edit distance is 1. Input : s1 : saturday s2 : sunday Output : 3 */ // Dynamic Programming Solution : TC O(n^2) // Porgram Author : Abhisek Kumar Gupta #include using namespace std; // Function to find the minimum edit distance between two strings int find_edit_distance(string s1, string s2, int l1, int l2){ // Create a 2D array dp to store the edit distances int dp[100][100] = {}; // Initialize the base cases for empty strings for(int i = 0; i <= l1; i++){ dp[i][0] = i; // Minimum edit distance for transforming s1[0...i] to an empty string } for(int i = 0; i <= l2; i++){ dp[0][i] = i; // Minimum edit distance for transforming an empty string to s2[0...i] } // Calculate the edit distance for the rest of the strings for(int i = 1; i <= l1; i++){ for(int j = 1; j <= l2; j++){ if(s1[i] == s2[j]) dp[i][j] = dp[i - 1][j - 1]; // No edit required if characters match else{ int del = dp[i][j - 1]; // Deletion (from s2) int replace = dp[i - 1][j - 1]; // Replacement (of s1[i] with s2[j]) int insert = dp[i - 1][j]; // Insertion (into s1) dp[i][j] = min(del, min(replace, insert)) + 1; // Choose the minimum of the three operations } } } // Print the edit distance matrix (optional) for(int i = 0; i <= l1; i++){ for(int j = 0; j <= l2; j++){ cout << setw(5) << dp[i][j] << " "; } cout << "\n"; } return dp[l1][l2]; // Return the minimum edit distance } int main(){ string s1 = "abhisek"; string s2 = "tsunade"; int l1 = s1.length() - 1; int l2 = s2.length() - 1; int result = find_edit_distance(s1, s2, l1, l2); cout << "Minimum Edit Distance: " << result; return 0; } ================================================ FILE: Dynamic Programming/edit_distance_dp.go ================================================ /* Given two strings str1 and str2 and following three operations that can performed on str1. 1) Insert 2) Remove 3) Replace Find minimum number of operations required to convert ‘str1’ into ‘str2’. For example if input strings are CAT AND CAR the edit distance is 1. Input : s1 : saturday s2 : sunday Output : 3 */ // Dynamic Programming Solution : TC O(n^2) package main import ( "fmt" ) // findEditDistance finds the minimum edit distance between two strings. func findEditDistance(s1, s2 string) int { l1, l2 := len(s1), len(s2) // Create a 2D array dp to store the edit distances. dp := make([][]int, l1+1) for i := range dp { dp[i] = make([]int, l2+1) } // Initialize the base cases for empty strings. for i := 0; i <= l1; i++ { dp[i][0] = i // Minimum edit distance for transforming s1[0...i] to an empty string. } for i := 0; i <= l2; i++ { dp[0][i] = i // Minimum edit distance for transforming an empty string to s2[0...i]. } // Calculate the edit distance for the rest of the strings. for i := 1; i <= l1; i++ { for j := 1; j <= l2; j++ { if s1[i-1] == s2[j-1] { dp[i][j] = dp[i-1][j-1] // No edit required if characters match. } else { del := dp[i][j-1] // Deletion (from s2). replace := dp[i-1][j-1] // Replacement (of s1[i] with s2[j]). insert := dp[i-1][j] // Insertion (into s1). dp[i][j] = min(del, min(replace, insert)) + 1 // Choose the minimum of the three operations. } } } // Print the edit distance matrix (optional). for i := 0; i <= l1; i++ { for j := 0; j <= l2; j++ { fmt.Printf("%5d ", dp[i][j]) } fmt.Println() } return dp[l1][l2] // Return the minimum edit distance. } func main() { s1 := "abhisek" s2 := "tsunade" result := findEditDistance(s1, s2) fmt.Printf("Minimum Edit Distance: %d\n", result) } ================================================ FILE: Dynamic Programming/edit_distance_dp.java ================================================ /* Given two strings str1 and str2 and following three operations that can performed on str1. 1) Insert 2) Remove 3) Replace Find minimum number of operations required to convert ‘str1’ into ‘str2’. For example if input strings are CAT AND CAR the edit distance is 1. Input : s1 : saturday s2 : sunday Output : 3 */ // Dynamic Programming Solution : TC O(n^2) public class EditDistance { // Function to find the minimum edit distance between two strings public static int findEditDistance(String s1, String s2) { int l1 = s1.length(); int l2 = s2.length(); // Create a 2D array dp to store the edit distances int[][] dp = new int[l1 + 1][l2 + 1]; // Initialize the base cases for empty strings for (int i = 0; i <= l1; i++) { dp[i][0] = i; // Minimum edit distance for transforming s1[0...i] to an empty string } for (int i = 0; i <= l2; i++) { dp[0][i] = i; // Minimum edit distance for transforming an empty string to s2[0...i] } // Calculate the edit distance for the rest of the strings for (int i = 1; i <= l1; i++) { for (int j = 1; j <= l2; j++) { if (s1.charAt(i - 1) == s2.charAt(j - 1)) { dp[i][j] = dp[i - 1][j - 1]; // No edit required if characters match } else { int del = dp[i][j - 1]; // Deletion (from s2) int replace = dp[i - 1][j - 1]; // Replacement (of s1[i] with s2[j]) int insert = dp[i - 1][j]; // Insertion (into s1) dp[i][j] = Math.min(del, Math.min(replace, insert)) + 1; // Choose the minimum of the three operations } } } // Print the edit distance matrix (optional) for (int i = 0; i <= l1; i++) { for (int j = 0; j <= l2; j++) { System.out.printf("%5d ", dp[i][j]); } System.out.println(); } return dp[l1][l2]; // Return the minimum edit distance } public static void main(String[] args) { String s1 = "abhisek"; String s2 = "tsunade"; int result = findEditDistance(s1, s2); System.out.println("Minimum Edit Distance: " + result); } } ================================================ FILE: Dynamic Programming/edit_distance_dp.js ================================================ /* Given two strings str1 and str2 and following three operations that can performed on str1. 1) Insert 2) Remove 3) Replace Find minimum number of operations required to convert ‘str1’ into ‘str2’. For example if input strings are CAT AND CAR the edit distance is 1. Input : s1 : saturday s2 : sunday Output : 3 */ // Dynamic Programming Solution : TC O(n^2) // Function to find the minimum edit distance between two strings function findEditDistance(s1, s2) { const l1 = s1.length; const l2 = s2.length; // Create a 2D array dp to store the edit distances const dp = Array.from({ length: l1 + 1 }, () => Array(l2 + 1).fill(0)); // Initialize the base cases for empty strings for (let i = 0; i <= l1; i++) { dp[i][0] = i; // Minimum edit distance for transforming s1[0...i] to an empty string } for (let i = 0; i <= l2; i++) { dp[0][i] = i; // Minimum edit distance for transforming an empty string to s2[0...i] } // Calculate the edit distance for the rest of the strings for (let i = 1; i <= l1; i++) { for (let j = 1; j <= l2; j++) { if (s1[i - 1] === s2[j - 1]) { dp[i][j] = dp[i - 1][j - 1]; // No edit required if characters match } else { const del = dp[i][j - 1]; // Deletion (from s2) const replace = dp[i - 1][j - 1]; // Replacement (of s1[i] with s2[j]) const insert = dp[i - 1][j]; // Insertion (into s1) dp[i][j] = Math.min(del, Math.min(replace, insert)) + 1; // Choose the minimum of the three operations } } } // Print the edit distance matrix (optional) for (let i = 0; i <= l1; i++) { console.log( dp[i].map((value) => value.toString().padStart(5, " ")).join(" ") ); } return dp[l1][l2]; // Return the minimum edit distance } // Example usage const s1 = "abhisek"; const s2 = "tsunade"; const result = findEditDistance(s1, s2); console.log("Minimum Edit Distance:", result); ================================================ FILE: Dynamic Programming/edit_distance_dp.py ================================================ ''' Given two strings str1 and str2 and following three operations that can performed on str1. 1) Insert 2) Remove 3) Replace Find minimum number of operations required to convert ‘str1’ into ‘str2’. For example if input strings are CAT AND CAR the edit distance is 1. Input : s1 : saturday s2 : sunday Output : 3 */ // Dynamic Programming Solution : TC O(n^2) ''' # Function to find the minimum edit distance between two strings def find_edit_distance(s1, s2): l1, l2 = len(s1), len(s2) # Create a 2D list dp to store the edit distances dp = [[0] * (l2 + 1) for _ in range(l1 + 1)] # Initialize the base cases for empty strings for i in range(l1 + 1): dp[i][0] = i # Minimum edit distance for transforming s1[0...i] to an empty string for i in range(l2 + 1): dp[0][i] = i # Minimum edit distance for transforming an empty string to s2[0...i] # Calculate the edit distance for the rest of the strings for i in range(1, l1 + 1): for j in range(1, l2 + 1): if s1[i - 1] == s2[j - 1]: dp[i][j] = dp[i - 1][j - 1] # No edit required if characters match else: del_op = dp[i][j - 1] # Deletion (from s2) replace_op = dp[i - 1][j - 1] # Replacement (of s1[i] with s2[j]) insert_op = dp[i - 1][j] # Insertion (into s1) dp[i][j] = min(del_op, min(replace_op, insert_op)) + 1 # Choose the minimum of the three operations # Print the edit distance matrix (optional) for i in range(l1 + 1): print(" ".join(map(lambda value: f"{value:5}", dp[i]))) return dp[l1][l2] # Return the minimum edit distance # Example usage s1 = "abhisek" s2 = "tsunade" result = find_edit_distance(s1, s2) print("Minimum Edit Distance:", result) ================================================ FILE: Dynamic Programming/edit_distance_memoized.cpp ================================================ /* Given two strings str1 and str2 and following three operations that can performed on str1. 1) Insert 2) Remove 3) Replace Find minimum number of operations required to convert ‘str1’ into ‘str2’. For example if input strings are CAT AND CAR the edit distance is 1. Input : s1 : saturday s2 : sunday Output : 3 */ // Memoized Solution : TC O(n^2) // Porgram Author : Abhisek Kumar Gupta #include using namespace std; int calls = 0; int memoize[1009][1009]; int find_edit_distance(string s1, string s2, int l1, int l2){ calls++; if(l1 == 0) return l2; if(l2 == 0) return l1; if(memoize[l1][l2] != -1) return memoize[l1][l2]; if(s1[l1] == s2[l2]){ return find_edit_distance(s1, s2, l1 - 1, l2 - 1); } int del = find_edit_distance(s1, s2, l1, l2 - 1); int replace = find_edit_distance(s1, s2, l1 - 1, l2 - 1); int insert = find_edit_distance(s1, s2, l1 - 1, l2); memoize[l1][l2] = min (del, min(replace, insert)) + 1; return min (del, min(replace, insert)) + 1; } int main(){ memset(memoize, -1, sizeof(memoize)); string s1 = "abhisek"; string s2 = "tsunade"; int l1 = s1.length() - 1; int l2 = s2.length() - 1; int result = find_edit_distance(s1, s2, l1, l2); cout << result; cout << "\n" << calls; return 0; } ================================================ FILE: Dynamic Programming/edit_distance_recursive.cpp ================================================ /* Given two strings str1 and str2 and following three operations that can performed on str1. 1) Insert 2) Remove 3) Replace Find minimum number of operations required to convert ‘str1’ into ‘str2’. For example if input strings are CAT AND CAR the edit distance is 1. Input : s1 : saturday s2 : sunday Output : 3 */ // Recursive Solution : TC Exponential // Porgram Author : Abhisek Kumar Gupta #include using namespace std; int calls = 0; int find_edit_distance(string s1, string s2, int l1, int l2){ calls++; if(l1 == 0) return l2; if(l2 == 0) return l1; if(s1[l1] == s2[l2]){ return find_edit_distance(s1, s2, l1 - 1, l2 - 1); } int del = find_edit_distance(s1, s2, l1, l2 - 1); int replace = find_edit_distance(s1, s2, l1 - 1, l2 - 1); int insert = find_edit_distance(s1, s2, l1 - 1, l2); return min (del, min(replace, insert)) + 1; } int main(){ string s1 = "abhisek"; string s2 = "tsunade"; int l1 = s1.length() - 1; int l2 = s2.length() - 1; int result = find_edit_distance(s1, s2, l1, l2); cout << result; cout << "\n" << calls; return 0; } ================================================ FILE: Dynamic Programming/house_robber.cpp ================================================ /* You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security systems connected and it will automatically contact the police if two adjacent houses were broken into on the same night. Given an integer array nums representing the amount of money of each house, return the maximum amount of money you can rob tonight without alerting the police. Example 1: Input: nums = [1,2,3,1] Output: 4 Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3). Total amount you can rob = 1 + 3 = 4. Example 2: Input: nums = [2,7,9,3,1] Output: 12 Explanation: Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 (money = 1). Total amount you can rob = 2 + 9 + 1 = 12. Constraints: 1 <= nums.length <= 100 0 <= nums[i] <= 400 */ #include class Solution { public: int rob(vector& nums) { int len = nums.size(); /* if(len == 0) return 0; if(len == 1) return nums[0]; if(len == 2) return max(nums[0], nums[1]); int dp[len]; dp[0] = nums[0]; dp[1] = max(nums[0], nums[1]); for(int i = 2; i < len; i++){ dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]); } return dp[len-1]; */ int prev = 0, curr = 0, temp = 0; for(int i = 0; i < len; i++){ temp = max(nums[i] + prev, curr); prev = curr; curr = temp; } return curr; } }; ================================================ FILE: Dynamic Programming/juice_bottling.cpp ================================================ /* Explanation: 1. The function `JuiceBottling` takes an array `prices` as input, where `prices[i]` represents the price of a juice bottle of size `i`. 2. It initializes two arrays, `maxProfit` and `dividingPoints`, both of size `numSizes` (the number of bottle sizes). These arrays will be used to store information about maximum profit and dividing points. 3. The outer loop iterates through each possible bottle size, from 0 to `numSizes - 1`. 4. The inner loop iterates through possible dividing points for the current bottle size. For each combination of bottle size and dividing point, it calculates the possible profit by adding the maximum profit from the previous bottle sizes and the price of the bottle at the current dividing point. 5. If the calculated possible profit is greater than the current maximum profit for the bottle size, it updates both `maxProfit` and `dividingPoints` arrays. 6. After completing the loops, the function reconstructs the solution by backtracking from the last bottle size to the first. It appends the recorded dividing points to the `solution` array, which represents the optimal way to divide the bottles. 7. The function returns the `solution` array, which contains the indices of the dividing points that maximize profit. In summary, the code uses dynamic programming to determine the optimal division of juice bottles to maximize profit. It calculates the maximum profit for each bottle size and keeps track of the dividing points that lead to the maximum profit. The solution is then reconstructed by backtracking from the end using the recorded dividing points. */ #include #include std::vector juiceBottling(std::vector& prices) { int numSizes = prices.size(); std::vector maxProfit(numSizes); // Vector to store the maximum profit for each bottle size std::vector dividingPoints(numSizes); // Vector to store the dividing points that maximize profit // Loop through each bottle size for (int size = 0; size < numSizes; size++) { // Loop through possible dividing points for the current size for (int dividingPoint = 0; dividingPoint < size + 1; dividingPoint++) { // Calculate the possible profit by combining the previous maximum profit // with the price at the current dividing point int possibleProfit = maxProfit[size - dividingPoint] + prices[dividingPoint]; // Update maxProfit and dividingPoints if the new possible profit is greater if (possibleProfit > maxProfit[size]) { maxProfit[size] = possibleProfit; dividingPoints[size] = dividingPoint; } } } std::vector solution; int currentDividingPoint = numSizes - 1; // Reconstruct the solution by tracing back from the end // using the dividing points information while (currentDividingPoint > 0) { solution.push_back(dividingPoints[currentDividingPoint]); currentDividingPoint -= dividingPoints[currentDividingPoint]; } return solution; } int main() { std::vector prices = {3, 5, 8, 9, 10, 17, 17, 20}; std::vector result = juiceBottling(prices); std::cout << "Dividing Points for Maximum Profit:"; for (int point : result) { std::cout << " " << point; } std::cout << std::endl; return 0; } ================================================ FILE: Dynamic Programming/juice_bottling.go ================================================ /* Explanation: 1. The function `JuiceBottling` takes an array `prices` as input, where `prices[i]` represents the price of a juice bottle of size `i`. 2. It initializes two arrays, `maxProfit` and `dividingPoints`, both of size `numSizes` (the number of bottle sizes). These arrays will be used to store information about maximum profit and dividing points. 3. The outer loop iterates through each possible bottle size, from 0 to `numSizes - 1`. 4. The inner loop iterates through possible dividing points for the current bottle size. For each combination of bottle size and dividing point, it calculates the possible profit by adding the maximum profit from the previous bottle sizes and the price of the bottle at the current dividing point. 5. If the calculated possible profit is greater than the current maximum profit for the bottle size, it updates both `maxProfit` and `dividingPoints` arrays. 6. After completing the loops, the function reconstructs the solution by backtracking from the last bottle size to the first. It appends the recorded dividing points to the `solution` array, which represents the optimal way to divide the bottles. 7. The function returns the `solution` array, which contains the indices of the dividing points that maximize profit. In summary, the code uses dynamic programming to determine the optimal division of juice bottles to maximize profit. It calculates the maximum profit for each bottle size and keeps track of the dividing points that lead to the maximum profit. The solution is then reconstructed by backtracking from the end using the recorded dividing points. */ package main func JuiceBottling(prices []int) []int { numSizes := len(prices) maxProfit := make([]int, numSizes) // Array to store the maximum profit for each bottle size dividingPoints := make([]int, numSizes) // Array to store the dividing points that maximize profit // Loop through each bottle size for size := 0; size < numSizes; size++ { // Loop through possible dividing points for the current size for dividingPoint := 0; dividingPoint < size+1; dividingPoint++ { // Calculate the possible profit by combining the previous maximum profit // with the price at the current dividing point possibleProfit := maxProfit[size-dividingPoint] + prices[dividingPoint] // Update maxProfit and dividingPoints if the new possible profit is greater if possibleProfit > maxProfit[size] { maxProfit[size] = possibleProfit dividingPoints[size] = dividingPoint } } } solution := []int{} currentDividingPoint := numSizes - 1 // Reconstruct the solution by tracing back from the end // using the dividing points information for currentDividingPoint > 0 { solution = append(solution, dividingPoints[currentDividingPoint]) currentDividingPoint -= dividingPoints[currentDividingPoint] } return solution } ================================================ FILE: Dynamic Programming/juice_bottling.java ================================================ /* Explanation: 1. The function `JuiceBottling` takes an array `prices` as input, where `prices[i]` represents the price of a juice bottle of size `i`. 2. It initializes two arrays, `maxProfit` and `dividingPoints`, both of size `numSizes` (the number of bottle sizes). These arrays will be used to store information about maximum profit and dividing points. 3. The outer loop iterates through each possible bottle size, from 0 to `numSizes - 1`. 4. The inner loop iterates through possible dividing points for the current bottle size. For each combination of bottle size and dividing point, it calculates the possible profit by adding the maximum profit from the previous bottle sizes and the price of the bottle at the current dividing point. 5. If the calculated possible profit is greater than the current maximum profit for the bottle size, it updates both `maxProfit` and `dividingPoints` arrays. 6. After completing the loops, the function reconstructs the solution by backtracking from the last bottle size to the first. It appends the recorded dividing points to the `solution` array, which represents the optimal way to divide the bottles. 7. The function returns the `solution` array, which contains the indices of the dividing points that maximize profit. In summary, the code uses dynamic programming to determine the optimal division of juice bottles to maximize profit. It calculates the maximum profit for each bottle size and keeps track of the dividing points that lead to the maximum profit. The solution is then reconstructed by backtracking from the end using the recorded dividing points. */ import java.util.ArrayList; import java.util.List; public class JuiceBottling { public static List juiceBottling(int[] prices) { int numSizes = prices.length; int[] maxProfit = new int[numSizes]; // Array to store the maximum profit for each bottle size int[] dividingPoints = new int[numSizes]; // Array to store the dividing points that maximize profit // Loop through each bottle size for (int size = 0; size < numSizes; size++) { // Loop through possible dividing points for the current size for (int dividingPoint = 0; dividingPoint < size + 1; dividingPoint++) { // Calculate the possible profit by combining the previous maximum profit // with the price at the current dividing point int possibleProfit = maxProfit[size - dividingPoint] + prices[dividingPoint]; // Update maxProfit and dividingPoints if the new possible profit is greater if (possibleProfit > maxProfit[size]) { maxProfit[size] = possibleProfit; dividingPoints[size] = dividingPoint; } } } List solution = new ArrayList<>(); int currentDividingPoint = numSizes - 1; // Reconstruct the solution by tracing back from the end // using the dividing points information while (currentDividingPoint > 0) { solution.add(dividingPoints[currentDividingPoint]); currentDividingPoint -= dividingPoints[currentDividingPoint]; } return solution; } public static void main(String[] args) { int[] prices = {3, 5, 8, 9, 10, 17, 17, 20}; List result = juiceBottling(prices); System.out.println("Dividing Points for Maximum Profit: " + result); } } ================================================ FILE: Dynamic Programming/juice_bottling.js ================================================ /* Explanation: 1. The function `JuiceBottling` takes an array `prices` as input, where `prices[i]` represents the price of a juice bottle of size `i`. 2. It initializes two arrays, `maxProfit` and `dividingPoints`, both of size `numSizes` (the number of bottle sizes). These arrays will be used to store information about maximum profit and dividing points. 3. The outer loop iterates through each possible bottle size, from 0 to `numSizes - 1`. 4. The inner loop iterates through possible dividing points for the current bottle size. For each combination of bottle size and dividing point, it calculates the possible profit by adding the maximum profit from the previous bottle sizes and the price of the bottle at the current dividing point. 5. If the calculated possible profit is greater than the current maximum profit for the bottle size, it updates both `maxProfit` and `dividingPoints` arrays. 6. After completing the loops, the function reconstructs the solution by backtracking from the last bottle size to the first. It appends the recorded dividing points to the `solution` array, which represents the optimal way to divide the bottles. 7. The function returns the `solution` array, which contains the indices of the dividing points that maximize profit. In summary, the code uses dynamic programming to determine the optimal division of juice bottles to maximize profit. It calculates the maximum profit for each bottle size and keeps track of the dividing points that lead to the maximum profit. The solution is then reconstructed by backtracking from the end using the recorded dividing points. */ function juiceBottling(prices) { const numSizes = prices.length; const maxProfit = new Array(numSizes).fill(0); // Array to store the maximum profit for each bottle size const dividingPoints = new Array(numSizes).fill(0); // Array to store the dividing points that maximize profit // Loop through each bottle size for (let size = 0; size < numSizes; size++) { // Loop through possible dividing points for the current size for (let dividingPoint = 0; dividingPoint < size + 1; dividingPoint++) { // Calculate the possible profit by combining the previous maximum profit // with the price at the current dividing point const possibleProfit = maxProfit[size - dividingPoint] + prices[dividingPoint]; // Update maxProfit and dividingPoints if the new possible profit is greater if (possibleProfit > maxProfit[size]) { maxProfit[size] = possibleProfit; dividingPoints[size] = dividingPoint; } } } const solution = []; let currentDividingPoint = numSizes - 1; // Reconstruct the solution by tracing back from the end // using the dividing points information while (currentDividingPoint > 0) { solution.push(dividingPoints[currentDividingPoint]); currentDividingPoint -= dividingPoints[currentDividingPoint]; } return solution; } // Example usage: const prices = [3, 5, 8, 9, 10, 17, 17, 20]; const result = juiceBottling(prices); console.log("Dividing Points for Maximum Profit:", result); ================================================ FILE: Dynamic Programming/juice_bottling.py ================================================ ''' Explanation: 1. The function `JuiceBottling` takes an array `prices` as input, where `prices[i]` represents the price of a juice bottle of size `i`. 2. It initializes two arrays, `maxProfit` and `dividingPoints`, both of size `numSizes` (the number of bottle sizes). These arrays will be used to store information about maximum profit and dividing points. 3. The outer loop iterates through each possible bottle size, from 0 to `numSizes - 1`. 4. The inner loop iterates through possible dividing points for the current bottle size. For each combination of bottle size and dividing point, it calculates the possible profit by adding the maximum profit from the previous bottle sizes and the price of the bottle at the current dividing point. 5. If the calculated possible profit is greater than the current maximum profit for the bottle size, it updates both `maxProfit` and `dividingPoints` arrays. 6. After completing the loops, the function reconstructs the solution by backtracking from the last bottle size to the first. It appends the recorded dividing points to the `solution` array, which represents the optimal way to divide the bottles. 7. The function returns the `solution` array, which contains the indices of the dividing points that maximize profit. In summary, the code uses dynamic programming to determine the optimal division of juice bottles to maximize profit. It calculates the maximum profit for each bottle size and keeps track of the dividing points that lead to the maximum profit. The solution is then reconstructed by backtracking from the end using the recorded dividing points. ''' def juice_bottling(prices): num_sizes = len(prices) max_profit = [0] * num_sizes # List to store the maximum profit for each bottle size dividing_points = [0] * num_sizes # List to store the dividing points that maximize profit # Loop through each bottle size for size in range(num_sizes): # Loop through possible dividing points for the current size for dividing_point in range(size + 1): # Calculate the possible profit by combining the previous maximum profit # with the price at the current dividing point possible_profit = max_profit[size - dividing_point] + prices[dividing_point] # Update max_profit and dividing_points if the new possible profit is greater if possible_profit > max_profit[size]: max_profit[size] = possible_profit dividing_points[size] = dividing_point solution = [] current_dividing_point = num_sizes - 1 # Reconstruct the solution by tracing back from the end # using the dividing points information while current_dividing_point > 0: solution.append(dividing_points[current_dividing_point]) current_dividing_point -= dividing_points[current_dividing_point] return solution # Example usage: prices = [3, 5, 8, 9, 10, 17, 17, 20] result = juice_bottling(prices) print("Dividing Points for Maximum Profit:", result) ================================================ FILE: Dynamic Programming/knapsack.cpp ================================================ /* You're given an array of arrays where each subarray holds two integer values and represents an item; the first integer is the item's value, and the second integer is the item's weight. You're also given an integer representing the maximum capacity of a knapsack that you have. Your goal is to fit items in your knapsack without having the sum of their weights exceed the knapsack's capacity, all the while maximizing their combined value. Note that you only have one of each item at your disposal. Write a function that returns the maximized combined value of the items that you should pick as well as an array of the indices of each item picked. Sample Input:= [[1, 2], [4, 3], [5, 6], [6, 7]] Output:= [10, [1, 3]] // items [4, 3] and [6, 7] Explanation: Sure! Let's break down the code step by step: 1. `KnapsackProblem` function: This function takes in two arguments - `items`, a 2D slice representing the list of items with their values and weights, and `capacity`, an integer representing the maximum weight capacity of the knapsack. It returns an interface slice containing the maximum value that can be achieved and the sequence of items included in the knapsack to achieve that maximum value. 2. Initializing the `values` array: The function creates a 2D slice called `values` to store the maximum achievable values for different knapsack configurations. The size of this array is `(len(items)+1) x (capacity+1)`, where `(len(items)+1)` represents the number of items, and `(capacity+1)` represents the weight capacity of the knapsack. The `values` array will be filled during the dynamic programming process. 3. Filling the `values` array: The function iterates through the `items` array and fills the `values` array using dynamic programming. For each item at index `i`, the function calculates the maximum achievable value for all possible capacities from `0` to `capacity`. 4. Inner loop: The inner loop iterates from `0` to `capacity` and calculates the maximum achievable value for the current item at index `i` and the current capacity `c`. 5. Updating the `values` array: There are two possibilities for each item: a. If the weight of the current item `items[i-1][1]` is greater than the current capacity `c`, we cannot include the item in the knapsack at this capacity. So, we use the value from the previous row `values[i-1][c]` for the current cell `values[i][c]`. b. If we can include the current item, we have two choices: i. Not include the current item, so the value remains the same as in the previous row `values[i-1][c]`. ii. Include the current item, which adds its value `items[i-1][0]` to the value of the knapsack at capacity `c - items[i-1][1]`. We choose the maximum of these two options and update the current cell `values[i][c]`. 6. Finding the maximum value: Once the `values` array is filled, the maximum achievable value for the knapsack is stored in the bottom-right cell `values[len(items)][capacity]`. 7. Calling `getKnapSackItems` function: The function calls the `getKnapSackItems` function to find the sequence of items included in the knapsack to achieve the maximum value. 8. `getKnapSackItems` function: This function takes in the `values` array and the `items` array as input and returns a slice containing the indices of the items included in the knapsack. 9. Traversing back to find the items: Starting from the bottom-right cell of the `values` array, the function traverses back to find the items included in the knapsack. It does this by comparing the value in the current cell `values[i][c]` with the value in the cell above `values[i-1][c]`. If the values are the same, it means the current item was not included, so it moves to the previous row. Otherwise, it means the current item was included, so it adds the index of the current item `(i-1)` to the `sequence` slice and updates the capacity `c` accordingly. 10. Reversing the `sequence`: The sequence of items is built in reverse order, so the function uses the `reverse` helper function to reverse the order of elements in the `sequence` slice. 11. Returning the result: The function returns the maximum value and the sequence of items included in the knapsack as an interface slice. 12. Helper functions: The `max` function is a simple helper function that returns the maximum of two integers, and the `reverse` function is used to reverse the order of elements in a slice. Time and Space complexity: O(nc) time | O(nc) space - where n is the number of items and c is the capacity */ #include #include using namespace std; vector knapsackProblem(vector>& items, int capacity) { // Create a 2D vector to store the values of different knapsack configurations. vector> values(items.size() + 1, vector(capacity + 1, 0)); // Iterate through the items and fill the values vector. for (int i = 1; i <= items.size(); i++) { int currentValue = items[i - 1][0]; int currentWeight = items[i - 1][1]; for (int c = 0; c <= capacity; c++) { // If the current item's weight is more than the current capacity (c), // then we cannot include it, so we use the value from the previous row (i - 1). if (currentWeight > c) { values[i][c] = values[i - 1][c]; } else { // If we can include the current item, we have two choices: // 1. Not include the current item, so the value remains the same as the previous row. // 2. Include the current item, which adds its value to the value of the knapsack at capacity (c - currentWeight). // We choose the maximum of these two options. values[i][c] = max(values[i - 1][c], values[i - 1][c - currentWeight] + currentValue); } } } // The value at the bottom-right corner of the values vector represents the maximum achievable value for the knapsack problem. int value = values[items.size()][capacity]; // Call the getKnapSackItems function to find the items that were included in the knapsack to achieve the maximum value. vector sequence = getKnapSackItems(values, items); // Return the maximum value and the sequence of items included in the knapsack. vector result = {value}; result.insert(result.end(), sequence.begin(), sequence.end()); return result; } // getKnapSackItems is a helper function to find the sequence of items included in the knapsack. vector getKnapSackItems(vector>& values, vector>& items) { vector sequence; int i = values.size() - 1; int c = values[0].size() - 1; // Starting from the bottom-right corner of the values vector, // we traverse back to find the items included in the knapsack. while (i > 0) { if (values[i][c] == values[i - 1][c]) { // If the value is the same as in the previous row, it means the current item was not included. // So, we move to the previous row without adding the item to the sequence. i--; } else { // If the value is greater than the value in the previous row, it means the current item was included. // So, we add the index of the current item (i-1) to the sequence and update the capacity (c) accordingly. sequence.push_back(i - 1); c -= items[i - 1][1]; i--; } // If the capacity becomes 0, it means we have included all the items needed to achieve the maximum value. if (c == 0) { break; } } // The sequence of items is built in reverse order, so we need to reverse it to get the correct order. reverse(sequence.begin(), sequence.end()); return sequence; } ================================================ FILE: Dynamic Programming/knapsack.go ================================================ /* You're given an array of arrays where each subarray holds two integer values and represents an item; the first integer is the item's value, and the second integer is the item's weight. You're also given an integer representing the maximum capacity of a knapsack that you have. Your goal is to fit items in your knapsack without having the sum of their weights exceed the knapsack's capacity, all the while maximizing their combined value. Note that you only have one of each item at your disposal. Write a function that returns the maximized combined value of the items that you should pick as well as an array of the indices of each item picked. Sample Input:= [[1, 2], [4, 3], [5, 6], [6, 7]] Output:= [10, [1, 3]] // items [4, 3] and [6, 7] Explanation: Sure! Let's break down the code step by step: 1. `KnapsackProblem` function: This function takes in two arguments - `items`, a 2D slice representing the list of items with their values and weights, and `capacity`, an integer representing the maximum weight capacity of the knapsack. It returns an interface slice containing the maximum value that can be achieved and the sequence of items included in the knapsack to achieve that maximum value. 2. Initializing the `values` array: The function creates a 2D slice called `values` to store the maximum achievable values for different knapsack configurations. The size of this array is `(len(items)+1) x (capacity+1)`, where `(len(items)+1)` represents the number of items, and `(capacity+1)` represents the weight capacity of the knapsack. The `values` array will be filled during the dynamic programming process. 3. Filling the `values` array: The function iterates through the `items` array and fills the `values` array using dynamic programming. For each item at index `i`, the function calculates the maximum achievable value for all possible capacities from `0` to `capacity`. 4. Inner loop: The inner loop iterates from `0` to `capacity` and calculates the maximum achievable value for the current item at index `i` and the current capacity `c`. 5. Updating the `values` array: There are two possibilities for each item: a. If the weight of the current item `items[i-1][1]` is greater than the current capacity `c`, we cannot include the item in the knapsack at this capacity. So, we use the value from the previous row `values[i-1][c]` for the current cell `values[i][c]`. b. If we can include the current item, we have two choices: i. Not include the current item, so the value remains the same as in the previous row `values[i-1][c]`. ii. Include the current item, which adds its value `items[i-1][0]` to the value of the knapsack at capacity `c - items[i-1][1]`. We choose the maximum of these two options and update the current cell `values[i][c]`. 6. Finding the maximum value: Once the `values` array is filled, the maximum achievable value for the knapsack is stored in the bottom-right cell `values[len(items)][capacity]`. 7. Calling `getKnapSackItems` function: The function calls the `getKnapSackItems` function to find the sequence of items included in the knapsack to achieve the maximum value. 8. `getKnapSackItems` function: This function takes in the `values` array and the `items` array as input and returns a slice containing the indices of the items included in the knapsack. 9. Traversing back to find the items: Starting from the bottom-right cell of the `values` array, the function traverses back to find the items included in the knapsack. It does this by comparing the value in the current cell `values[i][c]` with the value in the cell above `values[i-1][c]`. If the values are the same, it means the current item was not included, so it moves to the previous row. Otherwise, it means the current item was included, so it adds the index of the current item `(i-1)` to the `sequence` slice and updates the capacity `c` accordingly. 10. Reversing the `sequence`: The sequence of items is built in reverse order, so the function uses the `reverse` helper function to reverse the order of elements in the `sequence` slice. 11. Returning the result: The function returns the maximum value and the sequence of items included in the knapsack as an interface slice. 12. Helper functions: The `max` function is a simple helper function that returns the maximum of two integers, and the `reverse` function is used to reverse the order of elements in a slice. Time and Space complexity: O(nc) time | O(nc) space - where n is the number of items and c is the capacity */ package main func KnapsackProblem(items [][]int, capacity int) []interface{} { // Create a 2D array to store the values of different knapsack configurations. values := make([][]int, len(items)+1) for i := range values { values[i] = make([]int, capacity+1) } // Iterate through the items and fill the values array. for i := 1; i < len(items)+1; i++ { currentValue := items[i-1][0] currentWeight := items[i-1][1] for c := 0; c < capacity+1; c++ { // If the current item's weight is more than the current capacity (c), // then we cannot include it, so we use the value from the previous row (i - 1). if currentWeight > c { values[i][c] = values[i-1][c] } else { // If we can include the current item, we have two choices: // 1. Not include the current item, so the value remains the same as the previous row. // 2. Include the current item, which adds its value to the value of the knapsack at capacity (c - currentWeight). // We choose the maximum of these two options. values[i][c] = max(values[i-1][c], values[i-1][c-currentWeight]+currentValue) } } } // The value at the bottom-right corner of the values array represents the maximum achievable value for the knapsack problem. value := values[len(items)][capacity] // Call the getKnapSackItems function to find the items that were included in the knapsack to achieve the maximum value. sequence := getKnapSackItems(values, items) // Return the maximum value and the sequence of items included in the knapsack as an interface slice. return []interface{}{value, sequence} } // getKnapSackItems is a helper function to find the sequence of items included in the knapsack. func getKnapSackItems(values [][]int, items [][]int) []int { sequence := []int{} i, c := len(values)-1, len(values[0])-1 // Starting from the bottom-right corner of the values array, // we traverse back to find the items included in the knapsack. for i > 0 { if values[i][c] == values[i-1][c] { // If the value is the same as in the previous row, it means the current item was not included. // So, we move to the previous row without adding the item to the sequence. i-- } else { // If the value is greater than the value in the previous row, it means the current item was included. // So, we add the index of the current item (i-1) to the sequence and update the capacity (c) accordingly. sequence = append(sequence, i-1) c -= items[i-1][1] i-- } // If the capacity becomes 0, it means we have included all the items needed to achieve the maximum value. if c == 0 { break } } // The sequence of items is built in reverse order, so we need to reverse it to get the correct order. reverse(sequence) return sequence } // max returns the maximum of two integers. func max(a, b int) int { if a > b { return a } return b } // reverse reverses the order of elements in the given slice. func reverse(numbers []int) { for i, j := 0, len(numbers)-1; i < j; i, j = i+1, j-1 { numbers[i], numbers[j] = numbers[j], numbers[i] } } ================================================ FILE: Dynamic Programming/knapsack.java ================================================ /* You're given an array of arrays where each subarray holds two integer values and represents an item; the first integer is the item's value, and the second integer is the item's weight. You're also given an integer representing the maximum capacity of a knapsack that you have. Your goal is to fit items in your knapsack without having the sum of their weights exceed the knapsack's capacity, all the while maximizing their combined value. Note that you only have one of each item at your disposal. Write a function that returns the maximized combined value of the items that you should pick as well as an array of the indices of each item picked. Sample Input:= [[1, 2], [4, 3], [5, 6], [6, 7]] Output:= [10, [1, 3]] // items [4, 3] and [6, 7] Explanation: Sure! Let's break down the code step by step: 1. `KnapsackProblem` function: This function takes in two arguments - `items`, a 2D slice representing the list of items with their values and weights, and `capacity`, an integer representing the maximum weight capacity of the knapsack. It returns an interface slice containing the maximum value that can be achieved and the sequence of items included in the knapsack to achieve that maximum value. 2. Initializing the `values` array: The function creates a 2D slice called `values` to store the maximum achievable values for different knapsack configurations. The size of this array is `(len(items)+1) x (capacity+1)`, where `(len(items)+1)` represents the number of items, and `(capacity+1)` represents the weight capacity of the knapsack. The `values` array will be filled during the dynamic programming process. 3. Filling the `values` array: The function iterates through the `items` array and fills the `values` array using dynamic programming. For each item at index `i`, the function calculates the maximum achievable value for all possible capacities from `0` to `capacity`. 4. Inner loop: The inner loop iterates from `0` to `capacity` and calculates the maximum achievable value for the current item at index `i` and the current capacity `c`. 5. Updating the `values` array: There are two possibilities for each item: a. If the weight of the current item `items[i-1][1]` is greater than the current capacity `c`, we cannot include the item in the knapsack at this capacity. So, we use the value from the previous row `values[i-1][c]` for the current cell `values[i][c]`. b. If we can include the current item, we have two choices: i. Not include the current item, so the value remains the same as in the previous row `values[i-1][c]`. ii. Include the current item, which adds its value `items[i-1][0]` to the value of the knapsack at capacity `c - items[i-1][1]`. We choose the maximum of these two options and update the current cell `values[i][c]`. 6. Finding the maximum value: Once the `values` array is filled, the maximum achievable value for the knapsack is stored in the bottom-right cell `values[len(items)][capacity]`. 7. Calling `getKnapSackItems` function: The function calls the `getKnapSackItems` function to find the sequence of items included in the knapsack to achieve the maximum value. 8. `getKnapSackItems` function: This function takes in the `values` array and the `items` array as input and returns a slice containing the indices of the items included in the knapsack. 9. Traversing back to find the items: Starting from the bottom-right cell of the `values` array, the function traverses back to find the items included in the knapsack. It does this by comparing the value in the current cell `values[i][c]` with the value in the cell above `values[i-1][c]`. If the values are the same, it means the current item was not included, so it moves to the previous row. Otherwise, it means the current item was included, so it adds the index of the current item `(i-1)` to the `sequence` slice and updates the capacity `c` accordingly. 10. Reversing the `sequence`: The sequence of items is built in reverse order, so the function uses the `reverse` helper function to reverse the order of elements in the `sequence` slice. 11. Returning the result: The function returns the maximum value and the sequence of items included in the knapsack as an interface slice. 12. Helper functions: The `max` function is a simple helper function that returns the maximum of two integers, and the `reverse` function is used to reverse the order of elements in a slice. Time and Space complexity: O(nc) time | O(nc) space - where n is the number of items and c is the capacity */ import java.util.ArrayList; import java.util.List; public class KnapsackProblem { public static List knapsackProblem(int[][] items, int capacity) { // Create a 2D array to store the values of different knapsack configurations. int[][] values = new int[items.length + 1][capacity + 1]; // Iterate through the items and fill the values array. for (int i = 1; i <= items.length; i++) { int currentValue = items[i - 1][0]; int currentWeight = items[i - 1][1]; for (int c = 0; c <= capacity; c++) { // If the current item's weight is more than the current capacity (c), // then we cannot include it, so we use the value from the previous row (i - 1). if (currentWeight > c) { values[i][c] = values[i - 1][c]; } else { // If we can include the current item, we have two choices: // 1. Not include the current item, so the value remains the same as the previous row. // 2. Include the current item, which adds its value to the value of the knapsack at capacity (c - currentWeight). // We choose the maximum of these two options. values[i][c] = Math.max(values[i - 1][c], values[i - 1][c - currentWeight] + currentValue); } } } // The value at the bottom-right corner of the values array represents the maximum achievable value for the knapsack problem. int value = values[items.length][capacity]; // Call the getKnapSackItems function to find the items that were included in the knapsack to achieve the maximum value. List sequence = getKnapSackItems(values, items); // Return the maximum value and the sequence of items included in the knapsack as a list of objects. List result = new ArrayList<>(); result.add(value); result.add(sequence); return result; } // getKnapSackItems is a helper function to find the sequence of items included in the knapsack. private static List getKnapSackItems(int[][] values, int[][] items) { List sequence = new ArrayList<>(); int i = values.length - 1; int c = values[0].length - 1; // Starting from the bottom-right corner of the values array, // we traverse back to find the items included in the knapsack. while (i > 0) { if (values[i][c] == values[i - 1][c]) { // If the value is the same as in the previous row, it means the current item was not included. // So, we move to the previous row without adding the item to the sequence. i--; } else { // If the value is greater than the value in the previous row, it means the current item was included. // So, we add the index of the current item (i-1) to the sequence and update the capacity (c) accordingly. sequence.add(i - 1); c -= items[i - 1][1]; i--; } // If the capacity becomes 0, it means we have included all the items needed to achieve the maximum value. if (c == 0) { break; } } // The sequence of items is built in reverse order, so we need to reverse it to get the correct order. reverseList(sequence); return sequence; } // max returns the maximum of two integers. private static int max(int a, int b) { return Math.max(a, b); } // reverseList reverses the order of elements in the given list. private static void reverseList(List list) { int left = 0; int right = list.size() - 1; while (left < right) { int temp = list.get(left); list.set(left, list.get(right)); list.set(right, temp); left++; right--; } } } ================================================ FILE: Dynamic Programming/knapsack.js ================================================ /* You're given an array of arrays where each subarray holds two integer values and represents an item; the first integer is the item's value, and the second integer is the item's weight. You're also given an integer representing the maximum capacity of a knapsack that you have. Your goal is to fit items in your knapsack without having the sum of their weights exceed the knapsack's capacity, all the while maximizing their combined value. Note that you only have one of each item at your disposal. Write a function that returns the maximized combined value of the items that you should pick as well as an array of the indices of each item picked. Sample Input:= [[1, 2], [4, 3], [5, 6], [6, 7]] Output:= [10, [1, 3]] // items [4, 3] and [6, 7] Explanation: Sure! Let's break down the code step by step: 1. `KnapsackProblem` function: This function takes in two arguments - `items`, a 2D slice representing the list of items with their values and weights, and `capacity`, an integer representing the maximum weight capacity of the knapsack. It returns an interface slice containing the maximum value that can be achieved and the sequence of items included in the knapsack to achieve that maximum value. 2. Initializing the `values` array: The function creates a 2D slice called `values` to store the maximum achievable values for different knapsack configurations. The size of this array is `(len(items)+1) x (capacity+1)`, where `(len(items)+1)` represents the number of items, and `(capacity+1)` represents the weight capacity of the knapsack. The `values` array will be filled during the dynamic programming process. 3. Filling the `values` array: The function iterates through the `items` array and fills the `values` array using dynamic programming. For each item at index `i`, the function calculates the maximum achievable value for all possible capacities from `0` to `capacity`. 4. Inner loop: The inner loop iterates from `0` to `capacity` and calculates the maximum achievable value for the current item at index `i` and the current capacity `c`. 5. Updating the `values` array: There are two possibilities for each item: a. If the weight of the current item `items[i-1][1]` is greater than the current capacity `c`, we cannot include the item in the knapsack at this capacity. So, we use the value from the previous row `values[i-1][c]` for the current cell `values[i][c]`. b. If we can include the current item, we have two choices: i. Not include the current item, so the value remains the same as in the previous row `values[i-1][c]`. ii. Include the current item, which adds its value `items[i-1][0]` to the value of the knapsack at capacity `c - items[i-1][1]`. We choose the maximum of these two options and update the current cell `values[i][c]`. 6. Finding the maximum value: Once the `values` array is filled, the maximum achievable value for the knapsack is stored in the bottom-right cell `values[len(items)][capacity]`. 7. Calling `getKnapSackItems` function: The function calls the `getKnapSackItems` function to find the sequence of items included in the knapsack to achieve the maximum value. 8. `getKnapSackItems` function: This function takes in the `values` array and the `items` array as input and returns a slice containing the indices of the items included in the knapsack. 9. Traversing back to find the items: Starting from the bottom-right cell of the `values` array, the function traverses back to find the items included in the knapsack. It does this by comparing the value in the current cell `values[i][c]` with the value in the cell above `values[i-1][c]`. If the values are the same, it means the current item was not included, so it moves to the previous row. Otherwise, it means the current item was included, so it adds the index of the current item `(i-1)` to the `sequence` slice and updates the capacity `c` accordingly. 10. Reversing the `sequence`: The sequence of items is built in reverse order, so the function uses the `reverse` helper function to reverse the order of elements in the `sequence` slice. 11. Returning the result: The function returns the maximum value and the sequence of items included in the knapsack as an interface slice. 12. Helper functions: The `max` function is a simple helper function that returns the maximum of two integers, and the `reverse` function is used to reverse the order of elements in a slice. Time and Space complexity: O(nc) time | O(nc) space - where n is the number of items and c is the capacity */ function KnapsackProblem(items, capacity) { // Create a 2D array to store the values of different knapsack configurations. const values = new Array(items.length + 1) .fill(0) .map(() => new Array(capacity + 1).fill(0)); // Iterate through the items and fill the values array. for (let i = 1; i < items.length + 1; i++) { const currentValue = items[i - 1][0]; const currentWeight = items[i - 1][1]; for (let c = 0; c < capacity + 1; c++) { // If the current item's weight is more than the current capacity (c), // then we cannot include it, so we use the value from the previous row (i - 1). if (currentWeight > c) { values[i][c] = values[i - 1][c]; } else { // If we can include the current item, we have two choices: // 1. Not include the current item, so the value remains the same as the previous row. // 2. Include the current item, which adds its value to the value of the knapsack at capacity (c - currentWeight). // We choose the maximum of these two options. values[i][c] = Math.max( values[i - 1][c], values[i - 1][c - currentWeight] + currentValue ); } } } // The value at the bottom-right corner of the values array represents the maximum achievable value for the knapsack problem. const value = values[items.length][capacity]; // Call the getKnapSackItems function to find the items that were included in the knapsack to achieve the maximum value. const sequence = getKnapSackItems(values, items); // Return the maximum value and the sequence of items included in the knapsack as an array. return [value, sequence]; } // getKnapSackItems is a helper function to find the sequence of items included in the knapsack. function getKnapSackItems(values, items) { const sequence = []; let i = values.length - 1; let c = values[0].length - 1; // Starting from the bottom-right corner of the values array, // we traverse back to find the items included in the knapsack. while (i > 0) { if (values[i][c] == values[i - 1][c]) { // If the value is the same as in the previous row, it means the current item was not included. // So, we move to the previous row without adding the item to the sequence. i--; } else { // If the value is greater than the value in the previous row, it means the current item was included. // So, we add the index of the current item (i-1) to the sequence and update the capacity (c) accordingly. sequence.push(i - 1); c -= items[i - 1][1]; i--; } // If the capacity becomes 0, it means we have included all the items needed to achieve the maximum value. if (c == 0) { break; } } // Reverse the sequence of items to get the correct order. sequence.reverse(); return sequence; } // max returns the maximum of two integers. function max(a, b) { return a > b ? a : b; } ================================================ FILE: Dynamic Programming/knapsack.py ================================================ ''' You're given an array of arrays where each subarray holds two integer values and represents an item; the first integer is the item's value, and the second integer is the item's weight. You're also given an integer representing the maximum capacity of a knapsack that you have. Your goal is to fit items in your knapsack without having the sum of their weights exceed the knapsack's capacity, all the while maximizing their combined value. Note that you only have one of each item at your disposal. Write a function that returns the maximized combined value of the items that you should pick as well as an array of the indices of each item picked. Sample Input:= [[1, 2], [4, 3], [5, 6], [6, 7]] Output:= [10, [1, 3]] // items [4, 3] and [6, 7] Explanation: Sure! Let's break down the code step by step: 1. `KnapsackProblem` function: This function takes in two arguments - `items`, a 2D slice representing the list of items with their values and weights, and `capacity`, an integer representing the maximum weight capacity of the knapsack. It returns an interface slice containing the maximum value that can be achieved and the sequence of items included in the knapsack to achieve that maximum value. 2. Initializing the `values` array: The function creates a 2D slice called `values` to store the maximum achievable values for different knapsack configurations. The size of this array is `(len(items)+1) x (capacity+1)`, where `(len(items)+1)` represents the number of items, and `(capacity+1)` represents the weight capacity of the knapsack. The `values` array will be filled during the dynamic programming process. 3. Filling the `values` array: The function iterates through the `items` array and fills the `values` array using dynamic programming. For each item at index `i`, the function calculates the maximum achievable value for all possible capacities from `0` to `capacity`. 4. Inner loop: The inner loop iterates from `0` to `capacity` and calculates the maximum achievable value for the current item at index `i` and the current capacity `c`. 5. Updating the `values` array: There are two possibilities for each item: a. If the weight of the current item `items[i-1][1]` is greater than the current capacity `c`, we cannot include the item in the knapsack at this capacity. So, we use the value from the previous row `values[i-1][c]` for the current cell `values[i][c]`. b. If we can include the current item, we have two choices: i. Not include the current item, so the value remains the same as in the previous row `values[i-1][c]`. ii. Include the current item, which adds its value `items[i-1][0]` to the value of the knapsack at capacity `c - items[i-1][1]`. We choose the maximum of these two options and update the current cell `values[i][c]`. 6. Finding the maximum value: Once the `values` array is filled, the maximum achievable value for the knapsack is stored in the bottom-right cell `values[len(items)][capacity]`. 7. Calling `getKnapSackItems` function: The function calls the `getKnapSackItems` function to find the sequence of items included in the knapsack to achieve the maximum value. 8. `getKnapSackItems` function: This function takes in the `values` array and the `items` array as input and returns a slice containing the indices of the items included in the knapsack. 9. Traversing back to find the items: Starting from the bottom-right cell of the `values` array, the function traverses back to find the items included in the knapsack. It does this by comparing the value in the current cell `values[i][c]` with the value in the cell above `values[i-1][c]`. If the values are the same, it means the current item was not included, so it moves to the previous row. Otherwise, it means the current item was included, so it adds the index of the current item `(i-1)` to the `sequence` slice and updates the capacity `c` accordingly. 10. Reversing the `sequence`: The sequence of items is built in reverse order, so the function uses the `reverse` helper function to reverse the order of elements in the `sequence` slice. 11. Returning the result: The function returns the maximum value and the sequence of items included in the knapsack as an interface slice. 12. Helper functions: The `max` function is a simple helper function that returns the maximum of two integers, and the `reverse` function is used to reverse the order of elements in a slice. Time and Space complexity: O(nc) time | O(nc) space - where n is the number of items and c is the capacity ''' def knapsack_problem(items, capacity): # Create a 2D list to store the values of different knapsack configurations. values = [[0] * (capacity + 1) for _ in range(len(items) + 1)] # Iterate through the items and fill the values list. for i in range(1, len(items) + 1): current_value = items[i - 1][0] current_weight = items[i - 1][1] for c in range(capacity + 1): # If the current item's weight is more than the current capacity (c), # then we cannot include it, so we use the value from the previous row (i - 1). if current_weight > c: values[i][c] = values[i - 1][c] else: # If we can include the current item, we have two choices: # 1. Not include the current item, so the value remains the same as the previous row. # 2. Include the current item, which adds its value to the value of the knapsack at capacity (c - current_weight). # We choose the maximum of these two options. values[i][c] = max(values[i - 1][c], values[i - 1][c - current_weight] + current_value) # The value at the bottom-right corner of the values list represents the maximum achievable value for the knapsack problem. value = values[len(items)][capacity] # Call the get_knapsack_items function to find the items that were included in the knapsack to achieve the maximum value. sequence = get_knapsack_items(values, items) # Return the maximum value and the sequence of items included in the knapsack. return [value] + sequence # get_knapsack_items is a helper function to find the sequence of items included in the knapsack. def get_knapsack_items(values, items): sequence = [] i = len(values) - 1 c = len(values[0]) - 1 # Starting from the bottom-right corner of the values list, # we traverse back to find the items included in the knapsack. while i > 0: if values[i][c] == values[i - 1][c]: # If the value is the same as in the previous row, it means the current item was not included. # So, we move to the previous row without adding the item to the sequence. i -= 1 else: # If the value is greater than the value in the previous row, it means the current item was included. # So, we add the index of the current item (i-1) to the sequence and update the capacity (c) accordingly. sequence.append(i - 1) c -= items[i - 1][1] i -= 1 # If the capacity becomes 0, it means we have included all the items needed to achieve the maximum value. if c == 0: break # The sequence of items is built in reverse order, so we need to reverse it to get the correct order. sequence.reverse() return sequence ================================================ FILE: Dynamic Programming/knight_probability_chessboard.cpp ================================================ /* On an n x n chessboard, a knight starts at the cell (row, column) and attempts to make exactly k moves. The rows and columns are 0-indexed, so the top-left cell is (0, 0), and the bottom-right cell is (n - 1, n - 1). A chess knight has eight possible moves it can make, as illustrated below. Each move is two cells in a cardinal direction, then one cell in an orthogonal direction. Each time the knight is to move, it chooses one of eight possible moves uniformly at random (even if the piece would go off the chessboard) and moves there. The knight continues moving until it has made exactly k moves or has moved off the chessboard. Return the probability that the knight remains on the board after it has stopped moving. Example 1: Input: n = 3, k = 2, row = 0, column = 0 Output: 0.06250 Explanation: There are two moves (to (1,2), (2,1)) that will keep the knight on the board. From each of those positions, there are also two moves that will keep the knight on the board. The total probability the knight stays on the board is 0.0625. Example 2: Input: n = 1, k = 0, row = 0, column = 0 Output: 1.00000 */ #include #include using namespace std; class Solution { public: double knightProbability(int n, int k, int row, int column) { // Create a 3D vector for memoization vector>> dp(k + 1, vector>(n, vector(n, -1.0))); // Call the DFS function to compute the probability return dfs(n, k, row, column, dp); } double dfs(int n, int k, int row, int column, vector>>& dp) { // Base case: Check if the knight goes off the board if (row < 0 || row >= n || column < 0 || column >= n) { return 0.0; } // Base case: If no more moves left, knight remains on the board if (k == 0) { return 1.0; } // If result is already computed, return it if (dp[k][row][column] != -1.0) { return dp[k][row][column]; } double probability = 0.0; int directions[8][2] = {{-2, -1}, {-1, -2}, {-2, 1}, {-1, 2}, {2, -1}, {1, -2}, {2, 1}, {1, 2}}; // Try all 8 possible knight moves for (int i = 0; i < 8; ++i) { int newRow = row + directions[i][0]; int newColumn = column + directions[i][1]; probability += 0.125 * dfs(n, k - 1, newRow, newColumn, dp); } // Memoize the result and return it dp[k][row][column] = probability; return probability; } }; int main() { Solution solution; // Example 1 int n1 = 3, k1 = 2, row1 = 0, column1 = 0; cout << "Output 1: " << solution.knightProbability(n1, k1, row1, column1) << endl; // Example 2 int n2 = 1, k2 = 0, row2 = 0, column2 = 0; cout << "Output 2: " << solution.knightProbability(n2, k2, row2, column2) << endl; return 0; } ================================================ FILE: Dynamic Programming/knight_probability_chessboard.go ================================================ /* On an n x n chessboard, a knight starts at the cell (row, column) and attempts to make exactly k moves. The rows and columns are 0-indexed, so the top-left cell is (0, 0), and the bottom-right cell is (n - 1, n - 1). A chess knight has eight possible moves it can make, as illustrated below. Each move is two cells in a cardinal direction, then one cell in an orthogonal direction. Each time the knight is to move, it chooses one of eight possible moves uniformly at random (even if the piece would go off the chessboard) and moves there. The knight continues moving until it has made exactly k moves or has moved off the chessboard. Return the probability that the knight remains on the board after it has stopped moving. Example 1: Input: n = 3, k = 2, row = 0, column = 0 Output: 0.06250 Explanation: There are two moves (to (1,2), (2,1)) that will keep the knight on the board. From each of those positions, there are also two moves that will keep the knight on the board. The total probability the knight stays on the board is 0.0625. Example 2: Input: n = 1, k = 0, row = 0, column = 0 Output: 1.00000 */ func knightProbability(n int, k int, row int, column int) float64 { // Create a 3D grid to store the probabilities dp := make([][][]float64, n) for i := 0; i < n; i++ { dp[i] = make([][]float64, n) for j := 0; j < n; j++ { dp[i][j] = make([]float64, k+1) } } // Define the eight possible knight moves moves := [][]int{{-2, -1}, {-2, 1}, {-1, -2}, {-1, 2}, {1, -2}, {1, 2}, {2, -1}, {2, 1}} // Set the initial probability of the knight being on the starting cell to 1 dp[row][column][0] = 1.0 // Calculate the probabilities for each move for s := 1; s <= k; s++ { for i := 0; i < n; i++ { for j := 0; j < n; j++ { for _, move := range moves { x := i + move[0] y := j + move[1] // Check if the move is within the chessboard if x >= 0 && x < n && y >= 0 && y < n { // Accumulate the probability for the current cell dp[i][j][s] += dp[x][y][s-1] / 8.0 } } } } } // Calculate the total probability of the knight remaining on the board probability := 0.0 for i := 0; i < n; i++ { for j := 0; j < n; j++ { probability += dp[i][j][k] } } return probability } ================================================ FILE: Dynamic Programming/knight_probability_chessboard.java ================================================ /* On an n x n chessboard, a knight starts at the cell (row, column) and attempts to make exactly k moves. The rows and columns are 0-indexed, so the top-left cell is (0, 0), and the bottom-right cell is (n - 1, n - 1). A chess knight has eight possible moves it can make, as illustrated below. Each move is two cells in a cardinal direction, then one cell in an orthogonal direction. Each time the knight is to move, it chooses one of eight possible moves uniformly at random (even if the piece would go off the chessboard) and moves there. The knight continues moving until it has made exactly k moves or has moved off the chessboard. Return the probability that the knight remains on the board after it has stopped moving. Example 1: Input: n = 3, k = 2, row = 0, column = 0 Output: 0.06250 Explanation: There are two moves (to (1,2), (2,1)) that will keep the knight on the board. From each of those positions, there are also two moves that will keep the knight on the board. The total probability the knight stays on the board is 0.0625. Example 2: Input: n = 1, k = 0, row = 0, column = 0 Output: 1.00000 */ public class KnightProbability { public double knightProbability(int n, int k, int row, int column) { // Create a 3D array for memoization double[][][] dp = new double[k + 1][n][n]; // Call the recursive function to compute the probability return dfs(n, k, row, column, dp); } private double dfs(int n, int k, int row, int column, double[][][] dp) { // Base case: Check if the knight goes off the board if (row < 0 || row >= n || column < 0 || column >= n) { return 0.0; } // Base case: If no more moves left, knight remains on the board if (k == 0) { return 1.0; } // If result is already computed, return it if (dp[k][row][column] != 0.0) { return dp[k][row][column]; } double probability = 0.0; int[][] directions = {{-2, -1}, {-1, -2}, {-2, 1}, {-1, 2}, {2, -1}, {1, -2}, {2, 1}, {1, 2}}; // Try all 8 possible knight moves for (int i = 0; i < 8; ++i) { int newRow = row + directions[i][0]; int newColumn = column + directions[i][1]; probability += 0.125 * dfs(n, k - 1, newRow, newColumn, dp); } // Memoize the result and return it dp[k][row][column] = probability; return probability; } public static void main(String[] args) { KnightProbability solution = new KnightProbability(); // Example 1 int n1 = 3, k1 = 2, row1 = 0, column1 = 0; System.out.println("Output 1: " + solution.knightProbability(n1, k1, row1, column1)); // Example 2 int n2 = 1, k2 = 0, row2 = 0, column2 = 0; System.out.println("Output 2: " + solution.knightProbability(n2, k2, row2, column2)); } } ================================================ FILE: Dynamic Programming/knight_probability_chessboard.js ================================================ /* On an n x n chessboard, a knight starts at the cell (row, column) and attempts to make exactly k moves. The rows and columns are 0-indexed, so the top-left cell is (0, 0), and the bottom-right cell is (n - 1, n - 1). A chess knight has eight possible moves it can make, as illustrated below. Each move is two cells in a cardinal direction, then one cell in an orthogonal direction. Each time the knight is to move, it chooses one of eight possible moves uniformly at random (even if the piece would go off the chessboard) and moves there. The knight continues moving until it has made exactly k moves or has moved off the chessboard. Return the probability that the knight remains on the board after it has stopped moving. Example 1: Input: n = 3, k = 2, row = 0, column = 0 Output: 0.06250 Explanation: There are two moves (to (1,2), (2,1)) that will keep the knight on the board. From each of those positions, there are also two moves that will keep the knight on the board. The total probability the knight stays on the board is 0.0625. Example 2: Input: n = 1, k = 0, row = 0, column = 0 Output: 1.00000 */ var knightProbability = function (n, k, row, column) { // Create a 3D grid to store the probabilities let dp = new Array(n) .fill(0) .map(() => new Array(n).fill(0).map(() => new Array(k + 1).fill(0))); // Define the eight possible knight moves const moves = [ [-2, -1], [-2, 1], [-1, -2], [-1, 2], [1, -2], [1, 2], [2, -1], [2, 1], ]; // Set the initial probability of the knight being on the starting cell to 1 dp[row][column][0] = 1.0; // Calculate the probabilities for each move for (let s = 1; s <= k; s++) { for (let i = 0; i < n; i++) { for (let j = 0; j < n; j++) { for (const move of moves) { const x = i + move[0]; const y = j + move[1]; // Check if the move is within the chessboard if (x >= 0 && x < n && y >= 0 && y < n) { // Accumulate the probability for the current cell dp[i][j][s] += dp[x][y][s - 1] / 8.0; } } } } } // Calculate the total probability of the knight remaining on the board let probability = 0.0; for (let i = 0; i < n; i++) { for (let j = 0; j < n; j++) { probability += dp[i][j][k]; } } return probability; }; ================================================ FILE: Dynamic Programming/knight_probability_chessboard.py ================================================ ''' On an n x n chessboard, a knight starts at the cell (row, column) and attempts to make exactly k moves. The rows and columns are 0-indexed, so the top-left cell is (0, 0), and the bottom-right cell is (n - 1, n - 1). A chess knight has eight possible moves it can make, as illustrated below. Each move is two cells in a cardinal direction, then one cell in an orthogonal direction. Each time the knight is to move, it chooses one of eight possible moves uniformly at random (even if the piece would go off the chessboard) and moves there. The knight continues moving until it has made exactly k moves or has moved off the chessboard. Return the probability that the knight remains on the board after it has stopped moving. Example 1: Input: n = 3, k = 2, row = 0, column = 0 Output: 0.06250 Explanation: There are two moves (to (1,2), (2,1)) that will keep the knight on the board. From each of those positions, there are also two moves that will keep the knight on the board. The total probability the knight stays on the board is 0.0625. Example 2: Input: n = 1, k = 0, row = 0, column = 0 Output: 1.00000 ''' class Solution: def knightProbability(self, n: int, k: int, row: int, column: int) -> float: # Create a 3D grid to store the probabilities dp = [[[0 for _ in range(k + 1)] for _ in range(n)] for _ in range(n)] # Define the eight possible knight moves moves = [(-2, -1), (-2, 1), (-1, -2), (-1, 2), (1, -2), (1, 2), (2, -1), (2, 1)] # Set the initial probability of the knight being on the starting cell to 1 dp[row][column][0] = 1.0 # Calculate the probabilities for each move for s in range(1, k + 1): for i in range(n): for j in range(n): for move in moves: x = i + move[0] y = j + move[1] # Check if the move is within the chessboard if 0 <= x < n and 0 <= y < n: # Accumulate the probability for the current cell dp[i][j][s] += dp[x][y][s - 1] / 8.0 # Calculate the total probability of the knight remaining on the board probability = 0.0 for i in range(n): for j in range(n): probability += dp[i][j][k] return probability ================================================ FILE: Dynamic Programming/kth_closest_point_to origin.java ================================================ /* K Closest Points to Origin Given an array of points where points[i] = [xi, yi] represents a point on the X-Y plane and an integer k, return the k closest points to the origin (0, 0). The distance between two points on the X-Y plane is the Euclidean distance (i.e., √(x1 - x2)2 + (y1 - y2)2). You may return the answer in any order. The answer is guaranteed to be unique (except for the order that it is in). Example 1: Input: points = [[1,3],[-2,2]], k = 1 Output: [[-2,2]] Explanation: The distance between (1, 3) and the origin is sqrt(10). The distance between (-2, 2) and the origin is sqrt(8). Since sqrt(8) < sqrt(10), (-2, 2) is closer to the origin. We only want the closest k = 1 points from the origin, so the answer is just [[-2,2]]. Example 2: Input: points = [[3,3],[5,-1],[-2,4]], k = 2 Output: [[3,3],[-2,4]] Explanation: The answer [[-2,4],[3,3]] would also be accepted. Constraints: 1 <= k <= points.length <= 104 -104 < xi, yi < 104 */ /* SOLUTION: Explanation: Solved this question using priority Queue. Created a priority queue with an Integer.comapre() function. The solution uses a max-heap, implemented as a PriorityQueue, to keep track of the k closest points seen so far. We use the Euclidean distance formula to calculate the distance between each point and the origin, which is given by the square root of the sum of the squares of the coordinates. However, to avoid the expensive square root operation, we square the distance formula and compare the squared distances instead. The PriorityQueue is initialized with a lambda function that compares the squared distances of two points, p1 and p2, using the Integer.compare() method. This lambda function sorts the points in descending order of their squared distances so that the largest squared distance is at the top of the queue. */ //code: class Solution { public int[][] kClosest(int[][] points, int k) { Queue q = new PriorityQueue((p1, p2) -> Integer.compare((p2[0] * p2[0] + p2[1] * p2[1]),(p1[0] * p1[0] + p1[1] * p1[1]))); // Created a priority queue (Implementing max-heap) with a comparison function. for(int i=0; i k){ q.poll(); } } int[][] arr = new int[k][2]; while(k>0){ arr[--k] = q.poll(); /* Creating a 2D array of size K and storing points in non decreasing order of their distance (from ,least-most). */ } return arr; //answer } } ================================================ FILE: Dynamic Programming/largest_rectangle.java ================================================ //Program Author : TheCodeVenturer [Niraj Modi] /* Problem Definition: Dynamic Programming: Given a rows x cols binary matrix filled with 0's and 1's, find the largest rectangle containing only 1's and return its area in Python Approach: This Problem can be termed as updated version of Largest Rectangle in Histogram If you are given a binary matrix then you can use no.of rows together with one as height Like 0 1 1 0 1 1 1 1 1 1 1 1 1 1 0 0 If you can Visualise then it will be clear that for first row the histogram row is like [0,1,1,0] second row the histogram row is like [1,2,2,1] third row the histogram row is like [2,3,3,2] fourth row the histogram row is like [3,4,0,0] then by using a monotonic stack for each row we can get The Largest Rectangle in the Binary Matrix we are taking here a row list which keeps track of current height of a particular column . Here, ShiftRow we are also using a solution variable to keep track of largest rectangle then first we will iterate through each row and inside each iteration we will go and look for the particular element of that row matrix[i][j] if it is 1 then will increase size of jth entry in the shiftRow else will convert it to zero next will initialize an empty Stack [Monotonic] next we will iterate through the shiftRow and will first check for the list is not empty and (it's top element is greater than or equal to current element or value of current column is equal to row size) then will store it's height from the current row array and will update width of the rectangle with stack's top element and will finally update the sol and will insert the element to the stack Complexity: Time Complexity: O(rows * col) for for traversing through each elements of the array Here in each iteration we are doint three times O(n) => O(3n) ~ O(n) Space Complexity: O(n) for the shiftRow and O(n) for the stack we are using => O(2n) ~ O(n) Sample input/outputs: Example 1: Input: [[0,1,1,0],[1,1,1,1],[1,1,1,1],[1,1,0,0]] Output: 8 Example 2: Input: [[0,1,1],[1,1,1],[0,1,1]] Output: 6 */ import java.util.*; public class Solution { public static int maxArea(int[][] matrix, int rows, int cols) { int[] shiftRow = new int[cols]; // initializing the row which updates after each iteration int sol = 0; for (int[] row : matrix) { for (int i = 0; i < row.length; i++) { // Updating the shiftRow if the value of ele is 1 => shiftRow[i] <- shiftRow[i] + 1 // else shiftRow[i] = 0 int ele = row[i]; if (ele == 1) shiftRow[i]++; else shiftRow[i] = 0; } Deque stack = new ArrayDeque<>(); for (int i = 0; i < cols + 1; i++) { while (!stack.isEmpty() && (i == cols || shiftRow[stack.peek()] >= shiftRow[i])) { // checking TOS (top of stack) stack.length - 1 int height = shiftRow[stack.peek()]; // for getting the height of the current index stack.pop(); int width = i; // setting width to i as it is only smallest from the beginning if (!stack.isEmpty()) width = i - stack.peek() - 1; // updating width if the stack is not empty as it is not the smallest element sol = Math.max(height * width, sol); // updating the sol } stack.push(i); // pushing the element's index to the stack } } return sol; } public static void main(String[] args) { int[][] matrix = { {0, 1, 1, 0}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 0, 0} }; System.out.println(maxArea(matrix, 4, 4)); } } ================================================ FILE: Dynamic Programming/largest_rectangle.js ================================================ //Program Author : TheCodeVenturer [Niraj Modi] /* Problem Definition: Dynamic Programming: Given a rows x cols binary matrix filled with 0's and 1's, find the largest rectangle containing only 1's and return its area in Python Approach: This Problem can be termed as updated version of Largest Rectangle in Histogram If you are given a binary matrix then you can use no.of rows together with one as height Like 0 1 1 0 1 1 1 1 1 1 1 1 1 1 0 0 If you can Visualise then it will be clear that for first row the histogram row is like [0,1,1,0] second row the histogram row is like [1,2,2,1] third row the histogram row is like [2,3,3,2] fourth row the histogram row is like [3,4,0,0] then by using a monotonic stack for each row we can get The Largest Rectangle in the Binary Matrix we are taking here a row list which keeps track of current height of a particular column . Here, ShiftRow we are also using a solution variable to keep track of largest rectangle then first we will iterate through each row and inside each iteration we will go and look for the particular element of that row matrix[i][j] if it is 1 then will increase size of jth entry in the shiftRow else will convert it to zero next will initialize an empty Stack [Monotonic] next we will iterate through the shiftRow and will first check for the list is not empty and (it's top element is greater than or equal to current element or value of current column is equal to row size) then will store it's height from the current row array and will update width of the rectangle with stack's top element and will finally update the sol and will insert the element to the stack Complexity: Time Complexity: O(rows * col) for for traversing through each elements of the array Here in each iteration we are doint three times O(n) => O(3n) ~ O(n) Space Complexity: O(n) for the shiftRow and O(n) for the stack we are using => O(2n) ~ O(n) Sample input/outputs: Example 1: Input: [[0,1,1,0],[1,1,1,1],[1,1,1,1],[1,1,0,0]] Output: 8 Example 2: Input: [[0,1,1],[1,1,1],[0,1,1]] Output: 6 */ var maxArea = function (matrix, rows, cols) { let shiftRow = new Array(cols).fill(0); //initialising the row which update after each iteration var sol = 0; for (let row of matrix) { for (let i = 0; i < row.length; i++) { // Updating the shiftRow if value of ele is 1 => ShiftRow[i] <- shiftRow[i]+1 // else shiftRow[i]=0 var ele = row[i]; if (ele == 1) shiftRow[i]++; else shiftRow[i] = 0; } st = []; for (let i = 0; i < cols + 1; i++) { while (st.length > 0 &&(i == cols || shiftRow[st[st.length - 1]] >= shiftRow[i])) { //checking TOS st.length-1 height = shiftRow[st[st.length - 1]]; //for getting height of Current index st.pop(); width = i; // setting width to i as it is only smallest from beginning if (st.length > 0) width = i - st[st.length - 1] - 1; // updating width is stack is not empty as it is not the smallest element sol = Math.max(height * width, sol); // Updating the sol } st.push(i); // Pushing the Element's index to the stack } } return sol; }; var matrix = [ [0, 1, 1, 0], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 0, 0], ]; console.log(maxArea(matrix, 4, 4)); ================================================ FILE: Dynamic Programming/largest_rectangle.py ================================================ #Program Author : TheCodeVenturer [Niraj Modi] ''' Problem Definition: Dynamic Programming: Given a rows x cols binary matrix filled with 0's and 1's, find the largest rectangle containing only 1's and return its area in Python Approach: This Problem can be termed as updated version of Largest Rectangle in Histogram If you are given a binary matrix then you can use no.of rows together with one as height Like 0 1 1 0 1 1 1 1 1 1 1 1 1 1 0 0 If you can Visualise then it will be clear that for first row the histogram row is like [0,1,1,0] second row the histogram row is like [1,2,2,1] third row the histogram row is like [2,3,3,2] fourth row the histogram row is like [3,4,0,0] then by using a monotonic stack for each row we can get The Largest Rectangle in the Binary Matrix we are taking here a row list which keeps track of current height of a particular column . Here, ShiftRow we are also using a solution variable to keep track of largest rectangle then first we will iterate through each row and inside each iteration we will go and look for the particular element of that row matrix[i][j] if it is 1 then will increase size of jth entry in the shiftRow else will convert it to zero next will initialize an empty Stack [Monotonic] next we will iterate through the shiftRow and will first check for the list is not empty and (it's top element is greater than or equal to current element or value of current column is equal to row size) then will store it's height from the current row array and will update width of the rectangle with stack's top element and will finally update the sol and will insert the element to the stack Complexity: Time Complexity: O(rows * col) for for traversing through each elements of the array Here in each iteration we are doint three times O(n) => O(3n) ~ O(n) Space Complexity: O(n) for the shiftRow and O(n) for the stack we are using => O(2n) ~ O(n) Sample input/outputs: Example 1: Input: [[0,1,1,0],[1,1,1,1],[1,1,1,1],[1,1,0,0]] Output: 8 Example 2: Input: [[0,1,1],[1,1,1],[0,1,1]] Output: 6 ''' class Solution: def maxArea(self,matrix, rows, cols): shiftRow = [0]*cols #initialising the row which update after each iteration sol=0 for row in matrix: for i,ele in enumerate(row): #used enumerate as it will give index as well as element # Updating the shiftRow if value of ele is 1 => ShiftRow[i] <- shiftRow[i]+1 # else shiftRow[i]=0 if ele==1: shiftRow[i]+=1 else: shiftRow[i]=0 st = [] for i in range(cols+1): while(len(st)>0 and(i==cols or shiftRow[st[-1]]>=shiftRow[i])): height = shiftRow[st[-1]] #for getting height of Current index st.pop() width = i # setting width to i as it is only smallest from beginning if(len(st)>0): width = i - st[-1] - 1 # updating width is stack is not empty as it is not the smallest element sol = max(height*width,sol) # Updating the sol st.append(i) # Pushing the Element's index to the stack return sol if __name__ == '__main__': matrix = [[0,1,1,0],[1,1,1,1],[1,1,1,1],[1,1,0,0]] print(Solution().maxArea(matrix, 4, 4)) ================================================ FILE: Dynamic Programming/longest_common_subsequence_dp.cpp ================================================ // DP : Find Longest Common Subsequence of two string // Program Author : Abhisek Kumar Gupta #include using namespace std; const int maxi = 10000; int find_longest_common_subsequence(string a, string b, int n, int m, int dp[][maxi]){ for(int i = 0; i <= n; i++){ dp[i][0] = 0; } for(int i = 0; i <= m; i++){ dp[0][i] = 0; } // dp solution builds the table of LCS of substrings and starts // computing the length building on the final solution // we place one string along row and one along the column for(int i = 1; i <= n; i++){ for(int j = 1; j <= m; j++){ // populating table in row-wise order if(a[i - 1] == b[j - 1]){ dp[i][j] = dp[i - 1][j - 1] + 1; } else{ dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); } } } return dp[m][n]; } int main(){ string a = "DABCEFIHKST"; string b = "DCEFKAAAQST"; int n = a.length(); int m = b.length(); int dp[n+1][maxi]; memset(dp, -1, sizeof(dp)); int result = find_longest_common_subsequence(a, b, n, m, dp); cout << result; return 0; } // Time complexity O(N * M) improvement over both // Recursion and memoization // Space complexity O(N * M) ================================================ FILE: Dynamic Programming/longest_common_subsequence_memoization.cpp ================================================ // Memoization : Find Longest Common Subsequence of two string // Program Author : Abhisek Kumar Gupta /* To avoid computation of subproblem many times we use memoization here we take 2D array of size n*m memoization[n][m] when length of lcs of first i characters of a and j characters of b is computed for first time it is stored in cell memoization[i][j] if function is called with n = i and m = j then LCS is not computed from scratch and stored value is returned from table */ #include using namespace std; const int maxi = 10000; int get_max(int x, int y){ return (x > y) ? x : y; } int find_longest_common_subsequence(string a, string b, int n, int m, int memoization[][maxi]){ if(m == 0 || n == 0) return 0; // if value is already computed then return the value if(memoization[n][m] != -1){ return memoization[n][m]; } if(a[n - 1] == b[m - 1]){ // memoize the solution in order to avoid recomputation memoization[n][m] = 1 + find_longest_common_subsequence(a, b, n - 1, m - 1, memoization); } else{ // memoize the solution in order to avoid recomputation memoization[n][m] = get_max(find_longest_common_subsequence(a, b, n - 1, m, memoization), find_longest_common_subsequence(a, b, n, m -1, memoization)); } return memoization[n][m]; } int main(){ string a = "DABCEFIHKST"; string b = "DCEFKAAAQST"; int n = a.length(); int m = b.length(); int memoization[n+1][maxi]; memset(memoization, -1, sizeof(memoization)); int result = find_longest_common_subsequence(a, b, n, m, memoization); cout << result; return 0; } // Time complexity from exponential to polynomial O(N * M) // Space complexity O(N * M) ================================================ FILE: Dynamic Programming/longest_common_subsequence_recursive.cpp ================================================ // Recursion : Find Longest Common Subsequence of two string // Program Author : Abhisek Kumar Gupta /* This problem exemplify optimal substructure property and the bigger problem can be defined in terms of mini subprobs of same type hence recursion */ #include using namespace std; int get_max(int x, int y){ return (x > y) ? x : y; } int find_longest_common_subsequence(string a, string b, int n, int m){ if(m == 0 || n == 0) return 0; // we start by comparing last two characters of strings and there are two // possibilities // 1) both are same [means we already have found one character in LCS so // add 1 and make recursive call with modified strings] if(a[n - 1] == b[m - 1]){ return 1 + find_longest_common_subsequence(a, b, n-1, m-1); } // 2) both are different [find length of tow lcs, first haveing n-1 character from first string // and m characters from second string, and another with m characters from first string and // m - 1 characters from second string and return the max of two] else{ return get_max(find_longest_common_subsequence(a, b, n-1,m), find_longest_common_subsequence(a, b, n, m-1)); } } int main(){ string a = "ABCEFIHST"; string b = "DCEFKAAAQST"; int n = a.length(); int m = b.length(); int result = find_longest_common_subsequence(a, b, n, m); cout << result; } // Code takes exponential time 2^n in the worst case i.e when all // two characters are different // ACB | ABC // ACB | AB AC | ABC // AC | A A | AB // A | A AC | A | A | AB // BECAUSE WE ARE SOLVING ONE SUB PROBLEM MULTIPLE TIMES SO LCS PROBLEM // DEMONSTRATES OPTIMAL SUBSTRUCTURE PROPERTY AND THERE ARE OVERLAPPING // SUBPROBLEMS TOO, SO WE WILL USE MEMOIZATION / DP TO SOLVE THIS PROBLEM // OPTIMALLY ================================================ FILE: Dynamic Programming/longest_increasing_subsequence.cpp ================================================ /* Longest Increasing Subsequence Input : 1 2 1 3 1 4 Output : 4 */ // Dynamic Programming Approach : TC O(n^2) // Program Author : Abhisek Kumar Gupta #include using namespace std; int find_longest_increasing_subsequence(vector V, int n){ int dp[1004]; for(int i = 0; i < 1000; i++) dp[i] = 1; int best = INT_MIN; for(int i = 1; i < n; i++){ for(int j = 0; j < i; j++){ if(V[j] <= V[i]){ // this means its in increasing order and we can take value stored at dp[j] and add 1 to it int curr_len = 1 + dp[j]; dp[i] = max(curr_len, dp[i]); } } best = max(dp[i], best); } return best; } int main(){ int n; cout << "Enter a number"; cin >> n; vector V(n); for(int i = 0; i < n; i++){ cin >> V[i]; } int result = find_longest_increasing_subsequence(V, n); cout << result; } ================================================ FILE: Dynamic Programming/longest_pallindromic_substring.cpp ================================================ /* Given a string s, return the longest palindromic substring in s. Example 1: Input: s = "babad" Output: "bab" Explanation: "aba" is also a valid answer. Example 2: Input: s = "cbbd" Output: "bb" Constraints: 1 <= s.length <= 1000 s consist of only digits and English letters. */ #include #include #include // Function to find the length of the longest palindromic substring int longestPalindromicSubstring(const std::string& Array) { int n = Array.length(); // Create a 2D boolean array L to store whether substrings are palindromes. std::vector> L(n, std::vector(n, false)); int max_length = 1; // Initialize the maximum palindrome length to 1 (single characters are palindromes) // Initialize the base cases for substrings of length 1 and 2. for (int i = 0; i < n - 1; i++) { L[i][i] = true; // Single characters are palindromes if (Array[i] == Array[i + 1]) { L[i][i + 1] = true; // Check for palindromes of length 2 max_length = 2; // Update the maximum palindrome length } } // Check for palindromes of length 3 and greater. for (int k = 3; k <= n; k++) { for (int i = 0; i < n - k + 1; i++) { int j = i + k - 1; // Check if the characters at the ends of the current substring match // and if the substring inside is a palindrome. if (Array[i] == Array[j] && L[i + 1][j - 1]) { L[i][j] = true; // Mark the current substring as a palindrome max_length = k; // Update the maximum palindrome length } } } return max_length; // Return the length of the longest palindromic substring } int main() { // Example usage: std::string input = "babad"; int result = longestPalindromicSubstring(input); std::cout << result << std::endl; // Output: 3 ("bab" or "aba" is the longest palindromic substring) return 0; } ================================================ FILE: Dynamic Programming/longest_pallindromic_substring.go ================================================ /* Given a string s, return the longest palindromic substring in s. Example 1: Input: s = "babad" Output: "bab" Explanation: "aba" is also a valid answer. Example 2: Input: s = "cbbd" Output: "bb" Constraints: 1 <= s.length <= 1000 s consist of only digits and English letters. */ package main import ( "fmt" ) // LongestPalindromicSubstring finds the length of the longest palindromic substring in a given string. func LongestPalindromicSubstring(Array string) int { n := len(Array) // Create a 2D boolean array L to store whether substrings are palindromes. L := make([][]bool, n) for i := range L { L[i] = make([]bool, n) // Defaults to false } max := 1 // Initialize the maximum palindrome length to 1 (single characters are palindromes) // Initialize the base cases for substrings of length 1 and 2. for i := 0; i < n-1; i++ { L[i][i] = true // Single characters are palindromes if Array[i] == Array[i+1] { L[i][i+1] = true // Check for palindromes of length 2 max = 2 // Update the maximum palindrome length } } // Check for palindromes of length 3 and greater. for k := 3; k <= n; k++ { for i := 0; i < n-k+1; i++ { j := i + k - 1 // Check if the characters at the ends of the current substring match and if the substring inside is a palindrome. if Array[i] == Array[j] && L[i+1][j-1] { L[i][j] = true // Mark the current substring as a palindrome max = k // Update the maximum palindrome length } else { L[i][j] = false // Mark the current substring as not a palindrome } } } return max // Return the length of the longest palindromic substring } func main() { // Example usage: fmt.Print(LongestPalindromicSubstring("babad")) } ================================================ FILE: Dynamic Programming/longest_pallindromic_substring.java ================================================ /* Given a string s, return the longest palindromic substring in s. Example 1: Input: s = "babad" Output: "bab" Explanation: "aba" is also a valid answer. Example 2: Input: s = "cbbd" Output: "bb" Constraints: 1 <= s.length <= 1000 s consist of only digits and English letters. */ public class LongestPalindromicSubstring { // Function to find the length of the longest palindromic substring public static int longestPalindromicSubstring(String Array) { int n = Array.length(); // Create a 2D boolean array L to store whether substrings are palindromes. boolean[][] L = new boolean[n][n]; int max_length = 1; // Initialize the maximum palindrome length to 1 (single characters are palindromes) // Initialize the base cases for substrings of length 1 and 2. for (int i = 0; i < n - 1; i++) { L[i][i] = true; // Single characters are palindromes if (Array.charAt(i) == Array.charAt(i + 1)) { L[i][i + 1] = true; // Check for palindromes of length 2 max_length = 2; // Update the maximum palindrome length } } // Check for palindromes of length 3 and greater. for (int k = 3; k <= n; k++) { for (int i = 0; i < n - k + 1; i++) { int j = i + k - 1; // Check if the characters at the ends of the current substring match // and if the substring inside is a palindrome. if (Array.charAt(i) == Array.charAt(j) && L[i + 1][j - 1]) { L[i][j] = true; // Mark the current substring as a palindrome max_length = k; // Update the maximum palindrome length } } } return max_length; // Return the length of the longest palindromic substring } public static void main(String[] args) { // Example usage: String input = "babad"; int result = longestPalindromicSubstring(input); System.out.println(result); // Output: 3 ("bab" or "aba" is the longest palindromic substring) } } ================================================ FILE: Dynamic Programming/longest_pallindromic_substring.js ================================================ /* Given a string s, return the longest palindromic substring in s. Example 1: Input: s = "babad" Output: "bab" Explanation: "aba" is also a valid answer. Example 2: Input: s = "cbbd" Output: "bb" Constraints: 1 <= s.length <= 1000 s consist of only digits and English letters. */ function longestPalindromicSubstring(Array) { const n = Array.length; // Create a 2D boolean array L to store whether substrings are palindromes. const L = Array.from({ length: n }, () => Array(n).fill(false)); let max_length = 1; // Initialize the maximum palindrome length to 1 (single characters are palindromes) // Initialize the base cases for substrings of length 1 and 2. for (let i = 0; i < n - 1; i++) { L[i][i] = true; // Single characters are palindromes if (Array[i] === Array[i + 1]) { L[i][i + 1] = true; // Check for palindromes of length 2 max_length = 2; // Update the maximum palindrome length } } // Check for palindromes of length 3 and greater. for (let k = 3; k <= n; k++) { for (let i = 0; i < n - k + 1; i++) { const j = i + k - 1; // Check if the characters at the ends of the current substring match // and if the substring inside is a palindrome. if (Array[i] === Array[j] && L[i + 1][j - 1]) { L[i][j] = true; // Mark the current substring as a palindrome max_length = k; // Update the maximum palindrome length } } } return max_length; // Return the length of the longest palindromic substring } // Example usage: const result = longestPalindromicSubstring("babad"); console.log(result); // Output: 3 ("bab" or "aba" is the longest palindromic substring) ================================================ FILE: Dynamic Programming/longest_pallindromic_substring.py ================================================ ''' Given a string s, return the longest palindromic substring in s. Example 1: Input: s = "babad" Output: "bab" Explanation: "aba" is also a valid answer. Example 2: Input: s = "cbbd" Output: "bb" Constraints: 1 <= s.length <= 1000 s consist of only digits and English letters. ''' def longest_palindromic_substring(Array): n = len(Array) # Create a 2D boolean array L to store whether substrings are palindromes. L = [[False] * n for _ in range(n)] max_length = 1 # Initialize the maximum palindrome length to 1 (single characters are palindromes) # Initialize the base cases for substrings of length 1 and 2. for i in range(n - 1): L[i][i] = True # Single characters are palindromes if Array[i] == Array[i + 1]: L[i][i + 1] = True # Check for palindromes of length 2 max_length = 2 # Update the maximum palindrome length # Check for palindromes of length 3 and greater. for k in range(3, n + 1): for i in range(n - k + 1): j = i + k - 1 # Check if the characters at the ends of the current substring match # and if the substring inside is a palindrome. if Array[i] == Array[j] and L[i + 1][j - 1]: L[i][j] = True # Mark the current substring as a palindrome max_length = k # Update the maximum palindrome length return max_length # Return the length of the longest palindromic substring # Example usage: result = longest_palindromic_substring("babad") print(result) # Output: 3 ("bab" or "aba" is the longest palindromic substring) ================================================ FILE: Dynamic Programming/max_path_sum.go ================================================ /// How would you approach finding the maximum path sum in a binary tree using dynamic programming in Go? // Provide a sample input and output, explain your approach using comments, // and analyze the time and space complexity of your solution. // sample input: 1 // / \ // 2 3 // sample output: Maximum Path Sum: 6 /// Explanation : // 1. Implement a function that calculates the maximum path sum in a binary tree using dynamic programming. // 2. Create a struct treeNode to represent each node in the binary tree, including a Val field for the node's value, Left field for the left child, and Right field for the right child. // 3. Define a helper function pathMaxSum that takes the root node as input and returns the maximum path sum. // 4 Within maxPathSum, initialize a variable maxSum with the minimum integer value to track the maximum path sum. // 5. Call the recursive helper function findMaxSum and pass the root node and the maxSum variable as arguments. // 6. In the findMaxSum function, handle the base case: if the node is nil, return 0. // 7. Recursively find the maximum path sum for the left and right subtrees by calling findMaxSum on the left and right children of the current node. // 8. Calculate the maximum path sum that includes the current node: // Check if the left sum is negative (less than 0), assign it as 0. // Check if the right sum is negative (less than 0), assign it as 0. // Update the maxSum by comparing it with the sum of the current node's value, left sum, and right sum.// // 9. Return the maximum path sum including the current node (either the current node's value plus the maximum of left or right sum). // 10. Finally, return the maxSum from the pathMaxSum function. // 11. In the main function, create a sample binary tree and call pathMaxSum to find the maximum path sum. Print the result. // Time Complexity: The time complexity of this solution is O(N), where N is the number of nodes in the binary tree, as we need to traverse each node once. // Space Complexity: The space complexity is O(H), where H is the height of the binary tree. This is due to the recursive calls on the stack, which can go up to the height of the tree. package main import ( "fmt" "math" ) type treenode struct { Val int Left *treenode Right *treenode } // Function to find the maximum path sum in a binary tree func pathMaxSum(root *treenode) int{ maxSum := math.MinInt32 findMaxSum(root, &maxSum) return maxSum } // finding the maximum path sum starting from a given node func findMaxSum(node *treenode, maxSum *int) int { if node == nil { return 0 } leftSum := max(0, findMaxSum(node.Left,maxSum)) rightSum := max(0,findMaxSum(node.Right,maxSum)) *maxSum = max(*maxSum,node.Val+leftSum+rightSum) // returning the max sum plus the current node return node.Val + max(leftSum,rightSum) } // function to find the maximum of two numbers func max(a,b int) int { if a >b { return a } return b } func main() { // creating sample binary tree root := &treenode{Val: 1} root.Left = &treenode{Val: 2} root.Right = &treenode{Val: 3} // Calculate the maximum path sum maxSum := pathMaxSum(root) fmt.Println("Maximum Path Sum:", maxSum) } ================================================ FILE: Dynamic Programming/max_sum_increasing_subsequence.cpp ================================================ /* Write a function that takes in a non-empty array of integers and returns the greatest sum that can be generated from a strictly-increasing subsequence in the array as well as an array of the numbers in that subsequence. Sample Input: = [10, 70, 20, 30, 50, 11, 30] Output : [110, [10, 20, 30, 50]] Explanation: The given code snippet implements a function called `MaxSumIncreasingSubsequence`, which finds the maximum sum increasing subsequence in a given array of integers. An increasing subsequence is a sequence of array elements where each element is strictly greater than the previous element. Here's a step-by-step explanation of the code: 1. The function `MaxSumIncreasingSubsequence` takes an input array of integers called `array`. 2. Two arrays `sums` and `sequences` are initialized with the same length as the input array. The `sums` array stores the maximum sum of increasing subsequences ending at the corresponding index, and the `sequences` array stores the previous index that contributes to the maximum sum at the current index. 3. The `maxSumIndex` variable is used to keep track of the index with the maximum sum of an increasing subsequence. 4. The code uses a dynamic programming approach to calculate the maximum sum of increasing subsequences. It iterates through the input array from left to right and, for each element, checks all the previous elements to find the ones that are less than the current element and can form an increasing subsequence with it. If a better sum is found for the current element, it updates the `sums` and `sequences` arrays. 5. After iterating through the entire array, the `maxSumIndex` stores the index with the maximum sum of an increasing subsequence. 6. The function `buildSequence` is used to reconstruct the actual increasing subsequence from the `sequences` array, starting from the `maxSumIndex` and going backward until it reaches an element with a value of `math.MinInt32`, which is used as a sentinel value to indicate the end of the sequence. 7. The `reverse` function is a helper function used to reverse the elements in the `sequence` array since the subsequence was built backward. 8. The function returns the maximum sum of the increasing subsequence (`sum`) and the subsequence itself (`sequence`). O(n^2) time | O(n) space - where n is the length of the input array */ #include #include std::pair> MaxSumIncreasingSubsequence(std::vector& array) { int n = array.size(); std::vector sums(n); // Store the maximum increasing sum up to index i. std::vector sequences(n, -1); // Store the previous index of the increasing subsequence. for (int i = 0; i < n; i++) { sums[i] = array[i]; for (int j = 0; j < i; j++) { if (array[i] > array[j] && sums[j] + array[i] > sums[i]) { sums[i] = sums[j] + array[i]; sequences[i] = j; } } } // Find the index of the maximum sum in 'sums'. int maxSumIndex = std::max_element(sums.begin(), sums.end()) - sums.begin(); int maxSum = sums[maxSumIndex]; // Build the increasing subsequence using the 'sequences' array. std::vector sequence; while (maxSumIndex != -1) { sequence.push_back(array[maxSumIndex]); maxSumIndex = sequences[maxSumIndex]; } std::reverse(sequence.begin(), sequence.end()); return std::make_pair(maxSum, sequence); } ================================================ FILE: Dynamic Programming/max_sum_increasing_subsequence.go ================================================ /* Write a function that takes in a non-empty array of integers and returns the greatest sum that can be generated from a strictly-increasing subsequence in the array as well as an array of the numbers in that subsequence. Sample Input: = [10, 70, 20, 30, 50, 11, 30] Output : [110, [10, 20, 30, 50]] Explanation: The given code snippet implements a function called `MaxSumIncreasingSubsequence`, which finds the maximum sum increasing subsequence in a given array of integers. An increasing subsequence is a sequence of array elements where each element is strictly greater than the previous element. Here's a step-by-step explanation of the code: 1. The function `MaxSumIncreasingSubsequence` takes an input array of integers called `array`. 2. Two arrays `sums` and `sequences` are initialized with the same length as the input array. The `sums` array stores the maximum sum of increasing subsequences ending at the corresponding index, and the `sequences` array stores the previous index that contributes to the maximum sum at the current index. 3. The `maxSumIndex` variable is used to keep track of the index with the maximum sum of an increasing subsequence. 4. The code uses a dynamic programming approach to calculate the maximum sum of increasing subsequences. It iterates through the input array from left to right and, for each element, checks all the previous elements to find the ones that are less than the current element and can form an increasing subsequence with it. If a better sum is found for the current element, it updates the `sums` and `sequences` arrays. 5. After iterating through the entire array, the `maxSumIndex` stores the index with the maximum sum of an increasing subsequence. 6. The function `buildSequence` is used to reconstruct the actual increasing subsequence from the `sequences` array, starting from the `maxSumIndex` and going backward until it reaches an element with a value of `math.MinInt32`, which is used as a sentinel value to indicate the end of the sequence. 7. The `reverse` function is a helper function used to reverse the elements in the `sequence` array since the subsequence was built backward. 8. The function returns the maximum sum of the increasing subsequence (`sum`) and the subsequence itself (`sequence`). O(n^2) time | O(n) space - where n is the length of the input array */ package main import "math" // MaxSumIncreasingSubsequence finds the maximum sum increasing subsequence in the given array of integers. func MaxSumIncreasingSubsequence(array []int) (int, []int) { // Initialize two arrays to store maximum sums and the previous elements contributing to the sums. sums := make([]int, len(array)) sequences := make([]int, len(array)) // Initialize each element in 'sums' array to its corresponding element in the input array. // Also, initialize each element in 'sequences' array to a sentinel value 'math.MinInt32'. for i := range sequences { sequences[i] = math.MinInt32 sums[i] = array[i] } // Variable to keep track of the index with the maximum sum. maxSumIndex := 0 // Iterate through the input array and calculate the maximum sum increasing subsequences. for i, currNum := range array { for j := 0; j < i; j++ { otherNum := array[j] if otherNum < currNum && currNum+sums[j] >= sums[i] { // If the current element can extend the increasing subsequence with a better sum, // update the 'sums' and 'sequences' arrays accordingly. sums[i] = currNum + sums[j] sequences[i] = j } } // Update the index with the maximum sum if the current sum is greater. if sums[i] > sums[maxSumIndex] { maxSumIndex = i } } // Get the maximum sum from the 'sums' array and the increasing subsequence from the 'sequences' array. sum := sums[maxSumIndex] sequence := buildSequence(array, sequences, maxSumIndex) return sum, sequence } // buildSequence reconstructs the increasing subsequence from the 'sequences' array. func buildSequence(array []int, sequences []int, index int) []int { sequence := []int{} // Traverse the 'sequences' array starting from the 'index' until reaching the sentinel value 'math.MinInt32'. for index != math.MinInt32 { // Add the element at the current index to the 'sequence' array. sequence = append(sequence, array[index]) // Move to the previous index using 'sequences' array. index = sequences[index] } // Reverse the 'sequence' array since it was built backward. reverse(sequence) return sequence } // reverse is a helper function used to reverse the elements in the 'sequence' array. func reverse(numbers []int) { for i, j := 0, len(numbers)-1; i < j; i, j = i+1, j-1 { numbers[i], numbers[j] = numbers[j], numbers[i] } } ================================================ FILE: Dynamic Programming/max_sum_increasing_subsequence.java ================================================ /* Write a function that takes in a non-empty array of integers and returns the greatest sum that can be generated from a strictly-increasing subsequence in the array as well as an array of the numbers in that subsequence. Sample Input: = [10, 70, 20, 30, 50, 11, 30] Output : [110, [10, 20, 30, 50]] Explanation: The given code snippet implements a function called `MaxSumIncreasingSubsequence`, which finds the maximum sum increasing subsequence in a given array of integers. An increasing subsequence is a sequence of array elements where each element is strictly greater than the previous element. Here's a step-by-step explanation of the code: 1. The function `MaxSumIncreasingSubsequence` takes an input array of integers called `array`. 2. Two arrays `sums` and `sequences` are initialized with the same length as the input array. The `sums` array stores the maximum sum of increasing subsequences ending at the corresponding index, and the `sequences` array stores the previous index that contributes to the maximum sum at the current index. 3. The `maxSumIndex` variable is used to keep track of the index with the maximum sum of an increasing subsequence. 4. The code uses a dynamic programming approach to calculate the maximum sum of increasing subsequences. It iterates through the input array from left to right and, for each element, checks all the previous elements to find the ones that are less than the current element and can form an increasing subsequence with it. If a better sum is found for the current element, it updates the `sums` and `sequences` arrays. 5. After iterating through the entire array, the `maxSumIndex` stores the index with the maximum sum of an increasing subsequence. 6. The function `buildSequence` is used to reconstruct the actual increasing subsequence from the `sequences` array, starting from the `maxSumIndex` and going backward until it reaches an element with a value of `math.MinInt32`, which is used as a sentinel value to indicate the end of the sequence. 7. The `reverse` function is a helper function used to reverse the elements in the `sequence` array since the subsequence was built backward. 8. The function returns the maximum sum of the increasing subsequence (`sum`) and the subsequence itself (`sequence`). O(n^2) time | O(n) space - where n is the length of the input array */ import java.util.Arrays; import java.util.List; import java.util.ArrayList; public class Main { public static int[] maxSumIncreasingSubsequence(int[] array) { int n = array.length; int[] sums = Arrays.copyOf(array, n); // Store the maximum increasing sum up to index i. int[] sequences = new int[n]; Arrays.fill(sequences, -1); // Store the previous index of the increasing subsequence. for (int i = 0; i < n; i++) { for (int j = 0; j < i; j++) { if (array[i] > array[j] && sums[j] + array[i] > sums[i]) { sums[i] = sums[j] + array[i]; sequences[i] = j; } } } // Find the index of the maximum sum in 'sums'. int maxSumIndex = 0; for (int i = 1; i < n; i++) { if (sums[i] > sums[maxSumIndex]) { maxSumIndex = i; } } int maxSum = sums[maxSumIndex]; // Build the increasing subsequence using the 'sequences' array. List sequenceList = new ArrayList<>(); while (maxSumIndex != -1) { sequenceList.add(array[maxSumIndex]); maxSumIndex = sequences[maxSumIndex]; } // Convert the list to an array and reverse it to get the correct order. int[] sequence = new int[sequenceList.size()]; for (int i = 0; i < sequence.length; i++) { sequence[i] = sequenceList.get(sequenceList.size() - i - 1); } return new int[]{maxSum, sequence}; } public static void main(String[] args) { int[] array = {4, 6, 1, 3, 8, 4, 6}; int[] result = maxSumIncreasingSubsequence(array); System.out.println("Max Sum: " + result[0]); System.out.println("Increasing Subsequence: " + Arrays.toString(result[1])); } } ================================================ FILE: Dynamic Programming/max_sum_increasing_subsequence.js ================================================ /* Write a function that takes in a non-empty array of integers and returns the greatest sum that can be generated from a strictly-increasing subsequence in the array as well as an array of the numbers in that subsequence. Sample Input: = [10, 70, 20, 30, 50, 11, 30] Output : [110, [10, 20, 30, 50]] Explanation: The given code snippet implements a function called `MaxSumIncreasingSubsequence`, which finds the maximum sum increasing subsequence in a given array of integers. An increasing subsequence is a sequence of array elements where each element is strictly greater than the previous element. Here's a step-by-step explanation of the code: 1. The function `MaxSumIncreasingSubsequence` takes an input array of integers called `array`. 2. Two arrays `sums` and `sequences` are initialized with the same length as the input array. The `sums` array stores the maximum sum of increasing subsequences ending at the corresponding index, and the `sequences` array stores the previous index that contributes to the maximum sum at the current index. 3. The `maxSumIndex` variable is used to keep track of the index with the maximum sum of an increasing subsequence. 4. The code uses a dynamic programming approach to calculate the maximum sum of increasing subsequences. It iterates through the input array from left to right and, for each element, checks all the previous elements to find the ones that are less than the current element and can form an increasing subsequence with it. If a better sum is found for the current element, it updates the `sums` and `sequences` arrays. 5. After iterating through the entire array, the `maxSumIndex` stores the index with the maximum sum of an increasing subsequence. 6. The function `buildSequence` is used to reconstruct the actual increasing subsequence from the `sequences` array, starting from the `maxSumIndex` and going backward until it reaches an element with a value of `math.MinInt32`, which is used as a sentinel value to indicate the end of the sequence. 7. The `reverse` function is a helper function used to reverse the elements in the `sequence` array since the subsequence was built backward. 8. The function returns the maximum sum of the increasing subsequence (`sum`) and the subsequence itself (`sequence`). O(n^2) time | O(n) space - where n is the length of the input array */ function maxSumIncreasingSubsequence(array) { const n = array.length; const sums = [...array]; // Store the maximum increasing sum up to index i. const sequences = new Array(n).fill(-1); // Store the previous index of the increasing subsequence. for (let i = 0; i < n; i++) { for (let j = 0; j < i; j++) { if (array[i] > array[j] && sums[j] + array[i] > sums[i]) { sums[i] = sums[j] + array[i]; sequences[i] = j; } } } // Find the index of the maximum sum in 'sums'. let maxSumIndex = 0; for (let i = 1; i < n; i++) { if (sums[i] > sums[maxSumIndex]) { maxSumIndex = i; } } const maxSum = sums[maxSumIndex]; // Build the increasing subsequence using the 'sequences' array. const sequence = []; while (maxSumIndex !== -1) { sequence.push(array[maxSumIndex]); maxSumIndex = sequences[maxSumIndex]; } sequence.reverse(); return [maxSum, sequence]; } ================================================ FILE: Dynamic Programming/max_sum_increasing_subsequence.py ================================================ ''' Write a function that takes in a non-empty array of integers and returns the greatest sum that can be generated from a strictly-increasing subsequence in the array as well as an array of the numbers in that subsequence. Sample Input: = [10, 70, 20, 30, 50, 11, 30] Output : [110, [10, 20, 30, 50]] Explanation: The given code snippet implements a function called `MaxSumIncreasingSubsequence`, which finds the maximum sum increasing subsequence in a given array of integers. An increasing subsequence is a sequence of array elements where each element is strictly greater than the previous element. Here's a step-by-step explanation of the code: 1. The function `MaxSumIncreasingSubsequence` takes an input array of integers called `array`. 2. Two arrays `sums` and `sequences` are initialized with the same length as the input array. The `sums` array stores the maximum sum of increasing subsequences ending at the corresponding index, and the `sequences` array stores the previous index that contributes to the maximum sum at the current index. 3. The `maxSumIndex` variable is used to keep track of the index with the maximum sum of an increasing subsequence. 4. The code uses a dynamic programming approach to calculate the maximum sum of increasing subsequences. It iterates through the input array from left to right and, for each element, checks all the previous elements to find the ones that are less than the current element and can form an increasing subsequence with it. If a better sum is found for the current element, it updates the `sums` and `sequences` arrays. 5. After iterating through the entire array, the `maxSumIndex` stores the index with the maximum sum of an increasing subsequence. 6. The function `buildSequence` is used to reconstruct the actual increasing subsequence from the `sequences` array, starting from the `maxSumIndex` and going backward until it reaches an element with a value of `math.MinInt32`, which is used as a sentinel value to indicate the end of the sequence. 7. The `reverse` function is a helper function used to reverse the elements in the `sequence` array since the subsequence was built backward. 8. The function returns the maximum sum of the increasing subsequence (`sum`) and the subsequence itself (`sequence`). O(n^2) time | O(n) space - where n is the length of the input array ''' def max_sum_increasing_subsequence(array): n = len(array) sums = [num for num in array] # Store the maximum increasing sum up to index i. sequences = [-1] * n # Store the previous index of the increasing subsequence. for i in range(n): for j in range(i): if array[i] > array[j] and sums[j] + array[i] > sums[i]: sums[i] = sums[j] + array[i] sequences[i] = j # Find the index of the maximum sum in 'sums'. max_sum_index = max(range(n), key=lambda i: sums[i]) max_sum = sums[max_sum_index] # Build the increasing subsequence using the 'sequences' array. sequence = [] while max_sum_index != -1: sequence.append(array[max_sum_index]) max_sum_index = sequences[max_sum_index] sequence.reverse() return max_sum, sequence ================================================ FILE: Dynamic Programming/maximal_sqaure.cpp ================================================ /* -> The maximalSquare function takes a matrix as input and returns the area of the maximal square found in the matrix. -> The function first checks if the matrix is empty (no rows or columns). In such cases, there can't be any squares, so it returns 0. -> Next, it initializes some variables. rows stores the number of rows in the matrix, cols stores the number of columns, and maxSide keeps track of the maximum side length of the square encountered so far. -> It creates a new matrix called dp using the Array.from method. This matrix will store the side lengths of squares. -> The next step is to initialize the first row and the first column of dp with the values from the input matrix. It iterates over each element in the first row and sets the corresponding element in dp to either 0 or 1, depending on whether the element in the input matrix is '0' or '1'. It also updates maxSide accordingly. -> Similarly, it iterates over each element in the first column and sets the corresponding element in dp to either 0 or 1, based on the input matrix. Again, maxSide is updated. -> Now, the function enters a nested loop to iterate over the remaining elements of the matrix, starting from the second row and the second column. For each element at position (i, j), it checks if the corresponding element in the input matrix is '1'. -> If the element is '1', it calculates the value of dp[i][j] by taking the minimum of the three adjacent elements: dp[i-1][j] (above), dp[i][j-1] (left), and dp[i-1][j-1] (diagonally above-left). It adds 1 to the minimum value and assigns it to dp[i][j]. Additionally, it updates maxSide if the current dp[i][j] value is greater. -> After the nested loop completes, the function has calculated the side lengths of squares for all positions in the matrix. It returns the area of the maximal square by squaring the value of maxSide. -> Finally, outside the function, an example usage is shown. The matrix variable represents a 2D array with 0s and 1s. The maximalSquare function is called with this matrix, and the returned result is logged to the console. In this example, the maximal square in the matrix has a side length of 2, so the output is 4 (2 * 2). The code utilizes dynamic programming to efficiently calculate the maximal square in the given matrix by storing the side lengths of squares in a separate matrix. By using previously calculated values, the algorithm avoids redundant calculations and improves performance. //Time complexity The time complexity of the provided maximal square algorithm is O(m * n), where m is the number of rows in the matrix and n is the number of columns. This is because we iterate over each element in the matrix once to calculate the side lengths of squares. The nested loops contribute to the linear time complexity. The space complexity is O(m * n) as well. We create an additional matrix, dp, with the same dimensions as the input matrix to store the side lengths of squares. Hence, the space required is proportional to the number of elements in the matrix. In summary, the time complexity and space complexity of the maximal square algorithm are both O(m * n), where m is the number of rows and n is the number of columns in the matrix. */ /* Example 1: Input: 1 0 1 0 0 1 0 1 1 1 1 1 1 1 1 1 0 0 1 0 Output: 4 The maximal square in the matrix is a 2x2 square with the top-left corner at position (1, 2) and the bottom-right corner at position (2, 3). The area of this square is 4. Example 2: Input: 0 1 1 1 1 1 1 0 1 0 0 1 1 1 1 1 1 1 1 0 Output: 9 The maximal square in the matrix is a 3x3 square with the top-left corner at position (0, 1) and the bottom-right corner at position (2, 3). The area of this square is 9. Example 3: Input: 0 0 0 0 0 0 0 0 0 Output: 0 There are no squares with side length greater than 0 in the matrix. Therefore, the output is 0. */ #include #include #include int maximalSquare(std::vector>& matrix) { if (matrix.empty() || matrix[0].empty()) { return 0; } int rows = matrix.size(); int cols = matrix[0].size(); int maxSide = 0; // Create a new matrix to store the side lengths of squares std::vector> dp(rows, std::vector(cols, 0)); // Initialize the first row of the dp matrix for (int i = 0; i < rows; i++) { dp[i][0] = matrix[i][0] - '0'; maxSide = std::max(maxSide, dp[i][0]); } // Initialize the first column of the dp matrix for (int j = 0; j < cols; j++) { dp[0][j] = matrix[0][j] - '0'; maxSide = std::max(maxSide, dp[0][j]); } // Iterate over the remaining elements of the matrix for (int i = 1; i < rows; i++) { for (int j = 1; j < cols; j++) { if (matrix[i][j] == '1') { // Calculate the minimum of the three adjacent squares and add 1 dp[i][j] = std::min({dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]}) + 1; maxSide = std::max(maxSide, dp[i][j]); } } } // Return the area of the maximal square return maxSide * maxSide; } ================================================ FILE: Dynamic Programming/maximal_sqaure.go ================================================ /* -> The maximalSquare function takes a matrix as input and returns the area of the maximal square found in the matrix. -> The function first checks if the matrix is empty (no rows or columns). In such cases, there can't be any squares, so it returns 0. -> Next, it initializes some variables. rows stores the number of rows in the matrix, cols stores the number of columns, and maxSide keeps track of the maximum side length of the square encountered so far. -> It creates a new matrix called dp using the Array.from method. This matrix will store the side lengths of squares. -> The next step is to initialize the first row and the first column of dp with the values from the input matrix. It iterates over each element in the first row and sets the corresponding element in dp to either 0 or 1, depending on whether the element in the input matrix is '0' or '1'. It also updates maxSide accordingly. -> Similarly, it iterates over each element in the first column and sets the corresponding element in dp to either 0 or 1, based on the input matrix. Again, maxSide is updated. -> Now, the function enters a nested loop to iterate over the remaining elements of the matrix, starting from the second row and the second column. For each element at position (i, j), it checks if the corresponding element in the input matrix is '1'. -> If the element is '1', it calculates the value of dp[i][j] by taking the minimum of the three adjacent elements: dp[i-1][j] (above), dp[i][j-1] (left), and dp[i-1][j-1] (diagonally above-left). It adds 1 to the minimum value and assigns it to dp[i][j]. Additionally, it updates maxSide if the current dp[i][j] value is greater. -> After the nested loop completes, the function has calculated the side lengths of squares for all positions in the matrix. It returns the area of the maximal square by squaring the value of maxSide. -> Finally, outside the function, an example usage is shown. The matrix variable represents a 2D array with 0s and 1s. The maximalSquare function is called with this matrix, and the returned result is logged to the console. In this example, the maximal square in the matrix has a side length of 2, so the output is 4 (2 * 2). The code utilizes dynamic programming to efficiently calculate the maximal square in the given matrix by storing the side lengths of squares in a separate matrix. By using previously calculated values, the algorithm avoids redundant calculations and improves performance. //Time complexity The time complexity of the provided maximal square algorithm is O(m * n), where m is the number of rows in the matrix and n is the number of columns. This is because we iterate over each element in the matrix once to calculate the side lengths of squares. The nested loops contribute to the linear time complexity. The space complexity is O(m * n) as well. We create an additional matrix, dp, with the same dimensions as the input matrix to store the side lengths of squares. Hence, the space required is proportional to the number of elements in the matrix. In summary, the time complexity and space complexity of the maximal square algorithm are both O(m * n), where m is the number of rows and n is the number of columns in the matrix. */ /* Example 1: Input: 1 0 1 0 0 1 0 1 1 1 1 1 1 1 1 1 0 0 1 0 Output: 4 The maximal square in the matrix is a 2x2 square with the top-left corner at position (1, 2) and the bottom-right corner at position (2, 3). The area of this square is 4. Example 2: Input: 0 1 1 1 1 1 1 0 1 0 0 1 1 1 1 1 1 1 1 0 Output: 9 The maximal square in the matrix is a 3x3 square with the top-left corner at position (0, 1) and the bottom-right corner at position (2, 3). The area of this square is 9. Example 3: Input: 0 0 0 0 0 0 0 0 0 Output: 0 There are no squares with side length greater than 0 in the matrix. Therefore, the output is 0. */ package main import ( "fmt" ) func maximalSquare(matrix [][]byte) int { if len(matrix) == 0 || len(matrix[0]) == 0 { return 0 } rows := len(matrix) cols := len(matrix[0]) maxSide := 0 // Create a new matrix to store the side lengths of squares dp := make([][]int, rows) for i := range dp { dp[i] = make([]int, cols) } // Initialize the first row of the dp matrix for i := 0; i < rows; i++ { dp[i][0] = int(matrix[i][0] - '0') maxSide = max(maxSide, dp[i][0]) } // Initialize the first column of the dp matrix for j := 0; j < cols; j++ { dp[0][j] = int(matrix[0][j] - '0') maxSide = max(maxSide, dp[0][j]) } // Iterate over the remaining elements of the matrix for i := 1; i < rows; i++ { for j := 1; j < cols; j++ { if matrix[i][j] == '1' { // Calculate the minimum of the three adjacent squares and add 1 dp[i][j] = min(min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1 maxSide = max(maxSide, dp[i][j]) } } } // Return the area of the maximal square return maxSide * maxSide } func max(a, b int) int { if a > b { return a } return b } func min(a, b int) int { if a < b { return a } return b } ================================================ FILE: Dynamic Programming/maximal_sqaure.js ================================================ /* -> The maximalSquare function takes a matrix as input and returns the area of the maximal square found in the matrix. -> The function first checks if the matrix is empty (no rows or columns). In such cases, there can't be any squares, so it returns 0. -> Next, it initializes some variables. rows stores the number of rows in the matrix, cols stores the number of columns, and maxSide keeps track of the maximum side length of the square encountered so far. -> It creates a new matrix called dp using the Array.from method. This matrix will store the side lengths of squares. -> The next step is to initialize the first row and the first column of dp with the values from the input matrix. It iterates over each element in the first row and sets the corresponding element in dp to either 0 or 1, depending on whether the element in the input matrix is '0' or '1'. It also updates maxSide accordingly. -> Similarly, it iterates over each element in the first column and sets the corresponding element in dp to either 0 or 1, based on the input matrix. Again, maxSide is updated. -> Now, the function enters a nested loop to iterate over the remaining elements of the matrix, starting from the second row and the second column. For each element at position (i, j), it checks if the corresponding element in the input matrix is '1'. -> If the element is '1', it calculates the value of dp[i][j] by taking the minimum of the three adjacent elements: dp[i-1][j] (above), dp[i][j-1] (left), and dp[i-1][j-1] (diagonally above-left). It adds 1 to the minimum value and assigns it to dp[i][j]. Additionally, it updates maxSide if the current dp[i][j] value is greater. -> After the nested loop completes, the function has calculated the side lengths of squares for all positions in the matrix. It returns the area of the maximal square by squaring the value of maxSide. -> Finally, outside the function, an example usage is shown. The matrix variable represents a 2D array with 0s and 1s. The maximalSquare function is called with this matrix, and the returned result is logged to the console. In this example, the maximal square in the matrix has a side length of 2, so the output is 4 (2 * 2). The code utilizes dynamic programming to efficiently calculate the maximal square in the given matrix by storing the side lengths of squares in a separate matrix. By using previously calculated values, the algorithm avoids redundant calculations and improves performance. //Time complexity The time complexity of the provided maximal square algorithm is O(m * n), where m is the number of rows in the matrix and n is the number of columns. This is because we iterate over each element in the matrix once to calculate the side lengths of squares. The nested loops contribute to the linear time complexity. The space complexity is O(m * n) as well. We create an additional matrix, dp, with the same dimensions as the input matrix to store the side lengths of squares. Hence, the space required is proportional to the number of elements in the matrix. In summary, the time complexity and space complexity of the maximal square algorithm are both O(m * n), where m is the number of rows and n is the number of columns in the matrix. */ /* Example 1: Input: 1 0 1 0 0 1 0 1 1 1 1 1 1 1 1 1 0 0 1 0 Output: 4 The maximal square in the matrix is a 2x2 square with the top-left corner at position (1, 2) and the bottom-right corner at position (2, 3). The area of this square is 4. Example 2: Input: 0 1 1 1 1 1 1 0 1 0 0 1 1 1 1 1 1 1 1 0 Output: 9 The maximal square in the matrix is a 3x3 square with the top-left corner at position (0, 1) and the bottom-right corner at position (2, 3). The area of this square is 9. Example 3: Input: 0 0 0 0 0 0 0 0 0 Output: 0 There are no squares with side length greater than 0 in the matrix. Therefore, the output is 0. */ function maximalSquare(matrix) { if (matrix.length === 0 || matrix[0].length === 0) { return 0; } const rows = matrix.length; const cols = matrix[0].length; let maxSide = 0; // Create a new matrix to store the side lengths of squares const dp = Array.from({ length: rows }, () => Array(cols).fill(0)); // Initialize the first row of the dp matrix for (let i = 0; i < rows; i++) { dp[i][0] = Number(matrix[i][0]); maxSide = Math.max(maxSide, dp[i][0]); } // Initialize the first column of the dp matrix for (let j = 0; j < cols; j++) { dp[0][j] = Number(matrix[0][j]); maxSide = Math.max(maxSide, dp[0][j]); } // Iterate over the remaining elements of the matrix for (let i = 1; i < rows; i++) { for (let j = 1; j < cols; j++) { if (matrix[i][j] === '1') { // Calculate the minimum of the three adjacent squares and add 1 dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1; maxSide = Math.max(maxSide, dp[i][j]); } } } // Return the area of the maximal square return maxSide * maxSide; } ================================================ FILE: Dynamic Programming/maximal_sqaure.py ================================================ ''' -> The maximalSquare function takes a matrix as input and returns the area of the maximal square found in the matrix. -> The function first checks if the matrix is empty (no rows or columns). In such cases, there can't be any squares, so it returns 0. -> Next, it initializes some variables. rows stores the number of rows in the matrix, cols stores the number of columns, and maxSide keeps track of the maximum side length of the square encountered so far. -> It creates a new matrix called dp using the Array.from method. This matrix will store the side lengths of squares. -> The next step is to initialize the first row and the first column of dp with the values from the input matrix. It iterates over each element in the first row and sets the corresponding element in dp to either 0 or 1, depending on whether the element in the input matrix is '0' or '1'. It also updates maxSide accordingly. -> Similarly, it iterates over each element in the first column and sets the corresponding element in dp to either 0 or 1, based on the input matrix. Again, maxSide is updated. -> Now, the function enters a nested loop to iterate over the remaining elements of the matrix, starting from the second row and the second column. For each element at position (i, j), it checks if the corresponding element in the input matrix is '1'. -> If the element is '1', it calculates the value of dp[i][j] by taking the minimum of the three adjacent elements: dp[i-1][j] (above), dp[i][j-1] (left), and dp[i-1][j-1] (diagonally above-left). It adds 1 to the minimum value and assigns it to dp[i][j]. Additionally, it updates maxSide if the current dp[i][j] value is greater. -> After the nested loop completes, the function has calculated the side lengths of squares for all positions in the matrix. It returns the area of the maximal square by squaring the value of maxSide. -> Finally, outside the function, an example usage is shown. The matrix variable represents a 2D array with 0s and 1s. The maximalSquare function is called with this matrix, and the returned result is logged to the console. In this example, the maximal square in the matrix has a side length of 2, so the output is 4 (2 * 2). The code utilizes dynamic programming to efficiently calculate the maximal square in the given matrix by storing the side lengths of squares in a separate matrix. By using previously calculated values, the algorithm avoids redundant calculations and improves performance. //Time complexity The time complexity of the provided maximal square algorithm is O(m * n), where m is the number of rows in the matrix and n is the number of columns. This is because we iterate over each element in the matrix once to calculate the side lengths of squares. The nested loops contribute to the linear time complexity. The space complexity is O(m * n) as well. We create an additional matrix, dp, with the same dimensions as the input matrix to store the side lengths of squares. Hence, the space required is proportional to the number of elements in the matrix. In summary, the time complexity and space complexity of the maximal square algorithm are both O(m * n), where m is the number of rows and n is the number of columns in the matrix. ''' ''' Example 1: Input: 1 0 1 0 0 1 0 1 1 1 1 1 1 1 1 1 0 0 1 0 Output: 4 The maximal square in the matrix is a 2x2 square with the top-left corner at position (1, 2) and the bottom-right corner at position (2, 3). The area of this square is 4. Example 2: Input: 0 1 1 1 1 1 1 0 1 0 0 1 1 1 1 1 1 1 1 0 Output: 9 The maximal square in the matrix is a 3x3 square with the top-left corner at position (0, 1) and the bottom-right corner at position (2, 3). The area of this square is 9. Example 3: Input: 0 0 0 0 0 0 0 0 0 Output: 0 There are no squares with side length greater than 0 in the matrix. Therefore, the output is 0. ''' def maximalSquare(matrix): if len(matrix) == 0 or len(matrix[0]) == 0: return 0 rows = len(matrix) cols = len(matrix[0]) maxSide = 0 # Create a new matrix to store the side lengths of squares dp = [[0] * cols for _ in range(rows)] # Initialize the first row of the dp matrix for i in range(rows): dp[i][0] = int(matrix[i][0]) maxSide = max(maxSide, dp[i][0]) # Initialize the first column of the dp matrix for j in range(cols): dp[0][j] = int(matrix[0][j]) maxSide = max(maxSide, dp[0][j]) # Iterate over the remaining elements of the matrix for i in range(1, rows): for j in range(1, cols): if matrix[i][j] == '1': # Calculate the minimum of the three adjacent squares and add 1 dp[i][j] = min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1 maxSide = max(maxSide, dp[i][j]) # Return the area of the maximal square return maxSide * maxSide ================================================ FILE: Dynamic Programming/maximal_square.java ================================================ /* Problem: Given an m x n binary matrix filled with 0's and 1's, find the largest square containing only 1's and return its area. Approach: 1. Initialize a variable `maxSide` to 0 to store the maximum side length of the square. 2. Check if the given matrix is empty, if so, return 0. 3. Create a dynamic programming array `dp` with dimensions (rows + 1) x (cols + 1), where `rows` and `cols` are the dimensions of the matrix. 4. Traverse through the matrix starting from index (1, 1) to (rows, cols). 5. For each cell, if the value is '1', calculate the side length of the square by taking the minimum of the values from the top, left, and diagonal cells in the `dp` array, and add 1. 6. Update the `maxSide` variable with the maximum side length found so far. 7. Finally, return the area of the largest square by multiplying `maxSide` with itself. Time Complexity: O(m*n), where m is the number of rows and n is the number of columns in the matrix. Space Complexity: O(m*n), as we use an additional dp array of the same dimensions as the matrix. Sample Input: char[][] matrix = { {'1', '0', '1', '0', '0'}, {'1', '0', '1', '1', '1'}, {'1', '1', '1', '1', '1'}, {'1', '0', '0', '1', '0'} }; Sample Output: 4 Note: In the given sample input, the largest square containing only 1's has a side length of 2, so the area is 4. */ public class LargestSquare { public int maximalSquare(char[][] matrix) { int maxSide = 0; // variable to store the maximum side length of the square if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { return maxSide; // return 0 if the matrix is empty } int rows = matrix.length; int cols = matrix[0].length; int[][] dp = new int[rows + 1][cols + 1]; // create a dynamic programming array to store the side length of the square for (int i = 1; i <= rows; i++) { for (int j = 1; j <= cols; j++) { if (matrix[i-1][j-1] == '1') { // if current cell is 1 dp[i][j] = Math.min(Math.min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1; // calculate the side length of the square based on top, left, and diagonal cells maxSide = Math.max(maxSide, dp[i][j]); // update the maximum side length } } } return maxSide * maxSide; // return the area of the largest square } } ================================================ FILE: Dynamic Programming/min_cost_travel_in_a_grid.cpp ================================================ /* Given a cost matrix cost[][] and a position (m, n) in cost[][], write a function that returns cost of minimum cost path to reach (m, n) from (0, 0). Each cell of the matrix represents a cost to traverse through that cell. The total cost of a path to reach (m, n) is the sum of all the costs on that path (including both source and destination). You can only traverse down and right from a given cell, i.e., from a given cell (i, j), cells (i+1, j), (i, j+1) can be traversed. You may assume that all costs are positive integers. Input : {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} Output : 30 [Path : 1->2->3->4->8->12] */ // Program Author : Abhisek Kumar Gupta #include using namespace std; int dp[100][100] = {}; int min_cost_path_in_a_grid(int grid[][100], int m, int n){ dp[0][0] = grid[0][0]; //row for(int i = 1; i < m; i++){ dp[i][0] = grid[i][0] + dp[i - 1][0]; } //col for(int i = 1; i < n; i++){ dp[0][i] = grid[0][i] + dp[0][i - 1]; } for(int i = 1; i < m; i++){ for(int j = 1; j < n; j++){ dp[i][j] = grid[i][j] + min(dp[i - 1][j], dp[i][j - 1]); } } return dp[m-1][n-1]; } int main(){ int grid[100][100] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }; int m = 3, n = 4; int result = min_cost_path_in_a_grid(grid, m, n); for(int i = 0; i < m; i++){ for(int j = 0; j < n; j++){ cout << setw(5) < "abcdecb", resulting in a palindrome. Approach: We can solve this problem using dynamic programming. Let's define a 2D table, dp, where dp[i][j] represents the minimum number of insertions needed to make the substring from index i to j a palindrome. If the characters at indices i and j are equal, then dp[i][j] = dp[i+1][j-1]. Otherwise, we have two options: 1. Insert the character at index i at the end, i.e., dp[i][j] = dp[i][j-1] + 1. 2. Insert the character at index j at the beginning, i.e., dp[i][j] = dp[i+1][j] + 1. We take the minimum of these two options as the minimum number of insertions required for the substring from index i to j. Finally, the minimum number of insertions needed for the entire string is dp[0][n-1], where n is the length of the string. Time complexity: O(n^2) Space complexity: O(n^2) */ public class PalindromeInsertion { public static int findMinInsertions(String s) { int n = s.length(); int[][] dp = new int[n][n]; for (int len = 2; len <= n; len++) { for (int i = 0; i <= n - len; i++) { int j = i + len - 1; if (s.charAt(i) == s.charAt(j)) dp[i][j] = dp[i + 1][j - 1]; else dp[i][j] = Math.min(dp[i][j - 1], dp[i + 1][j]) + 1; } } return dp[0][n - 1]; } public static void main(String[] args) { String input = "abcde"; int minInsertions = findMinInsertions(input); System.out.println("Minimum insertions required: " + minInsertions); } } ================================================ FILE: Dynamic Programming/min_number_of_jumps.cpp ================================================ /* The given code snippet calculates the minimum number of jumps required to reach the end of an array of integers. Each element in the array represents the maximum distance you can jump from that position. Here's a step-by-step explanation of the code: 1. The function `MinNumberOfJumps` takes an integer array `array` as input and returns the minimum number of jumps required to reach the last element. 2. The code first checks if the length of the `array` is 1. If so, it means there's only one element, and we don't need any jumps to reach the end. In this case, the function returns 0. 3. If the array has more than one element, the code proceeds with the jump calculation. 4. The variables `jumps`, `maxreach`, and `steps` are initialized. `jumps` keeps track of the total number of jumps taken, `maxreach` represents the farthest position that can be reached in a single jump, and `steps` represents the remaining steps until a new jump is required. 5. The loop starts from the second element (index 1) and iterates until the second-to-last element (index `len(array) - 1`). The reason for stopping at the second-to-last element is that we don't need to take any additional jumps from there, as we are already at the end. 6. For each iteration, the code checks if the current index plus the value at that index (`i + array[i]`) is greater than the current `maxreach`. If so, it updates `maxreach` to the new value, representing the farthest position that can be reached in a single jump. 7. It then decrements `steps` by 1, representing the steps taken in the current jump. 8. If `steps` becomes 0, it means the current jump is completed, and a new jump is required. So, it increments `jumps` by 1 and updates `steps` with the number of steps required to reach the farthest position (i.e., `maxreach - i`). 9. After the loop completes, the function returns `jumps + 1`. The `+1` is added because the loop doesn't consider the last element in the array (as we don't need an additional jump from there), so we need to add one more jump to reach the last element. In summary, the code efficiently calculates the minimum number of jumps required to reach the last element in the array by simulating the jumps and keeping track of the farthest position that can be reached in each jump. The final result is the total number of jumps taken to reach the end. */ #include #include int MinNumberOfJumpsOptimal(std::vector& array) { // If the array has only one element, no jumps are needed. if (array.size() == 1) { return 0; } // Initialize variables to keep track of jumps, maximum reachable position, and remaining steps in a jump. int jumps = 0; int maxreach = array[0]; int steps = array[0]; // Iterate through the array to calculate the minimum number of jumps. // We stop at the second-to-last element as we don't need an additional jump from there. for (int i = 1; i < array.size() - 1; i++) { // Update the maximum reachable position if the current position plus the value at that index is greater. if (i + array[i] > maxreach) { maxreach = i + array[i]; } // Decrement the remaining steps in the current jump. steps--; // If the current jump is completed (steps becomes 0), calculate the new jump. if (steps == 0) { // Increment jumps to count the completed jump. jumps++; // Update steps to the number of steps required to reach the farthest position. steps = maxreach - i; } } // The minimum number of jumps to reach the last element is the total number of jumps taken plus one // because the loop doesn't consider the last element (as we don't need an additional jump from there). return jumps + 1; } ================================================ FILE: Dynamic Programming/min_number_of_jumps.go ================================================ /* This Go code snippet implements the `MinNumberOfJumps` function, which calculates the minimum number of jumps required to reach the last element of the `array` by starting from the first element. Each element of the array represents the maximum number of steps that can be taken from that position. Here's a step-by-step explanation of the code: 1. The function `MinNumberOfJumps` takes an array `array` as input and returns the minimum number of jumps required. 2. The `ways` array is initialized to store the minimum number of jumps required to reach each position in the input array. The length of the `ways` array is the same as the input array, and all values are initialized to `math.MaxInt32`, which represents an unreachable state. 3. The base case is set for the first element of the `ways` array. Since we are already at the first element, the minimum number of jumps required is 0. So, `ways[0]` is set to 0. 4. Starting from the second element (i = 1) to the last element (i = len(array) - 1), the function iterates through the `array`. 5. For each element, it iterates through all previous elements (j) up to the current position (i) to check if it is possible to jump from j to i. 6. If `array[j] + j >= i`, it means we can jump from position `j` to position `i`. 7. The `ways[i]` value is then updated using the minimum between the current `ways[i]` value and `ways[j] + 1`. The `ways[j] + 1` represents the minimum number of jumps required to reach position `j`, and then from position `j` to position `i`. 8. After all iterations, `ways[len(ways) - 1]` will contain the minimum number of jumps required to reach the last element of the array. 9. The `min` function is a helper function that returns the minimum of two integers. The `MinNumberOfJumps` function uses dynamic programming to find the minimum number of jumps efficiently by keeping track of the minimum number of jumps required to reach each position from the previous positions. The time complexity of this function is O(n^2), where n is the length of the input array. The space complexity is O(n), as the `ways` array is used to store the minimum jumps for each position in the array. */ package main import "math" // MinNumberOfJumps calculates the minimum number of jumps required to reach the last element of the `array`. func MinNumberOfJumps(array []int) int { // Create an array to store the minimum number of jumps required to reach each position in the `array`. ways := make([]int, len(array)) // Initialize the `ways` array with maximum integer values representing an unreachable state. for i := range ways { ways[i] = math.MaxInt32 } // Base case: The first element requires 0 jumps to reach itself. ways[0] = 0 // Iterate through the array starting from the second element. for i := 1; i < len(array); i++ { // Check all previous elements to see if a jump from j to i is possible. for j := 0; j < i; j++ { // If it is possible to jump from j to i, update the `ways[i]` value. if array[j] + j >= i { ways[i] = min(ways[i], ways[j]+1) } } } // The value at `ways[len(ways) - 1]` will contain the minimum number of jumps required to reach the last element. return ways[len(ways)-1] } // min returns the minimum of two integers. func min(a, b int) int { if a < b { return a } return b } /* The given code snippet calculates the minimum number of jumps required to reach the end of an array of integers. Each element in the array represents the maximum distance you can jump from that position. Here's a step-by-step explanation of the code: 1. The function `MinNumberOfJumps` takes an integer array `array` as input and returns the minimum number of jumps required to reach the last element. 2. The code first checks if the length of the `array` is 1. If so, it means there's only one element, and we don't need any jumps to reach the end. In this case, the function returns 0. 3. If the array has more than one element, the code proceeds with the jump calculation. 4. The variables `jumps`, `maxreach`, and `steps` are initialized. `jumps` keeps track of the total number of jumps taken, `maxreach` represents the farthest position that can be reached in a single jump, and `steps` represents the remaining steps until a new jump is required. 5. The loop starts from the second element (index 1) and iterates until the second-to-last element (index `len(array) - 1`). The reason for stopping at the second-to-last element is that we don't need to take any additional jumps from there, as we are already at the end. 6. For each iteration, the code checks if the current index plus the value at that index (`i + array[i]`) is greater than the current `maxreach`. If so, it updates `maxreach` to the new value, representing the farthest position that can be reached in a single jump. 7. It then decrements `steps` by 1, representing the steps taken in the current jump. 8. If `steps` becomes 0, it means the current jump is completed, and a new jump is required. So, it increments `jumps` by 1 and updates `steps` with the number of steps required to reach the farthest position (i.e., `maxreach - i`). 9. After the loop completes, the function returns `jumps + 1`. The `+1` is added because the loop doesn't consider the last element in the array (as we don't need an additional jump from there), so we need to add one more jump to reach the last element. In summary, the code efficiently calculates the minimum number of jumps required to reach the last element in the array by simulating the jumps and keeping track of the farthest position that can be reached in each jump. The final result is the total number of jumps taken to reach the end. */ func MinNumberOfJumpsOptimal(array []int) int { // If the array has only one element, no jumps are needed. if len(array) == 1 { return 0 } // Initialize variables to keep track of jumps, maximum reachable position, and remaining steps in a jump. jumps := 0 maxreach, steps := array[0], array[0] // Iterate through the array to calculate the minimum number of jumps. // We stop at the second-to-last element as we don't need an additional jump from there. for i := 1; i < len(array)-1; i++ { // Update the maximum reachable position if the current position plus the value at that index is greater. if i+array[i] > maxreach { maxreach = i + array[i] } // Decrement the remaining steps in the current jump. steps-- // If the current jump is completed (steps becomes 0), calculate the new jump. if steps == 0 { // Increment jumps to count the completed jump. jumps++ // Update steps to the number of steps required to reach the farthest position. steps = maxreach - i } } // The minimum number of jumps to reach the last element is the total number of jumps taken plus one // because the loop doesn't consider the last element (as we don't need an additional jump from there). return jumps + 1 } ================================================ FILE: Dynamic Programming/min_number_of_jumps.java ================================================ public class min_number_of_jumps { } /* The given code snippet calculates the minimum number of jumps required to reach the end of an array of integers. Each element in the array represents the maximum distance you can jump from that position. Here's a step-by-step explanation of the code: 1. The function `MinNumberOfJumps` takes an integer array `array` as input and returns the minimum number of jumps required to reach the last element. 2. The code first checks if the length of the `array` is 1. If so, it means there's only one element, and we don't need any jumps to reach the end. In this case, the function returns 0. 3. If the array has more than one element, the code proceeds with the jump calculation. 4. The variables `jumps`, `maxreach`, and `steps` are initialized. `jumps` keeps track of the total number of jumps taken, `maxreach` represents the farthest position that can be reached in a single jump, and `steps` represents the remaining steps until a new jump is required. 5. The loop starts from the second element (index 1) and iterates until the second-to-last element (index `len(array) - 1`). The reason for stopping at the second-to-last element is that we don't need to take any additional jumps from there, as we are already at the end. 6. For each iteration, the code checks if the current index plus the value at that index (`i + array[i]`) is greater than the current `maxreach`. If so, it updates `maxreach` to the new value, representing the farthest position that can be reached in a single jump. 7. It then decrements `steps` by 1, representing the steps taken in the current jump. 8. If `steps` becomes 0, it means the current jump is completed, and a new jump is required. So, it increments `jumps` by 1 and updates `steps` with the number of steps required to reach the farthest position (i.e., `maxreach - i`). 9. After the loop completes, the function returns `jumps + 1`. The `+1` is added because the loop doesn't consider the last element in the array (as we don't need an additional jump from there), so we need to add one more jump to reach the last element. In summary, the code efficiently calculates the minimum number of jumps required to reach the last element in the array by simulating the jumps and keeping track of the farthest position that can be reached in each jump. The final result is the total number of jumps taken to reach the end. */ public static int minNumberOfJumpsOptimal(int[] array) { // If the array has only one element, no jumps are needed. if (array.length == 1) { return 0; } // Initialize variables to keep track of jumps, maximum reachable position, and remaining steps in a jump. int jumps = 0; int maxReach = array[0]; int steps = array[0]; // Iterate through the array to calculate the minimum number of jumps. // We stop at the second-to-last element as we don't need an additional jump from there. for (int i = 1; i < array.length - 1; i++) { // Update the maximum reachable position if the current position plus the value at that index is greater. if (i + array[i] > maxReach) { maxReach = i + array[i]; } // Decrement the remaining steps in the current jump. steps--; // If the current jump is completed (steps becomes 0), calculate the new jump. if (steps == 0) { // Increment jumps to count the completed jump. jumps++; // Update steps to the number of steps required to reach the farthest position. steps = maxReach - i; } } // The minimum number of jumps to reach the last element is the total number of jumps taken plus one // because the loop doesn't consider the last element (as we don't need an additional jump from there). return jumps + 1; } ================================================ FILE: Dynamic Programming/min_number_of_jumps.js ================================================ /* The given code snippet calculates the minimum number of jumps required to reach the end of an array of integers. Each element in the array represents the maximum distance you can jump from that position. Here's a step-by-step explanation of the code: 1. The function `MinNumberOfJumps` takes an integer array `array` as input and returns the minimum number of jumps required to reach the last element. 2. The code first checks if the length of the `array` is 1. If so, it means there's only one element, and we don't need any jumps to reach the end. In this case, the function returns 0. 3. If the array has more than one element, the code proceeds with the jump calculation. 4. The variables `jumps`, `maxreach`, and `steps` are initialized. `jumps` keeps track of the total number of jumps taken, `maxreach` represents the farthest position that can be reached in a single jump, and `steps` represents the remaining steps until a new jump is required. 5. The loop starts from the second element (index 1) and iterates until the second-to-last element (index `len(array) - 1`). The reason for stopping at the second-to-last element is that we don't need to take any additional jumps from there, as we are already at the end. 6. For each iteration, the code checks if the current index plus the value at that index (`i + array[i]`) is greater than the current `maxreach`. If so, it updates `maxreach` to the new value, representing the farthest position that can be reached in a single jump. 7. It then decrements `steps` by 1, representing the steps taken in the current jump. 8. If `steps` becomes 0, it means the current jump is completed, and a new jump is required. So, it increments `jumps` by 1 and updates `steps` with the number of steps required to reach the farthest position (i.e., `maxreach - i`). 9. After the loop completes, the function returns `jumps + 1`. The `+1` is added because the loop doesn't consider the last element in the array (as we don't need an additional jump from there), so we need to add one more jump to reach the last element. In summary, the code efficiently calculates the minimum number of jumps required to reach the last element in the array by simulating the jumps and keeping track of the farthest position that can be reached in each jump. The final result is the total number of jumps taken to reach the end. */ function minNumberOfJumpsOptimal(array) { // If the array has only one element, no jumps are needed. if (array.length === 1) { return 0; } // Initialize variables to keep track of jumps, maximum reachable position, and remaining steps in a jump. let jumps = 0; let maxReach = array[0]; let steps = array[0]; // Iterate through the array to calculate the minimum number of jumps. // We stop at the second-to-last element as we don't need an additional jump from there. for (let i = 1; i < array.length - 1; i++) { // Update the maximum reachable position if the current position plus the value at that index is greater. if (i + array[i] > maxReach) { maxReach = i + array[i]; } // Decrement the remaining steps in the current jump. steps--; // If the current jump is completed (steps becomes 0), calculate the new jump. if (steps === 0) { // Increment jumps to count the completed jump. jumps++; // Update steps to the number of steps required to reach the farthest position. steps = maxReach - i; } } // The minimum number of jumps to reach the last element is the total number of jumps taken plus one // because the loop doesn't consider the last element (as we don't need an additional jump from there). return jumps + 1; } ================================================ FILE: Dynamic Programming/min_number_of_jumps.py ================================================ ''' The given code snippet calculates the minimum number of jumps required to reach the end of an array of integers. Each element in the array represents the maximum distance you can jump from that position. Here's a step-by-step explanation of the code: 1. The function `MinNumberOfJumps` takes an integer array `array` as input and returns the minimum number of jumps required to reach the last element. 2. The code first checks if the length of the `array` is 1. If so, it means there's only one element, and we don't need any jumps to reach the end. In this case, the function returns 0. 3. If the array has more than one element, the code proceeds with the jump calculation. 4. The variables `jumps`, `maxreach`, and `steps` are initialized. `jumps` keeps track of the total number of jumps taken, `maxreach` represents the farthest position that can be reached in a single jump, and `steps` represents the remaining steps until a new jump is required. 5. The loop starts from the second element (index 1) and iterates until the second-to-last element (index `len(array) - 1`). The reason for stopping at the second-to-last element is that we don't need to take any additional jumps from there, as we are already at the end. 6. For each iteration, the code checks if the current index plus the value at that index (`i + array[i]`) is greater than the current `maxreach`. If so, it updates `maxreach` to the new value, representing the farthest position that can be reached in a single jump. 7. It then decrements `steps` by 1, representing the steps taken in the current jump. 8. If `steps` becomes 0, it means the current jump is completed, and a new jump is required. So, it increments `jumps` by 1 and updates `steps` with the number of steps required to reach the farthest position (i.e., `maxreach - i`). 9. After the loop completes, the function returns `jumps + 1`. The `+1` is added because the loop doesn't consider the last element in the array (as we don't need an additional jump from there), so we need to add one more jump to reach the last element. In summary, the code efficiently calculates the minimum number of jumps required to reach the last element in the array by simulating the jumps and keeping track of the farthest position that can be reached in each jump. The final result is the total number of jumps taken to reach the end. ''' def min_number_of_jumps_optimal(array): # If the array has only one element, no jumps are needed. if len(array) == 1: return 0 # Initialize variables to keep track of jumps, maximum reachable position, and remaining steps in a jump. jumps = 0 max_reach = array[0] steps = array[0] # Iterate through the array to calculate the minimum number of jumps. # We stop at the second-to-last element as we don't need an additional jump from there. for i in range(1, len(array) - 1): # Update the maximum reachable position if the current position plus the value at that index is greater. if i + array[i] > max_reach: max_reach = i + array[i] # Decrement the remaining steps in the current jump. steps -= 1 # If the current jump is completed (steps becomes 0), calculate the new jump. if steps == 0: # Increment jumps to count the completed jump. jumps += 1 # Update steps to the number of steps required to reach the farthest position. steps = max_reach - i # The minimum number of jumps to reach the last element is the total number of jumps taken plus one # because the loop doesn't consider the last element (as we don't need an additional jump from there). return jumps + 1 ================================================ FILE: Dynamic Programming/min_steps_to_make_string_palindrome.go ================================================ /* Given a string, find the minimum number of insertions needed to make it a palindrome. Sample Input: "abcde" Sample Output: 4 Explanation: The minimum insertions required are 'edcb' -> "abcdecb", resulting in a palindrome. Approach: We can solve this problem using dynamic programming. Let's define a 2D table, dp, where dp[i][j] represents the minimum number of insertions needed to make the substring from index i to j a palindrome. If the characters at indices i and j are equal, then dp[i][j] = dp[i+1][j-1]. Otherwise, we have two options: 1. Insert the character at index i at the end, i.e., dp[i][j] = dp[i][j-1] + 1. 2. Insert the character at index j at the beginning, i.e., dp[i][j] = dp[i+1][j] + 1. We take the minimum of these two options as the minimum number of insertions required for the substring from index i to j. Finally, the minimum number of insertions needed for the entire string is dp[0][n-1], where n is the length of the string. Time complexity: O(n^2) Space complexity: O(n^2) */ package main import ( "fmt" ) func lcs(s1, s2 string) int { n, m := len(s1), len(s2) dp := make([][]int, n+1) // Create a 2D matrix dp to store the lengths of LCS for i := 0; i <= n; i++ { dp[i] = make([]int, m+1) } for i := 1; i <= n; i++ { for j := 1; j <= m; j++ { if s1[i-1] == s2[j-1] { // If characters at the current positions match dp[i][j] = 1 + dp[i-1][j-1] // Increase the length of LCS by 1 } else { dp[i][j] = max(dp[i-1][j], dp[i][j-1]) // Take the maximum length from the previous positions } } } return dp[n][m] // Return the length of the LCS of s1 and s2 } func max(a, b int) int { if a > b { return a } return b } func minInsertions(s string) int { reverseS := "" for i := len(s) - 1; i >= 0; i-- { reverseS += string(s[i]) // Reverse the string s } lcsLength := lcs(s, reverseS) // Find the length of the LCS of s and its reverse return len(s) - lcsLength // Return the number of insertions required to make s a palindrome } func main() { var s string fmt.Scanln(&s) // Read input string ans := minInsertions(s) // Calculate the minimum number of insertions required fmt.Println(ans) // Print the result } ================================================ FILE: Dynamic Programming/min_steps_to_make_string_palindrome.java ================================================ /* Given a string, find the minimum number of insertions needed to make it a palindrome. Sample Input: "abcde" Sample Output: 4 Explanation: The minimum insertions required are 'edcb' -> "abcdecb", resulting in a palindrome. Approach: We can solve this problem using dynamic programming. Let's define a 2D table, dp, where dp[i][j] represents the minimum number of insertions needed to make the substring from index i to j a palindrome. If the characters at indices i and j are equal, then dp[i][j] = dp[i+1][j-1]. Otherwise, we have two options: 1. Insert the character at index i at the end, i.e., dp[i][j] = dp[i][j-1] + 1. 2. Insert the character at index j at the beginning, i.e., dp[i][j] = dp[i+1][j] + 1. We take the minimum of these two options as the minimum number of insertions required for the substring from index i to j. Finally, the minimum number of insertions needed for the entire string is dp[0][n-1], where n is the length of the string. Time complexity: O(n^2) Space complexity: O(n^2) */ import java.util.Scanner; public class Main { // Function to calculate the length of the Longest Common Subsequence (LCS) of two strings public static int lcs(String s1, String s2) { int n = s1.length(); int m = s2.length(); // Create a 2D array dp to store the lengths of LCS int[][] dp = new int[n+1][m+1]; // Initialize the first row and first column to 0 for (int i = 0; i <= n; i++) { dp[i][0] = 0; } for (int j = 0; j <= m; j++) { dp[0][j] = 0; } // Fill the dp array using dynamic programming for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (s1.charAt(i-1) == s2.charAt(j-1)) { // If characters at the current positions match, increase the length of LCS by 1 dp[i][j] = 1 + dp[i-1][j-1]; } else { // Take the maximum length from the previous positions dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]); } } } // Return the length of the LCS of s1 and s2 return dp[n][m]; } // Function to calculate the minimum number of insertions required to make a string palindrome public static int minInsertions(String s) { StringBuilder reverseSb = new StringBuilder(s); reverseSb.reverse(); // Reverse the string s String reverseS = reverseSb.toString(); int lcsLength = lcs(s, reverseS); // Find the length of the LCS of s and its reverse return s.length() - lcsLength; // Return the number of insertions required to make s a palindrome } public static void main(String[] args) { Scanner scanner = new Scanner(System.in); String s = scanner.nextLine(); // Read input string int ans = minInsertions(s); // Calculate the minimum number of insertions required System.out.println(ans); // Print the result scanner.close(); } } ================================================ FILE: Dynamic Programming/min_steps_to_make_string_palindrome.py ================================================ ''' Given a string, find the minimum number of insertions needed to make it a palindrome. Sample Input: "abcde" Sample Output: 4 Explanation: The minimum insertions required are 'edcb' -> "abcdecb", resulting in a palindrome. Approach: We can solve this problem using dynamic programming. Let's define a 2D table, dp, where dp[i][j] represents the minimum number of insertions needed to make the substring from index i to j a palindrome. If the characters at indices i and j are equal, then dp[i][j] = dp[i+1][j-1]. Otherwise, we have two options: 1. Insert the character at index i at the end, i.e., dp[i][j] = dp[i][j-1] + 1. 2. Insert the character at index j at the beginning, i.e., dp[i][j] = dp[i+1][j] + 1. We take the minimum of these two options as the minimum number of insertions required for the substring from index i to j. Finally, the minimum number of insertions needed for the entire string is dp[0][n-1], where n is the length of the string. Time complexity: O(n^2) Space complexity: O(n^2) ''' # Function to calculate the length of the Longest Common Subsequence (LCS) of two strings def lcs(s1, s2): n, m = len(s1), len(s2) # Create a 2D list dp to store the lengths of LCS dp = [[0] * (m + 1) for _ in range(n + 1)] # Fill the dp array using dynamic programming for i in range(1, n + 1): for j in range(1, m + 1): if s1[i - 1] == s2[j - 1]: # If characters at the current positions match, increase the length of LCS by 1 dp[i][j] = 1 + dp[i - 1][j - 1] else: # Take the maximum length from the previous positions dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) # Return the length of the LCS of s1 and s2 return dp[n][m] # Function to calculate the minimum number of insertions required to make a string palindrome def min_insertions(s): reverse_s = s[::-1] # Reverse the string s lcs_length = lcs(s, reverse_s) # Find the length of the LCS of s and its reverse return len(s) - lcs_length # Return the number of insertions required to make s a palindrome if __name__ == "__main__": s = input() # Read input string ans = min_insertions(s) # Calculate the minimum number of insertions required print(ans) # Print the result ================================================ FILE: Dynamic Programming/min_steps_to_make_string_pallindrome.cpp ================================================ /* Given a string, find the minimum number of insertions needed to make it a palindrome. Sample Input: "abcde" Sample Output: 4 Explanation: The minimum insertions required are 'edcb' -> "abcdecb", resulting in a palindrome. Approach: We can solve this problem using dynamic programming. Let's define a 2D table, dp, where dp[i][j] represents the minimum number of insertions needed to make the substring from index i to j a palindrome. If the characters at indices i and j are equal, then dp[i][j] = dp[i+1][j-1]. Otherwise, we have two options: 1. Insert the character at index i at the end, i.e., dp[i][j] = dp[i][j-1] + 1. 2. Insert the character at index j at the beginning, i.e., dp[i][j] = dp[i+1][j] + 1. We take the minimum of these two options as the minimum number of insertions required for the substring from index i to j. Finally, the minimum number of insertions needed for the entire string is dp[0][n-1], where n is the length of the string. Time complexity: O(n^2) Space complexity: O(n^2) */ #include using namespace std; int lcs(string s1,string s2){ int n=s1.size(),m=s2.size(); vector> dp(n+1,vector(m+1,-1)); // bottom up approach to calculate lcs // base conditions for(int i=0;i<=n;i++){ dp[i][0]=0; } for(int i=0;i<=m;i++){ dp[0][i]=0; } // filling the table to calculate the longest common subsequence for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(s1[i-1]==s2[j-1]){ // checking if the characters at both the string are same dp[i][j]=1+dp[i-1][j-1]; } else{ dp[i][j]=0+max(dp[i-1][j],dp[i][j-1]); } } } return dp[n][m]; } int main(){ string s; cin>>s; string reverse_s; for(int i=s.size()-1;i>=0;i--){ reverse_s.push_back(s[i]); } // the minimum steps to make a string pallindrome will require to calculate the // longest common subsequence in the given string and the reverse of the string int ans=lcs(s,reverse_s); ans=s.size()-ans; cout< "abcdecb", resulting in a palindrome. Approach: We can solve this problem using dynamic programming. Let's define a 2D table, dp, where dp[i][j] represents the minimum number of insertions needed to make the substring from index i to j a palindrome. If the characters at indices i and j are equal, then dp[i][j] = dp[i+1][j-1]. Otherwise, we have two options: 1. Insert the character at index i at the end, i.e., dp[i][j] = dp[i][j-1] + 1. 2. Insert the character at index j at the beginning, i.e., dp[i][j] = dp[i+1][j] + 1. We take the minimum of these two options as the minimum number of insertions required for the substring from index i to j. Finally, the minimum number of insertions needed for the entire string is dp[0][n-1], where n is the length of the string. Time complexity: O(n^2) Space complexity: O(n^2) */ // Function to calculate the length of the Longest Common Subsequence (LCS) of two strings function lcs(s1, s2) { const n = s1.length; const m = s2.length; // Create a 2D array dp to store the lengths of LCS const dp = new Array(n + 1).fill(0).map(() => new Array(m + 1).fill(0)); // Fill the dp array using dynamic programming for (let i = 1; i <= n; i++) { for (let j = 1; j <= m; j++) { if (s1[i - 1] === s2[j - 1]) { // If characters at the current positions match, increase the length of LCS by 1 dp[i][j] = 1 + dp[i - 1][j - 1]; } else { // Take the maximum length from the previous positions dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); } } } // Return the length of the LCS of s1 and s2 return dp[n][m]; } // Function to calculate the minimum number of insertions required to make a string palindrome function minInsertions(s) { const reverseS = s.split("").reverse().join(""); // Reverse the string s const lcsLength = lcs(s, reverseS); // Find the length of the LCS of s and its reverse return s.length - lcsLength; // Return the number of insertions required to make s a palindrome } // Example usage: const input = "abcde"; const result = minInsertions(input); // Calculate the minimum number of insertions required console.log(result); // Print the result ================================================ FILE: Dynamic Programming/min_steps_to_reduce_a_number_to_one.cpp ================================================ // Minimum steps to reduce a number to one conditions are as follows // a) subtract 1 [one operation] // b) divide by 2 [one operation] // c) divide by 3 [one operation] // Dynamic Programming solution // Program Author : Abhisek Kumar Gupta /* Input : 10 Output : 3 Explanation : 10 reduced to 9 reduced to 3 reduced to 1 [total 3 operations] Input : 15 Output : 4 */ #include using namespace std; int dp[10000]; int find_min_steps(int number){ int r1 = INT_MAX, r2 = INT_MAX, r3 = INT_MAX; dp[0] = 0; dp[1] = 0; dp[2] = 1; dp[3] = 1; for(int i = 4; i <= number; i++){ r1 = 1 + dp[i - 1]; if(i % 2 == 0) r2 = 1 + dp[i / 2]; if(i % 3 == 0) r3 = 1 + dp[i / 3]; dp[i] = min(r1, min(r2, r3)); r1 = INT_MAX, r2 = INT_MAX, r3 = INT_MAX; } return dp[number]; } int memoized[10004]; int find_min_steps(int number){ if(number == 1) return 0; int r1 = INT_MAX, r2 = INT_MAX, r3 = INT_MAX; if(memoized[number] != -1) return memoized[number]; r1 = 1 + find_min_steps(number - 1); if(number % 2 == 0) r2 = 1 + find_min_steps(number / 2); if(number % 3 == 0) r3 = 1 + find_min_steps(number / 3); memoized[number] = min(r1, min(r2, r3)); return memoized[number]; } int main(){ int number; cin >> number; memset(dp, 0, sizeof(dp)); int result = find_min_steps(number); cout << result; return 0; } ================================================ FILE: Dynamic Programming/num_ways_to_make_change.cpp ================================================ /* Given an array of distinct positive integers representing coin denominations and a single non-negative integer n representing a target amount of money, write a function that returns the number of ways to make change for that target amount using the given coin denominations. Sample Input: n = 6 denominations : [1, 5] Output: 2 (1 * 1 + 1 * 5 and 6 * 1) Explanation: The given code snippet is implementing the "NumberOfWaysToMakeChange" algorithm in Go. This algorithm calculates the number of ways to make change for a given amount using a set of denominations. Here's how the algorithm works: 1. The function "NumberOfWaysToMakeChange" takes two parameters: "n" (the target amount to make change for) and "denoms" (an array of coin denominations). 2. It initializes an array called "ways" of size "n+1" to keep track of the number of ways to make change for each amount from 0 to "n". The initial value of "ways[0]" is set to 1, representing the base case where there is one way to make change for zero. 3. The algorithm iterates over each denomination in the "denoms" array using a for-each loop. 4. For each denomination, it further iterates from 1 to "n+1" to calculate the number of ways to make change for each amount. 5. Inside the inner loop, it checks if the current denomination is less than or equal to the current amount. If so, it means that the current denomination can contribute to the change for the current amount. 6. It then updates the "ways[amount]" value by adding the number of ways to make change for the current amount minus the current denomination. This is done to accumulate all the possible ways to make change using the current denomination. 7. After completing the nested loops, the algorithm returns the value stored in "ways[n]", which represents the total number of ways to make change for the target amount "n" using the given denominations. In summary, this algorithm utilizes dynamic programming to calculate the number of ways to make change for a given amount using a set of denominations. By iteratively building up the solutions for smaller amounts, it efficiently computes the result in O(n * m) time complexity, where "n" is the target amount and "m" is the number of denominations. The space complexity is O(n), as the algorithm uses an array of size "n+1" to store the intermediate results. Time complexity : O(nd) Space complexity : O(n) where n is the target amount and d is the number of coin denominations */ #include int NumberOfWaysToMakeChange(int n, const std::vector& denoms) { // Create an array to store the number of ways to make change for each amount from 0 to n. std::vector ways(n + 1, 0); // Initialize the base case: There is one way to make change for amount 0 (using no coins). ways[0] = 1; // Iterate over each denomination. for (int denom : denoms) { // For each denomination, iterate over each amount from 1 to n. for (int amount = 1; amount < n + 1; amount++) { // Check if the denomination can be used to make change for the current amount. if (denom <= amount) { // Add the number of ways to make change for the current amount // by considering the current denomination. ways[amount] += ways[amount - denom]; } } } // Return the number of ways to make change for the target amount n. return ways[n]; } ================================================ FILE: Dynamic Programming/num_ways_to_make_change.go ================================================ /* Given an array of distinct positive integers representing coin denominations and a single non-negative integer n representing a target amount of money, write a function that returns the number of ways to make change for that target amount using the given coin denominations. Sample Input: n = 6 denominations : [1, 5] Output: 2 (1 * 1 + 1 * 5 and 6 * 1) Explanation: The given code snippet is implementing the "NumberOfWaysToMakeChange" algorithm in Go. This algorithm calculates the number of ways to make change for a given amount using a set of denominations. Here's how the algorithm works: 1. The function "NumberOfWaysToMakeChange" takes two parameters: "n" (the target amount to make change for) and "denoms" (an array of coin denominations). 2. It initializes an array called "ways" of size "n+1" to keep track of the number of ways to make change for each amount from 0 to "n". The initial value of "ways[0]" is set to 1, representing the base case where there is one way to make change for zero. 3. The algorithm iterates over each denomination in the "denoms" array using a for-each loop. 4. For each denomination, it further iterates from 1 to "n+1" to calculate the number of ways to make change for each amount. 5. Inside the inner loop, it checks if the current denomination is less than or equal to the current amount. If so, it means that the current denomination can contribute to the change for the current amount. 6. It then updates the "ways[amount]" value by adding the number of ways to make change for the current amount minus the current denomination. This is done to accumulate all the possible ways to make change using the current denomination. 7. After completing the nested loops, the algorithm returns the value stored in "ways[n]", which represents the total number of ways to make change for the target amount "n" using the given denominations. In summary, this algorithm utilizes dynamic programming to calculate the number of ways to make change for a given amount using a set of denominations. By iteratively building up the solutions for smaller amounts, it efficiently computes the result in O(n * m) time complexity, where "n" is the target amount and "m" is the number of denominations. The space complexity is O(n), as the algorithm uses an array of size "n+1" to store the intermediate results. Time complexity : O(nd) Space complexity : O(n) where n is the target amount and d is the number of coin denominations */ package main func NumberOfWaysToMakeChange(n int, denoms []int) int { // Create an array to store the number of ways to make change for each amount from 0 to n. ways := make([]int, n+1) // Initialize the base case: There is one way to make change for amount 0 (using no coins). ways[0] = 1 // Iterate over each denomination. for _, denom := range denoms { // For each denomination, iterate over each amount from 1 to n. for amount := 1; amount < n+1; amount++ { // Check if the denomination can be used to make change for the current amount. if denom <= amount { // Add the number of ways to make change for the current amount // by considering the current denomination. ways[amount] += ways[amount-denom] } } } // Return the number of ways to make change for the target amount n. return ways[n] } ================================================ FILE: Dynamic Programming/num_ways_to_make_change.java ================================================ /* Given an array of distinct positive integers representing coin denominations and a single non-negative integer n representing a target amount of money, write a function that returns the number of ways to make change for that target amount using the given coin denominations. Sample Input: n = 6 denominations : [1, 5] Output: 2 (1 * 1 + 1 * 5 and 6 * 1) Explanation: The given code snippet is implementing the "NumberOfWaysToMakeChange" algorithm in Go. This algorithm calculates the number of ways to make change for a given amount using a set of denominations. Here's how the algorithm works: 1. The function "NumberOfWaysToMakeChange" takes two parameters: "n" (the target amount to make change for) and "denoms" (an array of coin denominations). 2. It initializes an array called "ways" of size "n+1" to keep track of the number of ways to make change for each amount from 0 to "n". The initial value of "ways[0]" is set to 1, representing the base case where there is one way to make change for zero. 3. The algorithm iterates over each denomination in the "denoms" array using a for-each loop. 4. For each denomination, it further iterates from 1 to "n+1" to calculate the number of ways to make change for each amount. 5. Inside the inner loop, it checks if the current denomination is less than or equal to the current amount. If so, it means that the current denomination can contribute to the change for the current amount. 6. It then updates the "ways[amount]" value by adding the number of ways to make change for the current amount minus the current denomination. This is done to accumulate all the possible ways to make change using the current denomination. 7. After completing the nested loops, the algorithm returns the value stored in "ways[n]", which represents the total number of ways to make change for the target amount "n" using the given denominations. In summary, this algorithm utilizes dynamic programming to calculate the number of ways to make change for a given amount using a set of denominations. By iteratively building up the solutions for smaller amounts, it efficiently computes the result in O(n * m) time complexity, where "n" is the target amount and "m" is the number of denominations. The space complexity is O(n), as the algorithm uses an array of size "n+1" to store the intermediate results. Time complexity : O(nd) Space complexity : O(n) where n is the target amount and d is the number of coin denominations */ import java.util.Arrays; public class CoinChange { public static int numberOfWaysToMakeChange(int n, int[] denoms) { // Create an array to store the number of ways to make change for each amount from 0 to n. int[] ways = new int[n + 1]; // Initialize the base case: There is one way to make change for amount 0 (using no coins). ways[0] = 1; // Iterate over each denomination. for (int denom : denoms) { // For each denomination, iterate over each amount from 1 to n. for (int amount = 1; amount < n + 1; amount++) { // Check if the denomination can be used to make change for the current amount. if (denom <= amount) { // Add the number of ways to make change for the current amount // by considering the current denomination. ways[amount] += ways[amount - denom]; } } } // Return the number of ways to make change for the target amount n. return ways[n]; } public static void main(String[] args) { int n = 10; int[] denoms = {1, 2, 5}; int numberOfWays = numberOfWaysToMakeChange(n, denoms); System.out.println("Number of ways to make change: " + numberOfWays); } } ================================================ FILE: Dynamic Programming/num_ways_to_make_change.js ================================================ /* Given an array of distinct positive integers representing coin denominations and a single non-negative integer n representing a target amount of money, write a function that returns the number of ways to make change for that target amount using the given coin denominations. Sample Input: n = 6 denominations : [1, 5] Output: 2 (1 * 1 + 1 * 5 and 6 * 1) Explanation: The given code snippet is implementing the "NumberOfWaysToMakeChange" algorithm in Go. This algorithm calculates the number of ways to make change for a given amount using a set of denominations. Here's how the algorithm works: 1. The function "NumberOfWaysToMakeChange" takes two parameters: "n" (the target amount to make change for) and "denoms" (an array of coin denominations). 2. It initializes an array called "ways" of size "n+1" to keep track of the number of ways to make change for each amount from 0 to "n". The initial value of "ways[0]" is set to 1, representing the base case where there is one way to make change for zero. 3. The algorithm iterates over each denomination in the "denoms" array using a for-each loop. 4. For each denomination, it further iterates from 1 to "n+1" to calculate the number of ways to make change for each amount. 5. Inside the inner loop, it checks if the current denomination is less than or equal to the current amount. If so, it means that the current denomination can contribute to the change for the current amount. 6. It then updates the "ways[amount]" value by adding the number of ways to make change for the current amount minus the current denomination. This is done to accumulate all the possible ways to make change using the current denomination. 7. After completing the nested loops, the algorithm returns the value stored in "ways[n]", which represents the total number of ways to make change for the target amount "n" using the given denominations. In summary, this algorithm utilizes dynamic programming to calculate the number of ways to make change for a given amount using a set of denominations. By iteratively building up the solutions for smaller amounts, it efficiently computes the result in O(n * m) time complexity, where "n" is the target amount and "m" is the number of denominations. The space complexity is O(n), as the algorithm uses an array of size "n+1" to store the intermediate results. Time complexity : O(nd) Space complexity : O(n) where n is the target amount and d is the number of coin denominations */ function numberOfWaysToMakeChange(n, denoms) { // Create an array to store the number of ways to make change for each amount from 0 to n. const ways = new Array(n + 1).fill(0); // Initialize the base case: There is one way to make change for amount 0 (using no coins). ways[0] = 1; // Iterate over each denomination. for (const denom of denoms) { // For each denomination, iterate over each amount from 1 to n. for (let amount = 1; amount < n + 1; amount++) { // Check if the denomination can be used to make change for the current amount. if (denom <= amount) { // Add the number of ways to make change for the current amount // by considering the current denomination. ways[amount] += ways[amount - denom]; } } } // Return the number of ways to make change for the target amount n. return ways[n]; } // Example usage const n = 10; const denoms = [1, 2, 5]; const numberOfWays = numberOfWaysToMakeChange(n, denoms); console.log("Number of ways to make change:", numberOfWays); ================================================ FILE: Dynamic Programming/num_ways_to_make_change.py ================================================ ''' Given an array of distinct positive integers representing coin denominations and a single non-negative integer n representing a target amount of money, write a function that returns the number of ways to make change for that target amount using the given coin denominations. Sample Input: n = 6 denominations : [1, 5] Output: 2 (1 * 1 + 1 * 5 and 6 * 1) Explanation: The given code snippet is implementing the "NumberOfWaysToMakeChange" algorithm in Go. This algorithm calculates the number of ways to make change for a given amount using a set of denominations. Here's how the algorithm works: 1. The function "NumberOfWaysToMakeChange" takes two parameters: "n" (the target amount to make change for) and "denoms" (an array of coin denominations). 2. It initializes an array called "ways" of size "n+1" to keep track of the number of ways to make change for each amount from 0 to "n". The initial value of "ways[0]" is set to 1, representing the base case where there is one way to make change for zero. 3. The algorithm iterates over each denomination in the "denoms" array using a for-each loop. 4. For each denomination, it further iterates from 1 to "n+1" to calculate the number of ways to make change for each amount. 5. Inside the inner loop, it checks if the current denomination is less than or equal to the current amount. If so, it means that the current denomination can contribute to the change for the current amount. 6. It then updates the "ways[amount]" value by adding the number of ways to make change for the current amount minus the current denomination. This is done to accumulate all the possible ways to make change using the current denomination. 7. After completing the nested loops, the algorithm returns the value stored in "ways[n]", which represents the total number of ways to make change for the target amount "n" using the given denominations. In summary, this algorithm utilizes dynamic programming to calculate the number of ways to make change for a given amount using a set of denominations. By iteratively building up the solutions for smaller amounts, it efficiently computes the result in O(n * m) time complexity, where "n" is the target amount and "m" is the number of denominations. The space complexity is O(n), as the algorithm uses an array of size "n+1" to store the intermediate results. Time complexity : O(nd) Space complexity : O(n) where n is the target amount and d is the number of coin denominations ''' def number_of_ways_to_make_change(n, denoms): # Create a list to store the number of ways to make change for each amount from 0 to n. ways = [0] * (n + 1) # Initialize the base case: There is one way to make change for amount 0 (using no coins). ways[0] = 1 # Iterate over each denomination. for denom in denoms: # For each denomination, iterate over each amount from 1 to n. for amount in range(1, n + 1): # Check if the denomination can be used to make change for the current amount. if denom <= amount: # Add the number of ways to make change for the current amount # by considering the current denomination. ways[amount] += ways[amount - denom] # Return the number of ways to make change for the target amount n. return ways[n] # Example usage n = 10 denoms = [1, 2, 5] number_of_ways = number_of_ways_to_make_change(n, denoms) print("Number of ways to make change:", number_of_ways) ================================================ FILE: Dynamic Programming/num_ways_to_traverse_graph.cpp ================================================ /* You're given two positive integers representing the width and height of a grid-shaped, rectangular graph. Write a function that returns the number of ways to reach the bottom right corner of the graph when starting at the top left corner. Each move you take must either go down or right. In other words, you can never move up or left in the graph. For example, given the graph illustrated below, with width = 2 and height = 3 , there are three ways to reach the bottom right corner when starting at the top left corner: _ _ |_|_| |_|_| |_|_| Down Down Right Right Down Down Down Right Down Sample Input: Width : 4 height: 3 Output: 10 Explanation: The code snippet implements the `NumberOfWaysToTraverseGraph` function, which calculates the number of ways to traverse a 2D graph from the top-left corner to the bottom-right corner. The graph has a given width and height. Here's a breakdown of the code: 1. The function takes two parameters: `width` and `height`, representing the dimensions of the graph. 2. It initializes a 2D slice called `numberOfWays` with dimensions `(height+1) x (width+1)`. The additional "+1" is to account for the boundary cases when traversing the graph. 3. It enters a nested loop to iterate over the graph cells. The outer loop iterates over the width indices (`widthIdx`), and the inner loop iterates over the height indices (`heightIdx`). 4. For each cell, it checks if it is on the top row (`heightIdx == 1`) or the leftmost column (`widthIdx == 1`). If so, it means that there is only one way to reach that cell, either by moving right or moving down. Therefore, it sets `numberOfWays[heightIdx][widthIdx]` to 1. 5. If the cell is not on the top row or the leftmost column, it means that it can be reached by either moving from the cell above (up) or the cell to the left (left). The number of ways to reach the current cell is the sum of the number of ways to reach the cell above and the number of ways to reach the cell to the left. This value is stored in `numberOfWays[heightIdx][widthIdx]`. 6. After iterating over all cells, the function returns the value stored in the bottom-right corner of `numberOfWays`, which represents the total number of ways to traverse the graph. The algorithm uses dynamic programming to build the `numberOfWays` matrix iteratively, starting from the top-left corner and moving towards the bottom-right corner. By calculating the number of ways to reach each cell based on the number of ways to reach its neighboring cells, it avoids redundant calculations and computes the result efficiently. The time complexity of the algorithm is O(width * height) since it iterates over all cells of the graph. The space complexity is also O(width * height) since it uses the `numberOfWays` matrix to store intermediate results. */ int numberOfWaysToTraverseGraph(int width, int height) { // Create a 2D vector to store the number of ways to reach each cell vector> numberOfWays(height, vector(width, 1)); // Iterate through the cells from top to bottom and left to right for (int i = 1; i < height; i++) { for (int j = 1; j < width; j++) { // Calculate the number of ways to reach the current cell // by summing the number of ways from the cell above and the cell to the left numberOfWays[i][j] = numberOfWays[i - 1][j] + numberOfWays[i][j - 1]; } } // Return the number of ways to reach the bottom-right corner of the graph return numberOfWays[height - 1][width - 1]; } /* Combinatorics Solution The given code snippet aims to calculate the number of ways to traverse a graph from the top-left corner to the bottom-right corner. Let's break down the solution and provide a detailed explanation: 1. The `NumberOfWaysToTraverseGraph` function takes two parameters: `width` and `height`, representing the dimensions of the graph. 2. The variables `xDistanceToCorner` and `yDistanceToCorner` are calculated by subtracting 1 from the `width` and `height` respectively. These variables represent the distances from the top-left corner to the bottom-right corner along the x-axis and y-axis. 3. The `factorial` function is defined separately to calculate the factorial of a number. It takes a number `num` as input and uses an iterative approach to calculate the factorial. 4. In the `NumberOfWaysToTraverseGraph` function, the numerator is calculated as the factorial of the sum of `xDistanceToCorner` and `yDistanceToCorner`. This represents the total number of possible paths from the top-left corner to the bottom-right corner. 5. The denominator is calculated as the product of the factorials of `xDistanceToCorner` and `yDistanceToCorner`. This represents the number of ways to arrange the steps along the x-axis and y-axis. 6. Finally, the function returns the result by dividing the numerator by the denominator, giving the total number of ways to traverse the graph. The solution relies on the concept of combinatorics, specifically the binomial coefficient, to calculate the number of ways to traverse the graph. By using factorials, it accounts for all possible paths and eliminates duplicate paths. This approach provides an efficient solution to the problem. O(n + m) time | O(1) space - where n is the width of the graph and m is the height */ int numberOfWaysToTraverseGraphCombinatorics(int width, int height) { // Calculate the distances to the bottom-right corner of the graph int xDistanceToCorner = width - 1; int yDistanceToCorner = height - 1; // Calculate the numerator and denominator for the binomial coefficient int numerator = factorial(xDistanceToCorner + yDistanceToCorner); int denominator = factorial(xDistanceToCorner) * factorial(yDistanceToCorner); // Return the result by dividing the numerator by the denominator return numerator / denominator; } int factorial(int n) { // Base case: factorial of 0 or 1 is 1 if (n <= 1) { return 1; } // Recursive case: compute factorial by multiplying n with factorial(n-1) return n * factorial(n - 1); } /* Recursive solution The given solution aims to calculate the number of ways to traverse a graph from the top-left corner to the bottom-right corner. It uses a recursive approach to break down the problem into smaller subproblems. Here's how the solution works: 1. The function `NumberOfWaysToTraverseGraph` takes the width and height of the graph as input and returns the number of ways to traverse it. 2. The base case of the recursion is when either the width or height is equal to 1. In this case, there is only one way to traverse the graph: either by moving only horizontally or vertically. Therefore, the function returns 1. 3. For other cases where the width and height are both greater than 1, the function recursively calls itself with two smaller subproblems: - One subproblem is created by reducing the width by 1 and keeping the same height. - The other subproblem is created by keeping the same width and reducing the height by 1. 4. The number of ways to traverse the current graph is calculated by summing up the number of ways from the two subproblems. 5. The recursion continues until it reaches the base case, where the width or height becomes 1, and eventually returns the total number of ways to traverse the graph. While this recursive approach is conceptually simple, it suffers from efficiency issues due to exponential time complexity and redundant calculations. As the graph size increases, the number of recursive calls grows exponentially, leading to a significant increase in computation time. Additionally, without memoization, the function recalculates the same subproblems multiple times, further reducing efficiency. To address these drawbacks, alternative approaches like dynamic programming or memoization can be employed to store and reuse the results of previously solved subproblems, avoiding redundant calculations and improving efficiency. The given solution uses a recursive approach to calculate the number of ways to traverse a graph from the top-left corner to the bottom-right corner. However, this solution has some drawbacks that make it inefficient for larger inputs: 1. Exponential Time Complexity: The recursive function makes multiple recursive calls, each with a smaller width or height. As a result, the number of function calls grows exponentially with the size of the input. This leads to a high time complexity, making the solution inefficient for larger graphs. The time complexity is O(2^(width+height)), which can quickly become unmanageable. 2. Overlapping Subproblems: The recursive function suffers from redundant calculations of the same subproblems. For example, when calculating the number of ways for a specific width and height, the function may recursively calculate the number of ways for smaller widths and heights multiple times. This leads to redundant work and decreases efficiency. 3. Lack of Memoization: The solution does not utilize memoization to store the results of previously solved subproblems. Without memoization, the recursive function ends up recalculating the same subproblems multiple times, further reducing efficiency. Due to these reasons, the given recursive solution is considered inefficient and impractical for larger graph sizes. It is prone to exponential time complexity and redundant calculations, making it unsuitable for real-world scenarios where efficiency is crucial. Alternative approaches, such as the dynamic programming solution mentioned earlier, can provide better performance by avoiding redundant calculations and improving time complexity. */ int numberOfWaysToTraverseGraphRecursive(int width, int height) { // Base case: when the width or height is 1, there is only one way to reach the destination if (width == 1 || height == 1) { return 1; } // Recursive case: sum the number of ways from the cell above and the cell to the left return numberOfWaysToTraverseGraphRecursive(width - 1, height) + numberOfWaysToTraverseGraphRecursive(width, height - 1); } ================================================ FILE: Dynamic Programming/num_ways_to_traverse_graph.go ================================================ /* You're given two positive integers representing the width and height of a grid-shaped, rectangular graph. Write a function that returns the number of ways to reach the bottom right corner of the graph when starting at the top left corner. Each move you take must either go down or right. In other words, you can never move up or left in the graph. For example, given the graph illustrated below, with width = 2 and height = 3 , there are three ways to reach the bottom right corner when starting at the top left corner: _ _ |_|_| |_|_| |_|_| Down Down Right Right Down Down Down Right Down Sample Input: Width : 4 height: 3 Output: 10 Explanation: The code snippet implements the `NumberOfWaysToTraverseGraph` function, which calculates the number of ways to traverse a 2D graph from the top-left corner to the bottom-right corner. The graph has a given width and height. Here's a breakdown of the code: 1. The function takes two parameters: `width` and `height`, representing the dimensions of the graph. 2. It initializes a 2D slice called `numberOfWays` with dimensions `(height+1) x (width+1)`. The additional "+1" is to account for the boundary cases when traversing the graph. 3. It enters a nested loop to iterate over the graph cells. The outer loop iterates over the width indices (`widthIdx`), and the inner loop iterates over the height indices (`heightIdx`). 4. For each cell, it checks if it is on the top row (`heightIdx == 1`) or the leftmost column (`widthIdx == 1`). If so, it means that there is only one way to reach that cell, either by moving right or moving down. Therefore, it sets `numberOfWays[heightIdx][widthIdx]` to 1. 5. If the cell is not on the top row or the leftmost column, it means that it can be reached by either moving from the cell above (up) or the cell to the left (left). The number of ways to reach the current cell is the sum of the number of ways to reach the cell above and the number of ways to reach the cell to the left. This value is stored in `numberOfWays[heightIdx][widthIdx]`. 6. After iterating over all cells, the function returns the value stored in the bottom-right corner of `numberOfWays`, which represents the total number of ways to traverse the graph. The algorithm uses dynamic programming to build the `numberOfWays` matrix iteratively, starting from the top-left corner and moving towards the bottom-right corner. By calculating the number of ways to reach each cell based on the number of ways to reach its neighboring cells, it avoids redundant calculations and computes the result efficiently. The time complexity of the algorithm is O(width * height) since it iterates over all cells of the graph. The space complexity is also O(width * height) since it uses the `numberOfWays` matrix to store intermediate results. */ package main func NumberOfWaysToTraverseGraph(width int, height int) int { // Initialize the numberOfWays matrix with dimensions (height+1) x (width+1) // The extra "+1" is to account for the boundary cases when traversing the graph numberOfWays := make([][]int, height+1) for i := range numberOfWays { numberOfWays[i] = make([]int, width+1) } // Iterate over the graph cells for widthIdx := 1; widthIdx < width+1; widthIdx++ { for heightIdx := 1; heightIdx < height+1; heightIdx++ { // Check if the current cell is on the top row or the leftmost column if widthIdx == 1 || heightIdx == 1 { // If so, there is only one way to reach this cell (moving right or moving down) numberOfWays[heightIdx][widthIdx] = 1 } else { // If the cell is not on the top row or the leftmost column, // calculate the number of ways to reach this cell based on the // number of ways to reach the cell above (up) and the cell to the left (left) waysLeft := numberOfWays[heightIdx][widthIdx-1] waysUp := numberOfWays[heightIdx-1][widthIdx] numberOfWays[heightIdx][widthIdx] = waysLeft + waysUp } } } // Return the number of ways to reach the bottom-right corner of the graph return numberOfWays[height][width] } /* Combinatorics Solution The given code snippet aims to calculate the number of ways to traverse a graph from the top-left corner to the bottom-right corner. Let's break down the solution and provide a detailed explanation: 1. The `NumberOfWaysToTraverseGraph` function takes two parameters: `width` and `height`, representing the dimensions of the graph. 2. The variables `xDistanceToCorner` and `yDistanceToCorner` are calculated by subtracting 1 from the `width` and `height` respectively. These variables represent the distances from the top-left corner to the bottom-right corner along the x-axis and y-axis. 3. The `factorial` function is defined separately to calculate the factorial of a number. It takes a number `num` as input and uses an iterative approach to calculate the factorial. 4. In the `NumberOfWaysToTraverseGraph` function, the numerator is calculated as the factorial of the sum of `xDistanceToCorner` and `yDistanceToCorner`. This represents the total number of possible paths from the top-left corner to the bottom-right corner. 5. The denominator is calculated as the product of the factorials of `xDistanceToCorner` and `yDistanceToCorner`. This represents the number of ways to arrange the steps along the x-axis and y-axis. 6. Finally, the function returns the result by dividing the numerator by the denominator, giving the total number of ways to traverse the graph. The solution relies on the concept of combinatorics, specifically the binomial coefficient, to calculate the number of ways to traverse the graph. By using factorials, it accounts for all possible paths and eliminates duplicate paths. This approach provides an efficient solution to the problem. O(n + m) time | O(1) space - where n is the width of the graph and m is the height */ func NumberOfWaysToTraverseGraphCombinatorics(width int, height int) int { // Calculate the distance to the bottom-right corner of the graph xDistanceToCorner := width - 1 yDistanceToCorner := height - 1 // Calculate the number of ways to traverse the graph using combinatorics // by calculating the binomial coefficient of (xDistanceToCorner + yDistanceToCorner) choose xDistanceToCorner // where (n choose k) = n! / (k! * (n-k)!) numerator := factorial(xDistanceToCorner + yDistanceToCorner) denominator := factorial(xDistanceToCorner) * factorial(yDistanceToCorner) // Return the result by dividing the numerator by the denominator return numerator / denominator } func factorial(num int) int { // Calculate the factorial of a number using an iterative approach result := 1 for n := 2; n <= num; n++ { result *= n } return result } /* Recursive solution The given solution aims to calculate the number of ways to traverse a graph from the top-left corner to the bottom-right corner. It uses a recursive approach to break down the problem into smaller subproblems. Here's how the solution works: 1. The function `NumberOfWaysToTraverseGraph` takes the width and height of the graph as input and returns the number of ways to traverse it. 2. The base case of the recursion is when either the width or height is equal to 1. In this case, there is only one way to traverse the graph: either by moving only horizontally or vertically. Therefore, the function returns 1. 3. For other cases where the width and height are both greater than 1, the function recursively calls itself with two smaller subproblems: - One subproblem is created by reducing the width by 1 and keeping the same height. - The other subproblem is created by keeping the same width and reducing the height by 1. 4. The number of ways to traverse the current graph is calculated by summing up the number of ways from the two subproblems. 5. The recursion continues until it reaches the base case, where the width or height becomes 1, and eventually returns the total number of ways to traverse the graph. While this recursive approach is conceptually simple, it suffers from efficiency issues due to exponential time complexity and redundant calculations. As the graph size increases, the number of recursive calls grows exponentially, leading to a significant increase in computation time. Additionally, without memoization, the function recalculates the same subproblems multiple times, further reducing efficiency. To address these drawbacks, alternative approaches like dynamic programming or memoization can be employed to store and reuse the results of previously solved subproblems, avoiding redundant calculations and improving efficiency. The given solution uses a recursive approach to calculate the number of ways to traverse a graph from the top-left corner to the bottom-right corner. However, this solution has some drawbacks that make it inefficient for larger inputs: 1. Exponential Time Complexity: The recursive function makes multiple recursive calls, each with a smaller width or height. As a result, the number of function calls grows exponentially with the size of the input. This leads to a high time complexity, making the solution inefficient for larger graphs. The time complexity is O(2^(width+height)), which can quickly become unmanageable. 2. Overlapping Subproblems: The recursive function suffers from redundant calculations of the same subproblems. For example, when calculating the number of ways for a specific width and height, the function may recursively calculate the number of ways for smaller widths and heights multiple times. This leads to redundant work and decreases efficiency. 3. Lack of Memoization: The solution does not utilize memoization to store the results of previously solved subproblems. Without memoization, the recursive function ends up recalculating the same subproblems multiple times, further reducing efficiency. Due to these reasons, the given recursive solution is considered inefficient and impractical for larger graph sizes. It is prone to exponential time complexity and redundant calculations, making it unsuitable for real-world scenarios where efficiency is crucial. Alternative approaches, such as the dynamic programming solution mentioned earlier, can provide better performance by avoiding redundant calculations and improving time complexity. */ func NumberOfWaysToTraverseGraphRecursive(width int, height int) int { if width == 1 || height == 1 { return 1 } return NumberOfWaysToTraverseGraph(width - 1, height) + NumberOfWaysToTraverseGraph(width, height - 1) } ================================================ FILE: Dynamic Programming/num_ways_to_traverse_graph.js ================================================ /* You're given two positive integers representing the width and height of a grid-shaped, rectangular graph. Write a function that returns the number of ways to reach the bottom right corner of the graph when starting at the top left corner. Each move you take must either go down or right. In other words, you can never move up or left in the graph. For example, given the graph illustrated below, with width = 2 and height = 3 , there are three ways to reach the bottom right corner when starting at the top left corner: _ _ |_|_| |_|_| |_|_| Down Down Right Right Down Down Down Right Down Sample Input: Width : 4 height: 3 Output: 10 Explanation: The code snippet implements the `NumberOfWaysToTraverseGraph` function, which calculates the number of ways to traverse a 2D graph from the top-left corner to the bottom-right corner. The graph has a given width and height. Here's a breakdown of the code: 1. The function takes two parameters: `width` and `height`, representing the dimensions of the graph. 2. It initializes a 2D slice called `numberOfWays` with dimensions `(height+1) x (width+1)`. The additional "+1" is to account for the boundary cases when traversing the graph. 3. It enters a nested loop to iterate over the graph cells. The outer loop iterates over the width indices (`widthIdx`), and the inner loop iterates over the height indices (`heightIdx`). 4. For each cell, it checks if it is on the top row (`heightIdx == 1`) or the leftmost column (`widthIdx == 1`). If so, it means that there is only one way to reach that cell, either by moving right or moving down. Therefore, it sets `numberOfWays[heightIdx][widthIdx]` to 1. 5. If the cell is not on the top row or the leftmost column, it means that it can be reached by either moving from the cell above (up) or the cell to the left (left). The number of ways to reach the current cell is the sum of the number of ways to reach the cell above and the number of ways to reach the cell to the left. This value is stored in `numberOfWays[heightIdx][widthIdx]`. 6. After iterating over all cells, the function returns the value stored in the bottom-right corner of `numberOfWays`, which represents the total number of ways to traverse the graph. The algorithm uses dynamic programming to build the `numberOfWays` matrix iteratively, starting from the top-left corner and moving towards the bottom-right corner. By calculating the number of ways to reach each cell based on the number of ways to reach its neighboring cells, it avoids redundant calculations and computes the result efficiently. The time complexity of the algorithm is O(width * height) since it iterates over all cells of the graph. The space complexity is also O(width * height) since it uses the `numberOfWays` matrix to store intermediate results. */ function numberOfWaysToTraverseGraph(width, height) { // Create a 2D array to store the number of ways to reach each cell const numberOfWays = new Array(height) .fill(1) .map(() => new Array(width).fill(1)); // Iterate through the cells from top to bottom and left to right for (let i = 1; i < height; i++) { for (let j = 1; j < width; j++) { // Calculate the number of ways to reach the current cell // by summing the number of ways from the cell above and the cell to the left numberOfWays[i][j] = numberOfWays[i - 1][j] + numberOfWays[i][j - 1]; } } // Return the number of ways to reach the bottom-right corner of the graph return numberOfWays[height - 1][width - 1]; } /* Combinatorics Solution The given code snippet aims to calculate the number of ways to traverse a graph from the top-left corner to the bottom-right corner. Let's break down the solution and provide a detailed explanation: 1. The `NumberOfWaysToTraverseGraph` function takes two parameters: `width` and `height`, representing the dimensions of the graph. 2. The variables `xDistanceToCorner` and `yDistanceToCorner` are calculated by subtracting 1 from the `width` and `height` respectively. These variables represent the distances from the top-left corner to the bottom-right corner along the x-axis and y-axis. 3. The `factorial` function is defined separately to calculate the factorial of a number. It takes a number `num` as input and uses an iterative approach to calculate the factorial. 4. In the `NumberOfWaysToTraverseGraph` function, the numerator is calculated as the factorial of the sum of `xDistanceToCorner` and `yDistanceToCorner`. This represents the total number of possible paths from the top-left corner to the bottom-right corner. 5. The denominator is calculated as the product of the factorials of `xDistanceToCorner` and `yDistanceToCorner`. This represents the number of ways to arrange the steps along the x-axis and y-axis. 6. Finally, the function returns the result by dividing the numerator by the denominator, giving the total number of ways to traverse the graph. The solution relies on the concept of combinatorics, specifically the binomial coefficient, to calculate the number of ways to traverse the graph. By using factorials, it accounts for all possible paths and eliminates duplicate paths. This approach provides an efficient solution to the problem. O(n + m) time | O(1) space - where n is the width of the graph and m is the height */ function numberOfWaysToTraverseGraphCombinatorics(width, height) { // Calculate the distances to the bottom-right corner of the graph const xDistanceToCorner = width - 1; const yDistanceToCorner = height - 1; // Calculate the numerator and denominator for the binomial coefficient const numerator = factorial(xDistanceToCorner + yDistanceToCorner); const denominator = factorial(xDistanceToCorner) * factorial(yDistanceToCorner); // Return the result by dividing the numerator by the denominator return numerator / denominator; } function factorial(n) { // Base case: factorial of 0 or 1 is 1 if (n <= 1) { return 1; } // Recursive case: compute factorial by multiplying n with factorial(n-1) return n * factorial(n - 1); } /* Recursive solution The given solution aims to calculate the number of ways to traverse a graph from the top-left corner to the bottom-right corner. It uses a recursive approach to break down the problem into smaller subproblems. Here's how the solution works: 1. The function `NumberOfWaysToTraverseGraph` takes the width and height of the graph as input and returns the number of ways to traverse it. 2. The base case of the recursion is when either the width or height is equal to 1. In this case, there is only one way to traverse the graph: either by moving only horizontally or vertically. Therefore, the function returns 1. 3. For other cases where the width and height are both greater than 1, the function recursively calls itself with two smaller subproblems: - One subproblem is created by reducing the width by 1 and keeping the same height. - The other subproblem is created by keeping the same width and reducing the height by 1. 4. The number of ways to traverse the current graph is calculated by summing up the number of ways from the two subproblems. 5. The recursion continues until it reaches the base case, where the width or height becomes 1, and eventually returns the total number of ways to traverse the graph. While this recursive approach is conceptually simple, it suffers from efficiency issues due to exponential time complexity and redundant calculations. As the graph size increases, the number of recursive calls grows exponentially, leading to a significant increase in computation time. Additionally, without memoization, the function recalculates the same subproblems multiple times, further reducing efficiency. To address these drawbacks, alternative approaches like dynamic programming or memoization can be employed to store and reuse the results of previously solved subproblems, avoiding redundant calculations and improving efficiency. The given solution uses a recursive approach to calculate the number of ways to traverse a graph from the top-left corner to the bottom-right corner. However, this solution has some drawbacks that make it inefficient for larger inputs: 1. Exponential Time Complexity: The recursive function makes multiple recursive calls, each with a smaller width or height. As a result, the number of function calls grows exponentially with the size of the input. This leads to a high time complexity, making the solution inefficient for larger graphs. The time complexity is O(2^(width+height)), which can quickly become unmanageable. 2. Overlapping Subproblems: The recursive function suffers from redundant calculations of the same subproblems. For example, when calculating the number of ways for a specific width and height, the function may recursively calculate the number of ways for smaller widths and heights multiple times. This leads to redundant work and decreases efficiency. 3. Lack of Memoization: The solution does not utilize memoization to store the results of previously solved subproblems. Without memoization, the recursive function ends up recalculating the same subproblems multiple times, further reducing efficiency. Due to these reasons, the given recursive solution is considered inefficient and impractical for larger graph sizes. It is prone to exponential time complexity and redundant calculations, making it unsuitable for real-world scenarios where efficiency is crucial. Alternative approaches, such as the dynamic programming solution mentioned earlier, can provide better performance by avoiding redundant calculations and improving time complexity. */ function numberOfWaysToTraverseGraphRecursive(width, height) { // Base case: when the width or height is 1, there is only one way to reach the destination if (width === 1 || height === 1) { return 1; } // Recursive case: sum the number of ways from the cell above and the cell to the left return ( numberOfWaysToTraverseGraphRecursive(width - 1, height) + numberOfWaysToTraverseGraphRecursive(width, height - 1) ); } ================================================ FILE: Dynamic Programming/num_ways_to_traverse_graph.py ================================================ ''' You're given two positive integers representing the width and height of a grid-shaped, rectangular graph. Write a function that returns the number of ways to reach the bottom right corner of the graph when starting at the top left corner. Each move you take must either go down or right. In other words, you can never move up or left in the graph. For example, given the graph illustrated below, with width = 2 and height = 3 , there are three ways to reach the bottom right corner when starting at the top left corner: _ _ |_|_| |_|_| |_|_| Down Down Right Right Down Down Down Right Down Sample Input: Width : 4 height: 3 Output: 10 Explanation: The code snippet implements the `NumberOfWaysToTraverseGraph` function, which calculates the number of ways to traverse a 2D graph from the top-left corner to the bottom-right corner. The graph has a given width and height. Here's a breakdown of the code: 1. The function takes two parameters: `width` and `height`, representing the dimensions of the graph. 2. It initializes a 2D slice called `numberOfWays` with dimensions `(height+1) x (width+1)`. The additional "+1" is to account for the boundary cases when traversing the graph. 3. It enters a nested loop to iterate over the graph cells. The outer loop iterates over the width indices (`widthIdx`), and the inner loop iterates over the height indices (`heightIdx`). 4. For each cell, it checks if it is on the top row (`heightIdx == 1`) or the leftmost column (`widthIdx == 1`). If so, it means that there is only one way to reach that cell, either by moving right or moving down. Therefore, it sets `numberOfWays[heightIdx][widthIdx]` to 1. 5. If the cell is not on the top row or the leftmost column, it means that it can be reached by either moving from the cell above (up) or the cell to the left (left). The number of ways to reach the current cell is the sum of the number of ways to reach the cell above and the number of ways to reach the cell to the left. This value is stored in `numberOfWays[heightIdx][widthIdx]`. 6. After iterating over all cells, the function returns the value stored in the bottom-right corner of `numberOfWays`, which represents the total number of ways to traverse the graph. The algorithm uses dynamic programming to build the `numberOfWays` matrix iteratively, starting from the top-left corner and moving towards the bottom-right corner. By calculating the number of ways to reach each cell based on the number of ways to reach its neighboring cells, it avoids redundant calculations and computes the result efficiently. The time complexity of the algorithm is O(width * height) since it iterates over all cells of the graph. The space complexity is also O(width * height) since it uses the `numberOfWays` matrix to store intermediate results. ''' def number_of_ways_to_traverse_graph(width, height): # Initialize the numberOfWays matrix with dimensions (height+1) x (width+1) # The extra "+1" is to account for the boundary cases when traversing the graph numberOfWays = [[0] * (width + 1) for _ in range(height + 1)] # Iterate over the graph cells for widthIdx in range(1, width + 1): for heightIdx in range(1, height + 1): # Check if the current cell is on the top row or the leftmost column if widthIdx == 1 or heightIdx == 1: # If so, there is only one way to reach this cell (moving right or moving down) numberOfWays[heightIdx][widthIdx] = 1 else: # If the cell is not on the top row or the leftmost column, # calculate the number of ways to reach this cell based on the # number of ways to reach the cell above (up) and the cell to the left (left) waysLeft = numberOfWays[heightIdx][widthIdx - 1] waysUp = numberOfWays[heightIdx - 1][widthIdx] numberOfWays[heightIdx][widthIdx] = waysLeft + waysUp # Return the number of ways to reach the bottom-right corner of the graph return numberOfWays[height][width] ''' Combinatorics Solution The given code snippet aims to calculate the number of ways to traverse a graph from the top-left corner to the bottom-right corner. Let's break down the solution and provide a detailed explanation: 1. The `NumberOfWaysToTraverseGraph` function takes two parameters: `width` and `height`, representing the dimensions of the graph. 2. The variables `xDistanceToCorner` and `yDistanceToCorner` are calculated by subtracting 1 from the `width` and `height` respectively. These variables represent the distances from the top-left corner to the bottom-right corner along the x-axis and y-axis. 3. The `factorial` function is defined separately to calculate the factorial of a number. It takes a number `num` as input and uses an iterative approach to calculate the factorial. 4. In the `NumberOfWaysToTraverseGraph` function, the numerator is calculated as the factorial of the sum of `xDistanceToCorner` and `yDistanceToCorner`. This represents the total number of possible paths from the top-left corner to the bottom-right corner. 5. The denominator is calculated as the product of the factorials of `xDistanceToCorner` and `yDistanceToCorner`. This represents the number of ways to arrange the steps along the x-axis and y-axis. 6. Finally, the function returns the result by dividing the numerator by the denominator, giving the total number of ways to traverse the graph. The solution relies on the concept of combinatorics, specifically the binomial coefficient, to calculate the number of ways to traverse the graph. By using factorials, it accounts for all possible paths and eliminates duplicate paths. This approach provides an efficient solution to the problem. O(n + m) time | O(1) space - where n is the width of the graph and m is the height ''' import math def number_of_ways_to_traverse_graph_combinatorics(width, height): # Calculate the distance to the bottom-right corner of the graph x_distance_to_corner = width - 1 y_distance_to_corner = height - 1 # Calculate the number of ways to traverse the graph using combinatorics # by calculating the binomial coefficient of (x_distance_to_corner + y_distance_to_corner) choose x_distance_to_corner # where (n choose k) = n! / (k! * (n-k)!) numerator = math.factorial(x_distance_to_corner + y_distance_to_corner) denominator = math.factorial(x_distance_to_corner) * math.factorial(y_distance_to_corner) # Return the result by dividing the numerator by the denominator return numerator // denominator ''' Recursive solution The given solution aims to calculate the number of ways to traverse a graph from the top-left corner to the bottom-right corner. It uses a recursive approach to break down the problem into smaller subproblems. Here's how the solution works: 1. The function `NumberOfWaysToTraverseGraph` takes the width and height of the graph as input and returns the number of ways to traverse it. 2. The base case of the recursion is when either the width or height is equal to 1. In this case, there is only one way to traverse the graph: either by moving only horizontally or vertically. Therefore, the function returns 1. 3. For other cases where the width and height are both greater than 1, the function recursively calls itself with two smaller subproblems: - One subproblem is created by reducing the width by 1 and keeping the same height. - The other subproblem is created by keeping the same width and reducing the height by 1. 4. The number of ways to traverse the current graph is calculated by summing up the number of ways from the two subproblems. 5. The recursion continues until it reaches the base case, where the width or height becomes 1, and eventually returns the total number of ways to traverse the graph. While this recursive approach is conceptually simple, it suffers from efficiency issues due to exponential time complexity and redundant calculations. As the graph size increases, the number of recursive calls grows exponentially, leading to a significant increase in computation time. Additionally, without memoization, the function recalculates the same subproblems multiple times, further reducing efficiency. To address these drawbacks, alternative approaches like dynamic programming or memoization can be employed to store and reuse the results of previously solved subproblems, avoiding redundant calculations and improving efficiency. The given solution uses a recursive approach to calculate the number of ways to traverse a graph from the top-left corner to the bottom-right corner. However, this solution has some drawbacks that make it inefficient for larger inputs: 1. Exponential Time Complexity: The recursive function makes multiple recursive calls, each with a smaller width or height. As a result, the number of function calls grows exponentially with the size of the input. This leads to a high time complexity, making the solution inefficient for larger graphs. The time complexity is O(2^(width+height)), which can quickly become unmanageable. 2. Overlapping Subproblems: The recursive function suffers from redundant calculations of the same subproblems. For example, when calculating the number of ways for a specific width and height, the function may recursively calculate the number of ways for smaller widths and heights multiple times. This leads to redundant work and decreases efficiency. 3. Lack of Memoization: The solution does not utilize memoization to store the results of previously solved subproblems. Without memoization, the recursive function ends up recalculating the same subproblems multiple times, further reducing efficiency. Due to these reasons, the given recursive solution is considered inefficient and impractical for larger graph sizes. It is prone to exponential time complexity and redundant calculations, making it unsuitable for real-world scenarios where efficiency is crucial. Alternative approaches, such as the dynamic programming solution mentioned earlier, can provide better performance by avoiding redundant calculations and improving time complexity. ''' def number_of_ways_to_traverse_graph_recursive(width, height): if width == 1 or height == 1: return 1 return number_of_ways_to_traverse_graph_recursive(width - 1, height) + number_of_ways_to_traverse_graph_recursive(width, height - 1) ================================================ FILE: Dynamic Programming/numbers_in_pi.go ================================================ /* Given a string representation of the first n digits of Pi and a list of positive integers (all in string format), write a function that returns the smallest number of spaces that can be added to the n digits of Pi such that all resulting numbers are found in the list of integers. Sample Input pi = "3141592653589793238462643383279" numbers : ["314159265358979323846", "26433", "8", "3279", "314159265", "35897932384626433832", "79"] Output: 2 Explanation: The given code snippet is for solving the "Numbers in Pi" problem using a recursive approach with memoization (dynamic programming) to find the minimum number of spaces required to divide the given string representation of pi into valid numbers from a list of given numbers. The problem is as follows: Given a string representation of the irrational number pi and a list of numbers, find the minimum number of spaces required to divide the string into valid numbers such that each number is present in the given list. Let's go through the code step by step: 1. `NumbersInPi` function: - This is the main function that takes the string representation of pi and a list of numbers as input and returns the minimum number of spaces required. It initializes a `numbersTable` to store the numbers from the input list for quick lookup and then calls the `getMinSpaces` function with initial parameters. 2. `getMinSpaces` function: - This is a recursive function with memoization. It takes the string representation of pi, the `numbersTable`, a `cache` (a map to store previously calculated values to avoid redundant calculations), and the current `idx` (position in the pi string) as input. - It first checks if the base case has been reached by comparing `idx` with the length of the pi string. If so, it returns -1. - Next, it checks if the result for the current `idx` is already present in the cache. If yes, it returns the cached result. - If the base case is not reached and the result is not in the cache, it initializes a variable `minSpaces` to store the minimum spaces required for the current `idx`. It sets `minSpaces` to a large value (initialized as `math.MaxInt32`) to ensure correct comparisons later. - Then, it iterates from the current `idx` to the end of the pi string and forms a prefix string from `idx` to the current iteration index (`i`). - If the prefix string is found in the `numbersTable`, it means it is a valid number from the given list. The function then recursively calls itself with the suffix (remaining part) of the pi string starting from index `i+1`. - The result of the recursive call is stored in `minSpacesInSuffix`. - The minimum of `minSpaces` and `minSpacesInSuffix + 1` is computed and assigned back to `minSpaces`. The "+1" indicates the current valid number prefix, which requires one space. - The loop continues, trying all possible valid prefixes from the current index. - Finally, the `minSpaces` value is stored in the `cache` to avoid redundant calculations and returned as the result for the current `idx`. 3. `min` function: - A simple utility function to return the minimum of two integers. The `NumbersInPi` function is the entry point, and the `getMinSpaces` function handles the recursive computation with memoization. By using memoization, the code optimizes and reduces redundant calculations, making it more efficient than a pure recursive solution. The result returned by `NumbersInPi` is the minimum number of spaces required to divide the pi string into valid numbers from the given list. If it is not possible to form valid numbers using the given list, the function returns -1. */ package main import "math" // NumbersInPi finds the minimum number of spaces needed to divide the pi string // into valid numbers from the given list of numbers. func NumbersInPi(pi string, numbers []string) int { numbersTable := map[string]bool{} for _, number := range numbers { numbersTable[number] = true } // Cache to store results of subproblems to avoid redundant calculations cache := map[int]int{} minSpaces := getMinSpaces(pi, numbersTable, cache, 0) if minSpaces == math.MaxInt32 { return -1 } return minSpaces } // getMinSpaces calculates the minimum number of spaces needed to divide the remaining // suffix of the pi string into valid numbers from the numbersTable. func getMinSpaces(pi string, numbersTable map[string]bool, cache map[int]int, idx int) int { // Base case: If the end of the pi string is reached, return -1. // This indicates that the suffix of the pi string cannot be divided into valid numbers. if idx == len(pi) { return -1 } else if val, found := cache[idx]; found { // If the result for the current index is already in the cache, return it. return val } minSpaces := math.MaxInt32 // Iterate over possible prefixes starting from the current index. for i := idx; i < len(pi); i++ { prefix := pi[idx : i+1] // If the prefix is found in the numbersTable, it is a valid number prefix. if _, found := numbersTable[prefix]; found { // Recursively calculate the minimum number of spaces in the suffix. minSpacesInSuffix := getMinSpaces(pi, numbersTable, cache, i+1) // Update the minimum spaces with the current prefix if it leads to a valid number. minSpaces = min(minSpaces, minSpacesInSuffix+1) } } // Cache the result for the current index to avoid redundant calculations. cache[idx] = minSpaces return cache[idx] } // min returns the minimum of two integers. func min(a, b int) int { if a < b { return a } return b } ================================================ FILE: Dynamic Programming/reconstruct_bst_in_python.py ================================================ class TreeNode: def __init__(self, val): self.val = val self.left = None self.right = None def constructBST(preorder): if not preorder: return None root = TreeNode(preorder[0]) i = 1 while i < len(preorder) and preorder[i] < root.val: i += 1 root.left = constructBST(preorder[1:i]) root.right = constructBST(preorder[i:]) return root def inorderTraversal(root): if root is None: return [] return inorderTraversal(root.left) + [root.val] + inorderTraversal(root.right) # Sample Input preorder = [10, 4, 2, 1, 5, 17, 19, 18] # Construct the BST root = constructBST(preorder) # Print the inorder traversal of the reconstructed BST inorder = inorderTraversal(root) print(inorder) ================================================ FILE: Dynamic Programming/rod_cutting_problem_dp.cpp ================================================ /* Given a rod of length n inches and an array of prices that contains prices of all pieces of size smaller than n. Determine the maximum value obtainable by cutting up the rod and selling the pieces. For example, if length of the rod is 8 and the values of different pieces are given as following, then the maximum obtainable value is 22 (by cutting in two pieces of lengths 2 and 6) Input : 8 : 1 5 8 9 10 17 17 20 Output : 22 */ // Dynamic Programming solution TC : O(n^2) // Program Author: Abhisek Kumar Gupta #include using namespace std; int max_profit(vector profit, int total_length){ int dp[100] = {}; for(int length = 1; length <= total_length; length++){ int best = 0; for(int cut = 1; cut <= length; cut++){ best = max(best, profit[cut] + dp[length - cut]); } dp[length] = best; } return dp[total_length]; } int main(){ int total_length; cin >> total_length; vector profit(total_length + 1); for(int length = 1; length <= total_length; length++) cin >> profit[length]; int result = max_profit(profit, total_length); cout << result; return 0; } ================================================ FILE: Dynamic Programming/rod_cutting_problem_memoized.cpp ================================================ /* Given a rod of length n inches and an array of prices that contains prices of all pieces of size smaller than n. Determine the maximum value obtainable by cutting up the rod and selling the pieces. For example, if length of the rod is 8 and the values of different pieces are given as following, then the maximum obtainable value is 22 (by cutting in two pieces of lengths 2 and 6) Input : 8 : 1 5 8 9 10 17 17 20 Output : 22 */ // Memoized solution TC : O(n^2) // Program Author: Abhisek Kumar Gupta #include using namespace std; int memoized[1000]; int max_profit(vector length, int n){ if(n == 0) return 0; int best = 0; if(memoized[n] != -1) return memoized[n]; for(int i = 0; i < n; i++){ int total_profit = length[i] + max_profit(length, n - (i + 1)); best = max(best, total_profit); memoized[n] = best; } return memoized[n]; } int main(){ memset(memoized, -1, sizeof(memoized)); int n; cin >> n; vector length(n); for(int i = 0; i < n; i++) cin >> length[i]; int result = max_profit(length, n); cout << result; return 0; } ================================================ FILE: Dynamic Programming/rod_cutting_problem_recursive.cpp ================================================ /* Given a rod of length n inches and an array of prices that contains prices of all pieces of size smaller than n. Determine the maximum value obtainable by cutting up the rod and selling the pieces. For example, if length of the rod is 8 and the values of different pieces are given as following, then the maximum obtainable value is 22 (by cutting in two pieces of lengths 2 and 6) Input : 8 : 1 5 8 9 10 17 17 20 Output : 22 */ // Recursive solution TC : O(2^n) // Program Author: Abhisek Kumar Gupta #include using namespace std; int max_profit(vector length, int n){ if(n == 0) return 0; int best = 0; for(int i = 0; i < n; i++){ int total_profit = length[i] + max_profit(length, n - (i + 1)); best = max(best, total_profit); } return best; } int main(){ int n; cin >> n; vector length(n); for(int i = 0; i < n; i++) cin >> length[i]; int result = max_profit(length, n); cout << result; return 0; } ================================================ FILE: Dynamic Programming/trapping_rain_water.cpp ================================================ /* Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it can trap after raining. Example 1: Input: height = [0,1,0,2,1,0,1,3,2,1,2,1] Output: 6 Explanation: The above elevation map (black section) is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped. Example 2: Input: height = [4,2,0,3,2,5] Output: 9 Constraints: n == height.length 1 <= n <= 2 * 104 0 <= height[i] <= 105 */ #include class Solution { public: int trap(vector& height) { int len = height.size(), result = 0; if(len == 0) return 0; int low = 0, high = len - 1, leftmax = 0, rightmax = 0; while(low <= high){ if(height[low] < height[high]){ if(height[low] > leftmax) leftmax = height[low]; else result += leftmax - height[low]; low++; } else{ if(height[high] > rightmax) rightmax = height[high]; else result += rightmax - height[high]; high--; } } return result; } }; ================================================ FILE: Dynamic Programming/unique_paths_with_obstacles.cpp ================================================ /* You are given an m x n integer array grid. There is a robot initially located at the top-left corner (i.e., grid[0][0]). The robot tries to move to the bottom-right corner (i.e., grid[m - 1][n - 1]). The robot can only move either down or right at any point in time. An obstacle and space are marked as 1 or 0 respectively in grid. A path that the robot takes cannot include any square that is an obstacle. Return the number of possible unique paths that the robot can take to reach the bottom-right corner. The testcases are generated so that the answer will be less than or equal to 2 * 109. Example 1: Input: obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]] Output: 2 Explanation: There is one obstacle in the middle of the 3x3 grid above. There are two ways to reach the bottom-right corner: 1. Right -> Right -> Down -> Down 2. Down -> Down -> Right -> Right */ class Solution { public: int uniquePathsWithObstacles(vector>& obstacleGrid) { int m = obstacleGrid.size(), n = obstacleGrid[0].size(); vector >dp(m + 1, vector (n + 1, 0)); dp[0][1] = 1; for(int i = 1; i<= m; i++){ for(int j = 1; j <= n; j++){ if(!obstacleGrid[i - 1][j - 1]){ dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; } } } return dp[m][n]; } }; ================================================ FILE: Dynamic Programming/wine_selling_problem_dp.cpp ================================================ /* Given n wines in a row, with integers denoting the cost of each wine respectively. Each year you can sale the first or the last wine in the row. However, the price of wines increases over time. Let the initial profits from the wines be P1, P2, P3…Pn. On the Yth year, the profit from the ith wine will be Y*Pi. Calculate the maximum profit from all the wines. Input : 5 : 2 4 6 2 5 Output : 64 */ // Dynamic Programming Approach TC : O(N^2) // Program Author : Abhisek Kumar Gupta #include using namespace std; int find_max_profit(int *A, int n){ int dp[100][100] = {}; int year = n; for(int i = 0; i < n; i++){ dp[i][i] = year * A[i]; } year--; for(int i = 2; i <= n; i++){ int start = 0; int end = n - i; while(start <= end){ int end_window = start + i - 1; int x = A[start] * year + dp[start + 1][end_window]; int y = A[end_window]* year + dp[start][end_window - 1]; dp[start][end_window] = max(x, y); start++; } year--; } /* for(int i = 0; i < n; i++){ for(int j = 0; j < n; j++){ cout << setw(5) << dp[i][j] << " "; } cout << "\n"; } */ return dp[0][n-1]; } int main(){ int n; cin >> n; int *A; for(int i = 0; i < n; i++) cin >> A[i]; int start = 0; int end = n - 1; int year = 1; int result = find_max_profit(A, n); cout << result; return 0; } ================================================ FILE: Dynamic Programming/wine_selling_problem_memoized.cpp ================================================ /* Given n wines in a row, with integers denoting the cost of each wine respectively. Each year you can sale the first or the last wine in the row. However, the price of wines increases over time. Let the initial profits from the wines be P1, P2, P3…Pn. On the Yth year, the profit from the ith wine will be Y*Pi. Calculate the maximum profit from all the wines. Input : 5 : 2 4 6 2 5 Output : 64 */ // Memoized Approach TC : O(N^2) // Program Author : Abhisek Kumar Gupta #include using namespace std; int memoized[1000][1000]; int find_max_profit(int *A, int start, int end, int year){ if(start > end) return 0; if(memoized[start][end] != -1) return memoized[start][end]; int r1 = A[start] * year + find_max_profit(A, start + 1, end, year + 1); int r2 = A[end] * year + find_max_profit(A, start, end - 1, year + 1); int answer = max(r1, r2); ; memoized[start][end] = answer; return memoized[start][end]; } int main(){ memset(memoized, -1, sizeof(memoized)); int n; cin >> n; int *A; for(int i = 0; i < n; i++) cin >> A[i]; int start = 0; int end = n - 1; int year = 1; int result = find_max_profit(A, start, end, year); cout << result; return 0; } ================================================ FILE: Dynamic Programming/wine_selling_problem_recursive.cpp ================================================ /* Given n wines in a row, with integers denoting the cost of each wine respectively. Each year you can sale the first or the last wine in the row. However, the price of wines increases over time. Let the initial profits from the wines be P1, P2, P3…Pn. On the Yth year, the profit from the ith wine will be Y*Pi. Calculate the maximum profit from all the wines. Input : 5 : 2 4 6 2 5 Output : 64 */ // Recursive Approach TC : O(2^n) // Program Author : Abhisek Kumar Gupta #include using namespace std; int find_max_profit(int *A, int start, int end, int year){ if(start > end) return 0; int r1 = A[start] * year + find_max_profit(A, start + 1, end, year + 1); int r2 = A[end] * year + find_max_profit(A, start, end - 1, year + 1); return max(r1, r2); } int main(){ int n; cin >> n; int *A; for(int i = 0; i < n; i++) cin >> A[i]; int start = 0; int end = n - 1; int year = 1; int result = find_max_profit(A, start, end, year); cout << result; return 0; } ================================================ FILE: Famous Algorithms/N_queen.js ================================================ function solveNQueens(n) { const board = new Array(n).fill().map(() => new Array(n).fill('.')); // Create an empty NxN chessboard const solutions = []; function isSafe(row, col) { // Check if no other queens are in the same column for (let i = 0; i < row; i++) { if (board[i][col] === 'Q') { return false; } } // Check upper-left diagonal for (let i = row, j = col; i >= 0 && j >= 0; i--, j--) { if (board[i][j] === 'Q') { return false; } } // Check upper-right diagonal for (let i = row, j = col; i >= 0 && j < n; i--, j++) { if (board[i][j] === 'Q') { return false; } } return true; } function solve(row) { if (row === n) { // Found a valid solution, push a copy of the board to the solutions array solutions.push(board.map(row => row.join(''))); return; } for (let col = 0; col < n; col++) { if (isSafe(row, col)) { board[row][col] = 'Q'; // Place a queen solve(row + 1); // Recursively move to the next row board[row][col] = '.'; // Backtrack by removing the queen } } } solve(0); // Start solving from the first row return solutions; } // Example usage: const n = 4; // Change this to the desired board size const solutions = solveNQueens(n); console.log(`Solutions for ${n}-Queens:`); for (const solution of solutions) { console.log(solution); } ================================================ FILE: Famous Algorithms/euclidean_algorithm.java ================================================ //extended version of Euclid's algorithm to find GCD of 2 numbers //runs in O(log N) time/space complexity for GCD of numbers a and b //in comparison, the standard Euclidean algorithm runs in O (log (min (a,b))) public class euclidean_algorithm { public static int euclid(int a, int b, int c, int d){ if(a == 0){ c = 0; d = 0; return b; } int c1 = 1,d1 = 1; int result = euclid(b%a, a, c1, d1); //update with recursive call c = d1 - (b / a) * c1; d = c1; return result; } //driver public static void main(String[] args) { int c =1, d = 1; int a = 45; int b = 10; int gcd = euclid(a, b, c, d); System.out.print("gcd of "+ a+"," +b +" is equal to: " + gcd); } } ================================================ FILE: Famous Algorithms/euclidean_algorithm.js ================================================ //extended version of Euclid's algorithm to find GCD of 2 numbers //runs in O(log N) time/space complexity for GCD of numbers a and b //in comparison, the standard Euclidean algorithm runs in O (log (min (a,b))) function euclid(a,b,c,d){ if(a == 0){ c = 0; d = 1; return b; } let result = euclid(b%a,a,c,d); //update with recursive values c = d- (b / a) * c; d = c; return result; } //modify a and b to find gcd of any 2 numbers let a = 450; let b = 100; let gcd = euclid(a,b,0,0); console.log(`GCD of ${a}, ${b} is equal to ${gcd}`); ================================================ FILE: Famous Algorithms/euclidean_algorithm.py ================================================ #extended version of Euclid's algorithm to find GCD of 2 numbers #runs in O(log N) time/space complexity for GCD of numbers a and b #in comparison, the standard Euclidean algorithm runs in O (log (min (a,b))) def euclidExtended(a,b): if a==0: return b,0, 1 result, a1, b1 = euclidExtended(b%a,a) a2 = b1- (b//a) *a1 b2 = a1 return result, a2, b2 #used as input to recursive call, #example driver, change a and b as desired a,b = 45,10 g,x,y = euclidExtended(a,b) print("gcd of", a,"," ,b ,"is equal to: " , g) ================================================ FILE: Famous Algorithms/kadanes_algorithm.c++ ================================================ /* Name : Rajeev Kumar Github username : Tonystark121 Repository name : data-structures-and-algorithms Problem : Kadane's algorithm in C++ Issue Number : #1179 Problem statement : Given an integer array nums, find the subarray with the largest sum, and return its sum. Sample testcases: Testcase 1 --> Input: number of elements in array = 8 nums = [-2,-3,5,-1,-2,1,5,-3] Output: 8 Testcase 2 --> Input: number of elements in array = 5 nums = [5,4,-1,7,8] Output: 23 Time Complexity = O(n) Space Complexity = O(1) Explanation: This code asks the user to enter the number of elements in an array, and then prompts them to enter each element of the array one at a time. Once the array is complete, the code applies the Kadane's algorithm to find the maximum sum of any subarray within the array, and then prints the result to the console. Kadane's algorithm is a way of finding the maximum sum of a contiguous subarray within an array, and it does so by keeping track of the maximum sum seen so far as it iterates through the array. At each step, it adds the current element to a running sum, and if that sum becomes negative, it resets the running sum to zero. If the running sum is ever greater than the maximum seen so far, it updates the maximum. Finally, it returns the maximum sum. */ // ----------------------------------------------------------------------------- code begins now! #include using namespace std; int main(){ // taking input number of array elements. int n; cin>>n; // taking input array elements. int arr[n]; for(int i=0;i>arr[i]; } // declare current maximum and maximum so far variable. int curr_max=0,max_so_far=INT_MIN; for(int i=0;imax_so_far){ max_so_far = curr_max; } } // output result. cout< b { return a } return b } q ================================================ FILE: Famous Algorithms/kadanes_algorithm.java ================================================ /* Name : Aneesh Github username : 007aneesh Repository name : data-structures-and-algorithms Problem : Kadane's algorithm in Java Issue Number : #1180 Problem statement : Given an integer array nums, find the subarray with the largest sum, and return its sum. Sample testcases: Testcase 1 --> Input: number of elements in array = 9 nums = [-2,1,-3,4,-1,2,1,-5,4] Output: 6 Testcase 2 --> Input: number of elements in array nums = [5,4,-1,7,8] Output: 23 Time Complexity = O(n) Space Complexity = O(1) Explanation: This code asks the user to enter the number of elements in an array, and then prompts them to enter each element of the array one at a time. Once the array is complete, the code applies the Kadane's algorithm to find the maximum sum of any subarray within the array, and then prints the result to the console. Kadane's algorithm is a way of finding the maximum sum of a contiguous subarray within an array, and it does so by keeping track of the maximum sum seen so far as it iterates through the array. At each step, it adds the current element to a running sum, and if that sum becomes negative, it resets the running sum to zero. If the running sum is ever greater than the maximum seen so far, it updates the maximum. Finally, it returns the maximum sum. */ // ----------------------------------------------------------------------------- code begins now! import java.util.Scanner; public class kadanes_algo { public static int maxSubArraySum(int[] arr) { if (arr == null || arr.length == 0) { return 0; } int max = 0; int sum = Integer.MIN_VALUE; for (int i = 0; i < arr.length; i++) { max += arr[i]; if (max < arr[i]) { max = arr[i]; } if (sum < max) { sum = max; } } return sum; } public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.print("Enter the number of elements in the array: "); int n = sc.nextInt(); int[] arr = new int[n]; System.out.println("Enter the elements of the array:"); for (int i = 0; i < n; i++) { arr[i] = sc.nextInt(); } int maxSum = maxSubArraySum(arr); System.out.println("The maximum subarray sum is " + maxSum); } } ================================================ FILE: Famous Algorithms/kadanes_algorithm.py ================================================ """ What is Kadane's Algorithm? Kadane's Algorithm is a way to find the maximum subarray sum in an array with a runtime of O(n). It is a dynamic programming algorithm that uses the fact that the maximum subarray sum ending at index i is either the value at index i or the maximum subarray sum ending at index i-1 plus the value at index i. Note: The subarray must be contiguous, all the values in the subarray must be next to each other in the original array. How does it work? The algorithm works by iterating through the array and keeping track of the maximum subarray sum seen so far and the maximum subarray sum ending at the current index. The maximum subarray sum ending at the current index is either the value at the current index or the maximum subarray sum ending at the previous index plus the value at the current index. Lets take the example: {-2, -3, 4, -1, -2, 1, 5, -3} max_so_far = INT_MIN max_ending_here = 0 for i=0, a[0] = -2 max_ending_here = max_ending_here + (-2) Set max_ending_here = 0 because max_ending_here < 0 and set max_so_far = -2 for i=1, a[1] = -3 max_ending_here = max_ending_here + (-3) Since max_ending_here = -3 and max_so_far = -2, max_so_far will remain -2 Set max_ending_here = 0 because max_ending_here < 0 for i=2, a[2] = 4 max_ending_here = max_ending_here + (4) max_ending_here = 4 max_so_far is updated to 4 because max_ending_here greater than max_so_far which was -2 till now for i=3, a[3] = -1 max_ending_here = max_ending_here + (-1) max_ending_here = 3 for i=4, a[4] = -2 max_ending_here = max_ending_here + (-2) max_ending_here = 1 for i=5, a[5] = 1 max_ending_here = max_ending_here + (1) max_ending_here = 2 for i=6, a[6] = 5 max_ending_here = max_ending_here + (5) max_ending_here = 7 max_so_far is updated to 7 because max_ending_here is greater than max_so_far for i=7, a[7] = -3 max_ending_here = max_ending_here + (-3) max_ending_here = 4 Time Complexity: O(n) Space Complexity: O(1) """ from sys import maxint # maxint is a constant that holds the maximum possible value for an integer in Python. def maxSubArraySum(a, size): # we take the max_so_far to be the smallest possible integer value max_so_far = -maxint - 1 # initialize max_ending_here to 0 max_ending_here = 0 for i in range(0, size): max_ending_here = max_ending_here + a[i] if max_so_far < max_ending_here: max_so_far = max_ending_here # if max_ending_here is negative, we set it to 0 if max_ending_here < 0: max_ending_here = 0 return max_so_far # Driver function to check the above function a = [-2, -3, 4, -1, -2, 1, 5, -3] print("Maximum contiguous sum is", maxSubArraySum(a, len(a))) ================================================ FILE: Famous Algorithms/kadenes_algorithm.js ================================================ /* What is Kadane's Algorithm? Kadane's Algorithm is a way to find the maximum subarray sum in an array with a runtime of O(n). It is a dynamic programming algorithm that uses the fact that the maximum subarray sum ending at index i is either the value at index i or the maximum subarray sum ending at index i-1 plus the value at index i. Note: The subarray must be contiguous, all the values in the subarray must be next to each other in the original array. How does it work? In this algorithim we maintain two variables, one will hold the maximum sum of contagious subarray and the other variable will hold sum of next element + current sum from previous iteration. If at any point the current sum + next element sum is less then 0 then we will reset the current sum to 0 and current sum + next element sum will start again from the next element. If the current sum + next element sum is greater then 0 and it is also greater then the maximum sum of contagious subarray then the variable of maximum sum of contagious subarray will be updated with current sum + next element sum Lets take the example: {-2, -3, 4, -1, -2, 1, 5, -3} In this example maxLargestSumTillNow holds the maximum sum of contagious subarray and newLargestSum hold the value of current sum + next element On initalizing max so far will be the max -ve number maxLargestSumTillNow = INT_MIN newLargestSum = 0 for i=0, a[0] = -2 newLargestSum = newLargestSum + (-2) Set newLargestSum = 0 because newLargestSum < 0 and set maxLargestSumTillNow = -2 for i=1, a[1] = -3 newLargestSum = newLargestSum + (-3) Since newLargestSum = -3 and maxLargestSumTillNow = -2, maxLargestSumTillNow will remain -2 Set newLargestSum = 0 because newLargestSum < 0 for i=2, a[2] = 4 newLargestSum = newLargestSum + (4) newLargestSum = 4 maxLargestSumTillNow is updated to 4 because newLargestSum greater than maxLargestSumTillNow which was -2 till now for i=3, a[3] = -1 newLargestSum = newLargestSum + (-1) newLargestSum = 3 for i=4, a[4] = -2 newLargestSum = newLargestSum + (-2) newLargestSum = 1 for i=5, a[5] = 1 newLargestSum = newLargestSum + (1) newLargestSum = 2 for i=6, a[6] = 5 newLargestSum = newLargestSum + (5) newLargestSum = 7 maxLargestSumTillNow is updated to 7 because newLargestSum is greater than maxLargestSumTillNow for i=7, a[7] = -3 newLargestSum = newLargestSum + (-3) newLargestSum = 4 Time Complexity: O(n) Space Complexity: O(1) */ function largestSumOfSubArray(arr) { if (arr.lenth == 1) { return arr[0]; } // Variable for maintaining Maximum sum of the subarray var maxint = Math.pow(2, 53); var maxLargestSumTillNow = -maxint - 1; // Variable to calclate the sum of subarray after each iteration var newLargestSum = 0; // Looping through the entire array for (i = 0; i < arr.length - 1; i++) { // Calculating the largest sum on each iteration newLargestSum += arr[i]; // If the largest sum value is greater then the maximum largest subarray value we have maintained then we will assign new value to maintained maximum largest subarray if (maxLargestSumTillNow < newLargestSum) { maxLargestSumTillNow = newLargestSum; } // If the largest sum is negative then we will reset the value of largest sum to 0 and start the calculation again of largest sum from next element if (newLargestSum < 0) { newLargestSum = 0; } } // After the completion of iteration we will return the max largest sub array value return maxLargestSumTillNow; } // Driver code var arr = [-2, -3, 4, -1, -2, 1, 5, -3]; console.log(largestSumOfSubArray(arr)); // Input: arr = [-2, -3, 4, -1, -2, 1, 5, -3]; //Output: 7 ================================================ FILE: Famous Algorithms/kmp.java ================================================ /** * Summary: * This program implements the Knuth-Morris-Pratt (KMP) algorithm for string pattern matching. * Given a text string and a pattern string, the algorithm searches for the pattern in the text and returns * the index where the pattern is found in the text. If the pattern is not found, it returns -1. * * Inputs: * The program expects two strings as inputs: text and pattern. * * Outputs: * The output of the program is the index where the pattern is found in the text, or -1 if the pattern is not found. * * Example Usage: * // Input * String text = "ABABDABACDABABCABAB"; * String pattern = "ABABCABAB"; * * // Execute the KMP algorithm for string pattern matching * int index = searchPattern(text, pattern); * * // Output * if (index != -1) { * System.out.println("Pattern found at index: " + index); * } else { * System.out.println("Pattern not found in the text."); * } * * Complexity Analysis: * The time complexity of the KMP algorithm is O(n + m), where n is the length of the text and m is the length of the pattern. * This is because the algorithm avoids unnecessary comparisons by utilizing the computed Longest Proper Prefix-Suffix (LPS) array. * The space complexity is O(m) as it requires storing the LPS array of the pattern. */ public class Graph_KMPAlgorithm { public static int[] computeLPSArray(String pattern) { int[] lps = new int[pattern.length()]; int len = 0; // Length of the previous longest prefix suffix int i = 1; while (i < pattern.length()) { if (pattern.charAt(i) == pattern.charAt(len)) { len++; lps[i] = len; i++; } else { if (len != 0) { len = lps[len - 1]; } else { lps[i] = 0; i++; } } } return lps; } public static int searchPattern(String text, String pattern) { int n = text.length(); int m = pattern.length(); int[] lps = computeLPSArray(pattern); int i = 0; // index for text int j = 0; // index for pattern while (i < n) { if (text.charAt(i) == pattern.charAt(j)) { i++; j++; } if (j == m) { return i - j; } else if (i < n && text.charAt(i) != pattern.charAt(j)) { if (j != 0) { j = lps[j - 1]; } else { i++; } } } return -1; // pattern not found in text } public static void main(String[] args) { String text = "ABABDABACDABABCABAB"; String pattern = "ABABCABAB"; int index = searchPattern(text, pattern); if (index != -1) { System.out.println("Pattern found at index: " + index); } else { System.out.println("Pattern not found in the text."); } } } ================================================ FILE: Famous Algorithms/kmp.js ================================================ /**The Knuth-Morris-Pratt (KMP) algorithm is a string matching algorithm that efficiently finds occurrences of a pattern within a text. It was developed by Donald Knuth and Vaughan Pratt in 1977. The key idea behind the KMP algorithm is to take advantage of the information present in the pattern itself to avoid unnecessary character comparisons during the search process. It achieves this by utilizing a preprocessed array called the Longest Proper Prefix which is also Suffix (LPS) array or failure function. Here's a step-by-step explanation of the KMP algorithm: 1. Preprocessing (Compute LPS Array): - Given a pattern of length m, the first step is to compute the LPS array, which holds information about the longest proper prefix that is also a suffix for each position in the pattern. - The LPS array is initialized with the first element as 0 and then iteratively calculated for each position of the pattern using the following rules: - If the characters at the current position and the previous longest proper prefix suffix match, increment the length of the prefix and store it in the LPS array. - If the characters don't match: - If the length of the prefix is not zero, move to the previous longest proper prefix suffix and continue the comparison. - If the length of the prefix is zero, store 0 in the LPS array for the current position. - This preprocessing step is done in O(m) time complexity. 2. Search (Pattern Matching): - With the LPS array computed, the search process begins by comparing characters of the text and pattern. - Initialize two pointers, i for the text and j for the pattern, both starting from 0. - Iterate over the text from left to right until i reaches the end of the text: - If the characters at the current positions i and j match, increment both i and j. - If j reaches the end of the pattern, a match is found: - Store the index (i - j) as an occurrence of the pattern in the text. - Move j to the previous longest proper prefix suffix using the LPS array. - If the characters at the current positions i and j don't match: - If j is not at the beginning of the pattern, move j to the previous longest proper prefix suffix using the LPS array. - If j is at the beginning of the pattern, increment i and continue the search. - The search process iterates over the text once and performs comparisons using the LPS array, resulting in a time complexity of O(n), where n is the length of the text. The KMP algorithm provides an efficient way to search for patterns within a text by avoiding redundant comparisons. It achieves this by utilizing the LPS array, which stores information about the pattern's structure. This makes the KMP algorithm more efficient than naive approaches such as the brute-force method, especially when the pattern has repeated characters or subsequences. */ /** * Computes the Longest Proper Prefix which is also Suffix (LPS) array for the given pattern. * The LPS array is used to determine the longest prefix of the pattern that is also a suffix. * * @param {string} pattern - The pattern string. * @returns {number[]} - The LPS array. */ function computeLPSArray(pattern) { const lps = [0]; // Initialize LPS array with the first element as 0. let len = 0; // Length of the previous longest prefix suffix. let i = 1; // Current index in the pattern string. while (i < pattern.length) { if (pattern[i] === pattern[len]) { len++; lps[i] = len; i++; } else { if (len !== 0) { // Move len to the previous longest prefix suffix value. len = lps[len - 1]; } else { lps[i] = 0; i++; } } } return lps; } /** * Performs the Knuth-Morris-Pratt (KMP) algorithm to find all occurrences of a pattern within a text. * * @param {string} text - The text string. * @param {string} pattern - The pattern string. * @returns {number[]} - An array of indices where the pattern is found within the text. */ function KMP(text, pattern) { const m = pattern.length; // Length of the pattern. const n = text.length; // Length of the text. const lps = computeLPSArray(pattern); // Compute the LPS array for the pattern. const indices = []; // Array to store the indices where the pattern is found. let i = 0; // Current index in the text string. let j = 0; // Current index in the pattern string. while (i < n) { if (pattern[j] === text[i]) { i++; j++; } if (j === m) { // Pattern found at index i - j. indices.push(i - j); j = lps[j - 1]; // Move j to the previous longest prefix suffix value. } else if (i < n && pattern[j] !== text[i]) { if (j !== 0) { j = lps[j - 1]; // Move j to the previous longest prefix suffix value. } else { i++; } } } return indices; } // Example usage: const text = "ABABDABACDABABCABAB"; const pattern = "ABABCABAB"; const indices = KMP(text, pattern); console.log("Pattern found at indices:", indices); // Pattern found at indices: [10] /** Time Complexity: The KMP algorithm has a time complexity of O(m + n), where m is the length of the pattern and n is the length of the text. It computes the LPS array in O(m) time and performs a single pass over the text in O(n) time. Space Complexity: The space complexity of the KMP algorithm is O(m), where m is the length of the pattern. It is due to the LPS array, which requires O(m) space to store the longest prefix suffix values for each index in the pattern. The additional space used by the algorithm is minimal and does not depend on the input size */ ================================================ FILE: Famous Algorithms/kmp.py ================================================ # Implementation of KMP Algorithm. Program Author : SNEHA CHAUHAN ''' problem: Given a text txt and a pattern pat, write a function search(char pat[], char txt[]) that prints all occurrences of pat[] in txt[]. KMP algorithm is used to find the pattern in the given string. This is naive approach to find the pattern in the given string. Time Complexity: O(n*m) Space Complexity: O(1) Example: input: txt = “AAAABAAABA” pat = “AAAA” output: Pattern found at index 0 Algorithm: 1. Start from the leftmost character of txt and one by one compare it with each character of pat. 2. If a character matches, then move both txt and pat ahead and compare the next character. 3. If a mismatch occurs, then move pat to the index where the mismatch occurs and compare again. 4. If pat reaches its end without any mismatch, then pattern found. ''' def search(pat, txt): M = len(pat) N = len(txt) # A loop to slide pat[] one by one for i in range(N - M + 1): j = 0 # For current index i, check for pattern match for j in range(0, M): if (txt[i + j] != pat[j]): break if (j == M - 1): print("Pattern found at index ", i) # Driver Code if __name__ == '__main__': txt = "AABAACAADAABAAABAA" pat = "AABA" search(pat, txt) ================================================ FILE: Fast and Slow Pointers/happy_number.go ================================================ /* Write an algorithm to determine if a number num is happy. A happy number is a number defined by the following process: Starting with any positive integer, replace the number by the sum of the squares of its digits. Repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1 Those numbers for which this process ends in 1 are happy. Return TRUE if num is a happy number, and FALSE if not. Sample Input : 4 Output: False Sample Input : 19 Output: True */ package main import "fmt" // pow calculates the power of the given digit func pow(digit int, power int) int { res := 1 for i := 0; i < power; i++ { res = res * digit } return res } // sumDigits is a helper function that calculates the sum of digits. func sumDigits(number int) int { totalSum := 0 for number > 0 { digit := number % 10 number = number / 10 totalSum += pow(digit, 2) } return totalSum } func happyNumber(num int) bool { slow := num fast := sumDigits(num) for fast != 1 && fast != slow { slow = sumDigits(slow) fast = sumDigits(sumDigits(fast)) } return fast == 1 } func main() { fmt.Println(happyNumber(4)) // false fmt.Println(happyNumber(19)) // true fmt.Println(happyNumber(100)) // true } ================================================ FILE: Fast and Slow Pointers/linked_list_compute_midpoint.cpp ================================================ // Finding Midpoint of a LinkedList // Program Author : Abhisek Kumar Gupta // Naive Approach: Find the length of the linked list and return the (length/2)th node. // One pass Approach: Use two pointers, the 2nd pointer should traverse twice as fast at the first. #include using namespace std; class node{ public: int data; node* next; node(int d){ data = d; next = NULL; } }; void insert_at_tail(node *&head, int data){ if(head == NULL){ head = new node(data); return; } node *n = new node(data); node * temp = head; while(temp -> next != NULL){ temp = temp->next; } temp->next = n; } void print_linked_list(node *head){ while(head != NULL){ cout << head->data << "->"; head = head->next; } } void makeLinkedList(node *&head){ int data; cin >> data; while(data != -1){ insert_at_tail(head, data); cin >> data; } } node* compute_midpoint(node *head){ if(head->next == NULL || head == NULL){ return head; } node *slow = head; node *fast = head->next; while(fast != NULL && fast->next != NULL){ fast = fast->next->next; slow = slow->next; } return slow; } int main(){ node *head = NULL; makeLinkedList(head); print_linked_list(head); node *midpoint = compute_midpoint(head); cout << endl; cout << midpoint->data << endl; return 0; } ================================================ FILE: Fast and Slow Pointers/linked_list_compute_midpoint.java ================================================ /* * LEETCODE 876 Given the head of a singly linked list, return the middle node of the linked list. If there are two middle nodes, return the second middle node. *Example 1: Input: head = [1,2,3,4,5] Output: [3,4,5] Explanation: The middle node of the list is node 3. *Example 2: Input: head = [1,2,3,4,5,6] Output: [4,5,6] Explanation: Since the list has two middle nodes with values 3 and 4, we return the second one. *CODE EXPLAINATION WITH DRY RUN: This Java code finds the midpoint node of a singly-linked list. If the linked list has an even number of nodes, it returns the second middle node. First, the Node class is defined with a constructor that takes in an integer value and initializes the next reference to null. The insertAtTail() method takes in a head node and an integer value, and inserts a new node with the given value at the end of the linked list. The printLinkedList() method takes in a head node and prints out all the values in the linked list. The makeLinkedList() method prompts the user to enter integers until -1 is inputted. Each integer is inserted into a new node at the tail of the linked list. The computeMidpoint() method takes in a head node and returns the middle node(s) of the linked list. If the linked list has no nodes or only one node, it just returns the head node. Otherwise, it initializes a slow pointer and a fast pointer to the head node. The while loop advances the fast pointer by two nodes and the slow pointer by one node at each iteration until the fast pointer reaches the end of the linked list. At that point, the slow pointer will be pointing to the midpoint node(s) of the linked list. Finally, in the main() method, a new linked list is created by calling makeLinkedList(). The linked list is printed using printLinkedList(). The midpoint of the linked list is computed using computeMidpoint(), and its value is printed out. *Example Dry Run: Suppose we have the following input: 1 2 3 4 5 -1 This creates a linked list with the following structure: 1 -> 2 -> 3 -> 4 -> 5 -> null Initially, the slow pointer and fast pointer both point to the head node, which is 1. In the first iteration of the while loop, the fast pointer moves two nodes ahead to node 3, while the slow pointer moves one node ahead to node 2. In the second iteration, the fast pointer moves another two nodes ahead to null, while the slow pointer moves one more node ahead to node 3. At this point, the slow pointer is pointing to the midpoint node(s) of the linked list. Therefore, computeMidpoint() returns node 3, which is printed out as output. */ import java.util.Scanner; public class linked_list_compute_midpoint { static class Node { int data; Node next; public Node(int data) { this.data = data; next = null; } } static Node insertAtTail(Node head, int data) { if (head == null) { head = new Node(data); return head; } Node n = new Node(data); Node temp = head; while (temp.next != null) { temp = temp.next; } temp.next = n; return head; } static void printLinkedList(Node head) { while (head != null) { System.out.print(head.data + "->"); head = head.next; } } static Node makeLinkedList() { Scanner scanner = new Scanner(System.in); int data = scanner.nextInt(); Node head = null; while (data != -1) { head = insertAtTail(head, data); data = scanner.nextInt(); } scanner.close(); return head; } static Node computeMidpoint(Node head) { if (head == null || head.next == null) { return head; } Node slow = head; Node fast = head.next; while (fast != null && fast.next != null) { fast = fast.next.next; slow = slow.next; } return slow; } public static void main(String[] args) { Node head = makeLinkedList(); printLinkedList(head); System.out.println(); Node midpoint = computeMidpoint(head); System.out.println(midpoint.data); } } ================================================ FILE: Fast and Slow Pointers/linked_list_find_middle.py ================================================ # Finding Midpoint of a LinkedList # Node class class Node: # Function to initialise the node object def __init__(self, data): self.data = data self.next = None class LinkedList: def __init__(self): self.head = None def push(self, new_data): new_node = Node(new_data) new_node.next = self.head self.head = new_node # Function to get the middle of # the linked list using pointers def printMiddle(self): slow_ptr = self.head fast_ptr = self.head if self.head is not None: while (fast_ptr is not None and fast_ptr.next is not None): fast_ptr = fast_ptr.next.next slow_ptr = slow_ptr.next print("The middle element is: ", slow_ptr.data) # Driver code list1 = LinkedList() list1.push(25) list1.push(33) list1.push(14) list1.push(22) list1.push(9) list1.push(11) list1.push(20) list1.printMiddle() ================================================ FILE: Fast and Slow Pointers/linked_list_floyds_cycle_detection.cpp ================================================ // Floyds Cycle detection and removal // Program Author : Abhisek Kumar Gupta // The cycle detection problem is to find the cycle in a sequence, // and Floyd’s cycle detection algorithm, aka Tortoise and Hare algorithm, // is a two-pointer algorithm to detect the cycle and locate the start of the cycle as well. #include using namespace std; class node{ public: int data; node* next; node(int d){ data = d; next = NULL; } }; void insert_at_tail(node *&head, int data){ if(head == NULL){ head = new node(data); return; } node *n = new node(data); node * temp = head; while(temp -> next != NULL){ temp = temp->next; } temp->next = n; } void print_linked_list(node *head){ while(head != NULL){ cout << head->data << "->"; head = head->next; } } void makeLinkedList(node *&head){ int data; cin >> data; while(data != -1){ insert_at_tail(head, data); cin >> data; } head->next->next->next = head; } bool detect_cycle(node *head){ node *slow = head; node *fast = head; while(fast != NULL && fast->next != NULL){ fast = fast->next->next; slow = slow->next; if(fast == slow){ //return true; slow = head; if(slow == fast){ while(fast->next != slow) fast = fast->next; } else{ while(slow->next != fast->next){ slow = slow->next; fast = fast->next; } } fast->next = NULL; cout << "Cycle Detected and Removed \n"; } } return false; } int main(){ node *head = NULL; makeLinkedList(head); //print_linked_list(head); if(detect_cycle(head)){ cout << "Cycle detected \n"; } else { cout << "No cycle \n"; } print_linked_list(head); return 0; } ================================================ FILE: Fast and Slow Pointers/linked_list_floyds_cycle_detection.py ================================================ ''' Floyds Cycle detection and removal Program Author : Abhisek Kumar Gupta The cycle detection problem is to find the cycle in a sequence, and Floyd’s cycle detection algorithm, aka Tortoise and Hare algorithm, is a two-pointer algorithm to detect the cycle and locate the start of the cycle as well. ''' # Definition for singly-linked list. # class ListNode: # def __init__(self, x): # self.val = x # self.next = None class Solution: def hasCycle(self, head: Optional[ListNode]) -> bool: #Floyd's cycle detection algorithm uses slow and fast points to find the loop #Reference - https://www.geeksforgeeks.org/floyds-cycle-finding-algorithm/ slow,fast= head,head while(slow!=None and fast!=None and fast.next!=None): slow = slow.next fast = fast.next.next if(slow == fast): return True return False ================================================ FILE: Graphs/Diljstra.go ================================================ package main import ( "fmt" "math" ) // Define a struct to represent a graph. type Graph struct { nodes map[string]map[string]float64 } // Add an edge to the graph. func (g *Graph) AddEdge(node1, node2 string, weight float64) { if g.nodes == nil { g.nodes = make(map[string]map[string]float64) } if g.nodes[node1] == nil { g.nodes[node1] = make(map[string]float64) } if g.nodes[node2] == nil { g.nodes[node2] = make(map[string]float64) } g.nodes[node1][node2] = weight g.nodes[node2][node1] = weight // Assuming an undirected graph } // Dijkstra's algorithm to find the shortest path. func Dijkstra(graph Graph, startNode string) map[string]float64 { distances := make(map[string]float64) visited := make(map[string]bool) for node := range graph.nodes { distances[node] = math.Inf(1) } distances[startNode] = 0 for { var closestNode string var shortestDistance float64 = math.Inf(1) for node, distance := range distances { if !visited[node] && distance < shortestDistance { closestNode = node shortestDistance = distance } } if closestNode == "" { break } visited[closestNode] = true for neighbor, weight := range graph.nodes[closestNode] { if newDistance := distances[closestNode] + weight; newDistance < distances[neighbor] { distances[neighbor] = newDistance } } } return distances } func main() { // Create a graph. g := Graph{} g.AddEdge("A", "B", 1) g.AddEdge("A", "C", 4) g.AddEdge("B", "C", 2) g.AddEdge("B", "D", 5) g.AddEdge("C", "D", 1) g.AddEdge("D", "E", 3) g.AddEdge("E", "F", 2) // Find the shortest distances from node "A" to all other nodes. shortestDistances := Dijkstra(g, "A") // Print the shortest distances. for node, distance := range shortestDistances { fmt.Printf("Shortest distance from A to %s: %v\n", node, distance) } } ================================================ FILE: Graphs/GraphBFS.java ================================================ /** * The GraphBFS class represents a simple undirected graph using an adjacency list * and provides a breadth-first search (BFS) algorithm to traverse the graph. */ import java.util.*; import java.io.*; class GraphBFS { private int V; // Number of vertices in the graph private List adjacency[]; // Adjacency list to represent the graph // Constructor to initialize the graph with the given number of vertices GraphBFS(int v) { V = v; adjacency = new ArrayList[V]; for(int i = 0; i < V; i++) { adjacency[i] = new ArrayList(); } } // Method to add an edge between vertices 'u' and 'v' in the graph void addEdge(int u, int v) { adjacency[u].add(v); adjacency[v].add(u); } /** * Performs breadth-first search (BFS) starting from the given source vertex 's'. * Prints the vertices in BFS order. * @param s The source vertex from which BFS starts. */ void bfs(int s) { boolean[] visited = new boolean[V]; // Array to track visited vertices visited[s] = true; // Mark the source vertex as visited LinkedList Q = new LinkedList(); // Queue for BFS traversal Q.add(s); // Enqueue the source vertex // BFS traversal while(Q.size() != 0) { int current = Q.poll(); // Dequeue the current vertex System.out.print(current + " -> "); // Print the current vertex // Visit all neighbors of the current vertex for(int neighbour: adjacency[current]) { if(!visited[neighbour]) { visited[neighbour] = true; // Mark the neighbour as visited Q.add(neighbour); // Enqueue the neighbour for further exploration } } } } // Main method for testing the GraphBFS class public static void main(String[] args) { GraphBFS g = new GraphBFS(5); // Create a graph with 5 vertices g.addEdge(2, 3); // Add edges to the graph g.addEdge(2, 4); g.addEdge(3, 1); g.addEdge(4, 1); g.bfs(2); // Perform BFS starting from vertex 2 } } ================================================ FILE: Graphs/Graph_Dijstra.java ================================================ import java.util.*; class Graph { private int V; // Number of vertices private List> adj; // Adjacency list public Graph(int V) { this.V = V; adj = new ArrayList<>(V); for (int i = 0; i < V; i++) { adj.add(new ArrayList<>()); } } // Add an edge to the graph public void addEdge(int source, int destination, int weight) { Node node = new Node(destination, weight); adj.get(source).add(node); } public void dijkstra(int source) { int[] distance = new int[V]; Arrays.fill(distance, Integer.MAX_VALUE); distance[source] = 0; PriorityQueue pq = new PriorityQueue<>(V, Comparator.comparingInt(node -> node.weight)); pq.add(new Node(source, 0)); while (!pq.isEmpty()) { int u = pq.poll().vertex; for (Node neighbor : adj.get(u)) { int v = neighbor.vertex; int w = neighbor.weight; if (distance[u] != Integer.MAX_VALUE && distance[u] + w < distance[v]) { distance[v] = distance[u] + w; pq.add(new Node(v, distance[v])); } } } // Print the shortest distances from the source System.out.println("Shortest distances from source vertex " + source + ":"); for (int i = 0; i < V; i++) { System.out.println("Vertex " + i + ": " + distance[i]); } } private static class Node { int vertex; int weight; Node(int vertex, int weight) { this.vertex = vertex; this.weight = weight; } } } public class DijkstraAlgorithm { public static void main(String[] args) { int V = 6; // Number of vertices Graph graph = new Graph(V); // Add edges and their weights graph.addEdge(0, 1, 2); graph.addEdge(0, 2, 4); graph.addEdge(1, 2, 1); graph.addEdge(1, 3, 7); graph.addEdge(2, 4, 3); graph.addEdge(3, 4, 1); graph.addEdge(3, 5, 5); graph.addEdge(4, 5, 2); int source = 0; // Source vertex graph.dijkstra(source); } } ================================================ FILE: Graphs/Graphs_Dijkstras.py ================================================ # Graphs Dijkstras Implementation # Program Author : Tyler Le # Dijkstra's algorithm is a graph search algorithm # that solves the single-source shortest path problem # for a graph with non-negative edge weights def dijkstra(graph, start): # Create a dictionary to store the shortest distances to each node shortest_distances = {node: float('inf') for node in graph} shortest_distances[start] = 0 # Create a set to store nodes that have been processed processed_nodes = set() # Create a priority queue to store nodes and their distances queue = [(start, 0)] while queue: # Get the node with the smallest distance from the start node current_node, current_distance = min(queue, key=lambda x: x[1]) queue.remove((current_node, current_distance)) # If we've already processed this node, skip it if current_node in processed_nodes: continue # Mark this node as processed processed_nodes.add(current_node) # Update the shortest distances to all neighboring nodes for neighbor, weight in graph[current_node].items(): distance = current_distance + weight if distance < shortest_distances[neighbor]: shortest_distances[neighbor] = distance queue.append((neighbor, distance)) return shortest_distances # Example usage graph = { 'A': {'B': 5, 'C': 1}, 'B': {'A': 5, 'C': 2, 'D': 1}, 'C': {'A': 1, 'B': 2, 'D': 4}, 'D': {'B': 1, 'C': 4} } start_node = 'A' distances = dijkstra(graph, start_node) print(distances) ================================================ FILE: Graphs/Graphs_Ford_Fulkerson.cpp ================================================ The Ford-Fulkerson algorithm is a graph algorithm used to find the maximum flow in a flow network. Here is a high-level overview of the algorithm and some resources for further documentation: Ford-Fulkerson Algorithm Overview: Start with an initial flow of zero. While there exists an augmenting path from the source to the sink: Find the residual capacity of the augmenting path (minimum capacity edge along the path). Update the flow by increasing the flow along the augmenting path. Update the residual capacities of the edges. The maximum flow is the sum of the flows along the augmenting paths. Here's an example of the Ford-Fulkerson algorithm implemented in C++: #include #include #include #include using namespace std; // Number of vertices in the graph #define V 6 // A BFS based function to check whether there is an augmenting path // from source to sink. Returns true if there is an augmenting path, // else returns false bool bfs(int rGraph[V][V], int s, int t, int parent[]) { // Create a visited array and mark all vertices as not visited bool visited[V]; memset(visited, 0, sizeof(visited)); // Create a queue, enqueue source vertex and mark source vertex // as visited queue q; q.push(s); visited[s] = true; parent[s] = -1; // Standard BFS Loop while (!q.empty()) { int u = q.front(); q.pop(); for (int v = 0; v < V; v++) { if (visited[v] == false && rGraph[u][v] > 0) { q.push(v); parent[v] = u; visited[v] = true; } } } // If we reached the sink in BFS starting from source, then return // true, else false return (visited[t] == true); } // A function to implement the Ford-Fulkerson algorithm // This will find the maximum possible flow from the source to the sink int fordFulkerson(int graph[V][V], int s, int t) { int u, v; // Create a residual graph and fill the residual graph with // given capacities in the original graph as residual capacities // in residual graph int rGraph[V][V]; // Residual graph where rGraph[i][j] indicates // residual capacity of edge from i to j (if there // is an edge. If rGraph[i][j] is 0, then there is not) for (u = 0; u < V; u++) for (v = 0; v < V; v++) rGraph[u][v] = graph[u][v]; int parent[V]; // This array is filled by BFS and to store path int maxFlow = 0; // There is no flow initially // Augument the flow while there is path from source to sink while (bfs(rGraph, s, t, parent)) { // Find minimum residual capacity of the edges along the // path filled by BFS. Or we can say find the maximum flow // through the path found. int pathFlow = INT_MAX; for (v = t; v != s; v = parent[v]) { u = parent[v]; pathFlow = min(pathFlow, rGraph[u][v]); } // Update residual capacities of the edges and reverse edges // along the path for (v = t; v != s; v = parent[v]) { u = parent[v]; rGraph[u][v] -= pathFlow; rGraph[v][u] += pathFlow; } // Add path flow to overall flow maxFlow += pathFlow; } // Return the overall flow as the maximum flow return maxFlow; } // Driver program to test above functions int main() { // Let us create a graph shown in the above example int graph[V][V] = { { 0, 16, 13, 0, 0, 0 }, { 0, 0, 10, 12, 0, 0 }, { 0, 4, 0, 0, 14, 0 }, { 0, 0, 9, 0, 0, 20 }, { 0, 0, 0, 7, 0, 4 }, { 0, 0, 0, 0, 0, 0 } }; int source = 0; int sink = 5; cout << "The maximum possible flow is: " << fordFulkerson(graph, source, sink) << endl; return 0; } ================================================ FILE: Graphs/Graphs_bfs.cpp ================================================ // Graphs Adjacency List implementation for Generic Data // Program Author : Abhisek Kumar Gupta #include using namespace std; template class Graph{ map > adjList; public: Graph(){ } void addEdge(T u, T v, bool bidir = true){ adjList[u].push_back(v); if(bidir){ adjList[v].push_back(u); } } void printAdjList(){ for(auto obj : adjList){ cout << obj.first << "->"; for(auto element : obj.second){ cout << element << ","; } cout << endl; } } void bfs(int n){ queue q; q.push(n); map visited; visited[n] = true; while(!q.empty()){ int node_element = q.front(); cout << node_element << " "; q.pop(); for(int neighbour : adjList[node_element]){ if(!visited[neighbour]){ q.push(neighbour); visited[neighbour] = true; } } } } }; int main(){ Graph g; g.addEdge(0, 1); g.addEdge(0, 4); g.addEdge(1, 4); g.addEdge(1, 3); g.addEdge(1, 2); g.addEdge(2, 3); g.addEdge(4, 3); g.printAdjList(); g.bfs(0); return 0; } ================================================ FILE: Graphs/Graphs_bfs.js ================================================ // Define a function that takes a graph and a starting node as input function bfs(graph, startNode) { // Initialize an empty object to keep track of visited nodes const visited = {} // Initialize a queue with the starting node const queue = [startNode] // Mark the starting node as visited visited[startNode] = true // While there are nodes in the queue while (queue.length > 0) { // Get the next node from the front of the queue const currentNode = queue.shift() // Log the current node to the console (or do something else with it) console.log(currentNode) // Get the adjacent nodes of the current node from the graph const adjacentNodes = graph[currentNode] // For each adjacent node for (let i = 0; i < adjacentNodes.length; i++) { // Get the adjacent node const adjacentNode = adjacentNodes[i].node // If the adjacent node has not been visited if (!visited[adjacentNode]) { // Mark the adjacent node as visited visited[adjacentNode] = true // Add the adjacent node to the back of the queue queue.push(adjacentNode) } } } } // SAMPLE USE CASE // The graph will be represented in form of an adjacency list. /* The graph will be in form of an object where each key represents the nodes and each value will be an array of the neighbors of the node. The array in values will be an object array, where each object has the node, which is the neighbor, and weight, which is the distance to that neighbor from the current node. */ const graph = { 0: [{ node: 1, weight: 10 }], 1: [ { node: 0, weight: 10 }, { node: 2, weight: 10 }, { node: 3, weight: 5 }, ], 2: [{ node: 1, weight: 10 }], 3: [ { node: 1, weight: 5 }, { node: 4, weight: 10 }, { node: 5, weight: 10 }, ], 4: [ { node: 3, weight: 10 }, { node: 5, weight: 10 }, ], 5: [ { node: 3, weight: 10 }, { node: 4, weight: 10 }, ], } bfs(graph, 2) ================================================ FILE: Graphs/Graphs_bfs.py ================================================ from collections import defaultdict class Graph: def __init__(self): self.graph = defaultdict(list) def insertEdge(self,v1,v2): self.graph[v1].append(v2) def subBfs(self,visited,queue): while(queue): v=queue.pop(0) visited.add(v) print(v,end=" ") for neighbour in self.graph[v]: if neighbour not in visited: queue.append(neighbour) visited.add(neighbour) def bfs(self,v): visited=set() queue=[v] self.subBfs(visited,queue) g=Graph() g.insertEdge(1,2) g.insertEdge(2,1) g.insertEdge(2,3) g.insertEdge(3,4) g.insertEdge(4,5) g.insertEdge(5,6) g.insertEdge(2,6) g.bfs(1) ================================================ FILE: Graphs/Graphs_bfs_sssp.cpp ================================================ // Graphs Adjacency List implementation for Generic Data // Program Author : Abhisek Kumar Gupta #include using namespace std; template class Graph{ map > adjList; public: Graph(){ } void addEdge(T u, T v, bool bidir = true){ adjList[u].push_back(v); if(bidir){ adjList[v].push_back(u); } } void printAdjList(){ for(auto obj : adjList){ cout << obj.first << "->"; for(auto element : obj.second){ cout << element << ","; } cout << endl; } } void bfs_sssp(int n){ queue q; map distance; map parent; for(auto i : adjList){ distance[i.first] = INT_MAX; } q.push(n); distance[n] = 0; parent[n] = n; while(!q.empty()){ int node_element = q.front(); cout << node_element << " "; q.pop(); for(int neighbour : adjList[node_element]){ if(distance[neighbour] == INT_MAX){ q.push(neighbour); distance[neighbour] = distance[node_element] + 1; parent[neighbour] = node_element; } } } cout << endl; for(auto i : adjList){ int node = i.first; cout << "Distance of " << node << " from " << n << " is " << distance[node] << endl; } for(auto x: parent){ cout << x.first << " " << x.second << endl; } } }; int main(){ Graph g; g.addEdge(0, 1); g.addEdge(0, 4); g.addEdge(1, 4); g.addEdge(1, 3); g.addEdge(1, 2); g.addEdge(2, 3); g.addEdge(4, 3); g.printAdjList(); g.bfs_sssp(4); return 0; } ================================================ FILE: Graphs/Graphs_cycle_detection_bfs.cpp ================================================ // Graphs Cycle detection using BFS // Program Author : Abhisek Kumar Gupta #include using namespace std; template class Graph{ map > L; public: Graph(){ } void add_edge(T u, T v, bool bidir = true){ L[u].push_back(v); if(bidir){ L[v].push_back(u); } } void print_edge(){ for(auto x : L){ cout << x.first << "->"; for(auto element : x.second){ cout << element << ","; } cout << endl; } } bool is_cyclic(T source){ map visited; map parent; queue q; q.push(source); visited[source] = true; parent[source] = source; while(!q.empty()){ T node = q.front(); q.pop(); for(T neighbour : L[node]){ if(visited[neighbour]==true && parent[node] != neighbour){ return true; } else if(!visited[neighbour]){ visited[neighbour] = true; q.push(neighbour); parent[neighbour] = node; } } } return false; } }; int main(){ Graph g; g.add_edge(1,2); g.add_edge(1,4); g.add_edge(4,3); g.add_edge(2,3); g.print_edge(); if(g.is_cyclic(1)){ cout << "Graph is Cyclic"; } else{ cout << "Graph is not Cyclic"; } return 0; } ================================================ FILE: Graphs/Graphs_cycle_detection_dfs.cpp ================================================ // Graphs Cycle detection using DFS // Program Author : Abhisek Kumar Gupta #include using namespace std; template class Graph{ map > L; public: Graph(){ } void add_edge(T u, T v, bool bidir = true){ L[u].push_back(v); if(bidir){ L[v].push_back(u); } } bool is_cyclic_helper(T node, map &visited, map &in_stack){ visited[node] = true; in_stack[node] = true; for(T neighbour : L[node]){ if((!visited[neighbour] && is_cyclic_helper(neighbour, visited, in_stack)) || in_stack[neighbour]){ return true; } } in_stack[node] = false; return false; } bool is_cyclic(){ map visited; map in_stack; for(auto x: L){ T node = x.first; if(!visited[node]){ bool answer = is_cyclic_helper(node, visited, in_stack); if(answer){ return true; } else{ return false; } } } } }; int main(){ Graph g; g.add_edge(0, 2, false); g.add_edge(0, 1, false); g.add_edge(2, 3, false); g.add_edge(2, 4, false); // g.add_edge(3, 0, false); g.add_edge(4, 5, false); g.add_edge(1, 5, false); if(g.is_cyclic()){ cout << "Cycle present" << endl; } else{ cout << "Cycle not present" << endl; } return 0; } ================================================ FILE: Graphs/Graphs_dfs.cpp ================================================ // Graphs Adjacency List implementation for Generic Data // Program Author : Abhisek Kumar Gupta #include using namespace std; template class Graph{ map > adjList; public: Graph(){ } void addEdge(T u, T v, bool bidir = true){ adjList[u].push_back(v); if(bidir){ adjList[v].push_back(u); } } void printAdjList(){ for(auto obj : adjList){ cout << obj.first << "->"; for(auto element : obj.second){ cout << element << ","; } cout << endl; } } void dfsHelper(int node, map &visited){ visited[node] = true; cout << node << "->"; for(int neighbour : adjList[node]){ if(!visited[neighbour]){ dfsHelper(neighbour, visited); } } } void dfs(int src){ map visited; dfsHelper(src, visited); } }; int main(){ Graph g; g.addEdge(0, 1); g.addEdge(0, 4); g.addEdge(1, 4); g.addEdge(1, 3); g.addEdge(1, 2); g.addEdge(2, 3); g.addEdge(4, 3); g.printAdjList(); g.dfs(0); return 0; } ================================================ FILE: Graphs/Graphs_dfs.java ================================================ /*Name : Abhinav kumar Github username : Abhinavcode13 Repository name : data-structures-and-algorithms Problem : Implement Depth First Search in Java Issue Number : #733 Problem statement : Explanation of the below Java code : In this example, we have a Node class representing each node in the graph. Each Node has a value, a list of neighbors, and a boolean flag to track if it has been visited. The DepthFirstSearch class contains the dfs method, which performs the depth-first search traversal. It takes a starting node as an argument and recursively visits all unvisited neighbors of that node. When visiting a node, it marks it as visited and prints its value. In the main method, we create a small graph with five nodes and test the dfs method by starting the traversal from the first node (node1). The output will display the visited nodes in the order they are traversed: */ -------------------------------------------------------------------------//Java code begins here------------------------------------------------------------------------ import java.util.ArrayList; import java.util.List; class Node { int value; List neighbors; boolean visited; Node(int value) { this.value = value; this.neighbors = new ArrayList<>(); this.visited = false; } void addNeighbor(Node neighbor) { this.neighbors.add(neighbor); } } class DepthFirstSearch { void dfs(Node startNode) { // Mark the startNode as visited startNode.visited = true; System.out.println("Visited Node: " + startNode.value); // Recursively visit all unvisited neighbors for (Node neighbor : startNode.neighbors) { if (!neighbor.visited) { dfs(neighbor); } } } } public class Main { public static void main(String[] args) { // Create a graph for testing Node node1 = new Node(1); Node node2 = new Node(2); Node node3 = new Node(3); Node node4 = new Node(4); Node node5 = new Node(5); node1.addNeighbor(node2); node1.addNeighbor(node3); node2.addNeighbor(node4); node3.addNeighbor(node4); node4.addNeighbor(node5); DepthFirstSearch dfs = new DepthFirstSearch(); dfs.dfs(node1); } } ================================================ FILE: Graphs/Graphs_dfs.js ================================================ // Takes in a graph object and a starting node function dfs(graph, startNode) { // Initialize an empty visited object const visited = {} // Call the dfsHelper function with the starting node, the visited object, and the graph object dfsHelper(startNode, visited, graph) } // Recursive helper function for the DFS algorithm // Takes in a current node, a visited object, and a graph object function dfsHelper(node, visited, graph) { // Mark the current node as visited by adding it to the visited object visited[node] = true // Print the current node (or perform some other action) console.log(node) // Get the adjacent nodes of the current node from the graph object const adjacentNodes = graph[node] // Iterate over the adjacent nodes for (let i = 0; i < adjacentNodes.length; i++) { // Get the node of the current adjacent node const adjacentNode = adjacentNodes[i].node // If the adjacent node has not been visited yet, recursively call dfsHelper with the adjacent node if (!visited[adjacentNode]) { dfsHelper(adjacentNode, visited, graph) } } } // SAMPLE USE CASE // The graph will be represented in form of an adjacency list. /* The graph will be in form of an object where each key represents the nodes and each value will be an array of the neighbors of the node. The array in values will be an object array, where each object has the node, which is the neighbor, and weight, which is the distance to that neighbor from the current node. */ const graph = { 0: [{ node: 1, weight: 10 }], 1: [ { node: 0, weight: 10 }, { node: 2, weight: 10 }, { node: 3, weight: 5 }, ], 2: [{ node: 1, weight: 10 }], 3: [ { node: 1, weight: 5 }, { node: 4, weight: 10 }, { node: 5, weight: 10 }, ], 4: [ { node: 3, weight: 10 }, { node: 5, weight: 10 }, ], 5: [ { node: 3, weight: 10 }, { node: 4, weight: 10 }, ], } dfs(graph, 0) ================================================ FILE: Graphs/Graphs_dfs.py ================================================ from collections import defaultdict class Graph: def __init__(self): self.graph = defaultdict(list) def insertEdge(self,v1,v2): self.graph[v1].append(v2) def dfsSub(self,v,visited): visited.add(v) print(v,end=" ") for neighbour in self.graph[v]: if neighbour not in visited: self.dfsSub(neighbour,visited) def dfs(self,v): visited = set() self.dfsSub(v,visited) g=Graph() g.insertEdge(1,2) g.insertEdge(2,1) g.insertEdge(3,4) g.insertEdge(4,5) g.insertEdge(5,6) g.insertEdge(2,6) g.dfs(2) ================================================ FILE: Graphs/Graphs_dfs_connected_components.cpp ================================================ // Graphs Adjacency List implementation for Generic Data // Program Author : Abhisek Kumar Gupta #include using namespace std; template class Graph{ map > adjList; public: Graph(){ } void addEdge(T u, T v, bool bidir = true){ adjList[u].push_back(v); if(bidir){ adjList[v].push_back(u); } } void printAdjList(){ for(auto obj : adjList){ cout << obj.first << "->"; for(auto element : obj.second){ cout << element << ","; } cout << endl; } } void dfsHelper(T node, map &visited){ visited[node] = true; cout << node << "->"; for(T neighbour : adjList[node]){ if(!visited[neighbour]){ dfsHelper(neighbour, visited); } } } void dfs(T src){ map visited; int component = 1; dfsHelper(src, visited); cout << endl; for(auto x : adjList){ T city = x.first; if(!visited[city]){ dfsHelper(city, visited); component++; cout << endl; } } cout << "Graph has " << component << " component." << endl; } }; int main(){ Graph g; g.addEdge("Amritsar", "Jaipur"); g.addEdge("Amritsar", "Delhi"); g.addEdge("Delhi", "Jaipur"); g.addEdge("Mumbai", "Jaipur"); g.addEdge("Mumbai", "Bhopal"); g.addEdge("Delhi", "Bhopal"); g.addEdge("Mumbai", "Bangalore"); g.addEdge("Agra", "Delhi"); g.addEdge("Andaman", "Nicobar"); g.addEdge("Nagaland", "Manipur"); g.printAdjList(); g.dfs("Amritsar"); return 0; } ================================================ FILE: Graphs/Graphs_dijkstras.js ================================================ // A class representing a priority queue class PriorityQueue { constructor() { this.items = [] } // Adds an item to the priority queue with a given priority enqueue(item, priority) { let added = false for (let i = 0; i < this.items.length; i++) { if (priority < this.items[i].priority) { this.items.splice(i, 0, { item, priority }) added = true break } } if (!added) this.items.push({ item, priority }) } // Removes and returns the item with minimum priority from the priority queue dequeue() { if (this.isEmpty()) return null return this.items.shift() } // Returns true if the priority queue is empty, false otherwise isEmpty() { return this.items.length === 0 } // Returns the number of items in the priority queue size() { return this.items.length } } // Performs Dijkstra's shortest path algorithm on a given graph with a specified start node function dijkstra(graph, n, start) { const visited = new Array(n) // An array to keep track of visited nodes visited.fill(false) const distances = new Array(n) // An array to store the distances from the start node to each node in the graph based on indices distances.fill(Number.POSITIVE_INFINITY) distances[start] = 0 // An array to store the previous node in the shortest path to each node in the graph based on indices const prev = new Array(n) prev.fill(null) const pq = new PriorityQueue() //Creating a new priority queue pq.enqueue(start, 0) // Initializing the priority queue while (!pq.isEmpty()) { const val = pq.dequeue() // Dequeue the node with the lowest priority const idx = val.item // Get the index of the dequeued node const min = val.priority // Get the distance of the dequeued node visited[idx] = true // Mark the node as visited if (distances[idx] < min) continue // If the distance of the dequeued node is less than the minimum distance, continue to the next iteration of the loop // Iterate over the neighbors of the dequeued/current node for (let edge of graph[idx]) { if (visited[edge]) continue // If the destination node of the edge has already been visited, continue to the next iteration of the loop const newDist = distances[idx] + edge.weight // Calculate the new distance to the destination node // If the new distance is less than the current distance to the destination node, update the previous node, distance, and add the destination node to the priority queue if (newDist < distances[edge.node]) { prev[edge.node] = idx distances[edge.node] = newDist pq.enqueue(edge.node, newDist) } } } // Return the distances and previous nodes arrays in an object return { distances, prev } } // Finds the shortest path between the start and end nodes in the given graph using Dijkstra's algorithm function findShortestPath(graph, n, start, end) { // check if start node is present in the graph, if not, throw error. if (!graph.hasOwnProperty(start)) { throw new Error(`Start node ${start} not found in graph`) } // check if end node is present in the graph, if not, throw error. if (!graph.hasOwnProperty(end)) { throw new Error(`End node ${end} not found in graph`) } const res = dijkstra(graph, n, start) // Run Dijkstra's algorithm to get the shortest distances and paths from the start node to all other nodes const distances = res.distances // Extract the distances array from the result of Dijkstra's algorithm const prev = res.prev // Extract the previous nodes array from the result of Dijkstra's algorithm const path = [] // Initialize an empty array to store the shortest path from the start to end node // If the end node is not reachable from the start node, return an empty path if (distances[parseInt(end)] == Number.POSITIVE_INFINITY) return path // Traverse the previous nodes array backwards from the end node to the start node to construct the shortest path for (let i = end; i !== null; i = prev[i]) path.unshift(i) // Return the shortest path return path } // Sample Use Case // The graph will be represented in form of an adjacency list. /* The graph will be in form of an object where each key represents the nodes and each value will be an array of the neighbors of the node. The array in values will be an object array, where each object has the node, which is the neighbor, and weight, which is the distance to that neighbor from the current node. */ const graph = { 0: [{ node: 1, weight: 10 }], 1: [ { node: 0, weight: 10 }, { node: 2, weight: 10 }, { node: 3, weight: 5 }, ], 2: [{ node: 1, weight: 10 }], 3: [ { node: 1, weight: 5 }, { node: 4, weight: 10 }, { node: 5, weight: 10 }, ], 4: [ { node: 3, weight: 10 }, { node: 5, weight: 10 }, ], 5: [ { node: 3, weight: 10 }, { node: 4, weight: 10 }, ], } console.log(findShortestPath(graph, 6, 2, 5)) ================================================ FILE: Graphs/Graphs_flood_fill.cpp ================================================ /* Flood fill, also called seed fill, is a flooding algorithm that determines and alters the area connected to a given node in a multi-dimensional array with some matching attribute. It is used in the "bucket" fill tool of paint programs to fill connected, similarly-colored areas with a different color, and in games such as Go and Minesweeper for determining which pieces are cleared. Source(https://en.wikipedia.org/wiki/Flood_fill) Sample Input 15 30 .............................. .............#####............ .............#...#............ .....#########...#######...... ....###.....######.....###.... ...##....................##... ..##......................#... ..##.....................##... ..###...................##.... ....###................###.... ......###............###...... ........###........###........ ..........##########.......... .............................. ...........A.P.P.L.E.......... Output : .............................. .............#####............ .............#...#............ .....#########...#######...... ....###.....######.....###.... ...##....................##... ..##......................#... ..##.....................##... ..###...................##.... ....###................###.... ......###............###...... ........###........###........ ..........##########.......... .............................. ...........A.P.P.L.E.......... .............................. .............#####............ .............#...#............ .....#########...#######...... ....###rrrrr######rrrrr###.... ...##rrrrrrrrrrrrrrrrrrrr##... ..##rrrrrrrrrrrrrrrrrrrrrr#... ..##rrrrrrrrrrrrrrrrrrrrr##... ..###rrrrrrrrrrrrrrrrrrr##.... ....###rrrrrrrrrrrrrrrr###.... ......###rrrrrrrrrrrr###...... ........###rrrrrrrr###........ ..........##########.......... .............................. ...........A.P.P.L.E.......... .............................. .............#####............ .............#bbb#............ .....#########bbb#######...... ....###rrrrr######rrrrr###.... ...##rrrrrrrrrrrrrrrrrrrr##... ..##rrrrrrrrrrrrrrrrrrrrrr#... ..##rrrrrrrrrrrrrrrrrrrrr##... ..###rrrrrrrrrrrrrrrrrrr##.... ....###rrrrrrrrrrrrrrrr###.... ......###rrrrrrrrrrrr###...... ........###rrrrrrrr###........ ..........##########.......... .............................. ...........A.P.P.L.E.......... */ #include using namespace std; int R, C; void print_matrix(char input[][50]){ for(int i = 0; i < R; i++){ for(int j = 0; j < C; j++){ cout << input[i][j]; } cout << "\n"; } } int dx[] = {-1, 0, 1, 0}; int dy[] = {0, -1, 0, 1}; void flood_fill(char input[][50], int i, int j, char ch, char color){ // base case : make sure we do not cross boundries of input matrix if(i < 0 || j < 0 || i >= R || j >= C) return; // if we are coming to a cell that is not equal to ch then we dont do anything if(input[i][j] != ch) return; // fill the color input[i][j] = color; // dfs flood_fill on all directions for(int k = 0; k < 4; k++){ flood_fill(input, i + dx[k], j + dy[k], ch, color); } } int main(){ cin >> R >> C; char input[15][50]; for(int i = 0; i < R; i++){ for(int j = 0; j < C; j++){ cin >> input[i][j]; } } print_matrix(input); flood_fill(input, 8, 13, '.', 'r'); print_matrix(input); flood_fill(input, 2, 15, '.', 'b'); print_matrix(input); return 0; } ================================================ FILE: Graphs/Graphs_kill_process.cpp ================================================ /* Given a list of process where each process has a unique id and parent id. Parent id is the id of the process that initiated that process. You have to kill a particular process given by an integer kill. Print id of all the processes that will be killed to kill that process In order to kill a process, all its child processes should be killed as well also only one process have parent id as 0 ie the process that started itself Input : process id : [3, 1, 5, 7, 10, 11, 12] parent id : [0, 3, 3, 5, 5, 10, 10] kill_id : 5 Output: [5, 7, 10, 11, 12] */ #include using namespace std; template class Graph{ map > L; public: Graph(){ } void add_list(int u, int v, bool bidir = false){ L[u].push_back(v); if(bidir){ L[v].push_back(u); } } void print_graph(){ for(T node : L){ cout << node.first << "->"; for(T neighbour: node.second){ cout << neighbour << ","; } cout << endl; } } void kill_process(int process){ map visited; queue q; q.push(process); visited[process] = true; while(!q.empty()){ int node = q.front(); cout << node << "->"; q.pop(); for(T neighbour : L[node]){ if(!visited[neighbour]){ q.push(neighbour); visited[neighbour] = true; } } } } }; int main(){ Graph g; int n; cout << "Enter number of process : "; cin >> n; vector process_id(n); vector parent_id(n); for(int i = 0; i < n; i++){ cin >> process_id[i]; } for(int i = 0; i < n; i++){ cin >> parent_id[i]; } for(int i = 0; i < n; i++){ g.add_list(parent_id[i], process_id[i]); } int kill; cout << "Enter process to be killed : "; cin >> kill; g.kill_process(kill); return 0; } ================================================ FILE: Graphs/Graphs_kruskals_algo.cpp ================================================ /* Name : MAnmay Ghosh Github username : ManmayGhosh Repository name : data-structures-and-algorithms Problem : Implement Kruskal's algorithm in C++ Issue Number : #1301 Explanation of the below C++ code : In this implementation, In Kruskal’s algorithm, sort all edges of the given graph in increasing order. Then it keeps on adding new edges and nodes in the MST if the newly added edge does not form a cycle. It picks the minimum weighted edge at first at the maximum weighted edge at last. Thus we can say that it makes a locally optimal choice in each step in order to find the optimal solution pseudosteps 1. Sort all the edges in non-decreasing order of their weight. 2. Pick the smallest edge. Check if it forms a cycle with the spanning tree formed so far. If the cycle is not formed, include this edge. Else, discard it. 3. Repeat step#2 until there are (V-1) edges in the spanning tree. -------------------------------------------------------------------------//C++ code begins here------------------------------------------------------------------------ */ #include using namespace std; // DS data structure class will help in building graph class D_S { int* parent; // to create a parent relationship b/w two nodes int* child; // to create a child relationship b/w two nodes public: //This function will create a user defined data structure which will help to create a graph D_S(int n) { parent = new int[n]; child = new int[n]; for (int i = 0; i < n; i++) { parent[i] = -1; child[i] = 1; } } // Find function to find edges b/w vertices int find(int i) { if (parent[i] == -1) return i; return parent[i] = find(parent[i]); } // Union function to joint two nodes via smallest possible weighted edge void unite(int x, int y) { int s1 = find(x); int s2 = find(y); if (s1 != s2) { if (child[s1] < child[s2]) parent[s1] = s2; else if (child[s1] > child[s2]) parent[s2] = s1; else { parent[s2] = s1; child[s1] += 1; } } } }; class Graph { //As we know for kruskal's algorithm we have to maintain a list for edges b/w two vertices //from lowest weight to highest weight in increasing order vector> edgelist; int V; public: Graph(int V) { this->V = V; } // Function to add edge in a graph void addEdge(int x, int y, int w) { edgelist.push_back({ w, x, y }); } //Kruskal's Algorithm void kruskals_mst() { // Arrange all edges in ascending order to find minimum weight edge sort(edgelist.begin(), edgelist.end()); // Initialize the DSU D_S gr(V); int ans = 0; cout << "Following are the edges in the constructed MST"<< endl; for (auto edge : edgelist) { int w = edge[0]; int x = edge[1]; int y = edge[2]; // Take the edge in MST if it does not forms a cycle if (gr.find(x) != gr.find(y)) { gr.unite(x, y); ans += w; cout << x << " -- " << y << " == " << w<< endl; } } cout << "Minimum Cost Spanning Tree: " << ans; } }; // Driver code int main() { Graph g(4); g.addEdge(0, 1, 10); g.addEdge(1, 3, 15); g.addEdge(2, 3, 4); g.addEdge(2, 0, 6); g.addEdge(0, 3, 5); // Function call g.kruskals_mst(); return 0; } /* Time Complexity: O(E * logE) or O(E * logV) Sorting of edges takes O(E * logE) time. After sorting, we iterate through all edges and apply the find-union algorithm. The find and union operations can take at most O(logV) time. So overall complexity is O(E * logE + E * logV) time. The value of E can be at most O(V2), so O(logV) and O(logE) are the same. Therefore, the overall time complexity is O(E * logE) or O(E*logV). */ ================================================ FILE: Graphs/Graphs_kruskals_algorithm.js ================================================ /** * Graphs: Implement Kruskal's Algorithm in JavaScript * * This code implements Kruskal's Algorithm to find the Minimum Spanning Tree (MST) * in a graph. The graph is represented using an adjacency list. The algorithm works * by repeatedly adding the minimum weight edges that do not form a cycle in the MST. * * Time Complexity: O(E log V), where E is the number of edges and V is the number of vertices. * Space Complexity: O(V), where V is the number of vertices. */ /** * Class representing a Disjoint Set data structure. * Used to track disjoint sets and perform union-find operations. */ class DisjointSet { constructor(n) { this.parent = new Array(n).fill(-1); // Array to store parent of each node this.rank = new Array(n).fill(0); // Array to store the rank of each node } /** * Find the parent of a node in the disjoint set. * Implements path compression to optimize future finds. * @param {number} x - The node to find the parent for. * @returns {number} The parent of the given node. */ find(x) { if (this.parent[x] === -1) { return x; } this.parent[x] = this.find(this.parent[x]); // Path compression return this.parent[x]; } /** * Union two disjoint sets by rank. * Uses union by rank to optimize the merge operation. * @param {number} x - The first node to union. * @param {number} y - The second node to union. */ union(x, y) { let xRoot = this.find(x); let yRoot = this.find(y); if (this.rank[xRoot] < this.rank[yRoot]) { this.parent[xRoot] = yRoot; } else if (this.rank[xRoot] > this.rank[yRoot]) { this.parent[yRoot] = xRoot; } else { this.parent[yRoot] = xRoot; this.rank[xRoot]++; } } } /** * Function to implement Kruskal's Algorithm for finding the Minimum Spanning Tree (MST). * @param {number} n - The number of vertices in the graph. * @param {Array} edges - The edges of the graph represented as an adjacency list. * @returns {Array} The edges of the Minimum Spanning Tree. */ function kruskalsAlgorithm(n, edges) { // Sort edges in non-decreasing order by weight edges.sort((a, b) => a[2] - b[2]); const mst = []; // Minimum Spanning Tree const disjointSet = new DisjointSet(n); // Create a disjoint set for tracking sets for (let [src, dest, weight] of edges) { let srcRoot = disjointSet.find(src); let destRoot = disjointSet.find(dest); // If adding the edge does not create a cycle, add it to the MST if (srcRoot !== destRoot) { mst.push([src, dest, weight]); disjointSet.union(srcRoot, destRoot); } } return mst; } // Example usage const numVertices = 5; const graphEdges = [ [0, 1, 2], [1, 2, 3], [0, 3, 6], [1, 3, 8], [1, 4, 5], [2, 4, 7], [3, 4, 9], ]; const minimumSpanningTree = kruskalsAlgorithm(numVertices, graphEdges); console.log("Minimum Spanning Tree:", minimumSpanningTree); ================================================ FILE: Graphs/Graphs_topological_sort_bfs.cpp ================================================ /* Implementation of Topological sort using BFS According to Introduction to Algorithms, given a directed acyclic graph (DAG), a topological sort is a linear ordering of all vertices such that for any edge (u, v), u comes before v. Another way to describe it is that when you put all vertices horizontally on a line, all of the edges are pointing from left to right. */ #include using namespace std; template class Graph{ public: map > L; Graph(){ } void addEdge(int u, int v, bool bidir = false){ L[u].push_back(v); if(bidir){ L[v].push_back(u); } } void printList(){ for(auto i : L){ cout << i.first << "->"; for(auto element : i.second){ cout << element << ", "; } cout << endl; } } void bfsTopologicalSort(){ queue q; map visited; map indegree; for(auto i : L){ T node = i.first; visited[node] = false; indegree[node] = 0; } // Initialize the indegrees of all nodes for(auto i: L){ T u = i.first; for(T v : L[u]){ indegree[v]++; } } // Finding nodes with 0 indegree for(auto i : L){ T node = i.first; if(indegree[node] == 0){ q.push(node); } } while(!q.empty()){ T node = q.front(); q.pop(); cout << node << "->"; for(T neighbours : L[node]){ indegree[neighbours]--; if(indegree[neighbours] == 0){ q.push(neighbours); } } } } }; int main(){ Graph g; /* g.addEdge(0, 2); g.addEdge(1, 0); g.addEdge(0, 7); g.addEdge(1, 3); g.addEdge(2, 4); g.addEdge(3, 4); g.addEdge(4, 5); g.addEdge(4, 6); */ g.addEdge(1, 2); g.addEdge(1, 3); g.addEdge(1, 4); g.addEdge(2, 4); g.addEdge(4, 3); g.addEdge(2, 3); g.addEdge(3, 5); g.addEdge(4, 5); g.bfsTopologicalSort(); return 0; } ================================================ FILE: Graphs/Graphs_topological_sort_dfs.cpp ================================================ /* Implementation of Topological sort using DFS According to Introduction to Algorithms, given a directed acyclic graph (DAG), a topological sort is a linear ordering of all vertices such that for any edge (u, v), u comes before v. Another way to describe it is that when you put all vertices horizontally on a line, all of the edges are pointing from left to right. */ #include using namespace std; template class Graph{ map > L; public: Graph(){ } void addEdge(T v, T u, bool bidir = false){ L[v].push_back(u); if(bidir){ L[u].push_back(v); } } void print_edge(){ for(auto x : L){ cout << x.first << "->"; for(auto y : x.second){ cout << y << ","; } cout << endl; } } void dfsHelper(T node, map &visited, list &ordering){ visited[node] = true; for(T neighbours : L[node]){ if(!visited[neighbours]){ dfsHelper(neighbours, visited, ordering); } } // add current node to list because all children of curr node have been visited ordering.push_front(node); } void dfs(){ map visited; list ordering; for(auto i : L){ T node = i.first; if(!visited[node]){ dfsHelper(node, visited, ordering); } } for(T element : ordering){ cout << element << "->"; } } }; int main(){ Graph g; g.addEdge(0, 2); g.addEdge(1, 0); g.addEdge(0, 7); g.addEdge(1, 3); g.addEdge(2, 4); g.addEdge(3, 4); g.addEdge(4, 5); g.addEdge(4, 6); g.print_edge(); g.dfs(); return 0; } ================================================ FILE: Graphs/a_star_algorithm.py ================================================ """ A* Algorithm Motivation - To approximate the shortest path in real-life situations, like- in maps, games where there can be many hindrances. Unlike other traversal techniques which focus on the path to be taken, A* algorithm focuses on the distance to be travelled. It is a combination of Uniform Cost Search(UCS) and Best First Search(BFS). Explanation A* algorithm is a best-first search algorithm in which the cost associated with a node is f(n) = g(n) + h(n), where g(n) is the cost of the path from the initial state to node n and h(n) is the heuristic estimate or the cost or a path from node n to a goal. heuristic means guess or estimate. Consider a square grid having many obstacles and we are given a starting cell and a target cell. We want to reach the target cell (if possible) from the starting cell as quickly as possible. Here g(n) is the distance between the current cell and the start cell. So we can say g(n) is the cost of the path from initial state to n. h(n) is a heuristic function that is used to estimate the cost of the cheapest path from n to the goal. So we can say h(n) is the cost from n to goal. So we can say f(n) is the cost of the cheapest path from initial state to goal passing through n. pseudocode 1. Initialize the open list 2. Initialize the closed list put the starting node on the open list (you can leave its f at zero) 3. while the open list is not empty a) find the node with the least f on the open list, call it "q" b) pop q off the open list c) generate q's 8 successors and set their parents to q d) for each successor i) if successor is the goal, stop search ii) else, compute both g and h for successor successor.g = q.g + distance between successor and q successor.h = distance from goal to successor successor.f = successor.g + successor.h iii) if a node with the same position as successor is in the OPEN list which has a lower f than successor, skip this successor iv) if a node with the same position as successor is in the CLOSED list which has a lower f than successor, skip this successor otherwise, add the node to the open list e) push q on the closed list For more information, visit: https://www.geeksforgeeks.org/a-search-algorithm/ or https://stackabuse.com/courses/graphs-in-python-theory-and-implementation/lessons/a-star-search-algorithm/ """ from collections import deque class Graph: # example of adjacency list (or rather map) # adjacency_list = { # 'A': [('B', 1), ('C', 3), ('D', 7)], # 'B': [('D', 5)], # 'C': [('D', 12)] # } def __init__(self, adjacency_list): self.adjacency_list = adjacency_list def get_neighbors(self, v): return self.adjacency_list[v] # heuristic function with equal values for all nodes def h(self, n): H = {"A": 1, "B": 1, "C": 1, "D": 1} return H[n] def a_star_algorithm(self, start_node, stop_node): # open_list is a list of nodes which have been visited, but who's neighbors # haven't all been inspected, starts off with the start node # closed_list is a list of nodes which have been visited # and who's neighbors have been inspected open_list = set([start_node]) closed_list = set([]) # g contains current distances from start_node to all other nodes # the default value (if it's not found in the map) is +infinity g = {} g[start_node] = 0 # parents contains an adjacency map of all nodes parents = {} parents[start_node] = start_node while len(open_list) > 0: n = None # find a node with the lowest value of f() - evaluation function for v in open_list: if n == None or g[v] + self.h(v) < g[n] + self.h(n): n = v if n == None: print("Path does not exist!") return None # if the current node is the stop_node # then we begin reconstructin the path from it to the start_node if n == stop_node: reconst_path = [] while parents[n] != n: reconst_path.append(n) n = parents[n] reconst_path.append(start_node) reconst_path.reverse() print("Path found: {}".format(reconst_path)) return reconst_path # for all neighbors of the current node do for m, weight in self.get_neighbors(n): # if the current node isn't in both open_list and closed_list # add it to open_list and note n as it's parent if m not in open_list and m not in closed_list: open_list.add(m) parents[m] = n g[m] = g[n] + weight # otherwise, check if it's quicker to first visit n, then m # and if it is, update parent data and g data # and if the node was in the closed_list, move it to open_list else: if g[m] > g[n] + weight: g[m] = g[n] + weight parents[m] = n if m in closed_list: closed_list.remove(m) open_list.add(m) # remove n from the open_list, and add it to closed_list # because all of his neighbors were inspected open_list.remove(n) closed_list.add(n) print("Path does not exist!") return None # Driver code adjacency_list = { "A": [("B", 1), ("C", 3), ("D", 7)], "B": [("D", 5)], "C": [("D", 12)], } graph1 = Graph(adjacency_list) graph1.a_star_algorithm("A", "D") # Output: # Path found: ['A', 'B', 'D'] # Explanation: The path with the lowest cost is A -> B -> D with a cost of 6. ================================================ FILE: Graphs/adjacency_list.java ================================================ /* This code is an implementation of a graph data structure using an adjacency list representation in Java. Let's break down the code and explain it step by step: 1. `GraphNode` Class: - This class represents a node in the graph. - It has three attributes: - `name`: A string that represents the name or label of the node. - `index`: An integer that serves as a unique identifier/index for the node. - `neighbors`: An ArrayList of `GraphNode` objects that stores the neighboring nodes of the current node. 2. `AdjacencyList` Class: - This class represents the graph using an adjacency list. - It has the following attributes: - `nodeList`: An ArrayList of `GraphNode` objects that stores all the nodes in the graph. - The constructor takes an ArrayList of `GraphNode` objects and initializes the `nodeList` attribute with it. - The class provides the following methods: - `addUndirectedEdge(int i, int j)`: This method takes two indices (`i` and `j`) representing two nodes in the graph and adds an undirected edge between them. It does so by retrieving the corresponding `GraphNode` objects from the `nodeList` and adding each node to the `neighbors` list of the other. - `printGraph()`: This method returns a human-readable representation of the graph. It iterates through the `nodeList`, prints the name of each node, and lists its neighboring nodes. 3. `main` Class: - This is the main class to demonstrate the graph creation and printing. - Inside the `main` method: - An ArrayList of `GraphNode` objects (`nodeList`) is created, and five nodes are added to it, each with a name and index. - An `AdjacencyList` object (`al`) is created, passing the `nodeList` to its constructor. - Several undirected edges are added using the `addUndirectedEdge` method to establish connections between nodes. - Finally, the `printGraph` method is called on the `al` object to print the graph's structure. The code demonstrates how to create a graph using an adjacency list and provides a readable representation of the graph, including its nodes and edges. */ import java.util.ArrayList; public class GraphNode { public String name; public int index; // An ArrayList to store neighboring nodes public ArrayList neighbors = new ArrayList(); public GraphNode(String name, int index) { this.name = name; this.index = index; } } public class AdjacencyList { // An ArrayList to store all nodes in the graph ArrayList nodeList = new ArrayList(); public AdjacencyList(ArrayList nodeList) { this.nodeList = nodeList; } // Method to add an undirected edge between two nodes public void addUndirectedEdge(int i, int j) { GraphNode first = nodeList.get(i); GraphNode second = nodeList.get(j); first.neighbors.add(second); second.neighbors.add(first); } // Method to print a human-readable representation of the graph public String printGraph() { StringBuilder s = new StringBuilder(); for (int i = 0; i < nodeList.size(); i++) { s.append(nodeList.get(i).name + ": "); for (int j = 0; j < nodeList.get(i).neighbors.size(); j++) { if (j == nodeList.get(i).neighbors.size() - 1) { s.append(nodeList.get(i).neighbors.get(j).name); } else { s.append(nodeList.get(i).neighbors.get(j).name + " --> "); } } s.append("\n"); } return s.toString(); } } public class Main { public static void main(String[] args) { ArrayList nodeList = new ArrayList(); nodeList.add(new GraphNode("A", 0)); nodeList.add(new GraphNode("B", 1)); nodeList add(new GraphNode("C", 2)); nodeList.add(new GraphNode("D", 3)); nodeList.add(new GraphNode("E", 4)); AdjacencyList al = new AdjacencyList(nodeList); al.addUndirectedEdge(0, 1); al.addUndirectedEdge(0, 2); al.addUndirectedEdge(0, 3); al.addUndirectedEdge(1, 4); al.addUndirectedEdge(2, 3); al.addUndirectedEdge(3, 4); System.out.println(al.printGraph()); } } ================================================ FILE: Graphs/adjacency_matrix.go ================================================ package main import "errors" type AdjacencyMatrix struct { Vertices int Edges int GraphType GraphType AdjMatrix [][]int } type GraphType string const ( DIRECTED GraphType = "DIRECTED" UNDIRECTED GraphType = "UNDIRECTED" ) type Graph interface { Init() AddEdge(vertexOne int, vertexTwo int) error AddEdgeWithWeight(vertexOne int, vertexTwo int, weight int) error RemoveEdge(vertexOne int, vertexTwo int) error HasEdge(vertexOne int, vertexTwo int) bool GetGraphType() GraphType GetAdjacentNodesForVertex(vertex int) map[int]bool GetWeightOfEdge(vertexOne int, vertexTwo int) (int, error) GetNumberOfVertices() int GetNumberOfEdges() int GetIndegreeForVertex(vertex int) int } func (G *AdjacencyMatrix) Init() { G.AdjMatrix = make([][]int, G.Vertices) G.Edges = 0 for i := 0; i < G.Vertices; i++ { G.AdjMatrix[i] = make([]int, G.Vertices) // default initialization is 0 } } func (G *AdjacencyMatrix) AddEdge(vertexOne int, vertexTwo int) error { if vertexOne >= G.Vertices || vertexTwo >= G.Vertices || vertexOne < 0 || vertexTwo < 0 { return errors.New("Index out of bounds") } G.AdjMatrix[vertexOne][vertexTwo] = 1 G.Edges++ if G.GraphType == UNDIRECTED { G.AdjMatrix[vertexTwo][vertexOne] = 1 G.Edges++ } return nil } func (G *AdjacencyMatrix) AddEdgeWithWeight(vertexOne int, vertexTwo int, weight int) error { if vertexOne >= G.Vertices || vertexTwo >= G.Vertices || vertexOne < 0 || vertexTwo < 0 { return errors.New("Index out of bounds") } G.AdjMatrix[vertexOne][vertexTwo] = weight G.Edges++ if G.GraphType == UNDIRECTED { G.AdjMatrix[vertexTwo][vertexOne] = weight G.Edges++ } return nil } func (G *AdjacencyMatrix) RemoveEdge(vertexOne int, vertexTwo int) error { if vertexOne >= G.Vertices || vertexTwo >= G.Vertices || vertexOne < 0 || vertexTwo < 0 { return errors.New("Index out of bounds") } G.AdjMatrix[vertexOne][vertexTwo] = 0 G.Edges-- if G.GraphType == UNDIRECTED { G.AdjMatrix[vertexTwo][vertexOne] = 0 G.Edges-- } return nil } func (G *AdjacencyMatrix) HasEdge(vertexOne int, vertexTwo int) bool { if vertexOne >= G.Vertices || vertexTwo >= G.Vertices || vertexOne < 0 || vertexTwo < 0 { return false } return G.AdjMatrix[vertexOne][vertexTwo] != 0 } func (G *AdjacencyMatrix) GetIndegreeForVertex(vertex int) int { indegree := 0 adjacentNodes := G.GetAdjacentNodesForVertex(vertex) for key := range adjacentNodes { if adjacentNodes[key] { indegree++ } } return indegree } func (G *AdjacencyMatrix) GetGraphType() GraphType { return G.GraphType } func (G *AdjacencyMatrix) GetAdjacentNodesForVertex(vertex int) map[int]bool { adjacencyMatrixVertices := map[int]bool{} if vertex >= G.Vertices || vertex < 0 { return adjacencyMatrixVertices } for i := 0; i < G.Vertices; i++ { if G.AdjMatrix[vertex][i] != 0 { adjacencyMatrixVertices[i] = (G.AdjMatrix[vertex][i] != 0) } } return adjacencyMatrixVertices } func (G *AdjacencyMatrix) GetWeightOfEdge(vertexOne int, vertexTwo int) (int, error) { if vertexOne >= G.Vertices || vertexTwo >= G.Vertices || vertexOne < 0 || vertexTwo < 0 { return 0, errors.New("Error getting weight for vertex") } return G.AdjMatrix[vertexOne][vertexTwo], nil } func (G *AdjacencyMatrix) GetNumberOfVertices() int { return G.Vertices } func (G *AdjacencyMatrix) GetNumberOfEdges() int { return G.Edges } ================================================ FILE: Graphs/adjacency_matrix.java ================================================ /* This code snippet is a Java implementation of a graph using an adjacency matrix to represent the connections between nodes. Here's an explanation of the code: 1. `GraphNode` Class: - `GraphNode` represents a node or vertex in the graph. - It has two attributes: `name` to store the name of the node and `index` to store the index of the node. - The constructor initializes these attributes. 2. `AdjacencyMatrix` Class: - `AdjacencyMatrix` represents the graph using an adjacency matrix. - It has the following attributes: - `nodeList`: An `ArrayList` that stores all the nodes in the graph. - `adjacencyMatrix`: A 2D array that represents the connections between nodes. - The constructor takes an `ArrayList` of `GraphNode` objects and initializes the `nodeList` and `adjacencyMatrix` based on the size of the node list. 3. `addUndirectedEdge` Method: - This method is used to add an undirected edge between two nodes. - It takes two indices `i` and `j` to represent the nodes between which the edge is added. - It sets the corresponding values in the adjacency matrix to 1, indicating an edge exists between nodes `i` and `j`. Since it's an undirected graph, it sets both `adjacencyMatrix[i][j]` and `adjacencyMatrix[j][i]` to 1. 4. `printGraph` Method: - This method generates a human-readable string representation of the graph, including the adjacency matrix. - It first prints the node names as column headers and then iterates through the adjacency matrix to display the connections between nodes. 5. `Main` Class: - In the `Main` class, a list of `GraphNode` objects (`nodeList`) is created, each representing a node with a name and an index. - An `AdjacencyMatrix` object (`am`) is created, passing the `nodeList` to its constructor. - Undirected edges are added between nodes using the `addUndirectedEdge` method. - Finally, the graph is printed using the `printGraph` method. The example in the `main` method demonstrates the creation of a simple graph with five nodes and several edges. When you run this program, it will print a representation of the graph showing the connections between the nodes based on the adjacency matrix. Output: A B C D E A: 0 1 1 1 0 B: 1 0 0 0 1 C: 1 0 0 1 0 D: 1 0 1 0 1 E: 0 1 0 1 0 */ import java.util.ArrayList; // Class to represent a graph node public class GraphNode { public String name; public int index; // Constructor to initialize name and index GraphNode(String name, int index) { this.name = name; this.index = index; } } // Class to represent a graph using an adjacency matrix public class AdjacencyMatrix { ArrayList nodeList = new ArrayList(); int[][] adjacencyMatrix; // Constructor to initialize nodeList and adjacencyMatrix public AdjacencyMatrix(ArrayList nodeList) { this.nodeList = nodeList; adjacencyMatrix = new int[nodeList.size()][nodeList.size()]; } // Method to add an undirected edge between two nodes public void addUndirectedEdge(int i, int j) { adjacencyMatrix[i][j] = 1; adjacencyMatrix[j][i] = 1; } // Method to print a human-readable representation of the graph public String printGraph() { StringBuilder s = new StringBuilder(); // Print column headers (node names) s.append(" "); for (int i = 0; i < nodeList.size(); i++) { s.append(nodeList.get(i).name + " "); } s.append("\n"); // Print node names and adjacency matrix for (int i = 0; i < nodeList.size(); i++) { s.append(nodeList.get(i).name + ": "); for (int j : adjacencyMatrix[i]) { s.append((j) + " "); } s.append("\n"); } return s.toString(); } } // Main class to demonstrate the graph creation public class Main { public static void main(String[] args) { ArrayList nodeList = new ArrayList(); nodeList.add(new GraphNode("A", 0)); nodeList.add(new GraphNode("B", 1)); nodeList.add(new GraphNode("C", 2)); nodeList.add(new GraphNode("D", 3)); nodeList.add(new GraphNode("E", 4)); AdjacencyMatrix am = new AdjacencyMatrix(nodeList); // Adding undirected edges am.addUndirectedEdge(0, 1); am.addUndirectedEdge(0, 2); am.addUndirectedEdge(0, 3); am.addUndirectedEdge(1, 4); am.addUndirectedEdge(2, 3); am.addUndirectedEdge(3, 4); // Printing the graph System.out.println(am.printGraph()); } } ================================================ FILE: Graphs/dijkstras.cpp ================================================ /*Name : Abhinav kumar Github username : Abhinavcode13 Repository name : data-structures-and-algorithms Problem : Implement Dijkstra's algorithm in C++ Issue Number : #947 Problem statement : Explanation of the below C++ code : In this implementation, we have a dijkstra function that takes a graph represented as an adjacency list, the starting node, and the total number of nodes. It returns a vector containing the shortest distances from the start node to all other nodes. The dijkstra function initializes all distances to infinity except for the start node, which is set to 0. It uses a min heap priority queue to process nodes based on their distances. The algorithm iteratively selects the node with the minimum distance, updates the distances of its neighbors if a shorter path is found, and adds them to the priority queue. In the main function, we create a graph using the adjacency list representation. Each element of the graph vector is a vector of pairs, where the first element of the pair represents the neighbor node, and the second element represents the weight of the edge. We then call the dijkstra function with the graph, starting node, and the total number of nodes. Finally, we print the shortest distances from the start node to all other nodes. */ -------------------------------------------------------------------------//C++ code begins here------------------------------------------------------------------------ #include #include #include #include using namespace std; typedef pair pii; vector dijkstra(vector>& graph, int start, int n) { vector dist(n, INT_MAX); // Initialize distances to infinity dist[start] = 0; // Distance from start node to itself is 0 // Create a min heap priority queue to store vertices based on their distances priority_queue, greater> pq; pq.push(make_pair(0, start)); while (!pq.empty()) { int u = pq.top().second; pq.pop(); // Traverse all neighboring nodes of u for (auto& neighbor : graph[u]) { int v = neighbor.first; int weight = neighbor.second; // Update distance if a shorter path is found if (dist[v] > dist[u] + weight) { dist[v] = dist[u] + weight; pq.push(make_pair(dist[v], v)); } } } return dist; } int main() { int n, m; // Number of nodes and edges int start; // Starting node cout << "Enter the number of nodes: "; cin >> n; cout << "Enter the number of edges: "; cin >> m; cout << "Enter the starting node: "; cin >> start; // Create an adjacency list representation of the graph vector> graph(n); cout << "Enter the edges and their weights (node1 node2 weight):" << endl; for (int i = 0; i < m; i++) { int node1, node2, weight; cin >> node1 >> node2 >> weight; graph[node1].push_back(make_pair(node2, weight)); } // Run Dijkstra's algorithm vector distances = dijkstra(graph, start, n); // Print the shortest distances from the start node to all other nodes for (int i = 0; i < n; i++) { cout << "Shortest distance from node " << start << " to node " << i << ": " << distances[i] << endl; } return 0; } ================================================ FILE: Graphs/dijkstras.go ================================================ /* Write a function that computes the lengths of the shortest paths between start and all of the other vertices in the graph using Dijkstra's algorithm and returns them in an array. Sample Input: Start: 0 Edges : = [ [[1, 7]], [[2, 6], [3, 20], [4, 3]], [[3, 14]], [[4, 2]], [], [], ] Output: [0, 7, 13, 27, 10, -1] Dijkstras Algorithm Explanation: The code snippet is an implementation of Dijkstra's algorithm for finding the shortest path from a given starting vertex to all other vertices in a graph. Here's a breakdown of the code: 1. The `DijkstrasAlgorithm` function takes the starting vertex (`start`) and the graph represented by the adjacency list (`edges`) as input and returns a list of minimum distances from the starting vertex to all other vertices. 2. It initializes `numberOfVertices` as the total number of vertices in the graph. 3. The `minDistances` slice is initialized with maximum integer values to represent infinity distance for all vertices. The length of `minDistances` is set to the number of vertices. 4. The minimum distance from the starting vertex to itself is set to 0. 5. The `visited` map is used to keep track of visited vertices. Initially, it is empty. 6. The algorithm iterates until all vertices have been visited. In each iteration, it selects the vertex with the minimum distance from the `minDistances` slice using the `getVertexWithMinDistance` function. 7. If the current minimum distance is infinity (i.e., no more vertices to visit), the loop breaks. 8. The selected vertex is marked as visited by adding it to the `visited` map. 9. For each neighboring vertex of the selected vertex, it calculates the new path distance and updates the `minDistances` if the new distance is smaller. 10. After all iterations, the `finalDistances` slice is created to convert the `minDistances` into a format where unreachable vertices are represented as -1. 11. The `getVertexWithMinDistance` function returns the vertex with the minimum distance from the `distances` slice and the current minimum distance. Overall, the code implements Dijkstra's algorithm to find the shortest path from a starting vertex to all other vertices in a graph, using an adjacency list representation. It keeps track of minimum distances, visited vertices, and updates the distances based on the neighboring vertices. Time Complexity: O(V^2 + e) Space complexity: O(V) */ package main import "math" // DijkstrasAlgorithm finds the shortest path from a starting vertex to all other vertices in a graph. func DijkstrasAlgorithm(start int, edges [][][]int) []int { numberOfVertices := len(edges) minDistances := make([]int, 0, len(edges)) // Initialize the minDistances slice with maximum integer values for range edges { minDistances = append(minDistances, math.MaxInt32) } // Set the distance of the starting vertex to 0 minDistances[start] = 0 visited := map[int]bool{} // Iterate until all vertices have been visited for len(visited) != numberOfVertices { // Get the vertex with the minimum distance vertex, currentMinDistance := getVertexWithMinDistance(minDistances, visited) // If the current minimum distance is infinity, break the loop if currentMinDistance == math.MaxInt32 { break } // Mark the vertex as visited visited[vertex] = true // Explore neighboring vertices for _, edge := range edges[vertex] { destination, distanceToDestination := edge[0], edge[1] // Skip if the destination vertex is already visited if visited[destination] { continue } // Calculate the new path distance to the destination newPathDistance := currentMinDistance + distanceToDestination currentDestinationDistance := minDistances[destination] // Update the minimum distance if the new distance is smaller if newPathDistance < currentDestinationDistance { minDistances[destination] = newPathDistance } } } // Convert the minDistances slice to finalDistances, representing unreachable vertices as -1 finalDistances := make([]int, 0, len(minDistances)) for _, distance := range minDistances { if distance == math.MaxInt32 { finalDistances = append(finalDistances, -1) } else { finalDistances = append(finalDistances, distance) } } return finalDistances } // getVertexWithMinDistance returns the vertex with the minimum distance from the distances slice. func getVertexWithMinDistance(distances []int, visited map[int]bool) (int, int) { currentMinDistance := math.MaxInt32 vertex := -1 // Find the vertex with the minimum distance among unvisited vertices for vertexIdx, distance := range distances { if visited[vertexIdx] { continue } if distance <= currentMinDistance { vertex = vertexIdx currentMinDistance = distance } } return vertex, currentMinDistance } ================================================ FILE: Graphs/dijkstras.java ================================================ import java.util.*; class Main { // Represents a node in the graph static class Node implements Comparable { String name; int distance; Node(String name) { this.name = name; this.distance = Integer.MAX_VALUE; // Initialize distance to infinity } @Override public int compareTo(Node other) { return Integer.compare(this.distance, other.distance); } } // Represents a weighted edge between two nodes static class Edge { Node source; Node destination; int weight; Edge(Node source, Node destination, int weight) { this.source = source; this.destination = destination; this.weight = weight; } } // Dijkstra's algorithm implementation static void dijkstra(Map> graph, String start) { Map nodes = new HashMap<>(); // Stores nodes and their distances // Initialize nodes with their respective distances for (String nodeName : graph.keySet()) { Node node = new Node(nodeName); if (nodeName.equals(start)) { node.distance = 0; // Set distance of start node to 0 } nodes.put(nodeName, node); } PriorityQueue queue = new PriorityQueue<>(); // Priority queue for node selection queue.add(nodes.get(start)); // Add the start node to the queue // Dijkstra's algorithm main loop while (!queue.isEmpty()) { Node current = queue.poll(); // Get the node with the smallest distance from the queue // Iterate over the edges of the current node for (Edge edge : graph.get(current.name)) { int newDistance = current.distance + edge.weight; // Calculate new distance to the neighbor node Node neighbor = nodes.get(edge.destination.name); // Get the neighbor node // If the new distance is shorter, update the neighbor node's distance and re-add it to the queue if (newDistance < neighbor.distance) { queue.remove(neighbor); // Remove the neighbor node from the queue neighbor.distance = newDistance; // Update the distance of the neighbor node queue.add(neighbor); // Re-add the neighbor node to the queue } } } } public static void main(String[] args) { Map> graph = new HashMap<>(); // Graph represented as a map of nodes and edges // Create edges and add them to the graph List edgesA = new ArrayList<>(); edgesA.add(new Edge(new Node("A"), new Node("B"), 2)); edgesA.add(new Edge(new Node("A"), new Node("C"), 3)); graph.put("A", edgesA); List edgesB = new ArrayList<>(); edgesB.add(new Edge(new Node("B"), new Node("C"), 1)); edgesB.add(new Edge(new Node("B"), new Node("D"), 1)); graph.put("B", edgesB); List edgesC = new ArrayList<>(); edgesC.add(new Edge(new Node("C"), new Node("D"), 4)); graph.put("C", edgesC); List edgesD = new ArrayList<>(); edgesD.add(new Edge(new Node("D"), new Node("C"), 2)); graph.put("D", edgesD); String start = "A"; // Starting node dijkstra(graph, start); // Run Dijkstra's algorithm // Print the shortest distances from the start node to each node in the graph for (String nodeName : graph.keySet()) { Node node = graph.get(nodeName).get(0).source; System.out.println("Shortest distance from " + start + " to " + node.name + ": " + node.distance); } } } ================================================ FILE: Graphs/dijkstras.py ================================================ ''' Write a function that computes the lengths of the shortest paths between start and all of the other vertices in the graph using Dijkstra's algorithm and returns them in an array. Sample Input: Start: 0 Edges : = [ [[1, 7]], [[2, 6], [3, 20], [4, 3]], [[3, 14]], [[4, 2]], [], [], ] Output: [0, 7, 13, 27, 10, -1] Dijkstras Algorithm Explanation: The code snippet is an implementation of Dijkstra's algorithm for finding the shortest path from a given starting vertex to all other vertices in a graph. Here's a breakdown of the code: 1. The `DijkstrasAlgorithm` function takes the starting vertex (`start`) and the graph represented by the adjacency list (`edges`) as input and returns a list of minimum distances from the starting vertex to all other vertices. 2. It initializes `numberOfVertices` as the total number of vertices in the graph. 3. The `minDistances` slice is initialized with maximum integer values to represent infinity distance for all vertices. The length of `minDistances` is set to the number of vertices. 4. The minimum distance from the starting vertex to itself is set to 0. 5. The `visited` map is used to keep track of visited vertices. Initially, it is empty. 6. The algorithm iterates until all vertices have been visited. In each iteration, it selects the vertex with the minimum distance from the `minDistances` slice using the `getVertexWithMinDistance` function. 7. If the current minimum distance is infinity (i.e., no more vertices to visit), the loop breaks. 8. The selected vertex is marked as visited by adding it to the `visited` map. 9. For each neighboring vertex of the selected vertex, it calculates the new path distance and updates the `minDistances` if the new distance is smaller. 10. After all iterations, the `finalDistances` slice is created to convert the `minDistances` into a format where unreachable vertices are represented as -1. 11. The `getVertexWithMinDistance` function returns the vertex with the minimum distance from the `distances` slice and the current minimum distance. Overall, the code implements Dijkstra's algorithm to find the shortest path from a starting vertex to all other vertices in a graph, using an adjacency list representation. It keeps track of minimum distances, visited vertices, and updates the distances based on the neighboring vertices. Time Complexity: O(V^2 + e) Space complexity: O(V) ''' import "math" # DijkstrasAlgorithm finds the shortest path from a starting vertex to all other vertices in a graph. func DijkstrasAlgorithm(start int, edges [][][]int) []int { numberOfVertices := len(edges) minDistances := make([]int, 0, len(edges)) # Initialize the minDistances slice with maximum integer values for range edges { minDistances = append(minDistances, math.MaxInt32) } # Set the distance of the starting vertex to 0 minDistances[start] = 0 visited := map[int]bool{} # Iterate until all vertices have been visited for len(visited) != numberOfVertices { # Get the vertex with the minimum distance vertex, currentMinDistance := getVertexWithMinDistance(minDistances, visited) # If the current minimum distance is infinity, break the loop if currentMinDistance == math.MaxInt32 { break } # Mark the vertex as visited visited[vertex] = true # Explore neighboring vertices for _, edge := range edges[vertex] { destination, distanceToDestination := edge[0], edge[1] # Skip if the destination vertex is already visited if visited[destination] { continue } # Calculate the new path distance to the destination newPathDistance := currentMinDistance + distanceToDestination currentDestinationDistance := minDistances[destination] # Update the minimum distance if the new distance is smaller if newPathDistance < currentDestinationDistance { minDistances[destination] = newPathDistance } } } # Convert the minDistances slice to finalDistances, representing unreachable vertices as -1 finalDistances := make([]int, 0, len(minDistances)) for _, distance := range minDistances { if distance == math.MaxInt32 { finalDistances = append(finalDistances, -1) } else { finalDistances = append(finalDistances, distance) } } return finalDistances } # getVertexWithMinDistance returns the vertex with the minimum distance from the distances slice. func getVertexWithMinDistance(distances []int, visited map[int]bool) (int, int) { currentMinDistance := math.MaxInt32 vertex := -1 # Find the vertex with the minimum distance among unvisited vertices for vertexIdx, distance := range distances { if visited[vertexIdx] { continue } if distance <= currentMinDistance { vertex = vertexIdx currentMinDistance = distance } } return vertex, currentMinDistance } ================================================ FILE: Graphs/dijkstras_heap_based.go ================================================ /* Dijkstras heap based Explanation: The given code snippet implements Dijkstra's algorithm to find the shortest path from a given start vertex to all other vertices in a weighted directed graph. Here's how the code works: 1. The `DijkstrasAlgorithm` function takes the start vertex and the edges of the graph as input and returns an array of shortest distances from the start vertex to all other vertices. 2. First, it initializes the `minDistances` array with the maximum integer value except for the start vertex, which is set to 0. This array will store the minimum distances from the start vertex to each vertex. 3. It creates a min-heap data structure called `minDistancesHeap` to keep track of the minimum distances. Each item in the heap represents a vertex and its distance from the start vertex. 4. The algorithm starts by removing the vertex with the minimum distance from the `minDistancesHeap` and explores its outgoing edges. 5. For each edge, it calculates the new path distance from the start vertex to the destination vertex through the current vertex. If the new path distance is smaller than the current distance, it updates the `minDistances` array and the `minDistancesHeap` with the new distance. 6. The process continues until all vertices have been visited. 7. Finally, it constructs the final distances array (`finalDistances`) based on the `minDistances` array. If a vertex's distance is still set to the maximum integer value, it means there is no path from the start vertex to that vertex, so -1 is stored instead. 8. The `Item` struct represents a vertex and its distance in the min-heap. 9. The `MinHeap` struct represents the min-heap data structure. It contains an array of `Item` structs and a vertex-to-index map to efficiently update and access items in the heap. 10. The `Remove` method removes the item with the minimum distance from the heap and returns its vertex and distance. 11. The `Update` method updates the distance of a vertex in the heap and maintains the heap property by performing sift-up or sift-down operations. 12. The `siftDown` method performs the sift-down operation to maintain the heap property by comparing the distance of the current item with its children and swapping if necessary. 13. The `siftUp` method performs the sift-up operation to maintain the heap property by comparing the distance of the current item with its parent and swapping if necessary. The time complexity of Dijkstra's algorithm with a min-heap implementation is typically O((V + E) log V), where V is the number of vertices and E is the number of edges in the graph. The space complexity is O(V) for storing the `minDistances` array and the min-heap. */ package main import "math" // DijkstrasAlgorithm finds the shortest distances from the start vertex to all other vertices using Dijkstra's algorithm. func DijkstrasAlgorithm(start int, edges [][][]int) []int { // Get the number of vertices in the graph. numberOfVertices := len(edges) // Initialize the minDistances array with maximum integer values except for the start vertex, which is set to 0. minDistances := make([]int, 0, numberOfVertices) for range edges { minDistances = append(minDistances, math.MaxInt32) } minDistances[start] = 0 // Create a min-heap to store vertices and their distances from the start vertex. minDistancePairs := make([]Item, 0, len(edges)) for i := range edges { minDistancePairs = append(minDistancePairs, Item{i, math.MaxInt32}) } minDistancesHeap := NewMinHeap(minDistancePairs) minDistancesHeap.Update(start, 0) // Explore vertices in the graph using Dijkstra's algorithm until all vertices have been visited. for !minDistancesHeap.IsEmpty() { vertex, currentMinDistance := minDistancesHeap.Remove() // If the currentMinDistance is still set to the maximum integer value, there is no path from the start vertex to this vertex. if currentMinDistance == math.MaxInt32 { break } // Explore the outgoing edges of the current vertex. for _, edge := range edges[vertex] { destination, distanceToDestination := edge[0], edge[1] // Calculate the new path distance from the start vertex to the destination vertex through the current vertex. newPathDistance := currentMinDistance + distanceToDestination // Update the minDistances array and the minDistancesHeap with the new distance if it is smaller. currentDestinationDistance := minDistances[destination] if newPathDistance < currentDestinationDistance { minDistances[destination] = newPathDistance minDistancesHeap.Update(destination, newPathDistance) } } } // Construct the finalDistances array based on the minDistances array. finalDistances := make([]int, 0, len(minDistances)) for _, distance := range minDistances { // If a vertex's distance is still set to the maximum integer value, there is no path from the start vertex to that vertex. // So, -1 is stored instead. if distance == math.MaxInt32 { finalDistances = append(finalDistances, -1) } else { finalDistances = append(finalDistances, distance) } } return finalDistances } // Item represents a vertex and its distance in the min-heap. type Item struct { Vertex int // Vertex represents the index of the vertex. Distance int // Distance represents the distance from the start vertex to the current vertex. } // MinHeap represents a min-heap data structure. type MinHeap struct { array []Item // array is an array of Item structs to represent the heap. vertexMap map[int]int // vertexMap is a map to efficiently update and access items in the heap. } // NewMinHeap creates a new MinHeap instance with the given array of items. func NewMinHeap(array []Item) *MinHeap { // Initialize the vertexMap to store the index of each vertex in the heap. vertexMap := map[int]int{} for _, item := range array { vertexMap[item.Vertex] = item.Vertex } // Create the MinHeap with the array and vertexMap. heap := &MinHeap{array: array, vertexMap: vertexMap} heap.buildHeap() return heap } // IsEmpty checks if the min-heap is empty. func (h *MinHeap) IsEmpty() bool { return h.length() == 0 } // Remove removes the item with the minimum distance from the heap and returns its vertex and distance. func (h *MinHeap) Remove() (int, int) { l := h.length() h.swap(0, l-1) peeked := h.array[l-1] h.array = h.array[0:l-1] delete(h.vertexMap, peeked.Vertex) h.siftDown(0, l-2) return peeked.Vertex, peeked.Distance } // Update updates the distance of a vertex in the heap and maintains the heap property. func (h *MinHeap) Update(vertex int, value int) { h.array[h.vertexMap[vertex]] = Item{vertex, value} h.siftUp(h.vertexMap[vertex]) } // swap swaps two items in the min-heap. func (h MinHeap) swap(i, j int) { h.vertexMap[h.array[i].Vertex] = j h.vertexMap[h.array[j].Vertex] = i h.array[i], h.array[j] = h.array[j], h.array[i] } // length returns the length of the min-heap. func (h MinHeap) length() int { return len(h.array) } // buildHeap performs the build-heap operation on the min-heap. func (h *MinHeap) buildHeap() { first := (len(h.array) - 2) / 2 for currentIdx := first + 1; currentIdx >= 0; currentIdx-- { h.siftDown(currentIdx, len(h.array)-1) } } // siftDown performs the sift-down operation to maintain the heap property. func (h *MinHeap) siftDown(currentIdx, endIdx int) { childOneIdx := currentIdx*2 + 1 for childOneIdx <= endIdx { childTwoIdx := -1 if currentIdx*2+2 <= endIdx { childTwoIdx = currentIdx*2 + 2 } indexToSwap := childOneIdx if childTwoIdx > -1 && h.array[childTwoIdx].Distance < h.array[childOneIdx].Distance { indexToSwap = childTwoIdx } if h.array[indexToSwap].Distance < h.array[currentIdx].Distance { h.swap(currentIdx, indexToSwap) currentIdx = indexToSwap childOneIdx = currentIdx*2 + 1 } else { return } } } // siftUp performs the sift-up operation to maintain the heap property. func (h *MinHeap) siftUp(currentIdx int) { parentIdx := (currentIdx - 1) / 2 for currentIdx > 0 && h.array[currentIdx].Distance < h.array[parentIdx].Distance { h.swap(currentIdx, parentIdx) currentIdx = parentIdx parentIdx = (currentIdx - 1) / 2 } } ================================================ FILE: Graphs/ford_fulkerson.cpp ================================================ /* Name : Shruti Swarupa Dhar Github username : Shr-reny Repository name : data-structures-and-algorithms Problem : Implement Ford Fulkerson algorithm in C++ Issue Number : #1386 Problem statement : Given a graph which represents a flow network where every edge has a capacity. Also, given two vertices source ‘s’ and sink ‘t’ in the graph, find the maximum possible flow from s to t with the following constraints: Flow on an edge doesn’t exceed the given capacity of the edge. Incoming flow is equal to outgoing flow for every vertex except s and t. Explanation of the below C++ code : The Ford-Fulkerson algorithm is a widely used algorithm to solve the maximum flow problem in a flow network. The maximum flow problem involves determining the maximum amount of flow that can be sent from a source vertex to a sink vertex in a directed weighted graph, subject to capacity constraints on the edges. The algorithm works by iteratively finding an augmenting path, which is a path from the source to the sink in the residual graph, i.e., the graph obtained by subtracting the current flow from the capacity of each edge. The algorithm then increases the flow along this path by the maximum possible amount, which is the minimum capacity of the edges along the path. Time Complexity : O(|V| * E^2) ,where E is the number of edges and V is the number of vertices. Space Complexity :O(V) , as we created queue. */ // C++ program for implementation of Ford Fulkerson // algorithm #include #include #include #include using namespace std; // Number of vertices in given graph #define V 6 /* Returns true if there is a path from source 's' to sink 't' in residual graph. Also fills parent[] to store the path */ bool bfs(int rGraph[V][V], int s, int t, int parent[]) { // Create a visited array and mark all vertices as not // visited bool visited[V]; memset(visited, 0, sizeof(visited)); // Create a queue, enqueue source vertex and mark source // vertex as visited queue q; q.push(s); visited[s] = true; parent[s] = -1; // Standard BFS Loop while (!q.empty()) { int u = q.front(); q.pop(); for (int v = 0; v < V; v++) { if (visited[v] == false && rGraph[u][v] > 0) { // If we find a connection to the sink node, // then there is no point in BFS anymore We // just have to set its parent and can return // true if (v == t) { parent[v] = u; return true; } q.push(v); parent[v] = u; visited[v] = true; } } } // We didn't reach sink in BFS starting from source, so // return false return false; } // Returns the maximum flow from s to t in the given graph int fordFulkerson(int graph[V][V], int s, int t) { int u, v; // Create a residual graph and fill the residual graph // with given capacities in the original graph as // residual capacities in residual graph int rGraph[V] [V]; // Residual graph where rGraph[i][j] // indicates residual capacity of edge // from i to j (if there is an edge. If // rGraph[i][j] is 0, then there is not) for (u = 0; u < V; u++) for (v = 0; v < V; v++) rGraph[u][v] = graph[u][v]; int parent[V]; // This array is filled by BFS and to // store path int max_flow = 0; // There is no flow initially // Augment the flow while there is path from source to // sink while (bfs(rGraph, s, t, parent)) { // Find minimum residual capacity of the edges along // the path filled by BFS. Or we can say find the // maximum flow through the path found. int path_flow = INT_MAX; for (v = t; v != s; v = parent[v]) { u = parent[v]; path_flow = min(path_flow, rGraph[u][v]); } // update residual capacities of the edges and // reverse edges along the path for (v = t; v != s; v = parent[v]) { u = parent[v]; rGraph[u][v] -= path_flow; rGraph[v][u] += path_flow; } // Add path flow to overall flow max_flow += path_flow; } // Return the overall flow return max_flow; } // Driver program to test above functions int main() { // Let us create a graph shown in the above example int graph[V][V] = { { 0, 16, 13, 0, 0, 0 }, { 0, 0, 10, 12, 0, 0 }, { 0, 4, 0, 0, 14, 0 }, { 0, 0, 9, 0, 0, 20 }, { 0, 0, 0, 7, 0, 4 }, { 0, 0, 0, 0, 0, 0 } }; cout << "The maximum possible flow is " << fordFulkerson(graph, 0, 5); return 0; } /*Sample Output: The maximum possible flow is 23. */ ================================================ FILE: Graphs/graphs_adjacency_list.cpp ================================================ // Graphs Adjacency List implementation // Program Author : Abhisek Kumar Gupta #include using namespace std; class Graph{ public: int V; list *L; // a pointer to an array of linkedlist Graph(int v){ V = v; L = new list[V]; // Array of LL // there is a pointer to an array whose size is v and every object is list of integer } void addEdge(int u, int v, bool bidir = true){ L[u].push_back(v); if(bidir){ L[v].push_back(u); } } void printAdjacencyList(){ for(int i = 0; i < V; i++){ cout << i << "-> "; for(auto vertex : L[i]){ cout << vertex << ", "; } cout << endl; } } }; int main(){ //graph has 5 vertices numbered from 0-4 Graph g(5); g.addEdge(0, 1); g.addEdge(0, 4); g.addEdge(1, 4); g.addEdge(1, 3); g.addEdge(1, 2); g.addEdge(2, 3); g.addEdge(4, 3); g.printAdjacencyList(); return 0; } ================================================ FILE: Graphs/graphs_adjacency_list_generic.cpp ================================================ // Graphs Adjacency List implementation for Generic Data // Program Author : Abhisek Kumar Gupta #include using namespace std; template class Graph{ map > adjList; public: Graph(){ } void addEdge(T u, T v, bool bidir = true){ adjList[u].push_back(v); if(bidir){ adjList[v].push_back(u); } } void printAdjList(){ for(auto obj : adjList){ cout << obj.first << "->"; for(auto element : obj.second){ cout << element << ","; } cout << endl; } } }; int main(){ Graph g; g.addEdge("Ashish", "Asif", false); g.addEdge("Ashish", "Abhisek", false); g.addEdge("Ashish", "Amir", false); g.addEdge("Abhisek", "Asif", true); g.addEdge("Abhisek", "Anvesh", true); g.addEdge("Anvesh", "Sai", false); g.addEdge("Sai", "Abhisek", false); g.printAdjList(); return 0; } ================================================ FILE: Graphs/graphs_bfs.go ================================================ // Breadth First Search /* In this implementation, we define a Graph struct that represents a graph with a given number of vertices, edges, and a visited array to keep track of visited vertices. We also define two methods - addEdge to add an edge to the graph and BFS to perform the BFS algorithm. In the BFS method, we start by creating a queue to store the vertices to visit. We mark the start node as visited and enqueue it. Then, while the queue is not empty, we dequeue a vertex from the queue, print it out, and get all its adjacent vertices. For each adjacent vertex, if it hasn't been visited, we mark it as visited and enqueue it. This continues until all reachable vertices have been visited. In the main function, we create a new Graph with 5 vertices and add edges to it. We then perform BFS starting from vertex 2, and print out the visited vertices. The output of this program will be: BFS starting from vertex 2: 2 0 3 1 The time complexity of BFS (Breadth-First Search) algorithm is O(V + E), where V is the number of vertices and E is the number of edges in the graph. This is because BFS traverses all the vertices and edges of the graph exactly once, and the time taken to visit each vertex and edge is constant. Therefore, the time complexity of BFS is proportional to the size of the graph. The space complexity of BFS is O(|V|), where |V| is the number of vertices in the graph. This is because in the worst case scenario, we would need to store all vertices in the queue before we finish traversing the graph. */ package main import "fmt" // Define a Graph struct to represent a graph type Graph struct { vertices int // Number of vertices in the graph edges [][]int // Adjacency list to store edges visited []bool // Track if a vertex is visited or not } // Function to add an edge to the graph func (g *Graph) addEdge(u, v int) { // Add edge from u to v g.edges[u] = append(g.edges[u], v) // Add edge from v to u g.edges[v] = append(g.edges[v], u) } // Function to perform Breadth First Search func BFS(g *Graph, start int) { // Create a queue for BFS queue := []int{} // Mark the start node as visited and enqueue it g.visited[start] = true queue = append(queue, start) for len(queue) != 0 { // Dequeue a vertex from the queue currVertex := queue[0] queue = queue[1:] fmt.Printf("%d ", currVertex) // Get all adjacent vertices of the dequeued vertex currVertex // If an adjacent vertex has not been visited, mark it as visited and enqueue it for _, adjVertex := range g.edges[currVertex] { if !g.visited[adjVertex] { g.visited[adjVertex] = true queue = append(queue, adjVertex) } } } } func main() { // Create a new graph with 5 vertices g := Graph{ vertices: 5, edges: make([][]int, 5), // Initially all vertices are unvisited visited: make([]bool, 5), } // Add edges to the graph g.addEdge(0, 1) g.addEdge(0, 2) g.addEdge(1, 2) g.addEdge(2, 0) g.addEdge(2, 3) g.addEdge(3, 3) // Perform BFS starting from vertex 2 fmt.Println("BFS starting from vertex 2:") BFS(&g, 2) } ================================================ FILE: Graphs/graphs_dfs.go ================================================ /* This implementation represents a graph as a collection of nodes, where each node has a value and a list of neighboring nodes. The DFS algorithm is implemented recursively by starting from the given node, marking it as visited, and printing its value. Then, the algorithm recursively visits each of the neighboring nodes that have not been visited yet. The time complexity of DFS is O(V + E), where V is the number of vertices (nodes) in the graph and E is the number of edges between the vertices. Sample Input : graph = { 'A': ['B', 'C'], 'B': ['D', 'E'], 'C': ['F'], 'D': [], 'E': ['F'], 'F': [] } Output : DFS Traversal: A B D E F C Here, we have a graph represented as an adjacency list, with nodes A, B, C, D, E, and F and their respective neighbors. The DFS traversal starts at node A and visits each node in the graph, outputting the final order in which the nodes were visited. The time complexity of the DFS algorithm is O(V + E), where V is the number of vertices (nodes) and E is the number of edges in the graph. This is because the algorithm visits every vertex and every edge once. The space complexity of DFS depends on the maximum depth of the recursion stack. In the worst case, where the tree or graph is completely unbalanced, the recursion stack can reach a depth of O(n), where n is the number of nodes in the graph. Therefore, the space complexity of DFS is O(n). */ package main import "fmt" // Node represents a single node in the graph type Node struct { value int visited bool neighbors []*Node } // DFS traverses the graph starting from the given node using Depth First Search func DFS(node *Node) { // Mark the current node as visited node.visited = true // Print the value of the current node fmt.Printf("%d ", node.value) // Visit each of the neighboring nodes for _, neighbor := range node.neighbors { // If the neighbor has not been visited yet, visit it recursively if !neighbor.visited { DFS(neighbor) } } } func main() { // Create the nodes of the graph node1 := &Node{value: 1} node2 := &Node{value: 2} node3 := &Node{value: 3} node4 := &Node{value: 4} node5 := &Node{value: 5} // Add the neighbors for each node node1.neighbors = []*Node{node2, node3} node2.neighbors = []*Node{node1, node4, node5} node3.neighbors = []*Node{node1, node5} node4.neighbors = []*Node{node2} node5.neighbors = []*Node{node2, node3} // Start the DFS traversal from node1 DFS(node1) } ================================================ FILE: Graphs/kruskals_algorithm.java ================================================ /* What is a minimum spanning tree? A minimum spanning tree (MST) or minimum weight spanning tree for a weighted, connected, undirected graph is a spanning tree with a weight less than or equal to the weight of every other spanning tree. What is Kruskal’s algorithm? In Kruskal’s algorithm, sort all edges of the given graph in increasing order. Then it keeps on adding new edges and nodes in the MST if the newly added edge does not form a cycle. It picks the minimum weighted edge at first at the maximum weighted edge at last. Thus we can say that it makes a locally optimal choice in each step in order to find the optimal solution. How Kruskal's algorithm works It falls under a class of algorithms called greedy algorithms that find the local optimum in the hopes of finding a global optimum. We start from the edges with the lowest weight and keep adding edges until we reach our goal. The steps for implementing Kruskal's algorithm are as follows: 1. Sort all the edges from low weight to high 2. Take the edge with the lowest weight and add it to the spanning tree. If adding the edge created a cycle, then reject this edge. 3. Keep adding edges until we reach all vertices. Time Complexity: O(E * logE) or O(E * logV) Auxiliary Space: O(V + E), where V is the number of vertices and E is the number of edges in the graph. */ // Java program for Kruskal's algorithm import java.util.ArrayList; import java.util.Comparator; import java.util.List; public class KruskalsMST { // Defines edge structure static class Edge { int src, dest, weight; public Edge(int src, int dest, int weight) { this.src = src; this.dest = dest; this.weight = weight; } } // Defines subset element structure static class Subset { int parent, rank; public Subset(int parent, int rank) { this.parent = parent; this.rank = rank; } } // Starting point of program execution public static void main(String[] args) { int V = 4; List graphEdges = new ArrayList( List.of(new Edge(0, 1, 10), new Edge(0, 2, 6), new Edge(0, 3, 5), new Edge(1, 3, 15), new Edge(2, 3, 4))); // Sort the edges in non-decreasing order // (increasing with repetition allowed) graphEdges.sort(new Comparator() { @Override public int compare(Edge o1, Edge o2) { return o1.weight - o2.weight; } }); kruskals(V, graphEdges); } // Function to find the MST private static void kruskals(int V, List edges) { int j = 0; int noOfEdges = 0; // Allocate memory for creating V subsets Subset subsets[] = new Subset[V]; // Allocate memory for results Edge results[] = new Edge[V]; // Create V subsets with single elements for (int i = 0; i < V; i++) { subsets[i] = new Subset(i, 0); } // Number of edges to be taken is equal to V-1 while (noOfEdges < V - 1) { // Pick the smallest edge. And increment // the index for next iteration Edge nextEdge = edges.get(j); int x = findRoot(subsets, nextEdge.src); int y = findRoot(subsets, nextEdge.dest); // If including this edge doesn't cause cycle, // include it in result and increment the index // of result for next edge if (x != y) { results[noOfEdges] = nextEdge; union(subsets, x, y); noOfEdges++; } j++; } // Print the contents of result[] to display the // built MST System.out.println( "Following are the edges of the constructed MST:"); int minCost = 0; for (int i = 0; i < noOfEdges; i++) { System.out.println(results[i].src + " -- " + results[i].dest + " == " + results[i].weight); minCost += results[i].weight; } System.out.println("Total cost of MST: " + minCost); } // Function to unite two disjoint sets private static void union(Subset[] subsets, int x, int y) { int rootX = findRoot(subsets, x); int rootY = findRoot(subsets, y); if (subsets[rootY].rank < subsets[rootX].rank) { subsets[rootY].parent = rootX; } else if (subsets[rootX].rank < subsets[rootY].rank) { subsets[rootX].parent = rootY; } else { subsets[rootY].parent = rootX; subsets[rootX].rank++; } } // Function to find parent of a set private static int findRoot(Subset[] subsets, int i) { if (subsets[i].parent == i) return subsets[i].parent; subsets[i].parent = findRoot(subsets, subsets[i].parent); return subsets[i].parent; } } /* Output Following are the edges in the constructed MST 2 -- 3 == 4 0 -- 3 == 5 0 -- 1 == 10 Minimum Cost Spanning Tree: 19 */ ================================================ FILE: Graphs/kruskals_algorithm.py ================================================ #Question: Implement Kruskal's algorithm in Python to find the minimum spanning tree of a given graph. #Intuition: # Kruskal's algorithm is a greedy algorithm that aims to find the minimum spanning tree (MST) of a connected, weighted graph. # The MST is a subset of the graph's edges that connects all the vertices with the minimum total weight. # The algorithm works by iteratively selecting the edges in ascending order of their weights, while ensuring that adding an edge to the growing MST does not create a cycle. #It uses a disjoint set data structure, typically implemented with the Union-Find algorithm, to efficiently keep track of the connected components and # detect cycles. #Implementing Kruskal's algorithm with the help of a Union-Find data structure allows for efficient detection of cycles and union operations, #resulting in a time complexity of O(E log E), where E is the number of edges in the graph. # Initialize a Union-Find data structure to keep track of the connected components. class UnionFind: def __init__(self, n): # Initialize the parent and rank lists self.parent = list(range(n)) self.rank = [0] * n def find(self, x): # Find the root of the set to which x belongs if self.parent[x] != x: self.parent[x] = self.find(self.parent[x]) # Path compression return self.parent[x] def union(self, x, y): # Perform the union of two sets root_x = self.find(x) root_y = self.find(y) if root_x != root_y: if self.rank[root_x] < self.rank[root_y]: self.parent[root_x] = root_y elif self.rank[root_x] > self.rank[root_y]: self.parent[root_y] = root_x else: self.parent[root_y] = root_x self.rank[root_x] += 1 # Define the Kruskal function def kruskal(graph): edges = [] for u in range(len(graph)): for v, weight in graph[u]: edges.append((weight, u, v)) edges.sort() # Create an empty list to store the MST mst = [] uf = UnionFind(len(graph)) for weight, u, v in edges: if uf.find(u) != uf.find(v): uf.union(u, v) mst.append((u, v, weight)) return mst # Example usage: Create a list of all the edges in the graph, along with their weights. graph = [ [(1, 1), (2, 2)], # Node 0 [(0, 1), (2, 3), (3, 4)], # Node 1 [(0, 2), (1, 3), (3, 5)], # Node 2 [(1, 4), (2, 5)] # Node 3 ] #Sort the edges in ascending order based on their weights. mst = kruskal(graph) for u, v, weight in mst: print(f"Edge ({u}, {v}) with weight {weight}") ================================================ FILE: Graphs/remove_island.go ================================================ /* Remoev island that are not connected to Border Sample Input: 1 0 0 0 0 0 0 1 0 1 0 1 0 0 1 0 1 1 1 1 0 0 1 0 1 0 1 1 0 0 Output: 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 1 0 0 1 0 1 0 1 1 0 0 Explanation: The provided code snippet is an implementation of the "Remove Islands" algorithm. This algorithm aims to identify and remove islands in a binary matrix. An island is a connected region of 1s surrounded by 0s. The algorithm marks islands connected to the border of the matrix as non-islands and returns the modified matrix. Let's break down the code snippet and explain each part: 1. Initialization: onesConnectedToBorder := make([][]bool, len(matrix)) for i := range matrix { onesConnectedToBorder[i] = make([]bool, len(matrix[0])) } This part initializes a 2D boolean array `onesConnectedToBorder`, which has the same dimensions as the input matrix. It is used to mark cells that are connected to the border. 2. Marking Ones Connected to Border: for row := 0; row < len(matrix); row++ { for col := 0; col < len(matrix[row]); col++ { rowIsBorder := row == 0 || row == len(matrix)-1 colIsBorder := col == 0 || col == len(matrix[row])-1 isBorder := rowIsBorder || colIsBorder if !isBorder { continue } if matrix[row][col] != 1 { continue } findOnesConnectedToBorder(matrix, row, col, onesConnectedToBorder) } } This part iterates through the matrix and checks if each cell is on the border. If a cell is on the border and contains a 1, it calls the `findOnesConnectedToBorder` function to mark the connected 1s using a depth-first search approach. The connected 1s are marked in the `onesConnectedToBorder` array. 3. Removing Non-Border Islands: for row := 0; row < len(matrix)-1; row++ { for col := 0; col < len(matrix[row])-1; col++ { if onesConnectedToBorder[row][col] { continue } matrix[row][col] = 0 } } This part iterates through the matrix again, excluding the border cells. If a cell is not marked as connected to the border (an island cell), it is set to 0, effectively removing the island. 4. Utility Functions: The code also includes two utility functions: - `findOnesConnectedToBorder`: This function performs a depth-first search (DFS) to mark all the 1s connected to a border cell. It uses a stack to keep track of the cells to visit next. - `getNeighbors`: This function returns the valid neighboring cells (up, down, left, right) for a given cell in the matrix. Finally, the modified matrix is returned as the result. O(wh) time | O(wh) space - where w and h are the width and height of the input matrix Note: The code snippet provided assumes that the matrix is a 2D integer slice (`[][]int`) and uses Go syntax.. */ package main import "fmt" // Function to remove islands in a binary matrix func RemoveIslands(matrix [][]int) [][]int { // Create a boolean matrix to track if each cell is connected to the border onesConnectedToBorder := make([][]bool, len(matrix)) for i := range matrix { onesConnectedToBorder[i] = make([]bool, len(matrix[0])) } // Mark 1s connected to the border for row := 0; row < len(matrix); row++ { for col := 0; col < len(matrix[row]); col++ { rowIsBorder := row == 0 || row == len(matrix)-1 colIsBorder := col == 0 || col == len(matrix[row])-1 isBorder := rowIsBorder || colIsBorder if !isBorder { continue // Skip if not a border cell } if matrix[row][col] != 1 { continue // Skip if not a 1 } findOnesConnectedToBorder(matrix, row, col, onesConnectedToBorder) } } // Remove non-border islands for row := 0; row < len(matrix)-1; row++ { for col := 0; col < len(matrix[row])-1; col++ { if onesConnectedToBorder[row][col] { continue // Skip if connected to the border } matrix[row][col] = 0 // Set non-border island to 0 } } return matrix } // Function to perform DFS and mark 1s connected to the border func findOnesConnectedToBorder(matrix [][]int, startRow, startCol int, onesConnectedToBorder [][]bool) { stack := [][]int{{startRow, startCol}} var currentPosition []int for len(stack) > 0 { currentPosition, stack = stack[len(stack)-1], stack[:len(stack)-1] currentRow, currentCol := currentPosition[0], currentPosition[1] alreadyVisited := onesConnectedToBorder[currentRow][currentCol] if alreadyVisited { continue // Skip if already visited } onesConnectedToBorder[currentRow][currentCol] = true // Mark cell as connected to the border neighbors := getNeighbors(matrix, currentRow, currentCol) for _, neighbor := range neighbors { row, col := neighbor[0], neighbor[1] if matrix[row][col] != 1 { continue // Skip if not a 1 } stack = append(stack, neighbor) // Add neighbor to the stack } } } // Function to get valid neighboring cells func getNeighbors(matrix [][]int, row, col int) [][]int { neighbors := make([][]int, 0) numRows := len(matrix) numCols := len(matrix[row]) if row-1 >= 0 { neighbors = append(neighbors, []int{row - 1, col}) // Top neighbor } if row+1 < numRows { neighbors = append(neighbors, []int{row + 1, col}) // Bottom neighbor } if col-1 >= 0 { neighbors = append(neighbors, []int{row, col - 1}) // Left neighbor } if col+1 < numCols { neighbors = append(neighbors, []int{row, col + 1}) // Right neighbor } return neighbors } func main() { // Example usage matrix := [][]int{ {1, 0, 0, 0, 0, 0}, {0, 1, 0, 1, 0, 1}, {0, 0, 1, 0, 1, 1}, {1, 1, 0, 0, 1, 0}, {1, 0, 1, 1, 0, 0}, } fmt.Println("Original Matrix:") printMatrix(matrix) updatedMatrix := RemoveIslands(matrix) fmt.Println("Updated Matrix:") printMatrix(updatedMatrix) } // Helper function to print the matrix func printMatrix(matrix [][]int) { for _, row := range matrix { for _, val := range row { fmt.Printf("%d ", val) } fmt.Println() } } ================================================ FILE: Graphs/river_sizes.cpp ================================================ /* You're given a two-dimensional array (a matrix) of potentially unequal height and width containing only 0's and 1's. Each 0 represent land and each 1 represent part of river. A river consists of any number of 1's that are either horizontally or vertically adjacent (but not diagonally adjacent). The number of adjacent 1's forming a river determine its size. Note that a river can twist. In other words, it doesn't have to be a straight vertical line or a straight horizontal line; it can be L-shaped, for example. Write a function that returns an array of the sizes of all rivers represented in the input matrix. The sizes don't need to be in any particular order. Sample Input:[ [1, 0, 0, 1, 0], [1, 0, 1, 0, 0], [0, 0, 1, 0, 1], [1, 0, 1, 0, 1], [1, 0, 1, 1, 0], ] Output: [1, 2, 2, 2, 5] Explanation: 1. The function `RiverSizes` initializes an empty slice `sizes` to store the sizes of rivers found in the matrix. It also creates a 2D `visited` matrix of the same size as the input matrix to keep track of visited nodes. 2. The function then iterates over each cell in the matrix using nested loops. 3. If a cell has already been visited (marked as `true` in the `visited` matrix), the code continues to the next iteration to avoid processing it again. 4. If a cell has not been visited, the code calls the `traverseNode` function to explore the river connected to that cell and updates the `sizes` slice with the size of the river. 5. The `traverseNode` function takes the starting position (i, j) of a river, the matrix, the `visited` matrix, and the `sizes` slice as input. 6. It initializes a variable `currentRiverSize` to 0 to keep track of the size of the river being explored. 7. It maintains a queue (`nodesToExplore`) to store the nodes that need to be visited. It starts with the initial node (i, j). 8. The function enters a loop that continues until there are no more nodes to explore in the queue. 9. In each iteration, it dequeues the first node from the `nodesToExplore` queue and updates the current position (i, j) accordingly. 10. If the current node has already been visited, the code continues to the next iteration. 11. If the current node contains a value of 0 (indicating land), the code continues to the next iteration, as it's not part of the river. 12. If the current node contains a value of 1 (indicating a river), it increments the `currentRiverSize` by 1. 13. It then retrieves the unvisited neighboring nodes of the current node using the `getUnvisitedNeighbors` function and adds them to the `nodesToExplore` queue. 14. Once the loop finishes, if the `currentRiverSize` is greater than 0, it appends it to the `sizes` slice. 15. Finally, the `sizes` slice containing the sizes of all rivers is returned. 16. The `getUnvisitedNeighbors` function takes the current position (i, j), the matrix, and the `visited` matrix as input. 17. It checks the four neighboring cells (up, down, left, right) of the current cell and adds the unvisited neighbors to the `unvisitedNeighbors` slice. 18. The function returns the `unvisitedNeighbors` slice. The code essentially performs a depth-first search (DFS) traversal on the matrix to find connected regions of 1s (rivers) and keeps track of their sizes. The `visited` matrix helps avoid revisiting nodes that have already been processed, ensuring each river is counted only once. */ #include #include using namespace std; vector riverSizes(vector>& matrix) { // Vector to store the sizes of rivers vector sizes; // Create a visited matrix to keep track of visited nodes vector> visited(matrix.size(), vector(matrix[0].size(), false)); // Iterate over each cell in the matrix for (int i = 0; i < matrix.size(); i++) { for (int j = 0; j < matrix[i].size(); j++) { // If the cell has already been visited, continue to the next iteration if (visited[i][j]) { continue; } // Explore the river connected to the current cell and update sizes sizes = traverseNode(i, j, matrix, visited, sizes); } } return sizes; } vector traverseNode(int i, int j, vector>& matrix, vector>& visited, vector& sizes) { // Variable to track the size of the current river int currentRiverSize = 0; // Queue to store nodes that need to be visited vector> nodesToExplore{{i, j}}; // Loop until there are no more nodes to explore while (!nodesToExplore.empty()) { // Dequeue the first node from the queue and update the current position (i, j) vector currentNode = nodesToExplore[0]; nodesToExplore.erase(nodesToExplore.begin()); i = currentNode[0]; j = currentNode[1]; // If the current node has already been visited, continue to the next iteration if (visited[i][j]) { continue; } // Mark the current node as visited visited[i][j] = true; // If the current node is land (0), continue to the next iteration if (matrix[i][j] == 0) { continue; } // Increment the size of the current river currentRiverSize++; // Get the unvisited neighboring nodes of the current node vector> unvisitedNeighbors = getUnvisitedNeighbors(i, j, matrix, visited); // Add the unvisited neighbors to the nodesToExplore queue for (const auto& neighbor : unvisitedNeighbors) { nodesToExplore.push_back(neighbor); } } // If the current river size is greater than 0, append it to the sizes vector if (currentRiverSize > 0) { sizes.push_back(currentRiverSize); } return sizes; } vector> getUnvisitedNeighbors(int i, int j, vector>& matrix, vector>& visited) { // Vector to store unvisited neighboring nodes vector> unvisitedNeighbors; // Check the four neighboring cells (up, down, left, right) of the current cell // and add unvisited neighbors to the unvisitedNeighbors vector // Up neighbor if (i > 0 && !visited[i - 1][j]) { unvisitedNeighbors.push_back({i - 1, j}); } // Down neighbor if (i < matrix.size() - 1 && !visited[i + 1][j]) { unvisitedNeighbors.push_back({i + 1, j}); } // Left neighbor if (j > 0 && !visited[i][j - 1]) { unvisitedNeighbors.push_back({i, j - 1}); } // Right neighbor if (j < matrix[0].size() - 1 && !visited[i][j + 1]) { unvisitedNeighbors.push_back({i, j + 1}); } return unvisitedNeighbors; } int main() { // Test the riverSizes function vector> matrix = { {1, 0, 0, 1, 0}, {1, 0, 1, 0, 0}, {0, 0, 1, 0, 1}, {1, 0, 1, 0, 1}, {1, 0, 1, 1, 0} }; vector sizes = riverSizes(matrix); // Print the sizes of the rivers for (const auto& size : sizes) { cout << size << " "; } return 0; } ================================================ FILE: Graphs/river_sizes.go ================================================ /* You're given a two-dimensional array (a matrix) of potentially unequal height and width containing only 0's and 1's. Each 0 represent land and each 1 represent part of river. A river consists of any number of 1's that are either horizontally or vertically adjacent (but not diagonally adjacent). The number of adjacent 1's forming a river determine its size. Note that a river can twist. In other words, it doesn't have to be a straight vertical line or a straight horizontal line; it can be L-shaped, for example. Write a function that returns an array of the sizes of all rivers represented in the input matrix. The sizes don't need to be in any particular order. Sample Input:[ [1, 0, 0, 1, 0], [1, 0, 1, 0, 0], [0, 0, 1, 0, 1], [1, 0, 1, 0, 1], [1, 0, 1, 1, 0], ] Output: [1, 2, 2, 2, 5] Explanation: 1. The function `RiverSizes` initializes an empty slice `sizes` to store the sizes of rivers found in the matrix. It also creates a 2D `visited` matrix of the same size as the input matrix to keep track of visited nodes. 2. The function then iterates over each cell in the matrix using nested loops. 3. If a cell has already been visited (marked as `true` in the `visited` matrix), the code continues to the next iteration to avoid processing it again. 4. If a cell has not been visited, the code calls the `traverseNode` function to explore the river connected to that cell and updates the `sizes` slice with the size of the river. 5. The `traverseNode` function takes the starting position (i, j) of a river, the matrix, the `visited` matrix, and the `sizes` slice as input. 6. It initializes a variable `currentRiverSize` to 0 to keep track of the size of the river being explored. 7. It maintains a queue (`nodesToExplore`) to store the nodes that need to be visited. It starts with the initial node (i, j). 8. The function enters a loop that continues until there are no more nodes to explore in the queue. 9. In each iteration, it dequeues the first node from the `nodesToExplore` queue and updates the current position (i, j) accordingly. 10. If the current node has already been visited, the code continues to the next iteration. 11. If the current node contains a value of 0 (indicating land), the code continues to the next iteration, as it's not part of the river. 12. If the current node contains a value of 1 (indicating a river), it increments the `currentRiverSize` by 1. 13. It then retrieves the unvisited neighboring nodes of the current node using the `getUnvisitedNeighbors` function and adds them to the `nodesToExplore` queue. 14. Once the loop finishes, if the `currentRiverSize` is greater than 0, it appends it to the `sizes` slice. 15. Finally, the `sizes` slice containing the sizes of all rivers is returned. 16. The `getUnvisitedNeighbors` function takes the current position (i, j), the matrix, and the `visited` matrix as input. 17. It checks the four neighboring cells (up, down, left, right) of the current cell and adds the unvisited neighbors to the `unvisitedNeighbors` slice. 18. The function returns the `unvisitedNeighbors` slice. The code essentially performs a depth-first search (DFS) traversal on the matrix to find connected regions of 1s (rivers) and keeps track of their sizes. The `visited` matrix helps avoid revisiting nodes that have already been processed, ensuring each river is counted only once. */ package main func RiverSizes(matrix [][]int) []int { // Slice to store the sizes of rivers sizes := []int{} // Create a visited matrix to keep track of visited nodes visited := make([][]bool, len(matrix)) for i := range visited { visited[i] = make([]bool, len(matrix[i])) } // Iterate over each cell in the matrix for i := range matrix { for j := range matrix[i] { // If the cell has already been visited, continue to the next iteration if visited[i][j] { continue } // Explore the river connected to the current cell and update sizes sizes = traverseNode(i, j, matrix, visited, sizes) } } return sizes } func traverseNode(i, j int, matrix [][]int, visited [][]bool, sizes []int) []int { // Variable to track the size of the current river currentRiverSize := 0 // Queue to store nodes that need to be visited nodesToExplore := [][]int{{i, j}} // Loop until there are no more nodes to explore for len(nodesToExplore) > 0 { // Dequeue the first node from the queue and update the current position (i, j) currentNode := nodesToExplore[0] nodesToExplore = nodesToExplore[1:] i, j := currentNode[0], currentNode[1] // If the current node has already been visited, continue to the next iteration if visited[i][j] { continue } // Mark the current node as visited visited[i][j] = true // If the current node is land (0), continue to the next iteration if matrix[i][j] == 0 { continue } // Increment the size of the current river currentRiverSize++ // Get the unvisited neighboring nodes of the current node unvisitedNeighbors := getUnvisitedNeighbors(i, j, matrix, visited) // Add the unvisited neighbors to the nodesToExplore queue for _, neighbor := range unvisitedNeighbors { nodesToExplore = append(nodesToExplore, neighbor) } } // If the current river size is greater than 0, append it to the sizes slice if currentRiverSize > 0 { sizes = append(sizes, currentRiverSize) } return sizes } func getUnvisitedNeighbors(i, j int, matrix [][]int, visited [][]bool) [][]int { // Slice to store unvisited neighboring nodes unvisitedNeighbors := [][]int{} // Check the four neighboring cells (up, down, left, right) of the current cell // and add unvisited neighbors to the unvisitedNeighbors slice // Up neighbor if i > 0 && !visited[i-1][j] { unvisitedNeighbors = append(unvisitedNeighbors, []int{i - 1, j}) } // Down neighbor if i < len(matrix)-1 && !visited[i+1][j] { unvisitedNeighbors = append(unvisitedNeighbors, []int{i + 1, j}) } // Left neighbor if j > 0 && !visited[i][j-1] { unvisitedNeighbors = append(unvisitedNeighbors, []int{i, j - 1}) } // Right neighbor if j < len(matrix[0])-1 && !visited[i][j+1] { unvisitedNeighbors = append(unvisitedNeighbors, []int{i, j + 1}) } return unvisitedNeighbors } ================================================ FILE: Graphs/river_sizes.java ================================================ /* You're given a two-dimensional array (a matrix) of potentially unequal height and width containing only 0's and 1's. Each 0 represent land and each 1 represent part of river. A river consists of any number of 1's that are either horizontally or vertically adjacent (but not diagonally adjacent). The number of adjacent 1's forming a river determine its size. Note that a river can twist. In other words, it doesn't have to be a straight vertical line or a straight horizontal line; it can be L-shaped, for example. Write a function that returns an array of the sizes of all rivers represented in the input matrix. The sizes don't need to be in any particular order. Sample Input:[ [1, 0, 0, 1, 0], [1, 0, 1, 0, 0], [0, 0, 1, 0, 1], [1, 0, 1, 0, 1], [1, 0, 1, 1, 0], ] Output: [1, 2, 2, 2, 5] Explanation: 1. The function `RiverSizes` initializes an empty slice `sizes` to store the sizes of rivers found in the matrix. It also creates a 2D `visited` matrix of the same size as the input matrix to keep track of visited nodes. 2. The function then iterates over each cell in the matrix using nested loops. 3. If a cell has already been visited (marked as `true` in the `visited` matrix), the code continues to the next iteration to avoid processing it again. 4. If a cell has not been visited, the code calls the `traverseNode` function to explore the river connected to that cell and updates the `sizes` slice with the size of the river. 5. The `traverseNode` function takes the starting position (i, j) of a river, the matrix, the `visited` matrix, and the `sizes` slice as input. 6. It initializes a variable `currentRiverSize` to 0 to keep track of the size of the river being explored. 7. It maintains a queue (`nodesToExplore`) to store the nodes that need to be visited. It starts with the initial node (i, j). 8. The function enters a loop that continues until there are no more nodes to explore in the queue. 9. In each iteration, it dequeues the first node from the `nodesToExplore` queue and updates the current position (i, j) accordingly. 10. If the current node has already been visited, the code continues to the next iteration. 11. If the current node contains a value of 0 (indicating land), the code continues to the next iteration, as it's not part of the river. 12. If the current node contains a value of 1 (indicating a river), it increments the `currentRiverSize` by 1. 13. It then retrieves the unvisited neighboring nodes of the current node using the `getUnvisitedNeighbors` function and adds them to the `nodesToExplore` queue. 14. Once the loop finishes, if the `currentRiverSize` is greater than 0, it appends it to the `sizes` slice. 15. Finally, the `sizes` slice containing the sizes of all rivers is returned. 16. The `getUnvisitedNeighbors` function takes the current position (i, j), the matrix, and the `visited` matrix as input. 17. It checks the four neighboring cells (up, down, left, right) of the current cell and adds the unvisited neighbors to the `unvisitedNeighbors` slice. 18. The function returns the `unvisitedNeighbors` slice. The code essentially performs a depth-first search (DFS) traversal on the matrix to find connected regions of 1s (rivers) and keeps track of their sizes. The `visited` matrix helps avoid revisiting nodes that have already been processed, ensuring each river is counted only once. */ import java.util.ArrayList; import java.util.List; public class RiverSizes { public static List riverSizes(int[][] matrix) { List sizes = new ArrayList<>(); int numRows = matrix.length; int numCols = matrix[0].length; boolean[][] visited = new boolean[numRows][numCols]; // Iterate over each cell in the matrix for (int i = 0; i < numRows; i++) { for (int j = 0; j < numCols; j++) { // If the cell has already been visited, continue to the next iteration if (visited[i][j]) { continue; } // Explore the river connected to the current cell and update sizes sizes = traverseNode(i, j, matrix, visited, sizes); } } return sizes; } private static List traverseNode(int i, int j, int[][] matrix, boolean[][] visited, List sizes) { int currentRiverSize = 0; List nodesToExplore = new ArrayList<>(); nodesToExplore.add(new int[]{i, j}); while (!nodesToExplore.isEmpty()) { int[] currentNode = nodesToExplore.remove(0); i = currentNode[0]; j = currentNode[1]; // If the current node has already been visited, continue to the next iteration if (visited[i][j]) { continue; } // Mark the current node as visited visited[i][j] = true; // If the current node is land (0), continue to the next iteration if (matrix[i][j] == 0) { continue; } // Increment the size of the current river currentRiverSize++; // Get the unvisited neighboring nodes of the current node List unvisitedNeighbors = getUnvisitedNeighbors(i, j, matrix, visited); // Add the unvisited neighbors to the nodesToExplore list nodesToExplore.addAll(unvisitedNeighbors); } // If the current river size is greater than 0, add it to the sizes list if (currentRiverSize > 0) { sizes.add(currentRiverSize); } return sizes; } private static List getUnvisitedNeighbors(int i, int j, int[][] matrix, boolean[][] visited) { List unvisitedNeighbors = new ArrayList<>(); // Check the four neighboring cells (up, down, left, right) of the current cell // and add unvisited neighbors to the unvisitedNeighbors list // Up neighbor if (i > 0 && !visited[i - 1][j]) { unvisitedNeighbors.add(new int[]{i - 1, j}); } // Down neighbor if (i < matrix.length - 1 && !visited[i + 1][j]) { unvisitedNeighbors.add(new int[]{i + 1, j}); } // Left neighbor if (j > 0 && !visited[i][j - 1]) { unvisitedNeighbors.add(new int[]{i, j - 1}); } // Right neighbor if (j < matrix[0].length - 1 && !visited[i][j + 1]) { unvisitedNeighbors.add(new int[]{i, j + 1}); } return unvisitedNeighbors; } public static void main(String[] args) { int[][] matrix = { {1, 0, 0, 1, 0}, {1, 0, 1, 0, 0}, {0, 0, 1, 0, 1}, {1, 0, 1, 0, 1}, {1, 0, 1, 1, 0} }; List sizes = riverSizes(matrix); // Printing the sizes of rivers for (int size : sizes) { System.out.print(size + " "); } } } ================================================ FILE: Graphs/river_sizes.js ================================================ /* You're given a two-dimensional array (a matrix) of potentially unequal height and width containing only 0's and 1's. Each 0 represent land and each 1 represent part of river. A river consists of any number of 1's that are either horizontally or vertically adjacent (but not diagonally adjacent). The number of adjacent 1's forming a river determine its size. Note that a river can twist. In other words, it doesn't have to be a straight vertical line or a straight horizontal line; it can be L-shaped, for example. Write a function that returns an array of the sizes of all rivers represented in the input matrix. The sizes don't need to be in any particular order. Sample Input:[ [1, 0, 0, 1, 0], [1, 0, 1, 0, 0], [0, 0, 1, 0, 1], [1, 0, 1, 0, 1], [1, 0, 1, 1, 0], ] Output: [1, 2, 2, 2, 5] Explanation: 1. The function `RiverSizes` initializes an empty slice `sizes` to store the sizes of rivers found in the matrix. It also creates a 2D `visited` matrix of the same size as the input matrix to keep track of visited nodes. 2. The function then iterates over each cell in the matrix using nested loops. 3. If a cell has already been visited (marked as `true` in the `visited` matrix), the code continues to the next iteration to avoid processing it again. 4. If a cell has not been visited, the code calls the `traverseNode` function to explore the river connected to that cell and updates the `sizes` slice with the size of the river. 5. The `traverseNode` function takes the starting position (i, j) of a river, the matrix, the `visited` matrix, and the `sizes` slice as input. 6. It initializes a variable `currentRiverSize` to 0 to keep track of the size of the river being explored. 7. It maintains a queue (`nodesToExplore`) to store the nodes that need to be visited. It starts with the initial node (i, j). 8. The function enters a loop that continues until there are no more nodes to explore in the queue. 9. In each iteration, it dequeues the first node from the `nodesToExplore` queue and updates the current position (i, j) accordingly. 10. If the current node has already been visited, the code continues to the next iteration. 11. If the current node contains a value of 0 (indicating land), the code continues to the next iteration, as it's not part of the river. 12. If the current node contains a value of 1 (indicating a river), it increments the `currentRiverSize` by 1. 13. It then retrieves the unvisited neighboring nodes of the current node using the `getUnvisitedNeighbors` function and adds them to the `nodesToExplore` queue. 14. Once the loop finishes, if the `currentRiverSize` is greater than 0, it appends it to the `sizes` slice. 15. Finally, the `sizes` slice containing the sizes of all rivers is returned. 16. The `getUnvisitedNeighbors` function takes the current position (i, j), the matrix, and the `visited` matrix as input. 17. It checks the four neighboring cells (up, down, left, right) of the current cell and adds the unvisited neighbors to the `unvisitedNeighbors` slice. 18. The function returns the `unvisitedNeighbors` slice. The code essentially performs a depth-first search (DFS) traversal on the matrix to find connected regions of 1s (rivers) and keeps track of their sizes. The `visited` matrix helps avoid revisiting nodes that have already been processed, ensuring each river is counted only once. */ function riverSizes(matrix) { // Array to store the sizes of rivers const sizes = []; // Create a visited matrix to keep track of visited nodes const visited = Array.from({ length: matrix.length }, () => new Array(matrix[0].length).fill(false) ); // Iterate over each cell in the matrix for (let i = 0; i < matrix.length; i++) { for (let j = 0; j < matrix[i].length; j++) { // If the cell has already been visited, continue to the next iteration if (visited[i][j]) { continue; } // Explore the river connected to the current cell and update sizes sizes.push(traverseNode(i, j, matrix, visited)); } } return sizes; } function traverseNode(i, j, matrix, visited) { // Variable to track the size of the current river let currentRiverSize = 0; // Queue to store nodes that need to be visited const nodesToExplore = [[i, j]]; // Loop until there are no more nodes to explore while (nodesToExplore.length) { // Dequeue the first node from the queue and update the current position (i, j) const [currentI, currentJ] = nodesToExplore.shift(); // If the current node has already been visited, continue to the next iteration if (visited[currentI][currentJ]) { continue; } // Mark the current node as visited visited[currentI][currentJ] = true; // If the current node is land (0), continue to the next iteration if (matrix[currentI][currentJ] === 0) { continue; } // Increment the size of the current river currentRiverSize++; // Get the unvisited neighboring nodes of the current node const unvisitedNeighbors = getUnvisitedNeighbors( currentI, currentJ, matrix, visited ); // Add the unvisited neighbors to the nodesToExplore queue nodesToExplore.push(...unvisitedNeighbors); } return currentRiverSize; } function getUnvisitedNeighbors(i, j, matrix, visited) { // Array to store unvisited neighboring nodes const unvisitedNeighbors = []; // Check the four neighboring cells (up, down, left, right) of the current cell // and add unvisited neighbors to the unvisitedNeighbors array // Up neighbor if (i > 0 && !visited[i - 1][j]) { unvisitedNeighbors.push([i - 1, j]); } // Down neighbor if (i < matrix.length - 1 && !visited[i + 1][j]) { unvisitedNeighbors.push([i + 1, j]); } // Left neighbor if (j > 0 && !visited[i][j - 1]) { unvisitedNeighbors.push([i, j - 1]); } // Right neighbor if (j < matrix[0].length - 1 && !visited[i][j + 1]) { unvisitedNeighbors.push([i, j + 1]); } return unvisitedNeighbors; } // Test the riverSizes function const matrix = [ [1, 0, 0, 1, 0], [1, 0, 1, 0, 0], [0, 0, 1, 0, 1], [1, 0, 1, 0, 1], [1, 0, 1, 1, 0], ]; const sizes = riverSizes(matrix); // Print the sizes of the rivers console.log(sizes); ================================================ FILE: Graphs/river_sizes.py ================================================ ''' You're given a two-dimensional array (a matrix) of potentially unequal height and width containing only 0's and 1's. Each 0 represent land and each 1 represent part of river. A river consists of any number of 1's that are either horizontally or vertically adjacent (but not diagonally adjacent). The number of adjacent 1's forming a river determine its size. Note that a river can twist. In other words, it doesn't have to be a straight vertical line or a straight horizontal line; it can be L-shaped, for example. Write a function that returns an array of the sizes of all rivers represented in the input matrix. The sizes don't need to be in any particular order. Sample Input:[ [1, 0, 0, 1, 0], [1, 0, 1, 0, 0], [0, 0, 1, 0, 1], [1, 0, 1, 0, 1], [1, 0, 1, 1, 0], ] Output: [1, 2, 2, 2, 5] Explanation: 1. The function `RiverSizes` initializes an empty slice `sizes` to store the sizes of rivers found in the matrix. It also creates a 2D `visited` matrix of the same size as the input matrix to keep track of visited nodes. 2. The function then iterates over each cell in the matrix using nested loops. 3. If a cell has already been visited (marked as `true` in the `visited` matrix), the code continues to the next iteration to avoid processing it again. 4. If a cell has not been visited, the code calls the `traverseNode` function to explore the river connected to that cell and updates the `sizes` slice with the size of the river. 5. The `traverseNode` function takes the starting position (i, j) of a river, the matrix, the `visited` matrix, and the `sizes` slice as input. 6. It initializes a variable `currentRiverSize` to 0 to keep track of the size of the river being explored. 7. It maintains a queue (`nodesToExplore`) to store the nodes that need to be visited. It starts with the initial node (i, j). 8. The function enters a loop that continues until there are no more nodes to explore in the queue. 9. In each iteration, it dequeues the first node from the `nodesToExplore` queue and updates the current position (i, j) accordingly. 10. If the current node has already been visited, the code continues to the next iteration. 11. If the current node contains a value of 0 (indicating land), the code continues to the next iteration, as it's not part of the river. 12. If the current node contains a value of 1 (indicating a river), it increments the `currentRiverSize` by 1. 13. It then retrieves the unvisited neighboring nodes of the current node using the `getUnvisitedNeighbors` function and adds them to the `nodesToExplore` queue. 14. Once the loop finishes, if the `currentRiverSize` is greater than 0, it appends it to the `sizes` slice. 15. Finally, the `sizes` slice containing the sizes of all rivers is returned. 16. The `getUnvisitedNeighbors` function takes the current position (i, j), the matrix, and the `visited` matrix as input. 17. It checks the four neighboring cells (up, down, left, right) of the current cell and adds the unvisited neighbors to the `unvisitedNeighbors` slice. 18. The function returns the `unvisitedNeighbors` slice. The code essentially performs a depth-first search (DFS) traversal on the matrix to find connected regions of 1s (rivers) and keeps track of their sizes. The `visited` matrix helps avoid revisiting nodes that have already been processed, ensuring each river is counted only once. ''' def riverSizes(matrix): # List to store the sizes of rivers sizes = [] # Create a visited matrix to keep track of visited nodes visited = [[False for _ in range(len(matrix[0]))] for _ in range(len(matrix))] # Iterate over each cell in the matrix for i in range(len(matrix)): for j in range(len(matrix[i])): # If the cell has already been visited, continue to the next iteration if visited[i][j]: continue # Explore the river connected to the current cell and update sizes sizes = traverseNode(i, j, matrix, visited, sizes) return sizes def traverseNode(i, j, matrix, visited, sizes): # Variable to track the size of the current river currentRiverSize = 0 # Queue to store nodes that need to be visited nodesToExplore = [[i, j]] # Loop until there are no more nodes to explore while nodesToExplore: # Dequeue the first node from the queue and update the current position (i, j) currentNode = nodesToExplore.pop(0) i, j = currentNode[0], currentNode[1] # If the current node has already been visited, continue to the next iteration if visited[i][j]: continue # Mark the current node as visited visited[i][j] = True # If the current node is land (0), continue to the next iteration if matrix[i][j] == 0: continue # Increment the size of the current river currentRiverSize += 1 # Get the unvisited neighboring nodes of the current node unvisitedNeighbors = getUnvisitedNeighbors(i, j, matrix, visited) # Add the unvisited neighbors to the nodesToExplore queue nodesToExplore.extend(unvisitedNeighbors) # If the current river size is greater than 0, append it to the sizes list if currentRiverSize > 0: sizes.append(currentRiverSize) return sizes def getUnvisitedNeighbors(i, j, matrix, visited): # List to store unvisited neighboring nodes unvisitedNeighbors = [] # Check the four neighboring cells (up, down, left, right) of the current cell # and add unvisited neighbors to the unvisitedNeighbors list # Up neighbor if i > 0 and not visited[i - 1][j]: unvisitedNeighbors.append([i - 1, j]) # Down neighbor if i < len(matrix) - 1 and not visited[i + 1][j]: unvisitedNeighbors.append([i + 1, j]) # Left neighbor if j > 0 and not visited[i][j - 1]: unvisitedNeighbors.append([i, j - 1]) # Right neighbor if j < len(matrix[0]) - 1 and not visited[i][j + 1]: unvisitedNeighbors.append([i, j + 1]) return unvisitedNeighbors ================================================ FILE: Graphs/single_cycle_check.cpp ================================================ /* You're given an array of integers where each integer represents a jump of its value in the array. For instance, the integer 2 represents a jump of two indices forward in the array; the integer -3 represents a jump of three indices backward in the array. If a jump spills past the array's bounds, it wraps over to the other side. For instance, a jump of -1 at index 0 brings us to the last index in the array. Similarly, a jump of 1 at the last index in the array brings us to index 0 Write a function that returns a boolean representing whether the jumps in the array form a single cycle. A single cycle occurs if, starting at any index in the array and following the jumps, every element in the array is visited exactly once before landing back on the starting index. Sample Input: [2, 3, 1, -4, -4, 2] Output: True Explanation: The HasSingleCycle function takes an integer array as input and returns a boolean value indicating whether the array has a single cycle. It initializes two variables: nextElementVisited to keep track of the number of elements visited, and currIdx to track the current index in the array. The function enters a loop that continues until all elements in the array are visited. It checks if nextElementVisited is greater than 0 (indicating that at least one element has been visited) and currIdx is 0. If this condition is true, it means the loop has returned to the starting index prematurely, indicating multiple cycles. In such a case, the function returns false. Inside the loop, nextElementVisited is incremented by 1, and the currIdx is updated using the getNextIdx function, which we will examine next. The getNextIdx function takes an integer array and the current index as input and returns the index of the next element in the cycle. It first retrieves the jump value from the current index in the array. The jump value represents the number of steps to take from the current element. The nextIdx is calculated by adding the jump value to the current index and taking the modulo % operator with the length of the array. This ensures that the index stays within the valid range of the array. Finally, there is a check to ensure that nextIdx is non-negative. If it is negative, it means the index has wrapped around to the beginning of the array. In such a case, the function adds the length of the array to nextIdx to correctly calculate the next index. Once the loop in the HasSingleCycle function completes, the final check is made to ensure that the currIdx is back at the starting index (0). If it is, it means all elements have been visited in a single cycle, and the function returns true. Otherwise, it returns false. Overall, this code snippet implements the logic to determine whether an array has a single cycle by tracking the indices and jumps between elements. Time Complexity : O(n) where n is the length of the input array Space Complexity : O(1) */ #include #include using namespace std; // Function to check if the given array has a single cycle bool hasSingleCycle(vector& array) { int nextElementVisited = 0; int currIdx = 0; while (nextElementVisited < array.size()) { // Check if more than one element has been visited and current index is back to the starting index (0) if (nextElementVisited > 0 && currIdx == 0) { return false; // Multiple cycles detected, return false } nextElementVisited++; // Increment the count of visited elements currIdx = getNextIdx(array, currIdx); // Get the index of the next element } return currIdx == 0; // Return true if all elements have been visited in a single cycle } // Function to get the index of the next element in the cycle int getNextIdx(vector& array, int currIdx) { int jump = array[currIdx]; // Get the jump value from the current index int nextIdx = (currIdx + jump) % array.size(); // Calculate the index of the next element if (nextIdx >= 0) { return nextIdx; // Return the next index if it is non-negative } return nextIdx + array.size(); // Adjust the next index if it is negative (wrapped around to the beginning) } int main() { // Test cases vector array1 = {2, 3, 1, -4, -4, 2}; cout << "Array 1: " << (hasSingleCycle(array1) ? "true" : "false") << endl; vector array2 = {2, 2, -1}; cout << "Array 2: " << (hasSingleCycle(array2) ? "true" : "false") << endl; return 0; } ================================================ FILE: Graphs/single_cycle_check.go ================================================ /* You're given an array of integers where each integer represents a jump of its value in the array. For instance, the integer 2 represents a jump of two indices forward in the array; the integer -3 represents a jump of three indices backward in the array. If a jump spills past the array's bounds, it wraps over to the other side. For instance, a jump of -1 at index 0 brings us to the last index in the array. Similarly, a jump of 1 at the last index in the array brings us to index 0 Write a function that returns a boolean representing whether the jumps in the array form a single cycle. A single cycle occurs if, starting at any index in the array and following the jumps, every element in the array is visited exactly once before landing back on the starting index. Sample Input: [2, 3, 1, -4, -4, 2] Output: True Explanation: The HasSingleCycle function takes an integer array as input and returns a boolean value indicating whether the array has a single cycle. It initializes two variables: nextElementVisited to keep track of the number of elements visited, and currIdx to track the current index in the array. The function enters a loop that continues until all elements in the array are visited. It checks if nextElementVisited is greater than 0 (indicating that at least one element has been visited) and currIdx is 0. If this condition is true, it means the loop has returned to the starting index prematurely, indicating multiple cycles. In such a case, the function returns false. Inside the loop, nextElementVisited is incremented by 1, and the currIdx is updated using the getNextIdx function, which we will examine next. The getNextIdx function takes an integer array and the current index as input and returns the index of the next element in the cycle. It first retrieves the jump value from the current index in the array. The jump value represents the number of steps to take from the current element. The nextIdx is calculated by adding the jump value to the current index and taking the modulo % operator with the length of the array. This ensures that the index stays within the valid range of the array. Finally, there is a check to ensure that nextIdx is non-negative. If it is negative, it means the index has wrapped around to the beginning of the array. In such a case, the function adds the length of the array to nextIdx to correctly calculate the next index. Once the loop in the HasSingleCycle function completes, the final check is made to ensure that the currIdx is back at the starting index (0). If it is, it means all elements have been visited in a single cycle, and the function returns true. Otherwise, it returns false. Overall, this code snippet implements the logic to determine whether an array has a single cycle by tracking the indices and jumps between elements. Time Complexity : O(n) where n is the length of the input array Space Complexity : O(1) */ package main import "fmt" func HasSingleCycle(array []int) bool { nextElementVisited := 0 currIdx := 0 for nextElementVisited < len(array) { // Check if more than one element has been visited and current index is back to the starting index (0) if nextElementVisited > 0 && currIdx == 0 { return false // Multiple cycles detected, return false } nextElementVisited += 1 // Increment the count of visited elements currIdx = getNextIdx(array, currIdx) // Get the index of the next element } return currIdx == 0 // Return true if all elements have been visited in a single cycle } func getNextIdx(array []int, currIdx int) int { jump := array[currIdx] // Get the jump value from the current index nextIdx := (currIdx + jump) % len(array) // Calculate the index of the next element if nextIdx >= 0 { return nextIdx // Return the next index if it is non-negative } return nextIdx + len(array) // Adjust the next index if it is negative (wrapped around to the beginning) } func main() { // Test cases array1 := []int{2, 3, 1, -4, -4, 2} // has a single cycle fmt.Println("Array 1:", HasSingleCycle(array1)) // Output: true array2 := []int{2, 2, -1} // does have a single cycle fmt.Println("Array 2:", HasSingleCycle(array2)) // Output: true } ================================================ FILE: Graphs/single_cycle_check.java ================================================ /* You're given an array of integers where each integer represents a jump of its value in the array. For instance, the integer 2 represents a jump of two indices forward in the array; the integer -3 represents a jump of three indices backward in the array. If a jump spills past the array's bounds, it wraps over to the other side. For instance, a jump of -1 at index 0 brings us to the last index in the array. Similarly, a jump of 1 at the last index in the array brings us to index 0 Write a function that returns a boolean representing whether the jumps in the array form a single cycle. A single cycle occurs if, starting at any index in the array and following the jumps, every element in the array is visited exactly once before landing back on the starting index. Sample Input: [2, 3, 1, -4, -4, 2] Output: True Explanation: The HasSingleCycle function takes an integer array as input and returns a boolean value indicating whether the array has a single cycle. It initializes two variables: nextElementVisited to keep track of the number of elements visited, and currIdx to track the current index in the array. The function enters a loop that continues until all elements in the array are visited. It checks if nextElementVisited is greater than 0 (indicating that at least one element has been visited) and currIdx is 0. If this condition is true, it means the loop has returned to the starting index prematurely, indicating multiple cycles. In such a case, the function returns false. Inside the loop, nextElementVisited is incremented by 1, and the currIdx is updated using the getNextIdx function, which we will examine next. The getNextIdx function takes an integer array and the current index as input and returns the index of the next element in the cycle. It first retrieves the jump value from the current index in the array. The jump value represents the number of steps to take from the current element. The nextIdx is calculated by adding the jump value to the current index and taking the modulo % operator with the length of the array. This ensures that the index stays within the valid range of the array. Finally, there is a check to ensure that nextIdx is non-negative. If it is negative, it means the index has wrapped around to the beginning of the array. In such a case, the function adds the length of the array to nextIdx to correctly calculate the next index. Once the loop in the HasSingleCycle function completes, the final check is made to ensure that the currIdx is back at the starting index (0). If it is, it means all elements have been visited in a single cycle, and the function returns true. Otherwise, it returns false. Overall, this code snippet implements the logic to determine whether an array has a single cycle by tracking the indices and jumps between elements. Time Complexity : O(n) where n is the length of the input array Space Complexity : O(1) */ import java.util.*; public class Main { // Function to check if the given array has a single cycle public static boolean hasSingleCycle(int[] array) { int nextElementVisited = 0; int currIdx = 0; while (nextElementVisited < array.length) { // Check if more than one element has been visited and current index is back to the starting index (0) if (nextElementVisited > 0 && currIdx == 0) { return false; // Multiple cycles detected, return false } nextElementVisited++; // Increment the count of visited elements currIdx = getNextIdx(array, currIdx); // Get the index of the next element } return currIdx == 0; // Return true if all elements have been visited in a single cycle } // Function to get the index of the next element in the cycle public static int getNextIdx(int[] array, int currIdx) { int jump = array[currIdx]; // Get the jump value from the current index int nextIdx = (currIdx + jump) % array.length; // Calculate the index of the next element if (nextIdx >= 0) { return nextIdx; // Return the next index if it is non-negative } return nextIdx + array.length; // Adjust the next index if it is negative (wrapped around to the beginning) } public static void main(String[] args) { // Test cases int[] array1 = {2, 3, 1, -4, -4, 2}; System.out.println("Array 1: " + (hasSingleCycle(array1) ? "true" : "false")); int[] array2 = {2, 2, -1}; System.out.println("Array 2: " + (hasSingleCycle(array2) ? "true" : "false")); } } ================================================ FILE: Graphs/single_cycle_check.js ================================================ /* You're given an array of integers where each integer represents a jump of its value in the array. For instance, the integer 2 represents a jump of two indices forward in the array; the integer -3 represents a jump of three indices backward in the array. If a jump spills past the array's bounds, it wraps over to the other side. For instance, a jump of -1 at index 0 brings us to the last index in the array. Similarly, a jump of 1 at the last index in the array brings us to index 0 Write a function that returns a boolean representing whether the jumps in the array form a single cycle. A single cycle occurs if, starting at any index in the array and following the jumps, every element in the array is visited exactly once before landing back on the starting index. Sample Input: [2, 3, 1, -4, -4, 2] Output: True Explanation: The HasSingleCycle function takes an integer array as input and returns a boolean value indicating whether the array has a single cycle. It initializes two variables: nextElementVisited to keep track of the number of elements visited, and currIdx to track the current index in the array. The function enters a loop that continues until all elements in the array are visited. It checks if nextElementVisited is greater than 0 (indicating that at least one element has been visited) and currIdx is 0. If this condition is true, it means the loop has returned to the starting index prematurely, indicating multiple cycles. In such a case, the function returns false. Inside the loop, nextElementVisited is incremented by 1, and the currIdx is updated using the getNextIdx function, which we will examine next. The getNextIdx function takes an integer array and the current index as input and returns the index of the next element in the cycle. It first retrieves the jump value from the current index in the array. The jump value represents the number of steps to take from the current element. The nextIdx is calculated by adding the jump value to the current index and taking the modulo % operator with the length of the array. This ensures that the index stays within the valid range of the array. Finally, there is a check to ensure that nextIdx is non-negative. If it is negative, it means the index has wrapped around to the beginning of the array. In such a case, the function adds the length of the array to nextIdx to correctly calculate the next index. Once the loop in the HasSingleCycle function completes, the final check is made to ensure that the currIdx is back at the starting index (0). If it is, it means all elements have been visited in a single cycle, and the function returns true. Otherwise, it returns false. Overall, this code snippet implements the logic to determine whether an array has a single cycle by tracking the indices and jumps between elements. Time Complexity : O(n) where n is the length of the input array Space Complexity : O(1) */ function hasSingleCycle(array) { let nextElementVisited = 0; let currIdx = 0; while (nextElementVisited < array.length) { // Check if more than one element has been visited and current index is back to the starting index (0) if (nextElementVisited > 0 && currIdx === 0) { return false; // Multiple cycles detected, return false } nextElementVisited += 1; // Increment the count of visited elements currIdx = getNextIdx(array, currIdx); // Get the index of the next element } return currIdx === 0; // Return true if all elements have been visited in a single cycle } function getNextIdx(array, currIdx) { const jump = array[currIdx]; // Get the jump value from the current index const nextIdx = (currIdx + jump) % array.length; // Calculate the index of the next element if (nextIdx >= 0) { return nextIdx; // Return the next index if it is non-negative } return nextIdx + array.length; // Adjust the next index if it is negative (wrapped around to the beginning) } // Test cases const array1 = [2, 3, 1, -4, -4, 2]; console.log("Array 1:", hasSingleCycle(array1)); const array2 = [2, 2, -1]; console.log("Array 2:", hasSingleCycle(array2)); ================================================ FILE: Graphs/single_cycle_check.py ================================================ ''' You're given an array of integers where each integer represents a jump of its value in the array. For instance, the integer 2 represents a jump of two indices forward in the array; the integer -3 represents a jump of three indices backward in the array. If a jump spills past the array's bounds, it wraps over to the other side. For instance, a jump of -1 at index 0 brings us to the last index in the array. Similarly, a jump of 1 at the last index in the array brings us to index 0 Write a function that returns a boolean representing whether the jumps in the array form a single cycle. A single cycle occurs if, starting at any index in the array and following the jumps, every element in the array is visited exactly once before landing back on the starting index. Sample Input: [2, 3, 1, -4, -4, 2] Output: True Explanation: The HasSingleCycle function takes an integer array as input and returns a boolean value indicating whether the array has a single cycle. It initializes two variables: nextElementVisited to keep track of the number of elements visited, and currIdx to track the current index in the array. The function enters a loop that continues until all elements in the array are visited. It checks if nextElementVisited is greater than 0 (indicating that at least one element has been visited) and currIdx is 0. If this condition is true, it means the loop has returned to the starting index prematurely, indicating multiple cycles. In such a case, the function returns false. Inside the loop, nextElementVisited is incremented by 1, and the currIdx is updated using the getNextIdx function, which we will examine next. The getNextIdx function takes an integer array and the current index as input and returns the index of the next element in the cycle. It first retrieves the jump value from the current index in the array. The jump value represents the number of steps to take from the current element. The nextIdx is calculated by adding the jump value to the current index and taking the modulo % operator with the length of the array. This ensures that the index stays within the valid range of the array. Finally, there is a check to ensure that nextIdx is non-negative. If it is negative, it means the index has wrapped around to the beginning of the array. In such a case, the function adds the length of the array to nextIdx to correctly calculate the next index. Once the loop in the HasSingleCycle function completes, the final check is made to ensure that the currIdx is back at the starting index (0). If it is, it means all elements have been visited in a single cycle, and the function returns true. Otherwise, it returns false. Overall, this code snippet implements the logic to determine whether an array has a single cycle by tracking the indices and jumps between elements. Time Complexity : O(n) where n is the length of the input array Space Complexity : O(1) ''' def has_single_cycle(array): next_element_visited = 0 curr_idx = 0 while next_element_visited < len(array): # Check if more than one element has been visited and current index is back to the starting index (0) if next_element_visited > 0 and curr_idx == 0: return False # Multiple cycles detected, return False next_element_visited += 1 # Increment the count of visited elements curr_idx = get_next_idx(array, curr_idx) # Get the index of the next element return curr_idx == 0 # Return True if all elements have been visited in a single cycle def get_next_idx(array, curr_idx): jump = array[curr_idx] # Get the jump value from the current index next_idx = (curr_idx + jump) % len(array) # Calculate the index of the next element if next_idx >= 0: return next_idx # Return the next index if it is non-negative return next_idx + len(array) # Adjust the next index if it is negative (wrapped around to the beginning) # Test cases array1 = [2, 3, 1, -4, -4, 2] print("Array 1:", has_single_cycle(array1)) array2 = [2, 2, -1] print("Array 2:", has_single_cycle(array2)) ================================================ FILE: Graphs/snack_ladders.cpp ================================================ /* You are given an n x n integer matrix board where the cells are labeled from 1 to n2 in a Boustrophedon style starting from the bottom left of the board (i.e. board[n - 1][0]) and alternating direction each row. You start on square 1 of the board. In each move, starting from square curr, do the following: Choose a destination square next with a label in the range [curr + 1, min(curr + 6, n2)]. This choice simulates the result of a standard 6-sided die roll: i.e., there are always at most 6 destinations, regardless of the size of the board. If next has a snake or ladder, you must move to the destination of that snake or ladder. Otherwise, you move to next. The game ends when you reach the square n2. A board square on row r and column c has a snake or ladder if board[r][c] != -1. The destination of that snake or ladder is board[r][c]. Squares 1 and n2 do not have a snake or ladder. Note that you only take a snake or ladder at most once per move. If the destination to a snake or ladder is the start of another snake or ladder, you do not follow the subsequent snake or ladder. For example, suppose the board is [[-1,4],[-1,3]], and on the first move, your destination square is 2. You follow the ladder to square 3, but do not follow the subsequent ladder to 4. Return the least number of moves required to reach the square n2. If it is not possible to reach the square, return -1. Example 1: Input: board = [[-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1],[-1,35,-1,-1,13,-1],[-1,-1,-1,-1,-1,-1],[-1,15,-1,-1,-1,-1]] Output: 4 Explanation: In the beginning, you start at square 1 (at row 5, column 0). You decide to move to square 2 and must take the ladder to square 15. You then decide to move to square 17 and must take the snake to square 13. You then decide to move to square 14 and must take the ladder to square 35. You then decide to move to square 36, ending the game. This is the lowest possible number of moves to reach the last square, so return 4. Example 2: Input: board = [[-1,-1],[-1,3]] Output: 1 Constraints: n == board.length == board[i].length 2 <= n <= 20 board[i][j] is either -1 or in the range [1, n2]. The squares labeled 1 and n2 do not have any ladders or snakes. */ #include using namespace std; template class Graph{ map > adjList; public: Graph(){ } void addEdge(T u, T v, bool bidir = true){ adjList[u].push_back(v); if(bidir){ adjList[v].push_back(u); } } void printAdjList(){ for(auto obj : adjList){ cout << obj.first << "->"; for(auto element : obj.second){ cout << element << ","; } cout << endl; } } int bfs_sssp(int n, int m){ queue q; map dist; map parent; for(auto i : adjList){ dist[i.first] = INT_MAX; } q.push(n); dist[n] = 0; parent[n] = n; while(!q.empty()){ int node_element = q.front(); q.pop(); for(int neighbour : adjList[node_element]){ if(dist[neighbour] == INT_MAX){ q.push(neighbour); dist[neighbour] = dist[node_element] + 1; parent[neighbour] = node_element; } } } cout << endl; int temp = m; while(temp != n){ cout << temp << "-->"; temp = parent[temp]; } cout << n << endl; /* for(auto i : adjList){ int node = i.first; cout << "Distance of " << node << " is " << dist[node] << endl; } */ return dist[m]; } }; int main(){ Graph g; int board[50] = {0}; board[2] = 13; board[5] = 2; board[9] = 18; board[18] = 11; board[17] = -13; board[20] = -14; board[24] = -8; board[25] = -10; board[32] = -2; board[34] = -22; for(int u = 0; u <= 36; u++){ for(int dice = 1; dice <= 6; dice++){ int v = u + dice + board[u + dice]; g.addEdge(u, v, false); } } cout << "The shortest distance is " << g.bfs_sssp(1, 36) << endl; return 0; } ================================================ FILE: Graphs/snack_ladders.js ================================================ // Define the number of squares on the board const BOARD_SIZE = 100; // Define the start and end points of the snakes and ladders const snakesAndLadders = { 14: 4, 19: 8, 22: 20, 41: 38, 50: 39, 54: 34, 66: 53, 68: 63, 79: 67, 83: 72, 92: 88, 97: 76, 8: 15, 13: 23, 37: 43, 40: 48, 57: 63, 61: 69, 65: 72, 75: 83, 78: 87, 80: 92, 88: 94 }; // Define a function to build the game graph function buildGraph() { const graph = {}; for (let i = 1; i <= BOARD_SIZE; i++) { graph[i] = []; for (let j = i + 1; j <= i + 6 && j <= BOARD_SIZE; j++) { if (snakesAndLadders[j]) { graph[i].push(snakesAndLadders[j]); } else { graph[i].push(j); } } } return graph; } // Define the main function of the game function playGame() { // Build the game graph const graph = buildGraph(); // Define the starting node and the goal node const startNode = 1; const goalNode = BOARD_SIZE; // Define a queue for BFS const queue = [startNode]; // Define a visited set for BFS const visited = new Set(); visited.add(startNode); // Define a map for BFS to keep track of the parent node const parentMap = new Map(); parentMap.set(startNode, null); // Loop until the goal node is found or the queue is empty while (queue.length > 0) { // Dequeue the next node from the queue const currentNode = queue.shift(); // If the goal node is found, reconstruct the path and return it if (currentNode === goalNode) { const path = []; let node = currentNode; while (node !== null) { path.unshift(node); node = parentMap.get(node); } return path; } // Loop through the neighbors of the current node for (const neighbor of graph[currentNode]) { // If the neighbor has not been visited, add it to the queue if (!visited.has(neighbor)) { queue.push(neighbor); visited.add(neighbor); parentMap.set(neighbor, currentNode); } } } // If the goal node is not found, return null return null; } // Play the game and print the result console.log(playGame()); ================================================ FILE: Graphs/snack_ladders.py ================================================ ''' You are given an n x n integer matrix board where the cells are labeled from 1 to n2 in a Boustrophedon style starting from the bottom left of the board (i.e. board[n - 1][0]) and alternating direction each row. You start on square 1 of the board. In each move, starting from square curr, do the following: Choose a destination square next with a label in the range [curr + 1, min(curr + 6, n2)]. This choice simulates the result of a standard 6-sided die roll: i.e., there are always at most 6 destinations, regardless of the size of the board. If next has a snake or ladder, you must move to the destination of that snake or ladder. Otherwise, you move to next. The game ends when you reach the square n2. A board square on row r and column c has a snake or ladder if board[r][c] != -1. The destination of that snake or ladder is board[r][c]. Squares 1 and n2 do not have a snake or ladder. Note that you only take a snake or ladder at most once per move. If the destination to a snake or ladder is the start of another snake or ladder, you do not follow the subsequent snake or ladder. For example, suppose the board is [[-1,4],[-1,3]], and on the first move, your destination square is 2. You follow the ladder to square 3, but do not follow the subsequent ladder to 4. Return the least number of moves required to reach the square n2. If it is not possible to reach the square, return -1. Example 1: Input: board = [[-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1],[-1,35,-1,-1,13,-1],[-1,-1,-1,-1,-1,-1],[-1,15,-1,-1,-1,-1]] Output: 4 Explanation: In the beginning, you start at square 1 (at row 5, column 0). You decide to move to square 2 and must take the ladder to square 15. You then decide to move to square 17 and must take the snake to square 13. You then decide to move to square 14 and must take the ladder to square 35. You then decide to move to square 36, ending the game. This is the lowest possible number of moves to reach the last square, so return 4. Example 2: Input: board = [[-1,-1],[-1,3]] Output: 1 Constraints: n == board.length == board[i].length 2 <= n <= 20 board[i][j] is either -1 or in the range [1, n2]. The squares labeled 1 and n2 do not have any ladders or snakes. ''' from collections import deque def snakesAndLadders(board): n = len(board) target = n*n moves = {1: 0} queue = deque([1]) while queue: curr = queue.popleft() if curr == target: return moves[curr] for i in range(1, 7): next = curr + i if next > target: break row, col = getRowCol(next, n) if board[row][col] != -1: next = board[row][col] if next not in moves: moves[next] = moves[curr] + 1 queue.append(next) return -1 def getRowCol(idx, n): row = (idx - 1) // n col = (idx - 1) % n if row % 2 == 1: col = n - col - 1 row = n - row - 1 return row, col ================================================ FILE: Graphs/snakes_ladders.java ================================================ /*You are given an n x n integer matrix board where the cells are labeled from 1 to n2 in a Boustrophedon style starting from the bottom left of the board (i.e. board[n - 1][0]) and alternating direction each row. You start on square 1 of the board. In each move, starting from square curr, do the following: Choose a destination square next with a label in the range [curr + 1, min(curr + 6, n2)]. This choice simulates the result of a standard 6-sided die roll: i.e., there are always at most 6 destinations, regardless of the size of the board. If next has a snake or ladder, you must move to the destination of that snake or ladder. Otherwise, you move to next. The game ends when you reach the square n2. A board square on row r and column c has a snake or ladder if board[r][c] != -1. The destination of that snake or ladder is board[r][c]. Squares 1 and n2 do not have a snake or ladder. Note that you only take a snake or ladder at most once per move. If the destination to a snake or ladder is the start of another snake or ladder, you do not follow the subsequent snake or ladder. For example, suppose the board is [[-1,4],[-1,3]], and on the first move, your destination square is 2. You follow the ladder to square 3, but do not follow the subsequent ladder to 4. Return the least number of moves required to reach the square n2. If it is not possible to reach the square, return -1. Explanation: We start by importing the necessary classes and defining the class SnakesAndLadders. We declare the constant BOARD_SIZE to represent the number of cells on the board. We declare two maps: snakes to store the snake positions, and ladders to store the ladder positions. The SnakesAndLadders class has a constructor that initializes the snakes and ladders maps. The addSnake method is used to add a snake to the game by providing the start and end positions. The addLadder method is used to add a ladder to the game by providing the start and end positions. The playGame method simulates the game. It starts with the player's position at 0 and the number of dice rolls at 0. Inside the while loop, a dice is rolled using the rollDice method, and the player's position is updated. After each move, we check if the player has landed on a ladder or a snake. If so, we update the player's position accordingly. The rollDice method generates a random number between 1 and 6, simulating a dice roll. In the main method, we create an instance of SnakesAndLadders and add snakes and ladders to the game. Finally, we call the playGame method to start the game and print the number of dice rolls required to reach or exceed the BOARD_SIZE. Below is an implementation of the Snakes and Ladders game in Java, along with comments:*/ import java.util.HashMap; import java.util.Map; public class SnakesAndLadders { private static final int BOARD_SIZE = 100; // Number of cells in the board private Map snakes; // Map to store snake positions private Map ladders; // Map to store ladder positions public SnakesAndLadders() { // Initialize the snake and ladder positions snakes = new HashMap<>(); ladders = new HashMap<>(); } public void addSnake(int start, int end) { snakes.put(start, end); } public void addLadder(int start, int end) { ladders.put(start, end); } public int playGame() { int currentPlayerPosition = 0; // Player's current position int diceRolls = 0; // Number of dice rolls while (currentPlayerPosition < BOARD_SIZE) { // Roll a dice int dice = rollDice(); // Move the player currentPlayerPosition += dice; // Check if the player has landed on a ladder if (ladders.containsKey(currentPlayerPosition)) { // Climb the ladder currentPlayerPosition = ladders.get(currentPlayerPosition); } // Check if the player has landed on a snake if (snakes.containsKey(currentPlayerPosition)) { // Go down the snake currentPlayerPosition = snakes.get(currentPlayerPosition); } diceRolls++; // Increment the number of dice rolls } return diceRolls; } private int rollDice() { // Generates a random number between 1 and 6 (inclusive) return (int) (Math.random() * 6) + 1; } public static void main(String[] args) { SnakesAndLadders game = new SnakesAndLadders(); // Add snakes to the game game.addSnake(16, 6); game.addSnake(47, 26); game.addSnake(49, 11); game.addSnake(56, 53); game.addSnake(62, 19); game.addSnake(64, 60); game.addSnake(87, 24); game.addSnake(93, 73); game.addSnake(95, 75); game.addSnake(98, 78); // Add ladders to the game game.addLadder(1, 38); game.addLadder(4, 14); game.addLadder(9, 31); game.addLadder(21, 42); game.addLadder(28, 84); game.addLadder(36, 44); game.addLadder(51, 67); game.addLadder(71, 91); game.addLadder(80, 100); // Play the game int diceRolls = game.playGame(); System.out.println("Number of dice rolls: " + diceRolls); } } Time Complexity: the average generalized time complexity of the code can be expressed as O(N), where N represents the average number of dice rolls required to complete the game over a large number of instances. Space Complexity: The average generalized space complexity of the code can be expressed as O(M), where M represents the average number of snakes and ladders add- -ed to the game. ================================================ FILE: Graphs/topological_sort.cpp ================================================ /* You're given a list of arbitrary jobs that need to be completed; these jobs are represented by distinct integers. You're also given a list of dependencies. A dependency is represented as a pair of jobs where the first job is a prerequisite of the second one. In other words, the second job depends on the first one; it can only be completed once the first job is completed. Write a function that takes in a list of jobs and a list of dependencies and returns a list containing a valid order in which the given jobs can be completed. If no such order exists, the function should return an empty array. Sample INput: jobs: [1, 2, 3, 4] deps: = [[1, 2], [1, 3], [3, 2], [4, 2], [4, 3]] Output: [1, 4, 3, 2] or [4, 1, 3, 2] Explanation: The provided code implements a topological sorting algorithm to find the order in which jobs can be executed given their dependencies. It uses a directed acyclic graph (DAG) representation to represent the jobs and their dependencies. Let's go through each part of the code in detail: 1. `Dep` struct: - This is a simple struct that represents a dependency between two jobs. - `Prereq` field indicates the prerequisite job. - `Job` field indicates the job that depends on the prerequisite. 2. `TopologicalSort` function: - This is the main function that performs the topological sorting of jobs. - It takes two input parameters: `jobs`, a list of job IDs, and `deps`, a list of dependencies. - It first creates a job graph using the `createJobGraph` function and then gets the ordered jobs using the `getOrderedJobs` function. 3. `createJobGraph` function: - This function creates a job graph from the list of jobs and dependencies. - It creates a new instance of the `JobGraph` struct and iterates through the dependencies. - For each dependency, it adds the prerequisite job to the dependent job's list of prerequisites in the job graph. 4. `getOrderedJobs` function: - This function performs a depth-first traversal of the job graph to get the ordered jobs. - It iterates through the nodes of the graph until all nodes have been visited. - For each node, it calls the `depthFirstTraverse` function to perform the depth-first traversal and get the ordered jobs. - If a cycle is detected in the graph during the traversal, it means that there is a circular dependency, and the function returns an empty list. 5. `depthFirstTraverse` function: - This function performs the depth-first traversal of the graph starting from a given node. - It checks if the current node has been visited (i.e., it is not part of any cycle). - If the node is currently being visited (marked as `Visiting`), it means that there is a cycle, and the function returns `true`. - Otherwise, it marks the node as `Visiting`, recursively traverses through its prerequisites, and marks it as `Visited` after the traversal. - It appends the job to the `orderedJobs` list in the correct order. 6. `JobGraph` struct: - This struct represents the job graph. - `Nodes` is a list of all the job nodes in the graph. - `Graph` is a map where the key is the job ID, and the value is a pointer to the corresponding `JobNode` in the graph. 7. `NewJobGraph` function: - This function creates a new instance of the `JobGraph` struct and initializes its `Graph` field with an empty map. - It also adds individual job nodes to the `Nodes` list. 8. `Addprereq`, `AddNode`, and `GetNode` functions: - These are helper functions to add prerequisites to a job, add a new job node to the graph, and get an existing job node, respectively. 9. `JobNode` struct: - This struct represents a node in the job graph. - `Job` is the job ID. - `Prereqs` is a list of pointers to other job nodes that are prerequisites for the current job. - `Visited` and `Visiting` are boolean flags used during the depth-first traversal to track the traversal status of each node. Overall, the code implements the topological sorting algorithm using depth-first traversal to find the order in which jobs can be executed without violating their dependencies. It efficiently handles circular dependencies and returns the ordered jobs or an empty list if a circular dependency is detected. O(j + d) time | O(j + d) space - where j is the number of jobs and d is the number of dependencies */ #include #include using namespace std; struct Dep { int Prereq; int Job; }; class JobGraph; // Function prototypes JobGraph createJobGraph(vector& jobs, vector& deps); vector getOrderedJobs(JobGraph& graph); bool depthFirstTraverse(JobGraph& graph, int jobId, vector& orderedJobs); class JobGraph { public: // Constructor to create a job graph from a list of jobs JobGraph(vector& jobs) { for (int job : jobs) { // Add each job as a node to the graph addNode(job); } } // Function to add a prerequisite to a job node void addPrereq(int job, int prereq) { JobNode* jobNode = getNode(job); JobNode* prereqNode = getNode(prereq); // Add the prerequisite node to the list of prerequisites for the job node jobNode->prereqs.push_back(prereqNode); } // Function to add a new job node to the graph void addNode(int job) { // Create a new job node and add it to the graph and nodes list graph[job] = new JobNode(job); nodes.push_back(graph[job]); } // Function to get a job node from the graph by its ID JobNode* getNode(int job) { // If the node does not exist in the graph, create a new node and add it to the graph if (graph.find(job) == graph.end()) { addNode(job); } return graph[job]; } // Data members vector nodes; // List of all job nodes in the graph unordered_map graph; // Map to store the job nodes by their IDs }; // Function to perform topological sorting and return the ordered jobs vector TopologicalSort(vector& jobs, vector& deps) { // Create a job graph from the list of jobs and dependencies JobGraph graph = createJobGraph(jobs, deps); // Get the ordered jobs using depth-first traversal return getOrderedJobs(graph); } // Function to create a job graph from a list of jobs and dependencies JobGraph createJobGraph(vector& jobs, vector& deps) { // Initialize an empty graph JobGraph graph(jobs); // Add each dependency as a prerequisite to the corresponding job node in the graph for (Dep dep : deps) { graph.addPrereq(dep.Job, dep.Prereq); } return graph; } // Function to perform depth-first traversal and get the ordered jobs vector getOrderedJobs(JobGraph& graph) { vector orderedJobs; vector& nodes = graph.nodes; // Continue the traversal until all nodes have been visited while (!nodes.empty()) { // Get the last node from the list of nodes and remove it from the list JobNode* node = nodes.back(); nodes.pop_back(); // Perform depth-first traversal starting from the current node if (!depthFirstTraverse(graph, node->job, orderedJobs)) { return {}; // If a cycle is detected during traversal, return an empty list } } return orderedJobs; } // Function to perform depth-first traversal starting from a specific job node bool depthFirstTraverse(JobGraph& graph, int jobId, vector& orderedJobs) { // Get the job node from the graph JobNode* node = graph.getNode(jobId); // If the node has been visited, it means it is not part of any cycle, so return false if (node->visited) { return false; } // If the node is currently being visited, it means there is a cycle, so return true if (node->visiting) { return true; } // Mark the node as currently being visited node->visiting = true; // Recursively traverse through each prerequisite of the current node for (JobNode* prereqNode : node->prereqs) { // If a cycle is detected during the traversal, return true if (depthFirstTraverse(graph, prereqNode->job, orderedJobs)) { return true; } } // Mark the node as visited and no longer being visited node->visited = true; node->visiting = false; // Append the job to the orderedJobs list in the correct order orderedJobs.push_back(node->job); // Return false, as no cycle was detected during the traversal return false; } // Definition of the JobNode class class JobNode { public: int job; // Job ID vector prereqs; // List of prerequisite job nodes bool visited; // Flag to mark if the node has been visited bool visiting; // Flag to mark if the node is currently being visited // Constructor to initialize a job node JobNode(int jobId) : job(jobId), visited(false), visiting(false) {} }; ================================================ FILE: Graphs/topological_sort.go ================================================ /* You're given a list of arbitrary jobs that need to be completed; these jobs are represented by distinct integers. You're also given a list of dependencies. A dependency is represented as a pair of jobs where the first job is a prerequisite of the second one. In other words, the second job depends on the first one; it can only be completed once the first job is completed. Write a function that takes in a list of jobs and a list of dependencies and returns a list containing a valid order in which the given jobs can be completed. If no such order exists, the function should return an empty array. Sample INput: jobs: [1, 2, 3, 4] deps: = [[1, 2], [1, 3], [3, 2], [4, 2], [4, 3]] Output: [1, 4, 3, 2] or [4, 1, 3, 2] Explanation: The provided code implements a topological sorting algorithm to find the order in which jobs can be executed given their dependencies. It uses a directed acyclic graph (DAG) representation to represent the jobs and their dependencies. Let's go through each part of the code in detail: 1. `Dep` struct: - This is a simple struct that represents a dependency between two jobs. - `Prereq` field indicates the prerequisite job. - `Job` field indicates the job that depends on the prerequisite. 2. `TopologicalSort` function: - This is the main function that performs the topological sorting of jobs. - It takes two input parameters: `jobs`, a list of job IDs, and `deps`, a list of dependencies. - It first creates a job graph using the `createJobGraph` function and then gets the ordered jobs using the `getOrderedJobs` function. 3. `createJobGraph` function: - This function creates a job graph from the list of jobs and dependencies. - It creates a new instance of the `JobGraph` struct and iterates through the dependencies. - For each dependency, it adds the prerequisite job to the dependent job's list of prerequisites in the job graph. 4. `getOrderedJobs` function: - This function performs a depth-first traversal of the job graph to get the ordered jobs. - It iterates through the nodes of the graph until all nodes have been visited. - For each node, it calls the `depthFirstTraverse` function to perform the depth-first traversal and get the ordered jobs. - If a cycle is detected in the graph during the traversal, it means that there is a circular dependency, and the function returns an empty list. 5. `depthFirstTraverse` function: - This function performs the depth-first traversal of the graph starting from a given node. - It checks if the current node has been visited (i.e., it is not part of any cycle). - If the node is currently being visited (marked as `Visiting`), it means that there is a cycle, and the function returns `true`. - Otherwise, it marks the node as `Visiting`, recursively traverses through its prerequisites, and marks it as `Visited` after the traversal. - It appends the job to the `orderedJobs` list in the correct order. 6. `JobGraph` struct: - This struct represents the job graph. - `Nodes` is a list of all the job nodes in the graph. - `Graph` is a map where the key is the job ID, and the value is a pointer to the corresponding `JobNode` in the graph. 7. `NewJobGraph` function: - This function creates a new instance of the `JobGraph` struct and initializes its `Graph` field with an empty map. - It also adds individual job nodes to the `Nodes` list. 8. `Addprereq`, `AddNode`, and `GetNode` functions: - These are helper functions to add prerequisites to a job, add a new job node to the graph, and get an existing job node, respectively. 9. `JobNode` struct: - This struct represents a node in the job graph. - `Job` is the job ID. - `Prereqs` is a list of pointers to other job nodes that are prerequisites for the current job. - `Visited` and `Visiting` are boolean flags used during the depth-first traversal to track the traversal status of each node. Overall, the code implements the topological sorting algorithm using depth-first traversal to find the order in which jobs can be executed without violating their dependencies. It efficiently handles circular dependencies and returns the ordered jobs or an empty list if a circular dependency is detected. O(j + d) time | O(j + d) space - where j is the number of jobs and d is the number of dependencies */ package main type Dep struct { Prereq int Job int } // TopologicalSort performs topological sorting of jobs given their dependencies. func TopologicalSort(jobs []int, deps []Dep) []int { // Create a job graph from the list of jobs and dependencies. jobGraph := createJobGraph(jobs, deps) // Get the ordered jobs using depth-first traversal. return getOrderedJobs(jobGraph) } // createJobGraph creates a job graph from the list of jobs and dependencies. func createJobGraph(jobs []int, deps []Dep) *JobGraph { graph := NewJobGraph(jobs) for _, dep := range deps { // Add each dependency as a prerequisite to the corresponding job node in the graph. graph.Addprereq(dep.Job, dep.Prereq) } return graph } // getOrderedJobs performs a depth-first traversal of the job graph to get the ordered jobs. func getOrderedJobs(graph *JobGraph) []int { orderedJobs := []int{} nodes := graph.Nodes // Continue the traversal until all nodes have been visited. for len(nodes) != 0 { // Get the last node from the list of nodes and remove it from the list. node := nodes[len(nodes)-1] nodes = nodes[:len(nodes)-1] // Perform depth-first traversal starting from the current node. // If a cycle is detected, return an empty list. containsCycle := depthFirstTraverse(node, &orderedJobs) if containsCycle { return []int{} } } return orderedJobs } // depthFirstTraverse performs depth-first traversal starting from the given node. // It returns true if a cycle is detected, otherwise, it returns false. func depthFirstTraverse(node *JobNode, orderedJobs *[]int) bool { // If the node has been visited, it means it is not part of any cycle, so return false. if node.Visited { return false } // If the node is currently being visited it means there is a cycle, so return true. if node.Visiting { return true } // Mark the node as currently being visited. node.Visiting = true // Recursively traverse through each prerequisite of the current node. for _, prereqNode := range node.Prereqs { // If a cycle is detected during the traversal, return true. containsCycle := depthFirstTraverse(prereqNode, orderedJobs) if containsCycle { return true } } // Mark the node as visited and no longer being visited. node.Visited = true node.Visiting = false // Append the job to the orderedJobs list in the correct order. *orderedJobs = append(*orderedJobs, node.Job) // Return false, as no cycle was detected during the traversal. return false } // JobGraph represents the job graph. type JobGraph struct { Nodes []*JobNode Graph map[int]*JobNode } // NewJobGraph creates a new instance of the JobGraph and initializes its Graph field. func NewJobGraph(jobs []int) *JobGraph { g := &JobGraph{ Graph: map[int]*JobNode{}, } for _, job := range jobs { // Add individual job nodes to the Nodes list. g.AddNode(job) } return g } // Addprereq adds a prerequisite to a job in the job graph. func (g *JobGraph) Addprereq(job, prereq int) { jobNode := g.GetNode(job) prereqNode := g.GetNode(prereq) // Add the prerequisite node to the list of prerequisites for the job node. jobNode.Prereqs = append(jobNode.Prereqs, prereqNode) } // AddNode adds a new job node to the job graph. func (g *JobGraph) AddNode(job int) { // Create a new job node and add it to the graph and nodes list. g.Graph[job] = &JobNode{Job: job} g.Nodes = append(g.Nodes, g.Graph[job]) } // GetNode gets an existing job node from the job graph. func (g *JobGraph) GetNode(job int) *JobNode { // If the node does not exist in the graph, create a new node and add it to the graph. if _, found := g.Graph[job]; !found { g.AddNode(job) } return g.Graph[job] } // JobNode represents a node in the job graph. type JobNode struct { Job int Prereqs []*JobNode Visited bool Visiting bool } ================================================ FILE: Graphs/topological_sort.java ================================================ /* You're given a list of arbitrary jobs that need to be completed; these jobs are represented by distinct integers. You're also given a list of dependencies. A dependency is represented as a pair of jobs where the first job is a prerequisite of the second one. In other words, the second job depends on the first one; it can only be completed once the first job is completed. Write a function that takes in a list of jobs and a list of dependencies and returns a list containing a valid order in which the given jobs can be completed. If no such order exists, the function should return an empty array. Sample INput: jobs: [1, 2, 3, 4] deps: = [[1, 2], [1, 3], [3, 2], [4, 2], [4, 3]] Output: [1, 4, 3, 2] or [4, 1, 3, 2] Explanation: The provided code implements a topological sorting algorithm to find the order in which jobs can be executed given their dependencies. It uses a directed acyclic graph (DAG) representation to represent the jobs and their dependencies. Let's go through each part of the code in detail: 1. `Dep` struct: - This is a simple struct that represents a dependency between two jobs. - `Prereq` field indicates the prerequisite job. - `Job` field indicates the job that depends on the prerequisite. 2. `TopologicalSort` function: - This is the main function that performs the topological sorting of jobs. - It takes two input parameters: `jobs`, a list of job IDs, and `deps`, a list of dependencies. - It first creates a job graph using the `createJobGraph` function and then gets the ordered jobs using the `getOrderedJobs` function. 3. `createJobGraph` function: - This function creates a job graph from the list of jobs and dependencies. - It creates a new instance of the `JobGraph` struct and iterates through the dependencies. - For each dependency, it adds the prerequisite job to the dependent job's list of prerequisites in the job graph. 4. `getOrderedJobs` function: - This function performs a depth-first traversal of the job graph to get the ordered jobs. - It iterates through the nodes of the graph until all nodes have been visited. - For each node, it calls the `depthFirstTraverse` function to perform the depth-first traversal and get the ordered jobs. - If a cycle is detected in the graph during the traversal, it means that there is a circular dependency, and the function returns an empty list. 5. `depthFirstTraverse` function: - This function performs the depth-first traversal of the graph starting from a given node. - It checks if the current node has been visited (i.e., it is not part of any cycle). - If the node is currently being visited (marked as `Visiting`), it means that there is a cycle, and the function returns `true`. - Otherwise, it marks the node as `Visiting`, recursively traverses through its prerequisites, and marks it as `Visited` after the traversal. - It appends the job to the `orderedJobs` list in the correct order. 6. `JobGraph` struct: - This struct represents the job graph. - `Nodes` is a list of all the job nodes in the graph. - `Graph` is a map where the key is the job ID, and the value is a pointer to the corresponding `JobNode` in the graph. 7. `NewJobGraph` function: - This function creates a new instance of the `JobGraph` struct and initializes its `Graph` field with an empty map. - It also adds individual job nodes to the `Nodes` list. 8. `Addprereq`, `AddNode`, and `GetNode` functions: - These are helper functions to add prerequisites to a job, add a new job node to the graph, and get an existing job node, respectively. 9. `JobNode` struct: - This struct represents a node in the job graph. - `Job` is the job ID. - `Prereqs` is a list of pointers to other job nodes that are prerequisites for the current job. - `Visited` and `Visiting` are boolean flags used during the depth-first traversal to track the traversal status of each node. Overall, the code implements the topological sorting algorithm using depth-first traversal to find the order in which jobs can be executed without violating their dependencies. It efficiently handles circular dependencies and returns the ordered jobs or an empty list if a circular dependency is detected. O(j + d) time | O(j + d) space - where j is the number of jobs and d is the number of dependencies */ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; class Dep { int Prereq; int Job; public Dep(int Prereq, int Job) { this.Prereq = Prereq; this.Job = Job; } } class JobGraph { // Inner class representing a job node static class JobNode { int job; // Job ID List prereqs; // List of prerequisite job nodes boolean visited; // Flag to mark if the node has been visited boolean visiting; // Flag to mark if the node is currently being visited // Constructor to initialize a job node public JobNode(int job) { this.job = job; this.prereqs = new ArrayList<>(); this.visited = false; this.visiting = false; } } List nodes; // List of all job nodes in the graph Map graph; // Map to store the job nodes by their IDs // Constructor to create a job graph from a list of jobs public JobGraph(List jobs) { this.nodes = new ArrayList<>(); this.graph = new HashMap<>(); for (int job : jobs) { // Add each job as a node to the graph addNode(job); } } // Function to add a prerequisite to a job node public void addPrereq(int job, int prereq) { JobNode jobNode = getNode(job); JobNode prereqNode = getNode(prereq); // Add the prerequisite node to the list of prerequisites for the job node jobNode.prereqs.add(prereqNode); } // Function to add a new job node to the graph public void addNode(int job) { // Create a new job node and add it to the graph and nodes list JobNode node = new JobNode(job); graph.put(job, node); nodes.add(node); } // Function to get a job node from the graph by its ID public JobNode getNode(int job) { // If the node does not exist in the graph, create a new node and add it to the graph if (!graph.containsKey(job)) { addNode(job); } return graph.get(job); } } public class Main { // Function to perform topological sorting and return the ordered jobs public static List TopologicalSort(List jobs, List deps) { // Create a job graph from the list of jobs and dependencies JobGraph graph = createJobGraph(jobs, deps); // Get the ordered jobs using depth-first traversal return getOrderedJobs(graph); } // Function to create a job graph from a list of jobs and dependencies public static JobGraph createJobGraph(List jobs, List deps) { // Initialize an empty graph JobGraph graph = new JobGraph(jobs); // Add each dependency as a prerequisite to the corresponding job node in the graph for (Dep dep : deps) { graph.addPrereq(dep.Job, dep.Prereq); } return graph; } // Function to perform depth-first traversal and get the ordered jobs public static List getOrderedJobs(JobGraph graph) { List orderedJobs = new ArrayList<>(); List nodes = graph.nodes; // Continue the traversal until all nodes have been visited while (!nodes.isEmpty()) { // Get the last node from the list of nodes and remove it from the list JobGraph.JobNode node = nodes.remove(nodes.size() - 1); // Perform depth-first traversal starting from the current node if (!depthFirstTraverse(graph, node.job, orderedJobs)) { return new ArrayList<>(); // If a cycle is detected during traversal, return an empty list } } return orderedJobs; } // Function to perform depth-first traversal starting from a specific job node public static boolean depthFirstTraverse(JobGraph graph, int jobId, List orderedJobs) { // Get the job node from the graph JobGraph.JobNode node = graph.getNode(jobId); // If the node has been visited, it means it is not part of any cycle, so return false if (node.visited) { return false; } // If the node is currently being visited, it means there is a cycle, so return true if (node.visiting) { return true; } // Mark the node as currently being visited node.visiting = true; // Recursively traverse through each prerequisite of the current node for (JobGraph.JobNode prereqNode : node.prereqs) { // If a cycle is detected during the traversal, return true if (depth ================================================ FILE: Graphs/topological_sort.js ================================================ /* You're given a list of arbitrary jobs that need to be completed; these jobs are represented by distinct integers. You're also given a list of dependencies. A dependency is represented as a pair of jobs where the first job is a prerequisite of the second one. In other words, the second job depends on the first one; it can only be completed once the first job is completed. Write a function that takes in a list of jobs and a list of dependencies and returns a list containing a valid order in which the given jobs can be completed. If no such order exists, the function should return an empty array. Sample INput: jobs: [1, 2, 3, 4] deps: = [[1, 2], [1, 3], [3, 2], [4, 2], [4, 3]] Output: [1, 4, 3, 2] or [4, 1, 3, 2] Explanation: The provided code implements a topological sorting algorithm to find the order in which jobs can be executed given their dependencies. It uses a directed acyclic graph (DAG) representation to represent the jobs and their dependencies. Let's go through each part of the code in detail: 1. `Dep` struct: - This is a simple struct that represents a dependency between two jobs. - `Prereq` field indicates the prerequisite job. - `Job` field indicates the job that depends on the prerequisite. 2. `TopologicalSort` function: - This is the main function that performs the topological sorting of jobs. - It takes two input parameters: `jobs`, a list of job IDs, and `deps`, a list of dependencies. - It first creates a job graph using the `createJobGraph` function and then gets the ordered jobs using the `getOrderedJobs` function. 3. `createJobGraph` function: - This function creates a job graph from the list of jobs and dependencies. - It creates a new instance of the `JobGraph` struct and iterates through the dependencies. - For each dependency, it adds the prerequisite job to the dependent job's list of prerequisites in the job graph. 4. `getOrderedJobs` function: - This function performs a depth-first traversal of the job graph to get the ordered jobs. - It iterates through the nodes of the graph until all nodes have been visited. - For each node, it calls the `depthFirstTraverse` function to perform the depth-first traversal and get the ordered jobs. - If a cycle is detected in the graph during the traversal, it means that there is a circular dependency, and the function returns an empty list. 5. `depthFirstTraverse` function: - This function performs the depth-first traversal of the graph starting from a given node. - It checks if the current node has been visited (i.e., it is not part of any cycle). - If the node is currently being visited (marked as `Visiting`), it means that there is a cycle, and the function returns `true`. - Otherwise, it marks the node as `Visiting`, recursively traverses through its prerequisites, and marks it as `Visited` after the traversal. - It appends the job to the `orderedJobs` list in the correct order. 6. `JobGraph` struct: - This struct represents the job graph. - `Nodes` is a list of all the job nodes in the graph. - `Graph` is a map where the key is the job ID, and the value is a pointer to the corresponding `JobNode` in the graph. 7. `NewJobGraph` function: - This function creates a new instance of the `JobGraph` struct and initializes its `Graph` field with an empty map. - It also adds individual job nodes to the `Nodes` list. 8. `Addprereq`, `AddNode`, and `GetNode` functions: - These are helper functions to add prerequisites to a job, add a new job node to the graph, and get an existing job node, respectively. 9. `JobNode` struct: - This struct represents a node in the job graph. - `Job` is the job ID. - `Prereqs` is a list of pointers to other job nodes that are prerequisites for the current job. - `Visited` and `Visiting` are boolean flags used during the depth-first traversal to track the traversal status of each node. Overall, the code implements the topological sorting algorithm using depth-first traversal to find the order in which jobs can be executed without violating their dependencies. It efficiently handles circular dependencies and returns the ordered jobs or an empty list if a circular dependency is detected. O(j + d) time | O(j + d) space - where j is the number of jobs and d is the number of dependencies */ class Dep { constructor(Prereq, Job) { this.Prereq = Prereq; this.Job = Job; } } class JobGraph { constructor(jobs) { this.nodes = []; this.graph = new Map(); for (const job of jobs) { // Add each job as a node to the graph this.addNode(job); } } addPrereq(job, prereq) { const jobNode = this.getNode(job); const prereqNode = this.getNode(prereq); // Add the prerequisite node to the list of prerequisites for the job node jobNode.prereqs.push(prereqNode); } addNode(job) { // Create a new job node and add it to the graph and nodes list const node = { job, prereqs: [], visited: false, visiting: false }; this.graph.set(job, node); this.nodes.push(node); } getNode(job) { // If the node does not exist in the graph, create a new node and add it to the graph if (!this.graph.has(job)) { this.addNode(job); } return this.graph.get(job); } } function TopologicalSort(jobs, deps) { // Create a job graph from the list of jobs and dependencies const graph = createJobGraph(jobs, deps); // Get the ordered jobs using depth-first traversal return getOrderedJobs(graph); } function createJobGraph(jobs, deps) { // Initialize an empty graph const graph = new JobGraph(jobs); // Add each dependency as a prerequisite to the corresponding job node in the graph for (const dep of deps) { graph.addPrereq(dep.Job, dep.Prereq); } return graph; } function getOrderedJobs(graph) { const orderedJobs = []; const nodes = graph.nodes; // Continue the traversal until all nodes have been visited while (nodes.length > 0) { // Get the last node from the list of nodes and remove it from the list const node = nodes.pop(); // Perform depth-first traversal starting from the current node if (!depthFirstTraverse(graph, node.job, orderedJobs)) { return []; // If a cycle is detected during traversal, return an empty list } } return orderedJobs; } function depthFirstTraverse(graph, jobId, orderedJobs) { // Get the job node from the graph const node = graph.getNode(jobId); // If the node has been visited, it means it is not part of any cycle, so return false if (node.visited) { return false; } // If the node is currently being visited, it means there is a cycle, so return true if (node.visiting) { return true; } // Mark the node as currently being visited node.visiting = true; // Recursively traverse through each prerequisite of the current node for (const prereqNode of node.prereqs) { // If a cycle is detected during the traversal, return true if (depthFirstTraverse(graph, prereqNode.job, orderedJobs)) { return true; } } // Mark the node as visited and not currently being visited node.visited = true; node.visiting = false; // Add the job to the ordered jobs list orderedJobs.push(jobId); // Return false to indicate that no cycle was detected return false; } ================================================ FILE: Graphs/topological_sort.py ================================================ """ Implementation of Topological sort using DFS According to Introduction to Algorithms, given a directed acyclic graph (DAG), a topological sort is a linear ordering of all vertices such that for any edge (u, v), u comes before v. Another way to describe it is that when you put all vertices horizontally on a line, all of the edges are pointing from left to right. Time complexity O(Vertices + Edges) """ def main(): adj_list = { 5: [2, 0], 4: [0, 1], 3: [1], 2: [3], 1: [], 0: [] } dfs(adj_list, set(), []) def dfs(adj_list, visited, stack): for vertex in range(6): if vertex in visited: continue dfs_visit(adj_list, visited, vertex, stack) print(stack[::-1]) def dfs_visit(adj_list, visited, node, stack): visited.add(node) for neighbor in adj_list[node]: if neighbor in visited: continue dfs_visit(adj_list, visited, neighbor, stack) stack.append(node) if __name__ == "__main__": main() ================================================ FILE: Graphs/two_colorable.cpp ================================================ /* You're given a list of edges representing a connected, unweighted, undirected graph with at least one node. Write a function that returns a boolean representing whether the given graph is two-colorable. Explanation: The code snippet implements an algorithm to determine if a given graph is two-colorable or bipartite. A graph is two-colorable if its vertices can be divided into two groups such that no two adjacent vertices have the same color. The function `TwoColorable(edges [][]int) bool` takes a 2D array of integers `edges`, representing the edges of the graph. Each row `edges[i]` contains the list of vertices that are connected to vertex `i`. The algorithm uses a stack and a map to keep track of the colors assigned to the vertices. It starts by assigning the first vertex (vertex 0) to color true and pushing it onto the stack. Then, it performs a depth-first traversal of the graph using the stack. For each vertex popped from the stack, it explores its adjacent vertices. If an adjacent vertex has not been colored yet (not present in the colors map), it assigns the opposite color to it (i.e., `!colors[node]`) and pushes it onto the stack. If the adjacent vertex has already been colored and its color is the same as the current vertex's color, then the graph cannot be two-colorable, and the function returns false. If the traversal completes without any conflicts (i.e., no adjacent vertices have the same color), the function returns true, indicating that the graph is two-colorable. The algorithm relies on the fact that a graph is two-colorable if and only if it is bipartite, and the two colors represent the two disjoint sets of vertices in the bipartite graph. Here's a step-by-step explanation of the algorithm: 1. Initialize the `colors` map with the first vertex (0) assigned the color true (representing one group of vertices). 2. Initialize an empty stack and push the first vertex (0) onto it. 3. While the stack is not empty, repeat the following steps: a. Pop the top vertex from the stack (denoted by `node`). b. For each vertex `connection` connected to `node` (i.e., `edges[node]`), do the following: i. If `connection` has not been colored yet (not present in the `colors` map), assign it the opposite color of `node` (i.e., `!colors[node]`) and push it onto the stack. ii. If `connection` has already been colored and its color is the same as `node`'s color, return false since the graph is not two-colorable. 4. If the traversal completes without conflicts, return true, indicating that the graph is two-colorable. Note: The algorithm assumes that the graph is connected, meaning there is a path from any vertex to any other vertex. If the graph is not connected, the algorithm will only determine whether the connected component containing vertex 0 is two-colorable. O(v + e) time | O(v) space - where v is the number of vertices and e is the number of edges in the graph */ #include #include #include #include bool isTwoColorable(std::vector>& edges) { // colors keeps track of the colors assigned to each vertex. // We start by assigning the first vertex (0) the color true (denoted by 0: true). std::unordered_map colors; colors[0] = true; // stack is used for depth-first traversal of the graph. std::stack stack; stack.push(0); while (!stack.empty()) { // Pop the top vertex from the stack (denoted by 'node'). int node = stack.top(); stack.pop(); // Explore adjacent vertices (connections) of the current vertex 'node'. for (int connection : edges[node]) { // If the adjacent vertex has not been colored yet (not present in the 'colors' map), // assign it the opposite color of the current vertex (denoted by !colors[node]), // and push it onto the stack. if (colors.find(connection) == colors.end()) { colors[connection] = !colors[node]; stack.push(connection); } else { // If the adjacent vertex has already been colored and its color is the same as the current vertex's color, // then the graph cannot be two-colorable, return false. if (colors[connection] == colors[node]) { return false; } } } } // If the traversal completes without any conflicts (no adjacent vertices have the same color), // return true, indicating that the graph is two-colorable. return true; } int main() { std::vector> edges = {{1, 2}, {0, 3}, {0, 4}, {1}, {2}}; bool isTwoColorableResult = isTwoColorable(edges); std::cout << "Is Two-Colorable: " << std::boolalpha << isTwoColorableResult << std::endl; return 0; } ================================================ FILE: Graphs/two_colorable.go ================================================ /* You're given a list of edges representing a connected, unweighted, undirected graph with at least one node. Write a function that returns a boolean representing whether the given graph is two-colorable. Explanation: The code snippet implements an algorithm to determine if a given graph is two-colorable or bipartite. A graph is two-colorable if its vertices can be divided into two groups such that no two adjacent vertices have the same color. The function `TwoColorable(edges [][]int) bool` takes a 2D array of integers `edges`, representing the edges of the graph. Each row `edges[i]` contains the list of vertices that are connected to vertex `i`. The algorithm uses a stack and a map to keep track of the colors assigned to the vertices. It starts by assigning the first vertex (vertex 0) to color true and pushing it onto the stack. Then, it performs a depth-first traversal of the graph using the stack. For each vertex popped from the stack, it explores its adjacent vertices. If an adjacent vertex has not been colored yet (not present in the colors map), it assigns the opposite color to it (i.e., `!colors[node]`) and pushes it onto the stack. If the adjacent vertex has already been colored and its color is the same as the current vertex's color, then the graph cannot be two-colorable, and the function returns false. If the traversal completes without any conflicts (i.e., no adjacent vertices have the same color), the function returns true, indicating that the graph is two-colorable. The algorithm relies on the fact that a graph is two-colorable if and only if it is bipartite, and the two colors represent the two disjoint sets of vertices in the bipartite graph. Here's a step-by-step explanation of the algorithm: 1. Initialize the `colors` map with the first vertex (0) assigned the color true (representing one group of vertices). 2. Initialize an empty stack and push the first vertex (0) onto it. 3. While the stack is not empty, repeat the following steps: a. Pop the top vertex from the stack (denoted by `node`). b. For each vertex `connection` connected to `node` (i.e., `edges[node]`), do the following: i. If `connection` has not been colored yet (not present in the `colors` map), assign it the opposite color of `node` (i.e., `!colors[node]`) and push it onto the stack. ii. If `connection` has already been colored and its color is the same as `node`'s color, return false since the graph is not two-colorable. 4. If the traversal completes without conflicts, return true, indicating that the graph is two-colorable. Note: The algorithm assumes that the graph is connected, meaning there is a path from any vertex to any other vertex. If the graph is not connected, the algorithm will only determine whether the connected component containing vertex 0 is two-colorable. O(v + e) time | O(v) space - where v is the number of vertices and e is the number of edges in the graph */ package main func TwoColorable(edges [][]int) bool { // colors keeps track of the colors assigned to each vertex. // We start by assigning the first vertex (0) the color true (denoted by 0: true). colors := map[int]bool{ 0: true, } // stack is used for depth-first traversal of the graph. stack := []int{0} for len(stack) > 0 { // Pop the top vertex from the stack (denoted by 'node'). node := stack[len(stack)-1] stack = stack[:len(stack)-1] // Explore adjacent vertices (connections) of the current vertex 'node'. for _, connection := range edges[node] { // If the adjacent vertex has not been colored yet (not present in the 'colors' map), // assign it the opposite color of the current vertex (denoted by '!colors[node]'), // and push it onto the stack. if _, colorFound := colors[connection]; !colorFound { colors[connection] = !colors[node] stack = append(stack, connection) } else { // If the adjacent vertex has already been colored and its color is the same as the current vertex's color, // then the graph cannot be two-colorable, return false. if colors[connection] == colors[node] { return false } } } } // If the traversal completes without any conflicts (no adjacent vertices have the same color), // return true, indicating that the graph is two-colorable. return true } ================================================ FILE: Graphs/two_colorable.java ================================================ /* You're given a list of edges representing a connected, unweighted, undirected graph with at least one node. Write a function that returns a boolean representing whether the given graph is two-colorable. Explanation: The code snippet implements an algorithm to determine if a given graph is two-colorable or bipartite. A graph is two-colorable if its vertices can be divided into two groups such that no two adjacent vertices have the same color. The function `TwoColorable(edges [][]int) bool` takes a 2D array of integers `edges`, representing the edges of the graph. Each row `edges[i]` contains the list of vertices that are connected to vertex `i`. The algorithm uses a stack and a map to keep track of the colors assigned to the vertices. It starts by assigning the first vertex (vertex 0) to color true and pushing it onto the stack. Then, it performs a depth-first traversal of the graph using the stack. For each vertex popped from the stack, it explores its adjacent vertices. If an adjacent vertex has not been colored yet (not present in the colors map), it assigns the opposite color to it (i.e., `!colors[node]`) and pushes it onto the stack. If the adjacent vertex has already been colored and its color is the same as the current vertex's color, then the graph cannot be two-colorable, and the function returns false. If the traversal completes without any conflicts (i.e., no adjacent vertices have the same color), the function returns true, indicating that the graph is two-colorable. The algorithm relies on the fact that a graph is two-colorable if and only if it is bipartite, and the two colors represent the two disjoint sets of vertices in the bipartite graph. Here's a step-by-step explanation of the algorithm: 1. Initialize the `colors` map with the first vertex (0) assigned the color true (representing one group of vertices). 2. Initialize an empty stack and push the first vertex (0) onto it. 3. While the stack is not empty, repeat the following steps: a. Pop the top vertex from the stack (denoted by `node`). b. For each vertex `connection` connected to `node` (i.e., `edges[node]`), do the following: i. If `connection` has not been colored yet (not present in the `colors` map), assign it the opposite color of `node` (i.e., `!colors[node]`) and push it onto the stack. ii. If `connection` has already been colored and its color is the same as `node`'s color, return false since the graph is not two-colorable. 4. If the traversal completes without conflicts, return true, indicating that the graph is two-colorable. Note: The algorithm assumes that the graph is connected, meaning there is a path from any vertex to any other vertex. If the graph is not connected, the algorithm will only determine whether the connected component containing vertex 0 is two-colorable. O(v + e) time | O(v) space - where v is the number of vertices and e is the number of edges in the graph */ import java.util.*; public class TwoColorable { public static boolean isTwoColorable(List> edges) { // colors keeps track of the colors assigned to each vertex. // We start by assigning the first vertex (0) the color true (denoted by 0: true). Map colors = new HashMap<>(); colors.put(0, true); // stack is used for depth-first traversal of the graph. Stack stack = new Stack<>(); stack.push(0); while (!stack.isEmpty()) { // Pop the top vertex from the stack (denoted by 'node'). int node = stack.pop(); // Explore adjacent vertices (connections) of the current vertex 'node'. for (int connection : edges.get(node)) { // If the adjacent vertex has not been colored yet (not present in the 'colors' map), // assign it the opposite color of the current vertex (denoted by '!colors.get(node)'), // and push it onto the stack. if (!colors.containsKey(connection)) { colors.put(connection, !colors.get(node)); stack.push(connection); } else { // If the adjacent vertex has already been colored and its color is the same as the current vertex's color, // then the graph cannot be two-colorable, return false. if (colors.get(connection) == colors.get(node)) { return false; } } } } // If the traversal completes without any conflicts (no adjacent vertices have the same color), // return true, indicating that the graph is two-colorable. return true; } public static void main(String[] args) { List> edges = new ArrayList<>(); edges.add(Arrays.asList(1, 2)); edges.add(Arrays.asList(0, 3)); edges.add(Arrays.asList(0, 4)); edges.add(Arrays.asList(1)); edges.add(Arrays.asList(2)); boolean isTwoColorable = isTwoColorable(edges); System.out.println("Is Two-Colorable: " + isTwoColorable); } } ================================================ FILE: Graphs/two_colorable.js ================================================ /* You're given a list of edges representing a connected, unweighted, undirected graph with at least one node. Write a function that returns a boolean representing whether the given graph is two-colorable. Explanation: The code snippet implements an algorithm to determine if a given graph is two-colorable or bipartite. A graph is two-colorable if its vertices can be divided into two groups such that no two adjacent vertices have the same color. The function `TwoColorable(edges [][]int) bool` takes a 2D array of integers `edges`, representing the edges of the graph. Each row `edges[i]` contains the list of vertices that are connected to vertex `i`. The algorithm uses a stack and a map to keep track of the colors assigned to the vertices. It starts by assigning the first vertex (vertex 0) to color true and pushing it onto the stack. Then, it performs a depth-first traversal of the graph using the stack. For each vertex popped from the stack, it explores its adjacent vertices. If an adjacent vertex has not been colored yet (not present in the colors map), it assigns the opposite color to it (i.e., `!colors[node]`) and pushes it onto the stack. If the adjacent vertex has already been colored and its color is the same as the current vertex's color, then the graph cannot be two-colorable, and the function returns false. If the traversal completes without any conflicts (i.e., no adjacent vertices have the same color), the function returns true, indicating that the graph is two-colorable. The algorithm relies on the fact that a graph is two-colorable if and only if it is bipartite, and the two colors represent the two disjoint sets of vertices in the bipartite graph. Here's a step-by-step explanation of the algorithm: 1. Initialize the `colors` map with the first vertex (0) assigned the color true (representing one group of vertices). 2. Initialize an empty stack and push the first vertex (0) onto it. 3. While the stack is not empty, repeat the following steps: a. Pop the top vertex from the stack (denoted by `node`). b. For each vertex `connection` connected to `node` (i.e., `edges[node]`), do the following: i. If `connection` has not been colored yet (not present in the `colors` map), assign it the opposite color of `node` (i.e., `!colors[node]`) and push it onto the stack. ii. If `connection` has already been colored and its color is the same as `node`'s color, return false since the graph is not two-colorable. 4. If the traversal completes without conflicts, return true, indicating that the graph is two-colorable. Note: The algorithm assumes that the graph is connected, meaning there is a path from any vertex to any other vertex. If the graph is not connected, the algorithm will only determine whether the connected component containing vertex 0 is two-colorable. O(v + e) time | O(v) space - where v is the number of vertices and e is the number of edges in the graph */ function isTwoColorable(edges) { // colors keeps track of the colors assigned to each vertex. // We start by assigning the first vertex (0) the color true (denoted by 0: true). const colors = new Map(); colors.set(0, true); // stack is used for depth-first traversal of the graph. const stack = [0]; while (stack.length > 0) { // Pop the top vertex from the stack (denoted by 'node'). const node = stack.pop(); // Explore adjacent vertices (connections) of the current vertex 'node'. for (const connection of edges[node]) { // If the adjacent vertex has not been colored yet (not present in the 'colors' map), // assign it the opposite color of the current vertex (denoted by !colors.get(node)), // and push it onto the stack. if (!colors.has(connection)) { colors.set(connection, !colors.get(node)); stack.push(connection); } else { // If the adjacent vertex has already been colored and its color is the same as the current vertex's color, // then the graph cannot be two-colorable, return false. if (colors.get(connection) === colors.get(node)) { return false; } } } } // If the traversal completes without any conflicts (no adjacent vertices have the same color), // return true, indicating that the graph is two-colorable. return true; } // Example usage const edges = [[1, 2], [0, 3], [0, 4], [1], [2]]; const isTwoColorableResult = isTwoColorable(edges); console.log("Is Two-Colorable:", isTwoColorableResult); ================================================ FILE: Graphs/two_colorable.py ================================================ ''' You're given a list of edges representing a connected, unweighted, undirected graph with at least one node. Write a function that returns a boolean representing whether the given graph is two-colorable. Explanation: The code snippet implements an algorithm to determine if a given graph is two-colorable or bipartite. A graph is two-colorable if its vertices can be divided into two groups such that no two adjacent vertices have the same color. The function `TwoColorable(edges [][]int) bool` takes a 2D array of integers `edges`, representing the edges of the graph. Each row `edges[i]` contains the list of vertices that are connected to vertex `i`. The algorithm uses a stack and a map to keep track of the colors assigned to the vertices. It starts by assigning the first vertex (vertex 0) to color true and pushing it onto the stack. Then, it performs a depth-first traversal of the graph using the stack. For each vertex popped from the stack, it explores its adjacent vertices. If an adjacent vertex has not been colored yet (not present in the colors map), it assigns the opposite color to it (i.e., `!colors[node]`) and pushes it onto the stack. If the adjacent vertex has already been colored and its color is the same as the current vertex's color, then the graph cannot be two-colorable, and the function returns false. If the traversal completes without any conflicts (i.e., no adjacent vertices have the same color), the function returns true, indicating that the graph is two-colorable. The algorithm relies on the fact that a graph is two-colorable if and only if it is bipartite, and the two colors represent the two disjoint sets of vertices in the bipartite graph. Here's a step-by-step explanation of the algorithm: 1. Initialize the `colors` map with the first vertex (0) assigned the color true (representing one group of vertices). 2. Initialize an empty stack and push the first vertex (0) onto it. 3. While the stack is not empty, repeat the following steps: a. Pop the top vertex from the stack (denoted by `node`). b. For each vertex `connection` connected to `node` (i.e., `edges[node]`), do the following: i. If `connection` has not been colored yet (not present in the `colors` map), assign it the opposite color of `node` (i.e., `!colors[node]`) and push it onto the stack. ii. If `connection` has already been colored and its color is the same as `node`'s color, return false since the graph is not two-colorable. 4. If the traversal completes without conflicts, return true, indicating that the graph is two-colorable. Note: The algorithm assumes that the graph is connected, meaning there is a path from any vertex to any other vertex. If the graph is not connected, the algorithm will only determine whether the connected component containing vertex 0 is two-colorable. O(v + e) time | O(v) space - where v is the number of vertices and e is the number of edges in the graph ''' def is_two_colorable(edges): # colors keeps track of the colors assigned to each vertex. # We start by assigning the first vertex (0) the color True (denoted by 0: True). colors = {0: True} # stack is used for depth-first traversal of the graph. stack = [0] while stack: # Pop the top vertex from the stack (denoted by 'node'). node = stack.pop() # Explore adjacent vertices (connections) of the current vertex 'node'. for connection in edges[node]: # If the adjacent vertex has not been colored yet (not present in the 'colors' dictionary), # assign it the opposite color of the current vertex (denoted by not colors[node]), # and push it onto the stack. if connection not in colors: colors[connection] = not colors[node] stack.append(connection) else: # If the adjacent vertex has already been colored and its color is the same as the current vertex's color, # then the graph cannot be two-colorable, return False. if colors[connection] == colors[node]: return False # If the traversal completes without any conflicts (no adjacent vertices have the same color), # return True, indicating that the graph is two-colorable. return True # Example usage edges = [[1, 2], [0, 3], [0, 4], [1], [2]] is_two_colorable_result = is_two_colorable(edges) print("Is Two-Colorable:", is_two_colorable_result) ================================================ FILE: Graphs/union_find.cpp ================================================ /* Write a UnionFind class that implements the union-find (also called a disjoint set) data structure. This class should support three methods: The union-find data structure is similar to a traditional set data structure in that it contains a collection of unique values. However, these values are spread out amongst a variety of distinct disjoint sets, meaning that no set can have duplicate values, and no two sets can contain the same value. createSet(value) : : Adds a given value in a new set containing only that value. union(valueOne, valueTwo) : : Takes in two values and determines which sets they are in. If they are in different sets, the sets are combined into a single set. If either value is not in a set or they are in the same set, the function should have no effect. find(value): : Returns the "representative" value of the set for which a value belongs to. This can be any value in the set, but it should always be the same value, regardless of which value in the set find is passed. If the value is not in a set, the function should return null / none Explanation: The provided code snippet is an optimized version of the Union-Find data structure. Here's a detailed explanation: - `UnionFind` struct: It contains two fields, `parents` and `ranks`. The `parents` map stores the parent of each element, and the `ranks` map stores the rank of each element. The rank is used to optimize the Union operation. - `NewUnionFind` function: It initializes a new instance of the UnionFind struct by creating empty maps for `parents` and `ranks`. - `CreateSet` method: It creates a new set with the given value. It sets the parent of the value to itself and initializes its rank to 0. - `Find` method: It finds the root/representative of the set to which the given value belongs. It starts from the given value and traverses the parent pointers until it reaches the root. It uses path compression optimization to update the parent pointers along the path to the root. Finally, it returns a pointer to the root. `Union` method: It performs the union of two sets represented by the given values. It first checks if both values exist in the data structure. If either value is missing, it returns without performing any union operation. Otherwise, it finds the roots of the two sets using the `Find` method. It compares the ranks of the two roots and performs the union accordingly: If the rank of the root of `valueOne` is less than the rank of the root of `valueTwo`, it sets the parent of `valueOne`'s root to `valueTwo`'s root. If the rank of the root of `valueOne` is greater than the rank of the root of `valueTwo`, it sets the parent of `valueTwo`'s root to `valueOne`'s root. If the ranks are equal, it chooses one root as the parent and increments its rank by 1. By considering the ranks of the roots during the union, the height of the resulting union-find tree can be minimized. The time and space complexity of the operations in the `UnionFind` data structure are as follows: CreateSet : O(n) time O(1) space Find : O(log(n)) time O(1) space where n is total number of values Union : O(log(n)) time O(1) space where n is total number of values */ #include #include class UnionFind { public: std::unordered_map parents; // Map to store the parent of each element std::unordered_map ranks; // Map to store the rank of each element void createSet(int value) { parents[value] = value; // Set the parent of the value to itself ranks[value] = 0; // Initialize the rank of the value to 0 } int find(int value) { if (parents.find(value) == parents.end()) { return -1; // Return -1 if the value is not found (not part of any set) } int currentParent = value; while (currentParent != parents[currentParent]) { currentParent = parents[currentParent]; // Traverse the parent pointers until reaching the root } // Perform path compression by updating parent pointers along the path to the root // This optimization flattens the tree structure, reducing future lookup time while (value != currentParent) { int nextParent = parents[value]; parents[value] = currentParent; value = nextParent; } return currentParent; // Return the root/representative } void unionSets(int valueOne, int valueTwo) { if (parents.find(valueOne) == parents.end() || parents.find(valueTwo) == parents.end()) { return; // Return if either value is not found (not part of any set) } int valueOneRoot = find(valueOne); // Find the root of the set containing valueOne int valueTwoRoot = find(valueTwo); // Find the root of the set containing valueTwo if (ranks[valueOneRoot] < ranks[valueTwoRoot]) { parents[valueOneRoot] = valueTwoRoot; // Set the parent of valueOne's root to valueTwo's root } else if (ranks[valueOneRoot] > ranks[valueTwoRoot]) { parents[valueTwoRoot] = valueOneRoot; // Set the parent of valueTwo's root to valueOne's root } else { parents[valueOneRoot] = valueTwoRoot; // Set the parent of valueOne's root to valueTwo's root ranks[valueTwoRoot] += 1; // Increment the rank of valueTwo's root } } }; int main() { UnionFind unionFind; // Create individual sets unionFind.createSet(1); unionFind.createSet(2); unionFind.createSet(3); // Perform union operations unionFind.unionSets(1, 2); unionFind.unionSets(2, 3); // Find representatives of values int representative = unionFind.find(3); // Check if the representative is found if (representative != -1) { std::cout << "The representative of 3 is: " << representative << std::endl; } else { std::cout << "Value 3 is not found." << std::endl; } return 0; } ================================================ FILE: Graphs/union_find.go ================================================ /* Write a UnionFind class that implements the union-find (also called a disjoint set) data structure. This class should support three methods: The union-find data structure is similar to a traditional set data structure in that it contains a collection of unique values. However, these values are spread out amongst a variety of distinct disjoint sets, meaning that no set can have duplicate values, and no two sets can contain the same value. createSet(value) : : Adds a given value in a new set containing only that value. union(valueOne, valueTwo) : : Takes in two values and determines which sets they are in. If they are in different sets, the sets are combined into a single set. If either value is not in a set or they are in the same set, the function should have no effect. find(value): : Returns the "representative" value of the set for which a value belongs to. This can be any value in the set, but it should always be the same value, regardless of which value in the set find is passed. If the value is not in a set, the function should return null / none Explanation: The provided code snippet is an optimized version of the Union-Find data structure. Here's a detailed explanation: - `UnionFind` struct: It contains two fields, `parents` and `ranks`. The `parents` map stores the parent of each element, and the `ranks` map stores the rank of each element. The rank is used to optimize the Union operation. - `NewUnionFind` function: It initializes a new instance of the UnionFind struct by creating empty maps for `parents` and `ranks`. - `CreateSet` method: It creates a new set with the given value. It sets the parent of the value to itself and initializes its rank to 0. - `Find` method: It finds the root/representative of the set to which the given value belongs. It starts from the given value and traverses the parent pointers until it reaches the root. It uses path compression optimization to update the parent pointers along the path to the root. Finally, it returns a pointer to the root. `Union` method: It performs the union of two sets represented by the given values. It first checks if both values exist in the data structure. If either value is missing, it returns without performing any union operation. Otherwise, it finds the roots of the two sets using the `Find` method. It compares the ranks of the two roots and performs the union accordingly: If the rank of the root of `valueOne` is less than the rank of the root of `valueTwo`, it sets the parent of `valueOne`'s root to `valueTwo`'s root. If the rank of the root of `valueOne` is greater than the rank of the root of `valueTwo`, it sets the parent of `valueTwo`'s root to `valueOne`'s root. If the ranks are equal, it chooses one root as the parent and increments its rank by 1. By considering the ranks of the roots during the union, the height of the resulting union-find tree can be minimized. The time and space complexity of the operations in the `UnionFind` data structure are as follows: CreateSet : O(n) time O(1) space Find : O(log(n)) time O(1) space where n is total number of values Union : O(log(n)) time O(1) space where n is total number of values */ package main import "fmt" type UnionFind struct { parents map[int]int // Map to store the parent of each element ranks map[int]int // Map to store the rank of each element } // NewUnionFind creates a new instance of the UnionFind struct. func NewUnionFind() *UnionFind { return &UnionFind{ parents: map[int]int{}, ranks: map[int]int{}, } } // CreateSet creates a new set with the given value. func (union *UnionFind) CreateSet(value int) { union.parents[value] = value // Set the parent of the value to itself union.ranks[value] = 0 // Initialize the rank of the value to 0 } // Find finds the root/representative of the set to which the given value belongs. func (union *UnionFind) Find(value int) *int { if _, found := union.parents[value]; !found { return nil // Return nil if the value is not found (not part of any set) } currentParent := value for currentParent != union.parents[currentParent] { currentParent = union.parents[currentParent] // Traverse the parent pointers until reaching the root } // Perform path compression by updating parent pointers along the path to the root // This optimization flattens the tree structure, reducing future lookup time for value != currentParent { nextParent := union.parents[value] union.parents[value] = currentParent value = nextParent } return ¤tParent // Return a pointer to the root/representative } // Union performs the union of two sets represented by the given values. func (union *UnionFind) Union(valueOne, valueTwo int) { _, parentFoundOne := union.parents[valueOne] _, parentFoundTwo := union.parents[valueTwo] if !parentFoundOne || !parentFoundTwo { return // Return if either value is not found (not part of any set) } valueOneRoot := *union.Find(valueOne) // Find the root of the set containing valueOne valueTwoRoot := *union.Find(valueTwo) // Find the root of the set containing valueTwo if union.ranks[valueOneRoot] < union.ranks[valueTwoRoot] { union.parents[valueOneRoot] = valueTwoRoot // Set the parent of valueOne's root to valueTwo's root } else if union.ranks[valueOneRoot] > union.ranks[valueTwoRoot] { union.parents[valueTwoRoot] = valueOneRoot // Set the parent of valueTwo's root to valueOne's root } else { union.parents[valueOneRoot] = valueTwoRoot // Set the parent of valueOne's root to valueTwo's root union.ranks[valueTwoRoot] += 1 // Increment the rank of valueTwo's root } } func main() { // Create a new instance of UnionFind union := NewUnionFind() // Create individual sets union.CreateSet(1) union.CreateSet(2) union.CreateSet(3) // Perform union operations union.Union(1, 2) union.Union(2, 3) // Find representatives of values representative := union.Find(3) // Check if the representative is found if representative != nil { fmt.Println("The representative of 3 is:", *representative) } else { fmt.Println("Value 3 is not found.") } } ================================================ FILE: Graphs/union_find.java ================================================ /* Write a UnionFind class that implements the union-find (also called a disjoint set) data structure. This class should support three methods: The union-find data structure is similar to a traditional set data structure in that it contains a collection of unique values. However, these values are spread out amongst a variety of distinct disjoint sets, meaning that no set can have duplicate values, and no two sets can contain the same value. createSet(value) : : Adds a given value in a new set containing only that value. union(valueOne, valueTwo) : : Takes in two values and determines which sets they are in. If they are in different sets, the sets are combined into a single set. If either value is not in a set or they are in the same set, the function should have no effect. find(value): : Returns the "representative" value of the set for which a value belongs to. This can be any value in the set, but it should always be the same value, regardless of which value in the set find is passed. If the value is not in a set, the function should return null / none Explanation: The provided code snippet is an optimized version of the Union-Find data structure. Here's a detailed explanation: - `UnionFind` struct: It contains two fields, `parents` and `ranks`. The `parents` map stores the parent of each element, and the `ranks` map stores the rank of each element. The rank is used to optimize the Union operation. - `NewUnionFind` function: It initializes a new instance of the UnionFind struct by creating empty maps for `parents` and `ranks`. - `CreateSet` method: It creates a new set with the given value. It sets the parent of the value to itself and initializes its rank to 0. - `Find` method: It finds the root/representative of the set to which the given value belongs. It starts from the given value and traverses the parent pointers until it reaches the root. It uses path compression optimization to update the parent pointers along the path to the root. Finally, it returns a pointer to the root. `Union` method: It performs the union of two sets represented by the given values. It first checks if both values exist in the data structure. If either value is missing, it returns without performing any union operation. Otherwise, it finds the roots of the two sets using the `Find` method. It compares the ranks of the two roots and performs the union accordingly: If the rank of the root of `valueOne` is less than the rank of the root of `valueTwo`, it sets the parent of `valueOne`'s root to `valueTwo`'s root. If the rank of the root of `valueOne` is greater than the rank of the root of `valueTwo`, it sets the parent of `valueTwo`'s root to `valueOne`'s root. If the ranks are equal, it chooses one root as the parent and increments its rank by 1. By considering the ranks of the roots during the union, the height of the resulting union-find tree can be minimized. The time and space complexity of the operations in the `UnionFind` data structure are as follows: CreateSet : O(n) time O(1) space Find : O(log(n)) time O(1) space where n is total number of values Union : O(log(n)) time O(1) space where n is total number of values */ import java.util.HashMap; import java.util.Map; class UnionFind { private Map parents; // Map to store the parent of each element private Map ranks; // Map to store the rank of each element public UnionFind() { parents = new HashMap<>(); ranks = new HashMap<>(); } public void createSet(int value) { parents.put(value, value); // Set the parent of the value to itself ranks.put(value, 0); // Initialize the rank of the value to 0 } public int find(int value) { if (!parents.containsKey(value)) { return -1; // Return -1 if the value is not found (not part of any set) } int currentParent = value; while (currentParent != parents.get(currentParent)) { currentParent = parents.get(currentParent); // Traverse the parent pointers until reaching the root } // Perform path compression by updating parent pointers along the path to the root // This optimization flattens the tree structure, reducing future lookup time while (value != currentParent) { int nextParent = parents.get(value); parents.put(value, currentParent); value = nextParent; } return currentParent; // Return the root/representative } public void unionSets(int valueOne, int valueTwo) { if (!parents.containsKey(valueOne) || !parents.containsKey(valueTwo)) { return; // Return if either value is not found (not part of any set) } int valueOneRoot = find(valueOne); // Find the root of the set containing valueOne int valueTwoRoot = find(valueTwo); // Find the root of the set containing valueTwo if (ranks.get(valueOneRoot) < ranks.get(valueTwoRoot)) { parents.put(valueOneRoot, valueTwoRoot); // Set the parent of valueOne's root to valueTwo's root } else if (ranks.get(valueOneRoot) > ranks.get(valueTwoRoot)) { parents.put(valueTwoRoot, valueOneRoot); // Set the parent of valueTwo's root to valueOne's root } else { parents.put(valueOneRoot, valueTwoRoot); // Set the parent of valueOne's root to valueTwo's root ranks.put(valueTwoRoot, ranks.get(valueTwoRoot) + 1); // Increment the rank of valueTwo's root } } } public class Main { public static void main(String[] args) { UnionFind unionFind = new UnionFind(); // Create individual sets unionFind.createSet(1); unionFind.createSet(2); unionFind.createSet(3); // Perform union operations unionFind.unionSets(1, 2); unionFind.unionSets(2, 3); // Find representatives of values int representative = unionFind.find(3); // Check if the representative is found if (representative != -1) { System.out.println("The representative of 3 is: " + representative); } else { System.out.println("Value 3 is not found."); } } } ================================================ FILE: Graphs/union_find.js ================================================ /* Write a UnionFind class that implements the union-find (also called a disjoint set) data structure. This class should support three methods: The union-find data structure is similar to a traditional set data structure in that it contains a collection of unique values. However, these values are spread out amongst a variety of distinct disjoint sets, meaning that no set can have duplicate values, and no two sets can contain the same value. createSet(value) : : Adds a given value in a new set containing only that value. union(valueOne, valueTwo) : : Takes in two values and determines which sets they are in. If they are in different sets, the sets are combined into a single set. If either value is not in a set or they are in the same set, the function should have no effect. find(value): : Returns the "representative" value of the set for which a value belongs to. This can be any value in the set, but it should always be the same value, regardless of which value in the set find is passed. If the value is not in a set, the function should return null / none Explanation: The provided code snippet is an optimized version of the Union-Find data structure. Here's a detailed explanation: - `UnionFind` struct: It contains two fields, `parents` and `ranks`. The `parents` map stores the parent of each element, and the `ranks` map stores the rank of each element. The rank is used to optimize the Union operation. - `NewUnionFind` function: It initializes a new instance of the UnionFind struct by creating empty maps for `parents` and `ranks`. - `CreateSet` method: It creates a new set with the given value. It sets the parent of the value to itself and initializes its rank to 0. - `Find` method: It finds the root/representative of the set to which the given value belongs. It starts from the given value and traverses the parent pointers until it reaches the root. It uses path compression optimization to update the parent pointers along the path to the root. Finally, it returns a pointer to the root. `Union` method: It performs the union of two sets represented by the given values. It first checks if both values exist in the data structure. If either value is missing, it returns without performing any union operation. Otherwise, it finds the roots of the two sets using the `Find` method. It compares the ranks of the two roots and performs the union accordingly: If the rank of the root of `valueOne` is less than the rank of the root of `valueTwo`, it sets the parent of `valueOne`'s root to `valueTwo`'s root. If the rank of the root of `valueOne` is greater than the rank of the root of `valueTwo`, it sets the parent of `valueTwo`'s root to `valueOne`'s root. If the ranks are equal, it chooses one root as the parent and increments its rank by 1. By considering the ranks of the roots during the union, the height of the resulting union-find tree can be minimized. The time and space complexity of the operations in the `UnionFind` data structure are as follows: CreateSet : O(n) time O(1) space Find : O(log(n)) time O(1) space where n is total number of values Union : O(log(n)) time O(1) space where n is total number of values */ class UnionFind { constructor() { this.parents = {}; // Map to store the parent of each element this.ranks = {}; // Map to store the rank of each element } createSet(value) { this.parents[value] = value; // Set the parent of the value to itself this.ranks[value] = 0; // Initialize the rank of the value to 0 } find(value) { if (!(value in this.parents)) { return null; // Return null if the value is not found (not part of any set) } let currentParent = value; while (currentParent !== this.parents[currentParent]) { currentParent = this.parents[currentParent]; // Traverse the parent pointers until reaching the root } // Perform path compression by updating parent pointers along the path to the root // This optimization flattens the tree structure, reducing future lookup time while (value !== currentParent) { const nextParent = this.parents[value]; this.parents[value] = currentParent; value = nextParent; } return currentParent; // Return the root/representative } union(valueOne, valueTwo) { if (!(valueOne in this.parents) || !(valueTwo in this.parents)) { return; // Return if either value is not found (not part of any set) } const valueOneRoot = this.find(valueOne); // Find the root of the set containing valueOne const valueTwoRoot = this.find(valueTwo); // Find the root of the set containing valueTwo if (this.ranks[valueOneRoot] < this.ranks[valueTwoRoot]) { this.parents[valueOneRoot] = valueTwoRoot; // Set the parent of valueOne's root to valueTwo's root } else if (this.ranks[valueOneRoot] > this.ranks[valueTwoRoot]) { this.parents[valueTwoRoot] = valueOneRoot; // Set the parent of valueTwo's root to valueOne's root } else { this.parents[valueOneRoot] = valueTwoRoot; // Set the parent of valueOne's root to valueTwo's root this.ranks[valueTwoRoot] += 1; // Increment the rank of valueTwo's root } } } // Create a new instance of UnionFind const union = new UnionFind(); // Create individual sets union.createSet(1); union.createSet(2); union.createSet(3); // Perform union operations union.union(1, 2); union.union(2, 3); // Find representatives of values const representative = union.find(3); // Check if the representative is found if (representative !== null) { console.log("The representative of 3 is:", representative); } else { console.log("Value 3 is not found."); } ================================================ FILE: Graphs/union_find.py ================================================ ''' Write a UnionFind class that implements the union-find (also called a disjoint set) data structure. This class should support three methods: The union-find data structure is similar to a traditional set data structure in that it contains a collection of unique values. However, these values are spread out amongst a variety of distinct disjoint sets, meaning that no set can have duplicate values, and no two sets can contain the same value. createSet(value) : : Adds a given value in a new set containing only that value. union(valueOne, valueTwo) : : Takes in two values and determines which sets they are in. If they are in different sets, the sets are combined into a single set. If either value is not in a set or they are in the same set, the function should have no effect. find(value): : Returns the "representative" value of the set for which a value belongs to. This can be any value in the set, but it should always be the same value, regardless of which value in the set find is passed. If the value is not in a set, the function should return null / none Explanation: The provided code snippet is an optimized version of the Union-Find data structure. Here's a detailed explanation: - `UnionFind` struct: It contains two fields, `parents` and `ranks`. The `parents` map stores the parent of each element, and the `ranks` map stores the rank of each element. The rank is used to optimize the Union operation. - `NewUnionFind` function: It initializes a new instance of the UnionFind struct by creating empty maps for `parents` and `ranks`. - `CreateSet` method: It creates a new set with the given value. It sets the parent of the value to itself and initializes its rank to 0. - `Find` method: It finds the root/representative of the set to which the given value belongs. It starts from the given value and traverses the parent pointers until it reaches the root. It uses path compression optimization to update the parent pointers along the path to the root. Finally, it returns a pointer to the root. `Union` method: It performs the union of two sets represented by the given values. It first checks if both values exist in the data structure. If either value is missing, it returns without performing any union operation. Otherwise, it finds the roots of the two sets using the `Find` method. It compares the ranks of the two roots and performs the union accordingly: If the rank of the root of `valueOne` is less than the rank of the root of `valueTwo`, it sets the parent of `valueOne`'s root to `valueTwo`'s root. If the rank of the root of `valueOne` is greater than the rank of the root of `valueTwo`, it sets the parent of `valueTwo`'s root to `valueOne`'s root. If the ranks are equal, it chooses one root as the parent and increments its rank by 1. By considering the ranks of the roots during the union, the height of the resulting union-find tree can be minimized. The time and space complexity of the operations in the `UnionFind` data structure are as follows: CreateSet : O(n) time O(1) space Find : O(log(n)) time O(1) space where n is total number of values Union : O(log(n)) time O(1) space where n is total number of values ''' class UnionFind: def __init__(self): self.parents = {} # Map to store the parent of each element self.ranks = {} # Map to store the rank of each element def create_set(self, value): self.parents[value] = value # Set the parent of the value to itself self.ranks[value] = 0 # Initialize the rank of the value to 0 def find(self, value): if value not in self.parents: return None # Return None if the value is not found (not part of any set) current_parent = value while current_parent != self.parents[current_parent]: current_parent = self.parents[current_parent] # Traverse the parent pointers until reaching the root # Perform path compression by updating parent pointers along the path to the root # This optimization flattens the tree structure, reducing future lookup time while value != current_parent: next_parent = self.parents[value] self.parents[value] = current_parent value = next_parent return current_parent # Return the root/representative def union(self, value_one, value_two): if value_one not in self.parents or value_two not in self.parents: return # Return if either value is not found (not part of any set) value_one_root = self.find(value_one) # Find the root of the set containing value_one value_two_root = self.find(value_two) # Find the root of the set containing value_two if self.ranks[value_one_root] < self.ranks[value_two_root]: self.parents[value_one_root] = value_two_root # Set the parent of value_one's root to value_two's root elif self.ranks[value_one_root] > self.ranks[value_two_root]: self.parents[value_two_root] = value_one_root # Set the parent of value_two's root to value_one's root else: self.parents[value_one_root] = value_two_root # Set the parent of value_one's root to value_two's root self.ranks[value_two_root] += 1 # Increment the rank of value_two's root # Create a new instance of UnionFind union = UnionFind() # Create individual sets union.create_set(1) union.create_set(2) union.create_set(3) # Perform union operations union.union(1, 2) union.union(2, 3) # Find representatives of values representative = union.find(3) # Check if the representative is found if representative is not None: print("The representative of 3 is:", representative) else: print("Value 3 is not found.") ================================================ FILE: Graphs/validate_bst.cpp ================================================ /* Write a function that takes in a potentially invalid Binary Search Tree (BST) and returns a boolean representing whether the BST is valid. Explanation: This code defines a Binary Search Tree (BST) struct with an integer value and left and right nodes that can point to other BST nodes. The struct also has a method called ValidateBst() that returns a boolean indicating whether the tree is a valid BST or not. The BST struct has another method called validateBST() that is used by ValidateBst() to check whether the tree is a valid BST or not. The validateBST() method takes in two arguments, min and max, which represent the minimum and maximum values that the current node's value can take in order to be a valid BST. The validateBST() method first checks whether the current node's value is within the valid range determined by the min and max arguments. If not, the method returns false, indicating that the tree is not a valid BST. If the current node's value is within the valid range, the method then recursively calls itself on the left and right child nodes to check whether their values are within their valid ranges. The valid range for the left child node is defined by the minimum value and the parent node's value, while the valid range for the right child node is defined by the parent node's value and the maximum value. If all of the nodes in the tree satisfy the BST property, the method returns true, indicating that the tree is a valid BST. O(n) time | O(d) space - where n is the number of nodes in the BST and d is the depth (height) of the BST */ #include #include struct TreeNode { int value; TreeNode* left; TreeNode* right; TreeNode(int x) : value(x), left(nullptr), right(nullptr) {} }; // Function to check if the BST is valid bool isValidBST(TreeNode* root) { return isValidBSTHelper(root, std::numeric_limits::min(), std::numeric_limits::max()); } // Recursive helper function to check if the BST is valid bool isValidBSTHelper(TreeNode* node, long long minVal, long long maxVal) { // Base case: if the current node's value is outside the allowed range, then the tree is invalid if (!node || node->value <= minVal || node->value >= maxVal) { return false; } // Recursively check the left subtree, making sure all values are less than the current node's value if (!isValidBSTHelper(node->left, minVal, node->value)) { return false; } // Recursively check the right subtree, making sure all values are greater than or equal to the current node's value if (!isValidBSTHelper(node->right, node->value, maxVal)) { return false; } // If we reach this point, then the tree is valid return true; } int main() { TreeNode* root = new TreeNode(10); root->left = new TreeNode(5); root->right = new TreeNode(15); root->left->left = new TreeNode(2); root->left->right = new TreeNode(7); if (isValidBST(root)) { std::cout << "The BST is valid." << std::endl; } else { std::cout << "The BST is not valid." << std::endl; } // Clean up memory delete root->left->left; delete root->left->right; delete root->left; delete root->right; delete root; return 0; } ================================================ FILE: Graphs/validate_bst.go ================================================ /* Write a function that takes in a potentially invalid Binary Search Tree (BST) and returns a boolean representing whether the BST is valid. Explanation: This code defines a Binary Search Tree (BST) struct with an integer value and left and right nodes that can point to other BST nodes. The struct also has a method called ValidateBst() that returns a boolean indicating whether the tree is a valid BST or not. The BST struct has another method called validateBST() that is used by ValidateBst() to check whether the tree is a valid BST or not. The validateBST() method takes in two arguments, min and max, which represent the minimum and maximum values that the current node's value can take in order to be a valid BST. The validateBST() method first checks whether the current node's value is within the valid range determined by the min and max arguments. If not, the method returns false, indicating that the tree is not a valid BST. If the current node's value is within the valid range, the method then recursively calls itself on the left and right child nodes to check whether their values are within their valid ranges. The valid range for the left child node is defined by the minimum value and the parent node's value, while the valid range for the right child node is defined by the parent node's value and the maximum value. If all of the nodes in the tree satisfy the BST property, the method returns true, indicating that the tree is a valid BST. O(n) time | O(d) space - where n is the number of nodes in the BST and d is the depth (height) of the BST */ package main import "math" type BST struct { Value int Left *BST Right *BST } // ValidateBst is a method of BST that checks if the binary search tree is valid func (tree *BST) ValidateBst() bool { return tree.validateBST(math.MinInt32, math.MaxInt32) } // validateBST is a recursive helper function that checks if the binary search tree is valid // min is the minimum value that a node in the subtree rooted at this node can have // max is the maximum value that a node in the subtree rooted at this node can have func (tree *BST) validateBST(min, max int) bool { // if the current node's value is outside the allowed range, then the tree is invalid if tree.Value < min || tree.Value >= max { return false } // recursively check the left subtree, making sure all values are less than the current node's value if tree.Left != nil && !tree.Left.validateBST(min, tree.Value) { return false } // recursively check the right subtree, making sure all values are greater than or equal to the current node's value if tree.Right != nil && !tree.Right.validateBST(tree.Value, max) { return false } // if we reach this point, then the tree is valid return true } ================================================ FILE: Graphs/validate_bst.java ================================================ /* Write a function that takes in a potentially invalid Binary Search Tree (BST) and returns a boolean representing whether the BST is valid. Explanation: This code defines a Binary Search Tree (BST) struct with an integer value and left and right nodes that can point to other BST nodes. The struct also has a method called ValidateBst() that returns a boolean indicating whether the tree is a valid BST or not. The BST struct has another method called validateBST() that is used by ValidateBst() to check whether the tree is a valid BST or not. The validateBST() method takes in two arguments, min and max, which represent the minimum and maximum values that the current node's value can take in order to be a valid BST. The validateBST() method first checks whether the current node's value is within the valid range determined by the min and max arguments. If not, the method returns false, indicating that the tree is not a valid BST. If the current node's value is within the valid range, the method then recursively calls itself on the left and right child nodes to check whether their values are within their valid ranges. The valid range for the left child node is defined by the minimum value and the parent node's value, while the valid range for the right child node is defined by the parent node's value and the maximum value. If all of the nodes in the tree satisfy the BST property, the method returns true, indicating that the tree is a valid BST. O(n) time | O(d) space - where n is the number of nodes in the BST and d is the depth (height) of the BST */ import java.util.*; class TreeNode { int value; TreeNode left; TreeNode right; TreeNode(int x) { value = x; left = null; right = null; } } public class ValidateBST { // Public method to check if the BST is valid public static boolean isValidBST(TreeNode root) { return isValidBSTHelper(root, Long.MIN_VALUE, Long.MAX_VALUE); } // Private recursive helper function to check if the BST is valid private static boolean isValidBSTHelper(TreeNode node, long minVal, long maxVal) { // Base case: if the current node's value is outside the allowed range, then the tree is invalid if (node == null || node.value < minVal || node.value >= maxVal) { return false; } // Recursively check the left subtree, making sure all values are less than the current node's value if (!isValidBSTHelper(node.left, minVal, node.value)) { return false; } // Recursively check the right subtree, making sure all values are greater than or equal to the current node's value if (!isValidBSTHelper(node.right, node.value, maxVal)) { return false; } // If we reach this point, then the tree is valid return true; } public static void main(String[] args) { TreeNode root = new TreeNode(10); root.left = new TreeNode(5); root.right = new TreeNode(15); root.left.left = new TreeNode(2); root.left.right = new TreeNode(7); if (isValidBST(root)) { System.out.println("The BST is valid."); } else { System.out.println("The BST is not valid."); } } } ================================================ FILE: Graphs/validate_bst.js ================================================ /* Write a function that takes in a potentially invalid Binary Search Tree (BST) and returns a boolean representing whether the BST is valid. Explanation: This code defines a Binary Search Tree (BST) struct with an integer value and left and right nodes that can point to other BST nodes. The struct also has a method called ValidateBst() that returns a boolean indicating whether the tree is a valid BST or not. The BST struct has another method called validateBST() that is used by ValidateBst() to check whether the tree is a valid BST or not. The validateBST() method takes in two arguments, min and max, which represent the minimum and maximum values that the current node's value can take in order to be a valid BST. The validateBST() method first checks whether the current node's value is within the valid range determined by the min and max arguments. If not, the method returns false, indicating that the tree is not a valid BST. If the current node's value is within the valid range, the method then recursively calls itself on the left and right child nodes to check whether their values are within their valid ranges. The valid range for the left child node is defined by the minimum value and the parent node's value, while the valid range for the right child node is defined by the parent node's value and the maximum value. If all of the nodes in the tree satisfy the BST property, the method returns true, indicating that the tree is a valid BST. O(n) time | O(d) space - where n is the number of nodes in the BST and d is the depth (height) of the BST */ class TreeNode { constructor(value) { this.value = value; this.left = null; this.right = null; } } // Function to check if the BST is valid function isValidBST(root) { return isValidBSTHelper(root, -Infinity, Infinity); } // Recursive helper function to check if the BST is valid function isValidBSTHelper(node, minVal, maxVal) { // Base case: if the current node's value is outside the allowed range, then the tree is invalid if (!node || node.value <= minVal || node.value >= maxVal) { return false; } // Recursively check the left subtree, making sure all values are less than the current node's value if (!isValidBSTHelper(node.left, minVal, node.value)) { return false; } // Recursively check the right subtree, making sure all values are greater than or equal to the current node's value if (!isValidBSTHelper(node.right, node.value, maxVal)) { return false; } // If we reach this point, then the tree is valid return true; } // Example usage: const root = new TreeNode(10); root.left = new TreeNode(5); root.right = new TreeNode(15); root.left.left = new TreeNode(2); root.left.right = new TreeNode(7); if (isValidBST(root)) { console.log("The BST is valid."); } else { console.log("The BST is not valid."); } ================================================ FILE: Graphs/validate_bst.py ================================================ ''' Write a function that takes in a potentially invalid Binary Search Tree (BST) and returns a boolean representing whether the BST is valid. Explanation: This code defines a Binary Search Tree (BST) struct with an integer value and left and right nodes that can point to other BST nodes. The struct also has a method called ValidateBst() that returns a boolean indicating whether the tree is a valid BST or not. The BST struct has another method called validateBST() that is used by ValidateBst() to check whether the tree is a valid BST or not. The validateBST() method takes in two arguments, min and max, which represent the minimum and maximum values that the current node's value can take in order to be a valid BST. The validateBST() method first checks whether the current node's value is within the valid range determined by the min and max arguments. If not, the method returns false, indicating that the tree is not a valid BST. If the current node's value is within the valid range, the method then recursively calls itself on the left and right child nodes to check whether their values are within their valid ranges. The valid range for the left child node is defined by the minimum value and the parent node's value, while the valid range for the right child node is defined by the parent node's value and the maximum value. If all of the nodes in the tree satisfy the BST property, the method returns true, indicating that the tree is a valid BST. O(n) time | O(d) space - where n is the number of nodes in the BST and d is the depth (height) of the BST ''' import math class BST: def __init__(self, value): self.value = value self.left = None self.right = None # Public method to check if the BST is valid def validate_bst(self): return self._validate_bst(-math.inf, math.inf) # Private recursive helper function to check if the BST is valid def _validate_bst(self, min_val, max_val): # Base case: if the current node's value is outside the allowed range, then the tree is invalid if self.value < min_val or self.value >= max_val: return False # Recursively check the left subtree, making sure all values are less than the current node's value if self.left and not self.left._validate_bst(min_val, self.value): return False # Recursively check the right subtree, making sure all values are greater than or equal to the current node's value if self.right and not self.right._validate_bst(self.value, max_val): return False # If we reach this point, then the tree is valid return True # Example usage: root = BST(10) root.left = BST(5) root.right = BST(15) root.left.left = BST(2) root.left.right = BST(7) if root.validate_bst(): print("The BST is valid.") else: print("The BST is not valid.") ================================================ FILE: Graphs/youngest_common_ancestor.cpp ================================================ /* Write a function that returns the youngest common ancestor to the two descendants. Sample Input: Top ancestor: node A descendantOne: node e descandantTwo: node I Output: node B A / \ B C / \ / \ D E F G / \ H I Explanation: This code snippet implements a solution for finding the youngest common ancestor of two descendants in an ancestral tree. - `type AncestralTree struct` defines the structure of a node in the ancestral tree, which contains a name and a reference to its ancestor node. - `func GetYoungestCommonAncestor` is the main function that takes three parameters: `topAncestor` (the topmost ancestor in the tree), `descendantOne` (the first descendant), and `descendantTwo` (the second descendant). It calculates the depths of the two descendants from the top ancestor and then calls the `backTrackAncestralTree` function with the appropriate parameters. - `func getDescendantDepth` calculates the depth of a descendant node from the top ancestor. It iteratively increments the depth and traverses through the ancestors until reaching the top ancestor. - `func backTrackAncestralTree` is a helper function that backtracks the ancestral tree starting from the lowest descendant until the depths of both descendants are equal. It first adjusts the position of the lowest descendant based on the depth difference between the two descendants. Then, it moves both descendants up the tree in tandem until they reach the same ancestor node, which is the youngest common ancestor. The code assumes that the `topAncestor` provided is a valid ancestor of both `descendantOne` and `descendantTwo`. The `GetYoungestCommonAncestor` function returns the youngest common ancestor found in the ancestral tree. To use this code, you need to create instances of the `AncestralTree` struct representing the ancestral tree and its nodes. You can then call the `GetYoungestCommonAncestor` function with the appropriate parameters to find the youngest common ancestor of two descendants. O(d) time | O(1) space - where d is the depth (height) of the ancestral tree */ #include #include #include using namespace std; class AncestralTree { public: string name; AncestralTree* ancestor; AncestralTree(string name) { this->name = name; this->ancestor = NULL; } }; // Function to calculate the depth of a descendant from the top ancestor int getDescendantDepth(AncestralTree* descendant, AncestralTree* topAncestor) { int depth = 0; while (descendant != topAncestor) { depth++; descendant = descendant->ancestor; } return depth; } // Function to backtrack and find the youngest common ancestor AncestralTree* backTrackAncestralTree(AncestralTree* lowestDescendant, AncestralTree* higherDescendant, int diff) { while (diff > 0) { lowestDescendant = lowestDescendant->ancestor; diff--; } while (lowestDescendant != higherDescendant) { lowestDescendant = lowestDescendant->ancestor; higherDescendant = higherDescendant->ancestor; } return lowestDescendant; } // Function to find the youngest common ancestor of two descendants AncestralTree* getYoungestCommonAncestor(AncestralTree* topAncestor, AncestralTree* descendantOne, AncestralTree* descendantTwo) { int depthOne = getDescendantDepth(descendantOne, topAncestor); int depthTwo = getDescendantDepth(descendantTwo, topAncestor); if (depthOne > depthTwo) { return backTrackAncestralTree(descendantOne, descendantTwo, depthOne - depthTwo); } return backTrackAncestralTree(descendantTwo, descendantOne, depthTwo - depthOne); } int main() { // Create the ancestral tree AncestralTree* topAncestor = new AncestralTree("A"); AncestralTree* B = new AncestralTree("B"); AncestralTree* C = new AncestralTree("C"); AncestralTree* D = new AncestralTree("D"); AncestralTree* E = new AncestralTree("E"); AncestralTree* F = new AncestralTree("F"); AncestralTree* G = new AncestralTree("G"); AncestralTree* H = new AncestralTree("H"); AncestralTree* I = new AncestralTree("I"); // Set up the ancestral relationships // A // / \ // B C // / \ / \ // D E F G // / // H // / // I topAncestor->ancestor = NULL; B->ancestor = topAncestor; C->ancestor = topAncestor; D->ancestor = B; E->ancestor = B; F->ancestor = C; G->ancestor = C; H->ancestor = D; I->ancestor = H; // Find the youngest common ancestor of two descendants AncestralTree* descendantOne = F; AncestralTree* descendantTwo = I; AncestralTree* yca = getYoungestCommonAncestor(topAncestor, descendantOne, descendantTwo); cout << "The youngest common ancestor of " << descendantOne->name << " and " << descendantTwo->name << " is " << yca->name << "." << endl; // Clean up memory delete topAncestor; delete B; delete C; delete D; delete E; delete F; delete G; delete H; delete I; return 0; } ================================================ FILE: Graphs/youngest_common_ancestor.go ================================================ /* Write a function that returns the youngest common ancestor to the two descendants. Sample Input: Top ancestor: node A descendantOne: node e descandantTwo: node I Output: node B A / \ B C / \ / \ D E F G / \ H I Explanation: This code snippet implements a solution for finding the youngest common ancestor of two descendants in an ancestral tree. - `type AncestralTree struct` defines the structure of a node in the ancestral tree, which contains a name and a reference to its ancestor node. - `func GetYoungestCommonAncestor` is the main function that takes three parameters: `topAncestor` (the topmost ancestor in the tree), `descendantOne` (the first descendant), and `descendantTwo` (the second descendant). It calculates the depths of the two descendants from the top ancestor and then calls the `backTrackAncestralTree` function with the appropriate parameters. - `func getDescendantDepth` calculates the depth of a descendant node from the top ancestor. It iteratively increments the depth and traverses through the ancestors until reaching the top ancestor. - `func backTrackAncestralTree` is a helper function that backtracks the ancestral tree starting from the lowest descendant until the depths of both descendants are equal. It first adjusts the position of the lowest descendant based on the depth difference between the two descendants. Then, it moves both descendants up the tree in tandem until they reach the same ancestor node, which is the youngest common ancestor. The code assumes that the `topAncestor` provided is a valid ancestor of both `descendantOne` and `descendantTwo`. The `GetYoungestCommonAncestor` function returns the youngest common ancestor found in the ancestral tree. To use this code, you need to create instances of the `AncestralTree` struct representing the ancestral tree and its nodes. You can then call the `GetYoungestCommonAncestor` function with the appropriate parameters to find the youngest common ancestor of two descendants. O(d) time | O(1) space - where d is the depth (height) of the ancestral tree */ package main import "fmt" type AncestralTree struct { Name string Ancestor *AncestralTree } // GetYoungestCommonAncestor finds the youngest common ancestor of two descendants in an ancestral tree. func GetYoungestCommonAncestor(topAncestor, descendantOne, descendantTwo *AncestralTree) *AncestralTree { // Calculate the depths of the two descendants from the top ancestor depthOne := getDescendantDepth(descendantOne, topAncestor) depthTwo := getDescendantDepth(descendantTwo, topAncestor) if depthOne > depthTwo { // If depthOne is greater, backtrack the ancestral tree from descendantOne to align the depths return backTrackAncestralTree(descendantOne, descendantTwo, depthOne-depthTwo) } // If depthTwo is greater or both depths are equal, backtrack the ancestral tree from descendantTwo // to align the depths return backTrackAncestralTree(descendantTwo, descendantOne, depthTwo-depthOne) } // getDescendantDepth calculates the depth of a descendant node from the top ancestor. func getDescendantDepth(descendant, topAncestor *AncestralTree) int { depth := 0 for descendant != topAncestor { depth++ descendant = descendant.Ancestor } return depth } // backTrackAncestralTree backtracks the ancestral tree to find the youngest common ancestor. func backTrackAncestralTree(lowestDescendant, higherDescendant *AncestralTree, diff int) *AncestralTree { // Adjust the position of the lowest descendant based on the depth difference for diff > 0 { lowestDescendant = lowestDescendant.Ancestor diff-- } // Move both descendants up the tree until they reach the same ancestor node for lowestDescendant != higherDescendant { lowestDescendant = lowestDescendant.Ancestor higherDescendant = higherDescendant.Ancestor } return lowestDescendant } func main() { // Create the ancestral tree topAncestor := &AncestralTree{Name: "A"} B := &AncestralTree{Name: "B", Ancestor: topAncestor} C := &AncestralTree{Name: "C", Ancestor: topAncestor} D := &AncestralTree{Name: "D", Ancestor: B} F := &AncestralTree{Name: "F", Ancestor: C} I := &AncestralTree{Name: "I", Ancestor: D} descendantOne := F descendantTwo := I yca := GetYoungestCommonAncestor(topAncestor, descendantOne, descendantTwo) fmt.Printf("The youngest common ancestor of %s and %s is %s.\n", descendantOne.Name, descendantTwo.Name, yca.Name) } ================================================ FILE: Graphs/youngest_common_ancestor.java ================================================ /* Write a function that returns the youngest common ancestor to the two descendants. Sample Input: Top ancestor: node A descendantOne: node e descandantTwo: node I Output: node B A / \ B C / \ / \ D E F G / \ H I Explanation: This code snippet implements a solution for finding the youngest common ancestor of two descendants in an ancestral tree. - `type AncestralTree struct` defines the structure of a node in the ancestral tree, which contains a name and a reference to its ancestor node. - `func GetYoungestCommonAncestor` is the main function that takes three parameters: `topAncestor` (the topmost ancestor in the tree), `descendantOne` (the first descendant), and `descendantTwo` (the second descendant). It calculates the depths of the two descendants from the top ancestor and then calls the `backTrackAncestralTree` function with the appropriate parameters. - `func getDescendantDepth` calculates the depth of a descendant node from the top ancestor. It iteratively increments the depth and traverses through the ancestors until reaching the top ancestor. - `func backTrackAncestralTree` is a helper function that backtracks the ancestral tree starting from the lowest descendant until the depths of both descendants are equal. It first adjusts the position of the lowest descendant based on the depth difference between the two descendants. Then, it moves both descendants up the tree in tandem until they reach the same ancestor node, which is the youngest common ancestor. The code assumes that the `topAncestor` provided is a valid ancestor of both `descendantOne` and `descendantTwo`. The `GetYoungestCommonAncestor` function returns the youngest common ancestor found in the ancestral tree. To use this code, you need to create instances of the `AncestralTree` struct representing the ancestral tree and its nodes. You can then call the `GetYoungestCommonAncestor` function with the appropriate parameters to find the youngest common ancestor of two descendants. O(d) time | O(1) space - where d is the depth (height) of the ancestral tree */ class AncestralTree { String name; AncestralTree ancestor; public AncestralTree(String name) { this.name = name; this.ancestor = null; } } public class Main { // Function to calculate the depth of a descendant from the top ancestor public static int getDescendantDepth(AncestralTree descendant, AncestralTree topAncestor) { int depth = 0; while (descendant != topAncestor) { depth++; descendant = descendant.ancestor; } return depth; } // Function to backtrack and find the youngest common ancestor public static AncestralTree backTrackAncestralTree(AncestralTree lowestDescendant, AncestralTree higherDescendant, int diff) { while (diff > 0) { lowestDescendant = lowestDescendant.ancestor; diff--; } while (lowestDescendant != higherDescendant) { lowestDescendant = lowestDescendant.ancestor; higherDescendant = higherDescendant.ancestor; } return lowestDescendant; } // Function to find the youngest common ancestor of two descendants public static AncestralTree getYoungestCommonAncestor(AncestralTree topAncestor, AncestralTree descendantOne, AncestralTree descendantTwo) { int depthOne = getDescendantDepth(descendantOne, topAncestor); int depthTwo = getDescendantDepth(descendantTwo, topAncestor); if (depthOne > depthTwo) { return backTrackAncestralTree(descendantOne, descendantTwo, depthOne - depthTwo); } return backTrackAncestralTree(descendantTwo, descendantOne, depthTwo - depthOne); } public static void main(String[] args) { // Create the ancestral tree AncestralTree topAncestor = new AncestralTree("A"); AncestralTree B = new AncestralTree("B"); AncestralTree C = new AncestralTree("C"); AncestralTree D = new AncestralTree("D"); AncestralTree E = new AncestralTree("E"); AncestralTree F = new AncestralTree("F"); AncestralTree G = new AncestralTree("G"); AncestralTree H = new AncestralTree("H"); AncestralTree I = new AncestralTree("I"); // Set up the ancestral relationships // A // / \ // B C // / \ / \ // D E F G // / // H // / // I topAncestor.ancestor = null; B.ancestor = topAncestor; C.ancestor = topAncestor; D.ancestor = B; E.ancestor = B; F.ancestor = C; G.ancestor = C; H.ancestor = D; I.ancestor = H; // Find the youngest common ancestor of two descendants AncestralTree descendantOne = F; AncestralTree descendantTwo = I; AncestralTree yca = getYoungestCommonAncestor(topAncestor, descendantOne, descendantTwo); System.out.println("The youngest common ancestor of " + descendantOne.name + " and " + descendantTwo.name + " is " + yca.name); } } ================================================ FILE: Graphs/youngest_common_ancestor.js ================================================ /* Write a function that returns the youngest common ancestor to the two descendants. Sample Input: Top ancestor: node A descendantOne: node e descandantTwo: node I Output: node B A / \ B C / \ / \ D E F G / \ H I Explanation: This code snippet implements a solution for finding the youngest common ancestor of two descendants in an ancestral tree. - `type AncestralTree struct` defines the structure of a node in the ancestral tree, which contains a name and a reference to its ancestor node. - `func GetYoungestCommonAncestor` is the main function that takes three parameters: `topAncestor` (the topmost ancestor in the tree), `descendantOne` (the first descendant), and `descendantTwo` (the second descendant). It calculates the depths of the two descendants from the top ancestor and then calls the `backTrackAncestralTree` function with the appropriate parameters. - `func getDescendantDepth` calculates the depth of a descendant node from the top ancestor. It iteratively increments the depth and traverses through the ancestors until reaching the top ancestor. - `func backTrackAncestralTree` is a helper function that backtracks the ancestral tree starting from the lowest descendant until the depths of both descendants are equal. It first adjusts the position of the lowest descendant based on the depth difference between the two descendants. Then, it moves both descendants up the tree in tandem until they reach the same ancestor node, which is the youngest common ancestor. The code assumes that the `topAncestor` provided is a valid ancestor of both `descendantOne` and `descendantTwo`. The `GetYoungestCommonAncestor` function returns the youngest common ancestor found in the ancestral tree. To use this code, you need to create instances of the `AncestralTree` struct representing the ancestral tree and its nodes. You can then call the `GetYoungestCommonAncestor` function with the appropriate parameters to find the youngest common ancestor of two descendants. O(d) time | O(1) space - where d is the depth (height) of the ancestral tree */ class AncestralTree { constructor(name) { this.name = name; this.ancestor = null; } } // Function to calculate the depth of a descendant from the top ancestor function getDescendantDepth(descendant, topAncestor) { let depth = 0; while (descendant !== topAncestor) { depth += 1; descendant = descendant.ancestor; } return depth; } // Function to backtrack and find the youngest common ancestor function backTrackAncestralTree(lowestDescendant, higherDescendant, diff) { while (diff > 0) { lowestDescendant = lowestDescendant.ancestor; diff -= 1; } while (lowestDescendant !== higherDescendant) { lowestDescendant = lowestDescendant.ancestor; higherDescendant = higherDescendant.ancestor; } return lowestDescendant; } // Function to find the youngest common ancestor of two descendants function getYoungestCommonAncestor(topAncestor, descendantOne, descendantTwo) { const depthOne = getDescendantDepth(descendantOne, topAncestor); const depthTwo = getDescendantDepth(descendantTwo, topAncestor); if (depthOne > depthTwo) { return backTrackAncestralTree( descendantOne, descendantTwo, depthOne - depthTwo ); } return backTrackAncestralTree( descendantTwo, descendantOne, depthTwo - depthOne ); } // Create the ancestral tree const topAncestor = new AncestralTree("A"); const B = new AncestralTree("B"); const C = new AncestralTree("C"); const D = new AncestralTree("D"); const E = new AncestralTree("E"); const F = new AncestralTree("F"); const G = new AncestralTree("G"); const H = new AncestralTree("H"); const I = new AncestralTree("I"); // Set up the ancestral relationships // A // / \ // B C // / \ / \ // D E F G // / // H // / // I topAncestor.ancestor = null; B.ancestor = topAncestor; C.ancestor = topAncestor; D.ancestor = B; E.ancestor = B; F.ancestor = C; G.ancestor = C; H.ancestor = D; I.ancestor = H; // Find the youngest common ancestor of two descendants const descendantOne = F; const descendantTwo = I; const yca = getYoungestCommonAncestor( topAncestor, descendantOne, descendantTwo ); console.log( "The youngest common ancestor of", descendantOne.name, "and", descendantTwo.name, "is", yca.name ); ================================================ FILE: Graphs/youngest_common_ancestor.py ================================================ ''' Write a function that returns the youngest common ancestor to the two descendants. Sample Input: Top ancestor: node A descendantOne: node e descandantTwo: node I Output: node B A / \ B C / \ / \ D E F G / \ H I Explanation: This code snippet implements a solution for finding the youngest common ancestor of two descendants in an ancestral tree. - `type AncestralTree struct` defines the structure of a node in the ancestral tree, which contains a name and a reference to its ancestor node. - `func GetYoungestCommonAncestor` is the main function that takes three parameters: `topAncestor` (the topmost ancestor in the tree), `descendantOne` (the first descendant), and `descendantTwo` (the second descendant). It calculates the depths of the two descendants from the top ancestor and then calls the `backTrackAncestralTree` function with the appropriate parameters. - `func getDescendantDepth` calculates the depth of a descendant node from the top ancestor. It iteratively increments the depth and traverses through the ancestors until reaching the top ancestor. - `func backTrackAncestralTree` is a helper function that backtracks the ancestral tree starting from the lowest descendant until the depths of both descendants are equal. It first adjusts the position of the lowest descendant based on the depth difference between the two descendants. Then, it moves both descendants up the tree in tandem until they reach the same ancestor node, which is the youngest common ancestor. The code assumes that the `topAncestor` provided is a valid ancestor of both `descendantOne` and `descendantTwo`. The `GetYoungestCommonAncestor` function returns the youngest common ancestor found in the ancestral tree. To use this code, you need to create instances of the `AncestralTree` struct representing the ancestral tree and its nodes. You can then call the `GetYoungestCommonAncestor` function with the appropriate parameters to find the youngest common ancestor of two descendants. O(d) time | O(1) space - where d is the depth (height) of the ancestral tree ''' class AncestralTree: def __init__(self, name): self.name = name self.ancestor = None # Function to calculate the depth of a descendant from the top ancestor def get_descendant_depth(descendant, top_ancestor): depth = 0 while descendant != top_ancestor: depth += 1 descendant = descendant.ancestor return depth # Function to backtrack and find the youngest common ancestor def back_track_ancestral_tree(lowest_descendant, higher_descendant, diff): while diff > 0: lowest_descendant = lowest_descendant.ancestor diff -= 1 while lowest_descendant != higher_descendant: lowest_descendant = lowest_descendant.ancestor higher_descendant = higher_descendant.ancestor return lowest_descendant # Function to find the youngest common ancestor of two descendants def get_youngest_common_ancestor(top_ancestor, descendant_one, descendant_two): depth_one = get_descendant_depth(descendant_one, top_ancestor) depth_two = get_descendant_depth(descendant_two, top_ancestor) if depth_one > depth_two: return back_track_ancestral_tree(descendant_one, descendant_two, depth_one - depth_two) return back_track_ancestral_tree(descendant_two, descendant_one, depth_two - depth_one) # Create the ancestral tree top_ancestor = AncestralTree("A") B = AncestralTree("B") C = AncestralTree("C") D = AncestralTree("D") E = AncestralTree("E") F = AncestralTree("F") G = AncestralTree("G") H = AncestralTree("H") I = AncestralTree("I") # Set up the ancestral relationships # A # / \ # B C # / \ / \ # D E F G # / # H # / # I top_ancestor.ancestor = None B.ancestor = top_ancestor C.ancestor = top_ancestor D.ancestor = B E.ancestor = B F.ancestor = C G.ancestor = C H.ancestor = D I.ancestor = H # Find the youngest common ancestor of two descendants descendant_one = F descendant_two = I yca = get_youngest_common_ancestor(top_ancestor, descendant_one, descendant_two) print("The youngest common ancestor of", descendant_one.name, "and", descendant_two.name, "is", yca.name) ================================================ FILE: Greedy/coin_change.go ================================================ /* The coin change problem is a classic algorithmic problem, where given a target amount and a set of coin denominations, we need to find the minimum number of coins required to make up that amount. In this implementation, we start by sorting the array of coin denominations in descending order. We then iterate through the array from largest to smallest denomination, and for each denomination, we repeatedly subtract the largest possible multiple of that denomination from the target amount until we reach 0. The time complexity of this algorithm is O(n log n) due to the sorting step, where n is the number of coin denominations. However, the space complexity is O(1) since we are only using a constant amount of extra space. For the sample input coins := []int{1, 5, 10, 25} and target := 36, the output should be Minimum number of coins required to make 36 cents: 3. */ package main import "fmt" func coinChangeGreedy(coins []int, target int) int { count := 0 for i := len(coins) - 1; i >= 0; i-- { for target >= coins[i] { target -= coins[i] count++ } } return count } func main() { coins := []int{1, 5, 10, 25} target := 36 result := coinChangeGreedy(coins, target) fmt.Printf("Minimum number of coins required to make %d cents: %d\n", target, result) } ================================================ FILE: Greedy/coin_change.java ================================================ package Greedy; /* * write a code in java with comments foor As a first example, * we consider a problem where we are given a set of coins and our task is to * form a sum of money n using the coins. The values of the coins are coins = {c1, c2,..., ck}, * and each coin can be used as many times we want. What is the minimum number of coins needed? * For example, if the coins are the euro coins (in cents) {1,2,5,10,20,50,100,200} and n = 520, * we need at least four coins. The optimal solution is to select coins 200 + 200 + 100 + 20 whose sum is 520 */ import java.util.Arrays; public class CoinChange { /** * This method finds the minimum number of coins required to make change for a given amount of money using a given set of coins. * * coins The set of coins that can be used to make change. * n The amount of money to make change for. * @return The minimum number of coins required to make change for n. */ public static int minCoins(int[] coins, int n) { // Create an array to store the minimum number of coins required to make change for each amount of money. int[] dp = new int[n + 1]; // Initialize the array to Integer.MAX_VALUE. This means that we do not know the minimum number of coins required to make change for any amount of money yet. Arrays.fill(dp, Integer.MAX_VALUE); // Set the minimum number of coins required to make change for 0 to 0. dp[0] = 0; // Iterate over the coins array. for (int i = 0; i < coins.length; i++) { // Iterate over the dp array. for (int j = coins[i]; j <= n; j++) { // Update the dp[j] element to be the minimum of dp[j] and dp[j - coins[i]] + 1. dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1); } } // Return the minimum number of coins required to make change for n. return dp[n]; } public static void main(String[] args) { // Create a set of coins. int[] coins = {1, 2, 5, 10, 20, 50, 100, 200}; // The amount of money to make change for. int n = 520; // Find the minimum number of coins required to make change for n. int minCoins = minCoins(coins, n); // Print the minimum number of coins. System.out.println("The minimum number of coins required to make change for " + n + " cents is " + minCoins); } } // The minimum number of coins required to make change for 520 cents is 4 ================================================ FILE: Greedy/coin_change.js ================================================ function coinChangeGreedy(s, l) { // First step: sort l in descending order l.sort((a, b) => b - a); let r = []; // Result list let sumr = 0; // Keep track of the current sum of r to avoid iterating over r to calculate the sum for (let coin of l) { if (sumr === s) { // The sum of r is the target sum s break; } let n = Math.floor((s - sumr) / coin); // Calculate the max n with sum(r) + l[i] * n <= s for (let i = 0; i < n; i++) { // Append n elements of this coin to r r.push(coin); } sumr += coin * n; // Update the sum of r } if (sumr !== s) { // The target sum s was not reached return false; } return r; } // SAMPLE 1 (optimal) console.log(coinChangeGreedy(60, [5, 10, 25])); // SAMPLE 2 (not optimal) console.log(coinChangeGreedy(50, [25, 10, 30, 5])); // SAMPLE 3 (fails) console.log(coinChangeGreedy(50, [25, 30])); ================================================ FILE: Greedy/coin_change.py ================================================ """TASK Greedy: provide a greedy solution for the coin change problem A greedy algorithm is trying to make the best local decisions, without caring about future decisions. That means that a greedy algorithm is faster than better approaches but it has not always the best solution In some case a greedy approach does not find a solution at all. The coin change problem: -> given sum s -> given list of possible coin types l The output has to be a list r, which's elements represent coins from coin types from l. r[i] is in l for every i with [0 <= i < length r]; The sum of r has to be s; Furthermore the length of r has to be minimal; In other words we need to calculate a way to reach the sum s with minimal coins from coin types l SAMPLE DATA Take a look on the following Samples Sample A (good for greedy): s = 60 l = [25, 10, 5]; Best output r = [25,25,10] (take 2 coins from type 25 and one 10, we get 60 with only three coins) Sample B (bad for greedy): s = 50; l = [30, 25, 10, 5] Best output r = [25,25] (take 2 coins from type 25, we get 50 with only two coins) Sample C (greedy fails) s = 50 l = [30, 25] Best output r = [25,25] (take 2 coins from type 25, we get 50 with only two coins)decisions APPROACH The greedy approach is very simple 1. We have to sort l in descendig order 2. Iterate over the sorted list, in each step: 1. if the sum of r is equal to s, break 2. add n coins of type l[i] to r where n is maximal and sum(r) + l[i]*n <= s (add as many as we can from this coins type) 3. if the sum of r is equal to s, return r; otherwise the greedy algorithm has failed SAMPLE A 1. l in descending order l = [25,10,5] 2. loop over l (element 25, r=[]) 3. take 2 25 coins because 0+25*2 <= 60 but not 0+25*3 <= 60 (sum(r)+l[i]*n <= s) 4. next iteration (elment 10, r=[25,25]) 5. take 1 10 coin because 50+10*1 <= 60 (r=[25,25,10]) 6. sumr == s => break 7. return r SAMPLE B the greedy takes one 30 coin, a 25 coin cannot be taken (30+25 > 25), but 2 10 coins r = [30, 10, 10] the number of coins is 3, but the optimal solution is 2 [25,25]. A greedy algorithm does not provide the optimnal solution SAMPLE C the greedy takes the 30 coin. But this is a bad decision for the future, because the 25 coin cannot be taken (30+25 > 50) that means that the greedy does not provide any solution because a bad decision was made. To get this solution you have to do a dynamic programmic approach COMPLEXITY With sorting (random list l): sorting takes log(l)*l time. looping over l takes O(l) time looping over n takes O(n) time thats a time complexity of O(l*logl + l*n) n is dependend on s and l (for a big s and small l values n gets big, for heavily skewed l values too [1000000,1] for s = 1999999) that means that time complexity class will be O(n ^ 2) in worst case (because n is linear to s) If it is assumed that n is always in a certain range (i.e below 10), the time complexity class will be O(n*logn) sorting has O(l) space complexity the space complexity for the whole algorithm is O(l+r) with r being the length of r. Best case space complexity class is O(n) where r is assumed very small (linear or better) Otherwise the space complexity is O(r). r is dependend on s (linear): s = 9 l = [10, 1] r = [1,1,1,1,1,1,1,1,1] which is O(s) that means that space complexity class is O(n) Without sorting (sorted list l): looping over l takes O(l) time looping over n takes O(n) time thats a time complexity of O(l*n) n is dependend on s and l (linear) that means that time complexity class will be O(n ^ 2) in worst case If it is assumed that n is always in a certain range (i.e below 10), the time complexity class will be O(n) the space complexity for the whole algorithm is O(r) with r being the length of r. Best case space complexity class is O(1) where r is assumed very small (linear or better) Otherwise the space complexity is O(n) r is dependend on s (linear) that means that space complexity class is O(n) (or O(1) in best case) """ def coinChangeGreedy(s:int, l:list[int]): #first step: sort l descending l.sort(reverse=True) #uses some log(n)*n algorithm r = [] #return list sumr:int = 0 #keep track of the current sum of r to avoid iterating over r to calculate the sum for coin in l: if(sumr == s): #the sum of r is the target sum s break n = int((s - sumr)/coin) #calculate the max n with sum(r) + l[i]*n <= s for i in range(n): #append n elements of this coin to r r.append(coin) sumr += coin*n #update the r sum if(sumr != s): #the target sum s was not reached return False return r #without sorting def coinChangeGreedySorted(s:int, l:list[int]): r = [] #return list sumr:int = 0 #keep track of the current sum of r to avoid iterating over r to calculate the sum for coin in l: if(sumr == s): #the sum of r is the target sum s break n = int((s - sumr)/coin) #calculate the max n with sum(r) + l[i]*n <= s for i in range(n): #append n elements of this coin to r r.append(coin) sumr += coin*n #update the r sum if(sumr != s): #the target sum s was not reached return False return r #SAMPLE 1 (optimal) print(coinChangeGreedy(60, [5, 10, 25])) #SAMPLE 2 (not optimal) print(coinChangeGreedy(50, [25, 10, 30, 5])) #SAMPLE 3 (fails) print(coinChangeGreedy(50, [25, 30])) print() #SAMPLE 1 (optimal) print(coinChangeGreedySorted(60, [25, 10, 5])) #SAMPLE 2 (not optimal) print(coinChangeGreedySorted(50, [30, 25, 10, 5])) #SAMPLE 3 (fails) print(coinChangeGreedySorted(50, [30, 25])) ================================================ FILE: Greedy/task_assignment.cpp ================================================ /* You're given an integer k representing a number of workers and an array of positive integers representing durations of tasks that must be completed by the workers. Specifically, each worker must complete two unique tasks and can only work on one task at a time. The number of tasks will always be equal to 2k such that each worker always has exactly two tasks to complete. All tasks are independent of one another and can be completed in any order. Workers will complete their assigned tasks in parallel, and the time taken to complete all tasks will be equal to the time taken to complete the longest pair of tasks (see the sample output for an explanation). Write a function that returns the optimal assignment of tasks to each worker such that the tasks are completed as fast as possible. Your function should return a list of pairs, where each pair stores the indices of the tasks that should be completed by one worker. The pairs should be in the following format: [task1, task2] , where the order of task1 and task2 doesn't matter. Your function can return the pairs in any order. If multiple optimal assignments exist, any correct answer will be accepted. Sample Input: K = 3 tasks : [1, 3, 5, 3, 1, 4] Output: [ [0, 2], [4, 5], [1, 3] ] Explanation: The code snippet is an implementation of the "Task Assignment" algorithm. It pairs tasks from a list based on their durations. The goal is to assign tasks to workers in a way that minimizes the total execution time. - The function `TaskAssignment` takes two parameters: `k` (the number of workers) and `tasks` (a list of task durations). - It initializes variables for `pairedTasks`, which will store the paired tasks, and `taskDurationToIndices`, which is a map that will store the indices of each task duration. - The `getTaskDurationToIndices` function is called to create the map `taskDurationToIndices`, which maps each task duration to a list of indices. - The task durations are sorted in ascending order using `sort.Ints(tasks)`. This allows us to pair the shortest and longest durations together efficiently. - The loop runs `k` times to pair tasks. In each iteration: - The shortest duration task is selected and its index is retrieved from `taskDurationToIndices`. - The index of the selected task is removed from the list of indices to ensure it is not paired again. - The longest duration task is selected in a similar manner, but from the opposite end of the sorted task durations. - The paired task indices are added to `pairedTasks`. - Finally, `pairedTasks` is returned as the result. The `getTaskDurationToIndices` function is a helper function that creates a map `taskDurationToIndices`, which maps each task duration to a list of indices where tasks with that duration occur in the `tasks` list. Overall, the algorithm pairs tasks based on their durations while considering the shortest and longest durations together. The result is a list of paired task indices. O(nlog(n)) time | O(n) space - where n is the number of tasks */ #include #include #include using namespace std; // Function to create a map that maps each task duration to a list of indices // where tasks with that duration occur in the given tasks list. unordered_map> getTaskDurationToIndices(vector& tasks) { unordered_map> taskDurationToIndices; for (int idx = 0; idx < tasks.size(); idx++) { int taskDuration = tasks[idx]; taskDurationToIndices[taskDuration].push_back(idx); } return taskDurationToIndices; } // Function to pair tasks from the given list based on their durations. // It returns a vector of paired task indices. vector> TaskAssignment(int k, vector& tasks) { // Initialize variables vector> pairedTasks; unordered_map> taskDurationToIndices = getTaskDurationToIndices(tasks); sort(tasks.begin(), tasks.end()); int task1Idx, task2Idx; // Pair tasks for (int idx = 0; idx < k; idx++) { // Get the shortest duration task int task1Duration = tasks[idx]; vector& indicesWithTask1Duration = taskDurationToIndices[task1Duration]; task1Idx = indicesWithTask1Duration.back(); indicesWithTask1Duration.pop_back(); // Get the longest duration task int task2SortedIndex = tasks.size() - 1 - idx; int task2Duration = tasks[task2SortedIndex]; vector& indicesWithTask2Duration = taskDurationToIndices[task2Duration]; task2Idx = indicesWithTask2Duration.back(); indicesWithTask2Duration.pop_back(); // Add the paired tasks to the result pairedTasks.push_back({task1Idx, task2Idx}); } return pairedTasks; } ================================================ FILE: Greedy/task_assignment.go ================================================ /* You're given an integer k representing a number of workers and an array of positive integers representing durations of tasks that must be completed by the workers. Specifically, each worker must complete two unique tasks and can only work on one task at a time. The number of tasks will always be equal to 2k such that each worker always has exactly two tasks to complete. All tasks are independent of one another and can be completed in any order. Workers will complete their assigned tasks in parallel, and the time taken to complete all tasks will be equal to the time taken to complete the longest pair of tasks (see the sample output for an explanation). Write a function that returns the optimal assignment of tasks to each worker such that the tasks are completed as fast as possible. Your function should return a list of pairs, where each pair stores the indices of the tasks that should be completed by one worker. The pairs should be in the following format: [task1, task2] , where the order of task1 and task2 doesn't matter. Your function can return the pairs in any order. If multiple optimal assignments exist, any correct answer will be accepted. Sample Input: K = 3 tasks : [1, 3, 5, 3, 1, 4] Output: [ [0, 2], [4, 5], [1, 3] ] Explanation: The code snippet is an implementation of the "Task Assignment" algorithm. It pairs tasks from a list based on their durations. The goal is to assign tasks to workers in a way that minimizes the total execution time. - The function `TaskAssignment` takes two parameters: `k` (the number of workers) and `tasks` (a list of task durations). - It initializes variables for `pairedTasks`, which will store the paired tasks, and `taskDurationToIndices`, which is a map that will store the indices of each task duration. - The `getTaskDurationToIndices` function is called to create the map `taskDurationToIndices`, which maps each task duration to a list of indices. - The task durations are sorted in ascending order using `sort.Ints(tasks)`. This allows us to pair the shortest and longest durations together efficiently. - The loop runs `k` times to pair tasks. In each iteration: - The shortest duration task is selected and its index is retrieved from `taskDurationToIndices`. - The index of the selected task is removed from the list of indices to ensure it is not paired again. - The longest duration task is selected in a similar manner, but from the opposite end of the sorted task durations. - The paired task indices are added to `pairedTasks`. - Finally, `pairedTasks` is returned as the result. The `getTaskDurationToIndices` function is a helper function that creates a map `taskDurationToIndices`, which maps each task duration to a list of indices where tasks with that duration occur in the `tasks` list. Overall, the algorithm pairs tasks based on their durations while considering the shortest and longest durations together. The result is a list of paired task indices. O(nlog(n)) time | O(n) space - where n is the number of tasks */ import "sort" // TaskAssignment pairs tasks from the given list based on their durations. // It returns a list of paired task indices. func TaskAssignment(k int, tasks []int) [][]int { // Initialize variables pairedTasks := make([][]int, 0) taskDurationToIndices := getTaskDurationToIndices(tasks) sort.Ints(tasks) var task1Idx, task2Idx int // Pair tasks for idx := 0; idx < k; idx++ { // Get the shortest duration task task1Duration := tasks[idx] indicesWithTask1Duration := taskDurationToIndices[task1Duration] task1Idx, taskDurationToIndices[task1Duration] = indicesWithTask1Duration[len(indicesWithTask1Duration)-1], indicesWithTask1Duration[:len(indicesWithTask1Duration)-1] // Get the longest duration task task2SortedIndex := len(tasks) - 1 - idx task2Duration := tasks[task2SortedIndex] indicesWithTask2Duration := taskDurationToIndices[task2Duration] task2Idx, taskDurationToIndices[task2Duration] = indicesWithTask2Duration[len(indicesWithTask2Duration)-1], indicesWithTask2Duration[:len(indicesWithTask2Duration)-1] // Add the paired tasks to the result pairedTasks = append(pairedTasks, []int{task1Idx, task2Idx}) } return pairedTasks } // getTaskDurationToIndices creates a map that maps each task duration to a list of indices // where tasks with that duration occur in the given tasks list. func getTaskDurationToIndices(tasks []int) map[int][]int { taskDurationToIndices := map[int][]int{} for idx := range tasks { taskDuration := tasks[idx] taskDurationToIndices[taskDuration] = append(taskDurationToIndices[taskDuration], idx) } return taskDurationToIndices } ================================================ FILE: Greedy/task_assignment.java ================================================ /* You're given an integer k representing a number of workers and an array of positive integers representing durations of tasks that must be completed by the workers. Specifically, each worker must complete two unique tasks and can only work on one task at a time. The number of tasks will always be equal to 2k such that each worker always has exactly two tasks to complete. All tasks are independent of one another and can be completed in any order. Workers will complete their assigned tasks in parallel, and the time taken to complete all tasks will be equal to the time taken to complete the longest pair of tasks (see the sample output for an explanation). Write a function that returns the optimal assignment of tasks to each worker such that the tasks are completed as fast as possible. Your function should return a list of pairs, where each pair stores the indices of the tasks that should be completed by one worker. The pairs should be in the following format: [task1, task2] , where the order of task1 and task2 doesn't matter. Your function can return the pairs in any order. If multiple optimal assignments exist, any correct answer will be accepted. Sample Input: K = 3 tasks : [1, 3, 5, 3, 1, 4] Output: [ [0, 2], [4, 5], [1, 3] ] Explanation: The code snippet is an implementation of the "Task Assignment" algorithm. It pairs tasks from a list based on their durations. The goal is to assign tasks to workers in a way that minimizes the total execution time. - The function `TaskAssignment` takes two parameters: `k` (the number of workers) and `tasks` (a list of task durations). - It initializes variables for `pairedTasks`, which will store the paired tasks, and `taskDurationToIndices`, which is a map that will store the indices of each task duration. - The `getTaskDurationToIndices` function is called to create the map `taskDurationToIndices`, which maps each task duration to a list of indices. - The task durations are sorted in ascending order using `sort.Ints(tasks)`. This allows us to pair the shortest and longest durations together efficiently. - The loop runs `k` times to pair tasks. In each iteration: - The shortest duration task is selected and its index is retrieved from `taskDurationToIndices`. - The index of the selected task is removed from the list of indices to ensure it is not paired again. - The longest duration task is selected in a similar manner, but from the opposite end of the sorted task durations. - The paired task indices are added to `pairedTasks`. - Finally, `pairedTasks` is returned as the result. The `getTaskDurationToIndices` function is a helper function that creates a map `taskDurationToIndices`, which maps each task duration to a list of indices where tasks with that duration occur in the `tasks` list. Overall, the algorithm pairs tasks based on their durations while considering the shortest and longest durations together. The result is a list of paired task indices. O(nlog(n)) time | O(n) space - where n is the number of tasks */ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; public class TaskAssignment { public static int[][] taskAssignment(int k, int[] tasks) { // Initialize variables List pairedTasks = new ArrayList<>(); Map> taskDurationToIndices = getTaskDurationToIndices(tasks); Arrays.sort(tasks); int task1Idx, task2Idx; // Pair tasks for (int idx = 0; idx < k; idx++) { // Get the shortest duration task int task1Duration = tasks[idx]; List indicesWithTask1Duration = taskDurationToIndices.get(task1Duration); task1Idx = indicesWithTask1Duration.remove(indicesWithTask1Duration.size() - 1); // Get the longest duration task int task2SortedIndex = tasks.length - 1 - idx; int task2Duration = tasks[task2SortedIndex]; List indicesWithTask2Duration = taskDurationToIndices.get(task2Duration); task2Idx = indicesWithTask2Duration.remove(indicesWithTask2Duration.size() - 1); // Add the paired tasks to the result pairedTasks.add(new int[]{task1Idx, task2Idx}); } return pairedTasks.toArray(new int[0][2]); } private static Map> getTaskDurationToIndices(int[] tasks) { Map> taskDurationToIndices = new HashMap<>(); for (int idx = 0; idx < tasks.length; idx++) { int taskDuration = tasks[idx]; List indices = taskDurationToIndices.getOrDefault(taskDuration, new ArrayList<>()); indices.add(idx); taskDurationToIndices.put(taskDuration, indices); } return taskDurationToIndices; } public static void main(String[] args) { int k = 3; int[] tasks = {1, 3, 5, 3, 1, 4}; int[][] pairedTasks = taskAssignment(k, tasks); // Print the paired tasks for (int[] pair : pairedTasks) { System.out.println(Arrays.toString(pair)); } } } ================================================ FILE: Greedy/task_assignment.js ================================================ /* You're given an integer k representing a number of workers and an array of positive integers representing durations of tasks that must be completed by the workers. Specifically, each worker must complete two unique tasks and can only work on one task at a time. The number of tasks will always be equal to 2k such that each worker always has exactly two tasks to complete. All tasks are independent of one another and can be completed in any order. Workers will complete their assigned tasks in parallel, and the time taken to complete all tasks will be equal to the time taken to complete the longest pair of tasks (see the sample output for an explanation). Write a function that returns the optimal assignment of tasks to each worker such that the tasks are completed as fast as possible. Your function should return a list of pairs, where each pair stores the indices of the tasks that should be completed by one worker. The pairs should be in the following format: [task1, task2] , where the order of task1 and task2 doesn't matter. Your function can return the pairs in any order. If multiple optimal assignments exist, any correct answer will be accepted. Sample Input: K = 3 tasks : [1, 3, 5, 3, 1, 4] Output: [ [0, 2], [4, 5], [1, 3] ] Explanation: The code snippet is an implementation of the "Task Assignment" algorithm. It pairs tasks from a list based on their durations. The goal is to assign tasks to workers in a way that minimizes the total execution time. - The function `TaskAssignment` takes two parameters: `k` (the number of workers) and `tasks` (a list of task durations). - It initializes variables for `pairedTasks`, which will store the paired tasks, and `taskDurationToIndices`, which is a map that will store the indices of each task duration. - The `getTaskDurationToIndices` function is called to create the map `taskDurationToIndices`, which maps each task duration to a list of indices. - The task durations are sorted in ascending order using `sort.Ints(tasks)`. This allows us to pair the shortest and longest durations together efficiently. - The loop runs `k` times to pair tasks. In each iteration: - The shortest duration task is selected and its index is retrieved from `taskDurationToIndices`. - The index of the selected task is removed from the list of indices to ensure it is not paired again. - The longest duration task is selected in a similar manner, but from the opposite end of the sorted task durations. - The paired task indices are added to `pairedTasks`. - Finally, `pairedTasks` is returned as the result. The `getTaskDurationToIndices` function is a helper function that creates a map `taskDurationToIndices`, which maps each task duration to a list of indices where tasks with that duration occur in the `tasks` list. Overall, the algorithm pairs tasks based on their durations while considering the shortest and longest durations together. The result is a list of paired task indices. O(nlog(n)) time | O(n) space - where n is the number of tasks */ function taskAssignment(k, tasks) { // Initialize variables const pairedTasks = []; const taskDurationToIndices = getTaskDurationToIndices(tasks); tasks.sort((a, b) => a - b); let task1Idx, task2Idx; // Pair tasks for (let idx = 0; idx < k; idx++) { // Get the shortest duration task const task1Duration = tasks[idx]; const indicesWithTask1Duration = taskDurationToIndices[task1Duration]; task1Idx = indicesWithTask1Duration.pop(); // Get the longest duration task const task2SortedIndex = tasks.length - 1 - idx; const task2Duration = tasks[task2SortedIndex]; const indicesWithTask2Duration = taskDurationToIndices[task2Duration]; task2Idx = indicesWithTask2Duration.pop(); // Add the paired tasks to the result pairedTasks.push([task1Idx, task2Idx]); } return pairedTasks; } function getTaskDurationToIndices(tasks) { const taskDurationToIndices = {}; for (let idx = 0; idx < tasks.length; idx++) { const taskDuration = tasks[idx]; if (!taskDurationToIndices[taskDuration]) { taskDurationToIndices[taskDuration] = []; } taskDurationToIndices[taskDuration].push(idx); } return taskDurationToIndices; } const k = 3; const tasks = [1, 3, 5, 3, 1, 4]; const pairedTasks = taskAssignment(k, tasks); // Print the paired tasks pairedTasks.forEach((pair) => { console.log(pair); }); ================================================ FILE: Greedy/task_assignment.py ================================================ ''' You're given an integer k representing a number of workers and an array of positive integers representing durations of tasks that must be completed by the workers. Specifically, each worker must complete two unique tasks and can only work on one task at a time. The number of tasks will always be equal to 2k such that each worker always has exactly two tasks to complete. All tasks are independent of one another and can be completed in any order. Workers will complete their assigned tasks in parallel, and the time taken to complete all tasks will be equal to the time taken to complete the longest pair of tasks (see the sample output for an explanation). Write a function that returns the optimal assignment of tasks to each worker such that the tasks are completed as fast as possible. Your function should return a list of pairs, where each pair stores the indices of the tasks that should be completed by one worker. The pairs should be in the following format: [task1, task2] , where the order of task1 and task2 doesn't matter. Your function can return the pairs in any order. If multiple optimal assignments exist, any correct answer will be accepted. Sample Input: K = 3 tasks : [1, 3, 5, 3, 1, 4] Output: [ [0, 2], [4, 5], [1, 3] ] Explanation: The code snippet is an implementation of the "Task Assignment" algorithm. It pairs tasks from a list based on their durations. The goal is to assign tasks to workers in a way that minimizes the total execution time. - The function `TaskAssignment` takes two parameters: `k` (the number of workers) and `tasks` (a list of task durations). - It initializes variables for `pairedTasks`, which will store the paired tasks, and `taskDurationToIndices`, which is a map that will store the indices of each task duration. - The `getTaskDurationToIndices` function is called to create the map `taskDurationToIndices`, which maps each task duration to a list of indices. - The task durations are sorted in ascending order using `sort.Ints(tasks)`. This allows us to pair the shortest and longest durations together efficiently. - The loop runs `k` times to pair tasks. In each iteration: - The shortest duration task is selected and its index is retrieved from `taskDurationToIndices`. - The index of the selected task is removed from the list of indices to ensure it is not paired again. - The longest duration task is selected in a similar manner, but from the opposite end of the sorted task durations. - The paired task indices are added to `pairedTasks`. - Finally, `pairedTasks` is returned as the result. The `getTaskDurationToIndices` function is a helper function that creates a map `taskDurationToIndices`, which maps each task duration to a list of indices where tasks with that duration occur in the `tasks` list. Overall, the algorithm pairs tasks based on their durations while considering the shortest and longest durations together. The result is a list of paired task indices. O(nlog(n)) time | O(n) space - where n is the number of tasks ''' def task_assignment(k, tasks): # Initialize variables paired_tasks = [] task_duration_to_indices = get_task_duration_to_indices(tasks) tasks.sort() for idx in range(k): # Get the shortest duration task task1_duration = tasks[idx] indices_with_task1_duration = task_duration_to_indices[task1_duration] task1_idx = indices_with_task1_duration.pop() # Get the longest duration task task2_sorted_index = len(tasks) - 1 - idx task2_duration = tasks[task2_sorted_index] indices_with_task2_duration = task_duration_to_indices[task2_duration] task2_idx = indices_with_task2_duration.pop() # Add the paired tasks to the result paired_tasks.append([task1_idx, task2_idx]) return paired_tasks def get_task_duration_to_indices(tasks): task_duration_to_indices = {} for idx in range(len(tasks)): task_duration = tasks[idx] if task_duration not in task_duration_to_indices: task_duration_to_indices[task_duration] = [] task_duration_to_indices[task_duration].append(idx) return task_duration_to_indices # Example usage k = 3 tasks = [1, 3, 5, 3, 1, 4] paired_tasks = task_assignment(k, tasks) # Print the paired tasks for pair in paired_tasks: print(pair) ================================================ FILE: Hash Table/Bloomfilter.cpp ================================================ #include #include #include #include class BloomFilter { private: int size; // Size of the Bloom Filter bitset std::vector> hash_functions; std::bitset<1000000> bitset; // Bitset to represent the Bloom Filter public: // Constructor to initialize the Bloom Filter with a given size and hash functions BloomFilter(int size, std::vector> hash_functions) : size(size), hash_functions(hash_functions) {} // Function to add an element to the Bloom Filter void add(const std::string& element) { for (const auto& hash_function : hash_functions) { size_t index = hash_function(element) % size; bitset.set(index); } } // Function to check if an element may exist in the Bloom Filter bool contains(const std::string& element) { for (const auto& hash_function : hash_functions) { size_t index = hash_function(element) % size; if (!bitset.test(index)) { return false; // If any bit is not set, the element is definitely not in the filter } } return true; // If all bits are set, the element may exist in the filter (false positives are possible) } }; // Example hash function using std::hash size_t hash1(const std::string& str) { return std::hash{}(str); } // Example hash function using a custom hash function size_t hash2(const std::string& str) { size_t hash = 0; for (char c : str) { hash = (hash * 31) + c; } return hash; } int main() { // Create a Bloom Filter with a size of 1000000 and two hash functions BloomFilter bloomFilter(1000000, {hash1, hash2}); // Add elements to the Bloom Filter bloomFilter.add("apple"); bloomFilter.add("banana"); bloomFilter.add("cherry"); // Check if elements may exist in the Bloom Filter std::cout << "Contains 'apple': " << bloomFilter.contains("apple") << std::endl; // Should return 1 (true) std::cout << "Contains 'grape': " << bloomFilter.contains("grape") << std::endl; // Should return 0 (false) std::cout << "Contains 'cherry': " << bloomFilter.contains("cherry") << std::endl; // Should return 1 (true) return 0; } ================================================ FILE: Hash Table/Convert an array to reduced form using hashing.cpp ================================================ /*Declare an array of name ar[ ] with n elements Create a temp[ ] array of size n Create a hashmap for the same datatype of the array Copy the elements of array ar [ ] to temp[ ] array Sort the temp[ ] array in ascending order After sorting, mapping the values from 0 to n-1 with the temp array. Replace the ar[ ] element with the hashtable values. Print the ar[ ] */ #include using namespace std; int main() { int ar[] = {12,34,5,6,8}; /* calculating the size of the given array */ int size = sizeof(ar)/sizeof(ar[0]); /* creating temp array to sort and manipulate the array*/ int temp[size],val=0; /* creating an unordered_map*/ unordered_map mapped; /* copying the elements from ar to temp array*/ for(int i=0;i keys() { ArrayList allKeys = new ArrayList<>(); for (int i = 0; i < dataMap.length; i++) { Node temp = dataMap[i]; while (temp != null) { allKeys.add(temp.key); temp = temp.next; } } return allKeys; } } ================================================ FILE: Hash Table/Longest_substring_without_repeating_characters.py ================================================ ''' Given a string s, find the length of the longest substring without repeating characters. Example 1: Input: s = "abcabcbb" Output: 3 Explanation: The answer is "abc", with the length of 3. Example 2: Input: s = "bbbbb" Output: 1 Explanation: The answer is "b", with the length of 1. Example 3: Input: s = "pwwkew" Output: 3 Explanation: The answer is "wke", with the length of 3. Notice that the answer must be a substring, "pwke" is a subsequence and not a substring. Constraints: 0 <= s.length <= 5 * 104 s consists of English letters, digits, symbols and spaces. ''' class Solution: #Approach-1 #Time Complexity - O(N^2), Space complexity - O(N) #Adding brute-force values into hash_map and calculating the max_length def lengthOfLongestSubstring(self, s: str) -> int: if(len(s)==0 or len(s)==1): return len(s) max_length = 0 for i in range(len(s)): temp_length,temp = 0,set() for j in range(i,len(s)): if(temp_length int: if(len(s)==0): return 0 hash_set = set() start_ind,end_ind = 0,0 max_length = -1 while(end_ind> partition(String s) { List> result = new ArrayList<>(); List current = new ArrayList<>(); backtrack(result, current, s, 0); return result; } private static void backtrack(List> result, List current, String s, int start) { if (start == s.length()) { result.add(new ArrayList<>(current)); return; } for (int end = start; end < s.length(); end++) { if (isPalindrome(s, start, end)) { current.add(s.substring(start, end + 1)); backtrack(result, current, s, end + 1); current.remove(current.size() - 1); } } } private static boolean isPalindrome(String s, int start, int end) { while (start < end) { if (s.charAt(start) != s.charAt(end)) { return false; } start++; end--; } return true; } public static void main(String[] args) { String input = "aab"; List> partitions = partition(input); for (List partition : partitions) { System.out.println(partition); } } } ================================================ FILE: Hash Table/Partition_string.py ================================================ def is_palindrome(s): return s == s[::-1] def min_partition(s): n = len(s) # Create a hash table to store the results of subproblems # dp[i] represents the minimum number of partitions in s[0:i] dp = [float('inf')] * (n + 1) dp[0] = 0 # Iterate through the string for i in range(1, n + 1): # Check all possible substrings s[j:i] for j in range(i): # If s[j:i] is a palindrome, update the minimum number of partitions if is_palindrome(s[j:i]): dp[i] = min(dp[i], dp[j] + 1) # Return the minimum number of partitions for the entire string return dp[n] - 1 # Example usage string = "aabba" optimal_partitions = min_partition(string) print("Optimal number of partitions:", optimal_partitions) ''' The min_partition function utilizes dynamic programming to find the minimum number of partitions in the given string s. It iterates through each character in the string and checks all possible substrings. If a substring is a palindrome, it updates the minimum number of partitions accordingly. Note that the dp list is initialized with float('inf') values, except for the first element dp[0], which is set to 0. The final result subtracts 1 from dp[n] to account for the fact that the last character does not require a partition. The time complexity of the given solution is O(n^3), where n is the length of the input string s. This is because there are two nested loops, and for each combination of i and j, the is_palindrome function is called, which has a time complexity of O(n). Hence, the overall time complexity is O(n^3). The space complexity of the solution is O(n), where n is the length of the input string s. This is because the dp list, used to store the minimum number of partitions for each substring, has a length of n + 1. Therefore, the space required is linearly proportional to the length of the input string. ''' ================================================ FILE: Hash Table/add_first_missing_positive.cpp ================================================ /* Given an unsorted integer array nums, return the smallest missing positive integer. You must implement an algorithm that runs in O(n) time and uses O(1) auxiliary space. Sample Input: [3, 4, -1, 1] Sample Output: 2 Explanation: This code implements a solution to find the first missing positive integer from an unsorted array. The function first separates positive and negative numbers. It then considers the array with only positive numbers, and marks the index corresponding to every positive number in this array. The first index which is unmarked corresponds to the first missing positive number. This solution uses the array itself as a pseudo-hash table where the keys are the array indices, and the values are whether or not a positive integer is present in the array. By using the array in this way, we are able to find the solution in O(1) auxiliary space. Time complexity: O(n) Space complexity: O(1) */ #include #include #include int firstMissingPositive(std::vector& nums) { int n = nums.size(); // Mark numbers that are out of range as 0 for (int i = 0; i < n; i++) { if (nums[i] <= 0 || nums[i] > n) { nums[i] = 0; } } // Mark the index corresponding to the value of each number for (int i = 0; i < n; i++) { int num = std::abs(nums[i]); if (num > 0) { num--; // subtract 1 because of 0-based indexing // Mark as negative if (nums[num] > 0) { nums[num] = -nums[num]; } } } // Find the first number greater than 0 for (int i = 0; i < n; i++) { if (nums[i] >= 0) { return i + 1; // add 1 because of 0-based indexing } } // If no number is missing return n + 1; } int main() { std::vector nums = {3, 4, -1, 1}; int result = firstMissingPositive(nums); std::cout << "Smallest missing positive integer: " << result << std::endl; return 0; } ================================================ FILE: Hash Table/add_first_missing_positive.java ================================================ /* Given an unsorted integer array nums, return the smallest missing positive integer. You must implement an algorithm that runs in O(n) time and uses O(1) auxiliary space. Sample Input: [3, 4, -1, 1] Sample Output: 2 Explanation: This code implements a solution to find the first missing positive integer from an unsorted array. The function first separates positive and negative numbers. It then considers the array with only positive numbers, and marks the index corresponding to every positive number in this array. The first index which is unmarked corresponds to the first missing positive number. This solution uses the array itself as a pseudo-hash table where the keys are the array indices, and the values are whether or not a positive integer is present in the array. By using the array in this way, we are able to find the solution in O(1) auxiliary space. Time complexity: O(n) Space complexity: O(1) */ public class AddFirstMissingPositive{ public int firstMissingPositive(int[] nums) { int n = nums.length; // Mark numbers that are out of range as 0 for(int i = 0; i < n; i++) { if(nums[i] <= 0 || nums[i] > n) { nums[i] = 0; } } // Mark the index corresponding to the value of each number for(int i = 0; i < n; i++) { int num = Math.abs(nums[i]); if(num > 0) { num--; // subtract 1 because of 0-based indexing // Mark as negative if(nums[num] > 0) { nums[num] = -nums[num]; } } } // Find the first number greater than 0 for(int i = 0; i < n; i++) { if(nums[i] >= 0) { return i + 1; // add 1 because of 0-based indexing } } // If no number is missing return n + 1; } } ================================================ FILE: Hash Table/find_optimal_partition_of_string.cpp ================================================ /* OPTIMAL PARTITION OF STRING */ /* PROGRAM AUTHOR : VARUN GARG */ /* Our task is to partition the string into one or more substrings such that the characters in each substring are unique. Approach: Intuitively, we can consider adding characters to a substring as long as we don't see a character that has already been added to the current substring. When we see a character that is already present in the substring, we start a new substring and repeat this process until we iterate over the entire string s. we are declaring a set in which if the character is not present in the set then the character will be inserted in it else it will break from the loop then the count of the strings will be increased . sample input : abacaba sample output : a b a c a b a The minimum number of substrings required for partition 4 Time Complexity: O(n) Space Complexity: O(n) */ #include using namespace std; int main(){ string s; cin>>s; // taking input of the string int cnt=0; int n=s.size(); for(int i=0;i sub; // taking the unique substrings in a set by checking the character is present in the set or not while(i 0 { cuts[j] = min(cuts[j], cuts[i-1]+1) } else { cuts[j] = 0 } } } } return cuts[n-1] } func main() { s := "aabba" minimumCuts := minCut(s) fmt.Println("Minimum cuts:", minimumCuts) } ================================================ FILE: Hash Table/first_duplicate_value.go ================================================ /* Given an array of integers between 1 and n, inclusive, where n is the length of the array, write a function that returns the first integer that appears more than once (when the array is read from left to right). Sample Input = [2, 1, 5, 2, 3, 3, 4] Output : 2 The time complexity of the `FirstDuplicateValue` function is O(n), where n is the length of the input `array`. This is because the function iterates through the array once, performing constant time operations (checking if a value exists in a hash map and inserting values into the hash map). The worst case scenario is that the function iterates through the entire array without finding a duplicate, resulting in O(n) time complexity. The space complexity of the given implementation is O(n), where n is the length of the input array. This is because we are using a hash table (implemented as a map in Go) to store the elements we have seen so far. In the worst case, where there are no duplicates in the array, we will end up storing all n elements in the hash table, which would require O(n) space. */ package main import "fmt" /* This function takes an array of integers as input and returns the first duplicate value found in the array. It uses a hash table, implemented as a Go map, to keep track of the numbers seen so far. For each number in the input array, the function checks if it has already been seen before by checking if it exists as a key in the map. If it has been seen before, the function returns the number as it is the first duplicate value found. Otherwise, the function adds the number to the map with a value of true to indicate that it has been seen. */ func FirstDuplicateValue(array []int) int { seenSoFar := make(map[int]bool) for _, num := range array { if _, valueExists := seenSoFar[num]; valueExists { return num } seenSoFar[num] = true } return -1 } func main() { array := []int{2, 1, 5, 2, 3, 3, 4} firstDuplicate := FirstDuplicateValue(array) fmt.Println(firstDuplicate) } ================================================ FILE: Hash Table/first_missing_positve.go ================================================ /* -> The firstMissingPositive function takes a slice of integers called nums as input. -> The function determines the length of the slice nums and assigns it to the variable n. -> Step 1: Move positive numbers to their correct positions - The algorithm iterates through each element of the slice using a for loop. - Inside the loop, it checks if the current number (nums[i]) is positive and within the range of 1 to n. - If the number is valid, it checks whether the number at its correct position (nums[nums[i]-1]) is different from the current number itself. This ensures that the number is not already in its correct place. - If the numbers are different, it swaps the current number with the number at its correct position using the Go idiomatic way of swapping values (nums[nums[i]-1], nums[i] = nums[i], nums[nums[i]-1]). - The swapping process continues until the number at index i is either non-positive or already in its correct place. This step ensures that all positive numbers are placed in their respective positions. -> Step 2: Find the first missing positive After completing the first pass through the slice, the algorithm performs a second pass using another for loop. Inside the loop, it checks if the number at index i is equal to i + 1. If not, it means that i + 1 is missing from the slice. In this case, the algorithm returns i + 1 as the smallest missing positive integer. -> If all positive integers from 1 to n are present in the slice, the function reaches the end without finding a missing positive. In this case, it returns n + 1 as the smallest missing positive integer. The Go implementation of the algorithm ensures that it modifies the input slice nums in-place without using any additional data structures, meeting the O(1) auxiliary space requirement. The time complexity of the algorithm remains O(n) as it performs two passes through the slice, visiting each element once. */ /* Example-> Example 1: Input: nums := []int{1, 2, 0} Output: 3 Explanation: The smallest missing positive integer in the slice is 3. Example 2: Input: nums := []int{3, 4, -1, 1} Output: 2 Explanation: The smallest missing positive integer in the slice is 2. Example 3: Input: nums := []int{7, 8, 9, 11, 12} Output: 1 Explanation: The smallest missing positive integer in the slice is 1. */ func firstMissingPositive(nums []int) int { n := len(nums) // Step 1: Move positive numbers to their correct positions for i := 0; i < n; i++ { for nums[i] > 0 && nums[i] <= n && nums[nums[i]-1] != nums[i] { nums[nums[i]-1], nums[i] = nums[i], nums[nums[i]-1] } } // Step 2: Find the first missing positive for i := 0; i < n; i++ { if nums[i] != i+1 { return i + 1 } } // If all positive integers are present, return n + 1 return n + 1 } ================================================ FILE: Hash Table/first_missing_positve.js ================================================ // First Missing Positve /* -> The firstMissingPositive function takes an array of integers called nums as input. -> The function first determines the length of the array nums and assigns it to the variable n. -> Step 1: Move positive numbers to their correct positions - The algorithm iterates through each element of the array using a for loop. - Inside the loop, it checks if the current number (nums[i]) is positive and within the range of 1 to n. - If the number is valid, it checks whether the number at its correct position (nums[nums[i] - 1]) is different from the current number itself. This ensures that the number is not already in its correct place. - If the numbers are different, it swaps the current number with the number at its correct position using array destructuring ([nums[nums[i] - 1], nums[i]] = [nums[i], nums[nums[i] - 1]]). This moves the current number to its correct place in the array. - The swapping process continues until the number at index i is either non-positive or already in its correct place. This step ensures that all positive numbers are placed in their respective positions. Step 2: Find the first missing positive - After completing the first pass through the array, the algorithm performs a second pass using another for loop. - Inside the loop, it checks if the number at index i is equal to i + 1. If not, it means that i + 1 is missing from the array. - In this case, the algorithm returns i + 1 as the smallest missing positive integer. -> If all positive integers from 1 to n are present in the array, the function reaches the end without finding a missing positive. In this case, it returns n + 1 as the smallest missing positive integer. The algorithm meets the given time complexity requirement of O(n) because it performs two passes through the array, and each pass visits each element once. It also satisfies the space complexity requirement of O(1) since it modifies the input array in-place without using any additional data structures. */ /* Examples- Example 1: Input: nums = [1, 2, 0] Output: 3 Explanation: The smallest missing positive integer in the array is 3. Example 2: Input: nums = [3, 4, -1, 1] Output: 2 Explanation: The smallest missing positive integer in the array is 2. Example 3: Input: nums = [7, 8, 9, 11, 12] Output: 1 Explanation: The smallest missing positive integer in the array is 1. */ function firstMissingPositive(nums) { const n = nums.length; // Step 1: Move positive numbers to their correct positions for (let i = 0; i < n; i++) { while (nums[i] > 0 && nums[i] <= n && nums[nums[i] - 1] !== nums[i]) { [nums[nums[i] - 1], nums[i]] = [nums[i], nums[nums[i] - 1]]; } } // Step 2: Find the first missing positive for (let i = 0; i < n; i++) { if (nums[i] !== i + 1) { return i + 1; } } // If all positive integers are present, return n + 1 return n + 1; } ================================================ FILE: Hash Table/first_missing_positve.py ================================================ # first positive missing in python ''' To find the smallest missing positive integer in an unsorted integer array nums with the given time and space constraints, you can utilize the concept of in-place swapping and indexing. Explanation:- -> Iterate through the array nums and ignore any non-positive numbers (i.e., negative numbers and zero). Also, ignore numbers greater than the length of the array since they cannot be the smallest missing positive integer. -> For each positive number num encountered, find its correct index targetIndex as num - 1. -> Check if the number at index targetIndex is already in its correct place or not. If it is not, swap the numbers at indices i and targetIndex to move num to its correct place. Continue this swapping process until the number at index i is either non-positive or already in its correct place. -> After completing the iteration, perform a second pass through the array. The first index i that does not contain the value i + 1 represents the smallest missing positive integer. Return i + 1. ''' # Time complexity O(n) and Space complexity O(1). '''Examples- Example 1: Input: nums = [1, 2, 0] Output: 3 Explanation: The smallest missing positive integer in the array is 3. Example 2: Input: nums = [3, 4, -1, 1] Output: 2 Explanation: The smallest missing positive integer in the array is 2. Example 3: Input: nums = [7, 8, 9, 11, 12] Output: 1 Explanation: The smallest missing positive integer in the array is 1. ''' def firstMissingPositive(nums): n = len(nums) # Step 1: Move positive numbers to their correct positions for i in range(n): while 1 <= nums[i] <= n and nums[nums[i] - 1] != nums[i]: nums[nums[i] - 1], nums[i] = nums[i], nums[nums[i] - 1] # Step 2: Find the first missing positive for i in range(n): if nums[i] != i + 1: return i + 1 # If all positive integers are present, return n + 1 return n + 1 ================================================ FILE: Hash Table/first_non_repeated_character.go ================================================ /* The FirstNonRepeatingCharacter function takes a string as input and returns the index of the first non-repeating character in the string. If all the characters are repeated, then it returns -1. The function works by first creating an empty map called charFreq to keep track of the frequency of each character in the string. Then, it iterates through each character in the string using a for loop. Inside the loop, it checks if the current character already exists in the charFreq map. If it does, then it increments its frequency by 1, and if it doesn't, it adds the character to the map with a frequency of 1. After the map is constructed, the function iterates through the string again using another for loop, this time tracking the index of each character using the idx variable. For each character, it checks its frequency in the charFreq map. If the frequency is 1, then the character is non-repeating and the function returns its index. If no non-repeating character is found, then the function returns -1. Overall, the function has a time complexity of O(n) since it loops through the string twice, and a space complexity of O(k), where k is the number of unique characters in the string, since the charFreq map stores the frequency of each unique character. */ package main import "fmt" // This function takes a string as input and returns the index of the first non-repeating character in the string. func FirstNonRepeatingCharacter(str string) int { // Create a map to store the frequency of each character in the string. // The key of the map is a rune, which is an integer representation of a Unicode character. // The value of the map is an integer representing the frequency of the corresponding character. charFreq := make(map[rune]int) // Loop through each character in the string and update its frequency in the map. for _, char := range str { charFreq[char] += 1 } // Loop through each character in the string again. for idx, char := range str { // If the frequency of the current character is 1, it means it is the first non-repeating character in the string. if charFreq[char] == 1 { // Return the index of the non-repeating character. return idx } } // If no non-repeating character is found in the string, return -1. return -1 } func main() { fmt.Println(FirstNonRepeatingCharacter("abcdcaf")); // b index 1 } ================================================ FILE: Hash Table/first_repeated_character.go ================================================ // Program to find first repeating character in a word // Sample Input : "DataStructures" // Output: a package main import "fmt" func FirstRepeatedCCharacter(word string) byte { length := len(word) hMap := [256]int{} // Extended Ascii can support 256 different characters // initialize hMap values to 0 for i := 0; i < 256; i++ { hMap[i] = 0 } // every time you see a character in a word increment its position in hMap for i := 0; i < length; i++ { // if character is already present incase "1" then return character if hMap[word[i]] == 1 { return word[i] } // increment value in position of character hMap[word[i]]++ } return byte(0) } func main() { fmt.Printf("%c",FirstRepeatedCCharacter("DataStructures")) } ================================================ FILE: Hash Table/four_number_sum.java ================================================ import java.util.*; /** * Write a function that takes in a non-empty array of distinct integers and an integer representing a target sum. The function should find all quadruplets in the array that sum up to the target sum and return a two-dimensional array of all these quadruplets in no particular order. If no four numbers sum up to the target sum, the function should return an empty array. Sample Input array = [7, 6, 4, -1, 1, 2] targetSum = 16 Sample Output [[7, 6, 4, -1], [7, 6, 1, 2]] // the quadruplets could be ordered differently */ public class FourNumberSum { public static void main(String[] args) { // int[] array = {7, 6, 4, -1, 1, 2}; // int targetSum = 16; int[] array = {2, 2, 2, 2, 2}; int targetSum = 8; List result = solve(array, targetSum); for (var pair: result) { System.out.println(Arrays.toString(pair)); } } public static List solve(int[] array, int targetSum) { // Average: O(n^2) time | O(n^2) space // Worst: O(n^3) time | O(n^2) space Map> allPairSums = new HashMap<>(); List quardruplets = new ArrayList<>(); for (int i = 0; i < array.length - 1; i++) { for (int j = i + 1; j < array.length; j++) { int currentSum = array[i] + array[j]; int difference = targetSum - currentSum; if (allPairSums.containsKey(difference)) { for (Integer[] pair: allPairSums.get(difference)) { Integer[] newQuadruplet = {pair[0], pair[1], array[i], array[j]}; quardruplets.add(newQuadruplet); } } } for (int k = 0; k < i; k++) { int currentSum = array[i] + array[k]; Integer[] pair = {array[k], array[i]}; if(!allPairSums.containsKey(currentSum)) { List pairGroup = new ArrayList<>(); pairGroup.add(pair); allPairSums.put(currentSum, pairGroup); } else { allPairSums.get(currentSum).add(pair); } } } return quardruplets; } } ================================================ FILE: Hash Table/frequency_of_elements.java ================================================ /** * Problem Description * Given an array A. You have some integers given in the array B. * * For the i-th number, find the frequency of B[i] in the array A and return a list containing all the frequencies. * * * * Problem Constraints * 1 <= |A| <= 105 * * 1 <= |B| <= 105 * * 1 <= A[i] <= 105 * * 1 <= B[i] <= 105 * * * * Input Format * First argument A is an array of integers. * * Second argument B is an array of integers denoting the queries. * * * * Output Format * Return an array of integers containing frequency of the each element in B. * * * * Example Input * Input 1: * A = [1, 2, 1, 1] * B = [1, 2] * Input 2: * A = [2, 5, 9, 2, 8] * B = [3, 2] * * * Example Output * Output 1: * [3, 1] * Output 2: * [0, 2] * * * Example Explanation * For Input 1: * The frequency of 1 in the array A is 3. * The frequency of 2 in the array A is 1. * For Input 2: * The frequency of 3 in the array A is 0. * The frequency of 2 in the array A is 2. */ package Hashing; import java.util.Arrays; import java.util.HashMap; public class FrequencyOfElements { public static void main(String[] args) { int[] array = {2, 5, 9, 2, 8}; int[] queries = {3, 2}; int[] res = solve(array, queries); System.out.println(Arrays.toString(res)); } public static int[] solve(int[] nums, int[] queries) { // O(N) time | O(Q) space where N is length of array , Q is length of queries HashMap hashMap = new HashMap<>(); for (int num : nums) { if (!hashMap.containsKey(num)) hashMap.put(num, 0); hashMap.put(num, hashMap.get(num) + 1); } System.out.println(hashMap); int[] res = new int[queries.length]; for (int i = 0; i < queries.length; i++) if (hashMap.containsKey(queries[i])) res[i] = hashMap.get(queries[i]); return res; } } ================================================ FILE: Hash Table/group_anagrams.cpp ================================================ // Group Anagrams /* The function groupAnagrams takes a vector of strings strs as input and returns a vector of vectors of strings res, where each inner vector contains a group of anagrams. We create an unordered map mp to store the anagram groups. The keys of the map are the sorted versions of the anagrams, and the values are vectors containing the original strings that belong to that anagram group. We iterate through each string in the input vector strs, create a copy of the string t, sort the characters in the copy t, and add the original string s to the corresponding anagram group in the unordered map mp. We create a vector res to store the anagram groups and iterate through the unordered map mp. For each anagram group in the map, we add the corresponding vector of original strings to the res vector. We return the result vector res containing all the anagram groups. In the main function, we create an example input vector strs, call the groupAnagrams function and store the result in the output vector output, and iterate through the output vector to print each anagram group. The time complexity of this implementation is O(n * k log k), where n is the length of the input array and k is the length of the longest string in the array. This is due to the time complexity of sorting each string in the array. The space complexity of this implementation is O(n * k), as we need to store each string in the map along with its corresponding group of anagrams. */ #include #include #include #include #include using namespace std; vector> groupAnagrams(vector& strs) { unordered_map> mp; // create an unordered map to store anagrams // iterate through each string in the input vector for (string s : strs) { string t = s; // create a copy of the current string sort(t.begin(), t.end()); // sort the characters in the copy mp[t].push_back(s); // add the original string to the corresponding anagram group } vector> res; // create a vector to store the anagram groups for (auto p : mp) { // iterate through the unordered map res.push_back(p.second); // add the anagram group to the result vector } return res; // return the result vector containing all the anagram groups } int main() { // create an example input vector vector strs = {"eat", "tea", "tan", "ate", "nat", "bat"}; // call the groupAnagrams function and store the result in the output vector vector> output = groupAnagrams(strs); // iterate through the output vector and print each anagram group for (vector group : output) { for (string s : group) { cout << s << " "; } cout << endl; } return 0; } ================================================ FILE: Hash Table/group_anagrams.go ================================================ // Group Anagrams /* The program takes a slice of strings as input, where each string is an element in the slice. The goal is to group the strings into separate groups where each group contains only anagrams of each other. The first step in the program is to create a map where the keys are strings and the values are slices of strings. This map will be used to store the groups of anagrams. Next, the program loops through each string in the input slice. For each string, the program converts it to a slice of bytes, sorts the bytes in ascending order, and then converts the sorted slice of bytes back into a string. This sorted string is used as the key to the map. If the key already exists in the map, the string is added to the slice of values associated with that key. If the key doesn't exist, a new key-value pair is created with the key being the sorted string and the value being a new slice containing only the current string. Once all the strings have been processed, the program loops through the map and appends the slice of values associated with each key to the output slice. This output slice contains slices of strings, where each slice is a group of anagrams. Finally, the program returns the output slice. Overall, the program uses the fact that anagrams have the same set of characters (and thus, the same sorted order of characters) \ to group the input strings efficiently. The time complexity of this implementation is O(n * k log k), where n is the length of the input array and k is the length of the longest string in the array. This is due to the time complexity of sorting each string in the array. The space complexity of this implementation is O(n * k), as we need to store each string in the map along with its corresponding group of anagrams. */ package main import ( "fmt" "sort" ) func groupAnagrams(strs []string) [][]string { // Create a map to store anagrams and their grouped strings anagramMap := make(map[string][]string) // Loop through each string in the input array for _, str := range strs { // Sort the string to create a unique key for each anagram sortedStr := sortString(str) // Add the string to its corresponding group in the anagramMap anagramMap[sortedStr] = append(anagramMap[sortedStr], str) } // Create a 2D slice to store the grouped anagrams groups := make([][]string, 0, len(anagramMap)) // Loop through the anagramMap and append each group of anagrams to the groups slice for _, group := range anagramMap { groups = append(groups, group) } return groups } // Helper function to sort a string alphabetically func sortString(s string) string { sBytes := []byte(s) sort.Slice(sBytes, func(i, j int) bool { return sBytes[i] < sBytes[j] }) return string(sBytes) } func main() { strs := []string{"eat", "tea", "tan", "ate", "nat", "bat"} groups := groupAnagrams(strs) for _, group := range groups { fmt.Println(group) } } ================================================ FILE: Hash Table/group_anagrams.java ================================================ // Group Anagrams /* Explanation: The groupAnagrams method takes an array of strings and groups them into a list of anagram groups. To accomplish this, the method first creates a HashMap called anagramGroups to store the groups of anagrams. For each string in the input array, the method converts the string to a character array, sorts the array, and converts it back to a string. This sorted string serves as the key for the anagramGroups map. If the sorted string is not already in the map, the method adds it with an empty list as its value. Then, the original string is added to the list of its corresponding anagram group. Finally, the method returns a list of the values in the anagramGroups map, which is a list of the anagram groups. In the main method, we create an instance of the GroupAnagrams class and call the groupAnagrams method on an example input array. We then print out the resulting list of anagram groups. The time complexity of this implementation is O(n * k log k), where n is the length of the input array and k is the length of the longest string in the array. This is due to the time complexity of sorting each string in the array. The space complexity of this implementation is O(n * k), as we need to store each string in the map along with its corresponding group of anagrams. */ import java.util.*; public class group_anagrams{ public List> groupAnagrams(String[] strs) { // Create a HashMap to store the groups of anagrams Map> anagramGroups = new HashMap<>(); // Loop through each string in the input array for (String str : strs) { // Convert the string to a char array, sort it, and convert it back to a string char[] chars = str.toCharArray(); Arrays.sort(chars); String sorted = new String(chars); // If the sorted string is not in the map, add it with an empty list as its value if (!anagramGroups.containsKey(sorted)) { anagramGroups.put(sorted, new ArrayList()); } // Add the original string to the list of its anagram group anagramGroups.get(sorted).add(str); } // Return a list of the anagram groups return new ArrayList<>(anagramGroups.values()); } public static void main(String[] args) { group_anagrams ga = new group_anagrams(); // Example input String[] strs = {"eat", "tea", "tan", "ate", "nat", "bat"}; // Group the anagrams List> groups = ga.groupAnagrams(strs); // Print the groups for (List group : groups) { System.out.println(group); } } } ================================================ FILE: Hash Table/group_anagrams.js ================================================ // Group Anagrams /* The function groupAnagrams takes an array of strings strs as input, and returns an array of arrays of anagrams. The approach taken is to sort the characters of each string alphabetically, and group strings with the same sorted form together. The resulting groups are stored in an object anagramGroups whose keys are the sorted strings and whose values are arrays of the original strings. Finally, the values of the anagramGroups object are returned as an array. To accomplish the sorting and grouping, the split, sort, and join methods are used. First, each string is split into an array of its characters using the split method. Then, the sort method is used to sort the characters alphabetically. Finally, the join method is used to combine the sorted characters back into a string. This sorted string is used as the key for grouping the anagrams in the anagramGroups object. The for...of loop is used to iterate over each string in the input array. Inside the loop, the sorted form of the string is computed, and a check is made to see if a group for the sorted form already exists in the anagramGroups object. If it doesn't, a new group is created as an empty array. Then, the original string is added to the appropriate group in the anagramGroups object using the push method. Finally, the Object.values method is used to extract the arrays of anagrams from the anagramGroups object, and these arrays are returned as the final result. The time complexity of this implementation is O(n * k log k), where n is the length of the input array and k is the length of the longest string in the array. This is due to the time complexity of sorting each string in the array. The space complexity of this implementation is O(n * k), as we need to store each string in the map along with its corresponding group of anagrams. */ /** * Given an array of strings, group anagrams together and return them as an array of arrays. * * @param {string[]} strs - The input array of strings. * @returns {string[][]} - An array of arrays of anagrams. */ function groupAnagrams(strs) { // Create an empty object to store groups of anagrams. const anagramGroups = {}; // Loop through each string in the input array. for (const str of strs) { // Sort the characters of the string alphabetically and use the sorted string as a key. const sortedStr = str.split("").sort().join(""); // If a group for the sorted string doesn't exist yet, create it as an empty array. if (!anagramGroups[sortedStr]) { anagramGroups[sortedStr] = []; } // Add the original string to the group of anagrams. anagramGroups[sortedStr].push(str); } // Return the values of the anagram groups object as an array. return Object.values(anagramGroups); } const words = ["eat", "tea", "tan", "ate", "nat", "bat"]; console.log(groupAnagrams(words)); ================================================ FILE: Hash Table/group_anagrams.py ================================================ # Group Anagrams ''' The function groupAnagrams takes a list of strings as input and returns a list of lists, where each inner list contains a group of anagrams from the input list. The function starts by creating an empty dictionary groups to store the groups of anagrams. The keys of the dictionary will be the unique signatures of the anagrams, and the values will be lists of the actual anagrams. Then, the function iterates through each string s in the input list strs. For each string, it sorts the characters of the string to get its unique signature sig. If the signature is not already in the dictionary, a new key-value pair is added to the dictionary with the signature as the key and an empty list as the value. Finally, the current string s is appended to the list of its corresponding signature in the dictionary. Once all the strings have been processed, the function returns the values of the dictionary as a list of lists, where each inner list contains a group of anagrams from the input list. For example, if the input list is ["eat", "tea", "tan", "ate", "nat", "bat"], the function will return [['eat', 'tea', 'ate'], ['tan', 'nat'], ['bat']], which represents the three groups of anagrams: ['eat', 'tea', 'ate'], ['tan', 'nat'], and ['bat']. The time complexity of this implementation is O(n * k log k), where n is the length of the input array and k is the length of the longest string in the array. This is due to the time complexity of sorting each string in the array. The space complexity of this implementation is O(n * k), as we need to store each string in the map along with its corresponding group of anagrams. ''' def groupAnagrams(strs): # create a dictionary to store the groups of anagrams groups = {} # iterate through each string in the list for s in strs: # sort the characters of the string to get its unique signature # for example, "eat" and "ate" both have the same signature "aet" sig = ''.join(sorted(s)) # check if the signature is already in the dictionary # if not, add a new key-value pair with an empty list as the value if sig not in groups: groups[sig] = [] # append the current string to the list of its corresponding signature groups[sig].append(s) # return the values of the dictionary as a list of lists return list(groups.values()) ================================================ FILE: Hash Table/integer_to_roman.cpp ================================================ // Integer to Roman /* In this implementation, we use an unordered map to store the mapping between integers and Roman numerals. We then loop through the map from the largest number to the smallest, checking if the current number can be divided by the key of the map. If it can, we append the corresponding Roman numeral to the result string and subtract the key from the number. We continue this process until the number is 0. The final result string is then returned. The time complexity of this implementation is O(1) since the size of the map is fixed, and the space complexity is O(1) since we only use a fixed amount of memory to store the map and the result string. */ #include #include using namespace std; string intToRoman(int num) { // Initialize the mapping between integers and Roman numerals unordered_map map = { {1000, "M"}, {900, "CM"}, {500, "D"}, {400, "CD"}, {100, "C"}, {90, "XC"}, {50, "L"}, {40, "XL"}, {10, "X"}, {9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"} }; // Initialize an empty string to store the result string result = ""; // Loop through the map from the largest number to the smallest for (auto it = map.rbegin(); it != map.rend(); ++it) { // Check if the current number can be divided by the key of the map while (num >= it->first) { // Append the corresponding Roman numeral to the result string result += it->second; // Subtract the key from the number num -= it->first; } } return result; } int main() { int num = 1994; cout << intToRoman(num) << endl; // Output: "MCMXCIV" return 0; } ================================================ FILE: Hash Table/integer_to_roman.go ================================================ // Integer to Roman /* In this implementation, we create two arrays symbols and values that hold the Roman numeral symbols and their corresponding integer values respectively. We then create a string builder to hold our result. We loop through the values array, and while the current num is greater than or equal to the current values element, we subtract the values element from num and append the corresponding symbols element to the result string builder. We continue this process until we have gone through all the values elements. Finally, we return the result string. */ package main import "strings" func intToRoman(num int) string { // Create two arrays to hold the Roman numeral symbols and their corresponding values symbols := []string{"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"} values := []int{1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1} var result strings.Builder // Loop through the values array and subtract the corresponding value from num while appending the symbol to the result for i := 0; i < len(values); i++ { for num >= values[i] { num -= values[i] result.WriteString(symbols[i]) } } return result.String() } func main() { print(intToRoman(8000)) } ================================================ FILE: Hash Table/integer_to_roman.java ================================================ // Integer to Roman /* Here, we create two arrays to store the Roman numerals and their corresponding values. We then use a StringBuilder to build the resulting Roman numeral string. We use a while loop to iterate through the values array until the number is reduced to 0. In each iteration, we check if the current value is less than or equal to the number. If it is, we subtract the value from the number and append the corresponding Roman numeral to the StringBuilder. If it's not, we move to the next value. Finally, we return the resulting Roman numeral string. The time complexity of this implementation is O(1), since the maximum number of iterations in the while loop is 13 (the length of the values array). The space complexity is also O(1), since we only use a StringBuilder and two constant-size arrays to store the Roman numerals and values. */ public static String intToRoman(int num) { // Create two arrays to store the Roman numerals and their corresponding values String[] romanNumerals = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}; int[] values = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}; StringBuilder sb = new StringBuilder(); int i = 0; // Iterate through the values array until the number is reduced to 0 while (num > 0) { // If the current value is less than or equal to the number, subtract the value and append the corresponding Roman numeral if (values[i] <= num) { num -= values[i]; sb.append(romanNumerals[i]); } else { i++; } } return sb.toString(); } ================================================ FILE: Hash Table/integer_to_roman.js ================================================ // Integer to Roman /* This solution uses a greedy approach to build the roman numeral string by subtracting the maximum possible value at each step until the input number is zero. The time complexity of this implementation is O(1) since there is a fixed number of roman numerals and the loop will always run 13 times. The space complexity is also O(1) since we only store a single string. */ function intToRoman(num) { // Create arrays for the symbols and their values const symbols = [ "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I", ]; const values = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]; let result = ""; // Initialize an empty string to store the roman numeral // Loop through the values array for (let i = 0; i < values.length; i++) { // While the input num is greater than or equal to the current value while (num >= values[i]) { result += symbols[i]; // Add the corresponding symbol to the result string num -= values[i]; // Subtract the value from the input num } } return result; // Return the roman numeral string } ================================================ FILE: Hash Table/integer_to_roman.py ================================================ # Integer to Roman ''' The function takes an integer num as input and returns the corresponding Roman numeral as a string. The mapping of values to symbols is defined in a dictionary symbols. The keys in the dictionary are the values that can be used to represent Roman numerals, and the values are the symbols themselves. The keys are sorted in descending order in a list called keys. This is done so that we can iterate through the keys in descending order and subtract the largest value from num until num is 0. We initialize an empty string called roman to store the Roman numeral representation. We then iterate through the keys in keys and subtract the largest value from num until num is less than the current key. We append the corresponding symbol to roman for each value subtracted. Finally, we return the string roman, which is the Roman numeral representation of the input integer num. ''' def intToRoman(num: int) -> str: # define the mapping of values to symbols symbols = {1: 'I', 4: 'IV', 5: 'V', 9: 'IX', 10: 'X', 40: 'XL', 50: 'L', 90: 'XC', 100: 'C', 400: 'CD', 500: 'D', 900: 'CM', 1000: 'M'} # create a list of keys in descending order keys = sorted(symbols.keys(), reverse=True) # initialize an empty string to store the roman numeral representation roman = '' # iterate through the keys and subtract the largest value from the number # until the number is 0 for k in keys: while num >= k: roman += symbols[k] num -= k return roman ================================================ FILE: Hash Table/remove_duplicates.go ================================================ // Given an array of integers, give an algorithm for removing the duplicates. package main import "fmt" // RemoveDuplicates: returns new string without duplicate characters // Approach: use hash table to see if value already exists or not, if it does the skip the value // else append it to the result string // Time Complexity: Θ(n) on average. Space Complexity: O(n). func RemoveDuplicates(word string) string { result := "" mp := map[byte]bool{} for i:= 0; i < len(word); i++ { if mp[word[i]] == true { // current character exist in hash table so do nothing } else { // mark as true(insert into hashtable) and add to result string mp[word[i]] = true result = result + string(word[i]) } } return result } func main() { fmt.Println(RemoveDuplicates("aabbcaaacdd")); } ================================================ FILE: Hash Table/roman_to_integer.cpp ================================================ /* Roman numerals are represented by seven different symbols: I, V, X, L, C, D and M. Symbol Value I 1 V 5 X 10 L 50 C 100 D 500 M 1000 For example, 2 is written as II in Roman numeral, just two ones added together. 12 is written as XII, which is simply X + II. The number 27 is written as XXVII, which is XX + V + II. Roman numerals are usually written largest to smallest from left to right. However, the numeral for four is not IIII. Instead, the number four is written as IV. Because the one is before the five we subtract it making four. The same principle applies to the number nine, which is written as IX. There are six instances where subtraction is used: I can be placed before V (5) and X (10) to make 4 and 9. X can be placed before L (50) and C (100) to make 40 and 90. C can be placed before D (500) and M (1000) to make 400 and 900. Given a roman numeral, convert it to an integer. Example 1: Input: s = "III" Output: 3 Explanation: III = 3. Example 2: Input: s = "LVIII" Output: 58 Explanation: L = 50, V= 5, III = 3. Example 3: Input: s = "MCMXCIV" Output: 1994 Explanation: M = 1000, CM = 900, XC = 90 and IV = 4. Constraints: 1 <= s.length <= 15 s contains only the characters ('I', 'V', 'X', 'L', 'C', 'D', 'M'). It is guaranteed that s is a valid roman numeral in the range [1, 3999]. */ class Solution { public: int romanToInt(string s) { // Map Roman Characters to integer values unordered_map mp = {{'I', 1}, {'V', 5}, {'X', 10}, {'L', 50}, {'C', 100}, {'D', 500}, {'M', 1000}, }; int len = s.size(); int sum = mp[s[len-1]]; // start from end and build up the value to start for(int i = len - 2; i >= 0; i--){ if(mp[s[i]] < mp[s[i + 1]]){ sum -= mp[s[i]]; } else{ sum += mp[s[i]]; } } return sum; } }; ================================================ FILE: Hash Table/roman_to_integer.go ================================================ // Roman To Integer /* This implementation converts a given Roman numeral string to its corresponding integer value. The implementation uses a map to store the integer value of each Roman numeral, and then iterates over the input string, checking if the next Roman numeral is greater than the current one to determine whether to add or subtract the current value from the result. The time complexity of this implementation is O(n), where n is the length of the input string, since it iterates over the string only once. The space complexity is O(1), since the implementation only uses a constant amount of extra space to store the map and result variable. Time complexity: O(n), where n is the length of the input string. The program iterates through each character in the string once and performs a constant number of operations for each character. Therefore, the time complexity is linear in the length of the input string. Space complexity: O(1). The program uses a constant amount of extra space to store the result and the current and previous values. Therefore, the space complexity is constant, or O(1). */ package main import ( "fmt" ) func romanToInt(s string) int { // Create a map to store the integer value of each Roman numeral romanToIntMap := map[byte]int{ 'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000, } // Initialize the result variable to 0 result := 0 // Iterate over the input string for i := 0; i < len(s); i++ { // Get the integer value of the current Roman numeral val := romanToIntMap[s[i]] // Check if the next Roman numeral is greater than the current one if i+1 < len(s) && romanToIntMap[s[i+1]] > val { // If the next Roman numeral is greater, subtract the current value from the result result -= val } else { // If the next Roman numeral is not greater, add the current value to the result result += val } } // Return the final result return result } func main() { input := "IV" output := romanToInt(input) fmt.Printf("Roman numeral %s converted to integer is: %d\n", input, output) } ================================================ FILE: Hash Table/roman_to_integer.java ================================================ // AUTHOR : Sagar Wadhwa class Solution { public int romanToInt(String s) { // Hashmap in java which stores key value pairs. Each key is unique and no key is repeated HashMap map = new HashMap<>(); // Defining the basic rules of mapping from roman numeral to integer numbers. map.put('I',1); map.put('V',5); map.put('X',10); map.put('L',50); map.put('C',100); map.put('D',500); map.put('M',1000); //extract the last character of the string, and find it in the map, store this keys value in the // variable named "res" int res = map.get(s.charAt(s.length()-1)); //iterate over the string from the second last character till the first character. Idea to solve // this problem is that in the loop, for every character we check whether the value of the roman // numeral at the i+1 th index is greater than or less than the value of the roman numeral at i th // index. If the value of the roman numeral at the i th index is less than the value of the // roman numeral at i+1 th index, then obviously that means we have to subtract the value of the // roman numeral at the i th index from the result calculated so far. Else we have to add the value // of the roman numeral at the i th index to the result calculated so far. Example: V means five, // but if it is IV, then here the value of the roman numeral I is less than the value of the roman // numeral V, so that means subtract I from V (4). If the string is VI, means 6, then here the value of // V is greater than the value of I, so add I in V, that is 6. // Doing so in an iterative fashion and storing the result and then adding or subtracting values // from it accordingly will give us our final result. for(int i=s.length()-2;i>=0;i--){ if(map.get(s.charAt(i)) < map.get(s.charAt(i+1))){ res -= map.get(s.charAt(i)); }else{ res += map.get(s.charAt(i)); } } return res; } } ================================================ FILE: Hash Table/roman_to_integer.js ================================================ // Approach: In case of symbols like IV, CM, XC, etc., the first value is less than the second value. // So we have to subtract the first value from the second value to get the exact value. // Example: CM --> C=100, M=1000, so CM=1000-100=900. // Therefore, we can conclude that in such cases, we have to subtract the first value from the whole result. // Time complexity O(1) and Space complexity O(1). var romanToInt = function(s) { let map = new Map(); // Create a hashmap to store (symbol, value) pairs. map['I'] = 1; map['V'] = 5; map['X'] = 10; map['L'] = 50; map['C'] = 100; map['D'] = 500; map['M'] = 1000; let arr = []; for (let i = 0; i < s.length; i++) { // Store the value of each symbol in arr from left to right. arr[i] = map[s.charAt(i)]; } let ans = 0; for (let i = 0; i < arr.length - 1; i++) { // Traverse from 0 to last-1. // If the i-th element is less than the i+1-th element, then subtract the i-th element. if (arr[i] < arr[i + 1]) { ans = ans - arr[i]; } // Otherwise, add the i-th element. else { ans = ans + arr[i]; } } // Add the last element because there is no further element to compare. ans = ans + arr[arr.length - 1]; return ans; }; ================================================ FILE: Hash Table/roman_to_integer.py ================================================ # Roman to Integer ''' The function takes a string s as an input and returns the corresponding integer value by converting the string from Roman numerals to its integer value. It does this by iterating through the string. If the current character has a lower value than the character after it, the current character value is subtracted from the total, so that it is added correctly. Otherwise, the current character value is added to the total. Finally, the total integer value is returned. ''' def roman_to_int(s: str) -> int: # define mapping of values to symbols symbols = { 'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000 } # initialise integer to return total value total = 0 # iterate through characters for i in range(len(s) - 1): # if the symbol after current symbol is less than it, # subtract it from the total value if symbols[s[i]] < symbols[s[i+1]]: total -= symbols[s[i]] # else, just add the corresponding value else: total += symbols[s[i]] # add the last value total += symbols[s[-1]] return total ================================================ FILE: Hash Table/sum_of_unique_elements.java ================================================ import java.util.HashMap; /* You are given an integer array nums. The unique elements of an array are the elements that appear exactly once in the array. Return the sum of all the unique elements of nums. Example 1: Input: nums = [1,2,3,2] Output: 4 Explanation: The unique elements are [1,3], and the sum is 4. Example 2: Input: nums = [1,1,1,1,1] Output: 0 Explanation: There are no unique elements, and the sum is 0. Example 3: Input: nums = [1,2,3,4,5] Output: 15 Explanation: The unique elements are [1,2,3,4,5], and the sum is 15. Constraints: 1 <= nums.length <= 100 1 <= nums[i] <= 100 */ class Solution { public int sumOfUnique(int[] nums) { HashMap hm=new HashMap(); int sum=0; for(int x:nums) { if(hm.containsKey(x)) hm.put(x,hm.get(x)+1); else hm.put(x,1); } for(int x:nums) { if(hm.get(x)== 1) sum+=x; } return sum; } } class SumOfUniqueElements { public static void main(String[] args) { int arr[] = {1,2,2,3}; Solution s = new Solution(); System.out.println(s.sumOfUnique(arr)); } } ================================================ FILE: Hash Table/three_number_sum.java ================================================ import java.util.*; /** * Write a function that takes in a non-empty array of distinct integers and an integer representing a target sum. The function should find all triplets in the array that sum up to the target sum and return a two-dimensional array of all these triplets. The numbers in each triplet should be ordered in ascending order, and the triplets themselves should be ordered in ascending order with respect to the numbers they hold. If no three numbers sum up to the target sum, the function should return an empty array. Sample Input array = [12, 3, 1, 2, -6, 5, -8, 6] targetSum = 0 Sample Output [[-8, 2, 6], [-8, 3, 5], [-6, 1, 5]] */ public class ThreeNumberSum { // O(n^2) time | O(n) space public static void main(String[] args){ int[] array = {12, 3, 1, 2, -6, 5, -8, 6}; int targetSum = 0; List triplets = solve(array, targetSum); for (var triplet: triplets) System.out.println(Arrays.toString(triplet)); } public static List solve(int[] array, int targetSum) { Arrays.sort(array); List triplets = new ArrayList<>(); for(int i = 0; i < array.length - 2; i++) { int left = i + 1; int right = array.length - 1; while(left < right) { int currentSum = array[i] + array[left] + array[right]; if(currentSum == targetSum) { Integer[] newTriplet = {array[i], array[left], array[right]}; triplets.add(newTriplet); left++; right--; } else if(currentSum < targetSum) left++; else right--; } } return triplets; } } ================================================ FILE: Hash Table/two_sum.cpp ================================================ /* Write a function that takes in a non-empty array of distinct integers and an integer representing a target sum. If any two numbers in the input array sum up to the target sum, the function should return them in an array, in any order. If no two numbers sum up to the target sum, the function should return an empty array. Sample Input: [2, 1, 3, -1, 11, 5, 4, 0] Target: 10 Output: [-1 11] */ #include #include #include std::vector twoNumberSum(std::vector& nums, int target) { // Create an unordered_map to store the indices of the elements in the vector std::unordered_map map; // Loop through the vector for (int i = 0; i < nums.size(); i++) { // Calculate the complement of the current element with respect to the target sum int complement = target - nums[i]; // Check if the complement is already in the map if (map.count(complement) > 0) { // If the complement is in the map, return the indices of the two elements that sum up to the target return { map[complement], i }; } // If the complement is not in the map, add the current element and its index to the map map[nums[i]] = i; } // If no two elements sum up to the target, return an empty vector return {}; } int main() { // Example usage std::vector nums = { 2, 7, 11, 15 }; int target = 9; std::vector result = twoNumberSum(nums, target); if (result.size() > 0) { std::cout << "Indices of the two numbers that sum up to " << target << ": "; for (int i : result) { std::cout << i << " "; } std::cout << std::endl; } else { std::cout << "No two numbers found that sum up to " << target << "." << std::endl; } return 0; } ================================================ FILE: Hash Table/two_sum.go ================================================ /* Write a function that takes in a non-empty array of distinct integers and an integer representing a target sum. If any two numbers in the input array sum up to the target sum, the function should return them in an array, in any order. If no two numbers sum up to the target sum, the function should return an empty array. Sample Input: [2, 1, 3, -1, 11, 5, 4, 0] Target: 10 Output: [-1 11] */ package main import ( "fmt" "sort" ) // Bruteforce method continuously scan the array // for every i run j len(array) times, if target sum is found then return func TwoNUmberSumBruteForce(array []int, target int) []int { // nil array to hold result var result []int for i := 0; i < len(array) - 1; i++ { for j := i + 1; j < len(array); j++ { // look for target if array[i] + array[j] == target { // add found values in array result = append(result, array[i]) result = append(result, array[j]) return result } } } return result } // Two Pointer approach // sort the given array, set i as initial index and j as last // add element at i and j and compate with target, if it matches then return // if element is greater then target decrease index of j by 1 // if element is lesser then target increase index of i by 1 func TwoNumberSumTwoPointerMethod(array []int, target int) []int { var result []int // sort the given array in place sort.Ints(array) j := len(array) - 1 // set j as last i := 0 // set i as initial index // i and j should not overlap for i < j { // check if it matches sum if array[i] + array[j] == target { // add result in array result = append(result, array[i]) result = append(result, array[j]) // return the result return result } else if array[i] + array[j] > target { // elements is greater that means look to left side of j j--; } else if array[i] + array[j] < target { // // elements is smaller that means look to right side of i i++; } } return result } func TwoNumberSum(array []int, target int) []int { // Create map to keep track of what we ahve seen so far m := make(map[int]int) // initialize empty array for result var result []int // traverse array for i := 0; i < len(array); i++ { // lets say first element in our array is 3, and target sum is 10 // then we will look for 7 in our map, if its present then we simply return 7 and 3 required := target - array[i] // if the required value is found then store result if _, ok := m[required]; ok { result = append(result, required) result = append(result, array[i]) return result } else { // keep track of what value in array we have seen so far m[array[i]] = i } } return result } func main() { arr := []int{2, 1, 3, -1, 11, 5, 4, 0} msg := TwoNumberSum(arr, 10) fmt.Println(msg) msg = TwoNUmberSumBruteForce(arr, 10) fmt.Println(msg) arr = []int{2, 1, 3, -1, 11, 5, 4, 0, 44} msg = TwoNumberSumTwoPointerMethod(arr, 9) fmt.Println(msg) } ================================================ FILE: Hash Table/two_sum.java ================================================ /* Write a function that takes in a non-empty array of distinct integers and an integer representing a target sum. If any two numbers in the input array sum up to the target sum, the function should return them in an array, in any order. If no two numbers sum up to the target sum, the function should return an empty array. Sample Input: [2, 1, 3, -1, 11, 5, 4, 0] Target: 10 Output: [-1 11] */ import java.util.*; public class TwoNumberSum { public static void main(String[] args) { int[] array = {3, 5, -4, 8, 11, 1, -1, 6}; int targetSum = 10; } // Brute Force Solution // Time complexity: O(N^2); // Space Complexity: O(1); public static int[] TwoSumBruteForce(int[] array, int targetSum) { for(int i = 0; i < array.length - 1; i++) { int firstNum = array[i]; for(int j = i + 1; j < array.length; j++) { int secondNum = array[j]; if(firstNum + secondNum == targetSum) return new int[] {firstNum, secondNum}; } } return new int[0]; } // Sorting Solution // Time complexity: O(N log N); // Space Complexity: O(1); public static int[] TwoSumBySorting(int[] array, int targetSum) { // O(nlog(n)) time | O(1) space Arrays.sort(array); int left = 0; int right = array.length - 1; while(left < right) { int firstNum = array[left]; int secondNum = array[right]; int currentSum = firstNum + secondNum; if(currentSum == targetSum) return new int[] {firstNum, secondNum}; else if(currentSum < targetSum) left += 1; else right -= 1; } return new int[0]; } // Optimal Solution // Time complexity: O(N); // Space Complexity: O(N); public static int[] TwoSumOptimal(int[] array, int targetSum) { Set nums = new HashSet<>(); for(int num: array) { int potentialMatch = targetSum - num; if(nums.contains(potentialMatch)) return new int[] {num, potentialMatch}; nums.add(num); } return new int[0]; } } ================================================ FILE: Hash Table/two_sum.js ================================================ /* Write a function that takes in a non-empty array of distinct integers and an integer representing a target sum. If any two numbers in the input array sum up to the target sum, the function should return them in an array, in any order. If no two numbers sum up to the target sum, the function should return an empty array. Sample Input: [2, 1, 3, -1, 11, 5, 4, 0] Target: 10 Output: [-1 11] */ function twoNumberSum(nums, target) { // Create a new Map object to store the indices of the elements in the array const map = new Map(); // Loop through the array for (let i = 0; i < nums.length; i++) { // Calculate the complement of the current element with respect to the target sum const complement = target - nums[i]; // Check if the complement is already in the map if (map.has(complement)) { // If the complement is in the map, return the indices of the two elements that sum up to the target return [map.get(complement), i]; } // If the complement is not in the map, add the current element and its index to the map map.set(nums[i], i); } // If no two elements sum up to the target, return an empty array return []; } // Example usage const nums = [2, 7, 11, 15]; const target = 9; const result = twoNumberSum(nums, target); if (result.length > 0) { console.log(`Indices of the two numbers that sum up to ${target}: ${result}`); } else { console.log(`No two numbers found that sum up to ${target}.`); } ================================================ FILE: Hash Table/two_sum.py ================================================ ''' Write a function that takes in a non-empty array of distinct integers and an integer representing a target sum. If any two numbers in the input array sum up to the target sum, the function should return them in an array, in any order. If no two numbers sum up to the target sum, the function should return an empty array. Sample Input: [2, 1, 3, -1, 11, 5, 4, 0] Target: 10 Output: [-1 11] ''' # Brute Force Approach # Time complexity: O(N^2); # Space Complexity: O(1); class Solution: def twoSum(self, nums: List[int], target: int) -> List[int]: for i in range(len(nums)): for j in range(i + 1, len(nums)): if nums[i] + nums[j] == target: return [i, j] return [] # Two Pointer approach # Time complexity: O(N); # Space Complexity: O(N); class Solution: def twoSum(self, nums: List[int], target: int) -> List[int]: # Create map to keep track of what we ahve seen so far numToIndex = {} for i in range(len(nums)): # lets say first element in our array is 3, and target sum is 10 # then we will look for 7 in our map, if its present then we simply return 7 and 3 # if the required value is found then store result if target - nums[i] in numToIndex: return [numToIndex[target - nums[i]], i] # keep track of what value in array we have seen so far numToIndex[nums[i]] = i return [] ================================================ FILE: Hash Table/zero_sum_subarray.cpp ================================================ /* You're given a list of integers nums. Write a function that returns a boolean representing whether there exists a zero-sum subarray of nums Sample Input : = [-5, -5, 2, 3, -2] Output : True The subarray [-5, 2, 3] has a sum of 0 Approach: Time and Space complexity : O(n) time | O(n) space - where n is the length of nums This implementation uses an unordered map to keep track of the prefix sum of the input array nums. We initialize the map with a key-value pair of 0 and -1, since a prefix sum of 0 indicates that the subarray from index 0 to -1 (i.e., an empty subarray) has a sum of 0. We then iterate through the input array nums, adding each element to the running sum sum and checking if the current sum is already in the map. If it is, then we've found a subarray whose sum is 0, so we add the starting and ending indices of the subarray to the result vector and break out of the loop. If we reach the end of the loop without finding a zero sum subarray, then we return an empty vector. Note that this implementation assumes that there is only one zero sum subarray in the input array. If there could be multiple zero sum subarrays, then we would need to modify the implementation to return all of them. */ #include #include #include using namespace std; vector zeroSumSubarray(vector& nums) { vector result; unordered_map mp; int sum = 0; mp[0] = -1; for (int i = 0; i < nums.size(); i++) { sum += nums[i]; if (mp.find(sum) != mp.end()) { result.push_back(mp[sum] + 1); result.push_back(i); break; } mp[sum] = i; } return result; } int main() { vector nums = {4, 2, -3, 1, 6}; vector result = zeroSumSubarray(nums); if (result.empty()) { cout << "No zero sum subarray found." << endl; } else { cout << "Zero sum subarray found: "; for (int i = result[0]; i <= result[1]; i++) { cout << nums[i] << " "; } cout << endl; } return 0; } ================================================ FILE: Hash Table/zero_sum_subarray.go ================================================ /* You're given a list of integers nums. Write a function that returns a boolean representing whether there exists a zero-sum subarray of nums Sample Input : = [-5, -5, 2, 3, -2] Output : True The subarray [-5, 2, 3] has a sum of 0 Approach: Time and Space complexity : O(n) time | O(n) space - where n is the length of nums This implementation uses an unordered map to keep track of the prefix sum of the input array nums. We initialize the map with a key-value pair of 0 and -1, since a prefix sum of 0 indicates that the subarray from index 0 to -1 (i.e., an empty subarray) has a sum of 0. We then iterate through the input array nums, adding each element to the running sum sum and checking if the current sum is already in the map. If it is, then we've found a subarray whose sum is 0, so we add the starting and ending indices of the subarray to the result vector and break out of the loop. If we reach the end of the loop without finding a zero sum subarray, then we return an empty vector. Note that this implementation assumes that there is only one zero sum subarray in the input array. If there could be multiple zero sum subarrays, then we would need to modify the implementation to return all of them. */ package main import "fmt" func ZeroSumSubarray(nums []int) bool { sums := map[int]bool{0: true} currentSum := 0 for _, num := range nums { currentSum += num if _, sumIsInSet := sums[currentSum]; sumIsInSet { return true } sums[currentSum] = true } return false } func main() { nums := []int{-5, -5, 2, 3, -2} fmt.Println(ZeroSumSubarray(nums)) nums = []int{1, 2, 3, 4, 5} fmt.Println(ZeroSumSubarray(nums)) } ================================================ FILE: Hash Table/zero_sum_subarray.java ================================================ /* Q -> Hash Table: You're given a list of integers nums. Write a function that returns a boolean representing whether there exists a zero-sum subarray of nums in Java */ //Here's a Java code that implements a solution to find if there is a zero-sum subarray in the given list of // integers using a hash table. import java.util.*; public class ZeroSumSubarray { public static boolean hasZeroSumSubarray(int[] nums) { Set set = new HashSet<>(); int sum = 0; for (int i = 0; i < nums.length; i++) { sum += nums[i]; if (sum == 0 || set.contains(sum)) { return true; } set.add(sum); } return false; } public static void main(String[] args) { int[] nums = {4, -3, 2, 1, 8}; boolean hasZeroSumSubarray = hasZeroSumSubarray(nums); System.out.println(hasZeroSumSubarray); } } /* Explanation:- The above code uses a HashSet to keep track of the prefix sum of the elements of the input array. At each index, it checks if the sum up to that index has already been seen before in the HashSet. If it has been seen, it means that there exists a subarray with a zero sum. If the sum equals zero, then the subarray starts from the beginning of the array. The time complexity of this algorithm is O(n), where n is the size of the input array, since we traverse the input array only once. The space complexity is also O(n), since the HashSet can contain up to n elements in the worst case. */ ================================================ FILE: Hash Table/zero_sum_subarray.js ================================================ /* You're given a list of integers nums. Write a function that returns a boolean representing whether there exists a zero-sum subarray of nums Sample Input : = [-5, -5, 2, 3, -2] Output : True The subarray [-5, 2, 3] has a sum of 0 Time and Space complexity : O(n) time | O(n) space - where n is the length of nums Approach: The function takes in an array of integers nums. It initializes a set sums to keep track of the subarray sums we've seen so far, and a variable currentSum to keep track of the current sum as we loop through the array. We then loop through each number in the array, adding the current number to the current sum. We check if the current sum is zero or if we've seen it before (i.e. if it's already in the sums set). If so, we've found a zero-sum subarray, so we return true. If we loop through the entire array without finding a zero-sum subarray, we return false. */ function hasZeroSumSubarray(nums) { const sums = new Set(); // Initialize a set to keep track of the subarray sums let currentSum = 0; // Initialize a variable to keep track of the current sum for (const num of nums) { // Loop through each number in the array currentSum += num; // Add the current number to the current sum if (currentSum === 0 || sums.has(currentSum)) { // Check if the current sum is zero or if we've seen it before return true; // If so, we've found a zero-sum subarray, so return true } sums.add(currentSum); // Add the current sum to the set of subarray sums } return false; // If we loop through the entire array without finding a zero-sum subarray, return false } ================================================ FILE: Hash Table/zero_sum_subarray.py ================================================ ''' You're given a list of integers nums. Write a function that returns a boolean representing whether there exists a zero-sum subarray of nums Sample Input : = [-5, -5, 2, 3, -2] Output : True The subarray [-5, 2, 3] has a sum of 0 Approach: Time and Space complexity : O(n) time | O(n) space - where n is the length of nums This implementation uses an unordered map to keep track of the prefix sum of the input array nums. We initialize the map with a key-value pair of 0 and -1, since a prefix sum of 0 indicates that the subarray from index 0 to -1 (i.e., an empty subarray) has a sum of 0. We then iterate through the input array nums, adding each element to the running sum sum and checking if the current sum is already in the map. If it is, then we've found a subarray whose sum is 0, so we add the starting and ending indices of the subarray to the result vector and break out of the loop. If we reach the end of the loop without finding a zero sum subarray, then we return an empty vector. Note that this implementation assumes that there is only one zero sum subarray in the input array. If there could be multiple zero sum subarrays, then we would need to modify the implementation to return all of them. ''' def zero_sum_subarray(nums): # Initialize a set to keep track of previously encountered prefix sums. prefix_sums = set() prefix_sum = 0 # Loop through each number in the array. for num in nums: # Add the current number to the running prefix sum. prefix_sum += num # If the current prefix sum is in the set of previous prefix sums, # then we have found a subarray whose sum is zero. if prefix_sum in prefix_sums: return True # Add the current prefix sum to the set of previous prefix sums. prefix_sums.add(prefix_sum) # If no subarray whose sum is zero was found, return False. return False ================================================ FILE: Heaps/heap.cpp ================================================ /* Implement a Min-Heap class that supports Building a Min Heap from an input array of integers. Inserting integers in the heap. Removing the heap's minimum / root value. Peeking at the heap's minimum / root value. Sifting integers up and down the heap, which is to be used when inserting and removing values. Note that the heap should be represented in the form of an array. Explanation: The code snippet implements a MinHeap data structure in Go. - `NewMinHeap`: This function creates a new MinHeap from an input array and returns a pointer to the MinHeap object. It calls the `BuildHeap` method to construct the heap structure. - `BuildHeap`: This method constructs the heap by iteratively calling `siftDown` on each parent node starting from the last non-leaf node. - `siftDown`: This method corrects the heap property by moving an element down the heap until it reaches its correct position. It compares the element with its children and swaps it with the smaller child if necessary. - `siftUp`: This method corrects the heap property by moving an element up the heap until it reaches its correct position. It compares the element with its parent and swaps it if necessary. - `Peek`: This method returns the minimum element in the heap (the root of the heap) without removing it. - `Remove`: This method removes and returns the minimum element in the heap. It swaps the root with the last element, removes the last element from the heap, and then calls `siftDown` to maintain the heap property. - `Insert`: This method inserts a new element into the heap. It appends the element to the end of the heap and then calls `siftUp` to maintain the heap property. - `swap`: This method swaps two elements in the heap given their indices. - `length`: This method returns the number of elements in the heap. Overall, this code provides a basic implementation of a MinHeap data structure, allowing for efficient insertion, removal, and retrieval of the minimum element. BuildHeap: O(n) time | O(1) space - where n is the length of the input array SiftDown: O(log(n)) time | O(1) space - where n is the length of the heap SiftUp: O(log(n)) time | O(1) space - where n is the length of the heap Peek: O(1) time | O(1) space Remove: O(log(n)) time | O(1) space - where n is the length of the heap Insert: O(log(n)) time | O(1) space - where n is the length of the heap */ #include class MinHeap { public: std::vector heap; // The heap represented as a vector void buildHeap(std::vector& array) { int first = (array.size() - 2) / 2; // Start from the last parent node for (int currentIdx = first; currentIdx >= 0; currentIdx--) { siftDown(currentIdx, array.size() - 1); } } void siftDown(int currentIndex, int endIndex) { int childOneIdx = currentIndex * 2 + 1; // Calculate the index of the first child while (childOneIdx <= endIndex) { int childTwoIdx = -1; // Initialize the index of the second child if (currentIndex * 2 + 2 <= endIndex) { childTwoIdx = currentIndex * 2 + 2; // Calculate the index of the second child if it exists } int indexToSwap = childOneIdx; // Assume the first child is the one to swap with if (childTwoIdx > -1 && heap[childOneIdx] > heap[childTwoIdx]) { // If the second child exists and is smaller, update the index to swap with indexToSwap = childTwoIdx; } if (heap[currentIndex] > heap[indexToSwap]) { // If the current element is greater than the one to swap with, perform the swap swap(currentIndex, indexToSwap); currentIndex = indexToSwap; childOneIdx = currentIndex * 2 + 1; // Update the index of the first child } else { return; } } } void siftUp() { int currentIdx = heap.size() - 1; // Start from the last element int parentIdx = (currentIdx - 1) / 2; // Calculate the index of the parent while (currentIdx > 0) { int current = heap[currentIdx]; int parent = heap[parentIdx]; if (current < parent) { // If the current element is smaller than the parent, perform the swap swap(currentIdx, parentIdx); currentIdx = parentIdx; parentIdx = (currentIdx - 1) / 2; // Update the index of the parent } else { return; } } } int peek() { if (heap.empty()) { return -1; } return heap[0]; // Return the minimum element at the top of the heap } int remove() { int l = heap.size(); swap(0, l - 1); // Swap the root with the last element int peeked = heap[l - 1]; // Remove the last element (minimum) and store it heap.pop_back(); siftDown(0, l - 2); // Sift down the new root element return peeked; } void insert(int value) { heap.push_back(value); // Append the new element to the end of the heap siftUp(); // Sift up the new element to its correct position } void swap(int i, int j) { int temp = heap[i]; heap[i] = heap[j]; heap[j] = temp; // Swap elements at indices i and j } int length() { return heap.size(); // Return the number of elements in the heap } }; MinHeap* newMinHeap(std::vector& array) { MinHeap* heap = new MinHeap(); // Create a new MinHeap object heap->buildHeap(array); // Build the heap using the given array return heap; } ================================================ FILE: Heaps/heap.go ================================================ /* Implement a Min-Heap class that supports Building a Min Heap from an input array of integers. Inserting integers in the heap. Removing the heap's minimum / root value. Peeking at the heap's minimum / root value. Sifting integers up and down the heap, which is to be used when inserting and removing values. Note that the heap should be represented in the form of an array. Explanation: The code snippet implements a MinHeap data structure in Go. - `NewMinHeap`: This function creates a new MinHeap from an input array and returns a pointer to the MinHeap object. It calls the `BuildHeap` method to construct the heap structure. - `BuildHeap`: This method constructs the heap by iteratively calling `siftDown` on each parent node starting from the last non-leaf node. - `siftDown`: This method corrects the heap property by moving an element down the heap until it reaches its correct position. It compares the element with its children and swaps it with the smaller child if necessary. - `siftUp`: This method corrects the heap property by moving an element up the heap until it reaches its correct position. It compares the element with its parent and swaps it if necessary. - `Peek`: This method returns the minimum element in the heap (the root of the heap) without removing it. - `Remove`: This method removes and returns the minimum element in the heap. It swaps the root with the last element, removes the last element from the heap, and then calls `siftDown` to maintain the heap property. - `Insert`: This method inserts a new element into the heap. It appends the element to the end of the heap and then calls `siftUp` to maintain the heap property. - `swap`: This method swaps two elements in the heap given their indices. - `length`: This method returns the number of elements in the heap. Overall, this code provides a basic implementation of a MinHeap data structure, allowing for efficient insertion, removal, and retrieval of the minimum element. BuildHeap: O(n) time | O(1) space - where n is the length of the input array SiftDown: O(log(n)) time | O(1) space - where n is the length of the heap SiftUp: O(log(n)) time | O(1) space - where n is the length of the heap Peek: O(1) time | O(1) space Remove: O(log(n)) time | O(1) space - where n is the length of the heap Insert: O(log(n)) time | O(1) space - where n is the length of the heap */ package main // MinHeap represents a min heap data structure. type MinHeap []int // NewMinHeap creates a new MinHeap from an input array and returns a pointer to it. func NewMinHeap(array []int) *MinHeap { // Create a heap from the input array heap := MinHeap(array) ptr := &heap // Build the heap structure ptr.BuildHeap(array) return ptr } // BuildHeap constructs the heap by calling siftDown on each parent node. func (h *MinHeap) BuildHeap(array []int) { // Calculate the index of the first parent node first := (len(array) - 2) / 2 // Iterate over each parent node and sift it down for currentIdx := first + 1; currentIdx >= 0; currentIdx-- { h.siftDown(currentIdx, len(array)-1) } } // siftDown moves an element down the heap until it reaches its correct position. func (h *MinHeap) siftDown(currentIndex, endIndex int) { childOneIdx := currentIndex*2 + 1 for childOneIdx <= endIndex { childTwoIdx := -1 if currentIndex*2+2 <= endIndex { childTwoIdx = currentIndex*2 + 2 } indexToSwap := childOneIdx if childTwoIdx > -1 && (*h)[childOneIdx] > (*h)[childTwoIdx] { indexToSwap = childTwoIdx } if (*h)[currentIndex] > (*h)[indexToSwap] { h.swap(currentIndex, indexToSwap) currentIndex = indexToSwap childOneIdx = currentIndex*2 + 1 } else { return } } } // siftUp moves an element up the heap until it reaches its correct position. func (h *MinHeap) siftUp() { currentIdx := h.length() - 1 parentIdx := (currentIdx - 1) / 2 for currentIdx > 0 { current, parent := (*h)[currentIdx], (*h)[parentIdx] if current < parent { h.swap(currentIdx, parentIdx) currentIdx = parentIdx parentIdx = (currentIdx - 1) / 2 } else { return } } } // Peek returns the minimum element in the heap without removing it. func (h MinHeap) Peek() int { if len(h) == 0 { return -1 } return h[0] } // Remove removes and returns the minimum element in the heap. func (h *MinHeap) Remove() int { l := h.length() h.swap(0, l-1) peeked := (*h)[l-1] *h = (*h)[:l-1] h.siftDown(0, l-1) return peeked } // Insert inserts a new element into the heap. func (h *MinHeap) Insert(value int) { *h = append(*h, value) h.siftUp() } // swap swaps two elements in the heap given their indices. func (h MinHeap) swap(i, j int) { h[i], h[j] = h[j], h[i] } // length returns the number of elements in the heap. func (h MinHeap) length() int { return len(h) } ================================================ FILE: Heaps/heap.java ================================================ /* Implement a Min-Heap class that supports Building a Min Heap from an input array of integers. Inserting integers in the heap. Removing the heap's minimum / root value. Peeking at the heap's minimum / root value. Sifting integers up and down the heap, which is to be used when inserting and removing values. Note that the heap should be represented in the form of an array. Explanation: The code snippet implements a MinHeap data structure in Go. - `NewMinHeap`: This function creates a new MinHeap from an input array and returns a pointer to the MinHeap object. It calls the `BuildHeap` method to construct the heap structure. - `BuildHeap`: This method constructs the heap by iteratively calling `siftDown` on each parent node starting from the last non-leaf node. - `siftDown`: This method corrects the heap property by moving an element down the heap until it reaches its correct position. It compares the element with its children and swaps it with the smaller child if necessary. - `siftUp`: This method corrects the heap property by moving an element up the heap until it reaches its correct position. It compares the element with its parent and swaps it if necessary. - `Peek`: This method returns the minimum element in the heap (the root of the heap) without removing it. - `Remove`: This method removes and returns the minimum element in the heap. It swaps the root with the last element, removes the last element from the heap, and then calls `siftDown` to maintain the heap property. - `Insert`: This method inserts a new element into the heap. It appends the element to the end of the heap and then calls `siftUp` to maintain the heap property. - `swap`: This method swaps two elements in the heap given their indices. - `length`: This method returns the number of elements in the heap. Overall, this code provides a basic implementation of a MinHeap data structure, allowing for efficient insertion, removal, and retrieval of the minimum element. BuildHeap: O(n) time | O(1) space - where n is the length of the input array SiftDown: O(log(n)) time | O(1) space - where n is the length of the heap SiftUp: O(log(n)) time | O(1) space - where n is the length of the heap Peek: O(1) time | O(1) space Remove: O(log(n)) time | O(1) space - where n is the length of the heap Insert: O(log(n)) time | O(1) space - where n is the length of the heap */ import java.util.Arrays; public class MinHeap { private int[] heap; // The heap represented as an array private int size; // The current size of the heap public MinHeap(int[] array) { heap = Arrays.copyOf(array, array.length); // Create a copy of the input array size = array.length; buildHeap(); // Build the heap } private void buildHeap() { int first = (size - 2) / 2; // Start from the last parent node for (int currentIdx = first; currentIdx >= 0; currentIdx--) { siftDown(currentIdx); } } private void siftDown(int currentIndex) { int childOneIdx = currentIndex * 2 + 1; // Calculate the index of the first child while (childOneIdx < size) { int childTwoIdx = -1; // Initialize the index of the second child if (currentIndex * 2 + 2 < size) { childTwoIdx = currentIndex * 2 + 2; // Calculate the index of the second child if it exists } int indexToSwap = childOneIdx; // Assume the first child is the one to swap with if (childTwoIdx > -1 && heap[childOneIdx] > heap[childTwoIdx]) { // If the second child exists and is smaller, update the index to swap with indexToSwap = childTwoIdx; } if (heap[currentIndex] > heap[indexToSwap]) { // If the current element is greater than the one to swap with, perform the swap swap(currentIndex, indexToSwap); currentIndex = indexToSwap; childOneIdx = currentIndex * 2 + 1; // Update the index of the first child } else { return; } } } private void siftUp() { int currentIdx = size - 1; // Start from the last element int parentIdx = (currentIdx - 1) / 2; // Calculate the index of the parent while (currentIdx > 0) { int current = heap[currentIdx]; int parent = heap[parentIdx]; if (current < parent) { // If the current element is smaller than the parent, perform the swap swap(currentIdx, parentIdx); currentIdx = parentIdx; parentIdx = (currentIdx - 1) / 2; // Update the index of the parent } else { return; } } } public int peek() { if (size == 0) { return -1; } return heap[0]; // Return the minimum element at the top of the heap } public int remove() { swap(0, size - 1); // Swap the root with the last element int peeked = heap[size - 1]; // Remove the last element (minimum) and store it size--; siftDown(0); // Sift down the new root element return peeked; } public void insert(int value) { if (size >= heap.length) { heap = Arrays.copyOf(heap, size * 2); // Resize the array if necessary } heap[size] = value; // Append the new element to the end of the heap size++; siftUp(); // Sift up the new element to its correct position } private void swap(int i, int j) { int temp = heap[i]; heap[i] = heap[j]; heap[j] = temp; // Swap elements at indices i and j } public int length() { return size; // Return the number of elements in the heap } } public class Main { public static void main(String[] args) { int[] array = { 9, 4, 7, 1, -2, 6, 5 }; MinHeap minHeap = new MinHeap(array); System.out.println("Peek: " + minHeap.peek()); System.out.println("Remove: " + minHeap.remove()); System.out.println("Length: " + minHeap.length()); minHeap.insert(2); minHeap.insert(-5); System.out.println("Peek: " + minHeap.peek()); System.out.println("Length: " + minHeap.length()); } } ================================================ FILE: Heaps/heap.js ================================================ /* Implement a Min-Heap class that supports Building a Min Heap from an input array of integers. Inserting integers in the heap. Removing the heap's minimum / root value. Peeking at the heap's minimum / root value. Sifting integers up and down the heap, which is to be used when inserting and removing values. Note that the heap should be represented in the form of an array. Explanation: The code snippet implements a MinHeap data structure in Go. - `NewMinHeap`: This function creates a new MinHeap from an input array and returns a pointer to the MinHeap object. It calls the `BuildHeap` method to construct the heap structure. - `BuildHeap`: This method constructs the heap by iteratively calling `siftDown` on each parent node starting from the last non-leaf node. - `siftDown`: This method corrects the heap property by moving an element down the heap until it reaches its correct position. It compares the element with its children and swaps it with the smaller child if necessary. - `siftUp`: This method corrects the heap property by moving an element up the heap until it reaches its correct position. It compares the element with its parent and swaps it if necessary. - `Peek`: This method returns the minimum element in the heap (the root of the heap) without removing it. - `Remove`: This method removes and returns the minimum element in the heap. It swaps the root with the last element, removes the last element from the heap, and then calls `siftDown` to maintain the heap property. - `Insert`: This method inserts a new element into the heap. It appends the element to the end of the heap and then calls `siftUp` to maintain the heap property. - `swap`: This method swaps two elements in the heap given their indices. - `length`: This method returns the number of elements in the heap. Overall, this code provides a basic implementation of a MinHeap data structure, allowing for efficient insertion, removal, and retrieval of the minimum element. BuildHeap: O(n) time | O(1) space - where n is the length of the input array SiftDown: O(log(n)) time | O(1) space - where n is the length of the heap SiftUp: O(log(n)) time | O(1) space - where n is the length of the heap Peek: O(1) time | O(1) space Remove: O(log(n)) time | O(1) space - where n is the length of the heap Insert: O(log(n)) time | O(1) space - where n is the length of the heap */ class MinHeap { constructor(array) { this.heap = array.slice(); // Create a copy of the input array this.size = array.length; this.buildHeap(); // Build the heap } buildHeap() { const first = Math.floor((this.size - 2) / 2); // Start from the last parent node for (let currentIdx = first; currentIdx >= 0; currentIdx--) { this.siftDown(currentIdx); } } siftDown(currentIndex) { let childOneIdx = currentIndex * 2 + 1; // Calculate the index of the first child while (childOneIdx < this.size) { let childTwoIdx = -1; // Initialize the index of the second child if (currentIndex * 2 + 2 < this.size) { childTwoIdx = currentIndex * 2 + 2; // Calculate the index of the second child if it exists } let indexToSwap = childOneIdx; // Assume the first child is the one to swap with if (childTwoIdx > -1 && this.heap[childOneIdx] > this.heap[childTwoIdx]) { // If the second child exists and is smaller, update the index to swap with indexToSwap = childTwoIdx; } if (this.heap[currentIndex] > this.heap[indexToSwap]) { // If the current element is greater than the one to swap with, perform the swap this.swap(currentIndex, indexToSwap); currentIndex = indexToSwap; childOneIdx = currentIndex * 2 + 1; // Update the index of the first child } else { return; } } } siftUp() { let currentIdx = this.size - 1; // Start from the last element let parentIdx = Math.floor((currentIdx - 1) / 2); // Calculate the index of the parent while (currentIdx > 0) { const current = this.heap[currentIdx]; const parent = this.heap[parentIdx]; if (current < parent) { // If the current element is smaller than the parent, perform the swap this.swap(currentIdx, parentIdx); currentIdx = parentIdx; parentIdx = Math.floor((currentIdx - 1) / 2); // Update the index of the parent } else { return; } } } peek() { if (this.size === 0) { return -1; } return this.heap[0]; // Return the minimum element at the top of the heap } remove() { this.swap(0, this.size - 1); // Swap the root with the last element const peeked = this.heap[this.size - 1]; // Remove the last element (minimum) and store it this.size--; this.heap.length = this.size; // Resize the heap array this.siftDown(0); // Sift down the new root element return peeked; } insert(value) { this.heap.push(value); // Append the new element to the end of the heap this.size++; this.siftUp(); // Sift up the new element to its correct position } swap(i, j) { [this.heap[i], this.heap[j]] = [this.heap[j], this.heap[i]]; // Swap elements at indices i and j } length() { return this.size; // Return the number of elements in the heap } } // Example usage: const array = [9, 4, 7, 1, -2, 6, 5]; const minHeap = new MinHeap(array); console.log("Peek:", minHeap.peek()); console.log("Remove:", minHeap.remove()); console.log("Length:", minHeap.length()); minHeap.insert(2); minHeap.insert(-5); console.log("Peek:", minHeap.peek()); console.log("Length:", minHeap.length()); ================================================ FILE: Heaps/heap.py ================================================ ''' Implement a Min-Heap class that supports Building a Min Heap from an input array of integers. Inserting integers in the heap. Removing the heap's minimum / root value. Peeking at the heap's minimum / root value. Sifting integers up and down the heap, which is to be used when inserting and removing values. Note that the heap should be represented in the form of an array. Explanation: The code snippet implements a MinHeap data structure in Go. - `NewMinHeap`: This function creates a new MinHeap from an input array and returns a pointer to the MinHeap object. It calls the `BuildHeap` method to construct the heap structure. - `BuildHeap`: This method constructs the heap by iteratively calling `siftDown` on each parent node starting from the last non-leaf node. - `siftDown`: This method corrects the heap property by moving an element down the heap until it reaches its correct position. It compares the element with its children and swaps it with the smaller child if necessary. - `siftUp`: This method corrects the heap property by moving an element up the heap until it reaches its correct position. It compares the element with its parent and swaps it if necessary. - `Peek`: This method returns the minimum element in the heap (the root of the heap) without removing it. - `Remove`: This method removes and returns the minimum element in the heap. It swaps the root with the last element, removes the last element from the heap, and then calls `siftDown` to maintain the heap property. - `Insert`: This method inserts a new element into the heap. It appends the element to the end of the heap and then calls `siftUp` to maintain the heap property. - `swap`: This method swaps two elements in the heap given their indices. - `length`: This method returns the number of elements in the heap. Overall, this code provides a basic implementation of a MinHeap data structure, allowing for efficient insertion, removal, and retrieval of the minimum element. BuildHeap: O(n) time | O(1) space - where n is the length of the input array SiftDown: O(log(n)) time | O(1) space - where n is the length of the heap SiftUp: O(log(n)) time | O(1) space - where n is the length of the heap Peek: O(1) time | O(1) space Remove: O(log(n)) time | O(1) space - where n is the length of the heap Insert: O(log(n)) time | O(1) space - where n is the length of the heap ''' class MinHeap: def __init__(self): self.heap = [] # The heap represented as a list def build_heap(self, array): # Build the heap by calling sift_down on each parent node first = (len(array) - 2) // 2 # Start from the last parent node for current_idx in range(first, -1, -1): self.sift_down(current_idx, len(array) - 1) def sift_down(self, current_idx, end_idx): child_one_idx = current_idx * 2 + 1 # Calculate the index of the first child while child_one_idx <= end_idx: child_two_idx = -1 # Initialize the index of the second child if current_idx * 2 + 2 <= end_idx: child_two_idx = current_idx * 2 + 2 # Calculate the index of the second child if it exists index_to_swap = child_one_idx # Assume the first child is the one to swap with if child_two_idx > -1 and self.heap[child_one_idx] > self.heap[child_two_idx]: # If the second child exists and is smaller, update the index to swap with index_to_swap = child_two_idx if self.heap[current_idx] > self.heap[index_to_swap]: # If the current element is greater than the one to swap with, perform the swap self.swap(current_idx, index_to_swap) current_idx = index_to_swap child_one_idx = current_idx * 2 + 1 # Update the index of the first child else: return def sift_up(self): current_idx = len(self.heap) - 1 # Start from the last element parent_idx = (current_idx - 1) // 2 # Calculate the index of the parent while current_idx > 0: current, parent = self.heap[current_idx], self.heap[parent_idx] if current < parent: # If the current element is smaller than the parent, perform the swap self.swap(current_idx, parent_idx) current_idx = parent_idx parent_idx = (current_idx - 1) // 2 # Update the index of the parent else: return def peek(self): if not self.heap: return -1 return self.heap[0] # Return the minimum element at the top of the heap def remove(self): l = len(self.heap) self.swap(0, l - 1) # Swap the root with the last element peeked = self.heap.pop() # Remove the last element (minimum) and store it self.sift_down(0, l - 2) # Sift down the new root element return peeked def insert(self, value): self.heap.append(value) # Append the new element to the end of the heap self.sift_up() # Sift up the new element to its correct position def swap(self, i, j): self.heap[i], self.heap[j] = self.heap[j], self.heap[i] # Swap elements at indices i and j def length(self): return len(self.heap) # Return the number of elements in the heap def new_min_heap(array): heap = MinHeap() # Create a new MinHeap object heap.build_heap(array) # Build the heap using the given array return heap ================================================ FILE: Heaps/k_closest.py ================================================ class Solution: def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]: min_heap = [] # For each point, find distance from origin for x,y in points: dist = sqrt(x**2 + y**2) min_heap.append([dist,x,y]) # Pop 'k' smallest distances heapify(min_heap) res = [] for _ in range(k): _,x,y = heappop(min_heap) res.append([x,y]) return res ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) [2022] [Abhisek Kumar Gupta] Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Linked List/Add_two_numbers.py ================================================ '''Name : Abhinav kumar Github username : Abhinavcode13 Repository name : data-structures-and-algorithms Problem :Linked List: Add Two Numbers in Python Issue Number : #621 Problem statement : Explanation of the below python code : This code implements a solution for the "Add Two Numbers" problem on LeetCode, where the inputs are two non-empty linked lists representing two non-negative integers. The goal is to return the sum of the two integers as a linked list. The function takes in two linked lists, l1 and l2, and creates a new linked list represented by the variable 'dummy' which will store the sum. The current node of the new linked list is represented by the variable 'curr'. The variable 'carry' stores the carry-over value from the previous sum. The function then loops through both input linked lists, l1 and l2, as well as any carry-over value from the previous sum. It adds the values of the corresponding nodes in l1 and l2 (if they exist) and any carry-over value from the previous sum. It then calculates the carry-over value and the new value for the current node by dividing the sum by 10 and taking the remainder. The carry-over value is then updated for the next iteration of the loop. The new node with the calculated value is added to the end of the new linked list using the 'curr' variable. 'curr' is then updated to point to the new node. Once the loop is completed, the new linked list is returned, but without the initial 'dummy' node, as the first node in the list is actually the second node in the sequence. Overall, the function creates a new linked list representing the sum of the input linked lists, using a carry-over value to handle any values greater than 10. It runs in linear time in the length of the input linked lists, making it an efficient solution. ''' -------------------------------------------------------------------------//python code begins here---------------------------------------------------------------------------------------------------------------------------------------------------------- # Definition for singly-linked list. # class ListNode: # def __init__(self, val=0, next=None): # self.val = val # self.next = next class Solution: def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode: dummy = ListNode() curr = dummy carry = 0 while l1 or l2 or carry: val = carry if l1: val += l1.val l1 = l1.next if l2: val += l2.val l2 = l2.next carry, val = divmod(val, 10) curr.next = ListNode(val) curr = curr.next return dummy.next ================================================ FILE: Linked List/Intersection_LL.cpp ================================================ /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) { ListNode *pA = headA; ListNode *pB = headB; // Traverse both linked lists until they meet or reach the end (NULL) while (pA != pB) { // Move pointers to the next node pA = (pA != nullptr) ? pA->next : headB; pB = (pB != nullptr) ? pB->next : headA; } // Return the intersection node (or NULL if no intersection) return pA; } }; /* Explanation: The provided code implements the solution to find the intersection point of two singly-linked lists headA and headB. The getIntersectionNode function takes two ListNode pointers as parameters and returns the intersection node if one exists, or NULL if there is no intersection. The approach used here is based on the concept of "runner pointers." The pointers pA and pB start from the heads of the two linked lists and traverse through the lists. When a pointer reaches the end of its list, it is redirected to the head of the other list. This ensures that the pointers will meet at the intersection point if it exists, or they will both reach the end of the lists (NULL) simultaneously if there is no intersection. */ ================================================ FILE: Linked List/LFU_Cache.cpp ================================================ // Question--> LFU Cache // Design and implement a data structure for a Least Frequently Used (LFU) cache. // Implement the LFUCache class: // LFUCache(int capacity) Initializes the object with the capacity of the data structure. // int get(int key) Gets the value of the key if the key exists in the cache. Otherwise, returns -1. // void put(int key, int value) Update the value of the key if present, or inserts the key if not already present. When the cache reaches its capacity, it should invalidate and remove the least frequently used key before inserting a new item. For this problem, when there is a tie (i.e., two or more keys with the same frequency), the least recently used key would be invalidated. // To determine the least frequently used key, a use counter is maintained for each key in the cache. The key with the smallest use counter is the least frequently used key. // When a key is first inserted into the cache, its use counter is set to 1 (due to the put operation). The use counter for a key in the cache is incremented either a get or put operation is called on it. // The functions get and put must each run in O(1) average time complexity // solution --> //minFreq is the smallest frequency so far //The main idea is to put all keys with the same frequency to a linked list so the most recent one can be evicted; //mIter stored the key's position in the linked list; #include using namespace std; class LFUCache { int cap; int size; int minFreq; unordered_map> m; //key to {value,freq}; unordered_map::iterator> mIter; //key to list iterator; unordered_map> fm; //freq to key list; public: LFUCache(int capacity) { // initialize your data structure here cap=capacity; size=0; } int get(int key) { if(m.count(key)==0) return -1; // if key not found return -1; fm[m[key].second].erase(mIter[key]); //erase from old freq list m[key].second++; // increase freq; fm[m[key].second].push_back(key); // add to new freq list; mIter[key]=--fm[m[key].second].end();// point to new freq list; // if old min freq list empty, increase min freq; if(fm[minFreq].size()==0 ) minFreq++; return m[key].first; // return value } void put(int key, int value) { if(cap<=0) return; // corner case int storedValue=get(key); // get value if already present // if already present update value, increase freq; if(storedValue!=-1) { m[key].first=value; return; } // if capacity full, erase least freq used element from this list if(size>=cap ) { m.erase( fm[minFreq].front() ); mIter.erase( fm[minFreq].front() ); fm[minFreq].pop_front(); size--; } m[key]={value, 1}; // insert new key value pair with freq 1; fm[1].push_back(key); // add to freq list 1; mIter[key]=--fm[1].end(); // point to new freq list; minFreq=1; // since new element added min freq will be 1; size++; // increase size } }; // https://leetcode.com/problems/lfu-cache/ ================================================ FILE: Linked List/Linked_List_Component.java ================================================ /**PROBLEM * You are given the head of a linked list containing unique integer values and an integer array nums *that is a subset of the linked list values. *Return the number of connected components in nums where two values are connected if *they appear consecutively in the linked list. */ // SAMPLE I/O // Input: head = [0,1,2,3], nums = [0,1,3] // Output: 2 // Explanation: 0 and 1 are connected, so [0, 1] and [3] are the two connected components. // Approach /** * Creating a HashMap to store all the values of nums[] * Iterating list and if current node(head) we check if the hashmap contains the value] * if yes the we increment the ans by one and setting the flag to false * * * * Time Complexity: O(N) * Space Complexity : O(N) */ /** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val = val; } * ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } */ class Solution { public int numComponents(ListNode head, int[] nums) { // Create a HashMap to store the values from nums as keys and their indices as values HashMap hm = new HashMap<>(); for (int i = 0; i < nums.length; i++) { hm.put(nums[i], i); } boolean flag = true; // Flag to track if a connected component is found int ans = 0; // Variable to store the number of connected components // Traverse the linked list while (head != null) { // Check if the current node's value is present in the HashMap while (head != null && hm.containsKey(head.val)) { head = head.next; // Move to the next node // If this is the start of a new connected component, increment the answer if (flag) { ans += 1; flag = false; } } flag = true; // Reset the flag if (head != null) { head = head.next; // Move to the next node } } return ans; } } ================================================ FILE: Linked List/MiddleOfLinkedList.java ================================================ /* QUESTION and SAMPLE I/O Given the head of a singly linked list, return the middle node of the linked list. If there are two middle nodes, return the second middle node. Sample Input = [1,2,3,4,5] Sample Output = 3 APPROACH and EXPLANATION This program is done using two pointers: slow pointer and fast pointer. * Both pointer starts from the head node. * Slow pointer moves one step at a time. * Fast pointer moves two steps at a time. * As the fast pointer reaches the end, slow pointer will be at the middle of the linked list. * Accessing the slow pointer's value for 'val' will give you the value for the middle element of linked list. Time Complexity: O(n) Space Complexity: O(1) */ /** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val = val; } * ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } */ class Solution { public ListNode middleNode(ListNode head) { ListNode slow_pointer = head; //initialize slow pointer ListNode fast_pointer = head; //initialize fast pointer while(fast_pointer!=null && fast_pointer.next!=null){ slow_pointer = slow_pointer.next; //moves to next node fast_pointer = fast_pointer.next.next; //moves two nodes forward } return slow_pointer; } } ================================================ FILE: Linked List/MiddleofLL.py ================================================ class ListNode: def __init__(self, value=0, next=None): self.value = value self.next = next def find_middle(head): # Initialize slow and fast pointers to the head of the linked list slow_ptr = head fast_ptr = head # Traverse the linked list with slow and fast pointers while fast_ptr is not None and fast_ptr.next is not None: # Move slow pointer one step at a time slow_ptr = slow_ptr.next # Move fast pointer two steps at a time fast_ptr = fast_ptr.next.next # When the fast pointer reaches the end, the slow pointer will be at the middle return slow_ptr # Create a sample linked list: 1 -> 2 -> 3 -> 4 -> 5 head = ListNode(1) head.next = ListNode(2) head.next.next = ListNode(3) head.next.next.next = ListNode(4) head.next.next.next.next = ListNode(5) # Find the middle node of the linked list middle_node = find_middle(head) # Print the value of the middle node print("Middle Node:", middle_node.value) # Output: Middle Node: 3 ================================================ FILE: Linked List/RemoveKthNodeFromEnd.java ================================================ //Problem Number : 19 - Leetcode /** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val = val; } * ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } */ /* * Problem Description : * Given the head of a linked list, remove the nth node * from the end of the list and return its head. * * linkedlist = 1 -> 2 -> 3 -> 4 -> 5-> null * n = 2 */ /* * Approach we follow to solve this Problem. * Given : head of linkedlist and n = number of node to remove from end * Solution : * => Since we have to remove node from end , we need to calculate length of * linkedlist. * To do so lets create a function calcLength() , it take head node as parameter * and return length of linked list . * => Store the length of linkedlist in variable namely (len) by calling * function calcLength(). * => To make it simple , lets calculate position of node from start of * linkedlist by subtracting length of node with nth node to remove. * and store the result in another variable called target. [ target = len - n] * => Now, we check for target == 0 and if its true than we have to remove our * head and point the head to next element . * for ex : head.next = head * After just return our head because we have no need to check other part. * => create a pointer = 1 and store head of LikedList to temp variable . * => After that , we have to iterate over linkedlist till our temp is not equal * to null to find our target element. * (Note : target element is always prev of node of node to remove for ex : 1-> 2-> 3->4-> 5 * In this case 3 is our target because we have to remove 4.) * => for each iteration , we check if our target == pointer , In case its true * the we have to handle two case : * Case 1 : if our target node is last node of linked list ,then point temp to * null * explanation : 1-> 2-> 3-> 4-> 5 -> null * if our target is 5 the our new linkedlist will we like this : 1-> 2-> 3-> 4-> * null * Case 2 : Target is in middle of linkedlist then , update temp pointer to * temp.next.next * explanation : 1-> 2-> 3-> 4-> 5 -> null * let say we have to remove 3 : then temp = 2 * temp.next = 3 : node to remove * temp.next.next = 4 * output : 1-> 2-> 4-> 5 -> null * => increment pointer and update temp to next node. * => In last just return node */ class Solution { public int calcLength(ListNode head) { int cnt = 0; while (head != null) { cnt++; head = head.next; } return cnt; } public ListNode removeNthFromEnd(ListNode head, int n) { int len = calcLength(head); int target = len - n; if (target == 0) { head = head.next; return head; } int pointer = 1; ListNode temp = head; while (temp != null) { if (pointer == target) { ListNode key = temp.next; if (key == null) { temp = null; } else { temp.next = key.next; } } pointer++; temp = temp.next; } return head; } } ================================================ FILE: Linked List/Remove_nth_node_from_end.java ================================================ /** Problem :-Remove the nth node from end of the list https://leetcode.com/problems/remove-nth-node-from-end-of-list/description/ Approach:- 1. First find the length of the list and store it in a variable named 'len'. 2. Then traverse the list (len-n) 3. Now you are one node behind the node that we have to remove. 4. Now set the current node's next to the next.next i.e temp.next=temp.next.next. Time Complexity : O(N) we traverse the list for calculating its length and removing the node.Hence O(N). Space Complexity : O(1) No extra space is required Note :- The code is well documented. So take a look. */ /** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val = val; } * ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } */ class Solution { public ListNode removeNthFromEnd(ListNode head, int n) { if (head == null) return head; // If the head is null, return the head itself if (head.next == null) { // If there is only one node in the list if (n == 1) return null; // If n is 1, remove the node and return null return head; // Otherwise, return the head itself } ListNode temp = head; // Create a temporary node pointing to the head int len = size(head); // Get the length of the list if (len - n == 0) { // If the node to be removed is the head itself head = head.next; // Move the head to the next node return head; } len -= n; // Calculate the index of the previous node of the node to be removed for (int i = 1; i < len; i++) { // Traverse to the previous node so we can remove the next node temp = temp.next; } if (temp != null && temp.next != null) { // If the previous node and the node to be removed exist temp.next = temp.next.next; // Point the previous node to the node after the one to be removed } return head; // Return the updated head } int size(ListNode temp) { ListNode s = temp; int n = 0; while (s != null) { // Traverse the list to count the number of nodes s = s.next; n += 1; } return n; // Return the size of the list } } ================================================ FILE: Linked List/add_two_numbers.cpp ================================================ /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */ class Solution { public: ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) { ListNode *ansHead = new ListNode((l1->val + l2->val) % 10); ListNode *ans = ansHead; int carry = (l1->val + l2->val) / 10; l1 = l1->next; l2 = l2->next; while (l1 != NULL && l2 != NULL) { ListNode *temp = new ListNode((l1->val + l2->val + carry) % 10); carry = (l1->val + l2->val + carry) / 10; l1 = l1->next; l2 = l2->next; ans->next = temp; ans = temp; } while (l1 != NULL) { ListNode *temp = new ListNode((l1->val + carry) % 10); carry = (l1->val + carry) / 10; l1 = l1->next; ans->next = temp; ans = temp; } while (l2 != NULL) { ListNode *temp = new ListNode((l2->val + carry) % 10); carry = (l2->val + carry) / 10; l2 = l2->next; ans->next = temp; ans = temp; } if (carry > 0) { ListNode *temp = new ListNode(carry); ans->next = temp; ans = temp; } return ansHead; } ListNode *solve(ListNode *&head1, ListNode *&head2) { if (head1 == NULL) { return head2; } if (head2 == NULL) { return head1; } // linked list which contains the final answer ListNode *ansHead = NULL; ListNode *ansTail = NULL; int carry = 0; while (head1 != NULL && head2 != NULL) { int sum = head1->val + head2->val + carry; int digit = sum % 10; carry = sum / 10; ListNode *newNode = new ListNode(digit); if (ansHead == NULL) { ansHead = newNode; ansTail = newNode; } else { ansTail->next = newNode; ansTail = newNode; } head1 = head1->next; head2 = head2->next; } // head1 linked list pending to be solved while (head1 != NULL) { int sum = carry + head1->val; int digit = sum % 10; carry = sum / 10; ListNode *newNode = new ListNode(digit); if (ansHead == NULL) { ansHead = newNode; ansTail = newNode; } else { ansTail->next = newNode; ansTail = newNode; } head1 = head1->next; } // head2 linked list pending to be solved while (head2 != NULL) { int sum = carry + head2->val; int digit = sum % 10; carry = sum / 10; ListNode *newNode = new ListNode(digit); if (ansHead == NULL) { ansHead = newNode; ansTail = newNode; } else { ansTail->next = newNode; ansTail = newNode; } head2 = head2->next; } while (carry != 0) { int sum = carry; int digit = sum % 10; carry = sum / 10; ListNode *newNode = new ListNode(digit); if (ansHead == NULL) { ansHead = newNode; ansTail = newNode; } else { ansTail->next = newNode; ansTail = newNode; } } return ansHead; } }; ================================================ FILE: Linked List/add_two_numbers.js ================================================ // Linked List: Add Two Numbers in Javascript #622 /* You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order, and each of their nodes contains a single digit. Add the two numbers and return the sum as a linked list. You may assume the two numbers do not contain any leading zero, except the number 0 itself. Example 1: - Input: l1 = [2,4,3], l2 = [5,6,4] - Output: [7,0,8] - Explanation: 342 + 465 = 807. Example 2: - Input: l1 = [0], l2 = [0] - Output: [0] Example 3: - Input: l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9] - Output: [8,9,9,9,0,0,0,1] Constraints: - The number of nodes in each linked list is in the range [1, 100]. - 0 <= Node.val <= 9 - It is guaranteed that the list represents a number that does not have leading zeros. */ /** * Definition for singly-linked list. * function ListNode(val, next) { * this.val = (val===undefined ? 0 : val) * this.next = (next===undefined ? null : next) * } */ /** * @param {ListNode} l1 * @param {ListNode} l2 * @return {ListNode} */ var addTwoNumbers = function (l1, l2) { const head = new ListNode(); // creating a new LL. let temp = head; // assign head into temp to traverse the LL. let carry = 0; // to store carry that we will get during the add operation. while (l1 || l2) { // loop will get executed until one of the LL becomes null. let sum = carry; //perform summation with both LL one by one and assign it to the sum variable. if (l1) { sum += l1.val; l1 = l1.next; } if (l2) { sum += l2.val; l2 = l2.next; } // to store the carry part we will do sum/10 and store it's floor value to carry variable. For Example: /* if sum = 12 so, sum/10 = 1.2 and Math.floor(1.2) = 1 and we get our carry part. > carry = 1. */ carry = Math.floor(sum / 10); temp.next = new ListNode(sum % 10); // at this point we are removing carry part from sum and adding remaining part into the ll. temp = temp.next; } if (carry > 0) { // after the completion of summation, if there is any cary left it will be added to list. temp.next = new ListNode(carry); } return head.next; // return the head of the node so ll can be traverse. }; ================================================ FILE: Linked List/delete_kth_node.js ================================================ /*Name : Abhinav kumar Github username : Abhinavcode13 Repository name : data-structures-and-algorithms Problem : Remove Kth node from end in Javascript Issue Number : #687 Problem statement : Explanation of the below Javascript code : Define a Node class to represent each node in the linked list. Create a function called removeKthFromEnd that accepts the head of the linked list and the value of k as parameters. Now you can create a linked list and call the removeKthFromEnd function to remove the kth node from the end. */ class Node { constructor(value) { this.value = value; this.next = null; } } function removeKthFromEnd(head, k) { // Step 1: Create two pointers, fast and slow, and set them both to the head of the linked list. let fast = head; let slow = head; // Step 2: Move the fast pointer k nodes ahead. for (let i = 0; i < k; i++) { if (fast === null) { // The linked list does not have k nodes. return head; } fast = fast.next; } // Step 3: If the fast pointer reaches the end, the kth node from the end is the head itself. if (fast === null) { head = head.next; return head; } // Step 4: Move the fast and slow pointers together until the fast pointer reaches the end. while (fast.next !== null) { fast = fast.next; slow = slow.next; } // Step 5: Remove the kth node by updating the next pointer of the previous node. slow.next = slow.next.next; // Step 6: Return the modified linked list. return head; } // Example usage: const head = new Node(1); head.next = new Node(2); head.next.next = new Node(3); head.next.next.next = new Node(4); head.next.next.next.next = new Node(5); const k = 2; const modifiedHead = removeKthFromEnd(head, k); // Display the modified linked list. let current = modifiedHead; while (current !== null) { console.log(current.value); current = current.next; } /* OUTPUT: 1 2 3 5 */ ================================================ FILE: Linked List/double_linked_list.go ================================================ // Implementation of a doubly linked list in Go /* In this implementation, we define a Node struct that contains the data as well as pointers to the previous and next nodes. We also define a LinkedList struct that contains a pointer to the head and tail nodes. The AddNode method adds a new node to the end of the list. If the list is empty, the new node is set as both the head and tail. Otherwise, the new node is added after the current tail, and the current tail is set as the previous node of the new node. Finally, the new node is set as the new tail. The PrintList method traverses the list from the head node and prints the data of each node. In the main function, we create a new doubly linked list, add some nodes to it, and print the list. */ package main import "fmt" // Node represents a node in the doubly linked list type Node struct { data int prev *Node next *Node } // LinkedList represents the doubly linked list type LinkedList struct { head *Node tail *Node } // AddNode adds a new node to the end of the doubly linked list func (list *LinkedList) AddNode(data int) { // Create a new node newNode := &Node{data: data} // If the list is empty, set the new node as head and tail if list.head == nil { list.head = newNode list.tail = newNode return } // Set the new node as the next node of the current tail list.tail.next = newNode // Set the current tail as the previous node of the new node newNode.prev = list.tail // Set the new node as the new tail list.tail = newNode } // PrintList prints the doubly linked list func (list *LinkedList) PrintList() { // Start from the head node currNode := list.head // Traverse the list and print the data of each node for currNode != nil { fmt.Printf("%d ", currNode.data) currNode = currNode.next } } func main() { // Create a new doubly linked list list := &LinkedList{} // Add nodes to the list list.AddNode(1) list.AddNode(2) list.AddNode(3) // Print the list list.PrintList() } ================================================ FILE: Linked List/doubly_linked_list.cpp ================================================ /* Name : Aneesh Github username : 007aneesh Repository name : data-structures-and-algorithms Problem : Doubly linked list in c++ Issue Number : #975 Problem statement : Given an integer n representing the length of linked list and you have to print linked list from start and end. Sample testcases: Testcase 1 --> Input: 5 1 2 3 4 5 Output: Printing forwardly................................ 1 2 3 4 5 Printing backwardly................................ 5 4 3 2 1 Testcase 2 --> Input: 3 10 20 30 Output: Printing forwardly................................ 10 20 30 Printing backwardly................................ 30 20 10 Time Complexity = O(n) Space Complexity = O(n) Explanation: In the main function, the program first takes input n, the number of nodes to be inserted in the linked list. Then, in a loop, it takes n values as input and creates new nodes with those values. The nodes are then linked together to form a doubly linked list. Finally, it calls the PrintForward and PrintBackward functions to print the elements of the list in forward and backward order, respectively. */ // ----------------------------------------------------------------------------- code begins now! #include using namespace std; // Define a Node class class Node { public: int data; Node* next; Node* prev; Node(int d) { this->data = d; next = NULL; prev = NULL; } }; // Function to print the linked list forward void PrintForward(Node* head) { Node* traverse = head; while (traverse != NULL) { cout << traverse->data << endl; traverse = traverse->next; } } // Function to print the linked list backward void PrintBackward(Node* tail) { Node* traverse = tail; while (tail != NULL) { cout << traverse->data << endl; traverse = traverse->prev; } } int main() { int n, value; cin>>n; // Read the number of nodes to be created Node *head = nullptr; // Initialize head pointer Node *tail = nullptr; // Initialize tail pointer // Read 'n' values and create a doubly linked list for (int i = 0; i < n; i++) { cin >> value; // Read the value for the current node Node* newNode = new Node(value); // Create a new node with the value // If the list is empty, set the head and tail to the new node if (head == NULL) { head = newNode; tail = newNode; } else { // Add the new node to the end of the list newNode->next = nullptr; newNode->prev = tail; tail->next = newNode; tail = newNode; } } // Print the linked list forward cout << "Printing forwardly................................" << endl; PrintForward(head); // Print the linked list backward cout << "Printing backwardly................................" << endl; PrintBackward(tail); return 0; } ================================================ FILE: Linked List/doubly_linked_list.java ================================================ /*A Doubly Linked List is a data structure in which each node contains a reference to the previous node and the next node in the sequence. It extends the functionality of a Singly Linked List by allowing traversal in both forward and backward directions.The first node of the list is referred to as the head, and the last node is referred to as the tail. In this implementation, the Node class represents a node in the doubly linked list. Each node has a data field to store the value, and prev and next fields to maintain references to the previous and next nodes in the list.*/ class Node { int data; Node prev; Node next; public Node(int data) { this.data = data; this.prev = null; this.next = null; } } class DoublyLinkedList { Node head; public DoublyLinkedList() { this.head = null; } // Insert a new node at the end of the list public void insert(int data) { Node newNode = new Node(data); if (head == null) { // If the list is empty, make the new node the head head = newNode; } else { Node current = head; // Traverse to the end of the list while (current.next != null) { current = current.next; } // Link the new node to the last node current.next = newNode; newNode.prev = current; } } // Delete a node with the given data value public void delete(int data) { Node current = head; while (current != null) { if (current.data == data) { if (current.prev != null) { // If the node to be deleted is not the first node // Update the previous node's next reference current.prev.next = current.next; } else { // If the node to be deleted is the first node // Update the head reference to the next node head = current.next; } if (current.next != null) { // If the node to be deleted is not the last node // Update the next node's previous reference current.next.prev = current.prev; } break; } current = current.next; } } // Display the elements of the list public void display() { Node current = head; while (current != null) { System.out.print(current.data + " "); current = current.next; } System.out.println(); } } public class Main { public static void main(String[] args) { DoublyLinkedList list = new DoublyLinkedList(); list.insert(10); list.insert(20); list.insert(30); list.insert(40); list.display(); // Output: 10 20 30 40 list.delete(20); list.delete(40); list.display(); // Output: 10 30 } } /* Time complexity: O(n) for insertions and deletions, and O(1) for display. Space complexity: O(1) for all operations */ ================================================ FILE: Linked List/doubly_linked_list.js ================================================ class Node { constructor(val) { this.val = val this.prev = null this.next = null } } class DoublyLinkedList { constructor() { this.head = null this.tail = null this.length = 0 } insertAtTail(val) { const node = new Node(val) if (!this.head) { this.head = node this.tail = node this.length++ } else { node.prev = this.tail this.tail.next = node this.tail = node this.length++ } } insertAtHead(val) { const node = new Node(val) if (!this.head) { this.head = node this.tail = node this.length++ } else { node.next = this.head this.head.prev = node this.head = node this.length++ } } deleteAtTail() { if (!this.head) throw new Error("Empty Linked List!") if (this.length === 1) { this.head = null this.tail = null this.length-- } else { this.tail = this.tail.prev this.tail.next = null this.length-- } } deleteAtHead() { if (!this.head) throw new Error("Empty Linked List!") if (this.length === 1) { this.head = null this.tail = null this.length-- } else { this.head = this.head.next this.head.prev = null this.length-- } } print() { if (!this.head) console.log("Empty Linked List!") else { let curr = this.head let str = "null <- " while (curr) { str += curr.val if (curr.next) { str += " <-> " } else { str += " -> null" } curr = curr.next } console.log(str) } } getLength() { return this.length } getAtIdx(idx) { if (idx >= this.length || idx < 0) throw new Error("Index out of bounds!") else { let currIdx = 0 let curr = this.head while (currIdx < idx) { curr = curr.next currIdx++ } return curr.val } } putAtIdx(val, idx) { if (idx > this.length || idx < 0) throw new Error("Index out of bounds!") const node = new Node(val) if (idx === 0) { this.head.prev = node node.next = this.head this.head = node } else { let currIdx = 0 let curr = this.head while (currIdx < idx) { curr = curr.next currIdx++ } node.next = curr node.prev = curr.prev curr.prev.next = node } this.length++ } deleteAtIdx(idx) { if (idx >= this.length || idx < 0) throw new Error("Index out of bounds!") let currIdx = 0 let curr = this.head while (currIdx < idx) { curr = curr.next currIdx++ } curr.prev.next = curr.next curr.next.prev = curr.prev this.length-- } search(val) { let curr = this.head let currIdx = 0 while (curr) { if (curr.val === val) return currIdx curr = curr.next currIdx++ } return "Value not found" } toArray() { let arr = [] let curr = this.head while (curr) { arr.push(curr.val) curr = curr.next } return arr } } // SAMPLE USECASE const list = new DoublyLinkedList () list.insertAtHead (10) list.insertAtTail (20) list.insertAtHead (30) list.insertAtTail (40) list.putAtIdx (100, 2) list.print () ================================================ FILE: Linked List/floyds_cycle_detection.cpp ================================================ /* Initialize two-pointers and start traversing the linked list. Move the slow pointer by one position. Move the fast pointer by two positions. If both pointers meet at some point then a loop exists and if the fast pointer meets the end position then no loop exists. Time complexity: O(n), as the loop is traversed once. Auxiliary Space: O(1), only two pointers are used therefore constant space complexity. */ #include using namespace std; class Node { public: int data; Node* next; Node(int data) { this->data = data; next = NULL; } }; // initialize a new head for the linked list Node* head = NULL; class Linkedlist { public: // insert new value at the start void insert(int value) { Node* newNode = new Node(value); if (head == NULL) head = newNode; else { newNode->next = head; head = newNode; } } // detect if there is a loop // in the linked list bool detectLoop() { Node *slowPointer = head, *fastPointer = head; while (slowPointer != NULL && fastPointer != NULL && fastPointer->next != NULL) { slowPointer = slowPointer->next; fastPointer = fastPointer->next->next; if (slowPointer == fastPointer) return 1; } return 0; } }; int main() { Linkedlist l1; // inserting new values l1.insert(10); l1.insert(20); l1.insert(30); l1.insert(40); l1.insert(50); // adding a loop for the sake // of this example Node* temp = head; while (temp->next != NULL) temp = temp->next; temp->next = head; // loop added; if (l1.detectLoop()) cout << "Loop exists in the" << " Linked List" << endl; else cout << "Loop does not exists " << "in the Linked List" << endl; return 0; } ================================================ FILE: Linked List/floyds_cycle_detection.go ================================================ // Floyds Cycle Detection /* Explanation: The Floyd's cycle detection algorithm is a two-pointer algorithm used to detect if a linked list has a cycle. It works by initializing two pointers, slow and fast, both pointing to the head of the linked list. Then, it moves the slow pointer by one step and the fast pointer by two steps. If there is a cycle in the linked list, the fast pointer will eventually catch up to the slow pointer. If there is no cycle, the fast pointer will reach the end of the linked list. This algorithm has a time complexity of O(n) and a space complexity of O(1). In the implementation above, we define a Node struct to represent a node in the linked list. The hasCycle function takes the head of the linked list as input and returns a boolean indicating whether the linked list has a cycle or not. We initialize two pointers, slow and fast, both pointing to the head of the linked list. We then loop through the linked list while the fast pointer is not null and the next of the fast pointer is not null. In each iteration, we move the slow pointer by one step and the fast pointer by two steps. If the slow and fast pointers meet, we know there is a cycle and we return true. Otherwise, we continue the loop until the end of the linked list and return false. The time complexity of Floyd's cycle detection algorithm is O(n), where n is the number of nodes in the linked list. This is because in the worst case, we would need to traverse the entire linked list twice: once to reach the point where the cycle begins, and once more to detect the cycle. The space complexity of the algorithm is O(1), as we are only using a few constant extra variables to perform the detection, regardless of the size of the linked list. */ package main import "fmt" // Definition for singly-linked list. type Node struct { value int next *Node } // DetectCycle detects if there's a cycle in a linked list using Floyd's cycle detection algorithm func hasCycle(head *Node) bool { // Initialize slow and fast pointers slow, fast := head, head // Move slow and fast pointers until they meet or fast pointer reaches end of the list for fast != nil && fast.next != nil { slow = slow.next fast = fast.next.next // If slow and fast pointers meet, there's a cycle if slow == fast { return true } } // If fast pointer reaches end of the list, there's no cycle return false } func main() { // Create a linked list with a cycle head := &Node{value: 1} node2 := &Node{value: 2} node3 := &Node{value: 3} node4 := &Node{value: 4} node5 := &Node{value: 5} head.next = node2 node2.next = node3 node3.next = node4 node4.next = node5 node5.next = node2 // Create a cycle // Detect cycle in the linked list hasCycle := hasCycle(head) fmt.Println(hasCycle) // Output: true } ================================================ FILE: Linked List/floyds_cycle_detection.java ================================================ /* Initialize two-pointers and start traversing the linked list. Move the slow pointer by one position. Move the fast pointer by two positions. If both pointers meet at some point then a loop exists and if the fast pointer meets the end position then no loop exists. Time complexity: O(n), as the loop is traversed once. Auxiliary Space: O(1), only two pointers are used therefore constant space complexity. */ import java.util.*; class GFG{ static class Node { int data; Node next; Node(int data) { this.data = data; next = null; } }; // initialize a new head for the linked list static Node head = null; static class Linkedlist { // insert new value at the start void insert(int value) { Node newNode = new Node(value); if (head == null) head = newNode; else { newNode.next = head; head = newNode; } } // detect if there is a loop // in the linked list boolean detectLoop() { Node slowPointer = head, fastPointer = head; while (slowPointer != null && fastPointer != null && fastPointer.next != null) { slowPointer = slowPointer.next; fastPointer = fastPointer.next.next; if (slowPointer == fastPointer) return true; } return false; } } public static void main(String[] args) { Linkedlist l1 = new Linkedlist(); // inserting new values l1.insert(10); l1.insert(20); l1.insert(30); l1.insert(40); l1.insert(50); // adding a loop for the sake // of this example Node temp = head; while (temp.next != null) temp = temp.next; temp.next = head; // loop added; if (l1.detectLoop()) System.out.print("Loop exists in the" + " Linked List" +"\n"); else System.out.print("Loop does not exists " + "in the Linked List" +"\n"); } } ================================================ FILE: Linked List/intersection_of_two_linked_lists.cpp ================================================ /* Given the heads of two singly linked-lists headA and headB, return the node at which the two lists intersect. If the two linked lists have no intersection at all, return null. Input: intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3 Output: Intersected at '8' Input: intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1 Output: Intersected at '2' Constraints: > The number of nodes of listA is in the m. > The number of nodes of listB is in the n. > 1 <= m, n <= 3 * 104 > 1 <= Node.val <= 105 > 0 <= skipA < m > 0 <= skipB < n > intersectVal is 0 if listA and listB do not intersect. > intersectVal == listA[skipA] == listB[skipB] if listA and listB intersect. */ /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) { // Define basic variables needed int len1=0; int len2=0; ListNode* tempA=headA; ListNode* tempB=headB; // Calculate the length of both Linked Lists and store in len1 and len2 while(tempA!=NULL) { len1++; tempA=tempA->next; } while(tempB!=NULL) { len2++; tempB=tempB->next; } // Here, we assume that length of Linked-List A is less than or equal // to that of Linked List B if(len1>len2){ swap(headA,headB); } // Re-initialize variables tempA=headA; tempB=headB; int n=abs(len2-len1); while(n--) { tempB=tempB->next; } // Finally, Find the Intersection Node while(tempA!=tempB) { tempA=tempA->next; tempB=tempB->next; } // Return the final answer return tempA; } }; ================================================ FILE: Linked List/liniked_list_sort_list.cpp ================================================ /* Given the head of a linked list, return the list after sorting it in ascending order. Input: head = [4,2,1,3] Output: [1,2,3,4] Input: head = [-1,5,3,4,0] Output: [-1,0,3,4,5] Input: head = [] Output: [] Constraints: The number of nodes in the list is in the range [0, 5 * 104]. -105 <= Node.val <= 105 Follow up: Can you sort the linked list in O(n logn) time and O(1) memory (i.e. constant space)? */ /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */ class Solution { public: ListNode* compute_midpoint(ListNode* head){ if(head == NULL || head->next == NULL){ return head; } ListNode* slow = head; ListNode* fast = head->next; while(fast != NULL && fast->next != NULL){ slow = slow->next; fast = fast->next->next; } return slow; } ListNode* merge_two_lists(ListNode* A, ListNode* B){ if(A == NULL) return B; else if(B == NULL) return A; ListNode* C = NULL; if(A->val < B->val){ C = A; C->next = merge_two_lists(A->next, B); } else{ C = B; C->next = merge_two_lists(A, B->next); } return C; } ListNode* sortList(ListNode* head) { if(head == NULL || head->next == NULL) return head; ListNode* mid = compute_midpoint(head); ListNode* A = head; ListNode* B = mid->next; mid->next = NULL; A = sortList(A); B = sortList(B); ListNode* C = merge_two_lists(A, B); return C; } }; ================================================ FILE: Linked List/linked_list.go ================================================ // Basic implementation of a Singly Linked List PushBack function // Sample Input: PushBack(10), PushBack(20), PushBack(30) // Output: 10 -> 20 -> 30 -> package main import "fmt" // has two fields [data] of type integer and [next] of type *node (holds the memory address of next node) type node struct { data int next *node } //has three fields length, head and tail node type linkedlist struct { length int head *node tail *node } // Received in *node as an input and nadds it to linkedlist func (ll *linkedlist) PushBack(n *node) { // make incoming *node as head and tail node if ll.head == nil { ll.head = n ll.tail = n ll.length++ } else { // store memory address of incoming node and point tail to the incoming node ll.tail.next = n ll.tail = n ll.length++ } } // Return's the head if its present or returns error func (ll linkedlist) Front() (int, error) { if ll.head == nil { return 0, fmt.Errorf("No Front value present") } return ll.head.data, nil } // Return's the tail if its present or returns error func (ll linkedlist) Back() (int, error) { if ll.tail == nil { return 0, fmt.Errorf("No tail found") } return ll.tail.data, nil } // Delete front node if any func (ll *linkedlist) DeleteFromFront() { if ll.head == nil { fmt.Println("Nothing to delete") return } ll.head = ll.head.next ll.length--; } // Delete from back func (ll *linkedlist) DeleteFromBack() { if ll.head == nil { fmt.Println("Nothing to delete") return } var prev *node = nil var temp *node = ll.head // traverse to second last element for temp.next != nil { prev = temp temp = temp.next } // point second last to nil prev.next = nil } // Traverse the linkedlist and print data func (ll linkedlist) Display() { // go till last and keep printing, for ll.head != nil { fmt.Printf("%v -> ", ll.head.data) ll.head = ll.head.next // at some point this will point ti nil so loop terminates } } func main() { list := linkedlist{} msg, err := list.Back() fmt.Println("\n",msg, err) list.DeleteFromBack() msg, err = list.Front() fmt.Println("\n",msg, err) node1 := &node{data: 10} node2 := &node{data: 20} node3 := &node{data: 30} node4 := &node{data: 40} list.PushBack(node1) list.PushBack(node2) list.PushBack(node3) list.PushBack(node4) list.Display() msg, err = list.Front() fmt.Println("\n",msg, err) msg, err = list.Back() fmt.Println("\n",msg, err) list.DeleteFromFront() list.Display() list.DeleteFromBack() fmt.Println("") list.Display() } ================================================ FILE: Linked List/linked_list.js ================================================ /* insertAtTail (val) insertAtHead (val) deleteAtTail () deleteAtHead () getLength () print () getAtIdx (idx) putAtIdx (val, idx) deleteAtIdx (idx) search (val) toArray () reverse () */ class Node { constructor(val) { this.val = val this.next = null } } class LinkedList { constructor() { this.head = null this.tail = null this.length = 0 } insertAtTail(val) { const node = new Node(val) if (!this.head) { this.head = node this.tail = node this.length++ } else { this.tail.next = node this.tail = node this.length++ } } insertAtHead(val) { const node = new Node(val) if (!this.head) { this.head = node this.tail = node this.length++ } else { node.next = this.head this.head = node this.length++ } } print() { if (this.length === 0) console.log ("Empty Linked List!") else { let curr = this.head let str = '' while (curr) { str += curr.val + ' -> ' curr = curr.next } console.log(str + 'null') } } deleteAtTail() { if (!this.head) console.log("Empty Linked List!") else { let curr = this.head let prev = this.head while (curr.next) { prev = curr curr = curr.next } prev.next = null this.tail = prev this.length-- } } deleteAtHead() { if (!this.head) console.log("Empty Linked List!") else { let curr = this.head this.head = curr.next this.length-- } } getLength() { return this.length } getAtIdx (idx) { if (idx >= this.length || idx < 0) throw new Error ("Index out of bounds") else { let currIdx = 0 let curr = this.head while (currIdx < idx) { currIdx++ curr = curr.next } return curr.val } } putAtIdx (val, idx) { if (idx > this.length || idx < 0) throw new Error ("Index out of bounds") const node = new Node (val) if (!this.head) { this.head = node this.length++ return } let currIdx = 0 let curr = this.head let prev = this.head while (currIdx < idx) { currIdx++ prev = curr curr = curr.next } prev.next = node node.next = curr this.length++ } deleteAtIdx (idx) { if (idx >= this.length || idx < 0) throw new Error ("Index out of bounds") else { let currIdx = 0 let curr = this.head let prev = this.head while (currIdx < idx) { currIdx++ prev = curr curr = curr.next } prev.next = curr.next this.length-- } } search (val) { if (!this.head) return "Empty Linked List!" if (!this.head.next && this.head.val !== val) return null if (!this.head.next && this.head.val === val) return 0 let currIdx = 0 let curr = this.head while (curr) { if (curr.val === val) return currIdx currIdx++ curr = curr.next } return null } toArray () { const arr = [] if (!this.head) return null let curr = this.head while (curr) { arr.push (curr.val) curr = curr.next } return arr } reverse () { if (!this.head) throw new Error ("Empty Linked List") if (!this.head.next) return let prev = null let curr = this.head let next = curr.next while (curr) { next = curr.next curr.next = prev prev = curr curr = next } this.head = prev } } // SAMPLE USECASE const list = new LinkedList() list.insertAtTail (10) list.insertAtTail (20) list.insertAtTail (30) list.insertAtTail (40) list.insertAtTail (50) list.reverse () list.print () ================================================ FILE: Linked List/linked_list.py ================================================ # Implementation of Singly Linked List in Python ''' This implementation defines a Node class to represent a node in the linked list, and a LinkedList class to represent the linked list itself. The LinkedList class has methods to insert nodes at the beginning or end of the list, delete nodes from the list, and print the list. Each node has two attributes: its data and a reference to the next node in the list (next). The LinkedList class has one attribute: the head node of the list (head). When a new node is inserted at the beginning of the list, its next attribute is set to the current head node, and the head attribute is set to the new node. When a new node is inserted at the end of the list, it is appended to the last node's next attribute. When a node is deleted, the list is traversed to find the node with the given data, and its next attribute is set to the node after it. Finally, the print_list method traverses the list and prints each node's data. ''' # Define a class to represent a node in the linked list class Node: def __init__(self, data): # Each node has two attributes: data and a reference to the next node self.data = data self.next = None # Define a class to represent the linked list itself class LinkedList: def __init__(self): # Each linked list has one attribute: the head node (which initially is None) self.head = None # Method to insert a new node at the beginning of the linked list def insert_at_beginning(self, data): # Create a new node new_node = Node(data) # Set the next node of the new node to be the current head node new_node.next = self.head # Set the head node to be the new node self.head = new_node # Method to insert a new node at the end of the linked list def insert_at_end(self, data): # Create a new node new_node = Node(data) # If the list is empty, set the head node to be the new node if self.head is None: self.head = new_node return # Traverse to the last node in the list current_node = self.head while current_node.next: current_node = current_node.next # Set the next node of the last node to be the new node current_node.next = new_node # Method to delete the first occurrence of a node with the given data def delete_node(self, data): # If the list is empty, do nothing if self.head is None: return # If the node to be deleted is the head node, set the head node to be the next node if self.head.data == data: self.head = self.head.next return # Traverse the list to find the node to be deleted current_node = self.head while current_node.next: if current_node.next.data == data: current_node.next = current_node.next.next return current_node = current_node.next # Method to print the linked list def print_list(self): # Traverse the list and print each node's data current_node = self.head while current_node: print(current_node.data, end=" -> ") current_node = current_node.next print("None") ================================================ FILE: Linked List/linked_list_add_two_numbers.py ================================================ ''' You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order, and each of their nodes contains a single digit. Add the two numbers and return the sum as a linked list. You may assume the two numbers do not contain any leading zero, except the number 0 itself. Example 1: Input: l1 = [2,4,3], l2 = [5,6,4] Output: [7,0,8] Explanation: 342 + 465 = 807. Example 2: Input: l1 = [0], l2 = [0] Output: [0] Example 3: Input: l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9] Output: [8,9,9,9,0,0,0,1] Constraints: The number of nodes in each linked list is in the range [1, 100]. 0 <= Node.val <= 9 It is guaranteed that the list represents a number that does not have leading zeros. Complexity Time complexity : O(max(l1,l2)). Space complexity : O(n). ''' # Definition for singly-linked list. # class ListNode: # def __init__(self, val=0, next=None): # self.val = val # self.next = next class Solution: def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]: carry = 0 head = ListNode('*'); header_pointer = head; while(l1 != None or l2 != None): temp = carry if(l1 == None): temp += l2.val l2 = l2.next elif(l2 == None): temp += l1.val l1 = l1.next else: temp += l1.val+l2.val l1 = l1.next l2 = l2.next head.next = ListNode(temp % 10) carry = temp // 10 head = head.next if(carry != 0): head.next = ListNode(carry) head = head.next return header_pointer.next ================================================ FILE: Linked List/linked_list_compute_middle.cpp ================================================ // Given the head of a singly linked list, return the middle node of the linked list. // If there are two middle nodes, return the second middle node. // Input: head = [1,2,3,4,5] // Output: [3,4,5] // Explanation: The middle node of the list is node 3. // TIME ANS SPACE COMPLEXITY OF THE SOLUTION IS :: // Time complexity: O(n) // Space complexity: O(1) #include using namespace std; // creating Node manualy class Node { public: int data; Node *next; Node() { this->data = 0; this->next = NULL; } Node(int data) { this->data = data; this->next = NULL; } }; Node *middleNode(Node *head) { Node *slow = head; Node *fast = head; // Move slow pointer by one node at a time and fast pointer two nodes at a time. // While fast pointer reaches the end, slow pointer must have reached the middle node. while (fast != NULL) { fast = fast->next; if (fast != NULL) { fast = fast->next; slow = slow->next; } } return slow; // return slow as ans } int main() { // creating nodes. Node *first = new Node(1); Node *second = new Node(2); Node *third = new Node(3); Node *fourth = new Node(4); Node *fifth = new Node(5); // head of linked list Node *head = first; // Creating connection of nodes first->next = second; second->next = third; third->next = fourth; fourth->next = fifth; cout << "The middle node of the list is node " << middleNode(head)->data << endl; return 0; } ================================================ FILE: Linked List/linked_list_compute_middle.java ================================================ /* In this code, the ListNode class represents a node in the linked list, with an integer value val and a reference to the next node next. The FindMiddleLinkedList class has a findMiddle method that takes a ListNode as its input and returns the middle node of the linked list using the fast and slow pointers algorithm. */ class ListNode { int val; ListNode next; ListNode(int val) { this.val = val; this.next = null; } } class FindMiddleLinkedList { public ListNode findMiddle(ListNode head) { // Create two pointers, slow and fast, both initially pointing to the head of the linked list. ListNode slow = head; ListNode fast = head; // Move the slow pointer one step at a time and the fast pointer two steps at a time, until the fast pointer reaches the end of the list. while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; } // When the fast pointer reaches the end of the list, the slow pointer will be pointing to the middle of the list. return slow; } public static void main(String[] args) { // Create a sample linked list ListNode head = new ListNode(1); head.next = new ListNode(2); head.next.next = new ListNode(3); head.next.next.next = new ListNode(4); head.next.next.next.next = new ListNode(5); // Create an instance of the FindMiddleLinkedList class FindMiddleLinkedList f = new FindMiddleLinkedList(); // Call the findMiddle method to get the middle node of the linked list ListNode middleNode = f.findMiddle(head); // Print the value of the middle node System.out.println("The middle node has value: " + middleNode.val); } } ================================================ FILE: Linked List/linked_list_delete_at_any_pos.cpp ================================================ // Insert at any position in a LinkedList // Program Author : Abhisek Kumar Gupta #include using namespace std; class node{ public: int data; node* next; node(int d){ data = d; next = NULL; } }; int length(node* head){ int len = 0; while(head != NULL){ head = head->next; len++; } return len; } void insert_at_head(node *&head, int data){ node *n = new node(data); n->next = head; head = n; } void insert_at_tail(node *&head, int data){ if(head == NULL){ head = new node(data); return; } node *n = new node(data); node * temp = head; while(temp -> next != NULL){ temp = temp->next; } temp->next = n; } void insert_at_anypos(node *&head, int data, int pos){ if(head == NULL || pos == 0){ insert_at_head(head, data); } else if(pos > length(head)){ insert_at_tail(head, data); } else{ node* temp = head; node *n = new node(data); for(int i = 0; i < pos - 1; i++){ temp = temp->next; } n->next = temp->next; temp->next = n; } } void delete_at_head(node *&head){ if(head == NULL){ return; } node *temp = head; head = head->next; delete temp; } void delete_at_tail(node *&head){ node *prev = NULL; node *temp = head; while(temp->next != NULL){ prev = temp; temp = temp->next; } prev->next = NULL; delete temp; } void delete_at_any_pos(node *&head, int pos){ if(pos == 0 || pos == 1){ delete_at_head(head); return; } else if(pos > length(head)){ delete_at_tail(head); } else{ node *prev = NULL; node *temp = head; for(int i = 0; i < pos - 1; i++){ prev = temp; temp = temp->next; } prev->next = temp->next; delete temp; } } void print_linked_list(node *head){ while(head != NULL){ cout << head->data << "->"; head = head->next; } } int main(){ node *head = NULL; insert_at_head(head, 1); insert_at_head(head, 2); insert_at_head(head, 3); insert_at_anypos(head, 6, 10); insert_at_tail(head, 33); insert_at_head(head, 44); insert_at_anypos(head, 66, 4); insert_at_tail(head, 100); insert_at_anypos(head, 11, 3); print_linked_list(head); delete_at_any_pos(head, 2); delete_at_any_pos(head, 3); delete_at_any_pos(head, 4); insert_at_anypos(head, 22, 2); cout << endl; print_linked_list(head); delete_at_any_pos(head, 2); cout << endl; print_linked_list(head); return 0; } ================================================ FILE: Linked List/linked_list_delete_at_head.cpp ================================================ // Delete from Head in a LinkedList // Program Author : Abhisek Kumar Gupta #include using namespace std; class node{ public: int data; node* next; node(int d){ data = d; next = NULL; } }; void insert_at_head(node *&head, int data){ node *n = new node(data); n->next = head; head = n; } void delete_at_head(node *&head){ if(head == NULL){ return; } node *temp = head; head = head->next; delete temp; } void print_linked_list(node *head){ while(head != NULL){ cout << head->data << "->"; head = head->next; } } int main(){ node *head = NULL; insert_at_head(head, 1); insert_at_head(head, 2); insert_at_head(head, 3); print_linked_list(head); cout << endl; delete_at_head(head); delete_at_head(head); print_linked_list(head); return 0; } ================================================ FILE: Linked List/linked_list_delete_at_tail.cpp ================================================ // Delete from Tail in a LinkedList // Program Author : Abhisek Kumar Gupta #include using namespace std; class node{ public: int data; node* next; node(int d){ data = d; next = NULL; } }; void insert_at_tail(node *&head, int data){ if(head == NULL){ head = new node(data); return; } node *n = new node(data); node * temp = head; while(temp -> next != NULL){ temp = temp->next; } temp->next = n; } void delete_at_tail(node *&head){ node *prev = NULL; node *temp = head; while(temp->next != NULL){ prev = temp; temp = temp->next; } prev->next = NULL; delete temp; } void print_linked_list(node *head){ while(head != NULL){ cout << head->data << "->"; head = head->next; } } int main(){ node *head = NULL; insert_at_tail(head, 20); insert_at_tail(head, 33); insert_at_tail(head, 100); insert_at_tail(head, 200); insert_at_tail(head, 201); insert_at_tail(head, 202); print_linked_list(head); cout << endl; delete_at_tail(head); delete_at_tail(head); print_linked_list(head); return 0; } ================================================ FILE: Linked List/linked_list_delete_node.go ================================================ /* We are given a pointer to a node (not the tail node) in a singly linked list. Delete that node from the linked list. */ package main /* Approach: 1. We can move the data from the next node into the current node 2. Delete the next node. Time Complexity: O(1). Space Complexity: O(1). */ func deleteNode(node *ListNode) { temp = node.next node.data = node.next.data node.next = temp.next temp = nil } ================================================ FILE: Linked List/linked_list_even_or_odd.go ================================================ /* Check whether the given Linked List length is even or odd? Approach: Use a 2x pointer. Take a pointer that moves at 2x [two nodes at a time]. At the end, if the length is even, then the pointer will be nil; otherwise it will point to the last node. */ // Time Complexity: O(⌊n/2⌋) ≈O(n). Space Complexity: O(1). package main // has two fields [data] of type integer and [next] of type *node (holds the memory address of next node) type node struct { data int next *node } //has three fields length, head and tail node type LinkedList struct { length int head *node tail *node } func (ll *LinkedList) IsLengthEven() bool { current := ll.head for current != nil && current.next != nil { current = current.next.next } if current != nil { return false } return true } ================================================ FILE: Linked List/linked_list_find_length.cpp ================================================ // Find length of a LinkedList // Program Author : Abhisek Kumar Gupta #include using namespace std; class node{ public: int data; node* next; node(int d){ data = d; next = NULL; } }; int length(node* head){ int len = 0; while(head != NULL){ head = head->next; len++; } return len; } void insert_at_head(node *&head, int data){ node *n = new node(data); n->next = head; head = n; } void print_linked_list(node *head){ while(head != NULL){ cout << head->data << "->"; head = head->next; } } int main(){ node *head = NULL; insert_at_head(head, 1); insert_at_head(head, 2); insert_at_head(head, 3); print_linked_list(head); int len = length(head); cout << endl < using namespace std; class node{ public: int data; node* next; node(int d){ data = d; next = NULL; } }; int length(node* head){ int len = 0; while(head != NULL){ head = head->next; len++; } return len; } void insert_at_head(node *&head, int data){ node *n = new node(data); n->next = head; head = n; } void insert_at_tail(node *&head, int data){ if(head == NULL){ head = new node(data); return; } node *n = new node(data); node * temp = head; while(temp -> next != NULL){ temp = temp->next; } temp->next = n; } void insert_at_anypos(node *&head, int data, int pos){ if(head == NULL || pos == 0){ insert_at_head(head, data); } else if(pos > length(head)){ insert_at_tail(head, data); } else{ node* temp = head; node *n = new node(data); for(int i = 0; i < pos - 1; i++){ temp = temp->next; } n->next = temp->next; temp->next = n; } } void print_linked_list(node *head){ while(head != NULL){ cout << head->data << "->"; head = head->next; } } int main(){ node *head = NULL; insert_at_head(head, 1); insert_at_head(head, 2); insert_at_head(head, 3); insert_at_anypos(head, 6, 10); insert_at_tail(head, 33); insert_at_head(head, 44); insert_at_anypos(head, 66, 4); insert_at_tail(head, 100); insert_at_anypos(head, 11, 3); print_linked_list(head); return 0; } ================================================ FILE: Linked List/linked_list_insert_at_head.cpp ================================================ // Search recursively in a LinkedList // Program Author : Abhisek Kumar Gupta #include using namespace std; class node{ public: int data; node* next; node(int d){ data = d; next = NULL; } }; void insert_at_head(node *&head, int data){ node *n = new node(data); n->next = head; head = n; } void print_linked_list(node *head){ while(head != NULL){ cout << head->data << "->"; head = head->next; } } int main(){ node *head = NULL; insert_at_head(head, 1); insert_at_head(head, 2); insert_at_head(head, 3); print_linked_list(head); return 0; } ================================================ FILE: Linked List/linked_list_insert_at_tail.cpp ================================================ // Insert at Tail in a LinkedList // Program Author : Abhisek Kumar Gupta #include using namespace std; class node{ public: int data; node* next; node(int d){ data = d; next = NULL; } }; void insert_at_tail(node *&head, int data){ if(head == NULL){ head = new node(data); return; } node *n = new node(data); node * temp = head; while(temp -> next != NULL){ temp = temp->next; } temp->next = n; } void print_linked_list(node *head){ while(head != NULL){ cout << head->data << "->"; head = head->next; } } int main(){ node *head = NULL; insert_at_tail(head, 20); insert_at_tail(head, 33); insert_at_tail(head, 100); insert_at_tail(head, 200); print_linked_list(head); return 0; } ================================================ FILE: Linked List/linked_list_kth_from_end.go ================================================ // Finding Kth node from end of a LinkedList package main /* Approach 1. Make two pointers slow and fast and keep them both on the head of the linked list. 2. Now, move the fast pointer k steps away from the slow pointer. 3. Now, move the fast pointer and the slow pointer each one step at a time till the fast pointer reaches the tail of the linked list. When the fast pointer reaches the tail, the node at which the slow pointer resides will be our answer. Time and space complexity Time complexity of the solution is O(n) as we have traversed the linked list once. We have traversed it in two parts. First we traversed k elements and then we traversed the remaining (size minus k) elements. Space complexity is O(1) as we have not used any extra space for the solution. */ func kthFromEnd(head *ListNode, n int) *ListNode { first, second := head, head for ; n > 0; n-- { second = second.next } for ; second.next != nil; first, second = first.next, second.next { } first.next = first.next.next return first } ================================================ FILE: Linked List/linked_list_kth_node_from_end.cpp ================================================ // Finding Kth node from end of a LinkedList // Program Author : Abhisek Kumar Gupta #include using namespace std; class node{ public: int data; node* next; node(int d){ data = d; next = NULL; } }; void insert_at_tail(node *&head, int data){ if(head == NULL){ head = new node(data); return; } node *n = new node(data); node * temp = head; while(temp -> next != NULL){ temp = temp->next; } temp->next = n; } void print_linked_list(node *head){ while(head != NULL){ cout << head->data << "->"; head = head->next; } } void makeLinkedList(node *&head){ int data; cin >> data; while(data != -1){ insert_at_tail(head, data); cin >> data; } } node* kth_node_from_end(node *head, int k){ if(head->next == NULL || head == NULL){ return head; } node *slow = head; node *fast = head; while(k--){ fast = fast->next; } while(fast){ fast = fast->next; slow = slow->next; } return slow; } int main(){ node *head = NULL; makeLinkedList(head); print_linked_list(head); int k; cin >> k; node *kth_node = kth_node_from_end(head, k); cout << endl; cout << kth_node->data << endl; return 0; } ================================================ FILE: Linked List/linked_list_linear_search.cpp ================================================ // Search linearly in a LinkedList // Program Author : Abhisek Kumar Gupta #include using namespace std; class node{ public: int data; node* next; node(int d){ data = d; next = NULL; } }; bool linear_search(node *head, int key){ if(head == NULL){ return false; } else{ node *temp = head; while(temp != NULL){ if(temp->data == key){ return true; } temp = temp->next; } } return false; } void insert_at_head(node *&head, int data){ node *n = new node(data); n->next = head; head = n; } void print_linked_list(node *head){ while(head != NULL){ cout << head->data << "->"; head = head->next; } } int main(){ node *head = NULL; insert_at_head(head, 1); insert_at_head(head, 2); insert_at_head(head, 3); print_linked_list(head); cout << endl; if(linear_search(head, 1)){ cout << "Found"; } else{ cout << "Not Found"; } return 0; } ================================================ FILE: Linked List/linked_list_merge_k_sorted_lists.cpp ================================================ // Merge K sorted LinkedLists // Program Author : Abhisek Kumar Gupta #include using namespace std; class node{ public: int data; node* next; node(int d){ data = d; next = NULL; } }; void insert_at_tail(node *&head, int data){ if(head == NULL){ head = new node(data); return; } node *n = new node(data); node * temp = head; while(temp -> next != NULL){ temp = temp->next; } temp->next = n; } void print_linked_list(node *head){ while(head != NULL){ cout << head->data << "->"; head = head->next; } } void makeLinkedList(node *&head){ int data; cin >> data; while(data != -1){ insert_at_tail(head, data); cin >> data; } } istream& operator>>(istream &is, node *&head){ makeLinkedList(head); return is; } ostream& operator<<(ostream &os, node *&head){ print_linked_list(head); return os; } node* merge_two_lists(node *a, node *b){ if(a == NULL){ return b; } else if(b == NULL){ return a; } node *c = NULL; if(a->data < b->data){ c = a; c->next = merge_two_lists(a->next, b); } else{ c = b; c->next = merge_two_lists(a, b->next); } return c; } node* merge_k_lists(node *arr[], int last){ while(last != 0){ int i = 0, j = last; while(i < j){ arr[i] = merge_two_lists(arr[i], arr[j]); i++; j--; if(i >= j) last = j; } } return arr[0]; } int main(){ int n; cin >> n; node *arr[n]; for(int i = 0; i < n; i++){ arr[i] = NULL; } for(int i = 0; i < n; i++){ cin >> arr[i]; } cout << endl; for(int i = 0; i < n; i++){ cout << arr[i] << endl;; } node *head = merge_k_lists(arr, n-1); cout << head; return 0; } ================================================ FILE: Linked List/linked_list_merge_two_sorted_linked_list.cpp ================================================ // Merge two sorted LinkedList // Program Author : Abhisek Kumar Gupta #include using namespace std; class node{ public: int data; node* next; node(int d){ data = d; next = NULL; } }; void insert_at_tail(node *&head, int data){ if(head == NULL){ head = new node(data); return; } node *n = new node(data); node * temp = head; while(temp -> next != NULL){ temp = temp->next; } temp->next = n; } void print_linked_list(node *head){ while(head != NULL){ cout << head->data << "->"; head = head->next; } } void makeLinkedList(node *&head){ int data; cin >> data; while(data != -1){ insert_at_tail(head, data); cin >> data; } } istream& operator>>(istream &is, node *&head){ makeLinkedList(head); return is; } ostream& operator<<(ostream &os, node *&head){ print_linked_list(head); return os; } node* merge_two_lists(node *a, node *b){ if(a == NULL){ return b; } else if(b == NULL){ return a; } node *c = NULL; if(a->data < b->data){ c = a; c->next = merge_two_lists(a->next, b); } else{ c = b; c->next = merge_two_lists(a, b->next); } return c; } int main(){ node *head = NULL; node *head2 = NULL; cin >> head >> head2; cout << head << endl << head2; cout << endl; node *x = merge_two_lists(head, head2); cout << x << endl; return 0; } ================================================ FILE: Linked List/linked_list_mergesort_an_unsorted_list.cpp ================================================ // MergeSort an unsorted LinkedList // Program Author : Abhisek Kumar Gupta #include using namespace std; class node{ public: int data; node* next; node(int d){ data = d; next = NULL; } }; void insert_at_tail(node *&head, int data){ if(head == NULL){ head = new node(data); return; } node *n = new node(data); node * temp = head; while(temp -> next != NULL){ temp = temp->next; } temp->next = n; } void print_linked_list(node *head){ while(head != NULL){ cout << head->data << "->"; head = head->next; } } void makeLinkedList(node *&head){ int data; cin >> data; while(data != -1){ insert_at_tail(head, data); cin >> data; } } node* compute_midpoint(node *head){ if(head->next == NULL || head == NULL){ return head; } node *slow = head; node *fast = head->next; while(fast != NULL && fast->next != NULL){ fast = fast->next->next; slow = slow->next; } return slow; } istream& operator>>(istream &is, node *&head){ makeLinkedList(head); return is; } ostream& operator<<(ostream &os, node *&head){ print_linked_list(head); return os; } node* merge_two_lists(node *a, node *b){ if(a == NULL){ return b; } else if(b == NULL){ return a; } node *c = NULL; if(a->data < b->data){ c = a; c->next = merge_two_lists(a->next, b); } else{ c = b; c->next = merge_two_lists(a, b->next); } return c; } node* mergeSort(node* head){ if(head == NULL || head->next == NULL){ return head; } node *mid = compute_midpoint(head); node *a = head; node *b = mid->next; mid->next = NULL; a = mergeSort(a); b = mergeSort(b); node *c = merge_two_lists(a, b); return c; } int main(){ node *head = NULL; node *head2 = NULL; cin >> head; cout << head << endl; node *x = mergeSort(head); cout << x << endl; return 0; } ================================================ FILE: Linked List/linked_list_middle.go ================================================ /** * Given the head of a singly linked list, return the middle node of the linked list. If there are two middle nodes, return the second middle node. * * Example #1: * Input: head = [1,2,3,4,5] * Output: [3,4,5] * Explanation: The middle node of the list is node 3. * * Example #2: * Input: head = [1,2,3,4,5,6] * Output: [4,5,6] * Explanation: Since the list has two middle nodes with values 3 and 4, we return the second one. * * Constraints: * The number of nodes in the list is in the range [1, 100]. * 1 <= Node.val <= 100 * * Definition for singly-linked list. * type ListNode struct { * Val int * Next *ListNode * } * * Solution: to find the middle of a linked list, we can use two pointers ("slow" and "fast"). They should both start at head. * While traversing the list, we move the slow pointer one node at a time and the fast pointer two nodes at a time. * When the fast pointer reaches the end of the linked list, the slow pointer will be pointing to the middle node. * * Time complexity: O(n), space complexity: O(n) */ func middleNode(head *ListNode) *ListNode { slow, fast := head, head for (fast != nil && fast.Next != nil) { slow = slow.Next fast = fast.Next.Next } return slow } ================================================ FILE: Linked List/linked_list_middle.js ================================================ /* Given the head of a singly linked list, return the middle node of the linked list. If there are two middle nodes, return the second middle node. Example 1: Input: head = [1,2,3,4,5] Output: [3,4,5] Explanation: The middle node of the list is node 3. Example 2: Input: head = [1,2,3,4,5,6] Output: [4,5,6] Explanation: Since the list has two middle nodes with values 3 and 4, we return the second one. Constraints: The number of nodes in the list is in the range [1, 100]. 1 <= Node.val <= 100 */ /** * Definition for singly-linked list. * function ListNode(val, next) { * this.val = (val===undefined ? 0 : val) * this.next = (next===undefined ? null : next) * } */ /** * @param {ListNode} head * @return {ListNode} */ var middleNode = function(head) { let curr = head // Initialize the current node to the head let size = 0 // Initialize the size to 0 // Traverse the Linked List while (curr) { size += 1 // Keep track of the size of the Linked List curr = curr.next } // curr is equal to null at the end of the loop let mid = Math.floor(size / 2) + 1 // Calculate the middle of the List let count = 0 curr = head // Reset current to head // Traverse the Linked List while (curr) { count += 1 // Keep track of the number of visited nodes in the List if (count === mid) return curr // When middle node found, return it curr = curr.next } }; ================================================ FILE: Linked List/linked_list_odd_even.cpp ================================================ // OddEven LinkedList // Program Author : Abhisek Kumar Gupta #include using namespace std; class node{ public: int data; node* next; node(int d){ data = d; next = NULL; } }; void insert_at_tail(node *&head, int data){ if(head == NULL){ head = new node(data); return; } node *n = new node(data); node * temp = head; while(temp -> next != NULL){ temp = temp->next; } temp->next = n; } void print_linked_list(node *head){ cout << endl; while(head != NULL){ cout << head->data << "->"; head = head->next; } } void makeLinkedList(node *&head){ int data; cin >> data; while(data != -1){ insert_at_tail(head, data); cin >> data; } } node* oddEvenLinkedList(node *head){ if(head == NULL || head->next == NULL){ return head; } node *odd = head; node *even = head->next; node *evenhead = even; while(even != NULL && even->next != NULL){ odd->next = even->next; odd = odd->next; even->next = odd->next; even = even->next; } odd->next = evenhead; return head; } int main(){ node *head = NULL; makeLinkedList(head); print_linked_list(head); oddEvenLinkedList(head); print_linked_list(head); return 0; } ================================================ FILE: Linked List/linked_list_pallindrome.cpp ================================================ /* Given the head of a singly linked list, return true if it is a palindrome or false otherwise. Example 1: Input: head = [1,2,2,1] Output: true Example 2: Input: head = [1,2] Output: false Constraints: The number of nodes in the list is in the range [1, 105]. 0 <= Node.val <= 9 Follow up: Could you do it in O(n) time and O(1) space? */ /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */ // Time Complexity : O(n) Space Complexity: O(n) // TODO: Implement O(n) Time and O(1) space solution class Solution { public: bool isPalindrome(ListNode* head) { vector contents; while(head != NULL) { int val = head->val; contents.push_back(val); head = head->next; } int low = 0, high = contents.size() - 1; while(low <= high) { if(contents[low] == contents[high]){ low++; high--; } else { return false; } } return true; } }; ================================================ FILE: Linked List/linked_list_recursive_search.cpp ================================================ // Search recursively in a LinkedList // Program Author : Abhisek Kumar Gupta #include using namespace std; class node{ public: int data; node* next; node(int d){ data = d; next = NULL; } }; bool recursive_search(node *head, int key){ if(head == NULL){ return false; } if(head->data == key){ return true; } else{ recursive_search(head->next, key); } } void insert_at_head(node *&head, int data){ node *n = new node(data); n->next = head; head = n; } void print_linked_list(node *head){ while(head != NULL){ cout << head->data << "->"; head = head->next; } } int main(){ node *head = NULL; insert_at_head(head, 1); insert_at_head(head, 2); insert_at_head(head, 3); print_linked_list(head); cout << endl; if(recursive_search(head, 3)){ cout << "Found"; } else{ cout << "Not Found"; } return 0; } ================================================ FILE: Linked List/linked_list_remove_dups.cpp ================================================ // Remove duplicates from an unsorted linked list // Program Author : Abhisek Kumar Gupta #include using namespace std; class Node{ public: int data; Node *next; Node(int d){ data = d; next = NULL; } }; void insert_at_tail(Node *&head, int data){ if(head == NULL){ head = new Node(data); return; } Node *n = new Node(data); Node *temp = head; while(temp->next != NULL){ temp = temp->next; } temp->next = n; } void print_linked_list(Node *head){ while(head != NULL){ cout << head->data << "->"; head = head->next; } } void make_linked_list(Node *&head){ int data; cin >> data; while(data != -1){ insert_at_tail(head, data); cin >> data; } } void remove_duplicates(Node *&head){ set S; Node *temp = head; Node *prev = NULL; Node *remove = NULL; while(temp != NULL){ if(S.find(temp->data) != S.end()){ remove = temp; prev->next = temp->next; } else{ S.insert(temp->data); prev = temp; } temp = temp->next; delete remove; } } void remove_duplicates_without_buffer(Node *&head){ Node *current = head; Node *remove = NULL; while(current != NULL){ Node *runner = current; while(runner->next != NULL){ if(runner->next->data == current->data){ remove = runner->next; runner->next = runner->next->next; delete remove; } else{ runner = runner->next; } } current = current->next; } } int main(){ Node *head = NULL; /* make_linked_list(head); print_linked_list(head); cout << endl; remove_duplicates(head); print_linked_list(head); */ make_linked_list(head); print_linked_list(head); cout << endl; remove_duplicates_without_buffer(head); print_linked_list(head); } ================================================ FILE: Linked List/linked_list_remove_kth_node_from_end.py ================================================ # PROBLEM TO SOLVE """ Remove the Nth node from the end of the linked list assume input is always valid and that k will be a non-negative number that is less than the length of the list """ # define node used in linked list class node: def __init__(self,v=0,n=None): self.val = v self.next = n # function to create list of length n, node values start at 1 and incrament # this is NOT part of the algorithm, just part of the setup def create_list(n): if n <= 0: raise Exception() head = node(1) curr = head for i in range(2,n+1): next = node(i) curr.next = next curr=curr.next return head def print_list(head): while head: print(head.val) head = head.next # helper function (used in both recursive and iterative) def __remove_node(prev,curr): prev.next = curr.next # recursive function def recursive(head, n): # create nested helper function called rec # this allows us to keep the function signature of recursive clean and consistant with iterative # in real life the client does not usually care if your function is iterative or recursive # so you want both functions to have just two arguments when the client calls them def rec(lag,lead, k): # move lead up k nodes before we even start moving lag if k > 0: k -= 1 lead = lead.next else: if not lead: # base case 1: remove head if lead goes passed last node return head.next elif not lead.next: # base case 2: skip kth node if lead is on last node lag.next = lag.next.next return head else: # move both nodes up until lead is at the last node lead = lead.next lag = lag.next return rec(lag,lead,k) # call the recursive case # if we did not create a helper function, the client would need to call recursive # with 3 arguments instead of 2 which is confusing. return rec(head,head,n) # iterative function def iterative(head,k): # start lag and lead at head lag = lead = head # lead gets a headstart, it moves up k nodes before lag starts moving for _ in range(k): lead = lead.next # if lead made it passed the end, then lag must still be at head if not lead: return lag.next # we want to keep moving both lead and lag by one until lead is at the last node # keep in mind that we want to stop lag one bofore the node we want to delete. # this is because need to skip over the kth node from the end while lead.next: lead = lead.next lag = lag.next # we skip the kth from end node by assiging lag.next to lag.next.next # pythons built in garbage collection will recognize that the kth node # is unreachable and will delete it for us, no need to call del! lag.next = lag.next.next return head # text functions below main if __name__ == "__main__": head = create_list(9) print("before removal") print_list(head) print("after removing 2nd from last node iteratively") rem_head = iterative(head,2) print_list(rem_head) head = create_list(9) print("after removing 3rd from last recursivly") rem_head = recursive(head,3) print_list(rem_head) ================================================ FILE: Linked List/linked_list_remove_nth_node_from_end.cpp ================================================ /* Given the head of a linked list, remove the nth node from the end of the list and return its head. Input: head = [1,2,3,4,5], n = 2 Output: [1,2,3,5] Example 2: Input: head = [1], n = 1 Output: [] Example 3: Input: head = [1,2], n = 1 Output: [1] Constraints: The number of nodes in the list is sz. 1 <= sz <= 30 0 <= Node.val <= 100 1 <= n <= sz */ #include /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */ /* In order to solve this problem in only one pass and O(1) extra space, however, we would need to find a way to both reach the end of the list with one pointer and also reach the n'th node from the end simultaneously with a second pointer. To do that, we can simply stagger our two pointers by n nodes by giving the first pointer (fast) a head start before starting the second pointer (slow). Doing this will cause slow to reach the n'th node from the end at the same time that fast reaches the end. */ class Solution { public: ListNode* removeNthFromEnd(ListNode* head, int n) { ListNode* slow = head; ListNode* fast = head; if(head == NULL || head->next == NULL) return NULL; while(n--){ fast = fast->next; } if(fast == NULL) return slow->next; while(fast->next != NULL){ slow = slow->next; fast = fast->next; } ListNode* temp = slow->next; slow->next = slow->next->next; delete temp; return head; } }; ================================================ FILE: Linked List/linked_list_remove_nth_node_from_end.py ================================================ class Solution: def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: dummy_header = ListNode('*') dummy_header.next=head slow = dummy_header fast = dummy_header for _ in range(1,n+2): fast=fast.next while(fast!=None): slow = slow.next fast = fast.next slow.next = slow.next.next return dummy_header.next ================================================ FILE: Linked List/linked_list_reverse.cpp ================================================ // Reverse a linkedlist iteratively // Program Author : Abhisek Kumar Gupta #include using namespace std; class node{ public: int data; node* next; node(int d){ data = d; next = NULL; } }; void insert_at_tail(node *&head, int data){ if(head == NULL){ head = new node(data); return; } node *n = new node(data); node * temp = head; while(temp -> next != NULL){ temp = temp->next; } temp->next = n; } void print_linked_list(node *head){ while(head != NULL){ cout << head->data << "->"; head = head->next; } } void makeLinkedList(node *&head){ int data; cin >> data; while(data != -1){ insert_at_tail(head, data); cin >> data; } } void reverse_linked_list(node *&head){ node *current = head; node *next = NULL; node *prev = NULL; while(current != NULL){ next = current->next; current->next = prev; prev = current; current = next; } head = prev; } int main(){ node *head = NULL; makeLinkedList(head); print_linked_list(head); cout << endl; reverse_linked_list(head); print_linked_list(head); return 0; } ================================================ FILE: Linked List/linked_list_reverse.js ================================================ //Problem statement-Given the head of a singly linked list, reverse the list, and return the reversed list. // Iteratively * @param {ListNode} head * @return {ListNode} */ var reverseList = function(head) { // Handling the edge cases if head node is null or when single node is there if (head === null || head.next === null) { return head; } // Using three pointer approach let prev = null; let curr = head; let fwd = null; while (curr !== null) { // Updatng the value of each of the pointers fwd = curr.next; curr.next = prev; prev = curr; curr = fwd; } // Returning the head of the reversed linked list return prev; }; // Time complexity - O(N) // Space complexity - O(1) // Recursively var reverseList = function(head) { // Base case- If the head is null or single node is there if (head === null || head.next === null) { return head; } // Recursively reverse the remaining list starting from the next node const reversedList = reverseList(head.next); // Reversing the links between the current and next node head.next.next = head; head.next = null; // Returning the head of the reversed linked list return reversedList; }; // Time complexity - O(N) // Space complexity - O(N) ================================================ FILE: Linked List/linked_list_reverse_recursive.cpp ================================================ // Reverse a linkedlist recursively // Program Author : Abhisek Kumar Gupta #include using namespace std; class node{ public: int data; node* next; node(int d){ data = d; next = NULL; } }; void insert_at_tail(node *&head, int data){ if(head == NULL){ head = new node(data); return; } node *n = new node(data); node * temp = head; while(temp -> next != NULL){ temp = temp->next; } temp->next = n; } void print_linked_list(node *head){ while(head != NULL){ cout << head->data << "->"; head = head->next; } } void makeLinkedList(node *&head){ int data; cin >> data; while(data != -1){ insert_at_tail(head, data); cin >> data; } } node* recursive_reverse(node *head){ if(head->next == NULL || head == NULL){ return head; } node *mini_head = recursive_reverse(head->next); node *current = head; current->next->next = current; current->next = NULL; return mini_head; } int main(){ node *head = NULL; makeLinkedList(head); print_linked_list(head); cout << endl; head = recursive_reverse(head); print_linked_list(head); return 0; } ================================================ FILE: Linked List/linked_list_sum_lists.cpp ================================================ // sum Lists : you have two numbers represented by a linked list // where each node contains a single digit, write a function // to add two numbers and returns the sum as linked list // Program Author: Abhisek Kumar Gupta #include using namespace std; class Node{ public: int data; Node *next; Node(int d){ data = d; } }; void insert_at_head(Node *&head, int data){ Node *new_node = new Node(data); new_node->next = head; head = new_node; } void print_linked_list(Node *head){ if(head == NULL) return; cout << head->data << "->"; print_linked_list(head->next); } void make_linked_list(Node *&head){ int data; cin >> data; while(data != -1){ insert_at_head(head, data); cin >> data; } } Node* add_lists(Node *l1, Node *l2, int carry){ if(l1 == NULL && l2 == NULL && carry == 0) return NULL; int value = carry; if(l1 != NULL) value += l1->data; if(l2 != NULL) value += l2->data; Node *result = new Node(value % 10); if(l1 != NULL || l2 != NULL) result->next = add_lists(l1 == NULL ? NULL : l1->next, l2 == NULL ? NULL : l2->next, value >= 10 ? 1 : 0); else result->next = NULL; return result; } int main(){ Node *head1 = NULL; Node *head2 = NULL; make_linked_list(head1); make_linked_list(head2); print_linked_list(head1); cout << endl; print_linked_list(head2); Node *result = add_lists(head1, head2, 0); cout << endl; print_linked_list(result); } ================================================ FILE: Linked List/linked_list_swap_nodes_in_pair.cpp ================================================ // Swap adjacant Nodes in pair Recursive // Program Author : Abhisek Kumar Gupta #include using namespace std; class node{ public: int data; node* next; node(int d){ data = d; next = NULL; } }; void insert_at_tail(node *&head, int data){ if(head == NULL){ head = new node(data); return; } node *n = new node(data); node * temp = head; while(temp -> next != NULL){ temp = temp->next; } temp->next = n; } void print_linked_list(node *head){ cout << endl; while(head != NULL){ cout << head->data << "->"; head = head->next; } } void makeLinkedList(node *&head){ int data; cin >> data; while(data != -1){ insert_at_tail(head, data); cin >> data; } } node* swapNodesInPairRecursive(node *head){ if(head == NULL || head->next == NULL){ return head; } node *newHead = swapNodesInPairRecursive(head->next->next); node *temp = head->next; temp->next = head; head->next = newHead; return temp; } int main(){ node *head = NULL; makeLinkedList(head); print_linked_list(head); head = swapNodesInPairRecursive(head); print_linked_list(head); return 0; } ================================================ FILE: Linked List/linked_list_swap_nodes_in_pair_iterative.cpp ================================================ // Swap adjacant Nodes in Pair Iterative // Program Author : Abhisek Kumar Gupta #include using namespace std; class node{ public: int data; node* next; node(int d){ data = d; next = NULL; } }; void insert_at_tail(node *&head, int data){ if(head == NULL){ head = new node(data); return; } node *n = new node(data); node * temp = head; while(temp -> next != NULL){ temp = temp->next; } temp->next = n; } void print_linked_list(node *head){ cout << endl; while(head != NULL){ cout << head->data << "->"; head = head->next; } } void makeLinkedList(node *&head){ int data; cin >> data; while(data != -1){ insert_at_tail(head, data); cin >> data; } } node* swapNodesInPairIterative(node *head){ node *dummy = new node(0); node *prev = dummy; node *curr = head; node *second = NULL; node *nextPair = NULL; while(curr && curr->next){ nextPair = curr->next->next; second = curr->next; second->next = curr; curr->next = nextPair; prev->next = second; prev = curr; curr = nextPair; } return dummy->next; } int main(){ node *head = NULL; makeLinkedList(head); print_linked_list(head); head = swapNodesInPairIterative(head); print_linked_list(head); return 0; } ================================================ FILE: Linked List/linked_list_take_input.cpp ================================================ // Finding Midpoint of a LinkedList // Program Author : Abhisek Kumar Gupta #include using namespace std; class node{ public: int data; node* next; node(int d){ data = d; next = NULL; } }; void insert_at_tail(node *&head, int data){ if(head == NULL){ head = new node(data); return; } node *n = new node(data); node * temp = head; while(temp -> next != NULL){ temp = temp->next; } temp->next = n; } void print_linked_list(node *head){ cout << endl; while(head != NULL){ cout << head->data << "->"; head = head->next; } } void makeLinkedList(node *&head){ int data; cin >> data; while(data != -1){ insert_at_tail(head, data); cin >> data; } } int main(){ node *head = NULL; makeLinkedList(head); print_linked_list(head); return 0; } ================================================ FILE: Linked List/linked_list_take_input_as_array_operator_overloading.cpp ================================================ // Take Input in a LinkedList as an array with "Operator overloading" // Program Author : Abhisek Kumar Gupta #include using namespace std; class node{ public: int data; node* next; node(int d){ data = d; next = NULL; } }; void insert_at_tail(node *&head, int data){ if(head == NULL){ head = new node(data); return; } node *n = new node(data); node * temp = head; while(temp -> next != NULL){ temp = temp->next; } temp->next = n; } void print_linked_list(node *head){ while(head != NULL){ cout << head->data << "->"; head = head->next; } } void makeLinkedList(node *&head){ int data; cin >> data; while(data != -1){ insert_at_tail(head, data); cin >> data; } } istream& operator>>(istream &is, node *&head){ makeLinkedList(head); return is; } ostream& operator<<(ostream &os, node *&head){ print_linked_list(head); return os; } int main(){ int n; cin >> n; node *arr[n]; for(int i = 0; i < n; i++){ arr[i] = NULL; } for(int i = 0; i < n; i++){ cin >> arr[i]; } cout << endl; for(int i = 0; i < n; i++){ cout << arr[i] << endl;; } return 0; } ================================================ FILE: Linked List/linked_list_take_input_operator_overloading.cpp ================================================ // Take Input in a LinkedList with "Operator overloading" // Program Author : Abhisek Kumar Gupta #include using namespace std; class node{ public: int data; node* next; node(int d){ data = d; next = NULL; } }; void insert_at_tail(node *&head, int data){ if(head == NULL){ head = new node(data); return; } node *n = new node(data); node * temp = head; while(temp -> next != NULL){ temp = temp->next; } temp->next = n; } void print_linked_list(node *head){ while(head != NULL){ cout << head->data << "->"; head = head->next; } } void makeLinkedList(node *&head){ int data; cin >> data; while(data != -1){ insert_at_tail(head, data); cin >> data; } } istream& operator>>(istream &is, node *&head){ makeLinkedList(head); return is; } ostream& operator<<(ostream &os, node *&head){ print_linked_list(head); return os; } int main(){ /*node *head = NULL; node *head2 = NULL; cin >> head >> head2; cout << head << endl << head2; */ int n; cin >> n; node *arr[n]; for(int i = 0; i < n; i++){ arr[i] = NULL; } for(int i = 0; i < n; i++){ cin >> arr[i]; } cout << endl; for(int i = 0; i < n; i++){ cout << arr[i] << endl;; } return 0; } ================================================ FILE: Linked List/recursive and iterative in singly linkedlist.py ================================================ ''' Issue#528 Date:16/06/2023 Input: Linked List: Reverse Linked List Recursively and Iteratively in Python Example 1: Enter the values for the linked list (space-separated): 1 2 3 4 5 Choose the reversal method (iterative or recursive): iterative 5 4 3 2 1 Explanation: *The `create_linked_list()` function: - This function prompts the user to enter values for the linked list, which are expected to be space-separated. - The input values are split into a list of strings using the `split()` method. - The function then iterates over the input values, creating a new `ListNode` object for each value. - The `next` pointers of the nodes are appropriately set to form the linked list. - Finally, the function returns the head of the linked list. * The `reverse_linked_list_iterative()` function: - This function takes the head of a linked list as input. - It initializes two pointers: `prev` and `current`. `prev` is initially set to `None`, and `current` is set to the head. - The function iterates over the linked list using a while loop. - In each iteration, it stores the next node in a variable called `next_node`. - Then, it updates the `next` pointer of the current node to point to the previous node (`prev`). - The `prev` pointer is updated to the current node, and the `current` pointer is updated to the next node (`next_node`). - This process continues until `current` becomes `None`, which means the end of the original linked list has been reached. - Finally, the function returns the new head of the reversed linked list, which is stored in `prev`. * The `reverse_linked_list_recursive()` function: - This function is a recursive implementation of reversing a linked list. - It takes the head of a linked list as input. - First, it checks two base cases: if the head is `None` or the head's `next` pointer is `None`. In these cases, it simply returns the head as it is. - If the base cases are not met, it recursively calls itself on the next node (`head.next`) to reverse the remaining sublist. - Once the recursion reaches the last node in the original linked list, it starts updating the `next` pointers to reverse the sublist. - The `next` pointer of the current node (`head`) is set to the previous node (`head.next.next`), effectively reversing the connection. - The `next` pointer of the current node is then set to `None` to complete the reversal. - Finally, the function returns the new head of the reversed linked list. Time Complexity: O(n)(both recursive and iterative method) Space Complexity: Iterative approach: O(1) space complexity. Recursive approach: O(n) space complexity. ''' class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next def create_linked_list(): # Take user input for the linked list values values = input("Enter the values for the linked list (space-separated): ").split() # Create the linked list from the input values head = None prev = None for val in values: node = ListNode(int(val)) if not head: head = node else: prev.next = node prev = node return head def reverse_linked_list_iterative(head): prev = None current = head while current: next_node = current.next current.next = prev prev = current current = next_node return prev def reverse_linked_list_recursive(head): if not head or not head.next: return head reversed_list = reverse_linked_list_recursive(head.next) head.next.next = head head.next = None return reversed_list def reverse_linked_list(head, method='iterative'): if method == 'iterative': return reverse_linked_list_iterative(head) elif method == 'recursive': return reverse_linked_list_recursive(head) else: raise ValueError('Invalid method specified.') # Create the linked list based on user input head = create_linked_list() # Ask the user to choose the reversal method method = input("Choose the reversal method (iterative or recursive): ") # Reverse the linked list using the chosen method reversed_list = reverse_linked_list(head, method=method) # Traverse the reversed linked list and print the values current = reversed_list while current: print(current.val, end=' ') current = current.next ================================================ FILE: Linked List/reverse_linked_list.go ================================================ // Implementation of reversing a linked list recursively and iteratively /* In this code, we define a ListNode struct which represents a node in the linked list. We then define two functions: ReverseListIteratively and ReverseListRecursively which reverse the linked list iteratively and recursively, respectively. In ReverseListIteratively, we declare two pointers prev and curr, initialize curr to head, and iterate over the linked list until curr becomes nil. In each iteration, we store the Next pointer of curr in a temporary variable nextTemp, point curr.Next to prev, update prev to curr, and update curr to nextTemp. Finally, we return prev, which now points to the new head of the reversed linked list. In ReverseListRecursively, we first check if head is nil or if head.Next is nil, in which case we return head itself. Otherwise, we call ReverseListRecursively on head.Next, which returns the new head of the reversed linked list. We then set head.Next.Next to head and head.Next to nil. Finally, we return the new head. In the main function, we create a linked list 1 -> 2 -> 3 -> 4 -> 5, reverse it both iteratively and recursively, and print out the reversed linked lists. */ package main import "fmt" // ListNode represents a node in the linked list type ListNode struct { Val int Next *ListNode } // ReverseListIteratively reverses a linked list iteratively func ReverseListIteratively(head *ListNode) *ListNode { var prev, curr *ListNode curr = head for curr != nil { nextTemp := curr.Next curr.Next = prev prev = curr curr = nextTemp } return prev } // ReverseListRecursively reverses a linked list recursively func ReverseListRecursively(head *ListNode) *ListNode { if head == nil || head.Next == nil { return head } newHead := ReverseListRecursively(head.Next) head.Next.Next = head head.Next = nil return newHead } func main() { // Create a linked list: 1 -> 2 -> 3 -> 4 -> 5 head := &ListNode{1, &ListNode{2, &ListNode{3, &ListNode{4, &ListNode{5, nil}}}}} // Reverse the linked list iteratively reversedListIteratively := ReverseListIteratively(head) fmt.Println("Reversed linked list (iteratively):") for reversedListIteratively != nil { fmt.Printf("%d ", reversedListIteratively.Val) reversedListIteratively = reversedListIteratively.Next } fmt.Println() head = &ListNode{1, &ListNode{2, &ListNode{3, &ListNode{4, &ListNode{5, nil}}}}} // Reverse the linked list recursively reversedListRecursively := ReverseListRecursively(head) fmt.Println("Reversed linked list (recursively):") for reversedListRecursively != nil { fmt.Printf("%d ", reversedListRecursively.Val) reversedListRecursively = reversedListRecursively.Next } fmt.Println() } ================================================ FILE: Linked List/reverse_linked_list.java ================================================ /** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val = val; } * ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } */ /*Explaination of the code which reverses the linked list * --> Initially we are given the head of the linked list. The approach used here is that * for reversing the linked list we can take the user of three pointers * * --> These pointers are named as prev, curr, and right. Initially prev points to NULL, curr * points to the current node (node of which the pointer has to be reversed) and the right node * which always points to the node next to the current * * --> Idea here is that at each pass we will be reversing the pointer of the current node and * move all the pointers forward by one step * * --> So initially, the current node points to head node, it's pointer is reversed and is made to * point to he NULL, since now this first node becomes the last node. * * --> To move forward, prev is now at the location of current node, current node moves by * one step, by making it point to the location where right is pointing now. (Using right pointer * since the track of the next node is lost as we have reveresed the pointer). * * --> Loop stops when the current node becomes null. At the itereation, last node is being pointed * by prev, which is now the first node logically, so assign head to prev. * * --> Example input: 1->2->3->4->5 * Output : 5->4->3->2->1 * */ class Solution { public ListNode reverseList(ListNode head) { ListNode prev=null; ListNode curr=head; while(curr!=null){ ListNode right=curr.next; curr.next=prev; prev=curr; curr=right; } head=prev; return head; } } ================================================ FILE: Linked List/singly_linked_list.cpp ================================================ // Implementation of singly LinkedList // Author : Mohit Singh /*Search for a node in the list You can determine and retrieve a specific node from the front, end, or anywhere in the list. The worst-case​ Time Complexity for retrieving a node from anywhere in the list is O(n). Add a node to the list You can add a node at the front, end, or anywhere in the linked list. The worst-case Time Complexities for performing these operations are: Add an item to the front of the list: O(1) Add an item to the end of the list: O(n) Add an item a​nywhere in the list: O(n) Remove a node from the list You can remove a node from the front, end, or anywhere in the list. The worst-case Time Complexities for performing this operation are: Remove an item from the front of the list: O(1) Remove an item from the end of the list: O(n) Remove an item from anywhere in the list: O(n) Time complexity : O(n) Space complexity : O(n) */ #include using namespace std; // Making a node struct containing an int data and a pointer // to next node struct Node { int data; Node *next; // Parameterised constructor with default argument Node(int val = 0) : data(val), next(nullptr) {} // Parameterise constructor Node(int val, Node *tempNext) : data(val), next(tempNext) {} }; class LinkedList { // Head pointer Node *head; public: // default constructor. Initializing head pointer LinkedList() : head(nullptr) { } // inserting elements (At start of the list) void insert(int val) { // make a new node Node *new_node = new Node(val); // If list is empty, make the new node, the head if (head == nullptr) { head = new_node; } // else, make the new_node the head and its next, the previous // head else { new_node->next = head; head = new_node; } } // loop over the list. return true if element found bool search(int val) { Node *temp = head; while (temp != nullptr) { if (temp->data == val) return true; temp = temp->next; } return false; } void remove(int val) { Node *temp = head; // If the head is to be deleted if (temp != nullptr && temp->data == val) { head = temp->next; delete temp; return; } // Else loop over the list and search for the node to delete else { Node *curr = head; while (temp != nullptr && temp->data != val) { // When node is found, delete the node and modify the pointers curr = temp; temp = temp->next; } // If values is not found in the linked list if (!temp) { cout << "Value not found" << endl; return; } curr->next = temp->next; delete temp; } } void display() { Node *temp = head; while (temp != nullptr) { cout << temp->data << " "; temp = temp->next; } cout << endl; } }; int main() { LinkedList l; // inserting elements l.insert(6); l.insert(9); l.insert(1); l.insert(3); l.insert(7); cout << "Current Linked List: "; l.display(); cout << "Deleting 1: "; l.remove(1); l.display(); cout << "Deleting 13: "; l.remove(13); cout << "Searching for 7: "; cout << l.search(7) << endl; cout << "Searching for 13: "; cout << l.search(13) << endl; } ================================================ FILE: Linked List/singly_linked_list.go ================================================ package main import ( "errors" "fmt" ) /* insertAtTail (val) insertAtHead (val) deleteAtTail () deleteAtHead () getLength () print () getAtIdx (idx) replaceAtIdx (val, idx) deleteAtIdx (idx) search (val) toArray () reverse () */ // struct for a node type, we use interface{} to allow for any type of value, you could replace this // with another value if you want to have more control over the type of value that can be inserted type node struct { val interface{} next *node } // struct for the linked list type type linkedList struct { head *node tail *node length int } // constructor for a node type func newNode(val interface{}) *node { return &node{val: val, next: nil} } // constructor for a linked list type func newLinkedList() *linkedList { return &linkedList{head: nil, tail: nil, length: 0} } // function for linked lists to check if the linked list is empty func (l *linkedList) IsEmpty() bool { return l.head == nil } // function for linked lists to insert a value at the tail of the linked list func (l *linkedList) insertAtTail(val interface{}) { node := newNode(val) if l.IsEmpty() { l.head = node l.tail = l.head l.length += 1 return } else { node := node l.tail.next = node l.tail = node l.length += 1 } } // function for linked lists to insert a value at the head of the linked list func (l *linkedList) insertAtHead(val interface{}) { // create a new node node := newNode(val) // check if the list is empty if l.IsEmpty() { // if it is, set the head and tail to the new node and increase the length l.head = node l.tail = l.head l.length += 1 return } else { // if it is not, set the new node's next to the current head and set the head to the new node node.next = l.head l.head = node l.length += 1 } } func (l *linkedList) print() { c := l.head for c != nil { fmt.Printf("%v", c.val) if c.next == nil { fmt.Println() break } c = c.next fmt.Print(" -> ") } fmt.Printf("Current Length: %v\n", l.length) } // function for linked lists to delete a node at the tail of the linked list func (l *linkedList) deleteAtTail() { if l.IsEmpty() { fmt.Println("Empty List") return } if l.head.next == nil { l.head = nil l.tail = nil l.length -= 1 return } c := l.head for { if c.next.next == nil { c.next = nil l.tail = c l.length -= 1 return } c = c.next } } // function for linked lists to delete a node at the head of the linked list func (l *linkedList) deleteAtHead() { if l.IsEmpty() { fmt.Println("Empty List") return } if l.head.next == nil { l.head = nil l.tail = nil l.length -= 1 return } l.head = l.head.next l.length -= 1 } // function for linked lists to get the length of the linked list func (l *linkedList) getLength() int { return l.length } // function of a linked list to get the value at a given index func (l *linkedList) getAtIdx(idx int) (interface{}, error) { if idx >= l.length { return nil, errors.New("index out of range") } c := l.head for i := 0; i < idx; i++ { c = c.next } return c.val, nil } // function of a linked list to replace the value at a given index // index starts at 0 func (l *linkedList) replaceAtIdx(val interface{}, idx int) error { if idx >= l.length { return errors.New("index out of range") } c := l.head for i := 0; i < idx; i++ { c = c.next } c.val = val return nil } // function of a linked list to delete the value at a given index func (l *linkedList) deleteAtIdx(idx int) error { if idx >= l.length { return errors.New("index out of range") } c := l.head for i := 0; i < idx-1; i++ { c = c.next } c.next = c.next.next l.length -= 1 return nil } // function to find the index of a given value // returns -1 and an error if the value is not found func (l *linkedList) search(val interface{}) (int, error) { c := l.head for i := 0; i < l.length; i++ { if c.val == val { return i, nil } c = c.next } return -1, errors.New("value not found") } // function to convert a linked list to an array func (l *linkedList) toArray() []interface{} { arr := make([]interface{}, l.length) c := l.head for i := 0; i < l.length; i++ { arr[i] = c.val c = c.next } return arr } // function to reverse the linked list // This is a recursive implementation to show something different func (l *linkedList) reverse() error { if l.IsEmpty() { return errors.New("Empty List") } l.head = reverseListRecursive(l.head) return nil } // recursive function to reverse a linked list recursively func reverseListRecursive(head *node) *node { if head == nil || head.next == nil { return head } rest := reverseListRecursive(head.next) head.next.next = head head.next = nil return rest } // example of a linked list func main() { list := newLinkedList() list.insertAtHead(1) list.insertAtTail(2) list.insertAtTail(3) list.insertAtTail(4) list.insertAtTail(5) list.insertAtTail(6) list.insertAtTail(7) list.insertAtTail(8) list.insertAtTail(9) list.insertAtTail(10) list.insertAtTail(11) list.print() list.deleteAtTail() list.print() err := list.replaceAtIdx(9000, 4) if err != nil { fmt.Println(err) } list.print() i, err := list.search(9000) if err != nil { fmt.Println(err) } else { fmt.Println(i) } err = list.reverse() if err != nil { fmt.Println(err) } list.print() idx, err := list.search(3) if err != nil { fmt.Println(err) } else { fmt.Println(idx) } fmt.Println(list.toArray()) } ================================================ FILE: Linked List/singly_linked_list.java ================================================ // Linked list: Implement Singly linked list in Java #982 /* APPROACH :Node Class: The Node class is defined to represent a single node in the linked list. It has two instance variables: data to store the value and next to store the reference to the next node. Insertion at the Start: The insertStart method allows inserting a new node at the beginning of the linked list. It creates a new node with the given data value and sets its next reference to the current head. Then, it updates the head to point to the newly inserted node. Deletion from the Start: The delete method removes the first node from the linked list. It checks if the head is null, indicating an empty list. If not, it updates the head to point to the next node, effectively removing the first node. Display: The display method is used to print the elements of the linked list. It starts from the head node and iterates through the list by moving to the next node until the current node becomes null. During each iteration, it prints the data value of the current node. Main Method: The main method demonstrates the usage of the linked list implementation. It creates an empty list by initializing the head to null. It then performs several insertions using the insertStart method to add nodes at the beginning of the list. After that, it calls the display method to print the elements of the list. Next, it performs several deletions using the delete method to remove nodes from the beginning of the list. Finally, it calls the display method again to print the updated list. */ import java.lang.*; // Node Class class Node { int data; Node next; Node(int x) // parameterized constructor { data = x; next = null; } } class Main { static Node insertStart(Node head, int data) { // Creating newNode memory & assigning data value Node newNode = new Node(data); // assigning this newNode's next as current head node newNode.next = head; // re-assigning head to this newNode head = newNode; return head; } public static Node delete(Node head) { if (head == null){ System.out.println("List is empty, not possible to delete"); return head; } System.out.println("Deleted: " + head.data); // move head to next node head = head.next; return head; } static void display(Node node) { //as linked list will end when Node is Null while (node != null) { System.out.print(node.data + " "); node = node.next; } System.out.println(""); } public static void main(String args[]) { Node head = null; head = insertStart(head,6); head = insertStart(head,5); head = insertStart(head,4); head = insertStart(head,3); head = insertStart(head,2); head = insertStart(head,1); display(head); head = delete(head); head = delete(head); head = delete(head); display(head); } } /* the space complexity is O(n), and the time complexity for each operation is either O(1) or O(n), depending on the specific operation being performed. */ ================================================ FILE: Linked List/singly_linked_list.py ================================================ ''' This is an implementation of a singly linked list in Python with a Node class and a LinkedList class. The Node class represents each element of the linked list and the LinkedList class has methods to manipulate the list, including append, prepend, delete_node, and print_list. ''' class Node: def __init__(self, data): self.data = data self.next = None # The next node in the list class LinkedList: def __init__(self): self.head = None # The first node in the list def append(self, data): new_node = Node(data) if not self.head: # If the list is empty, set the new node as the head self.head = new_node return curr_node = self.head while curr_node.next: # Traverse to the last node in the list curr_node = curr_node.next curr_node.next = new_node # Set the next node of the last node to the new node def prepend(self, data): new_node = Node(data) new_node.next = self.head # Set the next node of the new node to the current head self.head = new_node # Set the new node as the new head def delete_node(self, data): if not self.head: # If the list is empty, do nothing return if self.head.data == data: # If the head is the node to delete, set the next node as the new head self.head = self.head.next return curr_node = self.head while curr_node.next: # Traverse the list until the last node if curr_node.next.data == data: # If the next node is the node to delete, set the next node of the current node to the node after the next node curr_node.next = curr_node.next.next return curr_node = curr_node.next def print_list(self): curr_node = self.head while curr_node: # Traverse the list and print each node's data print(curr_node.data) curr_node = curr_node.next ================================================ FILE: Linked List/sll.go ================================================ /* The linked list consists of a series of structures called nodes. We can think of each node as a record. The first part of the record is a field that stores the data, and the second part of the record is a field that stores a pointer to a node. So, each node contains two fields: a data field and a next field which is a pointer used to link one node to the next node. Generally, "linked list" means a singly linked list. This list consists of a number of nodes in which each node has a pointer to the following element. The link of the last node in the list is nil, which indicates the end of the list. Basic Operations on a List --> Traversing the list --> Inserting an item in the list --> Deleting an item from the list */ package main import "fmt" // Linked list node contains a pointer to the next node as well as data. // The generic behavior is achieved by marking the data field as type interface type ListNode struct { // define a ListNode in a sll data interface{} // datum next *ListNode // pointer to next ListNode } type LinkedList struct { head *ListNode size int } func (ll *LinkedList) Display() error { if ll.head == nil { return fmt.Errorf("Display: List is empty") } current := ll.head for current != nil { fmt.Printf("%v->", current.data) current = current.next } fmt.Println() return nil } // Length: returns the linked list size func (ll *LinkedList) Length() int { size := 0 current := ll.head for current != nil { size++ current = current.next } return size } // InsertAtBeginning: Update the next pointer of new node to point to the current head // Update the head pointer to point to the new node func (ll *LinkedList) InsertAtBeginning(data interface{}) { node := &ListNode{ data: data, } // if there is no head then set new node as head if ll.head == nil { ll.head = node } else { node.next = ll.head ll.head = node } ll.size++ return } // InsertAtEnd: We need to modify two next pointers, new node next pointer points to nil // Last nodes next pointer points to the new node func (ll *LinkedList) InsertAtEnd(data interface{}) { node := &ListNode{ data: data, } if ll.head == nil { ll.head = node } else { current := ll.head for current.next != nil { current = current.next } current.next = node } ll.size++ return } // InsertAtAnyPos: To add an element at pos 3 we stop at pos 2, That means we traverse // 2 nodes and insert the new node. // Prev will point to the predecessor of new node and next pointer of new node // points to the next node of the prev node func (ll *LinkedList) InsertAtAnyPos(data interface{}, position int) error { // base case check position valid or not if position < 1 || position > ll.size + 1 { return fmt.Errorf("insert: Error out of bounds") } newNode := &ListNode{data, nil} var prev, current *ListNode prev = nil current = ll.head for position > 1 { prev = current current = current.next position-- } if prev != nil { prev.next = newNode newNode.next = current } else { newNode.next = current ll.head = newNode } ll.size++ return nil } // DeleteFirst: create a temporaty node which will point to the same node as that of head // and move the head nodes pointer to the next node and dispose temporary node // Time Complexity: O(1), for scanning the list of size. Space Complexity: O(1). func (ll *LinkedList) DeleteFirst() (interface{}, error) { if ll.head == nil { return nil, fmt.Errorf("deleteFront: List is empty") } data := ll.head.data ll.head = ll.head.next ll.size-- return data, nil } // DeleteLast: traverse the list, while traversingmaintain the previous node address. // by the time we reach end of list we have two pointers, one pointing to the tail node and // other pointing to the node before tail node // Time Complexity: O(n), for scanning the list of size 􀝊. Space Complexity: O(1). func (ll *LinkedList) DeleteLast() (interface{}, error) { if ll.head == nil { return nil, fmt.Errorf("deleteLast: List is empty") } var prev *ListNode current := ll.head // last and second last node for current.next != nil { prev = current current = current.next } if prev != nil { prev.next = nil } else { ll.head = nil } ll.size-- return current.data, nil } // DeleteFromAnyPos: maintain the previous node while traversing the list. // Once we find the node to be deleted, // change the previous node's next pointer to the next pointer of the node to be deleted // Time Complexity: O(n). In the worst case, we may need to delete the node from the end of the linked list. // Space Complexity: O(1). func (ll *LinkedList) DeleteFromAnyPos(position int) (interface{}, error) { if position < 1 || position > ll.size + 1 { return nil, fmt.Errorf("insert: Index out of bounds") } var prev, current *ListNode prev = nil current = ll.head pos := 0 // delete head and set head as next node if position == 1 { ll.head = ll.head.next } else { // maintain two nodes for pos != position - 1 { pos = pos + 1 prev = current current = current.next } if current != nil { prev.next = current.next } } ll.size-- return current.data, nil } ================================================ FILE: Linked List/sort_linked_list.cpp ================================================ /* Given the head of a linked list, return the list after sorting it in ascending order. */ /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */ class Solution { public: ListNode* compute_midpoint(ListNode* head){ if(head == NULL || head->next == NULL){ return head; } ListNode* slow = head; ListNode* fast = head->next; while(fast != NULL && fast->next != NULL){ slow = slow->next; fast = fast->next->next; } return slow; } ListNode* merge_two_lists(ListNode* A, ListNode* B){ if(A == NULL) return B; else if(B == NULL) return A; ListNode* C = NULL; if(A->val < B->val){ C = A; C->next = merge_two_lists(A->next, B); } else{ C = B; C->next = merge_two_lists(A, B->next); } return C; } ListNode* sortList(ListNode* head) { if(head == NULL || head->next == NULL) return head; ListNode* mid = compute_midpoint(head); ListNode* A = head; ListNode* B = mid->next; mid->next = NULL; A = sortList(A); B = sortList(B); ListNode* C = merge_two_lists(A, B); return C; } }; ================================================ FILE: Math/Basic_operations.py ================================================ def divide(dividend, divisor): if divisor == 0: raise ZeroDivisionError("Division by zero") if dividend == 0: return 0 negative = (dividend < 0) ^ (divisor < 0) dividend = abs(dividend) divisor = abs(divisor) quotient = 0 while dividend >= divisor: dividend -= divisor quotient += 1 if negative: quotient = -quotient INT_MAX = 2**31 - 1 INT_MIN = -2**31 if quotient > INT_MAX: return INT_MAX elif quotient < INT_MIN: return INT_MIN else: return quotient # Take user input for dividend and divisor dividend = int(input("Enter the dividend: ")) divisor = int(input("Enter the divisor: ")) # Perform the division and print the result result = divide(dividend, divisor) print(f"The result of {dividend} divided by {divisor} is: {result}") ================================================ FILE: Math/Count.go ================================================ class Solution { public: int countNumbersWithUniqueDigits(int n) { if (n == 0) { return 1; // There's only one number with 0 digits, which is 0. } int result = 10; // For n = 1, there are 10 numbers with unique digits (0-9). int uniqueDigits = 9; int availableDigits = 9; for (int i = 2; i <= n && availableDigits > 0; i++) { uniqueDigits *= availableDigits; result += uniqueDigits; availableDigits--; } return result; } }; ================================================ FILE: Math/Factorial.cpp ================================================ /* Explanation: - The `factorial` function takes an integer `num` as input and calculates its factorial. The factorial of a non-negative integer N is the product of all positive integers less than or equal to N. - In the function, we have a base case that handles the special case when `num` is 0. The factorial of 0 is defined as 1, so we return 1 for `num` equal to 0. - For non-zero `num`, we use recursion to calculate the factorial. We call the `factorial` function with `num - 1`, and then multiply the result by `num`. - The recursion continues until we reach the base case when `num` becomes 0, and the recursive calls start to return their values and calculate the final factorial. Time Complexity: The time complexity of the `factorial` function is O(N), where N is the value of the input number. This is because the function makes N recursive calls to calculate the factorial. Space Complexity: The space complexity is O(N) due to the recursion stack space used during the recursive calls. Each recursive call adds a new stack frame, and in the worst case, there will be N stack frames corresponding to the N recursive calls. Sample Input: Number: 5 Sample Output: Factorial of 5 is 120 */ #include // Function to calculate factorial unsigned long long factorial(int num) { // Base case: factorial of 0 is 1 if (num == 0) { return 1; } // Recursive case: calculate factorial using recursion return num * factorial(num - 1); } int main() { // Sample input int num = 5; // Calculate the factorial of the number unsigned long long result = factorial(num); // Print the result std::cout << "Factorial of " << num << " is " << result << std::endl; return 0; } ================================================ FILE: Math/Factorial.go ================================================ /* Approach and Explanation: The factorial of a non-negative integer `n` is given by the product of all positive integers less than or equal to `n`. The factorial function is typically implemented using recursion. In this implementation, we check the base case where the input `n` is less than or equal to 1, and in that case, we return 1. Otherwise, we recursively call the factorial function with `n-1` and multiply it with `n`. This process continues until the base case is reached. The code prompts the user to enter a number, reads the input, and then calls the `factorial` function to calculate the factorial of the entered number. The result is then printed on the console. Time and Space Complexity: The time complexity of this implementation is O(n) because the recursive function is called once for each integer from `n` to 1. The space complexity is O(n) as well since each recursive call adds a new frame to the call stack. Sample Input and Output: Enter a number: 5 The factorial of 5 is: 120 */ package main import ( "fmt" ) // Function to calculate the factorial of a number func factorial(n int) int { if n <= 1 { return 1 } return n * factorial(n-1) } func main() { // Getting user input var number int fmt.Print("Enter a number: ") fmt.Scanln(&number) // Calculating and printing the factorial result := factorial(number) fmt.Printf("The factorial of %d is: %d\n", number, result) } ================================================ FILE: Math/Factorial.java ================================================ /* What is a factorial of a number? The factorial of a non-negative integer n is the product of all positive integers less than or equal to n. It is denoted by n!. For example, the factorial of 5 is 5! = 5 x 4 x 3 x 2 x 1 = 120. Approach: The recursive approach is used to calculate the factorial. The function `factorial()` takes an integer `n` as input. In each recursive call, we check if `n` is either 0 or 1 (base case), and if so, we return 1. Otherwise, we multiply `n` with the factorial of `n-1` and return the result. Time Complexity: O(n) The time complexity of this algorithm is O(n) because the recursion depth is equal to n, and in each recursion, we perform a constant-time operation. Space Complexity: O(n) The space complexity is O(n) because, in the worst-case scenario, the recursion depth can reach up to n. So, n frames will be added to the call stack. Sample Input: 5 Sample Output: Factorial of 5 is: 120 */ public class Factorial { // Recursive function to calculate the factorial public static int factorial(int n) { // Base case: if n is 0 or 1, the factorial is always 1 if (n == 0 || n == 1) { return 1; } // Recursive case: multiply n with factorial of (n-1) return n * factorial(n - 1); } public static void main(String[] args) { int number = 5; // Sample input: calculate factorial of 5 int result = factorial(number); System.out.println("Factorial of " + number + " is: " + result); } } ================================================ FILE: Math/Factorial.py ================================================ ''' Approach: We will take the number as input from the user and calculate its factorial. To calculate the factorial, we will start from 1 and multiply it with all the numbers from 1 to n. Finally, we will return the factorial. Time Complexity: O(n) Space Complexity: O(1) Sample Input: 5 Sample Output: Factorial of 5 is 120 ''' # Initializing the factorial to 1 fact = 1 def factorial(num): # If the number is 0 or 1, then the factorial is 1. if num == 0 or num == 1: return 1 # Calculating factorial by multiplying every number from 1 to num for i in range(1, num+1): fact *= i return fact #Taking input from user num = int(input("Enter a number: ")) #Calculating and printing the factorial of the number print("Factorial of", num, "is", factorial(num)) ================================================ FILE: Math/Hamming_distance.py ================================================ ''' The Hamming distance between two integers is the number of positions at which the corresponding bits are different. Given two integers x and y, return the Hamming distance between them. Example 1: Input: x = 1, y = 4 Output: 2 Explanation: 1 (0 0 0 1) 4 (0 1 0 0) ↑ ↑ The above arrows point to positions where the corresponding bits are different. Example 2: Input: x = 3, y = 1 Output: 1 Constraints: 0 <= x, y <= 231 - 1 ''' class Solution: def totalHammingDistance(self, nums: List[int]) -> int: '''By using bit manipulation, as all the array elements are 32-bit array elements, we calculate all the number of set bits and unset bits as we need to consider the permutations, we take setbits*unsetbits.''' hamming_dist,n = 0,len(nums) for i in range(32): count = 0 for element in nums: #Right shifting the element by the index and performing &1 lets us know if a bit is set or not if((element>>i)&1): count+=1 #Adding all the combinations where there are set and unset bits. hamming_dist+=count*(n-count) return hamming_dist ================================================ FILE: Math/Hammingdistance.cpp ================================================ #include class Solution { public: int totalHammingDistance(std::vector& nums) { // Get the number of elements in the input vector int n = nums.size(); // Initialize the variable to store the total Hamming distance int ans = 0; // Iterate through each bit position (from 0 to 31) for (int i = 0; i < 32; i++) { // Initialize a variable to count the number of set bits at the current bit position int count = 0; // Iterate through all elements in the vector for (int k = 0; k < n; k++) { // Count the number of set bits at the current bit position for each element count += (nums[k] >> i) & 1; } // Update the total Hamming distance by adding the product of set bits and unset bits ans += count * (n - count); } // Return the total Hamming distance return ans; } }; /* This program defines a Solution class with a totalHammingDistance method that calculates the total Hamming distance for the input vector nums. The outer loop iterates over each bit position (from 0 to 31), and the inner loop counts the number of set bits at that bit position for all elements in the vector. It then updates the ans variable by adding the product of the count of set bits and the count of unset bits at that position. This code efficiently calculates the total Hamming distance for a vector of integers by considering each bit position separately. */ ================================================ FILE: Math/K_closest.cpp ================================================ /* Author: SUYASH SINGH Problem statement:-Find K Closest Elements Given a sorted integer array arr, two integers k and x, return the k closest integers to x in the array. The result should also be sorted in ascending order. An integer a is closer to x than an integer b if: |a - x| < |b - x|, or |a - x| == |b - x| and a < b Explaination: Input: points = [[1,3],[-2,2]], k = 1 Output: [[-2,2]] Explanation: The distance between (1, 3) and the origin is sqrt(10). The distance between (-2, 2) and the origin is sqrt(8). Since sqrt(8) < sqrt(10), (-2, 2) is closer to the origin. We only want the closest k = 1 points from the origin, so the answer is just [[-2,2]]. Example 2: Input: points = [[3,3],[5,-1],[-2,4]], k = 2 Output: [[3,3],[-2,4]] Explanation: The answer [[-2,4],[3,3]] would also be accepted. */ #include #include #include #include using namespace std; class Solution { public: vector> kClosest(vector>& points, int k) { // Answer vector vector> result(k); // Max heap storage initialization priority_queue> maxHeap; // Construction of max heap for (auto& p : points) { int x = p[0], y = p[1]; // Calculate the squared distance from the origin using the formula x^2 + y^2 // Store the distance along with the coordinates (x, y) in the maxHeap maxHeap.push({x * x + y * y, x, y}); // If the size of the maxHeap exceeds k, remove the point with the maximum distance if (maxHeap.size() > k) { maxHeap.pop(); } } // Extract the k closest points from the maxHeap and store them in the result vector for (int i = k - 1; i >= 0; --i) { vector top = maxHeap.top(); maxHeap.pop(); result[i] = {top[1], top[2]}; } // Return the result vector containing the k closest points return result; } }; int main() { vector> points = {{1, 3}, {-2, 2}, {5, -1}, {0, 0}, {3, 4}}; int k = 3; Solution solution; vector> closestPoints = solution.kClosest(points, k); cout << "The " << k << " closest points to the origin are:\n"; for (const auto& point : closestPoints) { cout << "(" << point[0] << ", " << point[1] << ")\n"; } return 0; }/* Time Complexity: Constructing the maxHeap: the overall time complexity is O(N log K). Extracting the k closest points: O(K log K). We extract the top element from the maxHeap k times, each operation taking O(log K) time. Hence, the time complexity is O(K log K). Therefore, the overall time complexity of the solution is O(N log K), where N is the number of points and K is the value of k. Space Complexity: The maxHeap stores at most k elements, so the space complexity for the maxHeap is O(K). The result vector stores k closest points, resulting in O(K) space. Apart from these, the solution uses a constant amount of space for variables and temporary storage.*/ ================================================ FILE: Math/K_closest_points_to_origin.java ================================================ /** Time Complexity: O(nlog(n)), Space Complexity: O(n) Given an array of points where points[i] = [xi, yi] represents a point on the X-Y plane and an integer k, return the k closest points to the origin (0, 0). The distance between two points on the X-Y plane is the Euclidean distance (i.e., √(x1 - x2)^2 + (y1 - y2)^2). You may return the answer in any order. The answer is guaranteed to be unique (except for the order that it is in). Example 1: Input: points = [[1,3],[-2,2]], k = 1 Output: [[-2,2]] Explanation: The distance between (1, 3) and the origin is sqrt(10). The distance between (-2, 2) and the origin is sqrt(8). Since sqrt(8) < sqrt(10), (-2, 2) is closer to the origin. We only want the closest k = 1 points from the origin, so the answer is just [[-2,2]]. Example 2: Input: points = [[3,3],[5,-1],[-2,4]], k = 2 Output: [[3,3],[-2,4]] Explanation: The answer [[-2,4],[3,3]] would also be accepted. **/ class Solution { public int[][] kClosest(int[][] points, int k) { /** The solution is very simple, for each point(x, y) we calculate the euclidean distance between (x, y) and the origin point (0, 0), then add all the distances in Priority Queue (Minimum Heap). The priority queue will sort them in increasing order. NOTE that we need to return the points not the distances, that's why we make Priority Queue of Pair>. **/ PriorityQueue>> pq = new PriorityQueue<>(new SortBySmallerDistanceComparator()); for (int i = 0; i < points.length; ++i) { int x = points[i][0], y = points[i][1]; double distance = Math.sqrt(x * x + y * y); pq.add(new Pair(distance, new Pair(x, y))); } int [][]closestK = new int[k][2]; for(int i = 0; i < k; ++i) { closestK[i][0] = pq.peek().getValue().getKey(); closestK[i][1] = pq.peek().getValue().getValue(); pq.poll(); } return closestK; } /* Compartor to sort the points based on the distance (smaller). */ class SortBySmallerDistanceComparator implements Comparator>> { @Override public int compare(Pair> a, Pair> b) { return (a.getKey() - b.getKey()) <= 0 ? -1 : 1; } } } ================================================ FILE: Math/K_closest_points_to_origin.py ================================================ ''' Given an array of points where points[i] = [xi, yi] represents a point on the X-Y plane and an integer k, return the k closest points to the origin (0, 0). The distance between two points on the X-Y plane is the Euclidean distance (i.e., √(x1 - x2)2 + (y1 - y2)2). You may return the answer in any order. The answer is guaranteed to be unique (except for the order that it is in). Example 1: Input: points = [[1,3],[-2,2]], k = 1 Output: [[-2,2]] Explanation: The distance between (1, 3) and the origin is sqrt(10). The distance between (-2, 2) and the origin is sqrt(8). Since sqrt(8) < sqrt(10), (-2, 2) is closer to the origin. We only want the closest k = 1 points from the origin, so the answer is just [[-2,2]]. Example 2: Input: points = [[3,3],[5,-1],[-2,4]], k = 2 Output: [[3,3],[-2,4]] Explanation: The answer [[-2,4],[3,3]] would also be accepted. Constraints: 1 <= k <= points.length <= 104 -104 < xi, yi < 104 ''' import math class Solution: def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]: distance_arr=[] #calculating the distance from center for i in points: distance_arr.append(math.sqrt(pow(i[0],2)+pow(i[1],2))) #Mapping the values of distance and point #Also considering the collision if two points have same distance from origin hash_map={} for i in range(len(points)): if(distance_arr[i] in hash_map): hash_map[distance_arr[i]].append(points[i]) else: hash_map[distance_arr[i]] = [points[i]] #sorting distances distance_arr.sort() #Retrieving the values in the order of distance from distance_arr final_arr,count = [],0 for i in range(len(points)): for j in hash_map[distance_arr[i]]: final_arr.append(j) count+=1 if(count>=k): break return final_arr ================================================ FILE: Math/Number_of_Substrings_With_Only_1s.cpp ================================================ /* Given a binary string s, return the number of substrings with all characters 1's. Since the answer may be too large, return it modulo 109 + 7. Example 1: Input: s = "0110111" Output: 9 Explanation: There are 9 substring in total with only 1's characters. "1" -> 5 times. "11" -> 3 times. "111" -> 1 time. Example 2: Input: s = "101" Output: 2 Explanation: Substring "1" is shown 2 times in s. Example 3: Input: s = "111111" Output: 21 Explanation: Each substring contains only 1's characters. Constraints: 1 <= s.length <= 105 s[i] is either '0' or '1'. Approach: First we count continuos number of ones and if we have n continuos 1's than total substring = (n*(n+1))/2. Using this we can count all substring and add them and return. using this property (a*b)%mod = ((a%mod)*(b%mod))%mod */ class Solution { public: int mod = 1000000007; int numSub(string s) { long long cnt = 0,ans=0; for(int i = 0;i 0) return x * temp * temp; else return (temp * temp) / x; } } } class PowXn { public static void main(String[] args) { double x=2.00; int y=10; Solution s=new Solution(); System.out.println(s.myPow(x,y)); } } ================================================ FILE: Math/PowXn.py ================================================ # Implement pow(x, n), which calculates x raised to the power n (i.e., xn). in Go # --- Recursion Approach --- # Example 1: # Input: x = 2, n = 10 # Output: 1024 # Example 2: # Input: x = 2.10000, n = 3 # Output: 9.26100 # Example 3: # Input: x = 2.00000, n = -2 # Output: 0.25000 # Explanation: 2^(-2) = 1/(2^2) = 1/4 = 0.25 def power(x, n): if n == 0: return 1 elif n<0: return power(1/x, -n) else: return (x*power(x, n-1)) if __name__ == '__main__': x = 2 n = 3 print(power(x, n)) ================================================ FILE: Math/Reverse_Integer.cpp ================================================ /* Given a signed 32-bit integer x, return x with its digits reversed. If reversing x causes the value to go outside the signed 32-bit integer range [-231, 231 - 1], then return 0. Assume the environment does not allow you to store 64-bit integers (signed or unsigned). Example 1: Input: x = 123 Output: 321 Example 2: Input: x = -123 Output: -321 Example 3: Input: x = 120 Output: 21 Constraints: -2^31 <= x <= 2^31 - 1 */ // function for reversing the integer; int reverse(int x) { int ans =0; if(x > INT_MAX || x < INT_MIN) // checking the integer is in range of -2^31 <= x <= 2^31 - 1 return 0; while(x != 0){ if(ans > INT_MAX/10 || ans < INT_MIN/10) // -2^31 <= x <= 2^31 - 1 this condition for border values of range return 0; ans = ans*10; // we increaing the unit position each itereation ans += x%10; // here we finding the last unit elemet so that we can add in ans x /=10; // decreamenting the value or we can say elemenating the last unit element that we are find } return ans; // returning the ans; } ================================================ FILE: Math/Sum_four.cpp ================================================ #include #include #include using namespace std; vector> fourSum(vector& nums, int target) { vector> result; int n = nums.size(); if (n < 4) { return result; } sort(nums.begin(), nums.end()); for (int i = 0; i < n - 3; i++) { // Avoid duplicates if (i > 0 && nums[i] == nums[i - 1]) { continue; } for (int j = i + 1; j < n - 2; j++) { // Avoid duplicates if (j > i + 1 && nums[j] == nums[j - 1]) { continue; } int left = j + 1; int right = n - 1; while (left < right) { int sum = nums[i] + nums[j] + nums[left] + nums[right]; if (sum < target) { left++; } else if (sum > target) { right--; } else { result.push_back({nums[i], nums[j], nums[left], nums[right]}); // Avoid duplicates while (left < right && nums[left] == nums[left + 1]) { left++; } while (left < right && nums[right] == nums[right - 1]) { right--; } left++; right--; } } } } return result; } int main() { vector nums = {1, 0, -1, 0, -2, 2}; int target = 0; vector> result = fourSum(nums, target); for (vector& quad : result) { cout << "["; for (int i = 0; i < 4; i++) { cout << quad[i]; if (i < 3) { cout << ", "; } } cout << "]" << endl; } return 0; } ================================================ FILE: Math/circle.cpp ================================================ class Solution { public int[] countPoints(int[][] points, int[][] queries) { int[] answer = new int[queries.length]; for (int j = 0; j < queries.length; j++) { int centerX = queries[j][0]; int centerY = queries[j][1]; int radius = queries[j][2]; for (int i = 0; i < points.length; i++) { int pointX = points[i][0]; int pointY = points[i][1]; // Check if the point (pointX, pointY) is inside the circle. if (isInsideCircle(pointX, pointY, centerX, centerY, radius)) { answer[j]++; } } } return answer; } private boolean isInsideCircle(int x, int y, int centerX, int centerY, int radius) { int distanceSquared = (x - centerX) * (x - centerX) + (y - centerY) * (y - centerY); return distanceSquared <= radius * radius; } } ================================================ FILE: Math/count_numbers_with_unique_digits.java ================================================ /* Implement countNumbersWithUniqueDigits(n), which calculates the number of all numbers with unique digits, x, where 0 <= x < 10^n. Explanation: - For n=0, the answer is 1 (only the number 0, here considered to have 0 digits). - For n=1, the answer is the number of one-digit numbers with unique digits (9), plus the result for n=0 (1). - For n=2, the answer is the number of two-digit numbers with unique digits (between 10 and 99), plus the result for n=1. The number of two-digit numbers with unique digits is 9*9 (9 possible first digits between 1-9, then 9 possible second digits between 0-9 that are different from the first digit). - For n=3, the answer is the number of three-digit number with unique digits (between 100 and 999), plus the result for n=2. The number of three-digit numbers with unique digits is 9*9*8 (9 possible first digits between 1-9, then 9 possible second digits between 0-9 that are different from the first digit, then 8 possible third digits between 0-9 that are different from the first two). - And so on, using recursive calls of the same function. Note: There are no numbers with 11 or more digits that have unique digits. So for n>=10, the result will be the same for any n. Example 1: Input: n=3 Output: 739 Example 2: Input: n=6 Output: 168571 Example 3: Input: n=8 Output: 2345851 Constraints: n >= 0 Time complexity: O(n) Space complexity: O(n) */ class Solution { public int countNumbersWithUniqueDigits(int n) { if (n==0) { return 1; } int counter = 9; for (int i=1; i=2 multiplied by any integer j>=2 is not prime. So the corresponding value of i*j in the array is changed to true. This will only affect cells with an index number higher than the current i, as i*j>i for any j>=2.*/ for (int j=2; i*j 0 <= n <= 8 */ class Solution { public: /* By using concepts of permutation and combination, we can count the unique numbers for N digits. For the first digit we can choose any 1 digit from 1-9 For the latter digits we can choose any 1 digit from 0-9 except the digits already used */ int countNumbersWithUniqueDigits(int n) { int ans=0; for(int i=0;i 0, the function initializes the count to 10 (digits 0-9), and then iterates through each number of digits from 2 to n. For each number of digits, the function calculates the number of possible unique numbers and adds it to the total count. The number of possible unique numbers for i digits is calculated by first choosing the first digit (9 choices) and then choosing each subsequent digit (8 choices for the second digit, 7 choices for the third digit, and so on). The product of all these choices is the total number of possible unique numbers for i digits. This calculation is repeated for each number of digits from 2 to n, and the total count is returned at the end. For example, if n = 2, the function should return 91. There are 10 possible numbers with 1 digit (0-9), and 91 possible numbers with 2 digits (10-99) that have unique digits */ package main import ( "fmt" ) func countNumbersWithUniqueDigits(n int) int { // Base case: n = 0, only 1 possible number (0) if n == 0 { return 1 } // For n > 0, initialize count to 10 (digits 0-9) count := 10 // Calculate count for 2 to n digits for i := 2; i <= n; i++ { // For each number of digits, calculate the number of possible unique numbers // First digit can be 1-9 (9 choices), subsequent digits can be any of the remaining 9-1 = 8 choices // Multiply the choices together to get the total number of possible unique numbers // Add this count to the total count currCount := 9 for j := 1; j < i; j++ { currCount *= 10 - j } count += currCount } return count } func main() { // Test case with n = 2 fmt.Println(countNumbersWithUniqueDigits(2)) // Output: 91 } ================================================ FILE: Math/factorial.py ================================================ ''' Approach: We will take the number as input from the user and calculate its factorial. To calculate the factorial, we will start from 1 and multiply it with all the numbers from 1 to n. Finally, we will return the factorial. Time Complexity: O(n) Space Complexity: O(1) Sample Input: 5 Sample Output: Factorial of 5 is 120 ''' # Initializing the factorial to 1 fact = 1 def factorial(num): # If the number is 0 or 1, then the factorial is 1. if num == 0 or num == 1: return 1 # Calculating factorial by multiplying every number from 1 to num for i in range(1, num+1): fact *= i return fact #Taking input from user num = int(input("Enter a number: ")) #Calculating and printing the factorial of the number print("Factorial of", num, "is", factorial(num)) ================================================ FILE: Math/factorial_iterative.js ================================================ // Program to find factorial till a given number function factorialIterative(number) { var factorialResult = 1; if (number == 0) { return 0; } for (i = 1; i <= number; i++) { factorialResult *= i; } return factorialResult; } // Driver code console.log(factorialIterative(2)); console.log(factorialIterative(3)); console.log(factorialIterative(4)); console.log(factorialIterative(5)); ================================================ FILE: Math/find_longest_increasing_subsequence.cpp ================================================ /* Given an integer array nums, return the length of the longest strictly increasing subsequence. Example 1: Input: nums = [10,9,2,5,3,7,101,18] Output: 4 Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4. Example 2: Input: nums = [0,1,0,3,2,3] Output: 4 Explanation: The longest increasing subsequence is [0,1,2,3], therefore the length is 4. Example 3: Input: nums = [7,7,7,7,7,7,7] Output: 1 Explanation of the approach: 1. We will use the concept of Dynamic Programming to solve this problem. 2. We will create a dp array of size n, where n is the size of the given array. 3. We will initialize the dp array with 1, as the length of the longest increasing subsequence ending at any index is atleast 1. 4. We will iterate over the array from the second index to the last index. 5. For each index, we will iterate over the array from the first index to the current index. 6. If the current element is greater than the element at the inner loop, then we will update the dp array at the current index with the maximum of the current value and the value at the inner loop + 1. 7. We will return the maximum value in the dp array. Time Complexity: O(N^2), where N is the size of the given array. Space Complexity: O(N), where N is the size of the given array. */ class Solution{ public: int lengthOfLIS(vector &nums) { // Initializing the dp array with 1, as the length of the longest increasing subsequence ending at any index is atleast 1. vector dp(nums.size(), 1); // Iterating over the array from the second index to the last index. for (int i = 1; i < nums.size(); i++) { // Iterating over the array from the first index to the current index. for (int j = 0; j < i; j++) { // If the current element is greater than the element at the inner loop, then we will update the dp array at the current index with the maximum of the current value and the value at the inner loop + 1. // This is because, if the current element is greater than the element at the inner loop, then we can add the current element to the increasing subsequence ending at the inner loop element, and the length of the increasing subsequence ending at the current element will be the length of the increasing subsequence ending at the inner loop element + 1. // We will take the maximum of the current value and the value at the inner loop + 1, because we want to find the length of the longest increasing subsequence ending at the current element. // We will update the dp array at the current index with the maximum of the current value and the value at the inner loop + 1. if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1); } } // Returning the maximum value in the dp array. return *max_element(dp.begin(), dp.end()); } }; ================================================ FILE: Math/hamming_distance.cpp ================================================ /* Example: Input: nums = [4,14,2] Output: 6 Explanation: In binary representation, the 4 is 0100, 14 is 1110, and 2 is 0010 (just showing the four bits relevant in this case). The answer will be: HammingDistance(4, 14) + HammingDistance(4, 2) + HammingDistance(14, 2) = 2 + 2 + 2 = 6. Detailed Explanation: The totalHammingDistance function takes a reference to a vector of integers nums, and returns the total Hamming distance between all possible pairs of elements in the vector. In the function, we initialize the answer ans to 0, and the size of the vector n is obtained using the size() method of the vector. We then loop through all 32 bits (since we are dealing with 32-bit integers), and for each bit position i, we count the number of elements in nums with the i-th bit set by looping through all elements and checking if the bit is set using bitwise AND operator &. The count c is then multiplied by (n-c), which gives the number of pairs with different bits at the i-th position. This count is added to the answer ans. Finally, the function returns the total Hamming distance. The main function is just an example usage, where we create a vector nums and call the totalHammingDistance method of a Solution object to get the total Hamming distance between all possible pairs of elements in nums. */ // code: #include #include using namespace std; class Solution { public: int totalHammingDistance(vector& nums) { int ans = 0; int n = nums.size(); for(int i = 0; i < 32; i++) { int c = 0; for(int j = 0; j < n; j++) { if((nums[j] & (1 << i))) { c++; } } ans += (c * (n - c)); } return ans; } }; int main() { // Example usage vector nums = {4, 14, 2}; Solution s; int result = s.totalHammingDistance(nums); return 0; } ================================================ FILE: Math/is_power_of_two.js ================================================ // Whether the provided number is power of two // Time complexity - O(log n) function isPowerOfTwo(inputNumber) { if (inputNumber <= 1) { return false; } var dividedNumber = inputNumber; while (dividedNumber !== 1) { if (dividedNumber % 2 !== 0) { return false; } dividedNumber = dividedNumber / 2; } return true; } // Time complexity - O(1) function isPowerOfTwoConst(inputNumber) { if (inputNumber <= 1) { return false; } return ( parseInt(Math.ceil(Math.log(n) / Math.log(2))) == parseInt(Math.floor(Math.log(n) / Math.log(2))) ); } // Time complexity - O(log n) function isPowerOfTwoConst(inputNumber) { if (inputNumber <= 1) { return false; } return ( parseInt(Math.ceil(Math.log(n) / Math.log(2))) == parseInt(Math.floor(Math.log(n) / Math.log(2))) ); } //Driver code console.log(isPowerOfTwo(8)); console.log(isPowerOfTwo(1)); console.log(isPowerOfTwo(7)); console.log(isPowerOfTwo(6)); console.log(isPowerOfTwo(16)); console.log(isPowerOfTwo(32)); ================================================ FILE: Math/is_prime.js ================================================ // Whether the given number is a prime nujmber or not // Time complexity O(n) function isPrime(input) { for (i = 2; i < input; i++) { if (input % i == 0) { return "The number is not a prime number"; break; } else { continue; } } return "Given number is a prime number"; } // Driver code console.log(isPrime(6)); // Time complexity: O(sqrt(n)) // Space complexity: O(1) const isPrime = (num) => { for (let i = 2, s = Math.sqrt(num); i <= s; i++) { if (num % i === 0) return false; } return num > 1; }; console.log(isPrime(10)); // Time complexity - O(logn) function isPrimeLogn(input) { for (i = 2; i < Math.sqrt(input); i++) { if (input % i == 0) { return "The number is not a prime number"; break; } else { continue; } } return "Given number is a prime number"; } // Driver code console.log(isPrime(6)); console.log(isPrimeLogn(7)); ================================================ FILE: Math/k_closest_points_to_origin.cpp ================================================ /* Given an array of points where points[i] = [xi, yi] represents a point on the X-Y plane and an integer k, return the k closest points to the origin (0, 0). You may return the answer in any order. The answer is guaranteed to be unique (except for the order that it is in). Input: points = [[1,3],[-2,2]], k = 1 Output: [[-2,2]] Input: points = [[3,3],[5,-1],[-2,4]], k = 2 Output: [[3,3],[-2,4]] Constraints: > 1 <= k <= points.length <= 104 > -104 < xi, yi < 104 */ class Solution { public: vector> kClosest(vector>& points, int k) { // Lets define some variables we will be needing vector> ans; vector> sorted; // For each point in the list points lets store distance_from_origin and point index as a pair for(int i=0;i using namespace std; int modular_expo(int x, int n, int mod){ int ans = 1; while(n >= 1){ if(n % 2 == 0){ x = ((x % mod) * (x % mod)) % mod; n /= 2; } else{ ans = ((ans % mod) * (x % mod)) % mod; n--; } } return ans; } int main(){ int mod = 1000000007; cout << modular_expo(5, 3, mod); return 0; } ================================================ FILE: Math/modular_expo_recursive.cpp ================================================ // Modular exponentiation is exponentiation performed over a modulus. // It is useful in computer science, especially in the field of public-key cryptography, // where it is used in both Diffie-Hellman Key Exchange and RSA public/private keys. // Sample Input : 10 3 // Output : 1000 // recursive approach #include using namespace std; int modular_expo(int x, int n, int mod){ if(n == 0) return 1; else if(n % 2 == 0){ int y = modular_expo(x, n / 2, mod); return (y * y) % mod; } else{ return ((x % mod) * modular_expo(x, n - 1, mod)) % mod; } } int main(){ int n, x, mod = 1000000007; cin >> x >> n; cout << modular_expo(x, n, mod); return 0; } ================================================ FILE: Math/num_points_inside_a_circle.cpp ================================================ /* You are given an array points where points[i] = [xi, yi] is the coordinates of the ith point on a 2D plane. Multiple points can have the same coordinates. You are also given an array queries where queries[j] = [xj, yj, rj] describes a circle centered at (xj, yj) with a radius of rj. For each query queries[j], compute the number of points inside the jth circle. Points on the border of the circle are considered inside. Return an array answer, where answer[j] is the answer to the jth query. Example 1: Input: points = [[1,3],[3,3],[5,3],[2,2]], queries = [[2,3,1],[4,3,1],[1,1,2]] Output: [3,2,2] Explanation: queries[0] is the first circle [2,3,1], which include the points [1,3], [2,2] and [3,3]. queries[1] is the second circle [4,3,1], which include the points [3,3] and [5,3]. and queries[2] is the third circle [1,1,2], which include the point [1,3] and [2,2]. Example 2: Input: points = [[1,1],[2,2],[3,3],[4,4],[5,5]], queries = [[1,2,2],[2,2,2],[4,3,2],[4,3,3]] Output: [2,3,2,4] Explanation: queries[0] is the first circle [1,2,2], which include the points [1,1] and [2,2]. queries[1] is the second circle [2,2,2], which include the points [1,1], [2,2] and [3,3]. queries[2] is the third circle [4,3,2], which include the points [3,3] and [4,4]. and queries[3] is the fourth circle [4,3,3], which include the points [2,2], [3,3], [4,4] and [5,5]. Explanation of the approach: 1. We will use the formula of distance between two points to find the distance between the center of the circle and the given point. 2. If the distance is less than or equal to the radius of the circle, then the point lies inside the circle. Time Complexity: O(N * Q), where N is the number of points and Q is the number of queries. Space Complexity: O(1) */ class Solution { public: vector countPoints(vector> &points, vector> &queries) { vector ans; // Iterating over the queries. for (int i = 0; i < queries.size(); i++) { // Initializing the count to 0. This will store the number of points inside the circle. int count = 0; // Iterating over the points. for (int j = 0; j < points.size(); j++) { // Calculating the distance between the center of the circle and the given point. int distance = pow(points[j][0] - queries[i][0], 2) + pow(points[j][1] - queries[i][1], 2); // If the distance is less than or equal to the radius of the circle, then the point lies inside the circle. if (distance <= pow(queries[i][2], 2)) count++; // If the point lies inside the circle, then increment the count. } // Pushing the count to the answer vector. ans.push_back(count); } return ans; } // Further reading: https://www.geeksforgeeks.org/queries-points-number-points-lie-given-circle/ }; ================================================ FILE: Math/num_points_inside_a_circle.js ================================================ // Program Author : TheCodeVenturer[Niraj Modi] /* Program Definition : Queries on Number of Points inside a circle Description : you are given n points in a cartesian plane [points] and a queries array Where each element inside it is an array each consisting of [xj, yj, rj] where, (xj,yj) is centre of the circle and rj is radius of the circle Now, you are required to find no.of points inside the circle Approach: The only possible soln somehow will be that you have to calculate distance of each point from the centre of each circle and if the distance <= radius then the point is inside the the circle else not Distance Formula = ((x1-x2)**2 + (y1-y2)**2)**(1/2) Complexity: The time complexity of this solution is O(m*n) where m = points.length and n = queries.length The space complexity is O(1),we are not using any Auxiliary Spaces. Sample input/outputs: Example 1: Input: points = [[1,3],[3,3],[5,3],[2,2]] queries = [[2,3,1],[4,3,1],[1,1,2]] Output: [3,2,2] Example 1: Input: points = [[1,1],[2,2],[3,3],[4,4],[5,5]] queries = [[1,2,2],[2,2,2],[4,3,2],[4,3,3]] Output: [2,3,2,4] */ var countPoints = function(points, queries) { sol=[] // Initialising solution array for(let query of queries){ var count=0 //no.of points inside that circle is initially zero for(let point of points){ d = Math.sqrt((query[0]-point[0])**2+(query[1]-point[1])**2) //calculating distance between the points if(d<=query[2])count++ //if it is <= radius it is inside the circle } sol.push(count) //finally add the solution to solution array } return sol }; points = [[1,3],[3,3],[5,3],[2,2]] queries = [[2,3,1],[4,3,1],[1,1,2]] console.log(countPoints(points,queries)) ================================================ FILE: Math/num_points_inside_a_circle.py ================================================ # Program Author : TheCodeVenturer[Niraj Modi] ''' Program Definition : Queries on Number of Points inside a circle Description : you are given n points in a cartesian plane [points] and a queries array Where each element inside it is an array each consisting of [xj, yj, rj] where, (xj,yj) is centre of the circle and rj is radius of the circle Now, you are required to find no.of points inside the circle Approach: The only possible soln somehow will be that you have to calculate distance of each point from the centre of each circle and if the distance <= radius then the point is inside the the circle else not Distance Formula = ((x1-x2)**2 + (y1-y2)**2)**(1/2) Complexity: The time complexity of this solution is O(m*n) where m = points.length and n = queries.length The space complexity is O(1),we are not using any Auxiliary Spaces. Sample input/outputs: Example 1: Input: points = [[1,3],[3,3],[5,3],[2,2]] queries = [[2,3,1],[4,3,1],[1,1,2]] Output: [3,2,2] Example 1: Input: points = [[1,1],[2,2],[3,3],[4,4],[5,5]] queries = [[1,2,2],[2,2,2],[4,3,2],[4,3,3]] Output: [2,3,2,4] ''' class Solution: def countPoints(self, points: list[list[int]], queries: list[list[int]]) -> list[int]: sol :list = [] #Initialising solution array for x1,y1,r in queries: count:int=0 #no.of points inside that circle is initially zero for x2,y2 in points: d = ((x2-x1)**2 + (y2-y1)**2)**(1/2) #calculating distance between the points if(d<=r): #if it is <= radius it is inside the circle count+=1 sol.append(count) #finally add the count to solution array return sol points = [[1,3],[3,3],[5,3],[2,2]] queries = [[2,3,1],[4,3,1],[1,1,2]] print(Solution().countPoints(points,queries)) ================================================ FILE: Math/num_steps_reduce_to_zero.Go ================================================ /* Given an integer num, return the number of steps to reduce it to zero. In one step, if the current number is even, you have to divide it by 2, otherwise, you have to subtract 1 from it. Example 1: Input: num = 14 Output: 6 Explanation: Step 1) 14 is even; divide by 2 and obtain 7. Step 2) 7 is odd; subtract 1 and obtain 6. Step 3) 6 is even; divide by 2 and obtain 3. Step 4) 3 is odd; subtract 1 and obtain 2. Step 5) 2 is even; divide by 2 and obtain 1. Step 6) 1 is odd; subtract 1 and obtain 0. Example 2: Input: num = 8 Output: 4 Explanation: Step 1) 8 is even; divide by 2 and obtain 4. Step 2) 4 is even; divide by 2 and obtain 2. Step 3) 2 is even; divide by 2 and obtain 1. Step 4) 1 is odd; subtract 1 and obtain 0. Example 3: Input: num = 123 Output: 12 Constraints: 0 <= num <= 106 */ func numberOfSteps(num int) int { var steps = 0 for num != 0 { if num % 2 == 0 { num /= 2 steps++ } else { num -= 1 steps++ } } return steps } ================================================ FILE: Math/num_steps_reduce_to_zero.cpp ================================================ /* Given an integer num, return the number of steps to reduce it to zero. In one step, if the current number is even, you have to divide it by 2, otherwise, you have to subtract 1 from it. Example 1: Input: num = 14 Output: 6 Explanation: Step 1) 14 is even; divide by 2 and obtain 7. Step 2) 7 is odd; subtract 1 and obtain 6. Step 3) 6 is even; divide by 2 and obtain 3. Step 4) 3 is odd; subtract 1 and obtain 2. Step 5) 2 is even; divide by 2 and obtain 1. Step 6) 1 is odd; subtract 1 and obtain 0. Example 2: Input: num = 8 Output: 4 Explanation: Step 1) 8 is even; divide by 2 and obtain 4. Step 2) 4 is even; divide by 2 and obtain 2. Step 3) 2 is even; divide by 2 and obtain 1. Step 4) 1 is odd; subtract 1 and obtain 0. Example 3: Input: num = 123 Output: 12 Constraints: 0 <= num <= 106 */ class Solution { public: int numberOfSteps(int num) { // We initialize the step counter. int steps = 0; // While the number is still not zero... while (num != 0) { // ...we check if it is odd or even. // If it is even... if (num % 2 == 0) { // ...we divide it by 2 and increase the step counter. num /= 2; steps++; // If it is odd... } else { // ...we substract 1 and increase the step counter. num -= 1; steps++; } } // Finally, we return the step counter. return steps; } }; ================================================ FILE: Math/num_steps_reduce_to_zero.java ================================================ /* Given an integer num, return the number of steps to reduce it to zero. In one step, if the current number is even, you have to divide it by 2, otherwise, you have to subtract 1 from it. Example 1: Input: num = 14 Output: 6 Explanation: Step 1) 14 is even; divide by 2 and obtain 7. Step 2) 7 is odd; subtract 1 and obtain 6. Step 3) 6 is even; divide by 2 and obtain 3. Step 4) 3 is odd; subtract 1 and obtain 2. Step 5) 2 is even; divide by 2 and obtain 1. Step 6) 1 is odd; subtract 1 and obtain 0. Example 2: Input: num = 8 Output: 4 Explanation: Step 1) 8 is even; divide by 2 and obtain 4. Step 2) 4 is even; divide by 2 and obtain 2. Step 3) 2 is even; divide by 2 and obtain 1. Step 4) 1 is odd; subtract 1 and obtain 0. Example 3: Input: num = 123 Output: 12 Constraints: 0 <= num <= 106 */ class Solution { public int numberOfSteps(int num) { // We initialize the step counter. int steps = 0; // While the number is still not zero... while (num != 0) { // ...we check if it is odd or even. // If it is even... if (num % 2 == 0) { // ...we divide it by 2 and increase the step counter. num /= 2; steps++; // If it is odd... } else { // ...we substract 1 and increase the step counter. num -= 1; steps++; } } // Finally, we return the step counter. return steps; } } ================================================ FILE: Math/num_steps_reduce_to_zero.js ================================================ /* Given an integer num, return the number of steps to reduce it to zero. In one step, if the current number is even, you have to divide it by 2, otherwise, you have to subtract 1 from it. Example 1: Input: num = 14 Output: 6 Explanation: Step 1) 14 is even; divide by 2 and obtain 7. Step 2) 7 is odd; subtract 1 and obtain 6. Step 3) 6 is even; divide by 2 and obtain 3. Step 4) 3 is odd; subtract 1 and obtain 2. Step 5) 2 is even; divide by 2 and obtain 1. Step 6) 1 is odd; subtract 1 and obtain 0. Example 2: Input: num = 8 Output: 4 Explanation: Step 1) 8 is even; divide by 2 and obtain 4. Step 2) 4 is even; divide by 2 and obtain 2. Step 3) 2 is even; divide by 2 and obtain 1. Step 4) 1 is odd; subtract 1 and obtain 0. Example 3: Input: num = 123 Output: 12 Constraints: 0 <= num <= 106 */ /** * @param {number} num * @return {number} */ var numberOfSteps = function (num) { // We initialize the step counter. let steps = 0; // While the number is still not zero... while (num != 0) { // ...we check if it is odd or even. // If it is even... if (num % 2 == 0) { // ...we divide it by 2 and increase the step counter. num /= 2; steps++; // If it is odd... } else { // ...we substract 1 and increase the step counter. num -= 1; steps++; } } // Finally, we return the step counter. return steps; }; ================================================ FILE: Math/num_steps_reduce_to_zero.py ================================================ ''' Given an integer num, return the number of steps to reduce it to zero. In one step, if the current number is even, you have to divide it by 2, otherwise, you have to subtract 1 from it. Example 1: Input: num = 14 Output: 6 Explanation: Step 1) 14 is even; divide by 2 and obtain 7. Step 2) 7 is odd; subtract 1 and obtain 6. Step 3) 6 is even; divide by 2 and obtain 3. Step 4) 3 is odd; subtract 1 and obtain 2. Step 5) 2 is even; divide by 2 and obtain 1. Step 6) 1 is odd; subtract 1 and obtain 0. Example 2: Input: num = 8 Output: 4 Explanation: Step 1) 8 is even; divide by 2 and obtain 4. Step 2) 4 is even; divide by 2 and obtain 2. Step 3) 2 is even; divide by 2 and obtain 1. Step 4) 1 is odd; subtract 1 and obtain 0. Example 3: Input: num = 123 Output: 12 Constraints: 0 <= num <= 106 ''' class Solution(object): def numberOfSteps(self, num): """ :type num: int :rtype: int """ steps = 0 while num != 0: if num % 2 == 0: num = num / 2 steps = steps + 1 else: num = num - 1 steps = steps + 1 return steps ================================================ FILE: Math/number_of_substrings_with_only_1s.js ================================================ /* PROBLEM STATEMENT: Given a binary string s, return the number of substrings with all characters 1's. Since the answer may be too large, return it modulo 109 + 7. EXAMPLE 1: Input: s = "0110111" Output: 9 Explanation: There are 9 substring in total with only 1's characters. "1" -> 5 times. "11" -> 3 times. "111" -> 1 time. EXAMPLE 2: Input: s = "101" Output: 2 Explanation: Substring "1" is shown 2 times in s. CONSTRAINT: Constraints: 1 <= s.length <= 105 s[i] is either '0' or '1'. */ /* Explaination The function begins by initializing two variables - "count" and "answer" - to 0. "count" will be used to keep track of the current number of consecutive 1's in the string, while "answer" will eventually store the final answer. The constant "mod" is defined to be 10^9+7, which will be used to take modulo of the answer at each step, in order to prevent overflow. The function then iterates through the input string using a for loop. For each character in the string, it checks if it is a 1 or a 0. If it is a 1, the "count" variable is incremented by 1. If it is a 0, the "count" variable is reset to 0. At each step of the loop, the value of "count" is added to "answer" and then taken modulo "mod". This is done to count the number of substrings with all 1's and prevent overflow of the result. After the loop has finished, the final value of "answer" is returned as the solution to the problem. */ var numSub = function(s) { // count stores the current number of consecutive 1's var count=0; // answer storese the final answer to be returned var answer=0; // we mod the sum at each step so that they don't overflow const mod=1000000007; // iterate the entire string for(let i=0;i= divisor) { dividend -= divisor; quotient++; } if (negativeResult) { quotient = -quotient; } // Check for overflow if (quotient < -(2**31) || quotient > 2**31 - 1) { return 2**31 - 1; // Return INT_MAX for overflow } return quotient; } // Example usage: const dividend = 10; const divisor = 3; const result = divide(dividend, divisor); console.log(result); // Output should be 3 ================================================ FILE: Math/palindrome_number.java ================================================ /* Question : Given an integer x, return true if x is a palindrome, and false otherwise. Example 1: Input: x = 121 Output: true Explanation: 121 reads as 121 from left to right and from right to left. Example 2: Input: x = -121 Output: false Explanation: From left to right, it reads -121. From right to left, it becomes 121-. Therefore it is not a palindrome. Example 3: Input: x = 10 Output: false Explanation: Reads 01 from right to left. Therefore it is not a palindrome. Constraints: -231 <= x <= 231 - 1 */ class Solution { public boolean isPalindrome(int x) { if(x < 0){ return false; } //Checking if the number is equal to its reverse, if true, it is a palindrome return x == rev(x); } // Function to find reverse of a number public int reverse(int n){ int reverse = 0; while(n != 0){ int digit = n % 10; rev = digit + reverse * 10; n /= 10; } return reverse; } } ================================================ FILE: Math/pallindrome_number.cpp ================================================ /* Given an integer x, return true if x is a palindrome, and false otherwise. Example 1: Input: x = 121 Output: true Explanation: 121 reads as 121 from left to right and from right to left. Example 2: Input: x = -121 Output: false Explanation: From left to right, it reads -121. From right to left, it becomes 121-. Therefore it is not a palindrome. Example 3: Input: x = 10 Output: false Explanation: Reads 01 from right to left. Therefore it is not a palindrome. Constraints: -231 <= x <= 231 - 1 */ class Solution { public: bool isPalindrome(int x) { int temp = x; if(x < 0) return false; long long new_num = 0; while(x) { int rem = x % 10; new_num = (new_num * 10) + rem; x /= 10; } return new_num == temp; } }; ================================================ FILE: Math/pallindrome_number.py ================================================ ''' Given an integer x, return true if x is a palindrome , and false otherwise. Example 1: Input: x = 121 Output: true Explanation: 121 reads as 121 from left to right and from right to left. Example 2: Input: x = -121 Output: false Explanation: From left to right, it reads -121. From right to left, it becomes 121-. Therefore it is not a palindrome. Example 3: Input: x = 10 Output: false Explanation: Reads 01 from right to left. Therefore it is not a palindrome. Constraints: -231 <= x <= 231 - 1 Follow up: Could you solve it without converting the integer to a string? ''' class Solution: def isPalindrome(self, x: int) -> bool: #check if the number is less than zero. if x < 0: return False #Then, calculating the reverse of the number to see if it is equal to the original number. rev = 0 original_value = x while x != 0: rev = rev * 10 + x % 10 x = x // 10 return rev == original_value ================================================ FILE: Math/powXn.cpp ================================================ /* Implement pow(x, n), which calculates x raised to the power n (i.e., xn). Example 1: Input: x = 2.00000, n = 10 Output: 1024.00000 Example 2: Input: x = 2.10000, n = 3 Output: 9.26100 Example 3: Input: x = 2.00000, n = -2 Output: 0.25000 Explanation: 2-2 = 1/22 = 1/4 = 0.25 Constraints: -100.0 < x < 100.0 -231 <= n <= 231-1 n is an integer. -104 <= xn <= 104 if it we solve it though simply by recursion it will give time complexity of O(n) so we will use divide and conquer approach that leads to O(logn) time complexity eg. 3^7 dive seven by two 333 * 333 3 than in 333 divide by two 3 * 3 3 on conquering 93 * 93 than again conquer 27 * 27 * 3 final output is 218 */ class Solution { public: double pow(double x, int n) { if(x==0)return 0; if(n==0)return 1; double res=pow(x,n/2); res=res*res; if(n%2)res=x*res; return res; } double myPow(double x, int n) { if(n<0)return pow(1/x,n); return pow(x,n); } }; ================================================ FILE: Math/powXn.js ================================================ // Implement pow(x, n), which calculates x raised to the power n (i.e., xn). // Time complexity: O(log n) | Space complexity: O(log n) function pow(x, n) { // Any number raised to 0 is 1) if (n === 0) return 1 // Power of a negative number is the reciprocal of power of positive number if (n < 0) return 1 / pow(x, -n) // If n is even, calculate pow(x, n / 2) and return the square of it if (n % 2 === 0) return pow(x, n / 2) * pow(x, n / 2) // If n is odd, return x * pow(x, n - 1) (x raised to odd power n can be represented as x * x^(n-1)) return x * pow(x, n - 1) } // Sample inputs and outputs console.log(pow(2, 10)) // 1024 console.log(pow(2, -3)) // 0.125 console.log(pow(3, 3)) // 27 console.log(pow(3, 0)) // 1 ================================================ FILE: Math/prime_factorization.cpp ================================================ // Prime factorisation is a method to find the prime factors of a given number, say a composite number. // These factors are nothing but the prime numbers. A prime number is a number // which has only two factors, i.e. 1 and the number itself. For example, 2 is a prime number which has two factors, 2 × 1 // Sample Input: 10 // Output // 2->1 Times // 5->1 Times #include using namespace std; int f[100], expo[100], len = -1; void prime_factor(int n){ int d = 2; if(n == 1){ len++; f[len] = 2; expo[len] = 0; return; } while(n > 1 && 1ll * d * d <= n){ int k = 0; while(n % d == 0){ n = n / d; k++; } if(k > 0){ len++; f[len] = d; expo[len] = k; } d++; } if(n > 1){ len++; f[len] = n; expo[len] = 1; } } int main(){ int n; cin >> n; prime_factor(n); for(int i = 0; i <= len; i++){ cout << f[i] << "->" << expo[i] << " Times" << endl; } return 0; } ================================================ FILE: Math/shuffle_an_array.cpp ================================================ /* Given an integer array nums, design an algorithm to randomly shuffle the array. All permutations of the array should be equally likely as a result of the shuffling. Implement the Solution class: Solution(int[] nums) Initializes the object with the integer array nums. int[] reset() Resets the array to its original configuration and returns it. int[] shuffle() Returns a random shuffling of the array. Input ["Solution", "shuffle", "reset", "shuffle"] [[[1, 2, 3]], [], [], []] Output [null, [3, 1, 2], [1, 2, 3], [1, 3, 2]] Constraints: > 1 <= nums.length <= 50 > -106 <= nums[i] <= 106 > All the elements of nums are unique. > At most 104 calls in total will be made to reset and shuffle. */ /* APPROACH 1. Use two lists to store the original nums array. 2. We can keep one array as it is and return it whenever the reset() function is called. 3. We use the second list to shuffle the permutation of elements and return a shuffled array when shuffle() function is called. 4. For shuffling, choose 2 indices on random basis from the array and swap them. Repeat for n times. (n=size of the array) */ // O(N) Time-Complexity and O(N) Space-Complexity Solution class Solution { public: // lets make 2 variables, one to store the shuffled version of the given array and the other to store the original version vector shuffled; vector original; Solution(vector& nums) { // Push the elements of given array to required variables as Initialization for(int i=0;i reset() { // Return the variable storing the original version of the array return original; } vector shuffle() { int N=shuffled.size(); // shuffle the variable storing the shuffled version of the array randomly for(int i=0;i param_1 = obj->reset(); * vector param_2 = obj->shuffle(); */ ================================================ FILE: Math/shuffle_an_array.py ================================================ #Problem statement - Given an integer array nums, design an algorithm to randomly shuffle the array. # All permutations of the array should be equally likely as a result of the shuffling. # Complete the provided functions class Solution: def __init__(self, nums: List[int]): #the value of nums is stored as an instance variable that can be accessed and used by other methods within the class self.nums = nums def reset(self) -> List[int]: #returning the original array after reseting it return self.nums def shuffle(self) -> List[int]: s = [x for x in self.nums] random.shuffle(s) return s # Time complexity - O(N), where N is the length of the input list self.nums #Space compelxity - O(N) # Your Solution object will be instantiated and called as such: # obj = Solution(nums) # param_1 = obj.reset() # param_2 = obj.shuffle() ================================================ FILE: Math/sieve_of_eratosthenes.cpp ================================================ // In mathematics, the sieve of Eratosthenes is an ancient algorithm for finding all prime numbers up to any given limit. // It does so by iteratively marking as composite (i.e., not prime) the multiples of each prime, // starting with the first prime number, 2. The multiples of a given prime are generated as a sequence of numbers // starting from that prime, with constant difference between them that is equal to that prime. // This is the sieve's key distinction from using trial division to sequentially test each candidate // number for divisibility by each prime. Once all the multiples of each discovered prime have been marked as composites, // the remaining unmarked numbers are primes. Source(https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes) // Sample INput: 100 // Output : 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97 #include using namespace std; const int nmax = 100001; bool is_prime[nmax]; void seive_of_ero(int n){ for(int i = 2; i <= n; i++){ is_prime[i] = true; } for(int i = 2; i <= n / 2; i++){ if(is_prime[i]){ for(int j = i * 2; j <= n; j += i){ is_prime[j] = false; } } } } int main(){ int n; cin >> n; seive_of_ero(n); for(int i = 0; i <= 500; i++){ if(is_prime[i]) cout << i << " " << endl; } return 0; } ================================================ FILE: Math/substrings_with_1s.py ================================================ '''Name : Abhinav kumar Github username : Abhinavcode13 Repository name : data-structures-and-algorithms Problem : Number of Substrings With Only 1s in Python Issue Number : #518 Problem statement : Explanation of the below Python code : The implementation is quite similar to the C++ implementation. We iterate over the string s using a for loop and keep track of the count of consecutive 1's using the variable count. Whenever we encounter a '0', we calculate the number of substrings that can be formed using the formula n*(n+1)/2, add it to the final answer ans, and reset the value of count to 0. Finally, we calculate the number of substrings for the last substring of consecutive 1's, add it to the final answer ans, and return the result modulo 10^9 + 7. Note that we have used the integer division operator // to perform the division in Python. ''' ----------------------------------------------------------------------------------------------//Python code begins here-------------------------------------------------------------------------------------------------------------------------------------- class Solution: def numSub(self, s: str) -> int: count = 0 # count the number of consecutive ones ans = 0 # variable to store the final answer for i in range(len(s)): if s[i] == '1': count += 1 else: # calculate the number of possible substrings that can be formed # from the current consecutive ones and add it to the final answer ans = (ans + (count*(count+1))//2) % (10**9 + 7) count = 0 # handle the case when the string ends with consecutive ones ans = (ans + (count*(count+1))//2) % (10**9 + 7) return ans ================================================ FILE: Math/unique_digits.cpp ================================================ /*Name : Abhinav kumar Github username : Abhinavcode13 Repository name : data-structures-and-algorithms Problem : Count Numbers with Unique Digits in C++ Issue Number : #500 Problem statement : Explanation of the below C++ code : In this implementation, we first handle the base case of n = 0 by returning 1. Then, we initialize the answer ans to 10 since there are 10 unique digits between 0 and 9. We also initialize the variables unique_digits and available_digits to 9 since we can't use 0 as the first digit. Next, we enter a loop that runs n-1 times (since we have already considered the case of i = 1). In each iteration of the loop, we compute unique_digits as the product of the current value of unique_digits and available_digits. We then add unique_digits to the answer ans and decrement available_digits. This is because we can't use the digits that have already been used for the previous digits. Finally, we return the value of ans. The time complexity of this algorithm is O(n), where n is the input parameter representing the number of digits. The space complexity of this algorithm is O(1), as we are only using a constant amount of extra memory to store the variables ans, unique_digits, and available_digits, regardless of the input size. */ -------------------------------------------------------------------------//C++ code begins here---------------------------------------------------------------------------- class Solution { public: int countNumbersWithUniqueDigits(int n) { if (n == 0) { return 1; // return 1 for n = 0 } int ans = 10; // start with 10 unique digits, as we can have numbers 0-9 int unique_digits = 9; // start with 9 digits, as we cannot use 0 as first digit int available_digits = 9; // remaining available digits while (n-- > 1 && available_digits > 0) { unique_digits *= available_digits; // calculate number of unique numbers that can be formed ans += unique_digits; // add number of unique numbers to the answer available_digits--; // reduce available digits by 1 } return ans; // return final answer } }; ================================================ FILE: Math/unique_digits.java ================================================ /*Name : Abhinav kumar Github username : Abhinavcode13 Repository name : data-structures-and-algorithms Problem : Count Numbers with Unique Digits in Java Issue Number : #501 Problem statement : Explanation of the below Java code : We handle the base case of n = 0 by returning 1. We initialize the answer ans to 10 since there are 10 unique digits between 0 and 9. We also initialize the variables unique_digits and available_digits to 9 since we can't use 0 as the first digit. We then enter a loop that runs n-1 times (since we have already considered the case of i = 1). In each iteration of the loop, we compute unique_digits as the product of the current value of unique_digits and available_digits. We then add unique_digits to the answer ans and decrement available_digits. This is because we can't use the digits that have already been used for the previous digits. Finally, we return the value of ans. The time complexity of the given code is O(n), where n is the input parameter, because the loop runs n-1 times. The space complexity is O(1), because the amount of memory used by the algorithm does not depend on the size of the input n. The algorithm only uses a few constant amount of variables for computation, such as ans, unique_digits and available_digits. Therefore, the time complexity of the code is linear and the space complexity is constant. */ --------------------------------------------------------------------------------------------------------//Java code begins here--------------------------------------------------------------------------------------------------------------------------------- class Solution { public int countNumbersWithUniqueDigits(int n) { if (n == 0) { return 1; // return 1 for n = 0 } int ans = 10; // start with 10 unique digits, as we can have numbers 0-9 int unique_digits = 9; // start with 9 digits, as we cannot use 0 as first digit int available_digits = 9; // remaining available digits while (n-- > 1 && available_digits > 0) { unique_digits *= available_digits; // calculate number of unique numbers that can be formed ans += unique_digits; // add number of unique numbers to the answer available_digits--; // reduce available digits by 1 } return ans; // return final answer } } ================================================ FILE: Math/unique_digits.py ================================================ ''' Given an integer n, return the count of all numbers with unique digits, x, where 0 <= x < 10n. Example 1: Input: n = 2 Output: 91 Explanation: The answer should be the total numbers in the range of 0 ≤ x < 100, excluding 11,22,33,44,55,66,77,88,99 Example 2: Input: n = 0 Output: 1 Constraints: 0 <= n <= 8 ''' class Solution: def countNumbersWithUniqueDigits(self, n: int) -> int: '''For n = 0, ans = 1 For n = 1, ans = 10 If we take 2 digit number, we have 9 options for first digit (1 - 9) and 9 options for second digit(0 & all other digits except the one taken as first digit (to keep digits unique)) therefore ans += (9 * 9) Similarly if we take 3 digit number we have 8 options for third digit, therefore ans += (9 * 9 * 8)''' if(n==0): return 1 if(n==1): return 10 unique = 10 digits = 9 for i in range(2,n+1): digits*=(10-i+1) unique+=digits return unique ================================================ FILE: Math/unique_integers_that_sum_up_to_0.cpp ================================================ /* Given an integer n, return any array containing n unique integers such that they add up to 0. Example 1: Input: n = 5 Output: [-7,-1,1,3,4] Explanation: These arrays also are accepted [-5,-1,1,2,3] , [-3,-1,2,-2,4]. Example 2: Input: n = 3 Output: [-1,0,1] Example 3: Input: n = 1 Output: [0] Constraints: 1 <= n <= 1000 */ class Solution { public: vector sumZero(int n) { int A[n]; for(int i = 0; i < n; i++){ A[i] = 0; } int start = 0; int end = n - 1; int i = 1; // populate start and end with n and -n while(start < end) { A[start] = i; A[end] = -i; i++; start++; end--; } vector R; for(int a: A){ R.push_back(a); } return R; } }; ================================================ FILE: Misc/tictactoe.java ================================================ /* This Java code defines a simple command-line Tic-Tac-Toe game that allows two players (X and O) to take turns making moves on a 3x3 game board. Here's an explanation of the code: 1. Constants: - `X` and `O` are integer constants representing the two players, with `X` assigned the value `1` and `O` assigned the value `-1`. - `EMPTY` is an integer constant representing an empty cell on the game board, with a value of `0`. 2. Game Board: - The game board is represented by a 3x3 integer array called `board`. - The `player` variable indicates which player's turn it is. It is initially set to `X`. 3. Constructor: - The `TicTacToe` class has a constructor that initializes a new game by clearing the board and setting the `player` to `X`. 4. `clearBoard` Method: - The `clearBoard` method initializes the game board by setting all cells to `EMPTY` and resets the `player` to `X`. 5. `putMark` Method: - The `putMark` method allows a player to make a move at a specified position (i, j). - It checks for valid coordinates and an empty cell. If the move is valid, the player's mark is placed, and the turn is switched to the other player. - If the move is invalid, an `IllegalArgumentException` is thrown. 6. `isWin` Method: - The `isWin` method checks if a player has won the game. It returns `true` if any of the winning conditions (e.g., a row, column, or diagonal with all marks of the same player) are met. Otherwise, it returns `false`. 7. `winner` Method: - The `winner` method determines the winner of the game. It checks for both `X` and `O` as potential winners using the `isWin` method and returns the result (positive for `X`, negative for `O`, or `0` for a tie). 8. `toString` Method: - The `toString` method converts the current game state to a human-readable string, representing the game board. It uses "X" and "O" to denote player marks and empty spaces as " " (space). 9. `main` Method: - In the `main` method, a new Tic-Tac-Toe game is created. - A series of moves are made using `putMark`, and the game state is printed after each move. - The final outcome of the game is determined using the `winner` method, and the result is displayed as "X wins," "O wins," or "Tie." The code provides a basic implementation of a command-line Tic-Tac-Toe game with validation for player moves and win conditions. It demonstrates how to represent the game board, make moves, and check for a win or tie in the game. */ public class TicTacToe { public static final int X = 1, O = -1; public static final int EMPTY = 0; private int board[][] = new int[3][3]; private int player; public TicTacToe() { clearBoard(); } public void clearBoard() { for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) board[i][j] = EMPTY; player = X; } public void putMark(int i, int j) throws IllegalArgumentException { if ((i < 0) || (i > 2) || (j < 0) || (j > 2)) throw new IllegalArgumentException("Invalid board position"); if (board[i][j] != EMPTY) throw new IllegalArgumentException("Board Position occupied"); board[i][j] = player; player = -player; // switch players } public boolean isWin(int mark) { return ((board[0][0] + board[0][1] + board[0][2] == mark * 3) || (board[1][0] + board[1][1] + board[1][2] == mark * 3) || (board[2][0] + board[2][1] + board[2][2] == mark * 3) || (board[0][0] + board[1][0] + board[2][0] == mark * 3) || (board[0][1] + board[1][1] + board[2][1] == mark * 3) || (board[0][2] + board[1][2] + board[2][2] == mark * 3) || (board[0][0] + board[1][1] + board[2][2] == mark * 3) || (board[2][0] + board[1][1] + board[0][2] == mark * 3) ); } public int winner() { if(isWin(X)) return (X); else if (isWin(O)) return (O); else return (0); } public String toString() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { switch (board[i][j]) { case X: sb.append("X"); break; case O: sb.append("O"); break; case EMPTY: sb.append(" "); break; } if (j < 2) sb.append("|"); } if(i < 2) sb.append("\n-----\n"); } return sb.toString(); } public static void main(String[] args) { TicTacToe game = new TicTacToe(); game.putMark(0, 0); game.putMark(0, 2); game.putMark(2, 2); game.putMark(1, 2); game.putMark(2, 0); game.putMark(1, 1); game.putMark(1, 0); System.out.println(game); int winningPlayer = game.winner(); String[] outcome = {"O wins", "Tie", "X wins"}; System.out.println(outcome[1 + winningPlayer]); } } { } ================================================ FILE: Patterns/CompleteSquare.java ================================================ /* pattern to print: for input:5 * * * * * * * * * * * * * * * * * * * * * * * * * APPROACH: 1) Identify the numbers of rows for the outer for loop 2) Identify the relation of column with respect to row. 3) for each loop , print the respective element. */ public class CompleteSquare { public static void main(String[] args) { int size = 5; for(int row=1;row <= size;row++) { for (int col = 1;col<=size;col++) { System.out.print("* "); } System.out.println(); } } } ================================================ FILE: Patterns/HollowPattern.cpp ================================================ Program: To generate a hollow pattern using asterisks ('*'). The program will take an integer 'n' as input, which represents the size of the pattern, and it will produce a hollow pattern of the given size. Approach: 1. We will use nested loops to iterate through rows and columns and print '*' at specific positions to form the hollow pattern. 2. For each row, we will check if it's the first row, last row, first column, or last column. If so, we will print a '*' character. Otherwise, we will print a space ' '. 3. This way, we will create the desired hollow pattern. Time Complexity: O(n^2) - The program uses two nested loops to print the pattern, one for rows and one for columns. So, the time complexity is proportional to the square of 'n'. Space Complexity: O(1) - The program uses a constant amount of extra space, as it only prints characters and does not use any additional data structures that depend on the input size 'n'. Sample Input: 5 Sample Output: * * * * * * * * * * * * * * * * In this example, the input 'n' is 5, and the program generates a hollow pattern of size 5x5 using asterisks. */ #include using namespace std; void printHollowPattern(int n) { // Iterate through each row for (int i = 0; i < n; i++) { // Iterate through each column in the row for (int j = 0; j < n; j++) { // Check if it's the first row, last row, first column, or last column if (i == 0 || i == n - 1 || j == 0 || j == n - 1) { cout << "* "; } else { cout << " "; } } // Move to the next line after each row is printed cout << endl; } } int main() { int size; cout << "Enter the size of the pattern: "; cin >> size; printHollowPattern(size); return 0; } ================================================ FILE: Patterns/HollowPattern.go ================================================ /* Program: To generate a hollow pattern using asterisks ('*'). The program will take an integer 'n' as input, which represents the size of the pattern, and it will produce a hollow pattern of the given size. Approach: 1. We will use nested loops to iterate through rows and columns and print '*' at specific positions to form the hollow pattern. 2. For each row, we will check if it's the first row, last row, first column, or last column. If so, we will print a '*' character. Otherwise, we will print a space ' '. 3. This way, we will create the desired hollow pattern. Time Complexity: O(n^2) - The program uses two nested loops to print the pattern, one for rows and one for columns. So, the time complexity is proportional to the square of 'n'. Space Complexity: O(1) - The program uses a constant amount of extra space, as it only prints characters and does not use any additional data structures that depend on the input size 'n'. Sample Input: 5 Sample Output: * * * * * * * * * * * * * * * * In this example, the input 'n' is 5, and the program generates a hollow pattern of size 5x5 using asterisks. */ package main import "fmt" func printHollowPattern(n int) { // Iterate through each row for i := 0; i < n; i++ { // Iterate through each column in the row for j := 0; j < n; j++ { // Check if it's the first row, last row, first column, or last column if i == 0 || i == n-1 || j == 0 || j == n-1 { fmt.Print("* ") } else { fmt.Print(" ") } } // Move to the next line after each row is printed fmt.Println() } } func main() { var size int fmt.Print("Enter the size of the pattern: ") fmt.Scan(&size) printHollowPattern(size) } ================================================ FILE: Patterns/HollowPattern.java ================================================ /* Program: To generate a hollow pattern using asterisks ('*'). The program will take an integer 'n' as input, which represents the size of the pattern, and it will produce a hollow pattern of the given size. Approach: 1. We will use nested loops to iterate through rows and columns and print '*' at specific positions to form the hollow pattern. 2. For each row, we will check if it's the first row, last row, first column, or last column. If so, we will print a '*' character. Otherwise, we will print a space ' '. 3. This way, we will create the desired hollow pattern. Time Complexity: O(n^2) - The program uses two nested loops to print the pattern, one for rows and one for columns. So, the time complexity is proportional to the square of 'n'. Space Complexity: O(1) - The program uses a constant amount of extra space, as it only prints characters and does not use any additional data structures that depend on the input size 'n'. Sample Input: 6 Sample Output: * * * * * * * * * * * * * * * * * * * * */ import java.util.Scanner; public class HollowPattern { public static void printHollowPattern(int n) { // Iterate through each row for (int i = 0; i < n; i++) { // Iterate through each column in the row for (int j = 0; j < n; j++) { // Check if it's the first row, last row, first column, or last column if (i == 0 || i == n - 1 || j == 0 || j == n - 1) { System.out.print("* "); } else { System.out.print(" "); } } // Move to the next line after each row is printed System.out.println(); } } public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("Enter the size of the pattern: "); int size = scanner.nextInt(); printHollowPattern(size); } } ================================================ FILE: Patterns/HollowPattern.py ================================================ ''' Problem: Python program to generate a hollow pattern using asterisks ('*'). The program will take an integer 'n' as input, which represents the size of the pattern, and it will produce a hollow pattern of the given size. Approach: 1. We will iterate through rows and columns and print '*' at specific positions to form the hollow pattern. 2. For each row, we will check if it's the first row, last row, first column, or last column. If so, we will print a '*' character. Otherwise, we will print a space ' '. 3. This way, we will create the desired hollow pattern. Time Complexity: O(n^2) - The program uses two nested loops to print the pattern, one for rows and one for columns. So, the time complexity is proportional to the square of 'n'. Space Complexity: O(1) - The program uses a constant amount of extra space, as it only prints characters and does not use any additional data structures that depend on the input size 'n'. Sample Input: 8 Sample Output: ``` * * * * * * * * * * * * * * * * * * * * * * * * * * * * ``` In this example, the input 'n' is 8, and the program generates a hollow pattern of size 8x8 using asterisks. ''' def print_hollow_pattern(n): # Iterate through each row for i in range(n): # Iterate through each column in the row for j in range(n): # Check if it's the first row, last row, first column, or last column if i == 0 or i == n - 1 or j == 0 or j == n - 1: print('*', end=' ') else: print(' ', end=' ') # Move to the next line after each row is printed print() # Sample input: n = 5 # Expected output: # * * * * * # * * # * * # * * # * * * * * print_hollow_pattern(5) ================================================ FILE: Patterns/Ladder_Pattern.java ================================================ /* Problem: Given an integer N, the task is to print the ladder with N steps using ‘*’. The ladder will be with the gap of 3 spaces between two side rails. Approach: First, we take input from the user for the number of steps in the ladder. We then iterate from 1 to N, where N is the number of steps, to print each step of the ladder. Inside the loop, we call the printSideRail() function to print a side rail at the beginning of each step. We then call the printStep(stepNumber) function to print the actual step. This function prints stepNumber asterisks with 3 spaces between each asterisk. Finally, we again call the printSideRail() function to print a side rail at the end of each step and move to the next line to start a new step. Note: The number of spaces between the asterisks in each step can be adjusted by modifying the value inside the 'printSideRail()' and 'printStep(stepNumber)' functions. Time complexity: O(N) for given input N steps Auxiliary Space: O(1) as constant extra space is used */ import java.util.Scanner; public class LadderPattern { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.print("Enter the number of steps in the ladder: "); int N = sc.nextInt(); // Print the ladder with N steps for (int i = 1; i <= N; i++) { printSideRail(); printStep(i); printSideRail(); System.out.println(); } } // Function to print a side rail public static void printSideRail() { System.out.print("* "); } // Function to print a step public static void printStep(int stepNumber) { for (int i = 1; i <= stepNumber; i++) { System.out.print("* "); } } } /* Enter the number of steps in the ladder: 5 * * * * * * * * * * * * * * * * * * * * */ ================================================ FILE: Patterns/ReverseRightTrianglePattern.java ================================================ /* Reverse Right Triangular Pattern Approach: 1) Identify the numbers of rows for the outer for loop 2) Identify the relation of column with respect to row. In this case col = size-row+1 3) for each loop , print the respective element. TIME COMPLEXITY: O(N^2) SPACE COMPLEXITY: O(1) Eg: For size = 5, OUTPUT: * * * * * * * * * * * * * * * */ public class ReverseRightTrianglePattern { public static void main(String[] args) { int size = 5; pattern(size); } public static void pattern(int size) { for(int row=1;row<=size;row++) { for(int col=1;col<=size-row+1;col++) { System.out.print("* "); } System.out.println(); } } } ================================================ FILE: Patterns/RightPascalTriangle.java ================================================ /* Right Pascal Triangle pattern in java Approach: 1) Identify the numbers of rows for the outer for loop 2) Identify the relation of column with respect to row. In this case col = size-row+1 3) for each loop , print the respective element. TIME COMPLEXITY: O(N^2) SPACE COMPLEXITY: O(1) Eg: For size = 5, OUTPUT: * * * * * * * * * * * * * * * * */ public class RightPascalTriangle { public static void main(String[] args) { int size = 4; pattern(size); } public static void pattern(int size) { for(int row=0;row< 2*size-1;row++){ int totalcol = row < size?row:2*size-row-2; for(int col =0;col <=totalcol;col++){ System.out.print("* "); } System.out.println(); } } } ================================================ FILE: Patterns/StarDiamond.java ================================================ /* DIAMOND PATTERN * * * * * * * * * * * * * * * * APPROACH: 1) Identify the numbers of rows for the outer for loop 2) Identify the relation of column with respect to row. 3) for each loop , print the respective element. */ public class StarDiamond { public static void main(String[] args) { int size = 5; pattern(size); } public static void pattern(int size) { for (int row=1;row< 2*size;row++) { int totalcolsinrow = row <= size ? row:2*size-row; int spaces = size-totalcolsinrow; for(int i =1;i<=spaces;i++) { System.out.print(" "); } for(int col =1;col<=totalcolsinrow;col++) { System.out.print("* "); } System.out.println(); } } } ================================================ FILE: Patterns/diamond_pattern.cpp ================================================ /* Approach: 1. We use two nested loops to iterate through each row and each column of the diamond pattern. 2. The first loop is used to iterate through the rows of the diamond. It starts from 0 and goes up to n-1 rows for the upper half of the diamond. 3. Inside the first loop, we use a nested loop to print spaces before the pattern of each row. The number of spaces decreases by 1 as we move down the rows. 4. Again inside the first loop, we use another nested loop to print the pattern of asterisks (*) for each row. The number of asterisks increases by 2 as we move down the rows. 5. After printing each row, we move to the next line using `cout << endl;`. 6. Now, we need to print the lower half of the diamond. For this, we use another loop that starts from n-2 (as we have already printed the top-most row in the first loop) and moves down to 0 rows. 7. Inside this loop, we print the spaces and pattern the same way as in the first loop. 8. Finally, we have the main function where we take input from the user for the number of rows and call the `printDiamondPattern` function. Time Complexity: O(n^2) - as we use nested loops to iterate through each row and each column of the diamond pattern. Space Complexity: O(1) - as we only use a constant amount of additional space for variables. Sample Input: Enter the number of rows: 5 Sample Output: * *** ***** ******* ********* ******* ***** *** * */ #include using namespace std; void printDiamondPattern(int n) { // Print the upper half of the diamond for (int i = 0; i < n; i++) { // Print spaces before the pattern for each row for (int j = 0; j < n - i - 1; j++) { cout << " "; } // Print the pattern for each row for (int j = 0; j < 2 * i + 1; j++) { cout << "*"; } cout << endl; // Move to the next line } // Print the lower half of the diamond for (int i = n-2; i >= 0; i--) { // Print spaces before the pattern for each row for (int j = 0; j < n - i - 1; j++) { cout << " "; } // Print the pattern for each row for (int j = 0; j < 2 * i + 1; j++) { cout << "*"; } cout << endl; // Move to the next line } } int main() { int n; cout << "Enter the number of rows: "; cin >> n; printDiamondPattern(n); return 0; } ================================================ FILE: Patterns/diamond_pattern.go ================================================ /* In this code, the user enters a number, and the program will print a diamond pattern with that number as the widest point. Explanation of the approach: - We define two helper functions: `printSpaces` and `printStars`. These functions are used to print the desired spaces and stars, respectively. - The `printDiamondPattern` function takes an integer `n` as input. It prints the upper half and lower half of the diamond pattern using a combination of spaces and stars. - In the upper half, the number of spaces in each row decreases by 1, while the number of stars increases by 2. - In the lower half, the number of spaces in each row increases by 1, while the number of stars decreases by 2. - The `main` function prompts the user to enter a number and calls the `printDiamondPattern` function to print the diamond pattern. Time Complexity: The time complexity of this code is O(n^2), where n is the number entered by the user. This is because there are two nested loops used to print the diamond pattern. Space Complexity: The space complexity of this code is O(1), as there are no additional data structures used that depend on the input size. Sample Input: Enter a number for the widest point of the diamond: 5 Sample Output: * *** ***** ******* ********* ******* ***** *** * */ package main import ( "fmt" "math" ) func printSpaces(num int) { for i := 1; i <= num; i++ { fmt.Print(" ") } } func printStars(num int) { for i := 1; i <= num; i++ { fmt.Print("* ") } } func printDiamondPattern(n int) { // upper half of the diamond for i := 0; i < n; i++ { printSpaces(n - i - 1) printStars(2*i + 1) fmt.Println() } // lower half of the diamond for i := 0; i < n-1; i++ { printSpaces(i + 1) printStars(2*(n-i-1) - 1) fmt.Println() } } func main() { var num int fmt.Print("Enter a number for the widest point of the diamond: ") fmt.Scan(&num) printDiamondPattern(num) } ================================================ FILE: Patterns/diamond_pattern.java ================================================ /* Approach: 1. We take the number of rows for the diamond pattern as input. 2. We start by printing the upper half of the diamond pattern. In each row, we first print the required spaces and then print the required number of asterisks. 3. After the upper half, we print the lower half of the diamond pattern using a similar logic. Time Complexity: The time complexity of this code is O(n^2) as we use nested loops to print the pattern. Space Complexity: The space complexity of this code is O(1) as we are not using any additional data structures. Sample Input: 5 Sample Output: * *** ***** ******* ********* ******* ***** *** * */ public class DiamondPattern { public static void main(String[] args) { int n = 5; // Specify the number of rows for the diamond pattern printDiamondPattern(n); } public static void printDiamondPattern(int n) { if (n <= 0) { System.out.println("Number of rows should be positive."); return; } // Printing upper half of the diamond pattern for (int i = 1; i <= n; i++) { for (int j = 1; j <= n - i; j++) { System.out.print(" "); } for (int k = 1; k <= i * 2 - 1; k++) { System.out.print("*"); } System.out.println(); } // Printing lower half of the diamond pattern for (int i = n - 1; i >= 1; i--) { for (int j = 1; j <= n - i; j++) { System.out.print(" "); } for (int k = 1; k <= i * 2 - 1; k++) { System.out.print("*"); } System.out.println(); } } ================================================ FILE: Patterns/diamond_pattern.py ================================================ ''' Approach: - The code above prints a diamond pattern with `n` rows. The approach used is to iterate through each row of the diamond pattern, calculate the number of spaces and asterisks in each row based on the current row number, and then print the spaces and asterisks accordingly. - In the upper half of the diamond, the number of spaces decreases by 1 and the number of asterisks increases by 2 in each row. In the lower half of the diamond, the number of spaces increases by 1 and the number of asterisks decreases by 2 in each row. Sample input: `4`, the function `print_diamond_pattern(4)` Sample Output: * *** ***** ******* ***** *** * Time Complexity: The time complexity of this code is O(n^2), where n is the input parameter representing the number of rows in the diamond pattern. This is because we need to loop through each row and perform a constant number of operations for each row. Space Complexity: The space complexity of this code is O(1), as we are not using any additional data structures whose space requirements depend on the input size. NOTE: This code assumes that the input `n` is a positive integer greater than 0. It does not handle invalid inputs or edge cases where `n` is negative or zero. ''' def print_diamond_pattern(n): # Calculate the number of rows in the diamond pattern num_rows = 2 * n - 1 # Iterate through each row of the diamond pattern for i in range(num_rows): # Calculate the number of spaces and asterisks in each row if i < n: # Upper half of the diamond num_spaces = n - i - 1 num_asterisks = 2 * i + 1 else: # Lower half of the diamond num_spaces = i - n + 1 num_asterisks = 2 * (num_rows - i) - 1 # Print the spaces before the asterisks for _ in range(num_spaces): print(" ", end="") # Print the asterisks for _ in range(num_asterisks): print("*", end="") # Move to the next line print() # Testing the function with a sample input print_diamond_pattern(4) ================================================ FILE: Patterns/numerical_pattern.java ================================================ /* Problem: This program prints a pattern of numbers in an organized way. The numbers start from 1 and increase in a triangular pattern. Each row in the pattern contains the numbers from 1 to the row number. The row numbers increase from 1 to the given input number. Approach: - Use nested loops to iterate through each row and each number within the row. - The outer loop controls the row number and runs from 1 to the input number. - The inner loop prints the numbers for each row and runs from 1 to the row number. - Print each number followed by a space. - After each row is printed, insert a new line. Time Complexity: O(n^2), where n is the given input number. This is because we have nested loops that iterate n times. Space Complexity: O(1) as no extra space is required other than the given input. Sample Input 1: 5 Sample Output 1: 1 1 2 1 2 3 1 2 3 4 1 2 3 4 5 Sample Input 2: 3 Sample Output 2: 1 1 2 1 2 3 */ public class NumericalPattern { public static void printPattern(int n) { for (int i = 1; i <= n; i++) { // iterate through each row for (int j = 1; j <= i; j++) { // iterate through each number within the row System.out.print(j + " "); // print the number followed by a space } System.out.println(); // insert a new line after each row } } public static void main(String[] args) { int n = 5; // sample input printPattern(n); } } ================================================ FILE: Patterns/triangular_pattern.java ================================================ /* Approach: - Initialize the `rows` variable to determine the number of rows in the pattern. - Initialize the `count` variable to keep track of the current number. - Use nested `for` loops to iterate through the rows and columns. - Print the current `count` value and increment it after printing. - Move to the next line after each row by using `System.out.println()`. Time Complexity: The nested loops iterate through the rows and columns of the pattern, resulting in a time complexity of O(rows^2). Space Complexity: The space complexity is O(1) as we only require a few variables to store the number of rows, current count, and loop indices. Sample Input (rows = 5): 5 Sample Output: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ //Java code for generating a number pattern in a triangular pattern, starting from 1: public class triangular_pattern { public static void main(String[] args) { int rows = 5; // number of rows in the pattern int count = 1; // variable to keep track of the current number // loop through the rows for (int i = 1; i <= rows; i++) { // loop through the columns for (int j = 1; j <= i; j++) { System.out.print(count + " "); count++; } System.out.println(); // move to the next line after each row } } } ================================================ FILE: Patterns/triangular_pattern.py ================================================ ''' Explanation: 1. The code defines a function `print_triangular_pattern()` that takes a parameter `n`. This parameter determines the number of rows in the triangular pattern. 2. Inside the function, `num` is initialized to 1, which is the starting number of the pattern. 3. The code then uses nested loops. The outer loop runs from 1 to `n`, which represents the number of rows. 4. The inner loop runs from 1 to the current row number (i). It prints the numbers in each row in increasing order. 5. After printing all the numbers in a row, the code moves to the next line using `print()` to create a new row. 6. Ultimately, the pattern is printed as a set of rows, forming a triangular shape. Time Complexity: The time complexity of this code is O(n^2), where n is the number of rows. This is because we have two nested loops, one iterating n times and the other iterating up to the current row number. Space Complexity: The space complexity of this code is O(1), as we are not using any additional data structures that depend on the input size. Sample Input: n = 5 Sample Output: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ''' #code in Python that prints a number pattern in a triangular pattern starting from 1: def print_triangular_pattern(n): num = 1 # Initialize the starting number for i in range(1, n + 1): # Iterate through each row for j in range(1, i + 1): # Print numbers in each row print(num, end=" ") num += 1 # Increment the number for the next iteration print() # Move to the next line after each row # Example usage n = 5 print_triangular_pattern(n) ================================================ FILE: Priority Queues/In_Place_HeapSort.cpp ================================================ /* Given an integer array of size N. Sort this array (in decreasing order) using heap sort. Note: Space complexity should be O(1). Input Format: The first line of input contains an integer, that denotes the value of the size of the array or N. The following line contains N space separated integers, that denote the value of the elements of the array. Output Format : The first and only line of output contains array elements after sorting. The elements of the array in the output are separated by single space. Constraints : 1 <= n <= 10^6 Time Limit: 1 sec Sample Input 1: 6 2 6 8 5 4 3 Sample Output 1: 8 6 5 4 3 2 Explaination : The code implements the heap sort algorithm, which is a sorting algorithm that works by first building a max heap. A max heap is a binary tree where the value of each parent node is greater than or equal to the value of each of its child nodes. The heap sort algorithm works by repeatedly swapping the root element of the heap with the last element of the array and then rebuilding the heap without the root element. This process is repeated until the array is sorted. The code first defines a function called heapSort(). This function takes two parameters: an array of integers and the size of the array. The function builds a max heap from the bottom up and then sorts the array by repeatedly swapping the root element with the last element and rebuilding the heap. The main function of the code prompts the user to enter the size of the array and then prompts the user to enter the elements of the array. The main function then calls the heapSort() function and prints the sorted array. The intuition behind the heap sort algorithm is that it takes advantage of the fact that a max heap is already partially sorted. By repeatedly swapping the root element with the last element of the array and rebuilding the heap, the algorithm is able to sort the array in a relatively efficient way. The heap sort algorithm is a divide-and-conquer algorithm, which means that it breaks the problem of sorting an array into smaller and smaller subproblems until they are trivial to solve. This makes the algorithm very efficient for sorting large arrays. */ #include using namespace std; void heapSort(int arr[], int n) { // This function sorts an array using the heap sort algorithm. // The algorithm works by first building a max heap. // Then, the root of the heap is swapped with the last element of the array. // The heap is then rebuilt without the root element. // This process is repeated until the array is sorted. for (int i = 1; i < n; i++) { // Recursively build a max heap from the bottom up. int childIndex = i; while (childIndex > 0) { int parentIndex = (childIndex - 1) / 2; if (arr[childIndex] < arr[parentIndex]) { // Swap the child and parent. int temp = arr[childIndex]; arr[childIndex] = arr[parentIndex]; arr[parentIndex] = temp; } else { // The heap is already a max heap. break; } childIndex = parentIndex; } } // Sort the array by repeatedly swapping the root element with the last element and rebuilding the heap. int j = n; while (j > 1) { // Swap the root element with the last element. int temp = arr[0]; arr[0] = arr[j - 1]; arr[j - 1] = temp; j--; // Rebuild the heap without the root element. int parentIndex = 0; int rightChildIndex = 2 * parentIndex + 2; int leftChildIndex = 2 * parentIndex + 1; while (leftChildIndex < j) { // Find the minimum child. int minIndex = parentIndex; if (arr[leftChildIndex] < arr[minIndex]) { minIndex = leftChildIndex; } if (rightChildIndex < j && arr[rightChildIndex] < arr[minIndex]) { minIndex = rightChildIndex; } // Swap the minimum child with the parent. int temp = arr[minIndex]; arr[minIndex] = arr[parentIndex]; arr[parentIndex] = temp; if (parentIndex == minIndex) { // The heap is already a max heap. break; } parentIndex = minIndex; rightChildIndex = 2 * parentIndex + 2; leftChildIndex = 2 * parentIndex + 1; } } } int main() { int size; cin >> size; int *input = new int[size]; for (int i = 0; i < size; i++) { cin >> input[i]; } heapSort(input, size); for (int i = 0; i < size; i++) { cout << input[i] << " "; } delete[] input; } ================================================ FILE: Priority Queues/buy_the_ticket.cpp ================================================ /* You want to buy a ticket for a well-known concert which is happening in your city. But the number of tickets available is limited. Hence the sponsors of the concert decided to sell tickets to customers based on some priority. A queue is maintained for buying the tickets and every person is attached with a priority (an integer, 1 being the lowest priority). The tickets are sold in the following manner - 1. The first person (pi) in the queue requests for the ticket. 2. If there is another person present in the queue who has higher priority than pi, then ask pi to move at end of the queue without giving him the ticket. 3. Otherwise, give him the ticket (and don't make him stand in queue again). Giving a ticket to a person takes exactly 1 minute and it takes no time for removing and adding a person to the queue. And you can assume that no new person joins the queue. Given a list of priorities of N persons standing in the queue and the index of your priority (indexing starts from 0). Find and return the time it will take until you get the ticket. Input Format: The first line of input contains an integer, that denotes the value of total number of people standing in queue or the size of the array of priorities. Let us denote it with the symbol N. The following line contains N space separated integers, that denote the value of the elements of the array of priorities. The following contains an integer, that denotes the value of index of your priority. Let us denote it with symbol k. Output Format : The first and only line of output contains the time required for you to get the ticket. Constraints: Time Limit: 1 sec Sample Input 1 : 3 3 9 4 2 Sample Output 1 : 2 Sample Output 1 Explanation : Person with priority 3 comes out. But there is a person with higher priority than him. So he goes and then stands in the queue at the end. Queue's status : {9, 4, 3}. Time : 0 secs. Next, the person with priority 9 comes out. And there is no person with higher priority than him. So he'll get the ticket. Queue's status : {4, 3}. Time : 1 secs. Next, the person with priority 4 comes out (which is you). And there is no person with higher priority than you. So you'll get the ticket. Time : 2 secs. Sample Input 2 : 5 2 3 2 2 4 3 Sample Output 2 : 4 Explaination : The code first defines two data structures: a priority queue and a queue. The priority queue is used to store the priorities of all the people in the queue, and the queue is used to store the indices of the people in the queue. The function buyTicket() takes three parameters: an array of priorities, the number of people in the queue, and the index of the person who is buying the ticket. The function first pushes all the priorities into the priority queue. Then, it pushes all the indices into the queue. The function then enters a loop. In each iteration of the loop, the function checks if the priority of the person at the front of the queue is equal to the priority of the person who is buying the ticket. If it is, the function increments the time by 1, pops the person at the front of the queue, and pops the person at the front of the queue. If it is not, the function pops the person at the front of the queue and pushes them back into the queue. The function continues looping until the person who is buying the ticket is at the front of the queue. The function then returns the time it took for the person to buy the ticket. The main function of the code first prompts the user to enter the number of people in the queue. Then, it creates an array of priorities and prompts the user to enter the priorities of all the people in the queue. Finally, it prompts the user to enter the index of the person who is buying the ticket, and it prints the time it took for the person to buy the ticket. */ #include #include #include using namespace std; int buyTicket(int *arr, int n, int k) { // Initialize a priority queue to store the priorities of all the people in the queue. priority_queue pq; // Initialize a queue to store the indices of all the people in the queue. queue indices; // Push all the priorities into the priority queue. for (int i = 0; i < n; i++) { pq.push(arr[i]); } // Push all the indices into the queue. for (int i = 0; i < n; i++) { indices.push(i); } // Initialize the time it took for the person to buy the ticket. int time = 0; // Loop until the person who is buying the ticket is at the front of the queue. while (!indices.empty()) { // Check if the priority of the person at the front of the queue is equal to the priority of the person who is buying the ticket. if (arr[indices.front()] == pq.top() && indices.front() == k) { time++; pq.pop(); indices.pop(); break; } // If the priorities match, increment the time by 1 and break out of the loop. if (arr[indices.front()] == pq.top()) { time++; pq.pop(); indices.pop(); } // If the priorities do not match, pop the person at the front of the queue and push them back into the queue. else { int temp = indices.front(); indices.pop(); indices.push(temp); } } // Return the time it took for the person to buy the ticket. return time; } int main() { // Prompt the user to enter the number of people in the queue. int n; cin >> n; // Create an array of priorities and prompt the user to enter the priorities of all the people in the queue. int *arr = new int[n]; for (int i = 0; i < n; i++) { cin >> arr[i]; } // Prompt the user to enter the index of the person who is buying the ticket. int k; cin >> k; // Print the time it took for the person to buy the ticket. cout << buyTicket(arr, n, k); // Delete the array of priorities. delete[] arr; } ================================================ FILE: Priority Queues/check_max_heap.cpp ================================================ /* Given an array of integers, check whether it represents max-heap or not. Return true if the given array represents max-heap, else return false. Input Format: The first line of input contains an integer, that denotes the value of the size of the array. Let us denote it with the symbol N. The following line contains N space separated integers, that denote the value of the elements of the array. Output Format : The first and only line of output contains true if it represents max-heap and false if it is not a max-heap. Constraints: 1 <= N <= 10^5 1 <= Ai <= 10^5 Time Limit: 1 sec Sample Input 1: 8 42 20 18 6 14 11 9 4 Sample Output 1: true Explaination : The code first defines a function called isMaxHeap(). This function takes two parameters: an array of integers and the size of the array. The function recursively checks if the array is a max heap. A max heap is a binary tree where the value of each parent node is greater than or equal to the value of each of its child nodes. The function starts by checking the last element of the array. If the parent of the last element is smaller than the last element, the function returns false. Otherwise, the function recursively checks the parent of the last element. This process continues until the function reaches the root of the tree. If the function reaches the root of the tree and all of the parent nodes are greater than or equal to their child nodes, the function returns true. Otherwise, the function returns false. The main function of the code prompts the user to enter the size of the array and then prompts the user to enter the elements of the array. The main function then calls the isMaxHeap() function and prints the result. */ #include using namespace std; bool isMaxHeap(int arr[], int n) { // This function recursively checks if the array is a max heap. // The function starts at the last element of the array and checks if the parent is smaller than the child. // If the parent is smaller than the child, the function returns false. // Otherwise, the function recursively checks the parent of the child. int childIndex = n - 1; while (childIndex >= 0) { int parentIndex = (childIndex - 1) / 2; // Check if the parent is smaller than the child. if (arr[parentIndex] < arr[childIndex]) { return false; } // Recursively check the parent of the child. childIndex--; } // The array is a max heap. return true; } int main() { // Prompt the user to enter the size of the array. int n; cin >> n; // Create an array and prompt the user to enter the elements of the array. int *arr = new int[n]; for (int i = 0; i < n; i++) { cin >> arr[i]; } // Check if the array is a max heap. cout << (isMaxHeap(arr, n) ? "true\n" : "false\n"); // Delete the array. delete[] arr; } ================================================ FILE: Priority Queues/kth_largest_element.cpp ================================================ /* Given an array A of random integers and an integer k, find and return the kth largest element in the array. Note: Try to do this question in less than O(N * logN) time. Input Format : The first line of input contains an integer, that denotes the value of the size of the array. Let us denote it with the symbol N. The following line contains N space separated integers, that denote the value of the elements of the array. The following contains an integer, that denotes the value of k. Output Format : The first and only line of output contains the kth largest element Constraints : 1 <= N, Ai, k <= 10^5 Time Limit: 1 sec Sample Input 1 : 6 9 4 8 7 11 3 2 Sample Output 1 : 9 Sample Input 2 : 8 2 6 10 11 13 4 1 20 4 Sample Output 2 : 10 Explaination : The code uses a min-heap priority queue (pq) to find the kth largest element in the given array. It initializes the priority queue with the first k elements of the array. Then, it iterates over the remaining elements of the array and compares each element with the smallest element (top) of the priority queue. If the current element is larger, it replaces the smallest element in the priority queue. Finally, it returns the top element of the priority queue, which will be the kth largest element. Note: The code dynamically allocates memory for the array using new and deallocates it using delete[] to ensure proper memory management. */ #include #include #include using namespace std; // Function to find the kth largest element in an array int kthLargest(int *arr, int n, int k) { // Create a min-heap priority queue priority_queue, greater> pq; // Insert the first k elements into the priority queue for (int i = 0; i < k; i++) { pq.push(arr[i]); } // Iterate over the remaining elements in the array for (int i = k; i < n; i++) { // If the current element is greater than the smallest element in the priority queue (top), // replace the smallest element with the current element if (arr[i] > pq.top()) { pq.push(arr[i]); pq.pop(); } } // The top element of the priority queue will be the kth largest element return pq.top(); } int main() { int n; cin >> n; // Dynamically allocate memory for the array int *arr = new int[n]; // Read the elements of the array from the user for (int i = 0; i < n; i++) { cin >> arr[i]; } int k; cin >> k; // Call the kthLargest function to find the kth largest element cout << kthLargest(arr, n, k); // Deallocate the memory for the array delete[] arr; } ================================================ FILE: Priority Queues/kth_smallest_element.cpp ================================================ /* You are given with an integer k and an array of integers that contain numbers in random order. Write a program to find k smallest numbers from given array. You need to save them in an array and return it. Time complexity should be O(n * logk) and space complexity should not be more than O(k). Note: Order of elements in the output is not important. Input Format : The first line of input contains an integer, that denotes the value of the size of the array. Let us denote it with the symbol N. The following line contains N space separated integers, that denote the value of the elements of the array. The following line contains an integer, that denotes the value of k. Output Format : The first and only line of output print k smallest elements. The elements in the output are separated by a single space. Constraints: Time Limit: 1 sec Sample Input 1 : 13 2 12 9 16 10 5 3 20 25 11 1 8 6 4 Sample Output 1 : 1 2 3 5 Explaination : The code first defines a function called kSmallest(). This function takes three parameters: an array of integers, the size of the array, and the number of smallest elements to find. The function uses a priority queue to find the k smallest elements in the array. The code then defines a main function. The main function prompts the user to enter the size of the array and the number of smallest elements to find. The main function then creates an array of integers and prompts the user to enter the elements of the array. The main function then calls the kSmallest() function and prints the k smallest elements in the array. The kSmallest() function works as follows: The function first creates a priority queue and pushes the first k elements of the array onto the priority queue. The function then iterates through the remaining elements of the array. If the current element is less than the top element of the priority queue, the function pushes the current element onto the priority queue and pops the top element of the priority queue. The function then returns a vector of the elements in the priority queue. */ #include #include #include #include using namespace std; // This function finds the k smallest elements in an array. vector kSmallest(int arr[], int n, int k) { // Create a priority queue and push the first k elements of the array onto the priority queue. priority_queue pq; for (int i = 0; i < k; i++) { pq.push(arr[i]); } // Iterate through the remaining elements of the array. // If the current element is less than the top element of the priority queue, // push the current element onto the priority queue and pop the top element of the priority queue. for (int i = k; i < n; i++) { if (arr[i] < pq.top()) { pq.push(arr[i]); pq.pop(); } } // Return a vector of the elements in the priority queue. vector vec; while (!pq.empty()) { vec.push_back(pq.top()); pq.pop(); } return vec; } // This is the main function. int main() { // Prompt the user to enter the size of the array and the number of smallest elements to find. int size; cin >> size; // Create an array of integers. int *input = new int[size]; // Prompt the user to enter the elements of the array. for (int i = 0; i < size; i++) { cin >> input[i]; } // Prompt the user to enter the number of smallest elements to find. int k; cin >> k; // Call the `kSmallest()` function and print the k smallest elements in the array. vector output = kSmallest(input, size, k); // Sort the output vector in ascending order. sort(output.begin(), output.end()); // Print the output vector. for (int i = 0; i < output.size(); i++) { cout << output[i] << " "; } // Delete the array. delete[] input; return 0; } ================================================ FILE: Priority Queues/merge_k_sorted_arrays.cpp ================================================ /* Given k different arrays, which are sorted individually (in ascending order). You need to merge all the given arrays such that output array should be sorted (in ascending order). Hint : Use Heaps. Input Format: The first line of input contains an integer, that denotes value of k. The following lines of input represent k sorted arrays. Each sorted array uses two lines of input. The first line contains an integer, that denotes the size of the array. The following line contains elements of the array. Output Format: The first and only line of output contains space separated elements of the merged and sorted array, as described in the task. Constraints: 0 <= k <= 1000 0 <= n1, n2 <= 10000 Time Limit: 1 second Sample Input 1: 4 3 1 5 9 2 45 90 5 2 6 78 100 234 1 0 Sample Output 1: 0 1 2 5 6 9 45 78 90 100 234 Explaination : The intuition behind the code is to use a min-heap (priority queue) to merge K sorted arrays efficiently. By taking the first element from each array and inserting it into the priority queue, the smallest element is always at the top. This ensures that when elements are extracted from the priority queue, they are in sorted order. The code iteratively extracts the smallest element from the priority queue, adds it to the merged sorted array, and checks if there are remaining elements in the array from which the extracted element came. If so, the next element from that array is inserted into the priority queue. This process continues until all elements from all arrays are processed and added to the merged sorted array. By utilizing the min-heap property of the priority queue, the code efficiently merges the sorted arrays in a way that the overall time complexity is optimized. The priority queue allows for easy retrieval of the smallest element, resulting in an overall time complexity of O(N log K), where N is the total number of elements across all arrays and K is the number of arrays. */ #include #include #include using namespace std; vector mergeKSortedArrays(vector *> input) { // Create a min-heap priority queue to store elements from different arrays priority_queue>, vector>>, greater>>> pq; // Insert the first element from each array into the priority queue for (int i = 0; i < input.size(); i++) { pair> p; p.first = input[i]->at(0); // value of the element p.second.first = i; // array number p.second.second = 0; // index of the element pq.push(p); } // Initialize a vector to store the merged sorted array vector ans; // Process the priority queue until it becomes empty while (!pq.empty()) { pair> p = pq.top(); pq.pop(); // Add the minimum element to the result vector ans.push_back(p.first); int arrNo = p.second.first; // array number int index = p.second.second + 1; // index of the next element in the array // If there are still elements remaining in the array, add the next element to the priority queue if (index < input[arrNo]->size()) { p.first = input[arrNo]->at(index); // value of the element p.second.second = index; // update the index pq.push(p); } } // Return the merged sorted array return ans; } int main() { int k; cin >> k; // Number of sorted arrays vector *> input; // Vector to store the arrays // Read the size and elements of each array for (int j = 1; j <= k; j++) { int size; cin >> size; // Size of the array // Create a dynamically allocated vector for the current array vector *current = new vector; // Read the elements of the array for (int i = 0; i < size; i++) { int a; cin >> a; // Element of the array current->push_back(a); // Add the element to the current array } // Add the current array to the vector of arrays input.push_back(current); } // Merge the sorted arrays using the mergeKSortedArrays function vector output = mergeKSortedArrays(input); // Print the merged sorted array for (int i = 0; i < output.size(); i++) { cout << output[i] << " "; } return 0; } ================================================ FILE: Priority Queues/running_median.cpp ================================================ /* You are given a stream of 'N' integers. For every 'i-th' integer added to the running list of integers, print the resulting median. Print only the integer part of the median. Input Format : The first line of input contains an integer 'N', denoting the number of integers in the stream. The second line of input contains 'N' integers separated by a single space. Output Format : Print the running median for every integer added to the running list in one line (space-separated). Input Constraints 0 <= N <= 10 ^ 5 1 <= ARR[i] <= 10 ^ 5 Time Limit: 1 sec Sample Input 1 : 6 6 2 1 3 7 5 Sample Output 1 : 6 4 2 2 3 4 Explanation of Sample Output 1 : S = {6}, median = 6 S = {6, 2} -> {2, 6}, median = 4 S = {6, 2, 1} -> {1, 2, 6}, median = 2 S = {6, 2, 1, 3} -> {1, 2, 3, 6}, median = 2 S = {6, 2, 1, 3, 7} -> {1, 2, 3, 6, 7}, median = 3 S = {6, 2, 1, 3, 7, 5} -> {1, 2, 3, 5, 6, 7}, median = 4 Sample Input 2 : 5 5 4 3 2 1 Sample Output 2 : 5 4 4 3 3 Explaination : The code aims to efficiently find and print the median of a stream of integers as they are read one by one. It achieves this by using two priority queues: `min` and `max`. These priority queues divide the elements encountered so far into the lower and higher halves, allowing for the calculation of the median. When reading the integers one by one, the code follows these steps: 1. For the first element, it is pushed into the `max` priority queue. At this point, the median is simply the first element. 2. For subsequent elements, the code compares each element with the current median (which is the top element of the `max` priority queue). If the new element is smaller, it is pushed into the `max` priority queue. This ensures that the lower half of the elements is stored in `max`, with the maximum element of the lower half at the top. 3. If the new element is larger than the current median, it is pushed into the `min` priority queue. This ensures that the higher half of the elements is stored in `min`, with the minimum element of the higher half at the top. 4. After each insertion, the code checks the size difference between the `min` and `max` priority queues. If the difference exceeds 1, it rebalances the queues by transferring the top element from the larger queue to the smaller one. This ensures that the size difference between the two halves remains at most 1, allowing for accurate median calculations. 5. To calculate and print the median, the code checks the total number of elements (c) in both priority queues. If c is even, the median is the average of the top elements of `min` and `max`. If c is odd, the median is the top element of the priority queue with more elements. By using two priority queues to maintain the lower and higher halves of the elements encountered so far, the code efficiently updates and calculates the median of the stream of integers in a scalable manner. The rebalancing step ensures that the size difference between the two halves remains at most 1, allowing for accurate median calculations even as new elements are added to the stream. */ #include #include using namespace std; void findMedian(int *arr, int n) { priority_queue, greater> min; // Min-heap to store the higher half of the elements encountered priority_queue max; // Max-heap to store the lower half of the elements encountered for (int i = 0; i < n; i++) { if (i == 0) { max.push(arr[i]); // For the first element, push it into the max heap } else { if (max.top() > arr[i]) { max.push(arr[i]); // If the new element is smaller than the current median, push it into the max heap } else { min.push(arr[i]); // If the new element is larger than the current median, push it into the min heap } } if (int(min.size() - max.size()) > 1) { int temp = min.top(); min.pop(); max.push(temp); // Rebalance the heaps by transferring the top element from min heap to max heap } else if (int(max.size() - min.size()) > 1) { int temp = max.top(); max.pop(); min.push(temp); // Rebalance the heaps by transferring the top element from max heap to min heap } int c = max.size() + min.size(); // Total count of elements if (c % 2 == 0) { cout << (min.top() + max.top()) / 2 << " "; // If total count is even, median is the average of the top elements of both heaps } else { if (min.size() > max.size()) { cout << min.top() << " "; // If total count is odd and min heap has more elements, median is the top element of min heap } else { cout << max.top() << " "; // If total count is odd and max heap has more elements, median is the top element of max heap } } } cout << endl; } int main() { int n; cin >> n; // Number of elements int *arr = new int[n]; // Dynamically allocate memory for the array for (int i = 0; i < n; ++i) { cin >> arr[i]; // Read the elements of the array } findMedian(arr, n); // Find and print the median of the stream delete[] arr; // Deallocate the memory return 0; } ================================================ FILE: Queue/queue.cpp ================================================ // Implement Queue data structure in C++ /* In this implementation, the Queue class has private member variables front, rear, and arr to keep track of the front and rear indices of the queue and the elements in the queue, respectively. The class also has public member functions isFull(), isEmpty(), enqueue(), dequeue(), peek(), and print() for checking if the queue is full or empty, adding elements to the rear of the queue, removing elements from the front of the queue, peeking at the element at the front of the queue, and printing the elements in the queue, respectively. The main() function demonstrates how to use the Queue class by creating a new queue object q, adding three elements to the queue, removing one element from the front of the queue, peeking at the element at the front of the queue, and printing the elements in the queue */ #include using namespace std; const int MAX_SIZE = 100; // maximum size of the queue class Queue { private: int front, rear; int arr[MAX_SIZE]; public: Queue() { front = -1; // initialize front and rear to -1 to indicate the queue is empty rear = -1; } bool isFull() { return rear == MAX_SIZE - 1; // check if the rear index is at the maximum size } bool isEmpty() { return front == -1 && rear == -1; // check if both front and rear indices are at -1, indicating an empty queue } void enqueue(int x) { if (isFull()) { cout << "Error: Queue is full" << endl; return; } if (isEmpty()) { front = rear = 0; // if the queue is empty, set both front and rear to 0 to add the first element } else { rear++; // increment rear to add the new element } arr[rear] = x; // add the new element to the rear of the queue } void dequeue() { if (isEmpty()) { cout << "Error: Queue is empty" << endl; return; } if (front == rear) { front = rear = -1; // if the queue has only one element, set both front and rear indices to -1 to indicate an empty queue } else { front++; // increment front to remove the element } } int peek() { if (isEmpty()) { cout << "Error: Queue is empty" << endl; return -1; } return arr[front]; // return the element at the front of the queue } void print() { if (isEmpty()) { cout << "Queue is empty" << endl; return; } cout << "Queue: "; for (int i = front; i <= rear; i++) { cout << arr[i] << " "; // print each element in the queue from front to rear } cout << endl; } }; int main() { Queue q; q.enqueue(1); q.enqueue(2); q.enqueue(3); q.print(); // output: Queue: 1 2 3 q.dequeue(); q.print(); // output: Queue: 2 3 cout << "Front element: " << q.peek() << endl; // output: Front element: 2 return 0; } ================================================ FILE: Queue/queue.go ================================================ // Queue Data Structure /* In this implementation, we define a struct for the queue that has a slice to store the values and pointers to the front and rear of the queue. We also define methods for checking if the queue is empty, adding an element to the rear of the queue, and removing an element from the front of the queue. The NewQueue() function creates a new queue and initializes its front and rear pointers to nil. The IsEmpty() method checks if the queue is empty by checking if the front pointer is nil. The Enqueue() method adds an element to the rear of the queue by appending it to the slice and updating the rear pointer. The Dequeue() method removes an element from the front of the queue by removing it from the slice and updating the front pointer. In the main() function, we create a new queue, add some elements to it, and then dequeue them one by one and print their values. */ package main import "fmt" // Define a struct for queue that has a slice to store values and pointers to the front and rear of the queue type Queue struct { values []int front *int rear *int } // Function to create a new queue and initialize its front and rear pointers func NewQueue() *Queue { q := Queue{values: make([]int, 0), front: nil, rear: nil} return &q } // Function to check if the queue is empty func (q *Queue) IsEmpty() bool { return q.front == nil } // Function to add an element to the rear of the queue func (q *Queue) Enqueue(val int) { // If the queue is empty, set both front and rear pointers to the new element if q.front == nil { q.front = &val q.rear = &val } else { // Otherwise, add the new element to the rear of the queue and update the rear pointer q.values = append(q.values, val) q.rear = &q.values[len(q.values)-1] } } // Function to remove an element from the front of the queue and return its value func (q *Queue) Dequeue() (int, error) { // If the queue is empty, return an error if q.front == nil { return 0, fmt.Errorf("queue is empty") } // Get the value of the element at the front of the queue val := *q.front // If there is only one element in the queue, set both front and rear pointers to nil if len(q.values) == 1 { q.front = nil q.rear = nil } else { // Otherwise, remove the front element from the slice and update the front pointer q.values = q.values[1:] q.front = &q.values[0] } // Return the value of the dequeued element return val, nil } func main() { // Create a new queue q := NewQueue() // Add some elements to the queue q.Enqueue(10) q.Enqueue(20) q.Enqueue(30) // Dequeue elements from the queue and print their values for !q.IsEmpty() { val, err := q.Dequeue() if err != nil { fmt.Println(err) } else { fmt.Println(val) } } } ================================================ FILE: Queue/queue.java ================================================ // 1. Queue Using Array: // 2. Queue Using LinkedList: /* In this implementation, we use an array to store the elements of the queue, and we keep track of the front and rear indices, as well as the current size of the queue. The enqueue operation adds an item to the rear of the queue, while the dequeue operation removes an item from the front of the queue. The isEmpty and isFull methods check whether the queue is empty or full, respectively, while the size method returns the current size of the queue. */ // 1. Queue Using Array: public class Queue { private int maxSize; private int[] queueArray; private int front; private int rear; private int currentSize; public Queue(int size) { this.maxSize = size; this.queueArray = new int[size]; this.front = 0; this.rear = -1; this.currentSize = 0; } public void enqueue(int item) { if (isFull()) { System.out.println("Queue is full"); return; } rear++; if (rear == maxSize) { rear = 0; } queueArray[rear] = item; currentSize++; } public int dequeue() { if (isEmpty()) { System.out.println("Queue is empty"); return -1; } int temp = queueArray[front]; front++; if (front == maxSize) { front = 0; } currentSize--; return temp; } public boolean isEmpty() { return currentSize == 0; } public boolean isFull() { return currentSize == maxSize; } public int size() { return currentSize; } } // Here's an example implementation of a Queue data structure in Java using a linked list /* In this implementation, we use a linked list to store the elements of the queue, and we keep track of the front and rear nodes, as well as the current size of the queue. The enqueue operation adds a new node to the rear of the linked list, while the dequeue operation removes the head node from the linked list. The isEmpty method checks whether the queue is empty, while the size method returns the current size of the queue. Note that this implementation uses generics to allow the queue to store elements of any type. */ // 2. Queue Using LinkedList: public class Queue { private Node front; private Node rear; private int size; public Queue() { front = null; rear = null; size = 0; } private static class Node { T data; Node next; public Node(T data) { this.data = data; this.next = null; } } public void enqueue(T item) { Node newNode = new Node<>(item); if (isEmpty()) { front = newNode; } else { rear.next = newNode; } rear = newNode; size++; } public T dequeue() { if (isEmpty()) { System.out.println("Queue is empty"); return null; } T item = front.data; front = front.next; if (front == null) { rear = null; } size--; return item; } public boolean isEmpty() { return size == 0; } public int size() { return size; } } ================================================ FILE: Queue/queue.js ================================================ // 1. Queue Using Array: // 2. Queue Using LinkedList: /* In this implementation, we use an array to store the elements of the queue, and we keep track of the front and rear indices, as well as the current size of the queue. The enqueue operation adds an item to the rear of the queue, while the dequeue operation removes an item from the front of the queue. The isEmpty and isFull methods check whether the queue is empty or full, respectively, while the size method returns the current size of the queue. */ // 1. Queue Using Array: class Queue { constructor() { this.items = []; } enqueue(element) { this.items.push(element); } dequeue() { if (this.isEmpty()) { return "Underflow"; } return this.items.shift(); } front() { if (this.isEmpty()) { return "No elements in Queue"; } return this.items[0]; } isEmpty() { return this.items.length === 0; } printQueue() { let str = ""; for (let i = 0; i < this.items.length; i++) { str += this.items[i] + " "; } return str; } } // Here's an example implementation of a Queue data structure in Java using a linked list /* In this implementation, we use a linked list to store the elements of the queue, and we keep track of the front and rear nodes, as well as the current size of the queue. The enqueue operation adds a new node to the rear of the linked list, while the dequeue operation removes the head node from the linked list. The isEmpty method checks whether the queue is empty, while the size method returns the current size of the queue. Note that this implementation uses generics to allow the queue to store elements of any type. */ // 2. Queue Using LinkedList: class Node { constructor(data) { this.data = data; this.next = null; } } class Queue { constructor() { this.front = null; this.rear = null; this.size = 0; } enqueue(data) { let newNode = new Node(data); if (this.rear === null) { this.front = newNode; this.rear = newNode; } else { this.rear.next = newNode; this.rear = newNode; } this.size++; } dequeue() { if (this.front === null) { return "Underflow"; } let removedNode = this.front; this.front = this.front.next; if (this.front === null) { this.rear = null; } this.size--; return removedNode.data; } isEmpty() { return this.size === 0; } getFront() { return this.front ? this.front.data : "No elements in Queue"; } printQueue() { let str = ""; let temp = this.front; while (temp) { str += temp.data + " "; temp = temp.next; } return str; } } ================================================ FILE: Queue/queue.py ================================================ # Implement Queue Data Structure ''' This implementation uses a Python list to store the items in the queue. The __init__() method initializes an empty list. The is_empty() method checks whether the list is empty or not by checking the length of the list. The enqueue() method adds an item to the back of the queue by appending it to the end of the list. The dequeue() method removes and returns the item at the front of the queue by using the pop() method to remove the first item in the list. If the list is empty, the method raises an IndexError. The peek() method returns the item at the front of the queue without removing it by returning the first item in the list. If the list is empty, the method raises an IndexError. The size() method returns the number of items in the list by returning the length of the list. ''' class Queue: def __init__(self): """ Initializes an empty queue. """ self.items = [] def is_empty(self): """ Returns True if the queue is empty, False otherwise. """ return len(self.items) == 0 def enqueue(self, item): """ Adds the given item to the back of the queue. """ self.items.append(item) def dequeue(self): """ Removes and returns the item at the front of the queue. If the queue is empty, raises an IndexError. """ if self.is_empty(): raise IndexError("Queue is empty") return self.items.pop(0) def peek(self): """ Returns the item at the front of the queue without removing it. If the queue is empty, raises an IndexError. """ if self.is_empty(): raise IndexError("Queue is empty") return self.items[0] def size(self): """ Returns the number of items in the queue. """ return len(self.items) ================================================ FILE: Queue/queue_using_stack.js ================================================ /* Implement a first in first out (FIFO) queue using only two stacks. The implemented queue should support all the functions of a normal queue (push, peek, pop, and empty). Implement the MyQueue class: void push(int x) Pushes element x to the back of the queue. - method adds an element to the back of the queue by pushing it onto stack1. int pop() Removes the element from the front of the queue and returns it. - method removes and returns the front element of the queue by first checking if stack2 is empty. - if it is, reversing the elements from stack1 onto stack2 so that the front element of the queue is now at the top of stack2. - Then it pops the top element from stack2. int peek() Returns the element at the front of the queue. - returns the front element of the queue without removing it. - First checking if stack2 is empty - if it is, reversing the elements from stack1 onto stack2 so that the front element of the queue is now at the top of stack2. - Then it returns the top element of stack2. boolean empty() Returns true if the queue is empty, false otherwise. - returns a boolean indicating whether the queue is empty or not. - It returns true if both stack1 and stack2 are empty, and false otherwise. */ var MyQueue = function() { this.stack1 = []; this.stack2 = []; }; /** * @param {number} x * @return {void} */ MyQueue.prototype.push = function(x) { this.stack1.push(x); }; /** * @return {number} */ MyQueue.prototype.pop = function() { if (this.stack2.length === 0) { if (this.stack1.length === 0) { return "Underflow"; } while (this.stack1.length > 0) { this.stack2.push(this.stack1.pop()); } } return this.stack2.pop(); }; /** * @return {number} */ MyQueue.prototype.peek = function() { if (this.stack2.length === 0) { if (this.stack1.length === 0) { return "Queue is empty"; } while (this.stack1.length > 0) { this.stack2.push(this.stack1.pop()); } } return this.stack2[this.stack2.length - 1]; }; /** * @return {boolean} */ MyQueue.prototype.empty = function() { return this.stack1.length === 0 && this.stack2.length === 0; }; // MyQueue object will be instantiated and called as such: let myQueue = new MyQueue(); var param_0 = myQueue.push(1); // queue is: [1] var param_1 = myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue) var param_2 = myQueue.peek(); // return 1 var param_3 = myQueue.pop(); // return 1, queue is [2] var param_4 = myQueue.empty(); // return false ================================================ FILE: Queue/queue_using_stacks.go ================================================ type MyQueue struct { stack1 []int stack2 []int } /** Initialize your data structure here. */ func Constructor() MyQueue { return MyQueue{} } /** Push element x to the back of queue. */ func (this *MyQueue) Push(x int) { this.stack1 = append(this.stack1, x) } /** Removes the element from in front of queue and returns that element. */ func (this *MyQueue) Pop() int { if len(this.stack2) == 0 { this.moveElements() } element := this.stack2[len(this.stack2)-1] this.stack2 = this.stack2[:len(this.stack2)-1] return element } /** Get the front element. */ func (this *MyQueue) Peek() int { if len(this.stack2) == 0 { this.moveElements() } return this.stack2[len(this.stack2)-1] } /** Returns whether the queue is empty. */ func (this *MyQueue) Empty() bool { return len(this.stack1) == 0 && len(this.stack2) == 0 } /** Move elements from stack1 to stack2. */ func (this *MyQueue) moveElements() { for len(this.stack1) > 0 { element := this.stack1[len(this.stack1)-1] this.stack1 = this.stack1[:len(this.stack1)-1] this.stack2 = append(this.stack2, element) } } /* The MyQueue struct is defined with two slices: stack1 and stack2. stack1 represents the main stack where elements are pushed initially, and stack2 is used to reverse the order of elements for queue operations. The Constructor function is a constructor for the MyQueue struct. It initializes an empty MyQueue instance and returns it. The Push method takes an integer x and appends it to the stack1 slice, which represents the back of the queue. The Pop method removes and returns the element from the front of the queue. It checks if stack2 is empty, and if so, it calls the moveElements method to transfer elements from stack1 to stack2. This ensures that the elements in stack2 are in the correct order for queue operations. The last element of stack2 is then removed and returned as the result. The Peek method returns the element at the front of the queue without removing it. It performs the same check as the Pop method to ensure that the elements in stack2 are up to date, and then returns the last element in stack2. The Empty method checks if both stack1 and stack2 are empty, indicating whether the queue is empty or not. The moveElements method is used to transfer elements from stack1 to stack2 in the correct order. It pops elements from stack1 and appends them to stack2 until stack1 is empty. This ensures that the elements in stack2 are in the reversed order, mimicking the behavior of a queue. Time Complexity: The Push operation has a time complexity of O(1) because it simply appends an element to the stack1 slice. The Pop operation has an amortized time complexity of O(1). When stack2 is not empty, popping an element is a constant-time operation. If stack2 is empty, the moveElements method is called, which transfers all elements from stack1 to stack2. This transfer operation takes O(n) time, where n is the number of elements in stack1. However, each element is transferred at most twice in total (once from stack1 to stack2 and once from stack2 back to stack1 in future push operations). Therefore, the average time complexity per pop operation is O(1). The Peek operation has the same amortized time complexity as Pop, which is O(1). The Empty operation has a time complexity of O(1) because it checks the lengths of both stack1 and stack2 to determine if the queue is empty. Space Complexity: The space complexity of the MyQueue struct is O(n), where n is the number of elements stored in the queue. This includes the space required for the stack1 and stack2 slices. In the worst case, when all elements are stored in stack1, the space complexity is O(n). However, when elements are transferred from stack1 to stack2, the space complexity remains O(n) because each element is moved and stored only once */ ================================================ FILE: Queue/queues_using_stacks.py ================================================ ''' Name : Abhinav kumar Github username : Abhinavcode13 Repository name : data-structures-and-algorithms Problem : Implement Queue using Stacks in Python Issue Number : #256 Problem statement : Explanation of the below python code : In the above code, the Queue class represents a queue implemented using two stacks. The enqueue method simply adds the element to the first stack (stack1). The dequeue method pops an element from the second stack (stack2) if it's not empty. If stack2 is empty, it transfers all elements from stack1 to stack2 in reverse order and then pops an element from stack2. The program provides the user with a menu to select the operation they want to perform - enqueue, dequeue, or quit. The user can input the value to enqueue, and the program prints the enqueued or dequeued value accordingly. Summary about the code and time complexity: This code implements a queue using two stacks. The enqueue operation is simply implemented by appending an element to one of the stacks, while the dequeue operation involves reversing the order of the elements by popping from one stack and pushing onto the other, and then popping the top element from the second stack. The time complexity of the enqueue operation is O(1), while the time complexity of the dequeue operation is O(n) in the worst case, where n is the number of elements in the queue. This is because in the worst case, all the elements will need to be moved from one stack to the other during the dequeue operation. ----------------------------------------------------------------------------------------------------------//Python code begins here----------------------------------------------------------------------------------------------------------------------- ''' class Queue: def __init__(self): # Initialize two empty stacks self.stack1 = [] self.stack2 = [] def enqueue(self, val): # Add an element to the end of the queue by appending it to stack1 self.stack1.append(val) def dequeue(self): # If stack2 is empty, reverse the order of the elements by popping from stack1 and pushing onto stack2 if not self.stack2: while self.stack1: self.stack2.append(self.stack1.pop()) # If stack2 is still empty, the queue is empty, so return None if not self.stack2: return None # Otherwise, pop the top element from stack2 and return it return self.stack2.pop() # Create an instance of the Queue class queue = Queue() # Enter into an infinite loop to prompt the user for operations while True: print("Select operation -\n" "1. Enqueue\n" "2. Dequeue\n" "3. Quit") # Prompt the user for their choice of operation choice = int(input("Enter choice: ")) if choice == 1: # If the user selects option 1, prompt them for a value to enqueue and enqueue it val = int(input("Enter value to enqueue: ")) queue.enqueue(val) print("Enqueued value:", val) elif choice == 2: # If the user selects option 2, dequeue a value and print it, or indicate that the queue is empty val = queue.dequeue() if val: print("Dequeued value:", val) else: print("Queue is empty.") elif choice == 3: # If the user selects option 3, quit the loop break else: # If the user selects an invalid option, prompt them to try again print("Invalid choice. Please try again.") ================================================ FILE: Queue/stack_using_queue.cpp ================================================ // ISSUE # 258 /* Aim : To implement Stack data structure using two Queues. Inspired from - https://leetcode.com/problems/implement-stack-using-queues/ Contributor - github user - harsh9607 LOGIC : We have two queues q1 and q2 within the Class Queue_As_stack . The Logic is to use the front of q1 to behave as top of the stack. q2 is used as a helper queue. So whenever we want to add a new element we transfer all existing elements from q1 into q2 . When q1 is empty we push the new element. Then we transfer back all the elements originally in q1 from q2. Approach In short : a) Push all from q1 -> q2 b) Insert new element c) Push all from q2 -> q1 EXAMPLES : Say we want to input three numbers in a stack - 1 , 2 , 3 Implementation will be as follows q1 : q2 : >> 1 q1 : 1 q2 : >> 2 ( existing data structure ) q1 : 1 q2 : (Step a ) Transfer from q1 -> q2 q1 : q2 : 1 Step b ) Insert new element to q1 q1 : 2 q2 : 1 Step c ) Transfer all from q2 -> q1 q1 : 2 , 1 q2 : >> 3 ( existing data structure ) q1 : 2 , 1 q2 : (Step a ) Transfer from q1 -> q2 q1 : q2 : 2 , 1 Step b ) Insert new element to q1 q1 : 3 q2 : 2 , 1 Step c ) Transfer all from q2 -> q1 q1 : 3 , 2 , 1 q2 : */ #include #include using namespace std; class Queue_As_stack { public : queue q1,q2; void PUSH(int d) { if (q1.empty()) { q1.push(d); } else { while (!q1.empty()) { q2.push(q1.front()); q1.pop(); } q1.push(d); while (!q2.empty()) { q1.push(q2.front()); q2.pop(); } } } // Pops out top of stack void POP() { q1.pop(); } // Returns the top of stack int TOP() { return q1.front(); } // It displays as well as empties the stack. void DISPLAY() { while (!q1.empty()) { cout << q1.front() << endl; q1.pop(); } } // To check if stack is empty bool EMPTY() { if(q1.empty()) { return true; } return false; } }; int main() { Queue_As_stack Q1; for (int i = 1; i <= 10; i++) { Q1.PUSH(i); } Q1.POP(); Q1.DISPLAY(); if(Q1.EMPTY()) { cout<<"Stack is empty ! "; } return 0; } ================================================ FILE: README.md ================================================

Data Structures & Algorithms

# Implementation of well known Data Structures and Algorithms Contributions are welcome! You can join the fun by following our [contributing guide](https://github.com/akgmage/data-structures-and-algorithms/blob/main/CONTRIBUTING.md). # Table of contents - [Intro](https://github.com/akgmage/data-structures-and-algorithms#intro) - [Time complexity](https://github.com/akgmage/data-structures-and-algorithms#time-complexity) - [Calculation Rules](https://github.com/akgmage/data-structures-and-algorithms#calculation-rules) - [Loops](https://github.com/akgmage/data-structures-and-algorithms#loops) - [Order of Magnitude](https://github.com/akgmage/data-structures-and-algorithms#order-of-magnitude) - [Phases](https://github.com/akgmage/data-structures-and-algorithms#phases) - [Several Variables](https://github.com/akgmage/data-structures-and-algorithms#several-variables) - [Recursion](https://github.com/akgmage/data-structures-and-algorithms#recursion) - [Complexity classes](https://github.com/akgmage/data-structures-and-algorithms#complexity-classes) - [Estimating efficiency](https://github.com/akgmage/data-structures-and-algorithms#estimating-efficiency) - [Data structures](https://github.com/akgmage/data-structures-and-algorithms#data-structures) - [Sorting](https://github.com/akgmage/data-structures-and-algorithms#sorting) - [Well Known Sorting Algorithms](https://github.com/akgmage/data-structures-and-algorithms#well-known-sorting-algorithms) - [Binary Search](https://github.com/akgmage/data-structures-and-algorithms#binary-search) - [Method 1](https://github.com/akgmage/data-structures-and-algorithms#method-1) - [Example](https://github.com/akgmage/data-structures-and-algorithms#example) - [Method 2](https://github.com/akgmage/data-structures-and-algorithms#method-2) - [Greedy algorithms](https://github.com/akgmage/data-structures-and-algorithms#greedy-algorithms) - [Coin problem](https://github.com/akgmage/data-structures-and-algorithms#coin-problem) - [General case](https://github.com/akgmage/data-structures-and-algorithms#general-case) - [Dynamic programming](https://github.com/akgmage/data-structures-and-algorithms#dynamic-programming) - [Coin problem](https://github.com/akgmage/data-structures-and-algorithms#coin-problem-1) - [Using memoization](https://github.com/akgmage/data-structures-and-algorithms#using-memoization) - [Amortized analysis](https://github.com/akgmage/data-structures-and-algorithms#amortized-analysis) - [Pattern 1: Two Pointers](https://github.com/akgmage/data-structures-and-algorithms#pattern-1-two-pointers) - [Practice problems for two pointers](https://github.com/akgmage/data-structures-and-algorithms#practice-problems-for-two-pointers) - [Pattern 2: Fast and Slow Pointers](https://github.com/akgmage/data-structures-and-algorithms#pattern-2-fast-and-slow-pointers) - [Practice problems for fast and slow pointers](https://github.com/akgmage/data-structures-and-algorithms#practice-problems-for-fast-and-slow-pointers) - [Pattern 3: Sliding Window](https://github.com/akgmage/data-structures-and-algorithms#pattern-3-sliding-window) - [Practice problems for sliding window](https://github.com/akgmage/data-structures-and-algorithms#practice-problems-for-sliding-window) - [Pattern 4: Merge Interval](https://github.com/akgmage/data-structures-and-algorithms#pattern-4-merge-interval) - [Practice problems for merge intervals](https://github.com/akgmage/data-structures-and-algorithms#practice-problems-for-merge-intervals) - [Pattern 5: In-place Reversal of a Linked List](https://github.com/akgmage/data-structures-and-algorithms#pattern-5-in-place-reversal-of-a-linked-list) - [Practice problems for in-place reversal of a linked list](https://github.com/akgmage/data-structures-and-algorithms#practice-problems-for-in-place-reversal-of-a-linked-list) - [Pattern 6: Two Heaps](https://github.com/akgmage/data-structures-and-algorithms#pattern-6-two-heaps) - [Practice problems for two heaps](https://github.com/akgmage/data-structures-and-algorithms#practice-problems-for-two-heaps) - [Pattern 7: K-way Merge](https://github.com/akgmage/data-structures-and-algorithms#pattern-7-k-way-merge) - [Practice problems for k-way Merge](https://github.com/akgmage/data-structures-and-algorithms#practice-problems-for-k-way-merge) - [Pattern 8: Top K Elements](https://github.com/akgmage/data-structures-and-algorithms#pattern-8-top-k-elements) - [Practice problems for Top K Elements](https://github.com/akgmage/data-structures-and-algorithms#practice-problems-for-top-k-elements) # Intro The design of algorithms consists of problem solving and mathematical thinking. Skills for analyzing problems and solving them creatively are needed. An algorithm for solving a problem has to be both correct and efficient, and the core of the problem is often about inventing an efficient algorithm. # Time complexity The efficiency of algorithms is important. Usually, it is easy to design an algorithm that solves the problem slowly, but the real challenge is to invent a fast algorithm. The time complexity of an algorithm estimates how much time the algorithm will use for some input. The idea is to represent the efficiency as a function whose parameter is the size of the input. By calculating the time complexity, we can find out whether the algorithm is fast enough without implementing it. ## Calculation rules The time complexity of an algorithm is denoted `O(...)` where the three dots represent some function. Usually, the variable n denotes the input size. For example, if the input is an array of numbers, n will be the size of the array, and if the input is a string, n will be the length of the string. ## Loops A common reason why an algorithm is slow is that it contains many loops that go through the input. The more nested loops the algorithm contains, the slower it is. If there are k nested loops, the time complexity is O(n^k). For example, the time complexity of the following code is O(n): ```cpp for (int i = 1; i <= n; i++) { // code } ``` And the time complexity of the following code is O(n^2): ```cpp for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { // code } } ``` ## Order of magnitude A time complexity does not tell us the exact number of times the code inside a loop is executed, but it only shows the order of magnitude. In the following examples, the code inside the loop is executed 3n, n + 5 and [n / 2] times, but the time complexity of each code is O(n). ```cpp for (int i = 1; i <= 3*n; i++) { // code } ``` ```cpp for (int i = 1; i <= n+5; i++) { // code } ``` ```cpp for (int i = 1; i <= n; i += 2) { // code } ``` As another example, the time complexity of the following code is O(n^2): ```cpp for (int i = 1; i <= n; i++) { for (int j = i+1; j <= n; j++) { // code } } ``` ## Phases If the algorithm consists of consecutive phases, the total time complexity is the largest time complexity of a single phase. The reason for this is that the slowest phase is usually the bottleneck of the code. For example, the following code consists of three phases with time complexities O(n), O(n^2) and O(n). Thus, the total time complexity is O(n^2). ```cpp for (int i = 1; i <= n; i++) { // code } for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { // code } } for (int i = 1; i <= n; i++) { // code } ``` ## Several variables Sometimes the time complexity depends on several factors. In this case, the time complexity formula contains several variables. For example, the time complexity of the following code is O(nm): ```cpp for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { // code } } ``` ## Recursion The time complexity of a recursive function depends on the number of times the function is called and the time complexity of a single call. The total time complexity is the product of these values. For example, consider the following function: ```cpp void f(int n) { if (n == 1) return; f(n-1); } ``` The call f(n) causes n function calls, and the time complexity of each call is O(1). Thus, the total time complexity is O(n). As another example, consider the following function: ```cpp void g(int n) { if (n == 1) return; g(n-1); g(n-1); } ``` In this case each function call generates two other calls, except for n = 1. Let us see what happens when g is called with parameter n. The following table shows the function calls produced by this single call: function call number of calls g(n)        1 g(n - 1)        2 g(n - 2)        4 ....        g(1)        2^(n - 1) Based on this, the time complexity is 1 + 2 + 4 + ... + 2^(n - 1) = 2^(n - 1) = O(2^n). # Complexity classes The following list contains common time complexities of algorithms: O(1) : The running time of a constant-time algorithm does not depend on the input size. A typical constant-time algorithm is a direct formula that calculates the answer. O(logn) : A logarithmic algorithm often halves the input size at each step. The running time of such an algorithm is logarithmic, because log base 2 n equals the number of times n must be divided by 2 to get 1. O(√n) : A square root algorithm is slower than O(logn) but faster than O(n). A special property of square roots is that √n = n / √n , so the square root √n lies, in some sense, in the middle of the input. O(n) : A linear algorithm goes through the input a constant number of times. This is often the best possible time complexity, because it is usually necessary to access each input element at least once before reporting the answer. O(nlogn) : This time complexity often indicates that the algorithm sorts the input, because the time complexity of efficient sorting algorithms is O(nlogn). Another possibility is that the algorithm uses a data structure where each operation takes O(logn) time. O(n^2) : A quadratic algorithm often contains two nested loops. It is possible to go through all pairs of the input elements in O(n^2) time. O(n^3) : A cubic algorithm often contains three nested loops. It is possible to go through all triplets of the input elements in O(n^3) time. O(2^n) : This time complexity often indicates that the algorithm iterates through all subsets of the input elements. For example, the subsets of {1,2,3} are Φ, {1}, {2}, {3}, {1,2}, {1,3}, {2,3} and {1,2,3}. O(n!) : This time complexity often indicates that the algorithm iterates through all permutations of the input elements. For example, the permutations of {1,2,3} are (1,2,3), (1,3,2), (2,1,3 , (2,3,1), (3,1,2) and (3,2,1). An algorithm is polynomial if its time complexity is at most O(n^k) where k is a constant. All the above time complexities except O(2^n) and O(n!) are polynomial. In the practice, the constant k is usually small, and therefore a polynomial time complexity roughly means that the algorithm is efficient. Still, there are many important problems for which no polynomial algorithm is known, i.e., nobody knows how to solve them efficiently. NP-hard problems are an important set of problems, for which no polynomial algorithm is known. # Estimating efficiency By calculating the time complexity of an algorithm, it is possible to check, before implementing the algorithm, that it is efficient enough for the problem. The starting point for estimations is the fact that a modern computer can perform some hundreds of millions of operations in a second. For example, assume that the time limit for a problem is one second and the input size is n = 10 ^ 5. If the time complexity is O(n^2), the algorithm will perform about (10 ^ 5) ^ 2 = 10 ^ 10 operations. This should take at least some tens of seconds, so the algorithm seems to be too slow for solving the problem. On the other hand, given the input size, we can try to guess the required time complexity of the algorithm that solves the problem. The following table contains some useful estimates assuming a time limit of one second. input size required time complexity n <= 10        O(n!) n <= 20        O(2 ^ n) n <= 500         O(n ^ 3) n <= 5000       O(n ^ 2) n <= 10 ^ 6      O(nlogn) or O(n) n is large      O(1) or O(logn) For example, if the input size is n = 10 ^ 5, it is probably expected that the time complexity of the algorithm is O(n) or O(nlogn). This information makes it easier to design the algorithm, because it rules out approaches that would yield an algorithm with a worse time complexity. Still, it is important to remember that a time complexity is only an estimate of efficiency, because it hides the constant factors. For example, an algorithm that runs in O(n) time may perform n/2 or 5n operations. This has an important effect on the actual running time of the algorithm. # Data structures A data structure is a way to store data in the memory of a computer. It is important to choose an appropriate data structure for a problem, because each data structure has its own advantages and disadvantages. The crucial question is: which operations are efficient in the chosen data structure? - `Dynamic arrays` : A dynamic array is an array whose size can be changed during the execution of the program. - `Set structures` : A set is a data structure that maintains a collection of elements. The basic operations of sets are element insertion, search and removal. - `Map structures` : A map is a generalized array that consists of key-value-pairs. While the keys in an ordinary array are always the consecutive integers 0,1,...,n-1, where n is the size of the array, the keys in a map can be of any data type and they do not have to be consecutive values. - `Deque` : A deque is a dynamic array whose size can be efficiently changed at both ends of the array. Like a vector, a deque provides the functions push_back and pop_back, but it also includes the functions push_front and pop_front which are not available in a vector. - `Stack` : A stack is a data structure that provides two O(1) time operations: adding an element to the top, and removing an element from the top. It is only possible to access the top element of a stack. - `Queue` : A queue also provides two O(1) time operations: adding an element to the end of the queue, and removing the first element in the queue. It is only possible to access the first and last element of a queue. - `Priority queue` : A priority queue maintains a set of elements. The supported operations are insertion and, depending on the type of the queue, retrieval and removal of either the minimum or maximum element. Insertion and removal take O(logn) time, and retrieval takes O(1) time. While an ordered set efficiently supports all the operations of a priority queue, the benefit of using a priority queue is that it has smaller constant factors. A priority queue is usually implemented using a heap structure that is much simpler than a balanced binary tree used in an ordered set. # Sorting Sorting is a fundamental algorithm design problem. Many efficient algorithms use sorting as a subroutine, because it is often easier to process data if the elements are in a sorted order. For example, the problem ”does an array contain two equal elements?” is easy to solve using sorting. If the array contains two equal elements, they will be next to each other after sorting, so it is easy to find them. Also, the problem ”what is the most frequent element in an array?” can be solved similarly. There are many algorithms for sorting, and they are also good examples of how to apply different algorithm design techniques. The efficient general sorting algorithms work in O(nlogn) time, and many algorithms that use sorting as a subroutine also have this time complexity. ```js heights = [6, 5, 4, 5, 2, 3]; heights.sort(); Output: [2, 3, 4, 5, 5, 6]; ``` Even though languages have built-in sorting method, sorting is a great example of how there may be many ways to think about the same problem, some perhaps better than others. Understanding sorting is a traditional first step towards mastery of algorithms and computer science. ### Well known sorting algorithms - Bubble Sort [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/bubble_sort.go) [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/bubble_sort.cpp) [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/bubble_sort.java) [Javascript](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/bubble_sort.js) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/bubble_sort.py) - Insertion Sort [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/insertion_sort.go) [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/insertion_sort.cpp) [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/insertion_sort.java) [Javascript](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/insertion_sort.js) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/insertion_sort.py) - Selection Sort [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/selection_sort.go) [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/selection_sort.cpp) [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/selection_sort.java) [Javascript](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/selection_sort.js) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/selection_sort.py) - Merge Sort [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/merge_sort.go) [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/merge_sort.cpp) [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/merge_sort.java) [Javascript](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/merge_sort.js) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/merge_sort.py) - Quick Sort [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/quick_sort.cpp) [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/quick_sort.go) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/quick_sort.py) [Javascript](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/quick_sort.js) [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/sorting/quick_sort.java) # Binary Search A general method for searching for an element in an array is to use a for loop that iterates through the elements of the array. For example, the following code searches for an element x in an array: ```cpp for (int i = 0; i < n; i++) { if (array[i] == x) { // x found at index i } } ``` The time complexity of this approach is O(n), because in the worst case, it is necessary to check all elements of the array. If the order of the elements is arbitrary, this is also the best possible approach, because there is no additional information available where in the array we should search for the element x. However, if the array is sorted, the situation is different. In this case it is possible to perform the search much faster, because the order of the elements in the array guides the search. The following binary search algorithm efficiently searches for an element in a sorted array in O(logn) time. ## Method 1 The usual way to implement binary search resembles looking for a word in a dictionary. The search maintains an active region in the array, which initially contains all array elements. Then, a number of steps is performed, each of which halves the size of the region. At each step, the search checks the middle element of the active region. If the middle element is the target element, the search terminates. Otherwise, the search recursively continues to the left or right half of the region, depending on the value of the middle element. The above idea can be implemented as follows: ```cpp int a = 0, b = n-1; while (a <= b) { int k = (a+b)/2; if (array[k] == x) { // x found at index k } if (array[k] > x) b = k-1; else a = k+1; } ``` ## Example [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Binary%20Search/binary_search_iterative.go) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Binary%20Search/binary_search.py) [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Binary%20Search/binary_search.cpp) [JS](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Binary%20Search/binary_search.js) ## Method 2 An alternative method to implement binary search is based on an efficient way to iterate through the elements of the array. The idea is to make jumps and slow the speed when we get closer to the target element. The search goes through the array from left to right, and the initial jump length is n/2. At each step, the jump length will be halved: first n/4, then n/8, n/16, etc., until finally the length is 1. After the jumps, either the target element has been found or we know that it does not appear in the array. The following code implements the above idea: ```cpp int k = 0; for (int b = n/2; b >= 1; b /= 2) { while (k+b < n && array[k+b] <= x) k += b; } if (array[k] == x) { // x found at index k } ``` During the search, the variable b contains the current jump length. The time complexity of the algorithm is O(logn), because the code in the while loop is performed at most twice for each jump length. # Greedy algorithms A greedy algorithm constructs a solution to the problem by always making a choice that looks the best at the moment. A greedy algorithm never takes back its choices, but directly constructs the final solution. For this reason, greedy algorithms are usually very efficient. The difficulty in designing greedy algorithms is to find a greedy strategy that always produces an optimal solution to the problem. The locally optimal choices in a greedy algorithm should also be globally optimal. It is often difficult to argue that a greedy algorithm works. ## Coin problem As a first example, we consider a problem where we are given a set of coins and our task is to form a sum of money n using the coins. The values of the coins are coins = `{c1, c2,..., ck}`, and each coin can be used as many times we want. What is the minimum number of coins needed? For example, if the coins are the euro coins (in cents) `{1,2,5,10,20,50,100,200}` and `n = 520`, we need at least four coins. The optimal solution is to select coins `200 + 200 + 100 + 20` whose sum is `520`. A simple greedy algorithm to the problem always selects the largest possible coin, until the required sum of money has been constructed. This algorithm works in the example case, because we first select two `200` cent coins, then one `100` cent coin and finally one `20` cent coin. But does this algorithm always work? It turns out that if the coins are the euro coins, the greedy algorithm always works, i.e., it always produces a solution with the fewest possible number of coins. The correctness of the algorithm can be shown as follows: First, each coin `1, 5, 10, 50 and 100` appears at most once in an optimal solution, because if the solution would contain two such coins, we could replace them by one coin and obtain a better solution. For example, if the solution would contain coins `5 + 5`, we could replace them by coin 10. In the same way, coins 2 and 20 appear at most twice in an optimal solution, because we could replace coins `2 + 2 + 2` by coins `5 + 1` and coins `20 + 20 + 20` by coins 50 + 10. Moreover, an optimal solution cannot contain coins `2 + 2 + 1` or `20 + 20 + 10`, because we could replace them by coins 5 and 50. Using these observations, we can show for each coin x that it is not possible to optimally construct a sum x or any larger sum by only using coins that are smaller than x. For example, `if x = 100`, the largest optimal sum using the smaller coins is `50 + 20 + 20 + 5 + 2 + 2 = 99`. Thus, the greedy algorithm that always selects the largest coin produces the optimal solution. This example shows that it can be difficult to argue that a greedy algorithm works, even if the algorithm itself is simple. ## General case In the general case, the coin set can contain any coins and the greedy algorithm does not necessarily produce an optimal solution. We can prove that a greedy algorithm does not work by showing a counterexample where the algorithm gives a wrong answer. In this problem we can easily find a counterexample: if the coins are `{1,3,4}` and the target sum is `6`, the greedy algorithm produces the solution 4 + 1 + 1 while the optimal solution is `3 + 3`. It is not known if the general coin problem can be solved using any greedy algorithm. # Dynamic programming Dynamic programming is a technique that combines the correctness of complete search and the efficiency of greedy algorithms. Dynamic programming can be applied if the problem can be divided into overlapping subproblems that can be solved independently. There are two uses for dynamic programming: - Finding an optimal solution: We want to find a solution that is as large as possible or as small as possible. - Counting the number of solutions: We want to calculate the total number of possible solutions. ## Coin problem We first focus on a problem that we have already seen in Greedy Algorithm Given a set of coin values coins = `{c1, c2,..., ck}` and a target sum of money n, our task is to form the sum n using as few coins as possible. We solved the problem using a greedy algorithm that always chooses the largest possible coin. The greedy algorithm works, for example, when the coins are the euro coins, but in the general case the greedy algorithm does not necessarily produce an optimal solution. Now is time to solve the problem efficiently using dynamic programming, so that the algorithm works for any coin set. The dynamic programming algorithm is based on a recursive function that goes through all possibilities how to form the sum, like a brute force algorithm. However, the dynamic programming algorithm is efficient because it uses memoization and calculates the answer to each subproblem only once. The idea in dynamic programming is to formulate the problem recursively so that the solution to the problem can be calculated from solutions to smaller subproblems. In the coin problem, a natural recursive problem is as follows: what is the smallest number of coins required to form a sum x? Let solve(x) denote the minimum number of coins required for a sum x. The values of the function depend on the values of the coins. For example, if `coins = {1,3,4}`, the first values of the function are as follows: ```solve(0) = 0 solve(1) = 1 solve(2) = 2 solve(3) = 1 solve(4) = 1 solve(5) = 2 solve(6) = 2 solve(7) = 2 solve(8) = 2 solve(9) = 3 solve(10) = 3 ``` For example, `solve(10) = 3`, because at least 3 coins are needed to form the sum `10`. The optimal solution is `3 + 3 + 4 = 10`. The essential property of solve is that its values can be recursively calculated from its smaller values. The idea is to focus on the first coin that we choose for the sum. For example, in the above scenario, the first coin can be either `1, 3 or 4`. If we first choose coin `1`, the remaining task is to form the sum 9 using the minimum number of coins, which is a subproblem of the original problem. Of course, the same applies to coins` 3 and 4`. Thus, we can use the following recursive formula to calculate the minimum number of coins: solve(x) = min(solve(x - 1) + 1, solve(x - 3) + 1, solve(x - 4) + 1). The base case of the recursion is solve(0) Æ 0, because no coins are needed to form an empty sum. For example, solve(10) = solve(7) + 1 = solve(4) + 2 = solve(0) + 3 = 3. Now we are ready to give a general recursive function that calculates the minimum number of coins needed to form a sum x: solve(x) = Infinity if x < 0 solve(x) = 0 if x == 0 solve(x) = min(c --> coins) ==> solve(x - c) + 1, if x > 0 Once a recursive function that solves the problem has been found, we can directly implement a solution, (the constant INF denotes infinity): ```cpp int solve(int x) { if (x < 0) return INF; if (x == 0) return 0; int best = INF; for (auto c : coins) { best = min(best, solve(x-c)+1); } return best; } ``` Still, this function is not efficient, because there may be an exponential number of ways to construct the sum. However, next we will see how to make the function efficient using a technique called memoization. ## Using memoization The idea of dynamic programming is to use memoization to efficiently calculate values of a recursive function. This means that the values of the function are stored in an array after calculating them. For each parameter, the value of the function is calculated recursively only once, and after this, the value can be directly retrieved from the array. In this problem, we use arrays ```cpp bool ready[N]; int value[N]; ``` where `ready[x]` indicates whether the value of `solve(x)` has been calculated, and if it is, `value[x]` contains this value. The constant `N` has been chosen so that all required values fit in the arrays. Now the function can be efficiently implemented as follows: ```cpp int solve(int x) { if (x < 0) return INF; if (x == 0) return 0; if (ready[x]) return value[x]; int best = INF; for (auto c : coins) { best = min(best, solve(x-c)+1); } value[x] = best; ready[x] = true; return best; } ``` # Amortized analysis The time complexity of an algorithm is often easy to analyze just by examining the structure of the algorithm: what loops does the algorithm contain and how many times the loops are performed. However, sometimes a straightforward analysis does not give a true picture of the efficiency of the algorithm. Amortized analysis can be used to analyze algorithms that contain operations whose time complexity varies. The idea is to estimate the total time used to all such operations during the execution of the algorithm, instead of focusing on individual operations. # Pattern 1: Two Pointers As the name suggests, the two pointers pattern uses two pointers to iterate over an array or list until the conditions of the problem are satisfied. This is useful because it allows us to keep track of the values of two different indexes in a single iteration. Whenever there’s a requirement to find two data elements in an array that satisfy a certain condition, the two pointers pattern should be the first strategy to come to mind. The pointers can be used to iterate the data structure in one or both directions, depending on the problem statement. For example, to identify whether a string is a palindrome, we can use one pointer to iterate the string from the beginning and the other to iterate it from the end. At each step, we can compare the values of the two pointers and see if they meet the palindrome properties. ## Practice problems for two pointers - Two sum [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Hash%20Table/two_sum.go) [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Hash%20Table/two_sum.cpp) [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Hash%20Table/two_sum.java) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Hash%20Table/two_sum.py) [Javascript](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Hash%20Table/two_sum.js) - Three Number Sum [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/triplet_sum.go) [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/triplet_sum.java) [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/triplet_sum.cpp) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/triplet_sum.py) [Javascript](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/triplet_sum.js) - Valid Pallindrome [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/is_pallindrome.go) [Javascript](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/is_pallindrome.js) [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/is_pallindrome.cpp) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/is_pallindrome.py) [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/is_pallindrome.java) - Reverse Word in a String [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/reverse_words_in_a_string.go) [Javascript](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/reverse_words_in_a_string.jd) [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/reverse_words_in_a_string.cpp) [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/reverse_words_in_a_string.java) - Valid Pallindrome II [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/valid_pallindrome2.go) [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/valid_pallindrome2.cpp) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/valid_pallindrome2.py) [Javascript](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/valid_pallindrome2.js) [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Strings/valid_pallindrome2.java) # Pattern 2: Fast and Slow Pointers The fast and slow pointer technique (also known as the tortoise and hare algorithm) uses two pointers to determine traits about directional data structures. This can be an array, singly-linked list, or a graph. Similar to the two pointers pattern, the fast and slow pointers pattern uses two pointers to traverse an iterable data structure at different speeds. It’s usually used to identify distinguishable features of directional data structures, such as a linked list or an array. The pointers can be used to traverse the array or list in either direction, however, one moves faster than the other. Generally, the slow pointer moves forward by a factor of one, and the fast pointer moves by a factor of two in each step. However, the speed can be adjusted according to the problem statement. Unlike the two pointers approach, which is concerned with data values, the fast and slow pointers approach is used to determine data structure traits using indices in arrays or node pointers in linked lists. The approach is commonly used to detect cycles in the given data structure, so it’s also known as Floyd’s cycle detection algorithm. The key idea is that the pointers start at the same location, but they move forward at different speeds. If there is a cycle, the two are bound to meet at some point in the traversal. To understand the concept, think of two runners on a track. While they start from the same point, they have different running speeds. If the race track is a circle, the faster runner will overtake the slower one after completing a lap. On the other hand, if the track is straight, the faster runner will end the race before the slower one, hence never meeting on the track again. The fast and slow pointers pattern uses the same intuition. ## Practice problems for fast and slow pointers - Linked List cycle detection [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Fast%20and%20Slow%20Pointers/linked_floyds_cycle_detection.cpp) [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Linked%20List/floyds_cycle_detection.go) [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Linked%20List/floyds_cycle_detection.java) - Find middle of Linked List [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Fast%20and%20Slow%20Pointers/linked_list_compute_midpoint.cpp) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Fast%20and%20Slow%20Pointers/linked_list_find_middle.py) - Happy Number [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Fast%20and%20Slow%20Pointers/happy_number.go) - Pallindrome Linked List [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Linked%20List/linked_list_pallindrome.cpp) - Remove Kth node from end [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Linked%20List/linked_list_kth_from_end.go) [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Linked%20List/linked_list_remove_nth_node_from_end.cpp) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Linked%20List/remove_kth_node_from_end.py) - Linked List Sort List [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Linked%20List/liniked_list_sort_list.cpp) # Pattern 3: Sliding Window The Sliding window is a problem-solving technique of data structure and algorithm for problems that apply arrays or lists. These problems are painless to solve using a brute force approach in O(n²) or O(n³). However, the Sliding window technique can reduce the time complexity to O(n). The sliding window pattern is a computational method aimed at reducing the use of nested loops in an algorithm. It’s a variation of the two pointers pattern, where the pointers can be used to set window bounds. A window is a sublist formed over a part of an iterable data structure. It can be used to slide over the data in chunks corresponding to the window size. The sliding window pattern allows us to process the data in segments instead of the entire list. The segment or window size can be set according to the problem’s requirements. For example, if we have to find three consecutive integers with the largest sum in an array, we can set the window size to 3. This will allow us to process the data three elements at a time. Why is this method more efficient? It isn’t if, for each window, we iterate over all the elements of the window because that gives us the same O(kn) time complexity. Instead, what if we focused on the element entering the window and the one leaving it? For example, after calculating the sum of the first three elements, we move the window one step forward, subtract the element that is no longer in the window from the sum, and add the new element that has entered it. Next we check if the new sum is greater than the first. If it is, we update the max sum found so far. Now, each time we move the window forward, we perform at most four operations, reducing the time complexity to O(4n), that is, O(n). ## Practice problems for sliding window - Find Maximum in Sliding Window [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Sliding%20Window/sliding_window_max.java) [Javascript](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Sliding%20Window/sliding_window_max.js) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Sliding%20Window/sliding_window_max.py) - Minimum Window Subsequence - Repeated DNA Sequences - Minimum Window Substring - Longest Substring without Repeating Characters [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Sliding%20Window/longest_substring_without_repeating_characters.go) # Pattern 4: Merge Interval The merge intervals pattern deals with problems involving overlapping intervals. Each interval is represented by a start and an end time. For example, an interval of [10,20] seconds means that the interval starts at 10 seconds and ends at 20seconds, such that both 10 and time 20 are included in the interval. The most common problems solved using this pattern are scheduling problems. The key to understanding this pattern and exploiting its power lies in understanding how any two intervals may overlap. Many problems in the real world use the merge intervals pattern. Let’s look at some examples. - Display busy schedule: Display the busy hours of a user to other users without revealing the individual meeting slots in a calendar. - Schedule a new meeting: Add a new meeting to the tentative meeting schedule of a user in such a way that no two meetings overlap each other. - Task scheduling in operating systems (OS): Schedule tasks for the OS based on task priority and the free slots in the machine’s processing schedule. ## Practice problems for merge intervals - Merge Intervals [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/merge_intervals.go) [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/merge_intervals.cpp) [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/merge_intervals.java) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/merge_intervals.py) [Javacript](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/merge_intervals.js) - Insert Interval [Go](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/insert_interval.go) [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/insert_interval.cpp) [Python](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/insert_interval.py) [Java](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/insert_interval.java) [Javascript](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Arrays/insert_interval.js) - Interval List Intersections - Employee Free Time - Meeting Rooms # Pattern 5: In-place Reversal of a Linked List The in-place reversal of a linked list pattern allows us to reverse a linked list without any additional memory, using only the given nodes. Many problems require a reversal of a set of nodes in a linked list without using additional memory. In such cases, using the in-place reversal pattern is the simplest solution. Instead of making a new linked list with reversed links, we can do it in place, without using additional memory. How can we achieve an in-place reversal of nodes? We iterate in a linked list and keep track of the current node, the next node, and the previous node simultaneously. Keeping track of the nodes allows us to easily change the links between them and make them point to a different node than before. When solving such problems, the naive approach of iterating the linked list using nested loops takes O(n²) time. However, using the in-place reversal pattern, the time complexity is O(n) time, since we use a single loop to iterate the linked list. Similarly, for space complexity: the naive approach requires the use of additional memory—if a linked list contains thousands of nodes, we’d need to allocate a lot of additional memory resources to solve the problem. However, the in-place reversal of a linked pattern will use only O(1) space. ## Practice problems for in-place reversal of a linked list - Reverse Linked List [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Linked%20List/linked_list_reverse.cpp) - Reverse Nodes in k-group - Reorder List - Swapping Nodes in a Linked List - Swapping Nodes in Pairs [C++](https://github.com/akgmage/data-structures-and-algorithms/blob/main/Linked%20List/linked_list_swap_nodes_in_pair.cpp) - Reverse Nodes in Even Length Groups # Pattern 6: Two Heaps As the name suggests, the two heaps pattern uses either two min-heaps, two max-heaps, or a min-heap and a max-heap simultaneously to solve the problem. Given that there are n elements in a heap, it takes O(log n) time to insert an element in it, O(log n) time to remove an element from it, and O(1) time to access the element at the root of the heap. The root stores the smallest element in the case of a min-heap and the largest element in a max-heap. In some problems, we’re given a set of data such that it can be divided into two parts. We can either use the first part to find the smallest element using the min-heap and the second part to find the largest element using the max-heap, or we can do the reverse and use the first part to find the largest element using the max-heap and the second part to find the smallest element using the min-heap. There might be cases where we need to find the two largest numbers from two different data sets. We’ll use two max-heaps to store two different data sets in that case. In other cases, we might need to find the two smallest numbers from two different data sets, and then we would use two min-heaps. Many problems in the real world use the two heaps pattern. Let’s look at some examples. - Video streaming: During a user session, there is often a possibility that packet drops and buffering might occur. We want to record the median number of buffering events that might occur in a particular session, which could then be used to improve the user experience. - Netflix: As part of a demographic study, we’re interested in the median age of our viewers. We want to implement a functionality whereby the median age can be updated efficiently whenever a new user signs up for video streaming. ## Practice problems for two heaps - Maximize Capital - Find Median from a data stream - Schedule Tasks on minimum machines # Pattern 7: K-way Merge The k-way merge pattern helps to solve problems involving a list of sorted arrays. Here is what the pattern looks like: 1. Insert the first element of each array in a min-heap. 2. Next, remove the smallest element from the heap and add it to the merged array. 3. Keep track of which array each element comes from. 4. Then, insert the next element of the same array into the heap. 5. Repeat steps 2 to 4 to fill the merged array in sorted order. Many problems in the real world use the k-way merge pattern. Let’s look at some examples. - Merge tweets in twitter feed: Sometimes we need to implement a module that adds a user’s Tweets into an already populated Twitter feed in chronological order. - Used in external sorting procedures: When an algorithm is processing huge amounts of data, it needs to repeatedly fetch it from external storage because RAM capacity is fixed. To overcome the speed limitation of external storage, k-way merges are used in external sorting. Let’s consider a case where we need to perform six merges. A binary merge requires three merge passes while a 6-way merge only requires one pass. K-way merge reduces the number of accesses to external storage, which in turn greatly improves performance when dealing with large amounts of data. ## Practice problems for k-way Merge - Merge Sorted Array - Kth smallest number in M sorted list - Find K pairs with smallest sums - Merge K sorted lists - Kth Smallest element in a sorted matrix - Median of two sorted arrays # Pattern 8: Top K Elements The top K elements pattern helps find some specific k number of elements from the given data with optimum time complexity. Many problems ask us to find the top, the smallest, or the most/least frequent k elements in an unsorted list of elements. To solve such problems, sorting the list takes O(nlog(n)) time, then finding the k elements takes O(k) time. However, the top k elements pattern can allow us to solve the problem using O(n logk) time without sorting the list first. Which data structure can we use to solve such problems? The best data structure to keep track of the smallest or largest k elements is heap. With this pattern, we either use a max-heap or a min-heap to find the smallest or largest k elements, respectively. For example, let’s look at how this pattern takes steps to solve the problem of finding the top k largest elements (using min-heap) or top k smallest elements (using max-heap): Insert the first k elements from the given set of elements to the min-heap or max-heap. Iterate through the rest of the elements. For min-heap, if you find the larger element, remove the top (smallest number) of the min-heap and insert the new larger element. For max-heap, if you find the smaller element, remove the top (largest number) of the max-heap and insert the new smaller element. Iterating the complete list takes O(n) time, and the heap takes O(logk) time for insertion. However, we get the O(1) access to the k elements using the heap. Many problems in the real world use the top K elements pattern. Let’s look at some examples. - Uber: Select at least the n nearest drivers within the user’s vicinity, avoiding the drivers that are too far away. - Stocks: Given the set of IDs of brokers, determine the top K broker’s performance with the frequently repeated IDs in the given data set. ## Practice problems for Top K Elements - Kth largest element in a stream - Reorganize string - K closest point to origin - Top K frequent element - Kth largest element in an array - Kth smallest element in an BST ================================================ FILE: Recursion/calculatextopowern.cpp ================================================ // Canculate power of x^n // Sample Input : 3 2 // Output : 9 #include using namespace std; // Method 1 Time complexity O(n) int power1(int x, int n){ if(n == 0) return 1; return x * power1(x, n-1); } // Method 2 : Time complexity O(log n) int power(int x, int n){ if(n == 0) return 1; else if(n % 2 == 0){ int y = power(x, n / 2); return y * y; } return x * power(x, n - 1); } int main(){ int x, n; cout << "Give me x annd n"; cin >> x >> n; int result = power(x,n); cout << result << endl; return 0; } ================================================ FILE: Recursion/count_digits.cpp ================================================ // Count the number of digits in an integer // Sample Input: 2003 // Output: 4 #include using namespace std; int count_digits(int n){ if(n == 0) return 0; // recursively hit base case and keep adding 1 afterwards int small_no = count_digits(n / 10); return small_no + 1; } int main(){ cout << count_digits(2003099910); return 0; } ================================================ FILE: Recursion/count_digits.go ================================================ // Count the number of digits in an integer // Sample Input: 2003 // Output: 4 package main import "fmt" func CountDigits(n int) int { if n == 0 { return 0; } // recursively hit base case and keep adding 1 afterwards smallNo := CountDigits(n / 10) return smallNo + 1 } func main() { msg := CountDigits(2003) fmt.Println(msg) } ================================================ FILE: Recursion/count_zeros.cpp ================================================ // Count the number of 0's in an integer // Sample Input : 10010 // Output: 3 #include using namespace std; int count_zeros(int n){ if(n == 0) return 0; // divide the number recursively and hit the base case int small_no = count_zeros(n / 10); int last_digit = n % 10; if(last_digit == 0) return small_no + 1; else return small_no; } int main(){ cout << count_zeros(2003010); return 0; } ================================================ FILE: Recursion/count_zeros.go ================================================ // Count the number of 0's in an integer // Sample Input : 10010 // Output: 3 package main import "fmt" func CountZeroes(n int) int { if n == 0 { return 0; } // divide the number recursively and hit the base case smallNo := CountZeroes(n / 10) lastDigit := n % 10 if lastDigit == 0 { smallNo += 1 } else { return smallNo } return smallNo } func main() { msg := CountZeroes(1000100100100) fmt.Println(msg) } ================================================ FILE: Recursion/factorial.cpp ================================================ // Factorial of an integer // Sample Input: 5 // Output: 120 #include using namespace std; int factorial(int n){ cout << "I am calculating f(" << n << ")\n"; if(n == 0) return 1; int f = n * factorial(n - 1); cout << "Done! f(" << n << ") = " << f << endl; return f; } int main(){ int n; cout << "Give me n : "; cin >> n; int result = factorial(n); cout << result << endl; return 0; } ================================================ FILE: Recursion/factorial.go ================================================ // Factorial of an integer // Sample Input: 5 // Output: 120 package main import "fmt" func Factorial(n int) int { if n == 0 { return 1; } return n * Factorial(n - 1) } func main() { msg := Factorial(5); fmt.Println(msg) } ================================================ FILE: Recursion/factorial.js ================================================ // Factorial of an integer // Sample Input: 5 // Output: 120 function recursiveFactorial(number) { //Base case if (number == 1) { return 1; } //recursively calling function to get factorial result return number * recursiveFactorial(number - 1); } // Driver code console.log(recursiveFactorial(2)); console.log(recursiveFactorial(3)); console.log(recursiveFactorial(4)); console.log(recursiveFactorial(5)); ================================================ FILE: Recursion/fibonacci.cpp ================================================ // Fibonacci numbers, commonly denoted Fn , form a sequence, the Fibonacci sequence, // in which each number is the sum of the two preceding ones. // The sequence commonly starts from 0 and 1, although some authors start the sequence // from 1 and 1 or sometimes (as did Fibonacci) from 1 and 2. Starting from 0 and 1, // the first few values in the sequence are: // 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144. // Sample Input: 5 // Output: 5 // Sample Input: 7 // Output: 13 #include using namespace std; int fib(int n){ if(n <= 1) return n; return fib(n-1) + fib(n-2); } int main(){ int n; cout << "Give me n : "; cin >> n; int result = fib(n); cout << result << endl; } ================================================ FILE: Recursion/fibonacci.go ================================================ // Fibonacci numbers, commonly denoted Fn , form a sequence, the Fibonacci sequence, // in which each number is the sum of the two preceding ones. // The sequence commonly starts from 0 and 1, although some authors start the sequence // from 1 and 1 or sometimes (as did Fibonacci) from 1 and 2. Starting from 0 and 1, // the first few values in the sequence are: // 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144. // Sample Input: 5 // Output: 5 // Sample Input: 7 // Output: 13 package main import "fmt" func Fibonacci(n int) int { if n <= 1 { return n } return Fibonacci(n - 1) + Fibonacci(n - 2) } func main() { msg := Fibonacci(7) fmt.Println(msg) } ================================================ FILE: Recursion/fibonacci_memoization.cpp ================================================ /* Implelement memoized fibonacci Memoization is an enhancement procedure used to speed up computer programs by keeping the values of distinct function calls and returning the stored input when the same function is invoked again. Sample Input: 6 Output: 8 */ #include using namespace std; int F[51]; int fib(int n){ if(F[n] != -1) return F[n]; F[n] = fib(n - 1) + fib(n-2); return F[n]; } int main(){ for(int i = 0; i < 51; i++) F[i] = -1; F[0] = 0; F[1] = 1; int n; cout << "Give me n : "; cin >> n; int result = fib(n); cout << result; return 0; } ================================================ FILE: Recursion/fibonacci_memoization.go ================================================ /* Implelement memoized fibonacci Memoization is an enhancement procedure used to speed up computer programs by keeping the values of distinct function calls and returning the stored input when the same function is invoked again. Sample Input: 6 Output: 8 */ package main import "fmt" var F = make([]int, 51) // array based implementation 0th fib = 1 func Fibonacci(n int) int { if F[n] != -1 { return F[n] } F[n] = Fibonacci(n - 1) + Fibonacci(n - 2) return F[n] } // more optimized 1st fibonacci = 0, second = 1 func GetNthFib(n int) int { firstNo := 0 secondNo := 1 result := 0 if n <= 2 { return n - 1 } for i := 2; i < n; i++ { // move first and second to to next iteration result = firstNo + secondNo firstNo = secondNo secondNo = result } return result } func main() { for i := 0; i < 51; i++ { F[i] = -1 } F[0] = 0 F[1] = 1 msg := Fibonacci(5) fmt.Println(msg) msg = GetNthFib(5) fmt.Println(msg) } ================================================ FILE: Recursion/geometric_sum.cpp ================================================ #include using namespace std; double g_sum(int m){ if(m == 0) return 1; double small_num = g_sum(m - 1); return small_num + 1.0 / pow(2, m); } int main(){ cout << g_sum(2); return 0; } ================================================ FILE: Recursion/is_array_sorted.cpp ================================================ // Given an array, check whether the array is in sorted order with recursion. // Sample Input: [1, 2, 3, 4, 4, 5, 3] // Output: 0 meaning false // Sample Output: [1, 2, 3, 4, 4, 5, 6] // Output: 1 meaning true #include using namespace std; bool is_sorted(int A[], int m){ if(m == 0 || m == 1) return true; if(A[0] > A[1]) return false; bool small_array = is_sorted(A + 1, m - 1); return small_array; } int main(){ int A[7] = {1, 2, 3, 4, 4, 5, 3}; cout << is_sorted(A, 7); return 0; } ================================================ FILE: Recursion/is_array_sorted.go ================================================ // Given an array, check whether the array is in sorted order with recursion. package main import "fmt" // Time Complexity: O(n). Space Complexity: O(n) for recursive stack space. func isSorted(A []int) bool { n := len(A) // Base case 1 if n == 1 { return true } // Base case 2 if A[n - 1] < A[n - 2] { return false } // recursive case return isSorted(A[:n-1]) } func main() { A := []int{10, 20, 23, 23, 45, 78, 88} fmt.Println(isSorted(A)) A = []int{10, 20, 23, 7, 23, 45, 78, 88} fmt.Println(isSorted(A)) } ================================================ FILE: Recursion/is_element_present.cpp ================================================ // Program to check if an element is present in an array // Sample Input: [1, 2, 3, 4, 4, 5, 3] key: 51 // Output: 0 meaning false // Sample Input: [1, 2, 3, 4, 4, 5, 3] key: 5 // Output: 1 meaning true #include using namespace std; bool is_element_present(int A[], int m, int key){ if(m == 0) return false; if(A[0] == key) return true; return is_element_present(A + 1, m - 1, key); } bool is_element_present2(int A[], int m, int key, int i){ if(i == m) return false; if(A[i] == key) return true; return is_element_present2(A, m, key, i + 1); } int main(){ int A[7] = {1, 2, 3, 4, 4, 5, 3}; int key = 51; cout << is_element_present(A, 7, key) << endl; cout << is_element_present2(A, 7, 5, 0) << endl; return 0; } ================================================ FILE: Recursion/modular_exponentiation.cpp ================================================ // Modular exponentiation is exponentiation performed over a modulus. // It is useful in computer science, especially in the field of public-key cryptography, // where it is used in both Diffie-Hellman Key Exchange and RSA public/private keys. // Sample Input : 2 3 100 // Output: 8 #include using namespace std; int mod(int x, int n, int m){ if(n == 0) return 1; else if( n % 2 == 0){ int y = mod(x, n/2, m); return y * y % m; } return ((x % m) * mod(x, n-1, m)) % m; } int main(){ int x, n, m; cout << "Give me x, n and m : "; cin >> x >> n >> m; int result = mod(x, n, m); cout << result << endl; return 0; } ================================================ FILE: Recursion/multiplication.cpp ================================================ // Multiply number using recursion // Sample Input: 7, 9 // Output: 63 #include using namespace std; int multiply(int n, int m){ if(m == 0) return 0; int small_m = multiply(n, m-1); return n + small_m; } int main(){ cout << multiply(7, 9); return 0; } ================================================ FILE: Recursion/powerset.cpp ================================================ /* Write a function that takes in an array of unique integers and returns its powerset. Sample Input : [1, 2, 3] Output: [[], [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]] Explanation: The code snippet represents a function `Powerset` that takes an array of integers as input and returns the powerset of the array. The powerset of a set is the set of all possible subsets of that set, including the empty set and the set itself. The function `Powerset` calls a helper function `powerset` to perform the actual computation. Here's how the code works: 1. The `Powerset` function initializes the computation by calling the `powerset` function with the array and the index of the last element in the array (`len(array) - 1`). 2. The `powerset` function is a recursive function that calculates the powerset. It takes the array and the current index as input. 3. At each recursive call, the function checks if the index is less than 0. If so, it means all elements have been processed, and it returns a 2D slice containing the empty set as the only subset. 4. If the index is not less than 0, the function retrieves the element at the current index from the array. 5. The function recursively calls itself with the array and the index decremented by 1 to generate the subsets without the current element. 6. It then calculates the length of the subsets generated so far. 7. Using a loop, the function iterates over the existing subsets and creates new subsets by appending the current element to each subset. The new subsets are added to the existing subset slice. 8. Finally, the function returns the updated subset slice, which contains all the subsets of the original array. By recursively generating subsets while building upon the subsets generated at each step, the function constructs the powerset of the given array. O(n*2^n) time | O(n*2^n) space - where n is the length of the input array */ #include using namespace std; vector> powerset(vector& array, int index) { // Base case: when the index reaches -1, return a vector with an empty subset if (index < 0) { return {{}}; } int element = array[index]; // Recursive call to generate the powerset for the elements up to index - 1 vector> subset = powerset(array, index - 1); // Length of the current subset vector int length = subset.size(); // Iterate through each subset and create new subsets by adding the current element for (int i = 0; i < length; i++) { vector currentSubset = subset[i]; // Create a new subset by making a copy of the current subset vector newSubset = currentSubset; // Add the current element to the new subset newSubset.push_back(element); // Add the new subset to the existing subset vector subset.push_back(newSubset); } // Return the resulting subset vector return subset; } vector> Powerset(vector& array) { // Call the helper function to generate the powerset starting from the last index return powerset(array, array.size() - 1); } // Iterative approach vector> powerset(vector& array) { vector> subset; subset.push_back({}); // Initialize the powerset with the empty subset // Iterate over each element in the input array for (int ele : array) { int length = subset.size(); // Get the current length of the subset // Iterate over each existing subset for (int i = 0; i < length; i++) { vector currentSubset = subset[i]; // Get the current subset // Create a new subset by making a copy of the current subset vector newSubset = currentSubset; // Add the current element to the new subset newSubset.push_back(ele); // Append the new subset to the powerset subset.push_back(newSubset); } } // Return the powerset return subset; } ================================================ FILE: Recursion/powerset.go ================================================ /* Write a function that takes in an array of unique integers and returns its powerset. Sample Input : [1, 2, 3] Output: [[], [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]] Explanation: The code snippet represents a function `Powerset` that takes an array of integers as input and returns the powerset of the array. The powerset of a set is the set of all possible subsets of that set, including the empty set and the set itself. The function `Powerset` calls a helper function `powerset` to perform the actual computation. Here's how the code works: 1. The `Powerset` function initializes the computation by calling the `powerset` function with the array and the index of the last element in the array (`len(array) - 1`). 2. The `powerset` function is a recursive function that calculates the powerset. It takes the array and the current index as input. 3. At each recursive call, the function checks if the index is less than 0. If so, it means all elements have been processed, and it returns a 2D slice containing the empty set as the only subset. 4. If the index is not less than 0, the function retrieves the element at the current index from the array. 5. The function recursively calls itself with the array and the index decremented by 1 to generate the subsets without the current element. 6. It then calculates the length of the subsets generated so far. 7. Using a loop, the function iterates over the existing subsets and creates new subsets by appending the current element to each subset. The new subsets are added to the existing subset slice. 8. Finally, the function returns the updated subset slice, which contains all the subsets of the original array. By recursively generating subsets while building upon the subsets generated at each step, the function constructs the powerset of the given array. O(n*2^n) time | O(n*2^n) space - where n is the length of the input array */ package main func Powerset(array []int) [][]int { // Call the powerset helper function to compute the powerset of the array return powerset(array, len(array)-1) } func powerset(array []int, index int) [][]int { // Base case: If the index is less than 0, all elements have been processed, return the empty set if index < 0 { return [][]int{{}} } // Retrieve the element at the current index element := array[index] // Recursively call the powerset function with the array and the index decremented by 1 subset := powerset(array, index-1) // Calculate the length of the current subset length := len(subset) // Iterate over the existing subsets and create new subsets by appending the current element for i := 0; i < length; i++ { currentSubset := subset[i] newSubset := append([]int{}, currentSubset...) // Create a new subset by making a copy of the current subset newSubset = append(newSubset, element) // Append the current element to the new subset subset = append(subset, newSubset) // Add the new subset to the existing subset slice } // Return the updated subset slice containing all the subsets of the original array return subset } // Iterative approach func PowersetIterative(array []int) [][]int { // Initialize the powerset with the empty subset subset := [][]int{{}} // Iterate over each element in the input array for _, ele := range array { // Get the current length of the subset length := len(subset) // Iterate over each existing subset for i := 0; i < length; i++ { // Get the current subset currentSubset := subset[i] // Create a new subset by making a copy of the current subset newSubset := append([]int{}, currentSubset...) // Add the current element to the new subset newSubset = append(newSubset, ele) // Append the new subset to the powerset subset = append(subset, newSubset) } } // Return the powerset return subset } ================================================ FILE: Recursion/powerset.java ================================================ /* Write a function that takes in an array of unique integers and returns its powerset. Sample Input : [1, 2, 3] Output: [[], [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]] Explanation: The code snippet represents a function `Powerset` that takes an array of integers as input and returns the powerset of the array. The powerset of a set is the set of all possible subsets of that set, including the empty set and the set itself. The function `Powerset` calls a helper function `powerset` to perform the actual computation. Here's how the code works: 1. The `Powerset` function initializes the computation by calling the `powerset` function with the array and the index of the last element in the array (`len(array) - 1`). 2. The `powerset` function is a recursive function that calculates the powerset. It takes the array and the current index as input. 3. At each recursive call, the function checks if the index is less than 0. If so, it means all elements have been processed, and it returns a 2D slice containing the empty set as the only subset. 4. If the index is not less than 0, the function retrieves the element at the current index from the array. 5. The function recursively calls itself with the array and the index decremented by 1 to generate the subsets without the current element. 6. It then calculates the length of the subsets generated so far. 7. Using a loop, the function iterates over the existing subsets and creates new subsets by appending the current element to each subset. The new subsets are added to the existing subset slice. 8. Finally, the function returns the updated subset slice, which contains all the subsets of the original array. By recursively generating subsets while building upon the subsets generated at each step, the function constructs the powerset of the given array. O(n*2^n) time | O(n*2^n) space - where n is the length of the input array */ import java.util.ArrayList; import java.util.List; class Main { public static List> powerset(int[] array, int index) { // Base case: when the index reaches -1, return a list with an empty subset if (index < 0) { List> subsets = new ArrayList<>(); subsets.add(new ArrayList<>()); return subsets; } int element = array[index]; // Recursive call to generate the powerset for the elements up to index - 1 List> subsets = powerset(array, index - 1); // Length of the current subsets list int length = subsets.size(); // Iterate through each subset and create new subsets by adding the current element for (int i = 0; i < length; i++) { List currentSubset = subsets.get(i); // Create a new subset by making a copy of the current subset List newSubset = new ArrayList<>(currentSubset); // Add the current element to the new subset newSubset.add(element); // Add the new subset to the existing subsets list subsets.add(newSubset); } // Return the resulting subsets list return subsets; } public static List> powerset(int[] array) { // Call the helper function to generate the powerset starting from the last index return powerset(array, array.length - 1); } public static void main(String[] args) { int[] array = {1, 2, 3}; List> subsets = powerset(array); // Print the subsets for (List subset : subsets) { System.out.println(subset); } } } // Iterative approach public class Powerset { public static List> powerset(int[] array) { List> subset = new ArrayList<>(); subset.add(new ArrayList<>()); // Initialize the powerset with the empty subset // Iterate over each element in the input array for (int ele : array) { int length = subset.size(); // Get the current length of the subset // Iterate over each existing subset for (int i = 0; i < length; i++) { List currentSubset = new ArrayList<>(subset.get(i)); // Get the current subset // Create a new subset by making a copy of the current subset List newSubset = new ArrayList<>(currentSubset); // Add the current element to the new subset newSubset.add(ele); // Append the new subset to the powerset subset.add(newSubset); } } // Return the powerset return subset; } public static void main(String[] args) { int[] array = {1, 2, 3}; List> result = powerset(array); System.out.println(result); } } ================================================ FILE: Recursion/powerset.js ================================================ /* Write a function that takes in an array of unique integers and returns its powerset. Sample Input : [1, 2, 3] Output: [[], [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]] Explanation: The code snippet represents a function `Powerset` that takes an array of integers as input and returns the powerset of the array. The powerset of a set is the set of all possible subsets of that set, including the empty set and the set itself. The function `Powerset` calls a helper function `powerset` to perform the actual computation. Here's how the code works: 1. The `Powerset` function initializes the computation by calling the `powerset` function with the array and the index of the last element in the array (`len(array) - 1`). 2. The `powerset` function is a recursive function that calculates the powerset. It takes the array and the current index as input. 3. At each recursive call, the function checks if the index is less than 0. If so, it means all elements have been processed, and it returns a 2D slice containing the empty set as the only subset. 4. If the index is not less than 0, the function retrieves the element at the current index from the array. 5. The function recursively calls itself with the array and the index decremented by 1 to generate the subsets without the current element. 6. It then calculates the length of the subsets generated so far. 7. Using a loop, the function iterates over the existing subsets and creates new subsets by appending the current element to each subset. The new subsets are added to the existing subset slice. 8. Finally, the function returns the updated subset slice, which contains all the subsets of the original array. By recursively generating subsets while building upon the subsets generated at each step, the function constructs the powerset of the given array. O(n*2^n) time | O(n*2^n) space - where n is the length of the input array */ function powerset(array, index) { // Base case: when the index reaches -1, return a list with an empty subset if (index < 0) { return [[]]; } const element = array[index]; // Recursive call to generate the powerset for the elements up to index - 1 const subsets = powerset(array, index - 1); // Create new subsets by adding the current element to existing subsets const length = subsets.length; for (let i = 0; i < length; i++) { const currentSubset = subsets[i]; // Create a new subset by making a copy of the current subset const newSubset = [...currentSubset]; // Add the current element to the new subset newSubset.push(element); // Add the new subset to the existing subsets array subsets.push(newSubset); } // Return the resulting subsets array return subsets; } function getPowerSet(array) { // Call the helper function to generate the powerset starting from the last index return powerset(array, array.length - 1); } // Example usage const array = [1, 2, 3]; const subsets = getPowerSet(array); // Print the subsets subsets.forEach((subset) => { console.log(subset); }); // Iterative approach function powersetIterative(array) { // Initialize the powerset with the empty subset let subset = [[]]; // Iterate over each element in the input array for (let ele of array) { // Get the current length of the subset let length = subset.length; // Iterate over each existing subset for (let i = 0; i < length; i++) { // Get the current subset let currentSubset = subset[i]; // Create a new subset by making a copy of the current subset let newSubset = [...currentSubset]; // Add the current element to the new subset newSubset.push(ele); // Append the new subset to the powerset subset.push(newSubset); } } // Return the powerset return subset; } ================================================ FILE: Recursion/powerset.py ================================================ ''' Write a function that takes in an array of unique integers and returns its powerset. Sample Input : [1, 2, 3] Output: [[], [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]] Explanation: The code snippet represents a function `Powerset` that takes an array of integers as input and returns the powerset of the array. The powerset of a set is the set of all possible subsets of that set, including the empty set and the set itself. The function `Powerset` calls a helper function `powerset` to perform the actual computation. Here's how the code works: 1. The `Powerset` function initializes the computation by calling the `powerset` function with the array and the index of the last element in the array (`len(array) - 1`). 2. The `powerset` function is a recursive function that calculates the powerset. It takes the array and the current index as input. 3. At each recursive call, the function checks if the index is less than 0. If so, it means all elements have been processed, and it returns a 2D slice containing the empty set as the only subset. 4. If the index is not less than 0, the function retrieves the element at the current index from the array. 5. The function recursively calls itself with the array and the index decremented by 1 to generate the subsets without the current element. 6. It then calculates the length of the subsets generated so far. 7. Using a loop, the function iterates over the existing subsets and creates new subsets by appending the current element to each subset. The new subsets are added to the existing subset slice. 8. Finally, the function returns the updated subset slice, which contains all the subsets of the original array. By recursively generating subsets while building upon the subsets generated at each step, the function constructs the powerset of the given array. O(n*2^n) time | O(n*2^n) space - where n is the length of the input array ''' def powerset(array): # Base case: when the index reaches -1, return a list with an empty subset if index < 0: return [[]] element = array[index] # Recursive call to generate the powerset for the elements up to index - 1 subset = powerset(array, index - 1) # Length of the current subset list length = len(subset) # Iterate through each subset and create new subsets by adding the current element for i in range(length): currentSubset = subset[i] # Create a new subset by making a copy of the current subset newSubset = currentSubset[:] # Add the current element to the new subset newSubset.append(element) # Add the new subset to the existing subset list subset.append(newSubset) # Return the resulting subset list return subset def Powerset(array): # Call the helper function to generate the powerset starting from the last index return powerset(array, len(array) - 1) # Iterative approach def powersetIterative(array): # Initialize the powerset with the empty subset subset = [[]] # Iterate over each element in the input array for ele in array: # Get the current length of the subset length = len(subset) # Iterate over each existing subset for i in range(length): # Get the current subset currentSubset = subset[i] # Create a new subset by making a copy of the current subset newSubset = list(currentSubset) # Add the current element to the new subset newSubset.append(ele) # Append the new subset to the powerset subset.append(newSubset) # Return the powerset return subset ================================================ FILE: Recursion/print_numbers.cpp ================================================ // Print numbers recursively // Sample Input: 5 // Output: 1 2 3 4 5 #include using namespace std; void print(int n){ if(n == 0) return; print(n - 1); cout << n << " "; } int main(){ print(5); return 0; } ================================================ FILE: Recursion/recursive_bubble_sort.cpp ================================================ // Implementation of Recursive Bubble sort. // Bubble sort, sometimes referred to as sinking sort, is a simple sorting algorithm // that repeatedly steps through the input list element by element, // comparing the current element with the one after it, swapping their values if needed. // These passes through the list are repeated until no swaps had to be performed during a pass, // meaning that the list has become fully sorted. (Source wiki) https://en.wikipedia.org/wiki/Bubble_sort // Time Complexity worst-case and average complexity O(n^{2}) // Bubble sort is O(n) on a list that is already sorted i.e. Best case // Sample Input : [2, 1, 9, 3, 5, 4, 0] // Output : [0 1 2 3 4 5 9] // Program Author : Abhisek Kumar Gupta #include using namespace std; void bubble_sort(int *A, int len){ if(len == 1) return; for(int i = 0; i < len - 1; i++){ if(A[i] > A[i+1]){ swap(A[i], A[i+1]); } } bubble_sort(A, len-1); } int main(){ int A[] = {5, 4, 3, 2, 1, 7}; int len = sizeof(A) / sizeof(int); bubble_sort(A, len); for(int x : A){ cout << x << " "; } return 0; } ================================================ FILE: Recursion/reverse_print.go ================================================ package main import "fmt" // Starting from n print number till 1 func print(n int) int { if n == 0 { // base case return 0 } fmt.Println(n) return print(n - 1) } func main() { print(10) } ================================================ FILE: Recursion/tower_of_hannoi.cpp ================================================ /* Towers of Hanoi puzzle. Source(https://en.wikipedia.org/wiki/Tower_of_Hanoi) Object of the game is to move all the disks over to Tower 3. But you cannot place a larger disk onto a smaller disk. Approach 1 Move the top 􀝊 − 1 disks from 􀜵􀝋􀝑􀝎􀜿􀝁 to 􀜣􀝑􀝔􀝅􀝈􀝅􀜽􀝎􀝕 tower, 2 Move the 􀝊􀯧􀯛 disk from 􀜵􀝋􀝑􀝎􀜿􀝁 to 􀜦􀝁􀝏􀝐􀝅􀝊􀜽􀝐􀝅􀝋􀝊 tower, 3 Move the 􀝊 − 1disks from 􀜣􀝑􀝔􀝅􀝈􀝅􀜽􀝎􀝕 tower to 􀜦􀝁􀝏􀝐􀝅􀝊􀜽􀝐􀝅􀝋􀝊 tower. */ #include void towerOfHanoi(int numDisks, char fromPeg, char toPeg, char auxPeg) { if (numDisks == 1) { std::cout << "Move disk 1 from peg " << fromPeg << " to peg " << toPeg << std::endl; } else { towerOfHanoi(numDisks - 1, fromPeg, auxPeg, toPeg); std::cout << "Move disk " << numDisks << " from peg " << fromPeg << " to peg " << toPeg << std::endl; towerOfHanoi(numDisks - 1, auxPeg, toPeg, fromPeg); } } int main() { towerOfHanoi(3, 'A', 'C', 'B'); return 0; } // This implementation uses recursion to solve the problem. The towerOfHanoi function takes four arguments: numDisks (the number of disks to move), fromPeg (the peg the disks start on), toPeg (the peg the disks should end up on), and auxPeg (the auxiliary peg used for moving the disks). // If numDisks is 1, the function simply moves the single disk from fromPeg to toPeg. Otherwise, it first moves numDisks - 1 disks from fromPeg to auxPeg using toPeg as the auxiliary peg. Then it moves the largest remaining disk from fromPeg to toPeg. Finally, it moves the numDisks - 1 disks from auxPeg to toPeg using fromPeg as the auxiliary peg. // The main function simply calls towerOfHanoi with the appropriate arguments (in this case, numDisks is 3 and the pegs are labeled A, B, and C). // When run, the program outputs the following to the console: // Move disk 1 from peg A to peg C // Move disk 2 from peg A to peg B // Move disk 1 from peg C to peg B // Move disk 3 from peg A to peg C // Move disk 1 from peg B to peg A // Move disk 2 from peg B to peg C // Move disk 1 from peg A to peg C // This output shows the sequence of moves that solve the Tower of Hanoi problem for 3 disks. ================================================ FILE: Recursion/tower_of_hannoi.java ================================================ /* The Tower of Hanoi problem is a classic problem in computer science and mathematics that involves moving a stack of disks from one peg to another peg. The problem consists of three pegs and a set of disks of different sizes that can slide onto any peg. The objective of the puzzle is to move the entire stack to another peg, obeying the following simple rules: Only one disk can be moved at a time. Each move consists of taking the upper disk from one of the stacks and placing it on top of another stack or an empty peg. No disk may be placed on top of a smaller disk. The Tower of Hanoi problem is often used as an example of recursive problem-solving. The solution to the problem can be divided into three parts: Move n-1 disks from the starting peg to the auxiliary peg. Move the largest disk from the starting peg to the destination peg. Move the n-1 disks from the auxiliary peg to the destination peg. This process is repeated recursively until all the disks have been moved from the starting peg to the destination peg. The number of moves required to solve the puzzle for n disks can be calculated using the formula 2^n - 1. Here's an implementation of the Tower of Hanoi problem in Java using recursion: */ public class TowerOfHanoi { public static void main(String[] args) { int n = 3; // number of disks char fromRod = 'A'; char toRod = 'C'; char auxRod = 'B'; System.out.println("Moves to solve Tower of Hanoi problem with " + n + " disks:"); solveHanoi(n, fromRod, toRod, auxRod); } public static void solveHanoi(int n, char fromRod, char toRod, char auxRod) { if (n == 1) { System.out.println("Move disk 1 from rod " + fromRod + " to rod " + toRod); return; } solveHanoi(n - 1, fromRod, auxRod, toRod); System.out.println("Move disk " + n + " from rod " + fromRod + " to rod " + toRod); solveHanoi(n - 1, auxRod, toRod, fromRod); } } // -----------------------Explanation------------------------------ // In this implementation, the solveHanoi method is called recursively to move the disks from one rod to another. The method takes in four parameters: // n: the number of disks // fromRod: the rod from which the disks are to be moved // toRod: the rod to which the disks are to be moved // auxRod: the auxiliary rod which can be used to move the disks // When the number of disks is 1, the method simply prints the move to be made. Otherwise, the method makes the following three recursive calls: // Move n-1 disks from fromRod to auxRod using toRod as the auxiliary rod. // Move the nth disk from fromRod to toRod. // Move the n-1 disks from auxRod to toRod using fromRod as the auxiliary rod. // This process is repeated recursively until all the disks have been moved from the starting rod to the destination rod. ================================================ FILE: Recursion/tower_of_hannoi.js ================================================ /* Towers of Hanoi puzzle. Source(https://en.wikipedia.org/wiki/Tower_of_Hanoi) Object of the game is to move all the disks over to Tower 3. But you cannot place a larger disk onto a smaller disk. Approach 1 Move the top 􀝊 − 1 disks from 􀜵􀝋􀝑􀝎􀜿􀝁 to 􀜣􀝑􀝔􀝅􀝈􀝅􀜽􀝎􀝕 tower, 2 Move the 􀝊􀯧􀯛 disk from 􀜵􀝋􀝑􀝎􀜿􀝁 to 􀜦􀝁􀝏􀝐􀝅􀝊􀜽􀝐􀝅􀝋􀝊 tower, 3 Move the 􀝊 − 1disks from 􀜣􀝑􀝔􀝅􀝈􀝅􀜽􀝎􀝕 tower to 􀜦􀝁􀝏􀝐􀝅􀝊􀜽􀝐􀝅􀝋􀝊 tower. */ function towerOfHanoi(numDisks, fromPeg, toPeg, auxPeg) { if (numDisks === 1) { console.log(`Move disk 1 from peg ${fromPeg} to peg ${toPeg}`); } else { towerOfHanoi(numDisks - 1, fromPeg, auxPeg, toPeg); console.log(`Move disk ${numDisks} from peg ${fromPeg} to peg ${toPeg}`); towerOfHanoi(numDisks - 1, auxPeg, toPeg, fromPeg); } } // Example usage: towerOfHanoi(3, "A", "C", "B"); // Move disk 1 from peg A to peg C, Move disk 2 from peg A to peg B, Move disk 1 from peg C to peg B, Move disk 3 from peg A to peg C, Move disk 1 from peg B to peg A, Move disk 2 from peg B to peg C, Move disk 1 from peg A to peg C // This implementation uses recursion to solve the problem. The towerOfHanoi function takes four arguments: numDisks (the number of disks to move), fromPeg (the peg the disks start on), toPeg (the peg the disks should end up on), and auxPeg (the auxiliary peg used for moving the disks). // If numDisks is 1, the function simply moves the single disk from fromPeg to toPeg. Otherwise, it first moves numDisks - 1 disks from fromPeg to auxPeg using toPeg as the auxiliary peg. Then it moves the largest remaining disk from fromPeg to toPeg. Finally, it moves the numDisks - 1 disks from auxPeg to toPeg using fromPeg as the auxiliary peg. // The function prints out the moves it makes to the console, so running towerOfHanoi(3, "A", "C", "B") would output: // Move disk 1 from peg A to peg C // Move disk 2 from peg A to peg B // Move disk 1 from peg C to peg B // Move disk 3 from peg A to peg C // Move disk 1 from peg B to peg A // Move disk 2 from peg B to peg C // Move disk 1 from peg A to peg C ================================================ FILE: Recursion/tower_of_hannoi.py ================================================ ''' Towers of Hanoi puzzle. Source(https://en.wikipedia.org/wiki/Tower_of_Hanoi) Object of the game is to move all the disks over to Tower 3. But you cannot place a larger disk onto a smaller disk. Approach 1 Move the top 􀝊 − 1 disks from 􀜵􀝋􀝑􀝎􀜿􀝁 to 􀜣􀝑􀝔􀝅􀝈􀝅􀜽􀝎􀝕 tower, 2 Move the 􀝊􀯧􀯛 disk from 􀜵􀝋􀝑􀝎􀜿􀝁 to 􀜦􀝁􀝏􀝐􀝅􀝊􀜽􀝐􀝅􀝋􀝊 tower, 3 Move the 􀝊 − 1disks from 􀜣􀝑􀝔􀝅􀝈􀝅􀜽􀝎􀝕 tower to 􀜦􀝁􀝏􀝐􀝅􀝊􀜽􀝐􀝅􀝋􀝊 tower. ''' #Tower of Hanoi explanation - https://youtu.be/YstLjLCGmgg def TowerOfHanoi(n, A,B,C): if n == 0: return TowerOfHanoi(n-1, A,C,B) print("Move disk", n, "from", A, "to rod", B) TowerOfHanoi(n-1, C, B, A) ================================================ FILE: Recursion/tower_of_hanoi.go ================================================ /* Towers of Hanoi puzzle. Source(https://en.wikipedia.org/wiki/Tower_of_Hanoi) Object of the game is to move all the disks over to Tower 3. But you cannot place a larger disk onto a smaller disk. */ /* Approach 1 Move the top 􀝊 − 1 disks from 􀜵􀝋􀝑􀝎􀜿􀝁 to 􀜣􀝑􀝔􀝅􀝈􀝅􀜽􀝎􀝕 tower, 2 Move the 􀝊􀯧􀯛 disk from 􀜵􀝋􀝑􀝎􀜿􀝁 to 􀜦􀝁􀝏􀝐􀝅􀝊􀜽􀝐􀝅􀝋􀝊 tower, 3 Move the 􀝊 − 1disks from 􀜣􀝑􀝔􀝅􀝈􀝅􀜽􀝎􀝕 tower to 􀜦􀝁􀝏􀝐􀝅􀝊􀜽􀝐􀝅􀝋􀝊 tower. */ package main import "fmt" func TowerOfHanoiHelper(n int, from, to, temp string) { // Base case // If only 1 disk, make the move and return if n == 1 { fmt.Println("Move disk ", n, " from peg ", from, " to peg ", to) return } // Move top n-1 disks from A to B, using C as auxiliary TowerOfHanoiHelper(n-1, from, temp, to) // Move remaining disks from A to C fmt.Println("Move disk ", n, " from peg ", from, " to peg ", to) // Move n-1 disks from B to C using A as auxiliary TowerOfHanoiHelper(n-1, temp, to, from) } func TowersOfHanoi(n int) { TowerOfHanoiHelper(n, "A", "C", "B") } func main() { TowersOfHanoi(3) } ================================================ FILE: Recursion/valid_palindrome_2.py ================================================ ''' Write a function that takes a string as input and checks whether it can be a valid palindrome by removing at most one character from it. Constraints: string.length The string only consists of English letters Sample Input : "madame" Output : True Sample Input : "masdasd" Output : False ''' class Solution: def subPalindrome(self,s,low,high,count): if(low>high): return True if(count>1): return False if(s[low]!=s[high]): return Solution.subPalindrome(self,s,low+1,high,count+1) or Solution.subPalindrome(self,s,low,high-1,count+1) else: return Solution.subPalindrome(self,s,low+1,high-1,count) def validPalindrome(self, s: str) -> bool: if(s==s[::-1]): return True return Solution.subPalindrome(self,s,0,len(s)-1,0) print(Solution().validPalindrome("ebcbbececabbacecbbcbe")) print(Solution().validPalindrome("cdbeeeabddddbaeedebdc")) ================================================ FILE: SECURITY.md ================================================ # Security Policy ================================================ FILE: Scheduling/fcfs.c ================================================ /* The First-Come, First-Served (FCFS) scheduling algorithm is one of the simplest process scheduling algorithms used in operating systems. It follows the principle of serving processes in the order they arrive in the ready queue. In FCFS, the process that arrives first will be the first one to be executed, and it continues until the process completes its execution or enters a waiting state. Algorithm Description: When a process enters the ready queue, it is added to the end of the queue (at the "tail" of the queue). The CPU scheduler selects the process at the front of the queue (the "head" of the queue) for execution. The selected process runs on the CPU until it completes its execution, enters a waiting state (e.g., I/O operation), or its time quantum (if there is a time-sharing system) expires. Once the currently running process finishes, the CPU scheduler selects the process at the front of the queue as the next process to run. This process will continue until it completes or enters a waiting state, and so on. This process of selecting and executing processes continues until all processes in the queue have executed. Example: Let's illustrate the FCFS algorithm with a simple example. Consider three processes, P1, P2, and P3, with their respective burst times (the time they need to complete execution): Process P1: Burst time = 2 ms Process P2: Burst time = 7 ms Process P3: Burst time = 4 ms Here's how FCFS will schedule and execute these processes: Initially, the ready queue is empty. Process P1 arrives first, so it is added to the ready queue: Ready Queue: [P1] Execution: P1 (2 ms) Process P1 completes its execution. Now, process P2 is selected because it's at the front of the queue: Ready Queue: [P2] Execution: P2 (7 ms) Process P2 completes its execution. Finally, process P3 is selected: Ready Queue: [P3] Execution: P3 (4 ms) Process P3 completes its execution. The order of execution in FCFS is P1 -> P2 -> P3. FCFS is non-preemptive, meaning once a process starts execution, it continues until it finishes or enters a waiting state. It can suffer from the "convoy effect," where a long process at the head of the queue can block shorter processes behind it. */ #include #include // Struct for the info of each process typedef struct { int process_id; int arrival_time; int burst_time; } process; int main() { // Number of processes int number=3; // Size of the array which will show the order of execution of the processes process *arr; arr = malloc(number * sizeof(process)); // Initialize for our example arr[0].process_id = 1; arr[0].arrival_time = 0; arr[0].burst_time = 2; arr[1].process_id = 2; arr[1].arrival_time = 1; arr[1].burst_time = 7; arr[2].process_id = 3; arr[2].arrival_time = 3; arr[2].burst_time = 4; //Sorting the struct by arrival time process temp; for(int i=0; i P2 -> P3. SJF aims to minimize the average waiting time for processes, making it an effective algorithm when you have information about the burst times of processes. However, it can suffer from starvation if a long job continually arrives after shorter jobs. */ #include #include // Struct for the info of each process typedef struct { int process_id; int arrival_time; int burst_time; } process; int main() { // Number of processes int number=3; // Size of the array which will show the order of execution of the processes process *arr; arr = malloc(number * sizeof(process)); // Initialize for our example arr[0].process_id = 1; arr[0].arrival_time = 0; arr[0].burst_time = 7; arr[1].process_id = 2; arr[1].arrival_time = 2; arr[1].burst_time = 3; arr[2].process_id = 3; arr[2].arrival_time = 4; arr[2].burst_time = 8; //Sorting the struct by arrival time process temp; for(int i=0; i= arr[j].arrival_time && arr[j].burst_time <= min) { temp = arr[k]; arr[k] = arr[j]; arr[j] = temp; } } k++; //increase index } //Variable for the sum of burst time int sum_of_burst_time=0; //Calculate sum of burst time for (int i=0; i P2 -> P1 -> P3. SRTF provides optimal turnaround times but may suffer from frequent context switches due to its preemptive nature. */ #include #include // Struct for the info of each process typedef struct { int process_id; int arrival_time; int burst_time; } process; int main() { // Number of processes int number=3; // Size of the array which will show the order of execution of the processes process *arr; arr = malloc(number * sizeof(process)); // Initialize for our example arr[0].process_id = 1; arr[0].arrival_time = 0; arr[0].burst_time = 7; arr[1].process_id = 2; arr[1].arrival_time = 2; arr[1].burst_time = 3; arr[2].process_id = 3; arr[2].arrival_time = 4; arr[2].burst_time = 8; //Sorting the struct by arrival time process temp; for (int i=0; i P2 -> P3. FCFS is non-preemptive, meaning once a process starts execution, it continues until it finishes or enters a waiting state. It can suffer from the "convoy effect," where a long process at the head of the queue can block shorter processes behind it. */ #include #include // Struct for the info of each process typedef struct { int process_id; int arrival_time; int burst_time; } process; int main() { // Number of processes int number=3; // Size of the array which will show the order of execution of the processes process *arr; arr = malloc(number * sizeof(process)); // Initialize for our example arr[0].process_id = 1; arr[0].arrival_time = 0; arr[0].burst_time = 2; arr[1].process_id = 2; arr[1].arrival_time = 1; arr[1].burst_time = 7; arr[2].process_id = 3; arr[2].arrival_time = 3; arr[2].burst_time = 4; //Sorting the struct by arrival time process temp; for(int i=0; i P2 -> P3. SJF aims to minimize the average waiting time for processes, making it an effective algorithm when you have information about the burst times of processes. However, it can suffer from starvation if a long job continually arrives after shorter jobs. */ #include #include // Struct for the info of each process typedef struct { int process_id; int arrival_time; int burst_time; } process; int main() { // Number of processes int number=3; // Size of the array which will show the order of execution of the processes process *arr; arr = malloc(number * sizeof(process)); // Initialize for our example arr[0].process_id = 1; arr[0].arrival_time = 0; arr[0].burst_time = 7; arr[1].process_id = 2; arr[1].arrival_time = 2; arr[1].burst_time = 3; arr[2].process_id = 3; arr[2].arrival_time = 4; arr[2].burst_time = 8; //Sorting the struct by arrival time process temp; for(int i=0; i= arr[j].arrival_time && arr[j].burst_time <= min) { temp = arr[k]; arr[k] = arr[j]; arr[j] = temp; } } k++; //increase index } //Variable for the sum of burst time int sum_of_burst_time=0; //Calculate sum of burst time for (int i=0; i P2 -> P1 -> P3. SRTF provides optimal turnaround times but may suffer from frequent context switches due to its preemptive nature. */ #include #include // Struct for the info of each process typedef struct { int process_id; int arrival_time; int burst_time; } process; int main() { // Number of processes int number=3; // Size of the array which will show the order of execution of the processes process *arr; arr = malloc(number * sizeof(process)); // Initialize for our example arr[0].process_id = 1; arr[0].arrival_time = 0; arr[0].burst_time = 7; arr[1].process_id = 2; arr[1].arrival_time = 2; arr[1].burst_time = 3; arr[2].process_id = 3; arr[2].arrival_time = 4; arr[2].burst_time = 8; //Sorting the struct by arrival time process temp; for (int i=0; i { // firstly iterate/loop through the given array for(let i = 0; i < arr.length; i++){ // then use the Array.prototype.lastIndexOf() method to check for duplicates. // finally return the first number that appers more than once. if(arr.lastIndexOf(arr[i]) !== i) return arr[i]; }; // return the message No duplicate found, if no dupliacte is found after the iteration process. return "No duplicate found!"; } console.log(firstDuplicate1([2, 1, 5, 2, 3, 3, 4])); // Big-O = O(n) time complexity // Big-O = O(n) space complexity const firstDuplicate2 = arr => { // first off, let's create our Set object // this Set object will allow us to store each element from the given array as a unique value let elementSet = new Set(); // then iterate/loop through the given array for (let i = 0; i < arr.length; i++) { // we'll check to see if the Set already contains the element that we're currently on in our loop // if it exists, then we've found our first duplicate! We'll return that value and be done if (elementSet.has(arr[i])) return arr[i]; // if the element isn't in our Set yet, then we add it to the Set and move on to the next element in the array. elementSet.add(arr[i]); } // return the message No duplicate found, if no dupliacte is found after the iteration process. return "No duplicates found!"; } console.log(firstDuplicate2([2, 1, 5, 2, 3, 3, 4])); ================================================ FILE: Searching/linear_search_string.cpp ================================================ // Linear search in an array of strings #include using namespace std; int main(){ char a[10][100]; int n; cin >> n; cin.ignore(); for(int i = 0 ; i < n; i++){ cin.getline(a[i], 100); } char key[100]; cout << "Enter string to search : "; cin.getline(key, 100); int i = 0; for(i = 0; i < n; i++){ if(strcmp(key, a[i]) == 0){ cout << "Found at index : " << i; break; } } if(i == n) cout << "Not Found" <= 0; i-- { x = append(x, s[i]) } return string(x) } ================================================ FILE: Searching/separate_0s_and_1s.go ================================================ // Program to separate Zeros and Ones // Sample input: [0, 1, 0, 1, 0, 0, 1] // Output : [0, 0, 0, 0, 1, 1, 1] package main import "fmt" func SeparateZerosAndOnes(Arr []int) []int { start, end := 0, len(Arr) - 1 for start < end { for Arr[start] == 0 && start < end { start++ // element in correct place so just increment the counter } for Arr[end] == 1 && start < end { end-- // element in correct place so just decrement the counter } // two pointers start and end which are not in correct place so need to be swapped if start < end { Arr[start], Arr[end] = Arr[end], Arr[start] start++ end-- } } return Arr } func main() { Arr := []int{0, 1, 1, 0, 1, 0, 0, 1} fmt.Println(SeparateZerosAndOnes(Arr)) } ================================================ FILE: Searching/separate_even_odd.go ================================================ // Program to separate Even and Odd numbers // Sample input: [1, 2, 3, 4, 5, 6] // Output : [6 2 4 3 5 1] package main import "fmt" func SeparateEvenAndOdd(Arr []int) []int { start, end := 0, len(Arr) - 1 for start < end { for Arr[start] % 2 == 0 && start < end { start++ // element in correct place so just increment the counter } for Arr[end] % 2 == 1 && start < end { end-- // element in correct place so just decrement the counter } // two pointers start and end which are not in correct place so need to be swapped if start < end { Arr[start], Arr[end] = Arr[end], Arr[start] start++ end-- } } return Arr } func main() { Arr := []int{1, 2, 3, 4, 5, 6} fmt.Println(SeparateEvenAndOdd(Arr)) } ================================================ FILE: Sliding Window/find_max.go ================================================ // Statement: //// Given an array and an integer K, find the maximum for each and every contiguous subarray of size K. // Example: //// Input: N = 5 arr[] = {5, 4, 1, 7, 3}, K = 2 //// Output: 5 4 7 7 //// Explanation: Maximum of 5, 4 is 5 //// Maximum of 4, 1 is 4 //// Maximum of 1, 7 is 7 //// Maximum of 7, 3 is 7 // Approach: //// We will travel from 0 to (N-K)th element of the given array. //// At each index, we will find the maximum of K indexes including the current index and print. //// Suppose we are at ith index then we will find maximum of i, i+1, i+2, .. i+k-1 elements and print it. // Complexity: //// Time Complexity: O(N*K) //// Space Complexity: O(1) // Code: package main import "fmt" func printKMax(arr []int, N int, K int) { var j, max int // Finding the maximum elemnt in each window for i := 0; i <= N-K; i++ { max = arr[i] for j = 1; j < K; j++ { if arr[i+j] > max { max = arr[i+j] } } fmt.Printf("%d ", max) } } func main() { var N, K int // Variable declaration fmt.Scan(&N) // Initialise length of array arr := make([]int, N) // Array Declaration for i := 0; i < N; i++ { // Array Initialisation fmt.Scan(&arr[i]) } fmt.Scan(&K) // Window length initialisation printKMax(arr, N, K) // Function call to print maximum element in the window } ================================================ FILE: Sliding Window/fruits_into_basket.cpp ================================================ /* Fruits into basket Summary: The given code defines a Java class `Solution` with a method `totalFruit` that solves a problem related to collecting fruits from fruit trees while adhering to a constraint of collecting fruits from at most two different types of fruit trees. The code uses a sliding window approach to find the maximum number of fruits that can be collected from the trees under this constraint. - The `fruits` array represents the types of fruit trees, where each element represents a type of fruit. - The code initializes variables `longest` and `max` to keep track of the length of the longest subarray with at most two distinct elements and the maximum number of fruits collected, respectively. - The `startWindow` variable represents the left boundary of the sliding window. - A `HashMap` called `basket` is used to keep track of the count of each type of fruit in the current window. - The code iterates through the `fruits` array using two pointers, `startWindow` and `endWindow`, to traverse the array from left to right. - Within the loop, the code updates the `basket` map with the count of fruit types in the current window, and it ensures that there are at most two different types of fruits in the window by adjusting the window as needed. - The `max` variable is updated with the maximum window size seen so far. - Finally, the method returns `max`, which represents the maximum number of fruits that can be collected under the given constraint. Space Complexity: - The space complexity of this code is O(1) in addition to the input `fruits` array. This is because the space used for variables such as `longest`, `max`, `startWindow`, and `basket` remains constant and does not depend on the size of the `fruits` array. Time Complexity: - The time complexity of this code is O(n), where 'n' is the length of the `fruits` array. The code iterates through the `fruits` array once with two pointers, and the work done within each iteration is constant time. Therefore, the overall time complexity is linear in the size of the input array. */ #include #include #include using namespace std; int totalFruit(vector& fruits) { int longest = 0; // Initialize a variable to track the longest subarray length int maxFruits = 0; // Initialize a variable to store the maximum number of fruits int startWindow = 0; // Initialize the left boundary of the window unordered_map basket; // Create an unordered_map to track fruit counts for (int endWindow = 0; endWindow < fruits.size(); endWindow++) { basket[fruits[endWindow]]++; // Add the current fruit to the basket and increment its count while (basket.size() > 2) { basket[fruits[startWindow]]--; if (basket[fruits[startWindow]] == 0) { basket.erase(fruits[startWindow]); } // Adjust the window to maintain at most two fruit types startWindow++; // Move the left boundary of the window to the right } maxFruits = max(maxFruits, endWindow - startWindow + 1); // Update maxFruits with the maximum window size seen so far } return maxFruits; // Return the maximum number of fruits that can be collected } int main() { vector fruits = {1, 2, 1, 2, 3}; int result = totalFruit(fruits); cout << result << endl; return 0; } ================================================ FILE: Sliding Window/fruits_into_basket.go ================================================ /* Fruits into basket Summary: The given code defines a Java class `Solution` with a method `totalFruit` that solves a problem related to collecting fruits from fruit trees while adhering to a constraint of collecting fruits from at most two different types of fruit trees. The code uses a sliding window approach to find the maximum number of fruits that can be collected from the trees under this constraint. - The `fruits` array represents the types of fruit trees, where each element represents a type of fruit. - The code initializes variables `longest` and `max` to keep track of the length of the longest subarray with at most two distinct elements and the maximum number of fruits collected, respectively. - The `startWindow` variable represents the left boundary of the sliding window. - A `HashMap` called `basket` is used to keep track of the count of each type of fruit in the current window. - The code iterates through the `fruits` array using two pointers, `startWindow` and `endWindow`, to traverse the array from left to right. - Within the loop, the code updates the `basket` map with the count of fruit types in the current window, and it ensures that there are at most two different types of fruits in the window by adjusting the window as needed. - The `max` variable is updated with the maximum window size seen so far. - Finally, the method returns `max`, which represents the maximum number of fruits that can be collected under the given constraint. Space Complexity: - The space complexity of this code is O(1) in addition to the input `fruits` array. This is because the space used for variables such as `longest`, `max`, `startWindow`, and `basket` remains constant and does not depend on the size of the `fruits` array. Time Complexity: - The time complexity of this code is O(n), where 'n' is the length of the `fruits` array. The code iterates through the `fruits` array once with two pointers, and the work done within each iteration is constant time. Therefore, the overall time complexity is linear in the size of the input array. */ package main import "fmt" func totalFruit(fruits []int) int { longest := 0 // Initialize a variable to track the longest subarray length maxFruits := 0 // Initialize a variable to store the maximum number of fruits startWindow := 0 // Initialize the left boundary of the window basket := make(map[int]int) // Create a map to track fruit counts for endWindow := 0; endWindow < len(fruits); endWindow++ { basket[fruits[endWindow]]++ // Add the current fruit to the basket and increment its count for len(basket) > 2 { basket[fruits[startWindow]]-- if basket[fruits[startWindow]] == 0 { delete(basket, fruits[startWindow]) } // Adjust the window to maintain at most two fruit types startWindow++ // Move the left boundary of the window to the right } if endWindow-startWindow+1 > maxFruits { maxFruits = endWindow - startWindow + 1 } // Update maxFruits with the maximum window size seen so far } return maxFruits // Return the maximum number of fruits that can be collected } func main() { fruits := []int{1, 2, 1, 2, 3} result := totalFruit(fruits) fmt.Println(result) } ================================================ FILE: Sliding Window/fruits_into_basket.java ================================================ /* Fruits into basket Summary: The given code defines a Java class `Solution` with a method `totalFruit` that solves a problem related to collecting fruits from fruit trees while adhering to a constraint of collecting fruits from at most two different types of fruit trees. The code uses a sliding window approach to find the maximum number of fruits that can be collected from the trees under this constraint. - The `fruits` array represents the types of fruit trees, where each element represents a type of fruit. - The code initializes variables `longest` and `max` to keep track of the length of the longest subarray with at most two distinct elements and the maximum number of fruits collected, respectively. - The `startWindow` variable represents the left boundary of the sliding window. - A `HashMap` called `basket` is used to keep track of the count of each type of fruit in the current window. - The code iterates through the `fruits` array using two pointers, `startWindow` and `endWindow`, to traverse the array from left to right. - Within the loop, the code updates the `basket` map with the count of fruit types in the current window, and it ensures that there are at most two different types of fruits in the window by adjusting the window as needed. - The `max` variable is updated with the maximum window size seen so far. - Finally, the method returns `max`, which represents the maximum number of fruits that can be collected under the given constraint. Space Complexity: - The space complexity of this code is O(1) in addition to the input `fruits` array. This is because the space used for variables such as `longest`, `max`, `startWindow`, and `basket` remains constant and does not depend on the size of the `fruits` array. Time Complexity: - The time complexity of this code is O(n), where 'n' is the length of the `fruits` array. The code iterates through the `fruits` array once with two pointers, and the work done within each iteration is constant time. Therefore, the overall time complexity is linear in the size of the input array. */ class Solution { public int totalFruit(int[] fruits) { int longest = 0; // Initialize a variable to track the longest subarray length int max = 0; // Initialize a variable to store the maximum number of fruits int startWindow = 0; // Initialize the left boundary of the window HashMap basket = new HashMap<>(); // Create a HashMap to track fruit counts for(int endWindow = 0; endWindow < fruits.length; endWindow++) { basket.put(fruits[endWindow], basket.getOrDefault(fruits[endWindow], 0) + 1); // Add the current fruit to the basket and increment its count while(basket.size() > 2) { basket.put(fruits[startWindow], basket.get(fruits[startWindow]) - 1); basket.remove(fruits[startWindow], 0); // Adjust the window to maintain at most two fruit types startWindow++; // Move the left boundary of the window to the right } max = Math.max(max, (endWindow - startWindow) + 1); // Update max with the maximum window size seen so far } return max; // Return the maximum number of fruits that can be collected } } ================================================ FILE: Sliding Window/fruits_into_basket.js ================================================ /* Fruits into basket Summary: The given code defines a Java class `Solution` with a method `totalFruit` that solves a problem related to collecting fruits from fruit trees while adhering to a constraint of collecting fruits from at most two different types of fruit trees. The code uses a sliding window approach to find the maximum number of fruits that can be collected from the trees under this constraint. - The `fruits` array represents the types of fruit trees, where each element represents a type of fruit. - The code initializes variables `longest` and `max` to keep track of the length of the longest subarray with at most two distinct elements and the maximum number of fruits collected, respectively. - The `startWindow` variable represents the left boundary of the sliding window. - A `HashMap` called `basket` is used to keep track of the count of each type of fruit in the current window. - The code iterates through the `fruits` array using two pointers, `startWindow` and `endWindow`, to traverse the array from left to right. - Within the loop, the code updates the `basket` map with the count of fruit types in the current window, and it ensures that there are at most two different types of fruits in the window by adjusting the window as needed. - The `max` variable is updated with the maximum window size seen so far. - Finally, the method returns `max`, which represents the maximum number of fruits that can be collected under the given constraint. Space Complexity: - The space complexity of this code is O(1) in addition to the input `fruits` array. This is because the space used for variables such as `longest`, `max`, `startWindow`, and `basket` remains constant and does not depend on the size of the `fruits` array. Time Complexity: - The time complexity of this code is O(n), where 'n' is the length of the `fruits` array. The code iterates through the `fruits` array once with two pointers, and the work done within each iteration is constant time. Therefore, the overall time complexity is linear in the size of the input array. */ var totalFruit = function (fruits) { let longest = 0; // Initialize a variable to track the longest subarray length let maxFruits = 0; // Initialize a variable to store the maximum number of fruits let startWindow = 0; // Initialize the left boundary of the window const basket = new Map(); // Create a Map to track fruit counts for (let endWindow = 0; endWindow < fruits.length; endWindow++) { basket.set(fruits[endWindow], (basket.get(fruits[endWindow]) || 0) + 1); // Add the current fruit to the basket and increment its count while (basket.size > 2) { basket.set(fruits[startWindow], basket.get(fruits[startWindow]) - 1); if (basket.get(fruits[startWindow]) === 0) { basket.delete(fruits[startWindow]); } // Adjust the window to maintain at most two fruit types startWindow++; // Move the left boundary of the window to the right } maxFruits = Math.max(maxFruits, endWindow - startWindow + 1); // Update maxFruits with the maximum window size seen so far } return maxFruits; // Return the maximum number of fruits that can be collected }; ================================================ FILE: Sliding Window/fruits_into_basket.py ================================================ ''' Fruits into basket Summary: The given code defines a Java class `Solution` with a method `totalFruit` that solves a problem related to collecting fruits from fruit trees while adhering to a constraint of collecting fruits from at most two different types of fruit trees. The code uses a sliding window approach to find the maximum number of fruits that can be collected from the trees under this constraint. - The `fruits` array represents the types of fruit trees, where each element represents a type of fruit. - The code initializes variables `longest` and `max` to keep track of the length of the longest subarray with at most two distinct elements and the maximum number of fruits collected, respectively. - The `startWindow` variable represents the left boundary of the sliding window. - A `HashMap` called `basket` is used to keep track of the count of each type of fruit in the current window. - The code iterates through the `fruits` array using two pointers, `startWindow` and `endWindow`, to traverse the array from left to right. - Within the loop, the code updates the `basket` map with the count of fruit types in the current window, and it ensures that there are at most two different types of fruits in the window by adjusting the window as needed. - The `max` variable is updated with the maximum window size seen so far. - Finally, the method returns `max`, which represents the maximum number of fruits that can be collected under the given constraint. Space Complexity: - The space complexity of this code is O(1) in addition to the input `fruits` array. This is because the space used for variables such as `longest`, `max`, `startWindow`, and `basket` remains constant and does not depend on the size of the `fruits` array. Time Complexity: - The time complexity of this code is O(n), where 'n' is the length of the `fruits` array. The code iterates through the `fruits` array once with two pointers, and the work done within each iteration is constant time. Therefore, the overall time complexity is linear in the size of the input array. ''' class Solution: def totalFruit(self, fruits): longest = 0 # Initialize a variable to track the longest subarray length max_fruits = 0 # Initialize a variable to store the maximum number of fruits start_window = 0 # Initialize the left boundary of the window basket = {} # Create a dictionary to track fruit counts for end_window in range(len(fruits)): basket[fruits[end_window]] = basket.get(fruits[end_window], 0) + 1 # Add the current fruit to the basket and increment its count while len(basket) > 2: basket[fruits[start_window]] -= 1 if basket[fruits[start_window]] == 0: del basket[fruits[start_window]] # Adjust the window to maintain at most two fruit types start_window += 1 # Move the left boundary of the window to the right max_fruits = max(max_fruits, (end_window - start_window) + 1) # Update max_fruits with the maximum window size seen so far return max_fruits # Return the maximum number of fruits that can be collected ================================================ FILE: Sliding Window/longest_repeated_character_replacement.cpp ================================================ /* You are given a string s and an integer k. You can choose any character of the string and change it to any other uppercase English character. You can perform this operation at most k times. Return the length of the longest substring containing the same letter you can get after performing the above operations. Example 1: Input: s = "ABAB", k = 2 Output: 4 Explanation: Replace the two 'A's with two 'B's or vice versa. Summary: The provided code defines a Java class `Solution` with a method `characterReplacement` that aims to find the longest substring within the input string `s` such that it can be created by replacing at most `k` characters with any other character. It uses a sliding window approach to efficiently compute the maximum length of such a substring. Time Complexity: - The code iterates through the input string `s` using a sliding window with two pointers (startWindow and endWindow). During each iteration, it updates character counts and evaluates the maximum length of a valid substring. Since each character is processed exactly once, the time complexity is O(N), where N is the length of the input string `s`. Space Complexity: - The code uses additional space to store integer variables (`count`, `startWindow`, `maxCount`, and `max`). The `count` array has a fixed size of 26 (for 26 English alphabet letters). Therefore, the space complexity is O(1), as the space used is constant and does not depend on the input size. In summary, the algorithm has a time complexity of O(N) and a space complexity of O(1), making it efficient for finding the longest substring with at most 'k' replacements in a given string. */ #include #include #include class Solution { public: int characterReplacement(std::string s, int k) { std::vector count(26, 0); // Initialize an array to count the occurrences of characters (26 letters in the English alphabet) int startWindow = 0; // The left end of the sliding window int maxCount = 0; // The maximum count of any character within the window int max = 0; // The maximum length of a substring that can be formed // Iterate through the string using a sliding window approach for (int endWindow = 0; endWindow < s.length(); endWindow++) { int val = s[endWindow] - 'A'; // Convert the character to an index (0-25) count[val]++; // Increment the count for the current character maxCount = std::max(maxCount, count[val]); // Update the maximum character count // While the length of the current window minus the maximum character count exceeds 'k', shrink the window while (endWindow - startWindow + 1 - maxCount > k) { val = s[startWindow] - 'A'; // Get the character at the start of the window count[val]--; // Decrement the count for the character at the start of the window startWindow++; // Move the start of the window to the right } // Update the maximum length of a substring that can be formed max = std::max(max, endWindow - startWindow + 1); } // Return the maximum length, which represents the longest substring with at most 'k' replacements return max; } }; ================================================ FILE: Sliding Window/longest_repeated_character_replacement.go ================================================ /* You are given a string s and an integer k. You can choose any character of the string and change it to any other uppercase English character. You can perform this operation at most k times. Return the length of the longest substring containing the same letter you can get after performing the above operations. Example 1: Input: s = "ABAB", k = 2 Output: 4 Explanation: Replace the two 'A's with two 'B's or vice versa. Summary: The provided code defines a Java class `Solution` with a method `characterReplacement` that aims to find the longest substring within the input string `s` such that it can be created by replacing at most `k` characters with any other character. It uses a sliding window approach to efficiently compute the maximum length of such a substring. Time Complexity: - The code iterates through the input string `s` using a sliding window with two pointers (startWindow and endWindow). During each iteration, it updates character counts and evaluates the maximum length of a valid substring. Since each character is processed exactly once, the time complexity is O(N), where N is the length of the input string `s`. Space Complexity: - The code uses additional space to store integer variables (`count`, `startWindow`, `maxCount`, and `max`). The `count` array has a fixed size of 26 (for 26 English alphabet letters). Therefore, the space complexity is O(1), as the space used is constant and does not depend on the input size. In summary, the algorithm has a time complexity of O(N) and a space complexity of O(1), making it efficient for finding the longest substring with at most 'k' replacements in a given string. */ func characterReplacement(s string, k int) int { count := make([]int, 26) // Initialize an array to count the occurrences of characters (26 letters in the English alphabet) startWindow := 0 // The left end of the sliding window maxCount := 0 // The maximum count of any character within the window max := 0 // The maximum length of a substring that can be formed // Iterate through the string using a sliding window approach for endWindow := 0; endWindow < len(s); endWindow++ { val := int(s[endWindow] - 'A') // Convert the character to an index (0-25) count[val]++ // Increment the count for the current character maxCount = maxInt(maxCount, count[val]) // Update the maximum character count // While the length of the current window minus the maximum character count exceeds 'k', shrink the window for endWindow - startWindow + 1 - maxCount > k { val = int(s[startWindow] - 'A') // Get the character at the start of the window count[val]-- // Decrement the count for the character at the start of the window startWindow++ // Move the start of the window to the right } // Update the maximum length of a substring that can be formed max = maxInt(max, endWindow - startWindow + 1) } // Return the maximum length, which represents the longest substring with at most 'k' replacements return max } func maxInt(a, b int) int { if a > b { return a } return b } ================================================ FILE: Sliding Window/longest_repeated_character_replacement.java ================================================ /* You are given a string s and an integer k. You can choose any character of the string and change it to any other uppercase English character. You can perform this operation at most k times. Return the length of the longest substring containing the same letter you can get after performing the above operations. Example 1: Input: s = "ABAB", k = 2 Output: 4 Explanation: Replace the two 'A's with two 'B's or vice versa. Summary: The provided code defines a Java class `Solution` with a method `characterReplacement` that aims to find the longest substring within the input string `s` such that it can be created by replacing at most `k` characters with any other character. It uses a sliding window approach to efficiently compute the maximum length of such a substring. Time Complexity: - The code iterates through the input string `s` using a sliding window with two pointers (startWindow and endWindow). During each iteration, it updates character counts and evaluates the maximum length of a valid substring. Since each character is processed exactly once, the time complexity is O(N), where N is the length of the input string `s`. Space Complexity: - The code uses additional space to store integer variables (`count`, `startWindow`, `maxCount`, and `max`). The `count` array has a fixed size of 26 (for 26 English alphabet letters). Therefore, the space complexity is O(1), as the space used is constant and does not depend on the input size. In summary, the algorithm has a time complexity of O(N) and a space complexity of O(1), making it efficient for finding the longest substring with at most 'k' replacements in a given string. */ class Solution { public int characterReplacement(String s, int k) { // Initialize an array to count the occurrences of characters (26 letters in the English alphabet) int[] count = new int[26]; int startWindow = 0; // The left end of the sliding window int maxCount = 0; // The maximum count of any character within the window int max = 0; // The maximum length of a substring that can be formed // Iterate through the string using a sliding window approach for (int endWindow = 0; endWindow < s.length(); endWindow++) { int val = s.charAt(endWindow) - 'A'; // Convert the character to an index (0-25) count[val]++; // Increment the count for the current character maxCount = Math.max(maxCount, count[val]); // Update the maximum character count // While the length of the current window minus the maximum character count exceeds 'k', shrink the window while (endWindow - startWindow + 1 - maxCount > k) { val = s.charAt(startWindow) - 'A'; // Get the character at the start of the window count[val]--; // Decrement the count for the character at the start of the window startWindow++; // Move the start of the window to the right } // Update the maximum length of a substring that can be formed max = Math.max(max, endWindow - startWindow + 1); } // Return the maximum length, which represents the longest substring with at most 'k' replacements return max; } } ================================================ FILE: Sliding Window/longest_repeated_character_replacement.js ================================================ /* You are given a string s and an integer k. You can choose any character of the string and change it to any other uppercase English character. You can perform this operation at most k times. Return the length of the longest substring containing the same letter you can get after performing the above operations. Example 1: Input: s = "ABAB", k = 2 Output: 4 Explanation: Replace the two 'A's with two 'B's or vice versa. Summary: The provided code defines a Java class `Solution` with a method `characterReplacement` that aims to find the longest substring within the input string `s` such that it can be created by replacing at most `k` characters with any other character. It uses a sliding window approach to efficiently compute the maximum length of such a substring. Time Complexity: - The code iterates through the input string `s` using a sliding window with two pointers (startWindow and endWindow). During each iteration, it updates character counts and evaluates the maximum length of a valid substring. Since each character is processed exactly once, the time complexity is O(N), where N is the length of the input string `s`. Space Complexity: - The code uses additional space to store integer variables (`count`, `startWindow`, `maxCount`, and `max`). The `count` array has a fixed size of 26 (for 26 English alphabet letters). Therefore, the space complexity is O(1), as the space used is constant and does not depend on the input size. In summary, the algorithm has a time complexity of O(N) and a space complexity of O(1), making it efficient for finding the longest substring with at most 'k' replacements in a given string. */ class Solution { characterReplacement(s, k) { const count = Array(26).fill(0); // Initialize an array to count the occurrences of characters (26 letters in the English alphabet) let startWindow = 0; // The left end of the sliding window let maxCount = 0; // The maximum count of any character within the window let max_length = 0; // The maximum length of a substring that can be formed // Iterate through the string using a sliding window approach for (let endWindow = 0; endWindow < s.length; endWindow++) { const val = s.charCodeAt(endWindow) - "A".charCodeAt(0); // Convert the character to an index (0-25) count[val]++; // Increment the count for the current character maxCount = Math.max(maxCount, count[val]); // Update the maximum character count // While the length of the current window minus the maximum character count exceeds 'k', shrink the window while (endWindow - startWindow + 1 - maxCount > k) { const val = s.charCodeAt(startWindow) - "A".charCodeAt(0); // Get the character at the start of the window count[val]--; // Decrement the count for the character at the start of the window startWindow++; // Move the start of the window to the right } // Update the maximum length of a substring that can be formed max_length = Math.max(max_length, endWindow - startWindow + 1); } // Return the maximum length, which represents the longest substring with at most 'k' replacements return max_length; } } // Example usage const solution = new Solution(); const result = solution.characterReplacement("ABAB", 2); console.log(result); // Output: 4 ================================================ FILE: Sliding Window/longest_repeated_character_replacement.py ================================================ ''' You are given a string s and an integer k. You can choose any character of the string and change it to any other uppercase English character. You can perform this operation at most k times. Return the length of the longest substring containing the same letter you can get after performing the above operations. Example 1: Input: s = "ABAB", k = 2 Output: 4 Explanation: Replace the two 'A's with two 'B's or vice versa. Summary: The provided code defines a Java class `Solution` with a method `characterReplacement` that aims to find the longest substring within the input string `s` such that it can be created by replacing at most `k` characters with any other character. It uses a sliding window approach to efficiently compute the maximum length of such a substring. Time Complexity: - The code iterates through the input string `s` using a sliding window with two pointers (startWindow and endWindow). During each iteration, it updates character counts and evaluates the maximum length of a valid substring. Since each character is processed exactly once, the time complexity is O(N), where N is the length of the input string `s`. Space Complexity: - The code uses additional space to store integer variables (`count`, `startWindow`, `maxCount`, and `max`). The `count` array has a fixed size of 26 (for 26 English alphabet letters). Therefore, the space complexity is O(1), as the space used is constant and does not depend on the input size. In summary, the algorithm has a time complexity of O(N) and a space complexity of O(1), making it efficient for finding the longest substring with at most 'k' replacements in a given string. ''' class Solution: def characterReplacement(self, s: str, k: int) -> int: count = [0] * 26 # Initialize a list to count the occurrences of characters (26 letters in the English alphabet) startWindow = 0 # The left end of the sliding window maxCount = 0 # The maximum count of any character within the window max_length = 0 # The maximum length of a substring that can be formed # Iterate through the string using a sliding window approach for endWindow in range(len(s)): val = ord(s[endWindow]) - ord('A') # Convert the character to an index (0-25) count[val] += 1 # Increment the count for the current character maxCount = max(maxCount, count[val]) # Update the maximum character count # While the length of the current window minus the maximum character count exceeds 'k', shrink the window while endWindow - startWindow + 1 - maxCount > k: val = ord(s[startWindow]) - ord('A') # Get the character at the start of the window count[val] -= 1 # Decrement the count for the character at the start of the window startWindow += 1 # Move the start of the window to the right # Update the maximum length of a substring that can be formed max_length = max(max_length, endWindow - startWindow + 1) # Return the maximum length, which represents the longest substring with at most 'k' replacements return max_length ================================================ FILE: Sliding Window/longest_substring_with_k_distinct_chars.go ================================================ /* * Given a string, find the length of the longest substring in it with no more than K distinct characters. */ package main import ( "fmt" ) func longestSubstringWithKDistinctChars(s string, k int) int { // Check for edge cases where s is empty, or k is 0. if len(s) == 0 || k == 0 { return 0 } startWindow := 0 // Initialize the start of the sliding window. maxLen := 0 // Initialize the maximum length of the substring. charCount := make(map[byte]int) // Create a map to store character frequencies. for endWindow := 0; endWindow < len(s); endWindow++ { right := s[endWindow] // Get the character at the end of the window. charCount[right]++ // Update the character count in the map. // While there are more than k distinct characters in the window. for len(charCount) > k { left := s[startWindow] // Get the character at the start of the window. charCount[left]-- // Decrease the count of the character. if charCount[left] == 0 { delete(charCount, left) // If the count becomes 0, remove the character from the map. } startWindow++ // Move the start of the window to the right. } maxLen = max(maxLen, endWindow-startWindow+1) // Update the maximum length. } return maxLen } func max(a, b int) int { if a > b { return a } return b } func main() { fmt.Println(longestSubstringWithKDistinctChars("araaci", 2)) fmt.Println(longestSubstringWithKDistinctChars("araaci", 1)) fmt.Println(longestSubstringWithKDistinctChars("cbbebi", 3)) } ================================================ FILE: Sliding Window/longest_substring_with_k_distinct_chars.java ================================================ /* * Given a string, find the length of the longest substring in it with no more than K distinct characters. */ public class LongestSubstringWithKDistinctChars { public int longestSubstringWithKDistinctChars(String s, int k) { // Check for edge cases where s is empty, null, or k is 0. if (s.length() == 0 || s == null || k == 0) { return 0; } int startWindow = 0; // Initialize the start of the sliding window. int maxlen = 0; // Initialize the maximum length of the substring. HashMap mp = new HashMap(); // Create a HashMap to store character frequencies. for (int endWindow = 0; endWindow < s.length(); endWindow++) { char right = s.charAt(endWindow); // Get the character at the end of the window. mp.put(right, mp.getOrDefault(right, 0) + 1); // Update the character count in the HashMap. // While there are more than k distinct characters in the window. while (mp.size() > k) { char left = s.charAt(startWindow); // Get the character at the start of the window. mp.put(left, mp.get(left) - 1); // Decrease the count of the character. startWindow++; // Move the start of the window to the right. if (mp.get(left) == 0) { mp.remove(left); // If the count becomes 0, remove the character from the HashMap. } } maxlen = Math.max(maxlen, endWindow - startWindow + 1); // Update the maximum length. } return maxlen; } public static void main(String[] args) { LongestSubstringWithKDistinctChars l = new LongestSubstringWithKDistinctChars(); System.out.println(l.longestSubstringWithKDistinctChars("araaci", 2)); System.out.println(l.longestSubstringWithKDistinctChars("araaci", 1)); System.out.println(l.longestSubstringWithKDistinctChars("cbbebi", 3)); } } ================================================ FILE: Sliding Window/longest_substring_with_k_distinct_chars.js ================================================ /* * Given a string, find the length of the longest substring in it with no more than K distinct characters. */ function longestSubstringWithKDistinctChars(s, k) { // Check for edge cases where s is empty, or k is 0. if (!s || k === 0) { return 0; } let startWindow = 0; // Initialize the start of the sliding window. let maxLen = 0; // Initialize the maximum length of the substring. const charCount = {}; // Create an object to store character frequencies. for (let endWindow = 0; endWindow < s.length; endWindow++) { const right = s[endWindow]; // Get the character at the end of the window. charCount[right] = (charCount[right] || 0) + 1; // Update the character count in the object. // While there are more than k distinct characters in the window. while (Object.keys(charCount).length > k) { const left = s[startWindow]; // Get the character at the start of the window. charCount[left]--; // Decrease the count of the character. if (charCount[left] === 0) { delete charCount[left]; // If the count becomes 0, remove the character from the object. } startWindow++; // Move the start of the window to the right. } maxLen = Math.max(maxLen, endWindow - startWindow + 1); // Update the maximum length. } return maxLen; } function max(a, b) { return a > b ? a : b; } console.log(longestSubstringWithKDistinctChars("araaci", 2)); console.log(longestSubstringWithKDistinctChars("araaci", 1)); console.log(longestSubstringWithKDistinctChars("cbbebi", 3)); ================================================ FILE: Sliding Window/longest_substring_with_k_distinct_chars.py ================================================ ''' Given a string, find the length of the longest substring in it with no more than K distinct characters. ''' def longestSubstringWithKDistinctChars(s, k): # Check for edge cases where s is empty, or k is 0. if not s or k == 0: return 0 start_window = 0 # Initialize the start of the sliding window. max_len = 0 # Initialize the maximum length of the substring. char_count = {} # Create a dictionary to store character frequencies. for end_window in range(len(s)): right = s[end_window] # Get the character at the end of the window. char_count[right] = char_count.get(right, 0) + 1 # Update the character count in the dictionary. # While there are more than k distinct characters in the window. while len(char_count) > k: left = s[start_window] # Get the character at the start of the window. char_count[left] -= 1 # Decrease the count of the character. if char_count[left] == 0: del char_count[left] # If the count becomes 0, remove the character from the dictionary. start_window += 1 # Move the start of the window to the right. max_len = max(max_len, end_window - start_window + 1) # Update the maximum length. return max_len def max(a, b): if a > b: return a return b if __name__ == "__main__": print(longestSubstringWithKDistinctChars("araaci", 2)) print(longestSubstringWithKDistinctChars("araaci", 1)) print(longestSubstringWithKDistinctChars("cbbebi", 3)) ================================================ FILE: Sliding Window/longest_substring_without_repeating_characters.go ================================================ package main /* Given a string s, find the length of the longest substring without repeating characters. Example 1: Input: s = "abcabcbb" Output: 3 Explanation: The answer is "abc", with the length of 3. Example 2: Input: s = "bbbbb" Output: 1 Explanation: The answer is "b", with the length of 1. Example 3: Input: s = "pwwkew" Output: 3 Explanation: The answer is "wke", with the length of 3. Notice that the answer must be a substring, "pwke" is a subsequence and not a substring. Constraints: 0 <= s.length <= 5 * 104 s consists of English letters, digits, symbols and spaces. */ // Approach 1 Brute force // Intuition // Check all the substring one by one to see if it has no duplicate character. // Time complexity O(n³ ) Space complexity O(m) where m is size of hMap func LengthOfLongestSubstring1(s string) int { n := len(s) res := 0 for i := 0; i < n; i++ { for j := i; j < n; j++ { if check(s, i, j) { res = max(res, j - i + 1) } } } return res } func max(a, b int) int { if a >= b { return a } else { return b } } func check(s string, start int, end int) bool { hMap := [128]int{} for i := start; i <= end; i++ { hMap[s[i]]++ if hMap[s[i]] > 1 { return false } } return true } // Approach 2: Sliding window // Time complexity O(n) Space complexity O(m) where m is size of char func LengthOfLongestSubstring2(s string) int { result := 0 for i := 0; i < len(s); i++ { char := make(map[byte]bool) char[s[i]] = true for j := i + 1; j < len(s); j++ { if _, ok := char[s[j]]; ok { break } char[s[j]] = true } if len(char) > result { result = len(char) } } return result } ================================================ FILE: Sliding Window/max_eraser_value.go ================================================ /* You are given an array of positive integers nums and want to erase a subarray containing unique elements. The score you get by erasing the subarray is equal to the sum of its elements. Return the maximum score you can get by erasing exactly one subarray. An array b is called to be a subarray of a if it forms a contiguous subsequence of a, that is, if it is equal to a[l],a[l+1],...,a[r] for some (l,r). Example 1: Input: nums = [4,2,4,5,6] Output: 17 Explanation: The optimal subarray here is [2,4,5,6]. Example 2: Input: nums = [5,2,1,2,5,2,1,2,5] Output: 8 Explanation: The optimal subarray here is [5,2,1] or [1,2,5]. Explanation: startWindow: The left end of the sliding window. windowSum: Sum of elements within the current window. res: Result, initialized to 0, representing the maximum unique subarray sum. mp: HashMap to store the last index where each element was seen. Sliding Window: Use a for loop to iterate through the array with the endWindow as the right end of the window. Check if the current element is already in the window using a while loop. If yes, remove the element at the start of the window from the HashMap and update windowSum and startWindow. Update HashMap and windowSum: Add the current element to the HashMap and update windowSum. Update Result: Update the result (res) with the maximum unique subarray sum. Return Result: Return the final result, which represents the maximum unique subarray sum. This code efficiently finds the maximum sum of a subarray where all elements are unique using a sliding window and a HashMap to keep track of the last index of each element encountered. */ package main import "fmt" func maximumUniqueSubarray(nums []int) int { startWindow := 0 // The left end of the sliding window windowSum := 0 // Sum of elements within the current window res := 0 // Result, which represents the maximum unique subarray sum mp := make(map[int]int) // Map to store the last index where each element was seen // Iterate through the array using a sliding window approach for endWindow := 0; endWindow < len(nums); endWindow++ { // Check if the current element is already in the window for mp[nums[endWindow]] > 0 { // Remove the element at the start of the window from the map and update windowSum mp[nums[startWindow]]-- windowSum -= nums[startWindow] startWindow++ } // Add the current element to the map and update windowSum mp[nums[endWindow]]++ windowSum += nums[endWindow] // Update the result with the maximum unique subarray sum if windowSum > res { res = windowSum } } // Return the result, which represents the maximum unique subarray sum return res } func main() { nums := []int{4, 2, 4, 5, 6} result := maximumUniqueSubarray(nums) fmt.Println(result) // Output: 17 } ================================================ FILE: Sliding Window/max_eraser_value.java ================================================ /* You are given an array of positive integers nums and want to erase a subarray containing unique elements. The score you get by erasing the subarray is equal to the sum of its elements. Return the maximum score you can get by erasing exactly one subarray. An array b is called to be a subarray of a if it forms a contiguous subsequence of a, that is, if it is equal to a[l],a[l+1],...,a[r] for some (l,r). Example 1: Input: nums = [4,2,4,5,6] Output: 17 Explanation: The optimal subarray here is [2,4,5,6]. Example 2: Input: nums = [5,2,1,2,5,2,1,2,5] Output: 8 Explanation: The optimal subarray here is [5,2,1] or [1,2,5]. Explanation: startWindow: The left end of the sliding window. windowSum: Sum of elements within the current window. res: Result, initialized to 0, representing the maximum unique subarray sum. mp: HashMap to store the last index where each element was seen. Sliding Window: Use a for loop to iterate through the array with the endWindow as the right end of the window. Check if the current element is already in the window using a while loop. If yes, remove the element at the start of the window from the HashMap and update windowSum and startWindow. Update HashMap and windowSum: Add the current element to the HashMap and update windowSum. Update Result: Update the result (res) with the maximum unique subarray sum. Return Result: Return the final result, which represents the maximum unique subarray sum. This code efficiently finds the maximum sum of a subarray where all elements are unique using a sliding window and a HashMap to keep track of the last index of each element encountered. */ class Solution { public int maximumUniqueSubarray(int[] nums) { // Initialize pointers, windowSum, and result int startWindow = 0; // The left end of the sliding window int windowSum = 0; // Sum of elements within the current window int res = 0; // Result, which represents the maximum unique subarray sum // HashMap to store the last index where each element was seen HashMap mp = new HashMap<>(); // Iterate through the array using a sliding window approach for (int endWindow = 0; endWindow < nums.length; endWindow++) { // Check if the current element is already in the window while (mp.containsKey(nums[endWindow])) { // Remove the element at the start of the window from the HashMap and update windowSum mp.remove(nums[startWindow]); windowSum -= nums[startWindow]; startWindow++; } // Add the current element to the HashMap and update windowSum mp.put(nums[endWindow], 1); windowSum += nums[endWindow]; // Update the result with the maximum unique subarray sum res = Math.max(res, windowSum); } // Return the result, which represents the maximum unique subarray sum return res; } } ================================================ FILE: Sliding Window/sliding_window_max.java ================================================ /* Given an integer array and a window of size windowSize, find the current maximum value in the window as it slides through the entire array */ import java.util.*; public class SlidingWindowMax { public static int[] findMaxSlidingWindow(int[] nums, int windowSize) { if (nums == null || windowSize <= 0 || nums.length < windowSize) { return new int[0]; } int n = nums.length; int[] result = new int[n - windowSize + 1]; Deque deque = new ArrayDeque<>(); // deque stores the indices of elements in the window // process the first window separately for (int i = 0; i < windowSize; i++) { while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) { deque.removeLast(); } deque.addLast(i); } result[0] = nums[deque.peekFirst()]; // process the remaining windows for (int i = windowSize; i < n; i++) { while (!deque.isEmpty() && deque.peekFirst() <= i - windowSize) { deque.removeFirst(); // remove elements outside the window } while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) { deque.removeLast(); // remove elements smaller than nums[i] } deque.addLast(i); result[i - windowSize + 1] = nums[deque.peekFirst()]; } return result; } public static void main(String[] args) { int[] nums = {1, 3, -1, -3, 5, 3, 6, 7}; int windowSize = 3; int[] maxInWindow = findMaxSlidingWindow(nums, windowSize); System.out.println("Max values in sliding window of size " + windowSize + ": "); for (int i = 0; i < maxInWindow.length; i++) { System.out.print(maxInWindow[i] + " "); } } } ---------------Explanation--------------------- This program defines a findMaxSlidingWindow method that takes an array nums and a window size windowSize as input and returns an array containing the maximum value in each sliding window. The method first checks if the input is valid and creates a deque to store the indices of elements in the window. The method then processes the first window separately by adding the indices of its elements to the deque in decreasing order of their values. This way, the first element in the deque will always be the maximum value in the window. The method stores this maximum value in the first element of the result array. The method then processes the remaining windows by first removing any elements in the deque that are outside the current window. The method then adds the index of the current element to the deque and removes any elements in the deque that are smaller than the current element. The method stores the maximum value in the window in the corresponding element of the result array. Finally, the method returns the result array. ================================================ FILE: Sliding Window/sliding_window_max.js ================================================ /* Given an integer array and a window of size windowSize, find the current maximum value in the window as it slides through the entire array */ function findMaxSlidingWindow(nums, windowSize) { if (!nums || windowSize <= 0 || nums.length < windowSize) { return []; } const n = nums.length; const result = new Array(n - windowSize + 1).fill(0); const deque = []; // deque stores the indices of elements in the window // process the first window separately for (let i = 0; i < windowSize; i++) { while (deque.length > 0 && nums[i] >= nums[deque[deque.length - 1]]) { deque.pop(); } deque.push(i); } result[0] = nums[deque[0]]; // process the remaining windows for (let i = windowSize; i < n; i++) { while (deque.length > 0 && deque[0] <= i - windowSize) { deque.shift(); // remove elements outside the window } while (deque.length > 0 && nums[i] >= nums[deque[deque.length - 1]]) { deque.pop(); // remove elements smaller than nums[i] } deque.push(i); result[i - windowSize + 1] = nums[deque[0]]; } return result; } const nums = [1, 3, -1, -3, 5, 3, 6, 7]; const windowSize = 3; const maxInWindow = findMaxSlidingWindow(nums, windowSize); console.log(`Max values in sliding window of size ${windowSize}:`); console.log(maxInWindow.join(' ')); -------------Explanation--------------------- This code defines a findMaxSlidingWindow function that takes an array nums and a window size windowSize as input and returns an array containing the maximum value in each sliding window. The function first checks if the input is valid and creates an empty array result and a deque to store the indices of elements in the window. The function then processes the first window separately by adding the indices of its elements to the deque in decreasing order of their values. This way, the first element in the deque will always be the maximum value in the window. The function stores this maximum value in the first element of the result array. The function then processes the remaining windows by first removing any elements in the deque that are outside the current window. The function then adds the index of the current element to the deque and removes any elements in the deque that are smaller than the current element. The function stores the maximum value in the window in the corresponding element of the result array. Finally, the function returns the result array. The code also includes an example usage of the findMaxSlidingWindow function on an example array and window size. When the code is executed, it prints the maximum values in each sliding window to the console. ================================================ FILE: Sliding Window/sliding_window_max.py ================================================ # Given an integer array and a window of size windowSize, find the current maximum value in the window as it slides through the entire array from collections import deque def slidingWindowMax(arr,windowSize) -> list[int]: dq = deque() max_elements = [] for i in range(len(arr)): if(len(dq) != 0 and dq[0]== i - windowSize): dq.popleft() while(len(dq) !=0 and arr[dq[-1]] < arr[i]): dq.pop() dq.append(i) if(i >= windowSize - 1): max_elements.append(arr[dq[0]]) return max_elements arr = list(map(int, input().split())) windowSize = int(input()) print(slidingWindowMax(arr, windowSize)) ================================================ FILE: Sliding Window/subaray_sum_equals_k.cpp ================================================ /* Given an array of integers nums and an integer k, return the total number of subarrays whose sum equals to k. A subarray is a contiguous non-empty sequence of elements within an array. Example 1: Input: nums = [1,1,1], k = 2 Output: 2 Example 2: Input: nums = [1,2,3], k = 3 Output: 2 ### Explanation: The code is designed to count the number of subarrays within the 'nums' array whose elements sum to a given target integer 'k'. It uses a hashmap to efficiently keep track of cumulative sums and their counts. Here's the code's key logic: 1. It initializes a hashmap `sumIndex` to store cumulative sums as keys and their counts as values. 2. It initializes variables `result` and `currentSum`. 3. It adds a key-value pair of `(0, 1)` to the `sumIndex` hashmap to represent the sum of an empty subarray (0) and its count (1). 4. It iterates through the elements of the 'nums' array. 5. For each element, it adds the element's value to `currentSum`. 6. It calculates the value to find in the hashmap by subtracting 'k' from the current cumulative sum, which is stored in the `toFind` variable. 7. It checks if the hashmap contains the value 'toFind' and, if found, adds the count of subarrays that sum to 'toFind' to the 'result'. 8. It updates the hashmap with the current cumulative sum. If the sum is already in the hashmap, it increments its count by 1. If it's not in the hashmap, it adds it with a count of 1. 9. Finally, it returns the 'result,' which represents the total number of subarrays with a sum of 'k'. ### Time Complexity: The time complexity of this code is O(n), where 'n' is the length of the 'nums' array. This is because the code iterates through the 'nums' array once, and each iteration consists of constant-time operations (e.g., hashmap lookups and additions). ### Space Complexity: The space complexity of this code is O(n), where 'n' is the length of the 'nums' array. The space is primarily used for the hashmap `sumIndex`, which can have up to 'n' distinct cumulative sums. In the worst case, all elements are unique, resulting in 'n' distinct cumulative sums, each with a count of 1. In summary, this code efficiently counts subarrays with a sum of 'k' in O(n) time and uses O(n) space to store cumulative sums and their counts. */ #include #include #include int subarraySum(std::vector& nums, int k) { // Create an unordered_map to store cumulative sums as keys and their counts as values. std::unordered_map sumIndex; // Initialize the result to 0. int result = 0; // Initialize a variable to track the current cumulative sum. int currentSum = 0; // Initialize the unordered_map with a key-value pair representing the sum of an empty subarray (0) and its count (1). sumIndex[0] = 1; // Iterate through the elements of the 'nums' vector. for (int i = 0; i < nums.size(); i++) { // Add the current element to the cumulative sum. currentSum += nums[i]; // Calculate the value to find in the unordered_map by subtracting 'k' from the current cumulative sum. int toFind = currentSum - k; // Check if the unordered_map contains the value 'toFind'. if (sumIndex.find(toFind) != sumIndex.end()) { // If found, add the count of subarrays that sum to 'toFind' to the 'result'. result += sumIndex[toFind]; } // Update the unordered_map with the current cumulative sum. // If it's already in the unordered_map, increment its count by 1. // If it's not in the unordered_map, add it with a count of 1. sumIndex[currentSum]++; } // Return the final result, which represents the total number of subarrays with a sum of 'k'. return result; } int main() { std::vector nums = {1, 1, 1}; int k = 2; int result = subarraySum(nums, k); std::cout << "Number of subarrays with sum " << k << " is: " << result << std::endl; return 0; } ================================================ FILE: Sliding Window/subaray_sum_equals_k.go ================================================ /* Given an array of integers nums and an integer k, return the total number of subarrays whose sum equals to k. A subarray is a contiguous non-empty sequence of elements within an array. Example 1: Input: nums = [1,1,1], k = 2 Output: 2 Example 2: Input: nums = [1,2,3], k = 3 Output: 2 ### Explanation: The code is designed to count the number of subarrays within the 'nums' array whose elements sum to a given target integer 'k'. It uses a hashmap to efficiently keep track of cumulative sums and their counts. Here's the code's key logic: 1. It initializes a hashmap `sumIndex` to store cumulative sums as keys and their counts as values. 2. It initializes variables `result` and `currentSum`. 3. It adds a key-value pair of `(0, 1)` to the `sumIndex` hashmap to represent the sum of an empty subarray (0) and its count (1). 4. It iterates through the elements of the 'nums' array. 5. For each element, it adds the element's value to `currentSum`. 6. It calculates the value to find in the hashmap by subtracting 'k' from the current cumulative sum, which is stored in the `toFind` variable. 7. It checks if the hashmap contains the value 'toFind' and, if found, adds the count of subarrays that sum to 'toFind' to the 'result'. 8. It updates the hashmap with the current cumulative sum. If the sum is already in the hashmap, it increments its count by 1. If it's not in the hashmap, it adds it with a count of 1. 9. Finally, it returns the 'result,' which represents the total number of subarrays with a sum of 'k'. ### Time Complexity: The time complexity of this code is O(n), where 'n' is the length of the 'nums' array. This is because the code iterates through the 'nums' array once, and each iteration consists of constant-time operations (e.g., hashmap lookups and additions). ### Space Complexity: The space complexity of this code is O(n), where 'n' is the length of the 'nums' array. The space is primarily used for the hashmap `sumIndex`, which can have up to 'n' distinct cumulative sums. In the worst case, all elements are unique, resulting in 'n' distinct cumulative sums, each with a count of 1. In summary, this code efficiently counts subarrays with a sum of 'k' in O(n) time and uses O(n) space to store cumulative sums and their counts. */ func subarraySum(nums []int, k int) int { // Create a map to store cumulative sums as keys and their counts as values. sumIndex := make(map[int]int) // Initialize the result to 0. result := 0 // Initialize a variable to track the current cumulative sum. currentSum := 0 // Initialize the map with a key-value pair representing the sum of an empty subarray (0) and its count (1). sumIndex[0] = 1 // Iterate through the elements of the 'nums' slice. for _, num := range nums { // Add the current element to the cumulative sum. currentSum += num // Calculate the value to find in the map by subtracting 'k' from the current cumulative sum. toFind := currentSum - k // Check if the map contains the value 'toFind'. if count, ok := sumIndex[toFind]; ok { // If found, add the count of subarrays that sum to 'toFind' to the 'result'. result += count } // Update the map with the current cumulative sum. // If it's already in the map, increment its count by 1. // If it's not in the map, add it with a count of 1. sumIndex[currentSum]++ } // Return the final result, which represents the total number of subarrays with a sum of 'k'. return result } ================================================ FILE: Sliding Window/subaray_sum_equals_k.java ================================================ /* Given an array of integers nums and an integer k, return the total number of subarrays whose sum equals to k. A subarray is a contiguous non-empty sequence of elements within an array. Example 1: Input: nums = [1,1,1], k = 2 Output: 2 Example 2: Input: nums = [1,2,3], k = 3 Output: 2 ### Explanation: The code is designed to count the number of subarrays within the 'nums' array whose elements sum to a given target integer 'k'. It uses a hashmap to efficiently keep track of cumulative sums and their counts. Here's the code's key logic: 1. It initializes a hashmap `sumIndex` to store cumulative sums as keys and their counts as values. 2. It initializes variables `result` and `currentSum`. 3. It adds a key-value pair of `(0, 1)` to the `sumIndex` hashmap to represent the sum of an empty subarray (0) and its count (1). 4. It iterates through the elements of the 'nums' array. 5. For each element, it adds the element's value to `currentSum`. 6. It calculates the value to find in the hashmap by subtracting 'k' from the current cumulative sum, which is stored in the `toFind` variable. 7. It checks if the hashmap contains the value 'toFind' and, if found, adds the count of subarrays that sum to 'toFind' to the 'result'. 8. It updates the hashmap with the current cumulative sum. If the sum is already in the hashmap, it increments its count by 1. If it's not in the hashmap, it adds it with a count of 1. 9. Finally, it returns the 'result,' which represents the total number of subarrays with a sum of 'k'. ### Time Complexity: The time complexity of this code is O(n), where 'n' is the length of the 'nums' array. This is because the code iterates through the 'nums' array once, and each iteration consists of constant-time operations (e.g., hashmap lookups and additions). ### Space Complexity: The space complexity of this code is O(n), where 'n' is the length of the 'nums' array. The space is primarily used for the hashmap `sumIndex`, which can have up to 'n' distinct cumulative sums. In the worst case, all elements are unique, resulting in 'n' distinct cumulative sums, each with a count of 1. In summary, this code efficiently counts subarrays with a sum of 'k' in O(n) time and uses O(n) space to store cumulative sums and their counts. */ public int subarraySum(int[] nums, int k) { // Create a hashmap to store cumulative sums as keys and their counts as values. HashMap sumIndex = new HashMap<>(); // Initialize the result to 0. int result = 0; // Initialize a variable to track the current cumulative sum. int currentSum = 0; // Initialize the hashmap with a key-value pair representing the sum of an empty subarray (0) and its count (1). sumIndex.put(0, 1); // Iterate through the elements of the 'nums' array. for (int i = 0; i < nums.length; i++) { // Add the current element to the cumulative sum. currentSum += nums[i]; // Calculate the value to find in the hashmap by subtracting 'k' from the current cumulative sum. int toFind = currentSum - k; // Check if the hashmap contains the value 'toFind'. if (sumIndex.containsKey(toFind)) { // If found, add the count of subarrays that sum to 'toFind' to the 'result'. result += sumIndex.get(toFind); } // Update the hashmap with the current cumulative sum. // If it's already in the hashmap, increment its count by 1. // If it's not in the hashmap, add it with a count of 1. sumIndex.put(currentSum, sumIndex.getOrDefault(currentSum, 0) + 1); } // Return the final result, which represents the total number of subarrays with a sum of 'k'. return result; } ================================================ FILE: Sliding Window/subaray_sum_equals_k.js ================================================ /* Given an array of integers nums and an integer k, return the total number of subarrays whose sum equals to k. A subarray is a contiguous non-empty sequence of elements within an array. Example 1: Input: nums = [1,1,1], k = 2 Output: 2 Example 2: Input: nums = [1,2,3], k = 3 Output: 2 ### Explanation: The code is designed to count the number of subarrays within the 'nums' array whose elements sum to a given target integer 'k'. It uses a hashmap to efficiently keep track of cumulative sums and their counts. Here's the code's key logic: 1. It initializes a hashmap `sumIndex` to store cumulative sums as keys and their counts as values. 2. It initializes variables `result` and `currentSum`. 3. It adds a key-value pair of `(0, 1)` to the `sumIndex` hashmap to represent the sum of an empty subarray (0) and its count (1). 4. It iterates through the elements of the 'nums' array. 5. For each element, it adds the element's value to `currentSum`. 6. It calculates the value to find in the hashmap by subtracting 'k' from the current cumulative sum, which is stored in the `toFind` variable. 7. It checks if the hashmap contains the value 'toFind' and, if found, adds the count of subarrays that sum to 'toFind' to the 'result'. 8. It updates the hashmap with the current cumulative sum. If the sum is already in the hashmap, it increments its count by 1. If it's not in the hashmap, it adds it with a count of 1. 9. Finally, it returns the 'result,' which represents the total number of subarrays with a sum of 'k'. ### Time Complexity: The time complexity of this code is O(n), where 'n' is the length of the 'nums' array. This is because the code iterates through the 'nums' array once, and each iteration consists of constant-time operations (e.g., hashmap lookups and additions). ### Space Complexity: The space complexity of this code is O(n), where 'n' is the length of the 'nums' array. The space is primarily used for the hashmap `sumIndex`, which can have up to 'n' distinct cumulative sums. In the worst case, all elements are unique, resulting in 'n' distinct cumulative sums, each with a count of 1. In summary, this code efficiently counts subarrays with a sum of 'k' in O(n) time and uses O(n) space to store cumulative sums and their counts. */ function subarraySum(nums, k) { // Create a map to store cumulative sums as keys and their counts as values. const sumIndex = new Map(); // Initialize the result to 0. let result = 0; // Initialize a variable to track the current cumulative sum. let currentSum = 0; // Initialize the map with a key-value pair representing the sum of an empty subarray (0) and its count (1). sumIndex.set(0, 1); // Iterate through the elements of the 'nums' array. for (let i = 0; i < nums.length; i++) { // Add the current element to the cumulative sum. currentSum += nums[i]; // Calculate the value to find in the map by subtracting 'k' from the current cumulative sum. const toFind = currentSum - k; // Check if the map contains the value 'toFind'. if (sumIndex.has(toFind)) { // If found, add the count of subarrays that sum to 'toFind' to the 'result'. result += sumIndex.get(toFind); } // Update the map with the current cumulative sum. // If it's already in the map, increment its count by 1. // If it's not in the map, add it with a count of 1. sumIndex.set(currentSum, (sumIndex.get(currentSum) || 0) + 1); } // Return the final result, which represents the total number of subarrays with a sum of 'k'. return result; } ================================================ FILE: Sliding Window/subaray_sum_equals_k.py ================================================ ''' Given an array of integers nums and an integer k, return the total number of subarrays whose sum equals to k. A subarray is a contiguous non-empty sequence of elements within an array. Example 1: Input: nums = [1,1,1], k = 2 Output: 2 Example 2: Input: nums = [1,2,3], k = 3 Output: 2 ### Explanation: The code is designed to count the number of subarrays within the 'nums' array whose elements sum to a given target integer 'k'. It uses a hashmap to efficiently keep track of cumulative sums and their counts. Here's the code's key logic: 1. It initializes a hashmap `sumIndex` to store cumulative sums as keys and their counts as values. 2. It initializes variables `result` and `currentSum`. 3. It adds a key-value pair of `(0, 1)` to the `sumIndex` hashmap to represent the sum of an empty subarray (0) and its count (1). 4. It iterates through the elements of the 'nums' array. 5. For each element, it adds the element's value to `currentSum`. 6. It calculates the value to find in the hashmap by subtracting 'k' from the current cumulative sum, which is stored in the `toFind` variable. 7. It checks if the hashmap contains the value 'toFind' and, if found, adds the count of subarrays that sum to 'toFind' to the 'result'. 8. It updates the hashmap with the current cumulative sum. If the sum is already in the hashmap, it increments its count by 1. If it's not in the hashmap, it adds it with a count of 1. 9. Finally, it returns the 'result,' which represents the total number of subarrays with a sum of 'k'. ### Time Complexity: The time complexity of this code is O(n), where 'n' is the length of the 'nums' array. This is because the code iterates through the 'nums' array once, and each iteration consists of constant-time operations (e.g., hashmap lookups and additions). ### Space Complexity: The space complexity of this code is O(n), where 'n' is the length of the 'nums' array. The space is primarily used for the hashmap `sumIndex`, which can have up to 'n' distinct cumulative sums. In the worst case, all elements are unique, resulting in 'n' distinct cumulative sums, each with a count of 1. In summary, this code efficiently counts subarrays with a sum of 'k' in O(n) time and uses O(n) space to store cumulative sums and their counts. ''' def subarraySum(nums, k): # Create a dictionary to store cumulative sums as keys and their counts as values. sum_index = {0: 1} # Initialize the result to 0. result = 0 # Initialize a variable to track the current cumulative sum. current_sum = 0 # Iterate through the elements of the 'nums' list. for num in nums: # Add the current element to the cumulative sum. current_sum += num # Calculate the value to find in the dictionary by subtracting 'k' from the current cumulative sum. to_find = current_sum - k # Check if the dictionary contains the value 'to_find'. if to_find in sum_index: # If found, add the count of subarrays that sum to 'to_find' to the 'result'. result += sum_index[to_find] # Update the dictionary with the current cumulative sum. # If it's already in the dictionary, increment its count by 1. # If it's not in the dictionary, add it with a count of 1. sum_index[current_sum] = sum_index.get(current_sum, 0) + 1 # Return the final result, which represents the total number of subarrays with a sum of 'k'. return result ================================================ FILE: Sliding Window/subarray_product_less_than_k,js ================================================ /** Given an array of integers nums and an integer k, return the number of contiguous subarrays where the product of all the elements in the subarray is strictly less than k. The provided code defines a Java class Solution with a method numSubarrayProductLessThanK that counts the number of subarrays in an input array nums whose product is less than a given threshold k. It uses a sliding window approach to efficiently compute this count. Time Complexity: The code iterates through the nums array once, using two pointers (startWindow and endWindow) to define the sliding window. This results in a time complexity of O(N), where N is the length of the input array nums. Space Complexity: The code uses a constant amount of additional space to store integer variables (startWindow, product, and count). Therefore, the space complexity is O(1), which means it is independent of the size of the input array. */ class Solution { numSubarrayProductLessThanK(nums, k) { let startWindow = 0; // The left end of the sliding window let product = 1; // Initialize product to 1 to accumulate the product let count = 0; // Count of subarrays with a product less than k // Iterate through the array using a sliding window approach for (let endWindow = 0; endWindow < nums.length; endWindow++) { // Multiply the current element to the product product *= nums[endWindow]; // Shrink the window by moving the start pointer as long as the product is greater than or equal to k while (startWindow <= endWindow && product >= k) { // Divide the product by the element at the start of the window product /= nums[startWindow]; // Move the start of the window to the right startWindow++; } // Update the count with the number of valid subarrays within the current window count += endWindow - startWindow + 1; } // Return the count, which represents the number of subarrays with a product less than k return count; } } // Example usage const solution = new Solution(); const result = solution.numSubarrayProductLessThanK([10, 5, 2, 6], 100); console.log(result); // Output: 8 ================================================ FILE: Sliding Window/subarray_product_less_than_k.cpp ================================================ /** Given an array of integers nums and an integer k, return the number of contiguous subarrays where the product of all the elements in the subarray is strictly less than k. The provided code defines a Java class Solution with a method numSubarrayProductLessThanK that counts the number of subarrays in an input array nums whose product is less than a given threshold k. It uses a sliding window approach to efficiently compute this count. Time Complexity: The code iterates through the nums array once, using two pointers (startWindow and endWindow) to define the sliding window. This results in a time complexity of O(N), where N is the length of the input array nums. Space Complexity: The code uses a constant amount of additional space to store integer variables (startWindow, product, and count). Therefore, the space complexity is O(1), which means it is independent of the size of the input array. */ #include class Solution { public: int numSubarrayProductLessThanK(std::vector& nums, int k) { int startWindow = 0; // The left end of the sliding window int product = 1; // Initialize product to 1 to accumulate the product int count = 0; // Count of subarrays with a product less than k // Iterate through the array using a sliding window approach for (int endWindow = 0; endWindow < nums.size(); endWindow++) { // Multiply the current element to the product product *= nums[endWindow]; // Shrink the window by moving the start pointer as long as the product is greater than or equal to k while (startWindow <= endWindow && product >= k) { // Divide the product by the element at the start of the window product /= nums[startWindow]; // Move the start of the window to the right startWindow++; } // Update the count with the number of valid subarrays within the current window count += endWindow - startWindow + 1; } // Return the count, which represents the number of subarrays with a product less than k return count; } }; ================================================ FILE: Sliding Window/subarray_product_less_than_k.go ================================================ /** Given an array of integers nums and an integer k, return the number of contiguous subarrays where the product of all the elements in the subarray is strictly less than k. The provided code defines a Java class Solution with a method numSubarrayProductLessThanK that counts the number of subarrays in an input array nums whose product is less than a given threshold k. It uses a sliding window approach to efficiently compute this count. Time Complexity: The code iterates through the nums array once, using two pointers (startWindow and endWindow) to define the sliding window. This results in a time complexity of O(N), where N is the length of the input array nums. Space Complexity: The code uses a constant amount of additional space to store integer variables (startWindow, product, and count). Therefore, the space complexity is O(1), which means it is independent of the size of the input array. */ func numSubarrayProductLessThanK(nums []int, k int) int { startWindow := 0 // The left end of the sliding window product := 1 // Initialize product to 1 to accumulate the product count := 0 // Count of subarrays with a product less than k // Iterate through the array using a sliding window approach for endWindow := 0; endWindow < len(nums); endWindow++ { // Multiply the current element to the product product *= nums[endWindow] // Shrink the window by moving the start pointer as long as the product is greater than or equal to k for startWindow <= endWindow && product >= k { // Divide the product by the element at the start of the window product /= nums[startWindow] // Move the start of the window to the right startWindow++ } // Update the count with the number of valid subarrays within the current window count += endWindow - startWindow + 1 } // Return the count, which represents the number of subarrays with a product less than k return count } ================================================ FILE: Sliding Window/subarray_product_less_than_k.java ================================================ /** Given an array of integers nums and an integer k, return the number of contiguous subarrays where the product of all the elements in the subarray is strictly less than k. The provided code defines a Java class Solution with a method numSubarrayProductLessThanK that counts the number of subarrays in an input array nums whose product is less than a given threshold k. It uses a sliding window approach to efficiently compute this count. Time Complexity: The code iterates through the nums array once, using two pointers (startWindow and endWindow) to define the sliding window. This results in a time complexity of O(N), where N is the length of the input array nums. Space Complexity: The code uses a constant amount of additional space to store integer variables (startWindow, product, and count). Therefore, the space complexity is O(1), which means it is independent of the size of the input array. */ class Solution { public int numSubarrayProductLessThanK(int[] nums, int k) { // Initialize pointers and a count variable int startWindow = 0; // The left end of the sliding window int product = 1; // Initialize product to 1 to accumulate the product int count = 0; // Count of subarrays with a product less than k // Iterate through the array using a sliding window approach for (int endWindow = 0; endWindow < nums.length; endWindow++) { // Multiply the current element to the product product *= nums[endWindow]; // Shrink the window by moving the start pointer as long as the product is greater than or equal to k while (startWindow <= endWindow && product >= k) { // Divide the product by the element at the start of the window product /= nums[startWindow]; // Move the start of the window to the right startWindow++; } // Update the count with the number of valid subarrays within the current window count += endWindow - startWindow + 1; } // Return the count, which represents the number of subarrays with a product less than k return count; } } ================================================ FILE: Sliding Window/subarray_product_less_than_k.py ================================================ ''' Given an array of integers nums and an integer k, return the number of contiguous subarrays where the product of all the elements in the subarray is strictly less than k. The provided code defines a Java class Solution with a method numSubarrayProductLessThanK that counts the number of subarrays in an input array nums whose product is less than a given threshold k. It uses a sliding window approach to efficiently compute this count. Time Complexity: The code iterates through the nums array once, using two pointers (startWindow and endWindow) to define the sliding window. This results in a time complexity of O(N), where N is the length of the input array nums. Space Complexity: The code uses a constant amount of additional space to store integer variables (startWindow, product, and count). Therefore, the space complexity is O(1), which means it is independent of the size of the input array. ''' from typing import List class Solution: def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int: startWindow = 0 # The left end of the sliding window product = 1 # Initialize product to 1 to accumulate the product count = 0 # Count of subarrays with a product less than k # Iterate through the list using a sliding window approach for endWindow in range(len(nums)): # Multiply the current element to the product product *= nums[endWindow] # Shrink the window by moving the start pointer as long as the product is greater than or equal to k while startWindow <= endWindow and product >= k: # Divide the product by the element at the start of the window product /= nums[startWindow] # Move the start of the window to the right startWindow += 1 # Update the count with the number of valid subarrays within the current window count += endWindow - startWindow + 1 # Return the count, which represents the number of subarrays with a product less than k return count ================================================ FILE: Stacks/Stack_with_max_API.cpp ================================================ /* Time Complexity of all Operations is O(1) Space Complexity is O(n) In this implementation, we use two stacks: s is the main stack that holds the elements of the stack, and max_s is a secondary stack that holds the maximum values in s. When a new value is pushed onto the stack, we compare it with the current maximum value in max_s and add it to max_s if it's greater than or equal to the current maximum. */ #include using namespace std; class MaxStack { private: stack s; // Main stack that holds the elements of the stack stack max_s; // Secondary stack that holds the maximum values in s public: void push(int val) { s.push(val); // Push the value onto the main stack if (max_s.empty() || val >= max_s.top()) { // If max_s is empty or the new value is greater than or equal to the current maximum, // push the value onto max_s max_s.push(val); } } void pop() { if (s.top() == max_s.top()) { // If the top value of s is equal to the top value of max_s, it means that the top value // of s is the current maximum, so we need to pop it from max_s as well max_s.pop(); } s.pop(); // Pop the value from the main stack } int top() { return s.top(); // Return the top value of the main stack } int max() { return max_s.top(); // Return the top value of max_s, which is the maximum value in the stack } bool empty() { return s.empty(); // Check if the main stack is empty } }; ================================================ FILE: Stacks/Stacks_using_queues.py ================================================ ''' Name : Abhinav kumar Github username : Abhinavcode13 Repository name : data-structures-and-algorithms Problem : Implement Stack using Queues in Python Issue Number : #261 Problem statement : Explanation of the below python code : __init__(self) initializes the two queues (q1 and q2) in the constructor. push(self, x: int) adds a new element x to the top of the stack. It first adds the element to q2, and then moves all the elements from q1 to q2 using a while loop. Finally, it swaps q1 and q2 so that q1 always contains the elements in the stack. pop(self) -> int removes and returns the element at the top of the stack, which is the first element in q1. top(self) -> int returns the element at the top of the stack, which is also the first element in q1. empty(self) -> bool returns True if the stack is empty (i.e., q1 is empty), and False otherwise. The deque data structure from the collections module is used to implement the two queues, since it provides efficient O(1) operations for adding and removing elements from both ends of the queue. Overall, this implementation is quite simple and efficient, since the push and pop operations take O(1) time complexity on average, while the top and empty operations take constant time. -------------------------------------------------------------------------//Python code begins here---------------------------------------------------------------------------- ''' from collections import deque class Stack: def __init__(self): #The Stack class is defined with a constructor that initializes a deque object as the Stack's underlying data structure. self.queue = deque() def push(self, x: int) -> None: self.queue.append(x) # Move all existing elements to the end of the queue #The push method takes an integer value as input and appends it to the end of the deque. It then moves all the existing elements in the deque to the end, effectively simulating the addition of the new element to the top of the Stack. for _ in range(len(self.queue) - 1): self.queue.append(self.queue.popleft()) def pop(self) -> int: return self.queue.popleft() #The pop method removes and returns the element at the top of the Stack, which is the first element in the deque. def top(self) -> int: #The top method returns the element at the top of the Stack without removing it, which is the first element in the deque. return self.queue[0] def empty(self) -> bool: #The empty method returns True if the Stack is empty (i.e., the deque has length 0), and False otherwise. return len(self.queue) == 0 stack = Stack() while True: print("1. Push") print("2. Pop") print("3. Top") print("4. Empty") print("5. Quit") choice = int(input("Enter your choice: ")) if choice == 1: x = int(input("Enter the element to push: ")) stack.push(x) print("Element pushed to stack") elif choice == 2: if stack.empty(): '''The code above shows an example usage of the Stack class, where the user can interact with the Stack through a command-line interface.The user is prompted with a menu of options to choose from, and their choice is read in as an integer value using the input() functiom''' Depending on the user's choice, the appropriate method is called on the stack object and the results are printed to the console. print("Stack is empty") else: x = stack.pop() print("Popped element:", x) elif choice == 3: if stack.empty(): print("Stack is empty") else: x = stack.top() print("Top element:", x) elif choice == 4: if stack.empty(): print("Stack is empty") else: print("Stack is not empty") elif choice == 5: break else: print("Invalid choice") ================================================ FILE: Stacks/balanced_parenthesis.go ================================================ // Checking balancing of symbols/parentheses. package main import ( "errors" "fmt" ) type Stack struct { top int capacity uint array []interface{} } // Init: Returns an Initialized Stack func (stack *Stack) Init(capacity uint) *Stack { stack.top = -1 stack.capacity = capacity stack.array = make([]interface{}, capacity) return stack } // NewStack: Returns a new Stack func NewStack(capacity uint) *Stack { return new(Stack).Init(capacity) } // Size: Returns the size of Stack // Time complexity O(1) func (stack *Stack) Size() uint { return uint(stack.top + 1) } // IsFull: Returns true if Stack is full or else false // Time complexity O(1) func (stack *Stack) IsFull() bool { // Stack is full when top is equal to the last index return stack.top == int(stack.capacity) - 1 } // IsEmpty: Returns true if Stack is empty or else false // Time complexity O(1) func (stack *Stack) IsEmpty() bool { // Stack is empty when top is equal to -1 return stack.top == -1 } // Resize: If the array is full, creates a new array of // twice the size, and copy the items func (stack *Stack) Resize() { fmt.Println("Resizing") if stack.IsFull() { stack.capacity *= 2 } else { stack.capacity /= 2 } target := make([]interface{}, stack.capacity) copy(target, stack.array[:stack.top+1]) stack.array = target } // Push: Pushes new [data] into Stack, resizes to double if stack is full // Time complexity O(1) // Space complexity for n push operations O(n) func (stack *Stack) Push(data interface{}) error { if stack.IsFull() { stack.Resize() } stack.top++ stack.array[stack.top] = data fmt.Printf("\n%v Pushed to stack", data) return nil } // Pop: Pops top most data from Stack, resizes to hald if sizeof stack is less than half the capacity // Time complexity O(1) func (stack *Stack) Pop() (interface{}, error) { if stack.IsEmpty() { return nil, errors.New("Stack is empty") } temp := stack.array[stack.top] fmt.Printf("\n%v Popped from stack", temp) stack.top-- if stack.Size() < stack.capacity / 2 { stack.Resize() } return temp, nil } // Peek: Returns top most element from Stack // Time complexity O(1) func (stack *Stack) Peek() (interface{}, error) { if stack.IsEmpty() { return nil, errors.New("Stack is empty") } temp := stack.array[stack.top] fmt.Printf("\n%v is the topmost element in stack", temp) return temp, nil } // Drain: Removes all elements that are currently in Stack // Time complexity O(1) func (stack *Stack) Drain() { stack.array = nil stack.top = -1 } type Pair struct { open rune close rune } var pairs = []Pair { {'(', ')'}, {'[', ']'}, {'{', '}'}, } // IsValid: returns true if string has balanced parenthesis // Approach : If the character read is not a symbol to be balanced, ignore it. // If the character is an opening symbol like (, [, {, push it onto the stack. // If it is a closing symbol like ),],}, then if the stack is empty report an error. Otherwise pop the stack. // If the symbol popped is not the corresponding opening symbol, report an error. // At end of input, if the stack is not empty report an error // Time Complexity: O(n). Since we are scanning the input only once. Space Complexity: O(n) [for stack]. func IsValid(s string) bool { stack := NewStack(1) for _, r := range s { for _, p := range pairs { temp, _ := stack.Peek() if r == p.open { stack.Push(r) } else if r == p.close && stack.IsEmpty() { return false } else if r == p.close && temp == p.open { stack.Pop() break } else if r == p.close && temp != p.open { return false } } } return stack.IsEmpty() } func main() { fmt.Println(IsValid("()(1[23){}{}")) } ================================================ FILE: Stacks/is_palindrome.go ================================================ package main // IsPalindrome: returns true if supplied string is a palindrome // Time Complexity: O(n). Space Complexity: O(n/2) ≈ O(n). func IsPalindrome(s string) bool { stack := NewStack(1) i, n := 0, len(s) // push hald element into stack for i < n/2 { stack.Push(s[i]) i++ } // escape mid character if n%2 == 1 { i++ } // compare other half of string with stack for i < len(s) { // if element doesn't match, or stack is empty then return false if stack.IsEmpty() || s[i] != stack.Pop() { return false } i++ } return true } ================================================ FILE: Stacks/next_greater_element.c++ ================================================ /* Write a function that takes in an array of integers and returns a new array containing, at each index, the next element in the input array that's greater than the element at that index in the input array. Sample Input:[2, 5, -3, -4, 6, 7, 2] Output: [5, 6, 6, 6, 7, -1, 5] Explanation: The given code snippet implements the Next Greater Element algorithm. Here's how it works: 1. The function `NextGreaterElement` takes an input array of integers and returns an array of the same length where each element represents the next greater element in the input array. If there is no greater element, the corresponding output element is set to -1. 2. The `result` slice is initialized with -1 values, indicating that there are no greater elements initially. 3. The `stack` is used to keep track of indices of elements from the input array. It will store indices in a way that maintains a decreasing order of values from the input array. 4. The algorithm performs two passes over the input array. In each pass, it considers the array elements in a circular manner by using the modulo operator `%` on the index. 5. In the inner loop, the algorithm checks if the current element is greater than the element at the top of the stack. If it is, it means the current element is the next greater element for the element at the top of the stack. 6. If a greater element is found, the top index is retrieved from the stack, and the corresponding element in the `result` slice is updated with the current element from the input array. 7. After updating the `result` slice, the top index is removed from the stack. 8. The current circular index is then pushed onto the stack to potentially find the next greater element for it in the future. 9. Once the algorithm completes the two passes over the input array, the `result` slice contains the next greater elements for each element in the input array, or -1 if there is no greater element. 10. The `result` slice is returned as the output. The algorithm utilizes a stack to efficiently find the next greater element for each element in the input array. By iterating over the array twice in a circular manner, it ensures that all elements have been considered for finding the next greater elements. Note that this implementation assumes the availability of the built-in append function to modify slices in Go. The time complexity of the `NextGreaterElement` function is O(n), where n is the length of the input array. This is because the function performs two passes over the input array, and in each pass, it processes each element once. The operations performed within each iteration, such as stack operations, have constant time complexity. The space complexity of the function is O(n) as well. This is because the function creates two additional slices: `result` and `stack`, each with a maximum size of n. Therefore, the space required by the function grows linearly with the size of the input array. */ #include #include std::vector nextGreaterElement(std::vector& array) { std::vector result(array.size(), -1); // Initialize result vector with -1 for all elements std::stack stack; // Stack to store indices of elements for (int idx = 0; idx < array.size() * 2; idx++) { int circularIdx = idx % array.size(); // Obtain the circular index of the current element while (!stack.empty() && array[circularIdx] > array[stack.top()]) { // While the stack is not empty and the current element is greater than the element at the top of the stack int top = stack.top(); // Get the index from the top of the stack stack.pop(); // Pop the index from the stack result[top] = array[circularIdx]; // Update the result for the popped index } stack.push(circularIdx); // Push the current index to the stack } return result; } ================================================ FILE: Stacks/next_greater_element.go ================================================ /* Write a function that takes in an array of integers and returns a new array containing, at each index, the next element in the input array that's greater than the element at that index in the input array. Sample Input:[2, 5, -3, -4, 6, 7, 2] Output: [5, 6, 6, 6, 7, -1, 5] Explanation: The given code snippet implements the Next Greater Element algorithm. Here's how it works: 1. The function `NextGreaterElement` takes an input array of integers and returns an array of the same length where each element represents the next greater element in the input array. If there is no greater element, the corresponding output element is set to -1. 2. The `result` slice is initialized with -1 values, indicating that there are no greater elements initially. 3. The `stack` is used to keep track of indices of elements from the input array. It will store indices in a way that maintains a decreasing order of values from the input array. 4. The algorithm performs two passes over the input array. In each pass, it considers the array elements in a circular manner by using the modulo operator `%` on the index. 5. In the inner loop, the algorithm checks if the current element is greater than the element at the top of the stack. If it is, it means the current element is the next greater element for the element at the top of the stack. 6. If a greater element is found, the top index is retrieved from the stack, and the corresponding element in the `result` slice is updated with the current element from the input array. 7. After updating the `result` slice, the top index is removed from the stack. 8. The current circular index is then pushed onto the stack to potentially find the next greater element for it in the future. 9. Once the algorithm completes the two passes over the input array, the `result` slice contains the next greater elements for each element in the input array, or -1 if there is no greater element. 10. The `result` slice is returned as the output. The algorithm utilizes a stack to efficiently find the next greater element for each element in the input array. By iterating over the array twice in a circular manner, it ensures that all elements have been considered for finding the next greater elements. Note that this implementation assumes the availability of the built-in append function to modify slices in Go. The time complexity of the `NextGreaterElement` function is O(n), where n is the length of the input array. This is because the function performs two passes over the input array, and in each pass, it processes each element once. The operations performed within each iteration, such as stack operations, have constant time complexity. The space complexity of the function is O(n) as well. This is because the function creates two additional slices: `result` and `stack`, each with a maximum size of n. Therefore, the space required by the function grows linearly with the size of the input array. */ package main // NextGreaterElement finds the next greater element for each element in the input array. func NextGreaterElement(array []int) []int { // Create a result slice to store the next greater elements. result := make([]int, 0) // Initialize the result slice with -1 values indicating no greater elements initially. for range array { result = append(result, -1) } // Create a stack to keep track of indices of elements from the input array. stack := make([]int, 0) // Perform two passes over the input array, considering elements in a circular manner. for idx := 0; idx < 2*len(array); idx++ { // Calculate the circular index by using the modulo operator. circularIdx := idx % len(array) // Check if the current element is greater than the element at the top of the stack. for len(stack) > 0 && array[circularIdx] > array[stack[len(stack)-1]] { // Retrieve the top index from the stack. top := stack[len(stack)-1] // Update the corresponding element in the result slice with the current element. result[top] = array[circularIdx] // Remove the top index from the stack. stack = stack[:len(stack)-1] } // Push the current circular index onto the stack. stack = append(stack, circularIdx) } // Return the result slice containing the next greater elements. return result } ================================================ FILE: Stacks/next_greater_element.java ================================================ /* Write a function that takes in an array of integers and returns a new array containing, at each index, the next element in the input array that's greater than the element at that index in the input array. Sample Input:[2, 5, -3, -4, 6, 7, 2] Output: [5, 6, 6, 6, 7, -1, 5] Explanation: The given code snippet implements the Next Greater Element algorithm. Here's how it works: 1. The function `NextGreaterElement` takes an input array of integers and returns an array of the same length where each element represents the next greater element in the input array. If there is no greater element, the corresponding output element is set to -1. 2. The `result` slice is initialized with -1 values, indicating that there are no greater elements initially. 3. The `stack` is used to keep track of indices of elements from the input array. It will store indices in a way that maintains a decreasing order of values from the input array. 4. The algorithm performs two passes over the input array. In each pass, it considers the array elements in a circular manner by using the modulo operator `%` on the index. 5. In the inner loop, the algorithm checks if the current element is greater than the element at the top of the stack. If it is, it means the current element is the next greater element for the element at the top of the stack. 6. If a greater element is found, the top index is retrieved from the stack, and the corresponding element in the `result` slice is updated with the current element from the input array. 7. After updating the `result` slice, the top index is removed from the stack. 8. The current circular index is then pushed onto the stack to potentially find the next greater element for it in the future. 9. Once the algorithm completes the two passes over the input array, the `result` slice contains the next greater elements for each element in the input array, or -1 if there is no greater element. 10. The `result` slice is returned as the output. The algorithm utilizes a stack to efficiently find the next greater element for each element in the input array. By iterating over the array twice in a circular manner, it ensures that all elements have been considered for finding the next greater elements. The time complexity of the `NextGreaterElement` function is O(n), where n is the length of the input array. This is because the function performs two passes over the input array, and in each pass, it processes each element once. The operations performed within each iteration, such as stack operations, have constant time complexity. The space complexity of the function is O(n) as well. This is because the function creates two additional slices: `result` and `stack`, each with a maximum size of n. Therefore, the space required by the function grows linearly with the size of the input array. */ import java.util.*; public class NextGreaterElement { public int[] nextGreaterElement(int[] array) { int[] result = new int[array.length]; Arrays.fill(result, -1); // Initialize result array with -1 for all elements Stack stack = new Stack<>(); // Stack to store indices of elements for (int idx = 0; idx < array.length * 2; idx++) { int circularIdx = idx % array.length; // Obtain the circular index of the current element while (!stack.isEmpty() && array[circularIdx] > array[stack.peek()]) { // While the stack is not empty and the current element is greater than the element at the top of the stack int top = stack.pop(); // Pop the index from the stack result[top] = array[circularIdx]; // Update the result for the popped index } stack.push(circularIdx); // Push the current index to the stack } return result; } } ================================================ FILE: Stacks/next_greater_element.js ================================================ /* Write a function that takes in an array of integers and returns a new array containing, at each index, the next element in the input array that's greater than the element at that index in the input array. Sample Input:[2, 5, -3, -4, 6, 7, 2] Output: [5, 6, 6, 6, 7, -1, 5] Explanation: The given code snippet implements the Next Greater Element algorithm. Here's how it works: 1. The function `NextGreaterElement` takes an input array of integers and returns an array of the same length where each element represents the next greater element in the input array. If there is no greater element, the corresponding output element is set to -1. 2. The `result` slice is initialized with -1 values, indicating that there are no greater elements initially. 3. The `stack` is used to keep track of indices of elements from the input array. It will store indices in a way that maintains a decreasing order of values from the input array. 4. The algorithm performs two passes over the input array. In each pass, it considers the array elements in a circular manner by using the modulo operator `%` on the index. 5. In the inner loop, the algorithm checks if the current element is greater than the element at the top of the stack. If it is, it means the current element is the next greater element for the element at the top of the stack. 6. If a greater element is found, the top index is retrieved from the stack, and the corresponding element in the `result` slice is updated with the current element from the input array. 7. After updating the `result` slice, the top index is removed from the stack. 8. The current circular index is then pushed onto the stack to potentially find the next greater element for it in the future. 9. Once the algorithm completes the two passes over the input array, the `result` slice contains the next greater elements for each element in the input array, or -1 if there is no greater element. 10. The `result` slice is returned as the output. The algorithm utilizes a stack to efficiently find the next greater element for each element in the input array. By iterating over the array twice in a circular manner, it ensures that all elements have been considered for finding the next greater elements. The time complexity of the `NextGreaterElement` function is O(n), where n is the length of the input array. This is because the function performs two passes over the input array, and in each pass, it processes each element once. The operations performed within each iteration, such as stack operations, have constant time complexity. The space complexity of the function is O(n) as well. This is because the function creates two additional slices: `result` and `stack`, each with a maximum size of n. Therefore, the space required by the function grows linearly with the size of the input array. */ function nextGreaterElement(array) { const result = new Array(array.length).fill(-1); // Initialize result array with -1 for all elements const stack = []; // Stack to store indices of elements for (let idx = 0; idx < array.length * 2; idx++) { const circularIdx = idx % array.length; // Obtain the circular index of the current element while ( stack.length && array[circularIdx] > array[stack[stack.length - 1]] ) { // While the stack is not empty and the current element is greater than the element at the top of the stack const top = stack.pop(); // Pop the index from the stack result[top] = array[circularIdx]; // Update the result for the popped index } stack.push(circularIdx); // Push the current index to the stack } return result; } ================================================ FILE: Stacks/next_greater_element.py ================================================ ''' Write a function that takes in an array of integers and returns a new array containing, at each index, the next element in the input array that's greater than the element at that index in the input array. Sample Input:[2, 5, -3, -4, 6, 7, 2] Output: [5, 6, 6, 6, 7, -1, 5] Explanation: The given code snippet implements the Next Greater Element algorithm. Here's how it works: 1. The function `NextGreaterElement` takes an input array of integers and returns an array of the same length where each element represents the next greater element in the input array. If there is no greater element, the corresponding output element is set to -1. 2. The `result` slice is initialized with -1 values, indicating that there are no greater elements initially. 3. The `stack` is used to keep track of indices of elements from the input array. It will store indices in a way that maintains a decreasing order of values from the input array. 4. The algorithm performs two passes over the input array. In each pass, it considers the array elements in a circular manner by using the modulo operator `%` on the index. 5. In the inner loop, the algorithm checks if the current element is greater than the element at the top of the stack. If it is, it means the current element is the next greater element for the element at the top of the stack. 6. If a greater element is found, the top index is retrieved from the stack, and the corresponding element in the `result` slice is updated with the current element from the input array. 7. After updating the `result` slice, the top index is removed from the stack. 8. The current circular index is then pushed onto the stack to potentially find the next greater element for it in the future. 9. Once the algorithm completes the two passes over the input array, the `result` slice contains the next greater elements for each element in the input array, or -1 if there is no greater element. 10. The `result` slice is returned as the output. The algorithm utilizes a stack to efficiently find the next greater element for each element in the input array. By iterating over the array twice in a circular manner, it ensures that all elements have been considered for finding the next greater elements. The time complexity of the `NextGreaterElement` function is O(n), where n is the length of the input array. This is because the function performs two passes over the input array, and in each pass, it processes each element once. The operations performed within each iteration, such as stack operations, have constant time complexity. The space complexity of the function is O(n) as well. This is because the function creates two additional slices: `result` and `stack`, each with a maximum size of n. Therefore, the space required by the function grows linearly with the size of the input array. ''' def next_greater_element(array): result = [-1] * len(array) # Initialize result array with -1 for all elements stack = [] # Stack to store indices of elements for idx in range(len(array) * 2): circular_idx = idx % len(array) # Obtain the circular index of the current element while stack and array[circular_idx] > array[stack[-1]]: # While the stack is not empty and the current element is greater than the element at the top of the stack top = stack.pop() # Pop the index from the stack result[top] = array[circular_idx] # Update the result for the popped index stack.append(circular_idx) # Push the current index to the stack return result ================================================ FILE: Stacks/queue using stack.go ================================================ // Queue using stack package main import "fmt" // Queue represents a queue data structure using two stacks. type Queue struct { enqueueStack []int // Stack for enqueue operations dequeueStack []int // Stack for dequeue operations } // Enqueue adds an element to the back of the queue. func (q *Queue) Enqueue(value int) { q.enqueueStack = append(q.enqueueStack, value) } // Dequeue removes and returns the front element of the queue. func (q *Queue) Dequeue() int { // If the dequeue stack is empty, transfer elements from the enqueue stack if len(q.dequeueStack) == 0 { for len(q.enqueueStack) > 0 { // Pop an element from the enqueue stack and push it onto the dequeue stack element := q.enqueueStack[len(q.enqueueStack)-1] q.enqueueStack = q.enqueueStack[:len(q.enqueueStack)-1] q.dequeueStack = append(q.dequeueStack, element) } } // If the dequeue stack is still empty, the queue is empty if len(q.dequeueStack) == 0 { panic("Queue is empty") } // Pop and return the front element from the dequeue stack element := q.dequeueStack[len(q.dequeueStack)-1] q.dequeueStack = q.dequeueStack[:len(q.dequeueStack)-1] return element } func main() { queue := Queue{} // Enqueue elements queue.Enqueue(1) queue.Enqueue(2) queue.Enqueue(3) // Dequeue elements fmt.Println(queue.Dequeue()) // Output: 1 fmt.Println(queue.Dequeue()) // Output: 2 // Enqueue more elements queue.Enqueue(4) queue.Enqueue(5) // Dequeue the remaining elements fmt.Println(queue.Dequeue()) // Output: 3 fmt.Println(queue.Dequeue()) // Output: 4 fmt.Println(queue.Dequeue()) // Output: 5 } ================================================ FILE: Stacks/queue using stack.java ================================================ /* Issue:#253 Author:maneesha Date:13/06/2023 ##Assignee:Mani1881 //About: Implement Queue using Stacks in java //Input: >>Implement a first in first out (FIFO) queue using only two stacks. >>The implemented queue should support all the functions of a normal queue (push, peek, pop, and empty). Implement the MyQueue class: ~void push(int x) Pushes element x to the back of the queue. ~int pop() Removes the element from the front of the queue and returns it. ~int peek() Returns the element at the front of the queue. ~boolean empty() Returns true if the queue is empty, false . //Example: >> Sample Input: ["MyQueue", "push", "push", "peek", "pop", "empty"] [[], [1], [2], [], [], []] >>Sample Output: [null, null, null, 1, 1, false] Explanation MyQueue myQueue = new MyQueue(); myQueue.push(1); // queue is: [1](here we are not returning push element so in output we got null) myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue) myQueue.peek(); // return 1 myQueue.pop(); // return 1, queue is [2] myQueue.empty(); // return false //Complexity: >>Time Complexity push:O(n) pop:O(1) peek:O(1) empty:O(1) >>Space Complexity:O(n) //Example: //Explanation >>The provided code implements a queue using two stacks (`s1` and `s2`). >>The `push()` method transfers elements from `s1` to `s2`, adds the new element to `s1`, and then transfers elements back to `s1`. >>The `pop()` method removes and returns the top element of `s1`. >>The `peek()` method returns the top element of `s1` without removing it. >>The `empty()` method checks if `s1` is empty. >>This implementation maintains FIFO order. */ class MyQueue { Stacks1=new Stack(); Stacks2=new Stack(); public void push(int x) { while(!s1.isEmpty()) { s2.push(s1.pop()); } s1.push(x); while(!s2.empty()) { s1.push(s2.pop()); } } public int pop() { return s1.pop(); } public int peek() { return s1.peek(); } public boolean empty() { return s1.isEmpty(); } public static void main(String[] args) { MyQueue queue = new MyQueue(); // Pushing elements into the queue queue.push(1); queue.push(2); queue.push(3); // Checking the front element of the queue System.out.println("Front element: " + queue.peek()); // Output: Front element: 1 // Removing elements from the queue System.out.println("Removed element: " + queue.pop()); // Output: Removed element: 1 System.out.println("Removed element: " + queue.pop()); // Output: Removed element: 2 // Checking if the queue is empty System.out.println("Is the queue empty? " + queue.empty()); // Output: Is the queue empty? false // Pushing another element into the queue queue.push(4); // Removing the remaining elements from the queue System.out.println("Removed element: " + queue.pop()); // Output: Removed element: 3 System.out.println("Removed element: " + queue.pop()); // Output: Removed element: 4 // Checking if the queue is empty after removing all elements System.out.println("Is the queue empty? " + queue.empty()); // Output: Is the queue empty? true } } ================================================ FILE: Stacks/queue using stack.js ================================================ // Queue using stack class Queue { constructor() { this.enqueueStack = []; // Stack for enqueue operations this.dequeueStack = []; // Stack for dequeue operations } enqueue(value) { // Enqueue: Add an element to the back of the queue. this.enqueueStack.push(value); } dequeue() { // Dequeue: Remove and return the front element of the queue. // If the dequeue stack is empty, transfer elements from the enqueue stack if (this.dequeueStack.length === 0) { while (this.enqueueStack.length > 0) { // Pop an element from the enqueue stack and push it onto the dequeue stack const element = this.enqueueStack.pop(); this.dequeueStack.push(element); } } // If the dequeue stack is still empty, the queue is empty if (this.dequeueStack.length === 0) { throw new Error("Queue is empty"); } // Pop and return the front element from the dequeue stack return this.dequeueStack.pop(); } } // Example usage const queue = new Queue(); queue.enqueue(1); queue.enqueue(2); queue.enqueue(3); console.log(queue.dequeue()); // Output: 1 console.log(queue.dequeue()); // Output: 2 queue.enqueue(4); queue.enqueue(5); console.log(queue.dequeue()); // Output: 3 console.log(queue.dequeue()); // Output: 4 console.log(queue.dequeue()); // Output: 5 ================================================ FILE: Stacks/queue using stack.py ================================================ # Queue using stack class Queue: def __init__(self): self.enqueue_stack = [] # Stack for enqueue operations self.dequeue_stack = [] # Stack for dequeue operations def enqueue(self, value): """Enqueue: Add an element to the back of the queue.""" self.enqueue_stack.append(value) def dequeue(self): """Dequeue: Remove and return the front element of the queue.""" # If the dequeue stack is empty, transfer elements from the enqueue stack if not self.dequeue_stack: while self.enqueue_stack: # Pop an element from the enqueue stack and push it onto the dequeue stack element = self.enqueue_stack.pop() self.dequeue_stack.append(element) # If the dequeue stack is still empty, the queue is empty if not self.dequeue_stack: raise IndexError("Queue is empty") # Pop and return the front element from the dequeue stack return self.dequeue_stack.pop() # Example usage queue = Queue() queue.enqueue(1) queue.enqueue(2) queue.enqueue(3) print(queue.dequeue()) # Output: 1 print(queue.dequeue()) # Output: 2 queue.enqueue(4) queue.enqueue(5) print(queue.dequeue()) # Output: 3 print(queue.dequeue()) # Output: 4 print(queue.dequeue()) # Output: 5 ================================================ FILE: Stacks/queue_using_stacks.cpp ================================================ /* The basic difference in stack and queues is the order they are filled in practically.In Stacks, they are inserted on top of previous element while In Queues they are inserted behind the last element. The push() function is what we have to look out for. First we will empty out our primary stack s1 to another stack s2. Then We push the element to be inserted x in s1(or s2). Then just empty out the stack s2 back into s1. But the element x would be at the last place just like in a Queue. The other Operations are the same as we have already implemented as a queue.*/ /*Complexity Analysis: Time Complexity: Push operation : O(1). Same as pop operation in stack. Pop operation : O(N). The difference from above method is that in this method element is returned and all elements are restored back in a single call. Auxiliary Space: O(N). Use of stack for storing values. */ #include using namespace std; struct Queue { stack s; // Enqueue an item to the queue void enQueue(int x) { s.push(x); } // Dequeue an item from the queue int deQueue() { if (s.empty()) { cout << "Q is empty"; exit(0); } // pop an item from the stack int x = s.top(); s.pop(); // if stack becomes empty, return // the popped item if (s.empty()) return x; // recursive call int item = deQueue(); // push popped item back to the stack s.push(x); // return the result of deQueue() call return item; } }; // Driver code int main() { Queue q; q.enQueue(10); q.enQueue(20); q.enQueue(30); cout << q.deQueue() << '\n'; cout << q.deQueue() << '\n'; cout << q.deQueue() << '\n'; return 0; } ================================================ FILE: Stacks/reverse_polish_notation.cpp ================================================ /* You are given an array of strings tokens that represents an arithmetic expression in a Reverse Polish Notation. Evaluate the expression. Return an integer that represents the value of the expression. Note that: The valid operators are '+', '-', '*', and '/'. Each operand may be an integer or another expression. The division between two integers always truncates toward zero. There will not be any division by zero. The input represents a valid arithmetic expression in a reverse polish notation. The answer and all the intermediate calculations can be represented in a 32-bit integer. Example 1: Input: tokens = ["2","1","+","3","*"] Output: 9 Explanation: ((2 + 1) * 3) = 9 Example 2: Input: tokens = ["4","13","5","/","+"] Output: 6 Explanation: (4 + (13 / 5)) = 6 Example 3: Input: tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"] Output: 22 Explanation: ((10 * (6 / ((9 + 3) * -11))) + 17) + 5 = ((10 * (6 / (12 * -11))) + 17) + 5 = ((10 * (6 / -132)) + 17) + 5 = ((10 * 0) + 17) + 5 = (0 + 17) + 5 = 17 + 5 = 22 Constraints: 1 <= tokens.length <= 104 tokens[i] is either an operator: "+", "-", "*", or "/", or an integer in the range [-200, 200]. */ class Solution { public: int evalRPN(vector& tokens) { stack S; for(int i = 0; i < tokens.size(); i++){ if(tokens[i] == "+" || tokens[i] == "-" || tokens[i] == "*" || tokens[i] == "/"){ int v1 = S.top(); S.pop(); int v2 = S.top(); S.pop(); if(tokens[i] == "+"){ S.push(v1 + v2); } if(tokens[i] == "-"){ S.push(v2 - v1); } if(tokens[i] == "*"){ S.push(v1 * v2); } if(tokens[i] == "/"){ S.push(v2 / v1); } } else{ S.push(atoi(tokens[i].c_str())); } } return S.top(); } }; ================================================ FILE: Stacks/stack.cpp ================================================ // Implemenmtation of stack data structure /* This implementation creates a class Stack that has a private integer top to keep track of the index of the topmost element in the stack, and an integer array arr to store the elements of the stack. The push, pop, peek, and isEmpty methods are used to add elements to, remove elements from, view the topmost element in, and check if the stack is empty, respectively. The main function creates an instance of Stack and demonstrates the use of the stack by pushing some elements onto it, printing the topmost element, popping an element from the stack, and checking if the stack is empty. */ #include using namespace std; const int MAX_SIZE = 100; // Maximum size of stack class Stack { private: int top; // Index of topmost element in stack int arr[MAX_SIZE]; // Array to store elements of stack public: Stack() { top = -1; // Initialize top index to -1, indicating empty stack } // Pushes an element onto the top of the stack void push(int x) { if (top == MAX_SIZE - 1) { // Check if stack is full cout << "Stack Overflow\n"; return; } arr[++top] = x; // Increment top index and add element to array } // Removes and returns the topmost element from the stack int pop() { if (top == -1) { // Check if stack is empty cout << "Stack Underflow\n"; return -1; } int val = arr[top]; // Store topmost element in a variable top--; // Decrement top index to remove element from stack return val; // Return topmost element } // Returns the topmost element in the stack without removing it int peek() { if (top == -1) { // Check if stack is empty cout << "Stack Underflow\n"; return -1; } return arr[top]; // Return topmost element } // Returns true if stack is empty, false otherwise bool isEmpty() { return top == -1; } }; int main() { Stack s; // Push some elements onto the stack s.push(5); s.push(10); s.push(15); // Print the topmost element without removing it cout << "Top element: " << s.peek() << endl; // Pop an element from the stack and print it cout << "Popped element: " << s.pop() << endl; // Check if stack is empty cout << "Is stack empty? " << (s.isEmpty() ? "Yes" : "No") << endl; return 0; } ================================================ FILE: Stacks/stack.go ================================================ // Implement Stack Data Structure // TODO: Make Stack Generic package main import "fmt" type Stack []string // IsEmpty func checks if the stack is empty func (s *Stack) IsEmpty() bool { return len(*s) == 0 } // Push func pushes a new value into the stack func (s *Stack) Push(str string) { fmt.Printf("%s pushed into stack\n", str) *s = append(*s, str) } // Pop func pops the top most value from stack if there is any func (s *Stack) Pop() (string, bool) { if s.IsEmpty() { return "", false } else { index := len(*s) - 1 element := (*s)[index] *s = (*s)[:index] return element, true } } func main() { var stack Stack // push 3 values in stack stack.Push("Hello0") stack.Push("Hello1") stack.Push("Hello2") // pop out all values from stack for len(stack) > 0 { x, y := stack.Pop() if y == true { fmt.Printf("%s popped from stack\n", x) } } } ================================================ FILE: Stacks/stack.java ================================================ // Implemenmtation of stack data structure /* This implementation uses a generic type T to allow the stack to store elements of any type. The stack is implemented using an array and has methods for pushing, popping, peeking, checking if the stack is empty, and getting the size of the stack. The comments explain each step of the implementation. */ public class Stack { // array to store elements of the stack private T[] elements; // top index of the stack private int top; // constructor to initialize the stack with a given capacity public Stack(int capacity) { // create a new array of type T with the given capacity elements = (T[]) new Object[capacity]; // initialize top index to -1 top = -1; } // push an element onto the top of the stack public void push(T element) { // check if the stack is full if (top == elements.length - 1) { // if the stack is full, throw an exception throw new RuntimeException("Stack overflow"); } // increment top index top++; // insert the element at the top index elements[top] = element; } // pop the top element from the stack and return it public T pop() { // check if the stack is empty if (top == -1) { // if the stack is empty, throw an exception throw new RuntimeException("Stack underflow"); } // get the top element T element = elements[top]; // decrement top index top--; // return the top element return element; } // return the top element of the stack without removing it public T peek() { // check if the stack is empty if (top == -1) { // if the stack is empty, throw an exception throw new RuntimeException("Stack underflow"); } // return the top element return elements[top]; } // return true if the stack is empty, false otherwise public boolean isEmpty() { return top == -1; } // return the size of the stack public int size() { return top + 1; } } ================================================ FILE: Stacks/stack.js ================================================ // Implemenmtation of stack data structure /* We define a class called Stack with an empty array items to store the stack elements. The class has four methods: push(item): Adds an item to the top of the stack by using the push method of the Array object. pop(): Removes and returns the item at the top of the stack using the pop method of the Array object. peek(): Returns the item at the top of the stack without removing it using the slice method of the Array object. isEmpty(): Returns true if the stack is empty, false otherwise by checking the length of the items array. This implementation uses the built-in Array object of JavaScript to implement the stack data structure. The push, pop, and slice methods are used to manipulate the items array to add, remove, and access the top item of the stack, respectively. The isEmpty method simply checks if the items array is empty or not. */ class Stack { constructor() { this.items = []; // initializing an empty array } // push function to add an element to the stack push(element) { this.items.push(element); } // pop function to remove the topmost element from the stack pop() { // checking if the stack is empty if (this.items.length === 0) return "Underflow"; return this.items.pop(); } // peek function to get the topmost element of the stack without removing it peek() { // checking if the stack is empty if (this.items.length === 0) return "No elements in Stack"; return this.items[this.items.length - 1]; } // isEmpty function to check if the stack is empty isEmpty() { return this.items.length === 0; } // printStack function to print the entire stack printStack() { let str = ""; for (let i = 0; i < this.items.length; i++) { str += this.items[i] + " "; } return str; } } // creating an instance of the Stack class let stack = new Stack(); // adding elements to the stack stack.push(10); stack.push(20); stack.push(30); // printing the stack console.log(stack.printStack()); // Output: 10 20 30 // removing the topmost element from the stack stack.pop(); // printing the stack console.log(stack.printStack()); // Output: 10 20 // checking the topmost element of the stack console.log(stack.peek()); // Output: 20 // checking if the stack is empty console.log(stack.isEmpty()); // Output: false ================================================ FILE: Stacks/stack.py ================================================ # Implemenmtation of stack data structure ''' In this implementation, the Stack class contains the following methods: __init__: Constructor function to initialize an empty stack. is_empty: Check if stack is empty. push: Push an item onto the stack. pop: Remove and return the top item from the stack. peek: Return the top item from the stack without removing it. size: Return the number of items in the stack. Each method includes a docstring that explains what it does, along with any arguments and return values. ''' class Stack: """ Stack class implementation using list in Python """ def __init__(self): """ Constructor function to initialize an empty stack """ self.items = [] def is_empty(self): """ Check if stack is empty Returns: bool: True if stack is empty, False otherwise """ return len(self.items) == 0 def push(self, item): """ Push an item onto the stack Args: item: Item to be pushed onto the stack """ self.items.append(item) def pop(self): """ Remove and return the top item from the stack Returns: item: Top item from the stack Raises: IndexError: If stack is empty """ if self.is_empty(): raise IndexError("Stack is empty") return self.items.pop() def peek(self): """ Return the top item from the stack without removing it Returns: item: Top item from the stack Raises: IndexError: If stack is empty """ if self.is_empty(): raise IndexError("Stack is empty") return self.items[-1] def size(self): """ Return the number of items in the stack Returns: int: Number of items in the stack """ return len(self.items) ================================================ FILE: Stacks/stack_array_based.go ================================================ // Array based Stack Implementation // Limitation : The maximum size of the stack must first be defined and it cannot be changed. Trying to push a new element into // a full Stack causes an implementation-specific exception. package main import ( "errors" "fmt" ) type Stack struct { top int capacity uint array []interface{} } // Init: Returns an Initialized Stack func (stack *Stack) Init(capacity uint) *Stack { stack.top = -1 stack.capacity = capacity stack.array = make([]interface{}, capacity) return stack } // NewStack: Returns a new Stack func NewStack(capacity uint) *Stack { return new(Stack).Init(capacity) } // IsFull: Returns true if Stack is full or else false // Time complexity O(1) func (stack *Stack) IsFull() bool { // Stack is full when top is equal to the last index return stack.top == int(stack.capacity)-1 } // IsEmpty: Returns true if Stack is empty or else false // Time complexity O(1) func (stack *Stack) IsEmpty() bool { // Stack is empty when top is equal to -1 return stack.top == -1 } // Size: Returns the size of Stack // Time complexity O(1) func (stack *Stack) Size() uint { return uint(stack.top + 1) } // Push: Pushes new [data] into Stack // Time complexity O(1) // Space complexity for n push operations O(n) func (stack *Stack) Push(data interface{}) error { if stack.IsFull() { return errors.New("Stack is full") } stack.top++ stack.array[stack.top] = data fmt.Printf("\n%v Pushed to stack", data) return nil } // Pop: Pops top most data from Stack // Time complexity O(1) func (stack *Stack) Pop() (interface{}, error) { if stack.IsEmpty() { return nil, errors.New("Stack is empty") } temp := stack.array[stack.top] fmt.Printf("\n%v Popped from stack", temp) stack.top-- return temp, nil } // Peek: Returns top most element from Stack // Time complexity O(1) func (stack *Stack) Peek() (interface{}, error) { if stack.IsEmpty() { return nil, errors.New("Stack is empty") } temp := stack.array[stack.top] fmt.Printf("\n%v is the topmost element in stack", temp) return temp, nil } // Drain: Removes all elements that are currently in Stack // Time complexity O(1) func (stack *Stack) Drain() { stack.array = nil stack.top = -1 } func main() { stack := NewStack(50) stack.Push(1) stack.Push(2) stack.Push(3) fmt.Println(stack.Size()) // returns 3 stack.Pop() stack.Pop() stack.Peek() stack.Drain() stack.Pop() stack.Peek() fmt.Println() fmt.Println(stack.Size()) // returns 0 after draining } ================================================ FILE: Stacks/stack_dynamic_array.go ================================================ // Using Repeated Array doubling technique. // If the array is full, create a new array of // twice the size, and copy the items. With this approach, pushing k items // takes time proportional to k package main import ( "errors" "fmt" "strings" ) type Stack struct { top int capacity uint array []interface{} } // Init: Returns an Initialized Stack func (stack *Stack) Init(capacity uint) *Stack { stack.top = -1 stack.capacity = capacity stack.array = make([]interface{}, capacity) return stack } // NewStack: Returns a new Stack func NewStack(capacity uint) *Stack { return new(Stack).Init(capacity) } // Size: Returns the size of Stack // Time complexity O(1) func (stack *Stack) Size() uint { return uint(stack.top + 1) } // IsFull: Returns true if Stack is full or else false // Time complexity O(1) func (stack *Stack) IsFull() bool { // Stack is full when top is equal to the last index return stack.top == int(stack.capacity) - 1 } // IsEmpty: Returns true if Stack is empty or else false // Time complexity O(1) func (stack *Stack) IsEmpty() bool { // Stack is empty when top is equal to -1 return stack.top == -1 } // Resize: If the array is full, creates a new array of // twice the size, and copy the items func (stack *Stack) Resize() { fmt.Println("Resizing") if stack.IsFull() { stack.capacity *= 2 } else { stack.capacity /= 2 } target := make([]interface{}, stack.capacity) copy(target, stack.array[:stack.top+1]) stack.array = target } // Push: Pushes new [data] into Stack, resizes to double if stack is full // Time complexity O(1) // Space complexity for n push operations O(n) func (stack *Stack) Push(data interface{}) error { if stack.IsFull() { stack.Resize() } stack.top++ stack.array[stack.top] = data fmt.Printf("\n%v Pushed to stack", data) return nil } // Pop: Pops top most data from Stack, resizes to hald if sizeof stack is less than half the capacity // Time complexity O(1) func (stack *Stack) Pop() (interface{}, error) { if stack.IsEmpty() { return nil, errors.New("Stack is empty") } temp := stack.array[stack.top] fmt.Printf("\n%v Popped from stack", temp) stack.top-- if stack.Size() < stack.capacity / 2 { stack.Resize() } return temp, nil } // Peek: Returns top most element from Stack // Time complexity O(1) func (stack *Stack) Peek() (interface{}, error) { if stack.IsEmpty() { return nil, errors.New("Stack is empty") } temp := stack.array[stack.top] fmt.Printf("\n%v is the topmost element in stack", temp) return temp, nil } // Drain: Removes all elements that are currently in Stack // Time complexity O(1) func (stack *Stack) Drain() { stack.array = nil stack.top = -1 } func IsOperator(c uint8) bool { return strings.ContainsAny(string(c), "+ & - & * & /") } func IsOperand(c uint8) bool { return c >= '0' && c <= '9' } func GetOperatorWeight(op string) int { switch op { case "+", "-" : return 1 case "*", "/" : return 2 } return -1 } func ToPostfix(s string) string { stack := NewStack(1) postfix := "" length := len(s) for i := 0; i < length; i++ { char := string(s[i]) if char == " " { continue } if char == "(" { stack.Push(char) } else if char == ")" { for !stack.IsEmpty() { temp, _ := stack.Peek() str := temp.(string) if str == "(" { break } postfix += " " + str stack.Pop() } stack.Pop() } } } func HasHigherPrecedence(op1 string, op2 string) bool { op1Weight := GetOperatorWeight(op1) op2Weight := GetOperatorWeight(op2) return op1Weight >= op2Weight } func main() { stack := NewStack(1) stack.Push(1) stack.Push(2) stack.Push(3) fmt.Println(stack.Size()) stack.Pop() stack.Pop() stack.Pop() stack.Drain() stack.Peek() fmt.Println(ToPostfix("((1+(4+5+2)-3)+(6+8))")) } ================================================ FILE: Stacks/stack_linked_list.go ================================================ package main import "fmt" type Stack struct { top *ListNode size int } type ListNode struct { data interface{} next *ListNode } // Size: Returns the size of Stack func (s *Stack) Length() int { return s.size } // Push: Pushes new [data] into Stack // Time complexity O(1) // Space complexity for n push operations O(n) func (s *Stack) Push(data interface{}) { s.top = &ListNode{data, s.top} s.size++ fmt.Printf("\n%v Pushed to stack", data) } // IsEmpty: Returns true if Stack is empty or else false // Time complexity O(1) func (s *Stack) IsEmpty() bool { return s.size == 0 } // IsFull: Returns false since its LL based implementation // Time complexity O(1) func (s *Stack) IsFull() bool { return false } // Pop: Pops top most data from Stack // Time complexity O(1) func (s *Stack) Pop() (data interface{}) { if s.size > 0 { data, s.top = s.top.data, s.top.next s.size-- fmt.Printf("\n%v Popped from stack", data) return data } return nil } // Peek: Returns top most element from Stack // Time complexity O(1) func (s *Stack) Peek() (data interface{}) { if s.size > 0 { data = s.top.data fmt.Printf("\n%v is the topmost element in stack", data) return data } return nil } func main() { stack := new(Stack) stack.Push("Hello") stack.Push("World!") stack.Push("Yo") stack.Peek() fmt.Println(stack.Length()) stack.Pop() stack.Pop() stack.Pop() } ================================================ FILE: Stacks/stack_using_queue.cpp ================================================ // stack using queues #include #include class StackUsingQueues { public: StackUsingQueues() {} // Push an element onto the stack. void push(int x) { // Add the element to the primary queue. primaryQueue.push(x); } // Remove and return the top element of the stack. int pop() { if (isEmpty()) { throw std::runtime_error("Stack is empty"); } // Move elements from the primary queue to the temporary queue except the last one. while (primaryQueue.size() > 1) { tempQueue.push(primaryQueue.front()); primaryQueue.pop(); } // Get the last element from the primary queue (top of the stack). int topElement = primaryQueue.front(); primaryQueue.pop(); // Swap the primary and temporary queues. primaryQueue = tempQueue; while (!tempQueue.empty()) { tempQueue.pop(); } return topElement; } // Return the top element of the stack without removing it. int top() { if (isEmpty()) { throw std::runtime_error("Stack is empty"); } int topElement = pop(); // Add the top element back to the stack. push(topElement); return topElement; } // Check if the stack is empty. bool isEmpty() { return primaryQueue.empty(); } private: std::queue primaryQueue; std::queue tempQueue; }; int main() { StackUsingQueues stack; // Push elements onto the stack. stack.push(1); stack.push(2); stack.push(3); // Pop elements from the stack. std::cout << stack.pop() << std::endl; // Output: 3 std::cout << stack.pop() << std::endl; // Output: 2 // Push more elements. stack.push(4); stack.push(5); // Peek at the top element. std::cout << stack.top() << std::endl; // Output: 5 // Check if the stack is empty. std::cout << stack.isEmpty() << std::endl; // Output: 0 (false) return 0; } ================================================ FILE: Stacks/stack_using_queue.go ================================================ // stack using queues package main import ( "container/list" "fmt" ) // Stack represents a stack data structure using two queues. type Stack struct { queue1 *list.List // Primary queue queue2 *list.List // Temporary queue for operations } // Constructor creates a new stack. func NewStack() *Stack { return &Stack{ queue1: list.New(), queue2: list.New(), } } // Push adds an element to the top of the stack. func (s *Stack) Push(value int) { // Add the element to the primary queue s.queue1.PushBack(value) } // Pop removes and returns the top element of the stack. func (s *Stack) Pop() int { // Move elements from the primary queue to the temporary queue except the last one for s.queue1.Len() > 1 { element := s.queue1.Front() s.queue1.Remove(element) s.queue2.PushBack(element.Value) } // Get the last element from the primary queue (top of the stack) topElement := s.queue1.Front().Value // Swap the primary and temporary queues s.queue1, s.queue2 = s.queue2, s.queue1 return topElement.(int) } // Top returns the top element of the stack without removing it. func (s *Stack) Top() int { // Similar to Pop, but don't remove the last element topElement := s.Pop() // Add the top element back to the stack s.Push(topElement) return topElement } // IsEmpty checks if the stack is empty. func (s *Stack) IsEmpty() bool { return s.queue1.Len() == 0 } func main() { stack := NewStack() // Push elements onto the stack stack.Push(1) stack.Push(2) stack.Push(3) // Pop elements from the stack fmt.Println(stack.Pop()) // Output: 3 fmt.Println(stack.Pop()) // Output: 2 // Push more elements stack.Push(4) stack.Push(5) // Peek at the top element fmt.Println(stack.Top()) // Output: 5 // Check if the stack is empty fmt.Println(stack.IsEmpty()) // Output: false } ================================================ FILE: Stacks/stack_using_queue.java ================================================ // stack using queues import java.util.LinkedList; import java.util.Queue; public class StackUsingQueues { private Queue primaryQueue; private Queue tempQueue; public StackUsingQueues() { primaryQueue = new LinkedList<>(); tempQueue = new LinkedList<>(); } public void push(int value) { // Add the element to the primary queue primaryQueue.offer(value); } public int pop() { if (isEmpty()) { throw new IllegalStateException("Stack is empty."); } // Move elements from the primary queue to the temporary queue except the last one while (primaryQueue.size() > 1) { tempQueue.offer(primaryQueue.poll()); } // Get the last element from the primary queue (top of the stack) int topElement = primaryQueue.poll(); // Swap the primary and temporary queues Queue swap = primaryQueue; primaryQueue = tempQueue; tempQueue = swap; return topElement; } public int top() { if (isEmpty()) { throw new IllegalStateException("Stack is empty."); } int topElement = pop(); // Add the top element back to the stack push(topElement); return topElement; } public boolean isEmpty() { return primaryQueue.isEmpty(); } public static void main(String[] args) { StackUsingQueues stack = new StackUsingQueues(); // Push elements onto the stack stack.push(1); stack.push(2); stack.push(3); // Pop elements from the stack System.out.println(stack.pop()); // Output: 3 System.out.println(stack.pop()); // Output: 2 // Push more elements stack.push(4); stack.push(5); // Peek at the top element System.out.println(stack.top()); // Output: 5 // Check if the stack is empty System.out.println(stack.isEmpty()); // Output: false } } ================================================ FILE: Stacks/stack_using_queue.js ================================================ // Stack using queues class StackUsingQueues { constructor() { this.primaryQueue = []; this.tempQueue = []; } // Push an element onto the stack. push(x) { // Add the element to the primary queue. this.primaryQueue.push(x); } // Remove and return the top element of the stack. pop() { if (this.isEmpty()) { throw new Error("Stack is empty"); } // Move elements from the primary queue to the temporary queue except the last one. while (this.primaryQueue.length > 1) { this.tempQueue.push(this.primaryQueue.shift()); } // Get the last element from the primary queue (top of the stack). const topElement = this.primaryQueue.shift(); // Swap the primary and temporary queues. [this.primaryQueue, this.tempQueue] = [this.tempQueue, this.primaryQueue]; return topElement; } // Return the top element of the stack without removing it. top() { if (this.isEmpty()) { throw new Error("Stack is empty"); } const topElement = this.pop(); // Add the top element back to the stack. this.push(topElement); return topElement; } // Check if the stack is empty. isEmpty() { return this.primaryQueue.length === 0; } } const stack = new StackUsingQueues(); // Push elements onto the stack. stack.push(1); stack.push(2); stack.push(3); // Pop elements from the stack. console.log(stack.pop()); // Output: 3 console.log(stack.pop()); // Output: 2 // Push more elements. stack.push(4); stack.push(5); // Peek at the top element. console.log(stack.top()); // Output: 5 // Check if the stack is empty. console.log(stack.isEmpty()); // Output: false ================================================ FILE: Stacks/stack_using_queue.py ================================================ ''' Implement Stack using Queues Implement a last-in-first-out (LIFO) stack using only two queues. The implemented stack should support all the functions of a normal stack (push, top, pop, and empty). Implement the MyStack class: void push(int x) Pushes element x to the top of the stack. int pop() Removes the element on the top of the stack and returns it. int top() Returns the element on the top of the stack. boolean empty() Returns true if the stack is empty, false otherwise. Notes: You must use only standard operations of a queue, which means that only push to back, peek/pop from front, size and is empty operations are valid. Depending on your language, the queue may not be supported natively. You may simulate a queue using a list or deque (double-ended queue) as long as you use only a queue's standard operations. Example 1: Input ["MyStack", "push", "push", "top", "pop", "empty"] [[], [1], [2], [], [], []] Output [null, null, null, 2, 2, false] Explanation MyStack myStack = new MyStack(); myStack.push(1); myStack.push(2); myStack.top(); // return 2 myStack.pop(); // return 2 myStack.empty(); // return False Constraints: 1 <= x <= 9 At most 100 calls will be made to push, pop, top, and empty. All the calls to pop and top are valid. ''' class MyStack: def __init__(self): """ Initialize your data structure here. """ self.stack=[] def push(self, x: int) -> None: """ Push element x onto stack. """ self.stack.append(x) def pop(self) -> int: """ Removes the element on top of the stack and returns that element. """ return self.stack.pop() def top(self) -> int: """ Get the top element. """ return self.stack[-1] def empty(self) -> bool: """ Returns whether the stack is empty. """ if(self.stack): return False return True # Your MyStack object will be instantiated and called as such: # obj = MyStack() # obj.push(x) # param_2 = obj.pop() # param_3 = obj.top() # param_4 = obj.empty() ================================================ FILE: Stacks/stacks_API.cpp ================================================ /* In this implementation, we use a std::priority_queue named heap to simulate the behavior of a stack. The push operation inserts elements into the heap with a unique sequence number generated for each element. The pop operation removes the top element from the heap. The top operation returns the top element without removing it. The empty operation checks if the heap is empty. The time complexity of the stack operations implemented using a heap is as follows: push: O(log n), where n is the number of elements in the stack. pop: O(log n), where n is the number of elements in the stack. top: O(1) empty: O(1) The push and pop operations have a time complexity of O(log n) because the std::priority_queue internally maintains the heap property, ensuring that the highest priority (in this case, the highest sequence number) element is always at the top */ #include #include class Stack { private: std::priority_queue heap; // Using max heap to simulate stack behavior int sequenceNumber; // To keep track of the order of elements public: Stack() : sequenceNumber(0) {} void push(int value) { heap.push(std::make_pair(sequenceNumber++, value)); } int pop() { if (heap.empty()) { throw std::runtime_error("Stack is empty"); } int value = heap.top().second; heap.pop(); return value; } int top() const { if (heap.empty()) { throw std::runtime_error("Stack is empty"); } return heap.top().second; } bool empty() const { return heap.empty(); } }; int main() { Stack stack; stack.push(5); stack.push(10); stack.push(3); std::cout << stack.top() << std::endl; // Output: 3 stack.pop(); std::cout << stack.top() << std::endl; // Output: 10 return 0; } ================================================ FILE: Stacks/stacks_using_queues.java ================================================ /* Name : Abhinav kumar Github username : Abhinavcode13 Repository name : data-structures-and-algorithms Problem : Implement Stack using Queues in Java Issue Number : #259 Problem statement : Explanation of the below Java code : In the above code, the main method demonstrates the usage of the stack by providing the user with a menu to select the operation they want to perform - push, pop, or quit. The user can input the value to push, and the program prints the pushed or popped value accordingly. The program uses the Scanner class to take user input from the console. The push method adds the element to the first queue (q1) and updates the top variable. The pop method transfers all elements except the last one from q1 to q2, removes the last element from q1 and swaps the two queues. The peek method returns the top variable, and the isEmpty method checks if q1 is empty. When the user selects the push operation, the program prompts the user to enter the value to push, and it calls the push method to push the value onto the stack. When the user selects the pop operation, the program checks if the stack is empty and prints an error message if it is. Otherwise, it calls the pop method to pop the value from the stack and prints it. The program continues to prompt the user for input until the user selects the quit operation */ -------------------------------------------------------------------------------------------------------//Java code begins here-------------------------------------------------------------------------------------------------------------------------- import java.util.LinkedList; import java.util.Queue; import java.util.Scanner; public class StackUsingQueues { // Create two queues as instance variables private Queue q1 = new LinkedList<>(); private Queue q2 = new LinkedList<>(); // Create a variable to hold the top element of the stack private int top; // Method to push an element onto the stack public void push(int x) { q1.add(x); // Add the element to the first queue top = x; // Update the top variable to hold the new element } // Method to pop an element from the stack public int pop() { // Move all elements except the last one from the first queue to the second queue while (q1.size() > 1) { top = q1.remove(); q2.add(top); } // Remove the last element from the first queue, which is the element to be popped int popValue = q1.remove(); // Swap the queues so that the second queue becomes the first queue for the next operation Queue temp = q1; q1 = q2; q2 = temp; return popValue; // Return the popped element } // Method to peek at the top element of the stack public int peek() { return top; // Return the top element of the stack } // Method to check if the stack is empty public boolean isEmpty() { return q1.isEmpty(); // Return whether the first queue is empty } // Main method to run the program public static void main(String[] args) { // Create a new instance of the StackUsingQueues class StackUsingQueues stack = new StackUsingQueues(); // Create a new Scanner object to read input from the user Scanner scanner = new Scanner(System.in); // Create a loop to continuously prompt the user for input while (true) { System.out.println("Select operation -\n" + "1. Push\n" + "2. Pop\n" + "3. Quit"); // Read the user's choice int choice = scanner.nextInt(); // Check the user's choice and perform the corresponding operation if (choice == 1) { System.out.print("Enter value to push: "); int val = scanner.nextInt(); stack.push(val); System.out.println("Pushed value: " + val); } else if (choice == 2) { if (stack.isEmpty()) { System.out.println("Stack is empty."); } else { int val = stack.pop(); System.out.println("Popped value: " + val); } } else if (choice == 3) { break; } else { System.out.println("Invalid choice. Please try again."); } } } } ================================================ FILE: Stacks/stacks_with_queues.cpp ================================================ /* This implementation maintains two queues, queue1 and queue2, where queue1 always holds the elements in the stack. When pushing a new element, it is added to queue2, and then all the elements from queue1 are moved to queue2, making the newly added element the front/top of the stack. Finally, the names of the two queues are swapped to maintain the order. The pop() function removes and returns the front/top element of queue1, while the top() function returns the front/top element without removing it. Both functions check if queue1 is empty and throw an exception if the stack is empty. The empty() function checks if queue1 is empty and returns true if it is, indicating an empty stack. Time complexity push(x): O(n) pop(): O(1) top(): O(1) empty(): O(1) */ #include class MyStack { private: std::queue queue1; std::queue queue2; public: MyStack() { } void push(int x) { // Add the new element to queue2 queue2.push(x); // Move all elements from queue1 to queue2 while (!queue1.empty()) { queue2.push(queue1.front()); queue1.pop(); } // Swap the names of the two queues std::swap(queue1, queue2); } int pop() { if (queue1.empty()) { throw std::runtime_error("Stack is empty"); } int topElement = queue1.front(); queue1.pop(); return topElement; } int top() { if (queue1.empty()) { throw std::runtime_error("Stack is empty"); } return queue1.front(); } bool empty() { return queue1.empty(); } }; ================================================ FILE: Stacks/stacks_with_queues.py ================================================ ''' __init__() - This method initializes the stack by creating two queues. Since no operations are performed on the queues in this method, the time complexity is O(1). push(item) - This method adds an item to the top of the stack. Initially, it adds the item to an empty queue, which takes O(1) time. Then, it moves all the elements from the other queue to the empty queue, which takes O(n) time, where n is the number of elements in the stack. Finally, it swaps the names of the two queues, which is a constant time operation. Therefore, the overall time complexity of push(item) is O(n). pop() - This method removes and returns the item at the top of the stack. It checks if the stack is empty, which takes O(1) time. Then, it removes and returns the front element of the non-empty queue, which is a constant time operation. Therefore, the overall time complexity of pop() is O(1). top() - This method returns the item at the top of the stack without removing it. It checks if the stack is empty, which takes O(1) time. Then, it retrieves the front element of the non-empty queue, which is a constant time operation. Therefore, the overall time complexity of top() is O(1). is_empty() - This method checks if the stack is empty by checking if the queue is empty, which takes O(1) time. Therefore, the time complexity of is_empty() is O(1). size() - This method returns the number of items in the stack by returning the size of the queue, which is a constant time operation. Therefore, the time complexity of size() is O(1). the time complexity of the operations in the Stack class implemented using queues is as follows: push(item): O(n) pop(): O(1) top(): O(1) is_empty(): O(1) size(): O(1) ''' from queue import Queue class Stack: """ Stack implementation using queues. """ def __init__(self): """ Initialize an empty stack. """ self.queue1 = Queue() self.queue2 = Queue() def push(self, item): """ Add an item to the top of the stack. """ # Add the item to the empty queue self.queue1.put(item) # Move all the elements from the other queue to the empty queue while not self.queue2.empty(): self.queue1.put(self.queue2.get()) # Swap the names of the two queues self.queue1, self.queue2 = self.queue2, self.queue1 def pop(self): """ Remove and return the item at the top of the stack. """ if self.queue2.empty(): raise IndexError("Stack is empty") # Remove and return the front element of the non-empty queue return self.queue2.get() def top(self): """ Return the item at the top of the stack without removing it. """ if self.queue2.empty(): raise IndexError("Stack is empty") # Get the front element of the non-empty queue return self.queue2.queue[0] def is_empty(self): """ Check if the stack is empty. """ return self.queue2.empty() def size(self): """ Return the number of items in the stack. """ return self.queue2.qsize() ================================================ FILE: Stacks/valid_parentheses.cpp ================================================ /* Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid. An input string is valid if: Open brackets must be closed by the same type of brackets. Open brackets must be closed in the correct order. Every close bracket has a corresponding open bracket of the same type. Example 1: Input: s = "()" Output: true Example 2: Input: s = "()[]{}" Output: true Example 3: Input: s = "(]" Output: false Constraints: 1 <= s.length <= 104 s consists of parentheses only '()[]{}'. */ class Solution { public: bool Are_pair(char opening, char closing){ if (opening == '(' && closing == ')') return true; else if (opening == '[' && closing == ']') return true; else if (opening == '{' && closing == '}') return true; return false; } bool isValid(string s) { stack Sta; int len = s.size(); for(int i = 0; i < len; i++){ if(s[i] == '(' || s[i] == '[' || s[i] == '{'){ Sta.push(s[i]); } else if(s[i] == ')' || s[i] == ']' || s[i] == '}'){ if(Sta.empty() || !Are_pair(Sta.top(), s[i])) return false; else Sta.pop(); } } return Sta.empty() ? true : false; } }; ================================================ FILE: Strings/Dp_plaindrome.py ================================================ def find_min_insertion_steps(s): n = len(s) dp = [[0] * n for _ in range(n)] for length in range(2, n + 1): for i in range(n - length + 1): j = i + length - 1 if s[i] == s[j]: dp[i][j] = dp[i + 1][j - 1] else: dp[i][j] = min(dp[i + 1][j], dp[i][j - 1]) + 1 return dp[0][n - 1] # Test the function string = "abcd" print("Minimum Insertion Steps:", find_min_insertion_steps(string)) ================================================ FILE: Strings/KMP.go ================================================ package main import ( "fmt" ) // computeLPSArray computes the Longest Prefix Suffix (LPS) array for the given pattern. func computeLPSArray(pattern string) []int { length := len(pattern) lps := make([]int, length) lps[0] = 0 j := 0 for i := 1; i < length; { if pattern[i] == pattern[j] { j++ lps[i] = j i++ } else { if j != 0 { j = lps[j-1] } else { lps[i] = 0 i++ } } } return lps } // KMPSearch searches for occurrences of the pattern within the given text using the KMP algorithm. func KMPSearch(text, pattern string) []int { n := len(text) m := len(pattern) lps := computeLPSArray(pattern) i := 0 // Index for text j := 0 // Index for pattern positions := make([]int, 0) for i < n { if pattern[j] == text[i] { i++ j++ } if j == m { positions = append(positions, i-j) j = lps[j-1] } else if i < n && pattern[j] != text[i] { if j != 0 { j = lps[j-1] } else { i++ } } } return positions } func main() { text := "ABABDABACDABABCABAB" pattern := "ABABCABAB" positions := KMPSearch(text, pattern) if len(positions) == 0 { fmt.Println("Pattern not found in the text.") } else { fmt.Printf("Pattern found at positions: %v\n", positions) } } ================================================ FILE: Strings/Longest_palindromic_substring.py ================================================ ''' Given a string s, return the longest palindromic substring in s. Example 1: Input: s = "babad" Output: "bab" Explanation: "aba" is also a valid answer. Example 2: Input: s = "cbbd" Output: "bb" Constraints: 1 <= s.length <= 1000 s consist of only digits and English letters. ''' class Solution: #Instead of checking if a value is palindrome from the end, we can consider each index to be the center #Then, Check if the left and right indexed values to it are different. #The edge case would to find even lengthed palindrome like the second example #Reference: https://www.youtube.com/watch?v=XYQecbcd6_c def __init__(self): self.max_length = -1 self.max_palindrome = "" def CheckLeftRight(self, s, index, value): low, high = index, index if(value == "even"): high = index + 1 while(low >= 0 and high < len(s) and s[low] == s[high]): if(high - low + 1 > self.max_length): self.max_length = high - low + 1 self.max_palindrome = s[low:high + 1] low -= 1 high += 1 def longestPalindrome(self, s: str) -> str: for i in range(len(s)): #odd values Solution.CheckLeftRight(self, s, i, "odd") #Even values Solution.CheckLeftRight(self, s, i, "even") return self.max_palindrome ================================================ FILE: Strings/MaxConcatenatedstr.java ================================================ class Solution { public: // i: arr[i] we are currently looking at // mask: Bitmask that represents all the characters that have been added to the current string // If 0-th bit in mask is set, it means that we have added "a" in the current string int solve(vector &arr, int i, int mask) { int n = arr.size(); if (i >= n) return 0; // Skip concatenating arr[i] int curRes = solve(arr, i+1, mask) // Mask to keep track of the characters that are present in arr[i] int curMask = 0; // Check whether any character in arr[i] is present in current string, i.e. // Check whether (arr[i]-'a')-th bit is set in mask // If any existing character's bit is set, it means that we cannot concatenate arr[i] // to the given string and so return curRes only which contains the result of skipping arr[i] // Also, use curMask to maintain the characters in arr[i] that have been seen. // It is possible that arr[i] itself has duplicate characters in which case, we will not be able to concatenate arr[i] // So check whether (c-'a')-th bit is set in curMask and after that set the (c-'a')-th bit in curMask for (char &c: arr[i]) { if (mask & (1 << (c - 'a'))) return curRes; if (curMask & (1 << (c - 'a'))) return curRes; curMask |= (1 << (c - 'a')); } // All the bits that were set in curMask will be now set in mask, // in order to add all characters of arr[i] to the current string mask |= curMask; // We make a call to i+1 with the updated mask and arr[i]'s length being added curRes = max(curRes, (int) arr[i].length() + solve(arr, i+1, mask)); return curRes; } int maxLength(vector& arr) { return solve(arr, 0, 0); } }; ================================================ FILE: Strings/Min_palindrome.js ================================================ function minInsertionStepsToPalindrome(str) { const n = str.length; // Create a 2D array to store the minimum steps needed to make substrings palindrome const dp = Array.from({ length: n }, () => Array(n).fill(0)); // Base case: single characters are palindromes, so dp[i][i] = 0 for (let i = 0; i < n; i++) { dp[i][i] = 0; } // Fill the dp table in bottom-up manner for (let len = 2; len <= n; len++) { for (let i = 0; i < n - len + 1; i++) { const j = i + len - 1; if (str[i] === str[j]) { // If the characters at the ends are the same, no additional insertion is needed dp[i][j] = dp[i + 1][j - 1]; } else { // Otherwise, choose the minimum between inserting character at i or j dp[i][j] = 1 + Math.min(dp[i + 1][j], dp[i][j - 1]); } } } return dp[0][n - 1]; } // Example usage: const str = "abcd"; const minInsertions = minInsertionStepsToPalindrome(str); console.log("Minimum insertion steps:", minInsertions); // Output: 3 (abcd -> dabcbad) ================================================ FILE: Strings/Valid_palindrome.py ================================================ '''Name : Abhinav kumar Github username : Abhinavcode13 Repository name : data-structures-and-algorithms Problem : Find Number of Good Pairs in Go Issue Number : #648 Problem statement : Check whether a given string is a Valid Palindrome in Python Explanation of the below cpp code : The is_valid_palindrome() function is the same as the one in the previous example, which checks whether a given string is a valid palindrome or not. The input() function is used to take input from the user. It displays a prompt message "Enter a string:" and waits for the user to enter a value followed by pressing the Enter key. The entered value is stored in the variable s. The if statement checks if the input string s is a valid palindrome or not using the is_valid_palindrome() function. If it is, it prints the message "The string is a valid palindrome", otherwise it prints "The string is not a valid palindrome". The time complexity of the given code is O(n) ''' ------------------------------------------------------------------------------------------------------//Python code begins here---------------------------------------------------------------------------------------------------------------------------------- def is_valid_palindrome(s): s = ''.join(filter(str.isalnum, s)).lower() return s == s[::-1] s = input("Enter a string: ") #Taking input from user if is_valid_palindrome(s): print("The string is a valid palindrome") #True case else: print("The string is not a valid palindrome") #False case ================================================ FILE: Strings/case_specific_sorting_of_strings.cpp ================================================ case-specific sorting of strings (GFG) Given a string S consisting of only uppercase and lowercase characters. The task is to sort uppercase and lowercase letters separately such that if the ith place in the original string had an Uppercase character then it should not have a lowercase character after being sorted and vice versa. Example 1: Input: N = 12 S = defRTSersUXI Output: deeIRSfrsTUX Explanation: Sorted form of given string with the same case of character as that in original string is deeIRSfrsTUX Example 2: Input: N = 6 S = srbDKi Output: birDKs Explanation: Sorted form of given string with the same case of character result in output as birDKs. Your Task: You only need to complete the function caseSort, that takes a string str and the length of the string n and returns sorted string. Explanation: In this problem, we create three strings based on the given string.One for storing the capital letters named as caps string (string caps=""), second for small letters (string small="") and finally the third string (string ans="") which would store the final result. We will sort the strings in order to maintain alphabetical order. Then we traverse the given string using x (pointer) , i and j pointers for the caps and small strings. j pointer is used to traverse the string caps while i used to traverse the string small. x pointer is used to iterate through the given string as a whole and for each capital letter found we will insert the the letter from string caps into the string ans. Similarly, for each small letter, we will insert it from the string small into the ans string. Finally we return the ans string. CODE: class Solution { public: //Function to perform case-specific sorting of strings. string caseSort(string str, int n) { string small="", caps = "",ans=""; for(auto x:str){ if(isupper(x)){ caps+=x; } else{ small+=x; } } sort(caps.begin(),caps.end()); sort(small.begin(),small.end()); int i=0,j=0; for(auto x:str){ if(isupper(x)){ ans+=caps[j]; j++; } else{ ans+=small[i]; i++; } } return ans; // your code here } }; ================================================ FILE: Strings/check panagram.java ================================================ /* Check whether a string is a Panagram or not. Note that a string is a Panagram if it contains all the character of the alphabets ignoring the case of the alphabets For example, str = “Abcdefghijklmnopqrstuvwxyz” Output: Yes Explanation: The given string contains all the letters from a to z (ignoring case). str = "AbhayChetri" Output: No Explaination: The given string doesn't contain all the letters from a to z. Approach:- 1.Convert each letter of the string to the lower or upper case. 2.Create a frequency array to mark the frequency of each letter from a to z. 3.Then, traverse the frequency array and if there is any letter that is not present in the given string then print "No", otherwise print "Yes". Time Complexity: O(N) Auxiliary Space: O(26) */ class Abhay { static int size = 26; // Function to check if ch is a letter static boolean isLetter(char ch) { if (!Character.isLetter(ch)) return false; return true; } // Function to check if a string // contains all the letters from // a to z static boolean allLetter(String str, int len) { // Convert the given string // into lowercase str = str.toLowerCase(); // Create a frequency array to // mark the present letters boolean[] present = new boolean[size]; // Traverse for each character // of the string for (int i = 0; i < len; i++) { // If the current character // is a letter if (isLetter(str.charAt(i))) { // Mark current letter as present int letter = str.charAt(i) - 'a'; present[letter] = true; } } // Traverse for every letter // from a to z for (int i = 0; i < size; i++) { // If the current character // is not present in string // then return false, // otherwise return true if (!present[i]) return false; } return true; } // Driver Code public static void main(String args[]) { // Given string str String str = "Abcdefghijklmnopqrstuvwxyz"; int len = str.length(); // Function Call if (allLetter(str, len)) System.out.println("Yes"); else System.out.println("No"); } } ================================================ FILE: Strings/check_anagrams.java ================================================ /** * You are given two lowercase strings A and B each of length N. Return 1 if they are anagrams to each other and 0 if not. * * Note : Two strings A and B are called anagrams to each other if A can be formed after rearranging the letters of B. * * * Problem Constraints * 1 <= N <= 105 * A and B are lowercase strings * * * Input Format * Both arguments A and B are a string. * * * Output Format * Return 1 if they are anagrams and 0 if not * * * Example Input * Input 1: * A = "cat" * B = "bat" * Input 2: * A = "secure" * B = "rescue" * * * Example Output * Output 1: * 0 * Output 2: * 1 * * * Example Explanation * For Input 1: * The words cannot be rearranged to form the same word. So, they are not anagrams. * For Input 2: * They are an anagram. */ package Strings; public class CheckAnagrams { public static void main(String[] args) { String string1 = "secure"; String string2 = "rescue"; int res = solve(string1, string2); System.out.println(res); } public static int solve(String string1, String string2) { // O(N) time | O(1) space int[] freq1 = new int[26]; int[] freq2 = new int[26]; // build freq array and store count for each letter for (int i = 0; i < string1.length(); i++) { freq1[string1.charAt(i) - 'a']++; freq2[string2.charAt(i) - 'a']++; } // compare if count of each letter // if mismatch occurs then return 0 for (int i = 0; i < 26; i++) { if (freq1[i] != freq2[i]) return 0; } return 1; // char[] char1 = string1.toCharArray(); // Arrays.sort(char1); // // char[] char2 = string2.toCharArray(); // Arrays.sort(char2); // // String str1 = new String(char1); // String str2 = new String(char2); // // if (str1.equals(str2)) return 1; // return 0; } } ================================================ FILE: Strings/check_palindrome.cpp ================================================ #include #include using namespace std; // 2 pointer approach to find if a number is palindrome or not // Time complexity O(n), Space complexity O(1). int palindrome_checker(string str) // palindrome checker function { int left = 0; int right = str.length() - 1; // initializing left and right variables while(left < right) { if(str[left] != str[right]) { return 0; } left = left + 1; // updating left and right variables right = right - 1; } return 1; } int main() { string str; cin>>str; int isPal = palindrome_checker(str); // calling palindrome checker function if(isPal == 1) { cout<<"String is palindrome"; } else { cout<<"String is not palindrome"; } return 0; } ================================================ FILE: Strings/check_permutations.cpp ================================================ // Check if a string is a permutation of other // Sample Input: s1 = abba s2 = baba // Output: true // Time Complexity O(n) Space complexity O(1) #include using namespace std; bool check_permutations(string a, string b){ vector Freq(26, 0); // assuming only letters for(int i = 0; i < a.length(); i++){ Freq[a[i] - 'a']++; } for(int x : Freq) cout << x << ","; cout << endl; for(int i = 0; i < b.length(); i++){ if(Freq[b[i] - 'a'] > 0) // seen a letter Freq[b[i] - 'a']--; // reduce count else Freq[b[i] - 'a']++; // not seen before so increase count } for(int x : Freq) cout << x << ","; cout << endl; int res = accumulate(Freq.begin(), Freq.end(), 0); // checl of sum of elements in freq is 0 return res == 0 ? true : false; } int main(){ string a, b; cin >> a >> b; if(check_permutations(a, b)) cout << "TRUE"; else cout << "FALSE"; return 0; } ================================================ FILE: Strings/count_occurances.java ================================================ /** * Find the number of occurrences of bob in string A consisting of lowercase English alphabets. * * * * Problem Constraints * 1 <= |A| <= 1000 * * * Input Format * The first and only argument contains the string A, consisting of lowercase English alphabets. * * * Output Format * Return an integer containing the answer. * * * Example Input * Input 1: * * "abobc" * Input 2: * * "bobob" * * * Example Output * Output 1: * * 1 * Output 2: * * 2 * * * Example Explanation * Explanation 1: * * The only occurrence is at second position. * Explanation 2: * * Bob occures at first and third position. */ package Strings; public class CountOccurrences { public static void main(String[] args) { String string = "bobob"; int res = solve(string); System.out.println(res); } public static int solve(String string) { // O(N) time | O(1) space int res = 0; for (int i = 0; i + 2 < string.length(); i++) { if (string.charAt(i) == 'b' && string.charAt(i + 1) == 'o' && string.charAt(i + 2) == 'b') ++res; } // for (int i = 0; i < string.length(); i++) { // char c = string.charAt(i); // StringBuilder sb = new StringBuilder(); // sb.append(c); // for (int j = i + 1; j < string.length(); j++) { // sb.append(string.charAt(j)); // if (sb.toString().equals("bob")) { // res++; // } // } // } return res; } } ================================================ FILE: Strings/group_anagrams.cpp ================================================ /* Group Anagrams Sample Input: = ["yo", "act", "flop", "tac", "foo", "cat", "oy", "olfp"] Output: [["yo", "oy"], ["flop", "olfp"], ["act", "tac", "cat"], ["foo"]] Explanation: The code snippet is a function that groups anagrams together. An anagram is a word or phrase formed by rearranging the letters of another word or phrase. The function first defines two functions: GroupAnagrams and sortWord. The GroupAnagrams function takes a list of words as input and returns a list of lists, where each inner list contains all the anagrams of a word in the original list. The sortWord function takes a word as input and returns a string that contains the word's letters in sorted order. The GroupAnagrams function works by first creating a map where the keys are sorted strings of words and the values are lists of words that have the same sorted string. Then, the function iterates through the list of words, calling the sortWord function to get the sorted string for each word. The function then adds the word to the list of words associated with the sorted string in the map. Finally, the function iterates through the map, adding each list of words to a list of lists. The sortWord function works by converting the word to a byte array and then calling the sort.Slice function to sort the byte array. The function then returns a string that contains the sorted byte array. O(w * n * log(n)) time | O(wn) space - where w is the number of words and n is the length of the longest word */ #include #include #include #include // Function to sort a word and return the sorted string std::string sortWord(const std::string& word) { std::string sortedWord = word; std::sort(sortedWord.begin(), sortedWord.end()); return sortedWord; } // Function to group anagrams together std::vector> GroupAnagrams(const std::vector& words) { // Create a map where the keys are sorted strings of words and the values are lists of words // that have the same sorted string. std::unordered_map> anagrams; // Iterate through the words for (const std::string& word : words) { // Get the sorted string for the word std::string sortedWord = sortWord(word); // Add the word to the list of words associated with the sorted string in the map anagrams[sortedWord].push_back(word); } // Create a vector of vectors, where each inner vector contains all the anagrams of a word in the original list std::vector> result; for (const auto& pair : anagrams) { result.push_back(pair.second); } // Return the vector of vectors return result; } int main() { // Example usage std::vector words = { "eat", "tea", "tan", "ate", "nat", "bat" }; std::vector> groupedAnagrams = GroupAnagrams(words); // Print the grouped anagrams for (const std::vector& group : groupedAnagrams) { for (const std::string& word : group) { std::cout << word << " "; } std::cout << std::endl; } return 0; } ================================================ FILE: Strings/group_anagrams.go ================================================ /* Group Anagrams Sample Input: = ["yo", "act", "flop", "tac", "foo", "cat", "oy", "olfp"] Output: [["yo", "oy"], ["flop", "olfp"], ["act", "tac", "cat"], ["foo"]] Explanation: The code snippet is a function that groups anagrams together. An anagram is a word or phrase formed by rearranging the letters of another word or phrase. The function first defines two functions: GroupAnagrams and sortWord. The GroupAnagrams function takes a list of words as input and returns a list of lists, where each inner list contains all the anagrams of a word in the original list. The sortWord function takes a word as input and returns a string that contains the word's letters in sorted order. The GroupAnagrams function works by first creating a map where the keys are sorted strings of words and the values are lists of words that have the same sorted string. Then, the function iterates through the list of words, calling the sortWord function to get the sorted string for each word. The function then adds the word to the list of words associated with the sorted string in the map. Finally, the function iterates through the map, adding each list of words to a list of lists. The sortWord function works by converting the word to a byte array and then calling the sort.Slice function to sort the byte array. The function then returns a string that contains the sorted byte array. O(w * n * log(n)) time | O(wn) space - where w is the number of words and n is the length of the longest word */ package main import "sort" // `GroupAnagrams` groups anagrams together. func GroupAnagrams(words []string) [][]string { // Create a map where the keys are sorted strings of words and the values are lists of words // that have the same sorted string. anagrams := map[string][]string{} for _, word := range words { // Get the sorted string for the word. sortedWord := sortWord(word) // Add the word to the list of words associated with the sorted string in the map. anagrams[sortedWord] = append(anagrams[sortedWord], word) } // Create a list of lists, where each inner list contains all the anagrams of a word in the original list. result := [][]string{} for _, group := range anagrams { result = append(result, group) } // Return the list of lists. return result } // `sortWord` takes a word as input and returns a string that contains the word's letters in sorted order. func sortWord(word string) string { // Convert the word to a byte array. wordBytes := []byte(word) // Sort the byte array. sort.Slice(wordBytes, func(i, j int) bool { return wordBytes[i] < wordBytes[j] }) // Return a string that contains the sorted byte array. return string(wordBytes) } ================================================ FILE: Strings/group_anagrams.java ================================================ /* Group Anagrams Sample Input: = ["yo", "act", "flop", "tac", "foo", "cat", "oy", "olfp"] Output: [["yo", "oy"], ["flop", "olfp"], ["act", "tac", "cat"], ["foo"]] Explanation: The code snippet is a function that groups anagrams together. An anagram is a word or phrase formed by rearranging the letters of another word or phrase. The function first defines two functions: GroupAnagrams and sortWord. The GroupAnagrams function takes a list of words as input and returns a list of lists, where each inner list contains all the anagrams of a word in the original list. The sortWord function takes a word as input and returns a string that contains the word's letters in sorted order. The GroupAnagrams function works by first creating a map where the keys are sorted strings of words and the values are lists of words that have the same sorted string. Then, the function iterates through the list of words, calling the sortWord function to get the sorted string for each word. The function then adds the word to the list of words associated with the sorted string in the map. Finally, the function iterates through the map, adding each list of words to a list of lists. The sortWord function works by converting the word to a byte array and then calling the sort.Slice function to sort the byte array. The function then returns a string that contains the sorted byte array. O(w * n * log(n)) time | O(wn) space - where w is the number of words and n is the length of the longest word */ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; public class GroupAnagrams { public List> groupAnagrams(String[] words) { // Create a map where the keys are sorted strings of words and the values are lists of words // that have the same sorted string. Map> anagrams = new HashMap<>(); // Iterate through the words for (String word : words) { // Get the sorted string for the word String sortedWord = sortWord(word); // Add the word to the list of words associated with the sorted string in the map if (!anagrams.containsKey(sortedWord)) { anagrams.put(sortedWord, new ArrayList<>()); } anagrams.get(sortedWord).add(word); } // Create a list of lists, where each inner list contains all the anagrams of a word in the original array List> result = new ArrayList<>(anagrams.values()); // Return the list of lists return result; } // Helper method to sort the characters in a word and return the sorted string private String sortWord(String word) { // Convert the word to a character array char[] wordChars = word.toCharArray(); // Sort the character array Arrays.sort(wordChars); // Return the sorted string return new String(wordChars); } public static void main(String[] args) { // Example usage String[] words = {"eat", "tea", "tan", "ate", "nat", "bat"}; GroupAnagrams groupAnagrams = new GroupAnagrams(); List> groupedAnagrams = groupAnagrams.groupAnagrams(words); // Print the grouped anagrams for (List group : groupedAnagrams) { System.out.println(group); } } } ================================================ FILE: Strings/group_anagrams.js ================================================ /* Group Anagrams Sample Input: = ["yo", "act", "flop", "tac", "foo", "cat", "oy", "olfp"] Output: [["yo", "oy"], ["flop", "olfp"], ["act", "tac", "cat"], ["foo"]] Explanation: The code snippet is a function that groups anagrams together. An anagram is a word or phrase formed by rearranging the letters of another word or phrase. The function first defines two functions: GroupAnagrams and sortWord. The GroupAnagrams function takes a list of words as input and returns a list of lists, where each inner list contains all the anagrams of a word in the original list. The sortWord function takes a word as input and returns a string that contains the word's letters in sorted order. The GroupAnagrams function works by first creating a map where the keys are sorted strings of words and the values are lists of words that have the same sorted string. Then, the function iterates through the list of words, calling the sortWord function to get the sorted string for each word. The function then adds the word to the list of words associated with the sorted string in the map. Finally, the function iterates through the map, adding each list of words to a list of lists. The sortWord function works by converting the word to a byte array and then calling the sort.Slice function to sort the byte array. The function then returns a string that contains the sorted byte array. O(w * n * log(n)) time | O(wn) space - where w is the number of words and n is the length of the longest word */ function sortWord(word) { // Sort the characters in the word and return the sorted string return word.split("").sort().join(""); } function groupAnagrams(words) { // Create an object where the keys are sorted strings of words and the values are arrays of words // that have the same sorted string. const anagrams = {}; // Iterate through the words for (const word of words) { // Get the sorted string for the word const sortedWord = sortWord(word); // Add the word to the array associated with the sorted string in the object if (!anagrams[sortedWord]) { anagrams[sortedWord] = []; } anagrams[sortedWord].push(word); } // Create an array of arrays, where each inner array contains all the anagrams of a word in the original array const result = Object.values(anagrams); // Return the array of arrays return result; } // Example usage const words = ["eat", "tea", "tan", "ate", "nat", "bat"]; const groupedAnagrams = groupAnagrams(words); // Print the grouped anagrams for (const group of groupedAnagrams) { console.log(group); } ================================================ FILE: Strings/group_anagrams.py ================================================ ''' Group Anagrams Sample Input: = ["yo", "act", "flop", "tac", "foo", "cat", "oy", "olfp"] Output: [["yo", "oy"], ["flop", "olfp"], ["act", "tac", "cat"], ["foo"]] Explanation: The code snippet is a function that groups anagrams together. An anagram is a word or phrase formed by rearranging the letters of another word or phrase. The function first defines two functions: GroupAnagrams and sortWord. The GroupAnagrams function takes a list of words as input and returns a list of lists, where each inner list contains all the anagrams of a word in the original list. The sortWord function takes a word as input and returns a string that contains the word's letters in sorted order. The GroupAnagrams function works by first creating a map where the keys are sorted strings of words and the values are lists of words that have the same sorted string. Then, the function iterates through the list of words, calling the sortWord function to get the sorted string for each word. The function then adds the word to the list of words associated with the sorted string in the map. Finally, the function iterates through the map, adding each list of words to a list of lists. The sortWord function works by converting the word to a byte array and then calling the sort.Slice function to sort the byte array. The function then returns a string that contains the sorted byte array. O(w * n * log(n)) time | O(wn) space - where w is the number of words and n is the length of the longest word ''' from typing import List from collections import defaultdict def sortWord(word: str) -> str: # Sort the characters in the word and return the sorted string return ''.join(sorted(word)) def GroupAnagrams(words: List[str]) -> List[List[str]]: # Create a defaultdict where the keys are sorted strings of words and the values are lists of words # that have the same sorted string. anagrams = defaultdict(list) # Iterate through the words for word in words: # Get the sorted string for the word sortedWord = sortWord(word) # Add the word to the list of words associated with the sorted string in the defaultdict anagrams[sortedWord].append(word) # Create a list of lists, where each inner list contains all the anagrams of a word in the original list result = list(anagrams.values()) # Return the list of lists return result # Example usage words = ["eat", "tea", "tan", "ate", "nat", "bat"] groupedAnagrams = GroupAnagrams(words) # Print the grouped anagrams for group in groupedAnagrams: print(group) ================================================ FILE: Strings/is_pallindrome.cpp ================================================ /* Write a function that takes in a non-empty string and that returns a boolean representing whether the string is a palindrome. Sample Input: abba Output: True Sample Input: aberba Output: False Explanation: we define a function isPalindrome that takes in a string and returns true if the string is a palindrome, and false otherwise. The function uses two indices, left and right, initialized to the beginning and end of the string respectively. It then iterates through the string by moving the left index to the right and the right index to the left, checking whether the characters at these indices match. If the characters do not match, the function returns false, indicating that the string is not a palindrome. If the entire string is iterated through and all characters match, the function returns true, indicating that the string is a palindrome. In the main function, we call the isPalindrome function on the string "racecar" and print the result. The output will be "racecar is a palindrome". */ #include #include using namespace std; /** * @brief Function to check whether a string is a palindrome or not * * @param str String to check * @return true if the string is a palindrome, false otherwise */ bool isPalindrome(string str) { int left = 0; // initialize left index to 0 int right = str.length() - 1; // initialize right index to last character while (left < right) { // while left index is less than right index if (str[left] != str[right]) { // if characters at left and right indices do not match return false; // the string is not a palindrome } left++; // move left index to the right right--; // move right index to the left } return true; // the string is a palindrome } int main() { string str = "racecar"; bool isPal = isPalindrome(str); if (isPal) { cout << str << " is a palindrome" << endl; } else { cout << str << " is not a palindrome" << endl; } return 0; } ================================================ FILE: Strings/is_pallindrome.go ================================================ /* Write a function that takes in a non-empty string and that returns a boolean representing whether the string is a palindrome. Sample Input: abba Output: True Sample Input: aberba Output: False */ package main import "fmt" func IsPalindrome(str string) bool { start := 0 end := len(str) - 1 for start <= end { // if string doesn't match return false straight away if str[start] != str[end] { return false } // string matches so bring start and end inwards start++ end-- } return true } func main() { msg := IsPalindrome("HelleH") fmt.Println(msg) msg = IsPalindrome("Hello") fmt.Println(msg) } ================================================ FILE: Strings/is_pallindrome.java ================================================ /* Write a function that takes in a non-empty string and that returns a boolean representing whether the string is a palindrome. Sample Input: abba Output: True Sample Input: aberba Output: False Explanation: This function takes in a non-empty string and returns a boolean representing whether the string is a palindrome. It first removes all non-alphanumeric characters from the string and converts it to lowercase using the replaceAll() and toLowerCase() string methods, respectively. It then initializes two pointers, one at the beginning and one at the end of the cleaned string. It iterates through the string from both ends, comparing characters at each step. If the characters do not match, it returns false. If the loop completes without returning false, the string is a palindrome and the function returns true. */ /** * This function takes in a non-empty string and returns a boolean representing * whether the string is a palindrome. * * @param str the string to check for palindrome * @return true if str is a palindrome, false otherwise */ public static boolean isPalindrome(String str) { // Remove all non-alphanumeric characters from the string and convert to lowercase String cleanStr = str.replaceAll("[^a-zA-Z0-9]", "").toLowerCase(); // Initialize two pointers, one at the beginning and one at the end of the string int left = 0; int right = cleanStr.length() - 1; // Iterate through the string from both ends, comparing characters while (left < right) { if (cleanStr.charAt(left) != cleanStr.charAt(right)) { return false; } left++; right--; } // If the loop completes without returning false, the string is a palindrome return true; } ================================================ FILE: Strings/is_pallindrome.js ================================================ /* Write a function that takes in a non-empty string and that returns a boolean representing whether the string is a palindrome. Sample Input: abba Output: True Sample Input: aberba Output: False Explanation : The function takes in a non-empty string as input and returns a boolean value indicating whether the string is a palindrome or not. It first converts the input string to lowercase and removes any non-alphanumeric characters using a regular expression. It then uses two pointers (one starting from the beginning of the string and the other starting from the end) to compare characters from opposite ends of the string. If any mismatch is found, the function returns false immediately. If the entire string is traversed without finding any mismatches, the function returns true, indicating that the string is a palindrome. Note that this implementation treats uppercase and lowercase letters as equivalent (i.e., "A" is the same as "a") and ignores any non-alphanumeric characters (such as spaces or punctuation marks) when checking for palindromicity. */ /** * Checks if a given string is a palindrome. * @param {string} str - The input string to check. * @returns {boolean} True if the string is a palindrome, false otherwise. */ function isPalindrome(str) { // Convert the input string to lowercase and remove any non-alphanumeric characters str = str.toLowerCase().replace(/[^a-z0-9]/g, ""); // Use two pointers to compare characters from the start and end of the string let left = 0; let right = str.length - 1; while (left < right) { if (str[left] !== str[right]) { return false; } left++; right--; } return true; } ================================================ FILE: Strings/is_pallindrome.py ================================================ ''' Write a function that takes in a non-empty string and that returns a boolean representing whether the string is a palindrome. Sample Input: abba Output: True Sample Input: aberba Output: False Explanation: In this implementation, we use Python's filter() function to remove all non-alphanumeric characters from the string and lower() method to convert the string to lowercase. Then we check if the reversed string is equal to the original string using the slice notation [::-1]. If the two strings are equal, we return True, indicating that the input string is a palindrome; otherwise, we return False. ''' def is_palindrome(s: str) -> bool: """ This function takes in a non-empty string and returns a boolean indicating whether the string is a palindrome or not. Parameters: s (str): The input string Returns: bool: True if s is a palindrome, False otherwise """ # Remove all non-alphanumeric characters and convert to lowercase s = ''.join(filter(str.isalnum, s)).lower() # Check if the reversed string is equal to the original string return s == s[::-1] ================================================ FILE: Strings/is_unique.cpp ================================================ /* Implement an algorithm to determine if a string has all unique characters. what if you cannot use additional data structures? Explanation: - The `isUniqueUsingBitVector` function takes a constant reference to a string `s` as input and returns a boolean value indicating whether the string has all unique characters. - The variable `checker` is initialized as an integer, representing the bit vector to keep track of character occurrences. - The function iterates over each character `c` in the string using a range-based for loop. - For each character, the variable `val` is computed by subtracting the ASCII value of `'a'` from the ASCII value of the character. This gives the corresponding index (0-25) for lowercase alphabetic characters. - The program checks if the bit at position `val` in `checker` is already set. If it is, it means the character has occurred before, and the function returns `false`. - If the character is unique, the bit at position `val` in `checker` is set by performing a bitwise OR operation with `(1 << val)`. This marks the occurrence of the character in the bit vector. - After iterating through all the characters, if no duplicate characters are found, the function returns `true`. - In the `main` function, a few test cases are provided, and the result of calling `isUniqueUsingBitVector` with each test case is printed. */ #include #include bool isUniqueUsingBitVector(const std::string& s) { int checker = 0; // Bit vector to keep track of character occurrences for (char c : s) { int val = c - 'a'; // Convert character to corresponding index (0-25) if ((checker & (1 << val)) > 0) { // If the bit corresponding to the character is already set, it means the character has occurred before return false; } checker |= (1 << val); // Set the bit corresponding to the character to mark its occurrence } return true; } int main() { std::string s = "ABCDD"; std::string t = "ABCD"; std::string u = "AAAAAABCD"; bool msg = isUniqueUsingBitVector(s); std::cout << std::boolalpha << msg << std::endl; msg = isUniqueUsingBitVector(t); std::cout << std::boolalpha << msg << std::endl; msg = isUniqueUsingBitVector(u); std::cout << std::boolalpha << msg << std::endl; msg = isUniqueUsingBitVector("aa"); std::cout << std::boolalpha << msg << std::endl; return 0; } ================================================ FILE: Strings/is_unique.go ================================================ // Implement an algorithm to determine if a string has all unique characters. // what if you cannot use additional data structures? // Program Author : Abhisek Kumar Gupta // Approach 1 : compare every character of string with other character TC O(n^2) // Approach 2 : Sort the string and compare neighbouring character for dups TC O(n log(n)) package main import "fmt" func is_unique_normal(s string) bool { if len(s) > 128 { return false } var visited [128]bool for i := 0; i < len(s); i++ { val := int(s[i]) // convert to ascii value if(visited[val]) { // if already seen then duplicate exists hence return false return false } visited[val] = true // mark seen as true so far } return true } func IsUniqueUsingBitVector(s string) bool { checker := 0 for _, s := range []rune(s){ val := s - 'a' if (checker & (1 << uint32(val))) > 0 { return false } checker |= (1 << uint32(val)) } return true } func main() { s := "ABCDD" t := "ABCD" u := "AAAAAABCD" msg := is_unique_normal(s) fmt.Println(msg) msg = is_unique_normal(t) fmt.Println(msg) msg = is_unique_normal(u) fmt.Println(msg) msg = IsUniqueUsingBitVector("aa") fmt.Println(msg) } ================================================ FILE: Strings/is_unique.java ================================================ /* Implement an algorithm to determine if a string has all unique characters. what if you cannot use additional data structures? Explanation: - The `isUniqueUsingBitVector` function takes a string `s` as input and returns a boolean value indicating whether the string has all unique characters. - The variable `checker` is initialized as an integer, representing the bit vector to keep track of character occurrences. - The function iterates over each character `c` in the string using a foreach loop. - For each character, the variable `val` is computed by subtracting the ASCII value of `'a'` from the ASCII value of the character. This gives the corresponding index (0-25) for lowercase alphabetic characters. - The program checks if the bit at position `val` in `checker` is already set. If it is, it means the character has occurred before, and the function returns `false`. - If the character is unique, the bit at position `val` in `checker` is set by performing a bitwise OR operation with `(1 << val)`. This marks the occurrence of the character in the bit vector. - After iterating through all the characters, if no duplicate characters are found, the function returns `true`. - In the `main` function, a few test cases are provided, and the result of calling `isUniqueUsingBitVector` with each test case is printed. */ public class Main { public static boolean isUniqueUsingBitVector(String s) { int checker = 0; // Bit vector to keep track of character occurrences for (char c : s.toCharArray()) { int val = c - 'a'; // Convert character to corresponding index (0-25) if ((checker & (1 << val)) > 0) { // If the bit corresponding to the character is already set, it means the character has occurred before return false; } checker |= (1 << val); // Set the bit corresponding to the character to mark its occurrence } return true; } public static void main(String[] args) { String s = "ABCDD"; String t = "ABCD"; String u = "AAAAAABCD"; boolean msg = isUniqueUsingBitVector(s); System.out.println(msg); msg = isUniqueUsingBitVector(t); System.out.println(msg); msg = isUniqueUsingBitVector(u); System.out.println(msg); msg = isUniqueUsingBitVector("aa"); System.out.println(msg); } } ================================================ FILE: Strings/is_unique.js ================================================ /* Implement an algorithm to determine if a string has all unique characters. what if you cannot use additional data structures? Explanation: 1. The `isUniqueUsingBitVector` function takes a string `s` as input and returns a boolean value indicating whether the string has all unique characters. 2. The variable `checker` is initialized as 0, representing the bit vector to keep track of character occurrences. 3. The function iterates over each character in the string using a for loop. 4. For each character, the variable `val` is computed by subtracting the ASCII value of `'a'` from the ASCII value of the character. This calculates the corresponding index (0-25) for lowercase alphabetic characters. 5. The program checks if the bit at position `val` in the `checker` is already set. If it is, it means the character has occurred before, and the function returns `false`. 6. If the character is unique, the bit at position `val` in the `checker` is set by performing a bitwise OR operation with `(1 << val)`. This marks the occurrence of the character in the bit vector. 7. After iterating through all the characters, if no duplicate characters are found, the function returns `true`. 8. In the test cases section, a few sample strings (`s`, `t`, `u`, and `"aa"`) are provided, and the result of calling `isUniqueUsingBitVector` with each test case is printed using `console.log()`. */ function isUniqueUsingBitVector(s) { let checker = 0; // Bit vector to keep track of character occurrences for (let i = 0; i < s.length; i++) { const val = s.charCodeAt(i) - "a".charCodeAt(0); // Convert character to corresponding index (0-25) if ((checker & (1 << val)) > 0) { // If the bit corresponding to the character is already set, it means the character has occurred before return false; } checker |= 1 << val; // Set the bit corresponding to the character to mark its occurrence } return true; } // Test cases const s = "ABCDD"; const t = "ABCD"; const u = "AAAAAABCD"; console.log(isUniqueUsingBitVector(s)); // false, 'D' appears more than once console.log(isUniqueUsingBitVector(t)); // true, all characters are unique console.log(isUniqueUsingBitVector(u)); // false, 'A' appears more than once console.log(isUniqueUsingBitVector("aa")); // false, 'a' appears more than once ================================================ FILE: Strings/is_unique.py ================================================ ''' Implement an algorithm to determine if a string has all unique characters. what if you cannot use additional data structures? Explanation: - The `is_unique_using_bit_vector` function takes a string `s` as input and returns a boolean value indicating whether the string has all unique characters. - The variable `checker` is initialized as an integer, representing the bit vector to keep track of character occurrences. - The function iterates over each character `c` in the string using a for loop. - For each character, the variable `val` is computed by subtracting the ASCII value of `'a'` from the ASCII value of the character. This gives the corresponding index (0-25) for lowercase alphabetic characters. - The program checks if the bit at position `val` in `checker` is already set. If it is, it means the character has occurred before, and the function returns `False`. - If the character is unique, the bit at position `val` in ''' def is_unique_using_bit_vector(s): checker = 0 # Bit vector to keep track of character occurrences for c in s: val = ord(c) - ord('a') # Convert character to corresponding index (0-25) if (checker & (1 << val)) > 0: # If the bit corresponding to the character is already set, it means the character has occurred before return False checker |= (1 << val) # Set the bit corresponding to the character to mark its occurrence return True s = "ABCDD" t = "ABCD" u = "AAAAAABCD" msg = is_unique_using_bit_vector(s) print(msg) msg = is_unique_using_bit_vector(t) print(msg) msg = is_unique_using_bit_vector(u) print(msg) msg = is_unique_using_bit_vector("aa") print(msg) ================================================ FILE: Strings/length_of_longest_substring.java ================================================ /* Implement lengthOfLongestSubstring(s), which calculates the length of the longest possible substring that does not contain repeating characters. Example 1: Input: "abcabcbaba" Output: 3 Example 2: Input: "dddddddd" Output: 1 Example 3: Input: "pwwkewo" Output: 4 Constraints: 0 <= s.length <= 5 * 10^4 Time complexity: O(n^2) Space complexity: O(1) */ class Solution { public int lengthOfLongestSubstring(String s) { int length = s.length(); if (length==0) { return 0; } int max = 1; // Aiming to find substring of s that starts at index i and ends at j-1. int i = 0; int j = i+1; while (j max) { max = j-i; } i++; } return max; } } ================================================ FILE: Strings/longest palindromic substring.java ================================================ /* Issue#415 //Input: Given a string s, return the longest palindromic substring in s. //Palindrome:A palindrome is a word, phrase, or sequence that reads the same forward and backward, like "level" or "A man, a plan, a canal, Panama." //Example 1: Input: s = "babad" Output: "bab" Explanation: "aba" is also a valid answer. //Example 2: Input: s = "cbbd" Output: "bb" //Time complexity: >>Time Complexity: O(n2) as there is two recursion calls which are applied as two pointers so here Complexity would be O(n2). >>Space Complexity: O(n) which is nothing but the storage consumed in this process. //Explanation: >>The function first converts the input string into a character array. >>If the length of the string is less than 2, it means the string itself is a palindrome, so it returns the string as is. >>The function then iterates through each character of the string. >>For each character, it expands outwards from that character and checks if it forms a palindrome. >>It does this by calling the expandPalindrome function twice: once for odd-length palindromes with the current character as the center, and once for even-length palindromes with the current character and the next character as centers. >>The expandPalindrome function checks if the characters at positions j and k are equal, and if so, it expands the palindrome by decrementing j and incrementing k. >>It continues this process until the characters at positions j and k are no longer equal or reach the boundaries of the string. >>If a longer palindrome is found, the maxLen and lo variables are updated accordingly. maxLen stores the length of the longest palindrome found so far, and lo stores the starting position of the longest palindrome. >>Finally, the function returns the substring of the original string from the starting position lo to lo + maxLen, which represents the longest palindrome found. */ class Solution { int maxLen = 0; // Length of the longest palindrome int lo = 0; // Starting position of the longest palindrome public String longestPalindrome(String s) { char[] input = s.toCharArray(); // Convert the input word to individual characters if (s.length() < 2) { return s; // If word has less than 2 letters, it is already a palindrome } for (int i = 0; i < input.length; i++) { expandPalindrome(input, i, i); // Check odd-length palindromes with current letter as center expandPalindrome(input, i, i + 1); // Check even-length palindromes with current and next letters as center } return s.substring(lo, lo + maxLen); // Return the longest palindrome found } public void expandPalindrome(char[] s, int j, int k) { while (j >= 0 && k < s.length && s[j] == s[k]) { j--; // Move left to expand potential palindrome k++; // Move right to expand potential palindrome } if (maxLen < k - j - 1) { maxLen = k - j - 1; // Update length of longest palindrome if longer one is found lo = j + 1; // Update starting position of longest palindrome } } public static void main(String[] args) { Solution solution = new Solution(); String input = "babad"; String longestPalindrome = solution.longestPalindrome(input); System.out.println("Longest Palindrome: " + longestPalindrome); } } ================================================ FILE: Strings/longest_common_prefix.cpp ================================================ /* Write a function to find the longest common prefix string amongst an array of strings. If there is no common prefix, return an empty string "". Example 1: Input: strs = ["flower","flow","flight"] Output: "fl" Example 2: Input: strs = ["dog","racecar","car"] Output: "" Explanation: There is no common prefix among the input strings. Constraints: 1 <= strs.length <= 200 0 <= strs[i].length <= 200 strs[i] consists of only lowercase English letters. Time complexity : O(strs) */ #include class Solution { public: string longestCommonPrefix(vector& strs) { if(strs.size() == 0) return ""; string ans = ""; // fix one string and check the common prefix of this string with other strings // s is the smallest string, so longest cant be greater than smallest string in array string s = *min_element(strs.begin(), strs.end()); for(int i = 0; i < s.size(); i++){ for(int j = 0; j < strs.size(); j++){ if(s[i] != strs[j][i]) return ans; } ans.push_back(s[i]); } return ans; } }; ================================================ FILE: Strings/longest_string.cpp ================================================ /*The code implements the dynamic programming approach to find the longest palindrome substring in a given string. It uses a two-dimensional table dp to store the results of subproblems, where dp[i][j] represents whether the substring from index i to index j is a palindrome. The algorithm first checks for all substrings of length 1 and marks them as palindromes. Then it checks for substrings of length 2 and updates the start and maxLen variables if a palindrome of length 2 is found. After that, it checks for substrings of length greater than 2 by iterating over all possible lengths and positions. If a palindrome is found at a particular position, it updates the start and maxLen variables accordingly. */ #include #include class Solution { public: /** * Finds the longest palindrome substring within a given string. * * @param s The input string. * @return The longest palindrome substring. */ std::string longestPalindrome(std::string s) { int n = s.length(); if (n < 2) { return s; } int start = 0; // start index of the longest palindrome int maxLen = 1; // length of the longest palindrome // Initialize a table to store the results of subproblems std::vector> dp(n, std::vector(n, false)); // All substrings of length 1 are palindromes for (int i = 0; i < n; i++) { dp[i][i] = true; } // Check for substrings of length 2 for (int i = 0; i < n - 1; i++) { if (s[i] == s[i + 1]) { dp[i][i + 1] = true; start = i; maxLen = 2; } } // Check for substrings of length greater than 2 for (int len = 3; len <= n; len++) { for (int i = 0; i < n - len + 1; i++) { int j = i + len - 1; if (s[i] == s[j] && dp[i + 1][j - 1]) { dp[i][j] = true; start = i; maxLen = len; } } } // Return the longest palindrome substring return s.substr(start, maxLen); } }; ================================================ FILE: Strings/one_edit.cpp ================================================ /* You're given two strings stringone and stringtwo. Write a function that determines if these two strings can be made equal using only one edit. There are 3 possible edits: Replace: One character in one string is swapped for a different character. Add:: One character is added at any index in one string. Remove: One character is removed at any index in one string. Sample Input: StringOne: alaska StringTwo: aloska Output: True Explanation: The code snippet is implementing the "One Edit Away" algorithm, which determines whether two given strings are one edit away from each other. An edit is defined as either inserting a character, removing a character, or replacing a character. The `OneEdit` function takes two strings as input and returns a boolean indicating whether they are one edit away. Here's the breakdown of the algorithm: 1. Calculate the lengths of the two strings. 2. Check if the difference in lengths is greater than 1. If so, return `false` because it's not possible to make one edit to make the strings equal. 3. Traverse both strings until the shorter one is fully traversed or an unequal character is found. 4. If an unequal character is found, check the remaining portion of the strings to determine if they are still one edit away. 5. Check the remaining characters in the longer string compared to the remaining characters in the shorter string. 6. Return `true` if the remaining portions match, indicating they are one edit away. 7. If the loop completes without finding any unequal characters, the strings are either identical or differ only in length by 1, which means they are one edit away. 8. The `abs` and `min` functions are utility functions used to calculate the absolute value and minimum of two integers, respectively. The algorithm efficiently checks for the possibility of one edit by comparing the characters at corresponding indices and handling cases where the lengths of the strings are different. O(n) time | O(1) space - where n is the length of the shorter string */ #include #include using namespace std; bool OneEdit(string stringOne, string stringTwo) { int lengthOne = stringOne.length(); int lengthTwo = stringTwo.length(); // Check the difference in lengths between the two strings. // If the difference is greater than 1, it is not possible to make one edit to make them equal. if (abs(lengthOne - lengthTwo) > 1) { return false; } // Traverse the strings until the shorter one is fully traversed or an unequal character is found. for (int i = 0; i < min(lengthOne, lengthTwo); i++) { // If an unequal character is found, check the remaining portion of the strings to determine if they are still one edit away. if (stringOne[i] != stringTwo[i]) { // Check the remaining characters in the longer string compared to the remaining characters in the shorter string. // Return true if they match, indicating they are one edit away. if (lengthOne > lengthTwo) { return stringOne.substr(i + 1) == stringTwo.substr(i); } else if (lengthTwo > lengthOne) { return stringTwo.substr(i + 1) == stringOne.substr(i); } else { return stringOne.substr(i + 1) == stringTwo.substr(i + 1); } } } // If the loop completes without finding any unequal characters, the strings are either identical or differ only in length by 1. return true; } int main() { string stringOne = "pale"; string stringTwo = "ple"; // Check if the strings are one edit away bool isOneEdit = OneEdit(stringOne, stringTwo); // Print the result cout << "The strings are" << (isOneEdit ? "" : " not") << " one edit away." << endl; return 0; } ================================================ FILE: Strings/one_edit.go ================================================ /* You're given two strings stringone and stringtwo. Write a function that determines if these two strings can be made equal using only one edit. There are 3 possible edits: Replace: One character in one string is swapped for a different character. Add:: One character is added at any index in one string. Remove: One character is removed at any index in one string. Sample Input: StringOne: alaska StringTwo: aloska Output: True Explanation: The code snippet is implementing the "One Edit Away" algorithm, which determines whether two given strings are one edit away from each other. An edit is defined as either inserting a character, removing a character, or replacing a character. The `OneEdit` function takes two strings as input and returns a boolean indicating whether they are one edit away. Here's the breakdown of the algorithm: 1. Calculate the lengths of the two strings. 2. Check if the difference in lengths is greater than 1. If so, return `false` because it's not possible to make one edit to make the strings equal. 3. Traverse both strings until the shorter one is fully traversed or an unequal character is found. 4. If an unequal character is found, check the remaining portion of the strings to determine if they are still one edit away. 5. Check the remaining characters in the longer string compared to the remaining characters in the shorter string. 6. Return `true` if the remaining portions match, indicating they are one edit away. 7. If the loop completes without finding any unequal characters, the strings are either identical or differ only in length by 1, which means they are one edit away. 8. The `abs` and `min` functions are utility functions used to calculate the absolute value and minimum of two integers, respectively. The algorithm efficiently checks for the possibility of one edit by comparing the characters at corresponding indices and handling cases where the lengths of the strings are different. O(n) time | O(1) space - where n is the length of the shorter string */ package main func OneEdit(stringOne string, stringTwo string) bool { lengthOne := len(stringOne) lengthTwo := len(stringTwo) // Check the difference in lengths between the two strings. // If the difference is greater than 1, it is not possible to make one edit to make them equal. if abs(lengthOne - lengthTwo) > 1 { return false } // Traverse the strings until the shorter one is fully traversed or an unequal character is found. for i := 0; i < min(lengthOne, lengthTwo); i++ { // If an unequal character is found, check the remaining portion of the strings to determine if they are still one edit away. if stringOne[i] != stringTwo[i] { // Check the remaining characters in the longer string compared to the remaining characters in the shorter string. // Return true if they match, indicating they are one edit away. if lengthOne > lengthTwo { return stringOne[i+1:] == stringTwo[i:] } else if lengthTwo > lengthOne { return stringTwo[i+1:] == stringOne[i:] } else { return stringOne[i+1:] == stringTwo[i+1:] } } } // If the loop completes without finding any unequal characters, the strings are either identical or differ only in length by 1. return true } func abs(a int) int { if a < 0 { return -a } return a } func min(a, b int) int { if a < b { return a } return b } ================================================ FILE: Strings/one_edit.java ================================================ /* You're given two strings stringone and stringtwo. Write a function that determines if these two strings can be made equal using only one edit. There are 3 possible edits: Replace: One character in one string is swapped for a different character. Add:: One character is added at any index in one string. Remove: One character is removed at any index in one string. Sample Input: StringOne: alaska StringTwo: aloska Output: True Explanation: The code snippet is implementing the "One Edit Away" algorithm, which determines whether two given strings are one edit away from each other. An edit is defined as either inserting a character, removing a character, or replacing a character. The `OneEdit` function takes two strings as input and returns a boolean indicating whether they are one edit away. Here's the breakdown of the algorithm: 1. Calculate the lengths of the two strings. 2. Check if the difference in lengths is greater than 1. If so, return `false` because it's not possible to make one edit to make the strings equal. 3. Traverse both strings until the shorter one is fully traversed or an unequal character is found. 4. If an unequal character is found, check the remaining portion of the strings to determine if they are still one edit away. 5. Check the remaining characters in the longer string compared to the remaining characters in the shorter string. 6. Return `true` if the remaining portions match, indicating they are one edit away. 7. If the loop completes without finding any unequal characters, the strings are either identical or differ only in length by 1, which means they are one edit away. 8. The `abs` and `min` functions are utility functions used to calculate the absolute value and minimum of two integers, respectively. The algorithm efficiently checks for the possibility of one edit by comparing the characters at corresponding indices and handling cases where the lengths of the strings are different. O(n) time | O(1) space - where n is the length of the shorter string */ public class Main { public static void main(String[] args) { String stringOne = "pale"; String stringTwo = "ple"; // Check if the strings are one edit away boolean isOneEdit = isOneEdit(stringOne, stringTwo); // Print the result System.out.println("The strings are" + (isOneEdit ? "" : " not") + " one edit away."); } public static boolean isOneEdit(String stringOne, String stringTwo) { int lengthOne = stringOne.length(); int lengthTwo = stringTwo.length(); // Check the difference in lengths between the two strings. // If the difference is greater than 1, it is not possible to make one edit to make them equal. if (Math.abs(lengthOne - lengthTwo) > 1) { return false; } // Traverse the strings until the shorter one is fully traversed or an unequal character is found. for (int i = 0; i < Math.min(lengthOne, lengthTwo); i++) { // If an unequal character is found, check the remaining portion of the strings to determine if they are still one edit away. if (stringOne.charAt(i) != stringTwo.charAt(i)) { // Check the remaining characters in the longer string compared to the remaining characters in the shorter string. // Return true if they match, indicating they are one edit away. if (lengthOne > lengthTwo) { return stringOne.substring(i + 1).equals(stringTwo.substring(i)); } else if (lengthTwo > lengthOne) { return stringTwo.substring(i + 1).equals(stringOne.substring(i)); } else { return stringOne.substring(i + 1).equals(stringTwo.substring(i + 1)); } } } // If the loop completes without finding any unequal characters, the strings are either identical or differ only in length by 1. return true; } } ================================================ FILE: Strings/one_edit.js ================================================ /* You're given two strings stringone and stringtwo. Write a function that determines if these two strings can be made equal using only one edit. There are 3 possible edits: Replace: One character in one string is swapped for a different character. Add:: One character is added at any index in one string. Remove: One character is removed at any index in one string. Sample Input: StringOne: alaska StringTwo: aloska Output: True Explanation: The code snippet is implementing the "One Edit Away" algorithm, which determines whether two given strings are one edit away from each other. An edit is defined as either inserting a character, removing a character, or replacing a character. The `OneEdit` function takes two strings as input and returns a boolean indicating whether they are one edit away. Here's the breakdown of the algorithm: 1. Calculate the lengths of the two strings. 2. Check if the difference in lengths is greater than 1. If so, return `false` because it's not possible to make one edit to make the strings equal. 3. Traverse both strings until the shorter one is fully traversed or an unequal character is found. 4. If an unequal character is found, check the remaining portion of the strings to determine if they are still one edit away. 5. Check the remaining characters in the longer string compared to the remaining characters in the shorter string. 6. Return `true` if the remaining portions match, indicating they are one edit away. 7. If the loop completes without finding any unequal characters, the strings are either identical or differ only in length by 1, which means they are one edit away. 8. The `abs` and `min` functions are utility functions used to calculate the absolute value and minimum of two integers, respectively. The algorithm efficiently checks for the possibility of one edit by comparing the characters at corresponding indices and handling cases where the lengths of the strings are different. O(n) time | O(1) space - where n is the length of the shorter string */ function isOneEdit(stringOne, stringTwo) { const lengthOne = stringOne.length; const lengthTwo = stringTwo.length; // Check the difference in lengths between the two strings. // If the difference is greater than 1, it is not possible to make one edit to make them equal. if (Math.abs(lengthOne - lengthTwo) > 1) { return false; } // Traverse the strings until the shorter one is fully traversed or an unequal character is found. for (let i = 0; i < Math.min(lengthOne, lengthTwo); i++) { // If an unequal character is found, check the remaining portion of the strings to determine if they are still one edit away. if (stringOne[i] !== stringTwo[i]) { // Check the remaining characters in the longer string compared to the remaining characters in the shorter string. // Return true if they match, indicating they are one edit away. if (lengthOne > lengthTwo) { return stringOne.slice(i + 1) === stringTwo.slice(i); } else if (lengthTwo > lengthOne) { return stringTwo.slice(i + 1) === stringOne.slice(i); } else { return stringOne.slice(i + 1) === stringTwo.slice(i + 1); } } } // If the loop completes without finding any unequal characters, the strings are either identical or differ only in length by 1. return true; } const stringOne = "pale"; const stringTwo = "ple"; // Check if the strings are one edit away const isOneEdit = isOneEdit(stringOne, stringTwo); // Print the result console.log(`The strings are${isOneEdit ? "" : " not"} one edit away.`); ================================================ FILE: Strings/one_edit.py ================================================ ''' You're given two strings stringone and stringtwo. Write a function that determines if these two strings can be made equal using only one edit. There are 3 possible edits: Replace: One character in one string is swapped for a different character. Add:: One character is added at any index in one string. Remove: One character is removed at any index in one string. Sample Input: StringOne: alaska StringTwo: aloska Output: True Explanation: The code snippet is implementing the "One Edit Away" algorithm, which determines whether two given strings are one edit away from each other. An edit is defined as either inserting a character, removing a character, or replacing a character. The `OneEdit` function takes two strings as input and returns a boolean indicating whether they are one edit away. Here's the breakdown of the algorithm: 1. Calculate the lengths of the two strings. 2. Check if the difference in lengths is greater than 1. If so, return `false` because it's not possible to make one edit to make the strings equal. 3. Traverse both strings until the shorter one is fully traversed or an unequal character is found. 4. If an unequal character is found, check the remaining portion of the strings to determine if they are still one edit away. 5. Check the remaining characters in the longer string compared to the remaining characters in the shorter string. 6. Return `true` if the remaining portions match, indicating they are one edit away. 7. If the loop completes without finding any unequal characters, the strings are either identical or differ only in length by 1, which means they are one edit away. 8. The `abs` and `min` functions are utility functions used to calculate the absolute value and minimum of two integers, respectively. The algorithm efficiently checks for the possibility of one edit by comparing the characters at corresponding indices and handling cases where the lengths of the strings are different. O(n) time | O(1) space - where n is the length of the shorter string ''' def OneEdit(stringOne, stringTwo): lengthOne = len(stringOne) lengthTwo = len(stringTwo) # Check the difference in lengths between the two strings. # If the difference is greater than 1, it is not possible to make one edit to make them equal. if abs(lengthOne - lengthTwo) > 1: return False # Traverse the strings until the shorter one is fully traversed or an unequal character is found. for i in range(min(lengthOne, lengthTwo)): # If an unequal character is found, check the remaining portion of the strings to determine if they are still one edit away. if stringOne[i] != stringTwo[i]: # Check the remaining characters in the longer string compared to the remaining characters in the shorter string. # Return True if they match, indicating they are one edit away. if lengthOne > lengthTwo: return stringOne[i+1:] == stringTwo[i:] elif lengthTwo > lengthOne: return stringTwo[i+1:] == stringOne[i:] else: return stringOne[i+1:] == stringTwo[i+1:] # If the loop completes without finding any unequal characters, the strings are either identical or differ only in length by 1. return True def abs(a): if a < 0: return -a return a def min(a, b): if a < b: return a return b ================================================ FILE: Strings/plaindrome_str.cpp ================================================ #include #include using namespace std; bool isPalindrome(string s, int start, int end) { while (start < end) { if (s[start] != s[end]) { return false; } start++; end--; } return true; } vector> partitionPalindrome(string s) { vector> result; vector current; backtrack(s, 0, current, result); return result; } void backtrack(string s, int start, vector& current, vector>& result) { if (start == s.length()) { result.push_back(current); return; } for (int end = start; end < s.length(); end++) { if (isPalindrome(s, start, end)) { current.push_back(s.substr(start, end - start + 1)); backtrack(s, end + 1, current, result); current.pop_back(); } } } int main() { string s = "aab"; vector> partitions = partitionPalindrome(s); for (auto partition : partitions) { for (string palindrome : partition) { cout << palindrome << " "; } cout << endl; } return 0; } ================================================ FILE: Strings/reverse_string.go ================================================ // Reverse a string // Program Author : Abhisek Kumar Gupta package main import "fmt" func reverse(s string) string { // since strings are immutable in go, we make array of runes chars := []rune(s) // swap start and end position characters for i, j := 0, len(chars)-1; i < j; i, j = i+1, j-1 { chars[i], chars[j] = chars[j], chars[i] } // cast chars to string return string(chars) } func main() { fmt.Printf("%v\n", reverse("abcdefg")) } ================================================ FILE: Strings/reverse_words_in_a_string.cpp ================================================ /* Given an input string s, reverse the order of the words. A word is defined as a sequence of non-space characters. The words in s will be separated by at least one space. Return a string of the words in reverse order concatenated by a single space. [Note that s may contain leading or trailing spaces or multiple spaces between two words. The returned string should only have a single space separating the words. Do not include any extra spaces.] Input: s = "the sky is blue" Output: "blue is sky the" Input: s = " hello world " Output: "world hello" Input: s = "a good example" Output: "example good a" Constraints: > 1 <= s.length <= 104 > s contains English letters (upper-case and lower-case), digits, and spaces ' '. > There is at least one word in s. */ class Solution { public: string reverseWords(string s) { //initialize the variables string ans; int i=0; while(i=s.length()) break; // if pointer exceeds the length of the string break! int j=i+1; // initialize another pointer just ahead of i while(j "dlroW olleH". // Now, let's iterate the sentence and reverse each word in place. // "dlroW olleH" -> "World Hello" start, end := 0, 0 for { // find the start index of each word by detecting spaces. for start < len(sentenceBytes) && sentenceBytes[start] == ' ' { start += 1 } if start == strLen { break } // Find the end index of the word. end = start + 1 for end < strLen && sentenceBytes[end] != ' ' { end += 1 } // Let's call our helper function to reverse the word in-place. sentenceBytes = strRev(sentenceBytes, start, end-1) start = end } return string(sentenceBytes) } // strRev function that reverses a whole sentence character by character func strRev(str []byte, startRev int, endRev int) []byte { // Starting from the two ends of the list, and moving // in towards the centre of the string, swap the characters for startRev < endRev { temp := str[startRev] // temp store for swapping str[startRev] = str[endRev] // swap step 1 str[endRev] = temp // swap step 2 startRev += 1 // move forwards towards the middle endRev -= 1 // move backwards towards the middle } // Removing multiple spaces // Removing multiple spaces i := 0 j := len(str) - 1 strRes := make([]byte, 0) for { if str[i] != ' ' { break } i++ } for { if str[j] != ' ' { break } j-- } for i <= j { if str[i] == ' ' && strRes[len(strRes)-1] != ' ' { strRes = append(strRes, str[i]) } else if str[i] != ' ' { strRes = append(strRes, str[i]) } i++ } // Returning the reversed sentence return strRes } // Driver code func main() { stringToReverse := []string{"Hello World!", "We love Python.", "The quick brown fox jumped over the lazy dog.", "Hey!", "To be, or not to be", "AAAAA", "Hello World"} for i, str := range stringToReverse { fmt.Printf("%d.\tActual string: \"%s\"\n", i+1, str) fmt.Printf("\tReversed string: \"%s\"\n", reverseWords(str)) fmt.Printf("%s\n", strings.Repeat("-", 100)) } fmt.Printf("%s\n", strings.Repeat("*", 100)) for i, str := range stringToReverse { fmt.Printf("%d.\tActual string: \"%s\"\n", i+1, str) fmt.Printf("\tReversed string: \"%s\"\n", ReverseWordsNew(str)) fmt.Printf("%s\n", strings.Repeat("-", 100)) } } // Using regexp and strings library func ReverseWordsNew(s string) string { // trim leading and multiple spaces trimmedString := trimHelper(s) // convert string into array bytes since strings are immutable in go sBytes := []byte(trimmedString) // reverse the entire sBytes first strlen := len(sBytes) reversedString := revHelper(sBytes, 0, strlen - 1) // this way words will be in desired position start, end := 0, 0 for { for start < strlen && reversedString[start] == ' ' { start += 1; } if start == strlen { break } end = start + 1 for end < strlen && reversedString[end] != ' ' { end += 1 } reversedString = revHelper(reversedString, start, end - 1) start = end } return string(reversedString) } func trimHelper(s string) string { result := strings.TrimSpace(s) regex := regexp.MustCompile("\\s+") result = regex.ReplaceAllString(result, " ") return result } func revHelper(s []byte, start int, end int) []byte { for start < end { s[start], s[end] = s[end], s[start] start += 1 end -= 1 } return s } ================================================ FILE: Strings/reverse_words_in_a_string.js ================================================ /// Problem Statement: /// You are given a string of length N. You need to reverse the string word by word. There can be multiple spaces between two words and there can be leading or trailing spaces but, in the output, reversed string you need to put a single space between two words, and your reversed string should not contain leading or trailing spaces. /// Sample Input: You need to reverse the string word by word /// Expected Output: word by word string the reverse to need You /// Decalaration of function which will receive string to reverese function reverseString(inputString) { /// Trim function removes all the leading and trailing spaces, Split method store them in the array inputStringArray var trimmedString = inputString.trim(); var inputStringArray = trimmedString.split(" "); /// Variable declaration to store the output result var outputString = ""; /// Loop through the array in reverse order to reverse the string, subtracting -1 from length because length starts from 1 but index strats from 0 for (i = inputStringArray.length - 1; i >= 0; i--) { /// If its a first iteration then store the word in variable otherwise concatenate it with the current string if (inputStringArray[i] == "") { continue; } else { outputString += inputStringArray[i] + " "; } } return outputString.trim(); } // console.log("You are given a string of length N"); // console.log(reverseString("You are given a string of length N")); console.log(" You need to reverse the string word by word "); console.log( reverseString( " You need to reverse the string word by word " ) ); ================================================ FILE: Strings/reverse_words_in_string.java ================================================ /** * Given an input string s, reverse the order of the words. * * A word is defined as a sequence of non-space characters. The words in s will be separated by at least one space. * * Return a string of the words in reverse order concatenated by a single space. * * Note that s may contain leading or trailing spaces or multiple spaces between two words. The returned string should only have a single space separating the words. Do not include any extra spaces. * * * * Example 1: * * Input: s = "the sky is blue" * Output: "blue is sky the" * Example 2: * * Input: s = " hello world " * Output: "world hello" * Explanation: Your reversed string should not contain leading or trailing spaces. */ package Strings; public class ReverseWordsInString { public static void main(String[] args) { String string = "the sky is blue"; String res = solve(string); System.out.println(res); } public static String solve(String string) { // O(N) time | O(N) - where N is the length of the array. //Convert String to String Builder //and trim spaces at the same time StringBuilder stringBuilder = trimSpaces(string); System.out.println(string); //reverse the whole word reverse(stringBuilder, 0, stringBuilder.length() - 1); System.out.println(stringBuilder); //reverse each word reverseEachWord(stringBuilder); System.out.println(stringBuilder); return stringBuilder.toString(); } public static StringBuilder trimSpaces(String string) { int leftIdx = 0, rightIdx = string.length() - 1; // remove leading spaces while (leftIdx <= rightIdx && string.charAt(leftIdx) == ' ') ++leftIdx; // remove trailing spaces while (leftIdx <= rightIdx && string.charAt(rightIdx) == ' ') --rightIdx; // reduce multiple spaces to single one StringBuilder stringBuilder = new StringBuilder(); while (leftIdx <= rightIdx) { char currentChar = string.charAt(leftIdx); if (currentChar != ' ') stringBuilder.append(currentChar); else if (stringBuilder.charAt(stringBuilder.length() - 1) != ' ') stringBuilder.append(currentChar); ++leftIdx; } return stringBuilder; } public static void reverse(StringBuilder stringBuilder, int leftIdx, int rightIdx) { while (leftIdx < rightIdx) { char temp = stringBuilder.charAt(leftIdx); stringBuilder.setCharAt(leftIdx++, stringBuilder.charAt(rightIdx)); stringBuilder.setCharAt(rightIdx--, temp); } } public static void reverseEachWord(StringBuilder stringBuilder) { int len = stringBuilder.length(); int startIdx = 0, endIdx = 0; while (startIdx < len) { // go to the end of the word while (endIdx < len && stringBuilder.charAt(endIdx) != ' ') ++endIdx; // reverse the word reverse(stringBuilder, startIdx, endIdx - 1); // move to the next word startIdx = endIdx + 1; ++endIdx; } } } ================================================ FILE: Strings/valid_palindrome.js ================================================ /*Name : Abhinav kumar Github username : Abhinavcode13 Repository name : data-structures-and-algorithms Problem :Two Pointers: Check whether a given string is a Valid Palindrome in Javascript Issue Number : #649 Problem statement : Explanation of the below Java code : we first define the isPalindrome() function, which converts the input string to lowercase and removes all non-alphanumeric characters using a regular expression, and then uses two pointers to loop through the string and check whether it is a valid palindrome. We then use the prompt() function to display a popup dialog box and prompt the user to enter a string input. The input value is then stored in the str variable. We then call the isPalindrome() function with the input string as an argument to check whether it is a palindrome. If it is, we use the console.log() function to display a message saying that the input string is a palindrome. If it is not, we display a message saying that the input string is not a palindrome. */ -------------------------------------------------------------------------------------------------------//Java code begins here------------------------------------------------------------------------------------------------------------------------------- function isPalindrome(str) { // Convert the input string to lowercase and remove non-alphanumeric characters str = str.toLowerCase().replace(/[^a-z0-9]/g, ''); // Initialize two pointers, one starting from the beginning of the string, and the other from the end let left = 0; let right = str.length - 1; // Loop through the string while the pointers haven't crossed each other while (left < right) { // If the characters at the left and right pointers don't match, the string is not a palindrome if (str[left] !== str[right]) { return false; } // Move the pointers towards each other left++; right--; } // If the loop finishes, the string is a palindrome return true; } // Prompt the user for a string input let str = prompt("Enter a string:"); // Check whether the input string is a palindrome if (isPalindrome(str)) { console.log(`${str} is a palindrome!`); } else { console.log(`${str} is not a palindrome.`); } ================================================ FILE: Strings/valid_pallindrome2.cpp ================================================ // Check whether a string can be a valid palindrome by removing at most one character from it /* Write a function that takes a string as input and checks whether it can be a valid palindrome by removing at most one character from it. Constraints: string.length The string only consists of English letters Sample Input : "madame" Output : True Sample Input : "masdasd" Output : False */ class Solution { public: bool validate(string s, int start, int end){ int len = s.size(); while(start < end){ if(s[start] == s[end]){ start++; end--; } // doesn't match then return false else{ return false; } } return true; } bool validPalindrome(string s) { int len = s.size(); // initialize two pointers at opposite end of string int start = 0, end = len - 1; while(start < end){ // if value at left and right index match then move them closer if(s[start] == s[end]){ start++; end--; } else{ // if mismatch occurs then skip one and check rest bool r1 = validate(s, start + 1, end); // skip one from left and check remaining bool r2 = validate(s, start, end -1); // skip one from right and check remaining return r1 || r2; // if either is true return true } } return true; } }; ================================================ FILE: Strings/valid_pallindrome2.go ================================================ // Check whether a string can be a valid palindrome by removing at most one character from it /* The given Go program takes a string as input and checks if it can be a valid palindrome by removing at most one character from it. The program starts by defining two pointers, left and right, that move towards each other from opposite ends of the input string. At each step, if the characters at the left and right pointers are equal, then they move towards the center of the string. If the characters are not equal, then we check whether removing the character at the left or right pointer would make the rest of the string a palindrome. If both removals fail, then the string cannot be a valid palindrome by removing at most one character. The program returns true if the string can be made a valid palindrome by removing at most one character, false otherwise. The time complexity of this algorithm is O(n), where n is the length of the input string. The space complexity is O(1), as we only use constant extra space to store a few variables. */ package main // isPalindromeWithRemoval checks if a string can be converted into a palindrome // by removing at most one character func isPalindromeWithRemoval(s string) bool { // Define two pointers to check the string from both ends i, j := 0, len(s)-1 for i < j { // If characters at the pointers are not equal if s[i] != s[j] { // Check if removing a character from the left pointer makes it a palindrome leftStr := s[:i] + s[i+1:j+1] if isPalindrome(leftStr) { return true } // Check if removing a character from the right pointer makes it a palindrome rightStr := s[:i] + s[i:j] if isPalindrome(rightStr) { return true } // If neither option works, it cannot be converted into a palindrome by removing at most one character return false } // Move the pointers towards the middle of the string i++ j-- } return true } // isPalindrome checks if a string is a palindrome func isPalindrome(s string) bool { // Define two pointers to check the string from both ends i, j := 0, len(s)-1 for i < j { // If characters at the pointers are not equal, it is not a palindrome if s[i] != s[j] { return false } // Move the pointers towards the middle of the string i++ j-- } return true } ================================================ FILE: Strings/valid_pallindrome2.java ================================================ // Check whether a string can be a valid palindrome by removing at most one character from it /* The given Go program takes a string as input and checks if it can be a valid palindrome by removing at most one character from it. The program starts by defining two pointers, left and right, that move towards each other from opposite ends of the input string. At each step, if the characters at the left and right pointers are equal, then they move towards the center of the string. If the characters are not equal, then we check whether removing the character at the left or right pointer would make the rest of the string a palindrome. If both removals fail, then the string cannot be a valid palindrome by removing at most one character. The program returns true if the string can be made a valid palindrome by removing at most one character, false otherwise. The time complexity of this algorithm is O(n), where n is the length of the input string. The space complexity is O(1), as we only use constant extra space to store a few variables. */ public static boolean isPalindromeWithRemoval(String s) { // Define two pointers to check the string from both ends int i = 0, j = s.length() - 1; while (i < j) { // If characters at the pointers are not equal if (s.charAt(i) != s.charAt(j)) { // Check if removing a character from the left pointer makes it a palindrome String leftStr = s.substring(0, i) + s.substring(i + 1); if (isPalindrome(leftStr)) { return true; } // Check if removing a character from the right pointer makes it a palindrome String rightStr = s.substring(0, j) + s.substring(j + 1); if (isPalindrome(rightStr)) { return true; } // If neither option works, it cannot be converted into a palindrome by removing at most one character return false; } // Move the pointers towards the middle of the string i++; j--; } return true; } public static boolean isPalindrome(String s) { // Define two pointers to check the string from both ends int i = 0, j = s.length() - 1; while (i < j) { // If characters at the pointers are not equal, it is not a palindrome if (s.charAt(i) != s.charAt(j)) { return false; } // Move the pointers towards the middle of the string i++; j--; } return true; } ================================================ FILE: Strings/valid_pallindrome2.js ================================================ // Check whether a string can be a valid palindrome by removing at most one character from it /* The validPalindrome function takes a string s as input and returns true if the string can be a valid palindrome by removing at most one character, and false otherwise. It works by initializing two pointers left and right to the left and right ends of the string, respectively. It then moves the pointers inward while checking if the characters at the left and right indices are equal. If they are not equal, it removes the left or right character, creates a new string, and checks if the new string is a palindrome using the isPalindrome helper function. If the new string is a palindrome, it returns true. If not, it continues moving the pointers inward and repeating the process. If the pointers meet, the string is already a palindrome, or it can be made into one by removing at most one character, so it returns true. The isPalindrome function takes a string s as input and returns true if the string is a palindrome, and false otherwise. It works by initializing two pointers left and right to the left and right ends of the string, respectively. It then moves the pointers inward while checking if the characters at the Time complexity: O(n) : We iterate over the input string once with two pointers, which takes O(n) time. Space complexity: O(1) : We only use a few constant amount of variables to keep track of the pointers, left and right. Therefore, the space complexity is O(1). */ /** * Checks whether a string can be a valid palindrome by removing at most one character. * @param {string} s - The input string to check. * @return {boolean} - Returns true if the string can be a valid palindrome, false otherwise. */ function validPalindrome(s) { let left = 0; // Pointer to the left end of the string. let right = s.length - 1; // Pointer to the right end of the string. while (left < right) { if (s[left] !== s[right]) { // If characters at left and right indices are not equal. // Check if string without left index is a palindrome. let str1 = s.slice(0, left) + s.slice(left + 1); if (isPalindrome(str1)) { return true; } // Check if string without right index is a palindrome. let str2 = s.slice(0, right) + s.slice(right + 1); if (isPalindrome(str2)) { return true; } // If string without left or right index is not a palindrome, return false. return false; } // Move pointers inward if characters at left and right indices are equal. left++; right--; } // If we get here, the string is already a palindrome, or it can be made into one // by removing at most one character, so return true. return true; } /** * Checks whether a string is a palindrome. * @param {string} s - The input string to check. * @return {boolean} - Returns true if the string is a palindrome, false otherwise. */ function isPalindrome(s) { let left = 0; // Pointer to the left end of the string. let right = s.length - 1; // Pointer to the right end of the string. while (left < right) { if (s[left] !== s[right]) { // If characters at left and right indices are not equal, return false. return false; } // Move pointers inward if characters at left and right indices are equal. left++; right--; } // If we get here, the string is a palindrome, so return true. return true; } ================================================ FILE: Strings/valid_pallindrome2.py ================================================ // Check whether a string can be a valid palindrome by removing at most one character from it ''' Explanation: The function valid_palindrome takes a string s as input and returns a boolean indicating whether the string can be a valid palindrome by removing at most one character from it. To check if the string can be a valid palindrome, we start by iterating over the string from both the left and right ends at the same time. If we encounter two characters that are not the same, we need to check if removing one of them would make the string a palindrome. We try removing the left character first and create two new substrings - one with the left character removed and one with the right character removed. We then check if either of the substrings is a palindrome. If either of the substrings is a palindrome, then the original string can be a palindrome by removing the corresponding character. If neither of the substrings is a palindrome, then the original string cannot be a palindrome even by removing a character. If we have checked the whole string and haven't returned yet, it means that the original string is a palindrome. The time complexity of the given function is O(n), where n is the length of the input string. This is because we are iterating through the string only once and performing constant-time operations. The space complexity of the function is also O(n), where n is the length of the input string. This is because we are using a constant amount of extra space to keep track of the counts of characters. The size of this extra space does not depend on the length of the input string. ''' # using two-pointer Technique def valid_palindrome(s: str) -> bool: left, right = 0, len(s) - 1 # iterate over the string from left and right at the same time while left < right: # if the characters at the left and right indices are not the same if s[left] != s[right]: # we need to check if removing one of them makes the string a palindrome # we try removing the left character first left_removed = s[left:right] right_removed = s[left+1:right+1] # if either of the substrings after removal is a palindrome, then the original string is also a palindrome return left_removed == left_removed[::-1] or right_removed == right_removed[::-1] # move to the next pair of characters left += 1 right -= 1 # if we have checked the whole string and haven't returned yet, it's a palindrome return True ================================================ FILE: Strings/well_formed_parentheses.cpp ================================================ /* Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses. Example 1: Input: n = 3 Output: ["((()))","(()())","(())()","()(())","()()()"] Example 2: Input: n = 1 Output: ["()"] Constraints: 1 <= n <= 8 */ #include class Solution { public: vector ans; int N; void solve(string s,int count) { if(s.size()==2*N) ans.push_back(s); if(count generateParenthesis(int n) { string st; solve(st,0); return ans; } }; ================================================ FILE: Strings/well_formed_parentheses.java ================================================ /** Time Complexity: O(2^n * n), Space Complexity: O(n). Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses. Example 1: Input: n = 3 Output: ["((()))","(()())","(())()","()(())","()()()"] Example 2: Input: n = 1 Output: ["()"] Constraints: 1 <= n <= 8 **/ class Solution { public List generateParenthesis(int n) { ArrayList validParenthesis = new ArrayList(); /** We need to generate every n pair valid parenthses. What we are going to do is try every possible combination if we have a 2 pair parenthes p: ???? each question mark could be '(' or ')'. ex: ()() or (()) or ((() so we have a total of (2 ^ (2 * n)) combination. if we say that an open bracket '(' can be expressed as 0 and a closed bracket ')' can be expressed 1. 0000 -> (((( 0001 -> ((() 0010 -> (()( 0011 -> (()) 0100 -> ()(( 0101 -> ()() . . . . . 1111 -> )))) **/ for (int i = 0; i < (1 << (2 * n)); ++i) { String aParenthesis = ""; for(int j = 0; j < 2 * n; ++j) { if(((i >> j) & 1) == 1) { aParenthesis += '('; } else aParenthesis += ')'; } if(isValid(aParenthesis)) validParenthesis.add(aParenthesis); } return validParenthesis; } /** Function: isValid check if parenthesis is balanced or not. For each open bracket we find we pushed it to the stack. If We find a closed bracket we check the top of the stack if it is an open bracket that's mean we find a corrsponding closing bracket so, we removed it. else the top of the stack is a closed bracket that means there is no open bracket that matches that closed bracket, that's means the parenthesis is NOT valid or balanced so, we return false. At the end if we find that the stack is empty that's mean that each open breack has a corrsponding closed bracket, so, we return true otherwise we return false. **/ public boolean isValid(String s) { int n = s.length(); Stack st = new Stack(); for(int i = 0; i < n; ++i) { char ch = s.charAt(i); if( ch == '(') { st.push(ch); } else { if(!st.empty() && st.peek() == '(') st.pop(); else return false; } } return st.empty(); } } ================================================ FILE: Strings/zigzag_conversion.cpp ================================================ /* The string "PAYPALISHIRING" is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility) P A H N A P L S I I G Y I R And then read line by line: "PAHNAPLSIIGYIR" Write the code that will take a string and make this conversion given a number of rows: string convert(string s, int numRows); Example 1: Input: s = "PAYPALISHIRING", numRows = 3 Output: "PAHNAPLSIIGYIR" Example 2: Input: s = "PAYPALISHIRING", numRows = 4 Output: "PINALSIGYAHRPI" Explanation: P I N A L S I G Y A H R P I Example 3: Input: s = "A", numRows = 1 Output: "A" Constraints:\ 1 <= s.length <= 1000 s consists of English letters (lower-case and upper-case), ',' and '.'. 1 <= numRows <= 1000 */ #include class Solution { public: string convert(string s, int numRows) { if(numRows <= 1) return s; string *A = new string[numRows]; int n = s.size(); int row = 0; int step = 1; for(int i = 0; i < n; i++){ A[row].push_back(s[i]); if(row == 0) step = 1; else if(row == numRows - 1) step = -1; row = row + step; } string ans = ""; for(int i = 0; i < numRows; i++){ ans += A[i]; } return ans; } }; ================================================ FILE: Trees/AVL/avl.go ================================================ package main /* A binary tree is said to be an AVL tree, if: 1 It is a binary search tree, and 2 For any node N, the height of left subtree of N and height of right subtree of N differ by at most 1. */ type AVLTreeNode struct { data int left *AVLTreeNode right *AVLTreeNode height int } // Height: returns the height of AVL tree // Time Complexity: O(1) func Height(node *AVLTreeNode) int { if node == nil { return -1 } else { return node.height } } // SingleLeftRotate: left rotate a root, and update node's height, return the new root // Time Complexity: O(1). Space Complexity: O(1). func SingleLeftRotate(X *AVLTreeNode) *AVLTreeNode { var W *AVLTreeNode if X != nil { W = X.left X.left = W.right W.right = X X.height = Max(Height(X.left), Height(X.right)) + 1 W.height = Max(Height(W.left), Height(W.right)) + 1 X = W } return X } // SingleRightRotate: right rotate a root, and update node's height, return the new root // Time Complexity: O(1). Space Complexity: O(1). func SingleRightRotate(X *AVLTreeNode) *AVLTreeNode { var W *AVLTreeNode if X != nil { W = X.right X.right = W.right W.left = X X.height = Max(Height(X.left), Height(X.right)) + 1 W.height = Max(Height(W.left), Height(W.right)) + 1 X = W } return X } // DoubleRotateRightLeft: left-right double rotation func DoubleRotateRightLeft(Z *AVLTreeNode) *AVLTreeNode { Z.right = SingleLeftRotate(Z.right) return SingleRightRotate(Z) } // DoubleRotateLeftRight: right-left double rotation func DoubleRotateLeftRight(Z *AVLTreeNode) *AVLTreeNode { Z.left = SingleRightRotate(Z.left) return SingleLeftRotate(Z) } func main() { } ================================================ FILE: Trees/Binary Search Trees/Kth_Largest_Value_In_BST.py ================================================ # Name : Manmay Ghosh # Github username : ManmayGhosh # Repository name : data-structures-and-algorithms # Problem : Kth Largest Value In BST in Python # Issue Number : #1225 # Explanation of the below Python code : # 1. The idea is to do reverse inorder traversal of BST. Keep a count of nodes visited. # 2. The reverse inorder traversal traverses all nodes in decreasing order, i.e, visit the right node then centre then left and continue traversing the nodes recursively. # 3. While doing the traversal, keep track of the count of nodes visited so far. # 4. When the count becomes equal to k, stop the traversal and print the key. # -------------------------------------------------------------------------//Python code begins here------------------------------------------------------------------------ class Node: # Constructor to create a new node def __init__(self, val): self.key = val self.left = None self.right = None # This function will find k'th largest element in a tree. def kthLargestUtil(root, k, c): # Base cases, c[0] >= k is important to avoid unnecessary recursive calls if root == None or c[0] >= k: return # Follow reverse inorder traversal so that the largest element is visited first kthLargestUtil(root.right, k, c) # Increment count of visited nodes c[0] += 1 # If c becomes k now, then this is the k'th largest if c[0] == k: print("K'th largest element is",root.key) return # Recurssion for left subtree kthLargestUtil(root.left, k, c) # Function to find k'th largest element def kthLargest(root, k): # Initialize count of nodes visited as 0 c = [0] # Note that c is passed by reference kthLargestUtil(root, k, c) # Function to insert a new node in BST */ def insert(node, key): if node == None: return Node(key) if key < node.key: node.left = insert(node.left, key) elif key > node.key: node.right = insert(node.right, key) return node # Driver Code if __name__ == '__main__': root = None root = insert(root, 50) insert(root, 30) insert(root, 20) insert(root, 40) insert(root, 70) insert(root, 60) insert(root, 80) k = int(input("Enter the Kth element(should be greater than 1)")) kthLargest(root, k) # Complexity Analysis: # Time Complexity: O(n). # In worst case the code can traverse each and every node of the tree if the k given is equal to n (total number of nodes in the tree). Therefore overall time complexity is O(n). # Auxiliary Space: O(h). # Max recursion stack of height h at a given time. ================================================ FILE: Trees/Binary Search Trees/Kth_largest_BST.cpp ================================================ /*TASK Binary Search Tree : Get kth largest element in a BST A BST is a tree structure that is sorted by the nodes' data. Following rules are true for every picked node in a BST: -> all nodes to the left have data that is less than the data of the picked node -> all nodes to the right have data that is greater than the the data of the picked node -> all nodes have 2 children (left and right), but they can be empty (null) too. */ /*SAMPLE DATA Take a look on the following BST 3 2 7 1 5 9 4 For k = 3, we should get 5 as an output, because 5 is the 3. largest element in the BST */ /*APPROACH an recursive algorithm that is going to get to the nodes in descending order. the visited nodes get appended to an array and if the kth node is visited the recursion will be left 1. start with the root node of a BST 2. for each node that is reached, do the first that is possible from the following: "if possible" means that the node has a valid child in that direction that is not already visited 1. go to the right if possible to get to the far right node which also has the largest data 2. go to the left if possible to get larger data than going back (If the algorithm goes back from the current node, all right nodes will be already visted, that means that we will land on a lower data node) 3. go back If we cannot do 1. we save this node as visited. The kth visited node is also the kth largest. if this node is reached it is getting saved in a class member variable. In each recursion call this variable gets checked. If the node is set (we already found the kth largest node), the recursion step returns before doing anything. that is how the programm will get out of the recursion fast after finding the target node. 3. if the member variable is set, that is the kth largest node, if its empty there is no kth largest node (out of range) SAMPLE 1. first we start with the root 3 2. we can go right, so we will do so (node 7) 3. we can go right (node 9) 4. we cannot go right, we save this in a visited node list (node 9, list 9) 5. we cannot go left, we go back (node 7) 6. we cannot go right, because 9 is already visited, we save this in visited nodes (node 7, list 9,7) 7. we can go left (node 5) 8. we cannot go right, we save this in visited nodes (node 5, list 9,7,5) 9. the list's size is equal to k, we will set the member variable to the last saved node 5 (node 5, list 9,7,5, member 5) 10. we have a member set, we go back (node 7) 11. we have a member set, we go back (node 3) 12. we have a member set, we go back (leaving recursion) 13. member variable holds 5, which is the 3. largest number in the BST */ /*COMPLEXITY the algorithm has to go to the far right in h steps, where h is the height of the tree and additionally k steps to get to the kth largest value. to break out the programm needs additionally h steps Time complexity is O(2*h+k). The complexity class is O(n), because h is linear in a worst case (skewed to one side) and k is also linear. Space complexity is O(h), because the algorithm needs to store a maximum of h recursion calls. The complexity class is O(n), because h is linear in a worst case (skewed to one side). But in a balanced tree it is only O(log(n)), because h is log(n), where n is the number of nodes in the balanced tree. */ #include #include #include class Node{ //Node class holds one node of the tree //every node holds some data (int) and has two children //an empty children gets represented with nullptr public: int data; Node* left; Node* right; public: Node(int item){ //init the node with no children data = item; left = nullptr; right = nullptr; } virtual ~Node(){ /* IMPORTANT to avoid memory leaks a virtual constructor is required to correctly delete all children (and children of children) checking if the child is not empty than calling the children's destructor */ if(left != nullptr){delete left;} if(right != nullptr){delete right;} }; }; class BinaryTree{ //holds the full tree (and tree functionalities) private: Node* kth_largest; //that is the member variable that our algorithm uses to save the target node std::vector visited_nodes; //the list of visited nodes public: Node* root; //pointer to the root node of the BST private: void traverseRL(Node* node, int k){ //this method is traversing the BST from the largest to the smallest element //this algorithm is taking the root node and the k if(this->kth_largest != nullptr){ //are we ready? return; } if(node->right != nullptr){ //can we go to the right? traverseRL(node->right, k); //go to the right } this->visited_nodes.push_back(node); //cannot go more right, add to visited nodes if(this->visited_nodes.size() == k){ //if the visited nodes list is long enough to hold the target node this->kth_largest = node; //save this node in the member variable return; //start to break down the recursion } if(node->left != nullptr){ //can we go to the left? traverseRL(node->left, k); //go to the left } return; //this node is already dealt with } public: BinaryTree(int root_data){ //creates a binary tree with one node (root) that gets some given data kth_largest = nullptr; root = new Node(root_data); } Node* insertNode(Node* node, int data){ //insert a new node on the right position (remember the BST rules) if(node == nullptr){ //if we reach an empty spot we set the new node there return new Node(data); } if(data <= node->data){ //data is smaller than the data of the current node node->left = insertNode(node->left, data); //we have to got to the left } else{ //data is greater than the data of the current node node->right = insertNode(node->right, data); //we have to got to the right } return node; //get the node back to the previous recursion layer } Node* getKthLargest(int k){ //get the kth largest data node this->kth_largest = nullptr; //init the member variables that are needed for the algorithm this->visited_nodes.clear(); this->traverseRL(this->root, k); //perform the search if(this->kth_largest == nullptr){ //kth largest element cannot be found (out of range) std::cout << "There is no " << k << ". largest element in this tree" << std::endl; std::cout << "Valid k's for a tree are numbers between 1 and the number of nodes inclusive"; return nullptr; }else{ return this->kth_largest; //returrn the kth largest node } } }; int main() { //sample int k = 3; BinaryTree tree = BinaryTree(3); //building up the sample BST tree.insertNode(tree.root, 7); tree.insertNode(tree.root, 2); tree.insertNode(tree.root, 5); tree.insertNode(tree.root, 9); tree.insertNode(tree.root, 1); tree.insertNode(tree.root, 4); std::cout << tree.getKthLargest(k)->data << std::endl; //print the result return 0; } ================================================ FILE: Trees/Binary Search Trees/Kth_largest_BST.java ================================================ /** * A class to represent a node in the BST. */ class Node { int data; Node left, right; Node(int data) { this.data = data; left = right = null; } } /** * A class to represent a binary search tree. */ class BST { Node root; BST() { root = null; } /** * Function to find the kth largest element in the BST. Time complexity: O(n), where n is the number of nodes in the BST. This is because we need to visit every node in the worst case to find the kth largest element. Space complexity: O(h), where h is the height of the BST. This is because we're recursively traversing the tree using a call stack, which can have at most h frames in it The kthLargest() function takes an integer k as input and returns the kth largest element in the BST. It first creates an empty result list, calls the kthLargestHelper() function to traverse the BST in reverse inorder, and collects the kth largest element in the result list. It then returns the last element of the result list, which is the kth largest element in the BST. Time complexity: O(k), since we're only collecting up to k elements in the result list. pace complexity: O(h), where h is the height of the BST. This is because we're recursively traversing the tree using a call stack, which can have at most h frames in it */ public int kthLargest(int k) { List result = new ArrayList<>(); kthLargestHelper(root, result, k); return result.get(result.size() - 1); } /**The kthLargestHelper() function is a helper function to traverse the BST in reverse inorder and collect the kth largest element in the result list. It first recursively calls itself on the right child of the current node, then adds the current node's `data */ private void kthLargestHelper(Node node, List result, int k) { if (node == null || result.size() == k) { return; } kthLargestHelper(node.right, result, k); if (result.size() < k) { result.add(node.data); } kthLargestHelper(node.left, result, k); } /** * Function to insert a node in the BST. Time complexity: O(h), where h is the height of the BST. This is because we're traversing the tree from the root to the correct position for the new node. Space complexity: O(1), since we're only creating a new node and not using any extra memory. */ public void insert(int data) { root = insertHelper(root, data); } /** * Helper function to insert a node in the BST. Time complexity: O(h), where h is the height of the BST. This is because we're traversing the tree from the root to the correct position for the new node. S pace complexity: O(1), since we're only creating a new node and not using any extra memory. */ private Node insertHelper(Node node, int data) { if (node == null) { node = new Node(data); return node; } if (data < node.data) { node.left = insertHelper(node.left, data); } else if (data > node.data) { node.right = insertHelper(node.right, data); } return node; } } /** * A class to test the BST implementation. */ public class KthLargestBST { public static void main(String[] args) { BST bst = new BST(); bst.insert(50); bst.insert(30); bst.insert(20); bst.insert(40); bst.insert(70); bst.insert(60); bst.insert(80); int k = 3; int kthLargest = bst.kthLargest(k); System.out.println(k + "th largest element in the BST is " + kthLargest); } } /**Overall, the time complexity of finding the kth largest element in a BST using this code is O(n), and the space complexity is O(h). */ ================================================ FILE: Trees/Binary Search Trees/Kth_largest_BST.js ================================================ /** The Node class represents a single node in the BST. It has three properties: value to store the node's value, left to store the reference to the left child node, and right to store the reference to the right child node. */ class Node { constructor(value) { this.value = value; this.left = null; this.right = null; } } /** the BST class represents the Binary Search Tree. It has one property: root to store the reference to the root node of the tree. */ class BST { constructor() { this.root = null; } /** insert(value): This function inserts a new node with the given value into the BST. It uses the insertNode helper function for recursive insertion. The time complexity is O(log n) on average (O(n) in the worst case if the tree is heavily unbalanced), and the space complexity is O(1). * @param {number} value - The value to be inserted */ insert(value) { const newNode = new Node(value); if (this.root === null) { this.root = newNode; } else { this.insertNode(this.root, newNode); } } /** The insertNode(node, newNode) function is a helper function used by the insert(value) method in the BST class. It recursively inserts a new node (newNode). Here's a breakdown of how the insertNode function works: * It compares the value of the newNode with the value of the node to determine whether to go left or right in the BST. * If the value of newNode is less than the value of node, it checks if the left child of node is null. If it is, the newNode becomes the left child; otherwise, the function recursively calls itself with the left child node of node. * If the value of newNode is greater than or equal to the value of node, it checks if the right child of node is null. If it is, the newNode becomes the right child; otherwise, the function recursively calls itself with the right child node of node. * This process continues until a suitable position is found for the newNode in the BST. The time complexity of the insertNode function depends on the structure of the BST. In the average case, when the tree is balanced, the time complexity is O(log n), where n is the number of nodes in the tree. This is because at each level of the tree, the function divides the remaining nodes to be searched by half. However, in the worst case scenario, when the tree is heavily unbalanced (e.g., resembles a linked list), the time complexity becomes O(n), where n is the number of nodes in the tree. This happens when all nodes are in a straight line from the root. The space complexity of the insertNode function is O(log n) in the average case and O(n) in the worst case. This is due to the recursive calls that consume memory on the call stack. In the average case, the maximum number of recursive calls is limited by the height of the balanced tree, which is logarithmic to the number of nodes. In the worst case, where the tree is unbalanced, the maximum number of recursive calls is equal to the number of nodes in the tree. * @param {Node} node - The current node being traversed * @param {Node} newNode - The new node to be inserted */ insertNode(node, newNode) { if (newNode.value < node.value) { if (node.left === null) { node.left = newNode; } else { this.insertNode(node.left, newNode); } } else { if (node.right === null) { node.right = newNode; } else { this.insertNode(node.right, newNode); } } } /** findKthLargest(k): This function finds the Kth largest value in the BST. It first performs an in-order traversal of the tree to retrieve a sorted array of values. If K is larger than the number of nodes in the tree, it returns null. Otherwise, it returns the Kth largest value from the sorted array. The time complexity of this function is O(n), where n is the number of nodes in the tree. The space complexity is O(n) since it stores all the values in the array during the traversal. * @param {number} k - The Kth largest value to find * @returns {number|null} - The Kth largest value, or null if it doesn't exist */ findKthLargest(k) { if (k <= 0) { throw new Error('k should be a positive integer'); } const sortedValues = []; this.inOrderTraversal(this.root, sortedValues); if (k > sortedValues.length) { return null; } return sortedValues[sortedValues.length - k]; } /** inOrderTraversal(node, values): This is a helper function that performs an in-order traversal of the BST and stores the sorted values in the given array. It recursively visits the left subtree, then the current node, and finally the right subtree. The time complexity of this function is O(n), where n is the number of nodes in the tree, as it needs to visit each node exactly once. The space complexity is O(n) since it uses the array to store the values. * @param {Node} node - The current node being traversed * @param {Array} values - The array to store the sorted values */ inOrderTraversal(node, values) { if (node !== null) { this.inOrderTraversal(node.left, values); values.push(node.value); this.inOrderTraversal(node.right, values); } } } // Sample input and imlplementation /** 5 / \ 3 7 / \ / \ 2 4 6 8 Now, let's find the 3rd largest value in the BST. The sorted order of the tree is [2, 3, 4, 5, 6, 7, 8]. The 3rd largest value is 6. Here's the updated tree with the 3rd largest value marked: 5 / \ 3 7 / \ / \ 2 4 6* 8 As you can see, the 3rd largest value, 6, is marked with an asterisk (*). */ // Create a new instance of BST const bst = new BST(); // Insert nodes into the BST bst.insert(5); bst.insert(3); bst.insert(7); bst.insert(2); bst.insert(4); bst.insert(6); bst.insert(8); // Find the 3rd largest value const kthLargest = bst.findKthLargest(3); console.log(kthLargest); // Output: 6 ================================================ FILE: Trees/Binary Search Trees/Validate_BST.cpp ================================================ // VALIDATE BINARY SEARCH TREE --->> LEETCODE // Given the root of a binary tree, determine if it is a valid binary search tree (BST). // A valid BST is defined as follows: // The left // subtree // of a node contains only nodes with keys less than the node's key. // The right subtree of a node contains only nodes with keys greater than the node's key. // Both the left and right subtrees must also be binary search trees. // ALGORITHM--> // Follow these steps while the current node is not null: // Process the current node and go to its right child if it doesn't have a left child. // Find the inorder predecessor of the current node—that is, the rightmost node in the left subtree—if the present node has a left child, and see if its value is smaller than the value of the current node. // If the predecessor's right child is null, go to the current node's left child and change the predecessor's right child to point to it. // In order to restore the binary tree's original structure, reset the predecessor's right child to null, process the current node, and then move to its right child if it is already referring to the current node. // C++ program to check if a given tree is BST. #include using namespace std; struct Node { // structure of a node of the tree. int data; struct Node *left, *right; Node(int data) { this->data = data; left = right = NULL; } }; bool validate(Node* root,long long int min , long long int max){ if(!root) return true; // if the root is null then it is a valid BST. it means that the tree is empty or we had reached the end of tree. // initializing the ans variable to false (temporarily). bool ans = false; // checking if the root's data is in the range of min and max. if(root->datadata>min) ans = true; else return ans; // if the root's data is not in the range of min and max then it is not a valid BST. hence returning false. // changing min and max for the left and right subtree. and checking for the left and right subtree with respesct to tree root and returning the ans. return ans && validate(root->left,min,root->data) && validate(root->right,root->data,max); } bool isValidBST(Node* root) { if(!root) return true; // calling validate function so that it can check for the left and right subtree .. while giving the range of the values of the nodes. return validate(root ,-9223372036854775807,9223372036854775807 ); } int main() { // Initializing the tree. struct Node* root = new Node(3); root->left = new Node(2); root->right = new Node(5); root->left->left = new Node(1); root->left->right = new Node(4); // calling the function to check BST. if (isValidBST(root)) cout << "Is BST"; else cout << "Not a BST"; return 0; } // T.C. O(N) // S.C. O(N) ---> for Auxillary stack ================================================ FILE: Trees/Binary Search Trees/Validate_BST.java ================================================ ////// Explanation: // To validate a Binary Search Tree (BST), we need to ensure that the values of nodes in the left subtree of any node are less than the value of the node, and the values of nodes in the right subtree are greater than the value of the node. // Additionally, the left and right subtrees themselves must also be valid BSTs. ////// Code: class validateBST { public static class Node { public int data; public Node left, right; public Node(int data) { this.data = data; left = null; right = null; } }; static Node prev; static Boolean isBST(Node root) { // traverse the tree in inorder fashion and // keep track of prev node if (root == null) return true; //If Left Node is not BST then return false if (!isBST(root.left)) return false; //Check if current node is less than its parent node if (prev != null && root.data <= prev.data) return false; prev = root; return isBST(root.right); } public static void main(String[] args) { //Creating BST Node root = new Node(1); root.left = new Node(-2); root.right = new Node(3); root.left.left = new Node(-4); root.left.right = new Node(-1); root.right.left = new Node(2); root.right.right = new Node(7); // Function call if (isBST(root)) System.out.println("Is Valid BST"); else System.out.println("Not a Valid BST"); } } ////// Complexity: // Time Complexity: O(n) where n is number of nodes // Space Complexity: O(1) ================================================ FILE: Trees/Binary Search Trees/Validate_BST.js ================================================ /* Write a function that takes in a potentially invalid Binary Search Tree (BST) and returns a boolean representing whether the BST is valid. Sample Input : 10 / \ 5 15 / \ / \ 2 5 13 22 / \ 1 14 Output : True Explanation: This code defines a Binary Search Tree (BST) struct with an integer value and left and right nodes that can point to other BST nodes. The struct also has a method called ValidateBst() that returns a boolean indicating whether the tree is a valid BST or not. The BST struct has another method called validateBST() that is used by ValidateBst() to check whether the tree is a valid BST or not. The validateBST() method takes in two arguments, min and max, which represent the minimum and maximum values that the current node's value can take in order to be a valid BST. The validateBST() method first checks whether the current node's value is within the valid range determined by the min and max arguments. If not, the method returns false, indicating that the tree is not a valid BST. If the current node's value is within the valid range, the method then recursively calls itself on the left and right child nodes to check whether their values are within their valid ranges. The valid range for the left child node is defined by the minimum value and the parent node's value, while the valid range for the right child node is defined by the parent node's value and the maximum value. If all of the nodes in the tree satisfy the BST property, the method returns true, indicating that the tree is a valid BST. O(n) time | O(d) space - where n is the number of nodes in the BST and d is the depth (height) of the BST */ class Node { constructor(data) { this.data = data; this.left = null; this.right = null; } } // isBst is a method of BST that checks if the binary search tree is valid function isBST(root) { let prev = null; function inorderTraversal(node) { // Base case if (node === null) { return true; } // recursively check the left subtree, making sure all values are less than the current node's value if (!inorderTraversal(node.left)) { return false; } // if the current node's value is outside the allowed range, then the tree is invalid if (prev !== null && node.data <= prev.data) { return false; } prev = node; return inorderTraversal(node.right); } return inorderTraversal(root); } // Create the tree const root = new Node(100); root.left = new Node(-2); root.right = new Node(3); root.left.left = new Node(-4); root.left.right = new Node(-1); root.right.left = new Node(2); root.right.right = new Node(7); // Function call if (isBST(root)) { console.log("Is Valid BST"); } else { console.log("Not a Valid BST"); } ////// Complexity: // Time Complexity: O(n) where n is number of nodes // Space Complexity: O(1) ================================================ FILE: Trees/Binary Search Trees/Validate_BST.py ================================================ ''' Write a function that takes in a potentially invalid Binary Search Tree (BST) and returns a boolean representing whether the BST is valid. Sample Input : 10 / \ 5 15 / \ / \ 2 5 13 22 / \ 1 14 Output : True Explanation: This code defines a Binary Search Tree (BST) struct with an integer value and left and right nodes that can point to other BST nodes. The struct also has a method called ValidateBst() that returns a boolean indicating whether the tree is a valid BST or not. The BST struct has another method called validateBST() that is used by ValidateBst() to check whether the tree is a valid BST or not. The validateBST() method takes in two arguments, min and max, which represent the minimum and maximum values that the current node's value can take in order to be a valid BST. The validateBST() method first checks whether the current node's value is within the valid range determined by the min and max arguments. If not, the method returns false, indicating that the tree is not a valid BST. If the current node's value is within the valid range, the method then recursively calls itself on the left and right child nodes to check whether their values are within their valid ranges. The valid range for the left child node is defined by the minimum value and the parent node's value, while the valid range for the right child node is defined by the parent node's value and the maximum value. If all of the nodes in the tree satisfy the BST property, the method returns true, indicating that the tree is a valid BST. O(n) time | O(d) space - where n is the number of nodes in the BST and d is the depth (height) of the BST ''' class Node: def __init__(self, data): self.data = data self.left = None self.right = None # ValidateBst is a method of BST that checks if the binary search tree is valid def is_bst(root): global prev prev = None #validateBST is a recursive helper function that checks if the binary search tree is valid #min is the minimum value that a node in the subtree rooted at this node can have #max is the maximum value that a node in the subtree rooted at this node can have def inorder_traversal(node): global prev # if the current node's value is outside the allowed range, then the tree is invalid if node is None: return True # If left subtree is not BST return false if not inorder_traversal(node.left): return False # recursively check the left subtree, making sure all values are less than the current node's value if prev is not None and node.data <= prev.data: return False prev = node return inorder_traversal(node.right) return inorder_traversal(root) # Create the tree root = Node(1) root.left = Node(-2) root.right = Node(3) root.left.left = Node(-4) root.left.right = Node(-1) root.right.left = Node(2) root.right.right = Node(7) # Function call if is_bst(root): print("Is Valid BST") else: print("Not a Valid BST") ## Complexity: # Time Complexity: O(n) where n is number of nodes # Space Complexity: O(1) ================================================ FILE: Trees/Binary Search Trees/bst.go ================================================ /* Binary Search tree implementation In computer science, a binary search tree (BST), also called an ordered or sorted binary tree, is a rooted binary tree data structure with the key of each internal node being greater than all the keys in the respective node's left subtree and less than the ones in its right subtree. The time complexity of operations on the binary search tree is directly proportional to the height of the tree. The complexity analysis of BST shows that, on average, the insert, delete and search takes O(log n) fot n nodes. In the worst case, they degrade to that of a singly linked list O(n). Source(https://en.wikipedia.org/wiki/Binary_search_tree) Opeartions on BST: 1 Find/ Find Minimum / Find Maximum element in binary search trees 2 Inserting an element in binary search trees 3 Deleting an element from binary search trees */ package main import ( "fmt" "math" "math/rand" ) type BSTNode struct { left *BSTNode data int right *BSTNode } // ConstructBST returns a new, random binary tree func ConstructBST(n, k int) *BSTNode { var root *BSTNode for _, v := range rand.Perm(n) { root = Insert(root, (1 + v) * k) } return root } // Insert, inserts an element in binary tree func Insert(root *BSTNode, data int) *BSTNode { if root == nil { return &BSTNode{nil, data, nil} } if data < root.data { root.left = Insert(root.left, data) return root } root.right = Insert(root.right, data) return root } // Time Complexity: O(n). Space Complexity: O(n). // Approach: start with root and keep moving left or right recursively // if data we are searching is same as current node's data then return current node // if data is less than node's data then resurse left, else recurse right // if no data then it will return nil func SearchElementRecursive(root *BSTNode, data int) *BSTNode { if root == nil { return root } if data < root.data { return SearchElementRecursive(root.left, data) } else if data > root.data { return SearchElementRecursive(root.right, data) } return root } // Time Complexity: O(n). Space Complexity: O(1). // Approach: start with root and keep moving left or right // if data we are searching is same as current node's data then return current node // if data is less than node's data then resurse left, else recurse right // if no data then it will return nil func SearchElementNonRecursive(root *BSTNode, data int) *BSTNode { if root == nil { return root } for root != nil { if data == root.data { return root } else if data < root.data { root = root.left } else { root = root.right } } return nil } // FindMin: finds min element in BST, // Approach: start with root and keep moving left // Min element is the left most node in BST // Time Complexity: O(n). Space Complexity: O(n). func FindMinRecursive(root *BSTNode) *BSTNode { if root == nil { return nil } else if root.left == nil { return root } else { return FindMinRecursive(root.left) } } // FindMIn: finds max element in BST, // Approach: start with root and keep moving right // Max element is the right most node in BST // Time Complexity: O(n). Space Complexity: O(1). func FindMinNonRecursive(root *BSTNode) *BSTNode { if root == nil { return root } for root.left != nil { root = root.left } return root } // FindMax: finds min element in BST, // Approach: start with root and keep moving left // min element is the left most node in BST // Time Complexity: O(n). Space Complexity: O(n). func FindMaxRecursive(root *BSTNode) *BSTNode { if root == nil { return nil } if root.right == nil { return root } return FindMaxRecursive(root.right) } // FindMIn: finds max element in BST, // Approach: start with root and keep moving right // Max element is the right most node in BST // Time Complexity: O(n). Space Complexity: O(1). func FindMaxNonRecursive(root *BSTNode) *BSTNode { if root == nil { return nil } for root.right != nil { root = root.right } return root } func DeleteMin(root *BSTNode) *BSTNode{ if root.left == nil { return root.right } root.left = DeleteMin(root.left) return root } // DeleteFromBST deleted given value from bst and doesn't break the node // Time Complexity: O(n). Space Complexity: O(n). func DeleteFromBST(root *BSTNode, data int) *BSTNode { if root == nil { return nil } // traverse left or right recursively until you find data if data < root.data { DeleteFromBST(root.left, data) } else if data > root.data { DeleteFromBST(root.right, data) } else { // straight forward conddition no child set root as nil and return root if root.right == nil && root.left == nil{ fmt.Println("hit") root = nil return root } // if left is nil and right has children set root as root.right if root.left == nil && root.right != nil { temp := root.right root = temp return root } // if right is nil and left has children set root as root.left if root.right == nil && root.left != nil { fmt.Println(" boom") temp := root.left root = temp return root } // if both left and right have children // find min in the right sub tree, // set root.data as minimum value you found you found in right sub tree // call DeleteFromBST and delete min value temp := FindMinNonRecursive(root.right) DeleteMin(root.right) root.data = temp.data root.right = DeleteFromBST(root.right, temp.data) } return root } // Pre-order traversal // Preorder traversal is defined as follows: // 1 Visit the root. // 2 Traverse the left subtree in Preorder. // 3 Traverse the right subtree in Preorder. // Time Complexity: O(n). Space Complexity: O(n). // The nodes of tree would be visited in the order: 1 2 4 5 3 6 7 func PreOrder(root *BSTNode) { if root == nil { return } fmt.Print(root.data) PreOrder(root.left) PreOrder(root.right) } func InOrder(root *BSTNode) { if root == nil { return } InOrder(root.left) fmt.Print(root.data) InOrder(root.right) } // IsBST: checks whether a given tree is a valid BST or not // Time Complexity: O(n2). Space Complexity: O(n). func IsBST(root *BSTNode) bool { if root == nil { return true } // if max on left tree is greater then root then return false max := FindMaxRecursive(root.left) if root.left != nil && max.data > root.data { return false } // if min on right tree is less then root then return false min := FindMinNonRecursive(root.right) if root.right != nil && min.data < root.data { return false } // recursively check if left or right is not a BST if !IsBST(root.left) || !IsBST(root.right) { return false } return true } // Time Complexity: O(n). Space Complexity: O(n). func IsBSTOptimal(root *BSTNode, min, max int) bool { if root == nil { return true } return ( root.data > min && root.data < max && IsBSTOptimal(root.left, min, root.data) && IsBSTOptimal(root.right, root.data, max)) } // IsBSTInorder: Using Inorder traversal we can solve this problem // Inorder traversal gives us sorted values, while traversing the BST in inorder, // at each node check the condition that its key value should be greater than // the key value of its previous visited node, initialize prev with minimum integer value // Time Complexity: O(n). Space Complexity: O(n). func IsBSTInorder(root *BSTNode, prev *int) bool { if root == nil { return true } if !IsBSTInorder(root.left, prev) { return false } // compare root value with prev visited value if root.data < *prev { return false } // set prev value as root's value *prev = root.data return IsBSTInorder(root.right, prev) } // Helper created balanced binary search tree // also it ensures height is balanced // Approach : Using Divide and Conquer strategy // It is similar to binary seaarch algorithm func Helper(Arr []int, low int, high int) *BSTNode { if low > high { return nil } // middle element will form the root of BST mid := low + (high - low) / 2 // create new node (allocate memory) node := new(BSTNode) // assign data to newly created node node.data = Arr[mid] // elements left from mid will form left sub tree node.left = Helper(Arr, low, mid - 1) // elements right from mid will form right sub tree node.right = Helper(Arr, mid + 1, high) return node } // Time Complexity: O(n). Space Complexity: O(n). func ConvertSortedArrayToBST(Arr []int) *BSTNode { if Arr == nil { return nil } return Helper(Arr, 0, len(Arr)-1) } // Time Complexity: O(n). Space Complexity: O(1). func kthSmallest(root *BSTNode, k int) *BSTNode { counter := 0 return helperKthSmallest(root, k, &counter) } // Helper method to find kth smallest element // Approach: Inorder traversal gives us sorted list , so we can determine kth smallest // element in tree easily func helperKthSmallest(root *BSTNode, k int, counter *int) *BSTNode { if root == nil { return nil } left := helperKthSmallest(root.left, k, counter) if left != nil { return left } // while traversing the tree keep track of the number of elements visited *counter += 1 if *counter == k { return root } return helperKthSmallest(root.right, k, counter) } // FloorInBST gives floor value of the supplied key in BST // Floor of the key is the largest key in the BST // less than tor equal to the key // Time Complexity: O(n). Space Complexity: O(n). func FloorInBST(root *BSTNode, key int) *BSTNode { if root == nil { return root } if key > root.data { r := FloorInBST(root.right, key) if r == nil { return root } else { return r } } else if key < root.data { return FloorInBST(root.left, key) } else { return root } } // CeilInBST gives ceil value of the supplied key in BST // Ceil of the key is the smallest key in the BST // greater than tor equal to the key // Time Complexity: O(n). Space Complexity: O(n). func CeilInBST(root *BSTNode, key int) *BSTNode { if root == nil { return root } if root.data == key { return root } else if root.data < key { return CeilInBST(root.right, key) } else { l := CeilInBST(root.left, key) if l != nil { return l } } return root } // RangePrintBST prints the value in trees which lies in range from start and end // Approach: Traverse in inorder, if key lie within range then print them // Time Complexity: O(n). Space Complexity: O(n). func RangePrintBST(root *BSTNode, start int, end int) { if root == nil { return } // look in left side if root.data >= start { RangePrintBST(root.left, start, end) } // if value lies within supplied range print them if root.data >= start && root.data <= end { fmt.Printf("%v ", root.data) } // look in right side if root.data <= end { RangePrintBST(root.right, start, end) } } // RangePrintQueueBST prints the value in trees which lies in range // from start and end using queue (level order traversal) // Approach: while adding the elements to queue check for range // Time Complexity: O(n). Space Complexity: O(n). func RangePrintQueueBST(root *BSTNode, start, end int) { if root == nil { return } var result [][]int queue := []*BSTNode{root} for len(queue) > 0 { qlen := len(queue) var level []int for i := 0; i < qlen; i++ { node := queue[0] level = append(level, node.data) queue = queue[1:] // if data lies within range then print data if node.data >= start && node.data <= end { fmt.Printf("%v", node.data) } // append left node to queue if node.left != nil && node.data >= start { queue = append(queue, node.left) } // append right node to queue if node.right != nil && node.data <= end { queue = append(queue, node.right) } } result = append(result, level) } } // CountTrees: returns how many structurally unique BST's are possible // Approach: Consider each value to be root, recursively find the size of the left and right // subtrees. Let p be the root in BST with n nodes from 1 to n // The left subtree has p - 1 nodes, right tree has n - p nodes. func CountTrees(n int) int { if n <= 1 { return 1 } else { sum := 0 // iterate through all values that could be the root for root := 1; root <= n; root++ { left := CountTrees(root - 1) right := CountTrees(n - root) // number of possible trees withcurr root = left * right sum += left * right } return sum } } func main() { tree := ConstructBST(10, 1) fmt.Println(tree) fmt.Println(SearchElementRecursive(tree, 5)) fmt.Println(SearchElementNonRecursive(tree, 5)) fmt.Println(FindMinRecursive(tree)) fmt.Println(FindMinNonRecursive(tree)) fmt.Println(FindMaxRecursive(tree)) fmt.Println(FindMaxNonRecursive(tree)) PreOrder(tree) tree = DeleteFromBST(tree, 6) fmt.Println() PreOrder(tree) //fmt.Println(SearchElementRecursive(tree, 7)) fmt.Print(IsBST(tree)) fmt.Print(IsBSTOptimal(tree, math.MinInt32, math.MaxInt32)) var prev = math.MinInt32 fmt.Println(IsBSTInorder(tree, &prev)) arr := []int {1, 2, 3, 4, 5 ,6} node := ConvertSortedArrayToBST(arr) InOrder(node) fmt.Println(kthSmallest(tree, 3)) RangePrintBST(tree, 3, 7) RangePrintQueueBST(tree, 3, 7) } ================================================ FILE: Trees/Binary Search Trees/find_closest_value.cpp ================================================ /* Write a function that takes in a Binary Search Tree (BST) and a target integer value and returns the closest value to that target value contained in the BST. Sample Input : 12 10 / \ 5 15 / \ / \ 2 5 13 22 / \ 1 14 Output : 13 Explanation: The code defines a BST (Binary Search Tree) class with member functions to find the closest value to a given target value. The findClosestValue function is the public interface that initializes the closest value with the root value and calls the helper function. The findClosestValueHelper function recursively traverses the tree, updating the closest value based on the absolute difference between the target and the current node value. It then continues the search in the appropriate subtree based on the comparison with the target value. The absDiff function calculates the absolute difference between two integers. Time and Space complexity: Average: O(log(n)) time | O(1) space - where n is the number of nodes in the BST Worst: O(n) time | O(1) space - where n is the number of nodes in the BST */ #include class BST { public: int value; BST* left; BST* right; BST(int val) { value = val; left = nullptr; right = nullptr; } int findClosestValue(int target) { // Call the helper function with the initial closest value as the root value return findClosestValueHelper(target, value); } private: int findClosestValueHelper(int target, int closest) { // Compare the absolute difference between the target and the current closest value // with the absolute difference between the target and the current node value if (std::abs(target - closest) > std::abs(target - value)) { closest = value; } // Look for the target in the left subtree if the target is smaller than the current node value if (target < value && left != nullptr) { return left->findClosestValueHelper(target, closest); } // Look for the target in the right subtree if the target is greater than the current node value else if (target > value && right != nullptr) { return right->findClosestValueHelper(target, closest); } return closest; } }; int main() { // Create a BST instance BST* bst = new BST(10); bst->left = new BST(5); bst->right = new BST(15); bst->left->left = new BST(2); bst->left->right = new BST(5); bst->right->left = new BST(13); bst->right->right = new BST(22); bst->left->left->left = new BST(1); bst->right->left->right = new BST(14); // Find the closest value to the target int target = 12; int closestValue = bst->findClosestValue(target); // Print the result std::cout << "The closest value to " << target << " is " << closestValue << std::endl; return 0; } ================================================ FILE: Trees/Binary Search Trees/find_closest_value.go ================================================ /* Write a function that takes in a Binary Search Tree (BST) and a target integer value and returns the closest value to that target value contained in the BST. Sample Input : 12 10 / \ 5 15 / \ / \ 2 5 13 22 / \ 1 14 Output : 13 Explanation: The code defines a BST (Binary Search Tree) class with member functions to find the closest value to a given target value. The findClosestValue function is the public interface that initializes the closest value with the root value and calls the helper function. The findClosestValueHelper function recursively traverses the tree, updating the closest value based on the absolute difference between the target and the current node value. It then continues the search in the appropriate subtree based on the comparison with the target value. The absDiff function calculates the absolute difference between two integers. Time and Space complexity: Average: O(log(n)) time | O(1) space - where n is the number of nodes in the BST Worst: O(n) time | O(1) space - where n is the number of nodes in the BST */ package main type BST struct { Value int Left *BST Right *BST } func (tree *BST) FindClosestValue(target int) int { // call helper function return tree.findClosestValue(target, tree.Value) } func (tree *BST) findClosestValue(target, closest int) int { if absDiff(target, closest) > absDiff(target, tree.Value) { closest = tree.Value } // look for target in left sub tree if target < tree.Value && tree.Left != nil { return tree.Left.findClosestValue(target, closest) } else if target > tree.Value && tree.Right != nil { // // look for target in right sub tree return tree.Right.findClosestValue(target, closest) } return closest } func absDiff(a, b int) int { if a > b { return a - b } return b - a } ================================================ FILE: Trees/Binary Search Trees/find_closest_value.js ================================================ /* Write a function that takes in a Binary Search Tree (BST) and a target integer value and returns the closest value to that target value contained in the BST. Sample Input : 12 10 / \ 5 15 / \ / \ 2 5 13 22 / \ 1 14 Output : 13 Explanation: The code defines a BST (Binary Search Tree) class with member functions to find the closest value to a given target value. The findClosestValue function is the public interface that initializes the closest value with the root value and calls the helper function. The findClosestValueHelper function recursively traverses the tree, updating the closest value based on the absolute difference between the target and the current node value. It then continues the search in the appropriate subtree based on the comparison with the target value. The absDiff function calculates the absolute difference between two integers. Time and Space complexity: Average: O(log(n)) time | O(1) space - where n is the number of nodes in the BST Worst: O(n) time | O(1) space - where n is the number of nodes in the BST */ class BST { constructor(value) { this.value = value; this.left = null; this.right = null; } findClosestValue(target) { // Call the helper function with the initial closest value as the root value return this._findClosestValueHelper(target, this.value); } _findClosestValueHelper(target, closest) { // Compare the absolute difference between the target and the current closest value // with the absolute difference between the target and the current node value if (Math.abs(target - closest) > Math.abs(target - this.value)) { closest = this.value; } // Look for the target in the left subtree if the target is smaller than the current node value if (target < this.value && this.left) { return this.left._findClosestValueHelper(target, closest); } // Look for the target in the right subtree if the target is greater than the current node value else if (target > this.value && this.right) { return this.right._findClosestValueHelper(target, closest); } return closest; } } // Create a BST instance const bst = new BST(10); bst.left = new BST(5); bst.right = new BST(15); bst.left.left = new BST(2); bst.left.right = new BST(5); bst.right.left = new BST(13); bst.right.right = new BST(22); bst.left.left.left = new BST(1); bst.right.left.right = new BST(14); // Find the closest value to the target const target = 12; const closestValue = bst.findClosestValue(target); // Print the result console.log(`The closest value to ${target} is ${closestValue}`); ================================================ FILE: Trees/Binary Search Trees/find_closest_value.py ================================================ ''' Write a function that takes in a Binary Search Tree (BST) and a target integer value and returns the closest value to that target value contained in the BST. Sample Input : 12 10 / \ 5 15 / \ / \ 2 5 13 22 / \ 1 14 Output : 13 Explanation: The code defines a BST (Binary Search Tree) class with member functions to find the closest value to a given target value. The findClosestValue function is the public interface that initializes the closest value with the root value and calls the helper function. The findClosestValueHelper function recursively traverses the tree, updating the closest value based on the absolute difference between the target and the current node value. It then continues the search in the appropriate subtree based on the comparison with the target value. The absDiff function calculates the absolute difference between two integers. Time and Space complexity: Average: O(log(n)) time | O(1) space - where n is the number of nodes in the BST Worst: O(n) time | O(1) space - where n is the number of nodes in the BST ''' class BST: def __init__(self, value): self.value = value self.left = None self.right = None def find_closest_value(self, target): # Call the helper function with the initial closest value as the root value return self._find_closest_value_helper(target, self.value) def _find_closest_value_helper(self, target, closest): # Compare the absolute difference between the target and the current closest value # with the absolute difference between the target and the current node value if abs(target - closest) > abs(target - self.value): closest = self.value # Look for the target in the left subtree if the target is smaller than the current node value if target < self.value and self.left: return self.left._find_closest_value_helper(target, closest) # Look for the target in the right subtree if the target is greater than the current node value elif target > self.value and self.right: return self.right._find_closest_value_helper(target, closest) return closest # Create a BST instance bst = BST(10) bst.left = BST(5) bst.right = BST(15) bst.left.left = BST(2) bst.left.right = BST(5) bst.right.left = BST(13) bst.right.right = BST(22) bst.left.left.left = BST(1) bst.right.left.right = BST(14) # Find the closest value to the target target = 12 closest_value = bst.find_closest_value(target) # Print the result print(f"The closest value to {target} is {closest_value}") ================================================ FILE: Trees/Binary Search Trees/insert_into_bst.cpp ================================================ // Binary Search Tree : Insert into Binary Search Tree // Program Author : Abhisek Kumar Gupta /* Input : 7 3 9 2 4 8 10 Output : 7 / \ 3 9 / \ / \ 2 4 8 10 Inorder : 2 3 4 7 8 9 10 Preorder : 7 3 2 4 9 8 10 Postorder : 2 4 3 8 10 9 7 */ #include using namespace std; class Node{ public: int data; Node* left; Node* right; Node(int x){ data = x; left = NULL; right = NULL; } }; void bfs(Node* root){ queue q; q.push(root); q.push(NULL); while(!q.empty()){ Node* element = q.front(); if(element == NULL){ cout << "\n"; q.pop(); if(!q.empty()){ q.push(NULL); } } else{ cout << element->data << "->"; q.pop(); if(element->left != NULL){ q.push(element->left); } if(element->right != NULL){ q.push(element->right); } } } return; } Node* insert_into_binary_search_tree(Node* root, int data){ if(root == NULL){ return new Node(data); } if(data <= root->data){ root->left = insert_into_binary_search_tree(root->left, data); } else{ root->right = insert_into_binary_search_tree(root->right, data); } return root; } Node* build_binary_search_tree(){ int data; cin >> data; Node* root = NULL; while(data != -1){ root = insert_into_binary_search_tree(root, data); cin >> data; } return root; } void inorder(Node*root){ if(root == NULL) return; inorder(root->left); cout << root->data << "->"; inorder(root->right); } void pre_order(Node*root){ if(root == NULL) return; cout << root->data << "->"; pre_order(root->left); pre_order(root->right); } void post_order(Node*root){ if(root == NULL) return; post_order(root->left); post_order(root->right); cout << root->data << "->"; } int main(){ Node* root = build_binary_search_tree(); cout << endl << "Inorder Traversal"; inorder(root); cout << endl << "Preorder Traversal"; pre_order(root); cout << endl << "Postorder Traversal"; post_order(root); cout << endl; bfs(root); } ================================================ FILE: Trees/Binary Search Trees/kth_largest.go ================================================ // Kth largest in BST package main // Approach 1: Using Brute Force // Since Inorder traversal of BSt always comes in sorted order, we can directly access kth from end type BST struct { Value int Left *BST Right *BST } func FindKthLargestValueInBst(tree *BST, k int) int { sortedValues := make([]int, 0) inOrderTraverse(tree, &sortedValues) return sortedValues[len(sortedValues) - k] } func inOrderTraverse(tree *BST, sortedValues *[]int) { if tree.Left != nil { inOrderTraverse(tree.Left, sortedValues) } *sortedValues = append(*sortedValues, tree.Value) if tree.Right != nil { inOrderTraverse(tree.Right, sortedValues) } } // Approach 2: Using Reverse InOrder Traversal /* The given code implements a function to find the Kth largest value in a Binary Search Tree (BST). Here's how it works: 1. The `BST` struct represents a node in the BST, with a `Value` field and pointers to the left and right child nodes. 2. The `treeInfo` struct is a helper struct used to store information during the traversal of the BST. It contains two fields: `numberOfNodesVisited` to keep track of the number of nodes visited, and `latestVisitedNodeValue` to store the value of the latest visited node. 3. The `FindKthLargestValueInBst` function takes the root of the BST and the value of K as input and returns the Kth largest value in the BST. 4. Inside the `FindKthLargestValueInBst` function, a `treeInfo` instance is created to track the traversal progress. 5. The `reverseInOrderTraverse` function is called to perform a reverse in-order traversal of the BST. It starts from the right child, then visits the current node, and finally visits the left child. 6. In the `reverseInOrderTraverse` function, the base case is checked: if the current node is `nil` or the desired Kth largest value has already been found (i.e., `numberOfNodesVisited` is equal to K), the function returns. 7. The `reverseInOrderTraverse` function is recursively called on the right child node, which traverses the BST in descending order. 8. After the right subtree is traversed, the function checks if `numberOfNodesVisited` is still less than K. If so, it increments `numberOfNodesVisited`, assigns the current node's value to `latestVisitedNodeValue`, and recursively calls the function on the left child node. 9. The reverse in-order traversal ensures that the largest values are visited first, so when `numberOfNodesVisited` reaches K, the value stored in `latestVisitedNodeValue` will be the Kth largest value in the BST. 10. Finally, the Kth largest value is returned by the `FindKthLargestValueInBst` function. The code efficiently finds the Kth largest value in a BST by performing a reverse in-order traversal, keeping track of the number of visited nodes and the latest visited node value. The time complexity of the algorithm is O(H + K), where H is the height of the BST and K is the input value. The space complexity of the code is O(h), where h is the height of the BST. During the execution of the FindKthLargestValueInBst function, the space usage is primarily determined by the recursive calls to the reverseInOrderTraverse function. Each recursive call adds a new frame to the call stack, consuming additional memory. In the worst case scenario, where the BST is skewed and has a height of h (resembling a linked list), the space complexity will be O(h). This is because the function will make h recursive calls before reaching the base case. However, in a balanced BST where the height is log(N), where N is the number of nodes in the tree, the space complexity will be O(log(N)). It's important to note that the space complexity only accounts for the additional space used by the recursive calls and the call stack. The space required to store the BST itself is not considered in this analysis as it is part of the input data. */ // Helper struct to store traversal information type treeInfo struct { numberOfNodesVisited int latestVisitedNodeValue int } // FindKthLargestValueInBst finds the Kth largest value in the BST func FindKthLargestValueInBst2(tree *BST, k int) int { // Create treeInfo to track traversal progress treeInfo := treeInfo{0, -1} reverseInOrderTraverse(tree, k, &treeInfo) return treeInfo.latestVisitedNodeValue } // reverseInOrderTraverse performs reverse in-order traversal of the BST func reverseInOrderTraverse(tree *BST, k int, treeInfo *treeInfo) { // Base case: if current node is nil or Kth largest value found, return if tree == nil || treeInfo.numberOfNodesVisited == k { return } // Traverse the right subtree reverseInOrderTraverse(tree.Right, k, treeInfo) // Check if Kth largest value has been found if treeInfo.numberOfNodesVisited < k { // Increment the count of visited nodes and update the latest visited node value treeInfo.numberOfNodesVisited++ treeInfo.latestVisitedNodeValue = tree.Value // Traverse the left subtree reverseInOrderTraverse(tree.Left, k, treeInfo) } } ================================================ FILE: Trees/Binary Search Trees/min_height_BST.cpp ================================================ /* Write a function that takes in a non-empty sorted array of distinct integers, constructs a BST from the integers, and returns the root of the BST. The function should minimize the height of the BST. Smple Input : [1, 2, 5, 7, 10, 13, 14, 15, 22] Output: 10 / \ 2 14 / \ / \ 1 5 13 15 \ \ 7 22 Explanation: The given code is used to construct a minimum height binary search tree (BST) from a sorted array. The goal is to create a balanced BST where the difference in height between the left and right subtrees is minimized. The MinHeightBST function serves as a wrapper function that initializes the construction process by calling the constructMinHeightBST helper function. The constructMinHeightBST function recursively constructs the minimum height BST. It takes the sorted array, a partially constructed bst, and the start and end indices that define the range of elements from the array currently being considered. The function follows these steps: Base Case: If end < start, it means there are no elements to consider in the current range, so it returns nil indicating an empty subtree. Calculate the mid index as the midpoint of the current range (start and end). Get the value from the array at the mid index. If the bst is nil, indicating that there are no values in the BST yet, create a new BST node with the value. Otherwise, insert the value into the existing bst using the Insert method. Recursively call constructMinHeightBST for the left half of the array by passing start and mid-1 as the new range. This constructs the left subtree. Recursively call constructMinHeightBST for the right half of the array by passing mid+1 and end as the new range. This constructs the right subtree. Finally, return the bst which represents the constructed minimum height BST. The BST struct represents a node in the binary search tree. It has a Value field to store the node's value and Left and Right fields to point to the left and right child nodes, respectively. The Insert method is used to insert a new value into the BST. It recursively finds the appropriate position to insert the value based on the comparison with the current node's value and updates the left or right child accordingly. Overall, this code efficiently constructs a minimum height BST from a sorted array by recursively dividing the array and inserting the mid-value into the BST, ensuring a balanced structure. Time Complexity: The MinHeightBST function calls the constructMinHeightBST helper function, which performs a binary search-like operation to divide the array and construct the BST. This process is recursive and takes O(log n) time, where n is the number of elements in the array. The bst.Insert(value) operation in constructMinHeightBST has a time complexity of O(log n) in the worst case, as it involves traversing the height of the BST to find the appropriate position to insert the value. Since each element in the array is inserted into the BST once, the overall time complexity is O(n log n), where n is the number of elements in the array. Space Complexity: The space complexity is determined by the stack space used during recursive calls and the space required to store the BST. The recursive calls in constructMinHeightBST consume additional stack space proportional to the height of the tree. In the worst case scenario, where the BST is skewed and its height is equal to the number of elements (n), the space complexity becomes O(n). The space required to store the BST is also O(n) in the worst case since each element in the array is inserted into the BST. Therefore, the overall space complexity is O(n) due to the stack space and the BST storage. In summary, the time complexity is O(n log n) and the space complexity is O(n). */ #include #include struct TreeNode { int value; TreeNode* left; TreeNode* right; TreeNode(int x) : value(x), left(nullptr), right(nullptr) {} }; TreeNode* minHeightBST(std::vector& array) { // Call helper method with start index, end index, and a nullptr root node return constructMinHeightBST(array, nullptr, 0, array.size() - 1); } TreeNode* constructMinHeightBST(std::vector& array, TreeNode* bst, int start, int end) { // Base case if (end < start) { return nullptr; } int mid = (start + end) / 2; int value = array[mid]; // If the BST is empty, create a new root node if (bst == nullptr) { bst = new TreeNode(value); } else { // Insert the value into the existing BST bst->insert(value); } // Recursively construct the left and right subtrees bst->left = constructMinHeightBST(array, bst->left, start, mid - 1); bst->right = constructMinHeightBST(array, bst->right, mid + 1, end); return bst; } class BST { public: int value; BST* left; BST* right; BST(int x) : value(x), left(nullptr), right(nullptr) {} void insert(int value) { if (value < this->value) { if (left == nullptr) { left = new BST(value); } else { left->insert(value); } } else { if (right == nullptr) { right = new BST(value); } else { right->insert(value); } } } }; int main() { std::vector array = {1, 2, 3, 4, 5, 6, 7}; TreeNode* root = minHeightBST(array); // Printing the BST is left as an exercise // Clean up memory deleteTree(root); return 0; } void deleteTree(TreeNode* root) { if (root == nullptr) { return; } deleteTree(root->left); deleteTree(root->right); delete root; } ================================================ FILE: Trees/Binary Search Trees/min_height_BST.go ================================================ /* Write a function that takes in a non-empty sorted array of distinct integers, constructs a BST from the integers, and returns the root of the BST. The function should minimize the height of the BST. Smple Input : [1, 2, 5, 7, 10, 13, 14, 15, 22] Output: 10 / \ 2 14 / \ / \ 1 5 13 15 \ \ 7 22 Explanation: The given code is used to construct a minimum height binary search tree (BST) from a sorted array. The goal is to create a balanced BST where the difference in height between the left and right subtrees is minimized. The MinHeightBST function serves as a wrapper function that initializes the construction process by calling the constructMinHeightBST helper function. The constructMinHeightBST function recursively constructs the minimum height BST. It takes the sorted array, a partially constructed bst, and the start and end indices that define the range of elements from the array currently being considered. The function follows these steps: Base Case: If end < start, it means there are no elements to consider in the current range, so it returns nil indicating an empty subtree. Calculate the mid index as the midpoint of the current range (start and end). Get the value from the array at the mid index. If the bst is nil, indicating that there are no values in the BST yet, create a new BST node with the value. Otherwise, insert the value into the existing bst using the Insert method. Recursively call constructMinHeightBST for the left half of the array by passing start and mid-1 as the new range. This constructs the left subtree. Recursively call constructMinHeightBST for the right half of the array by passing mid+1 and end as the new range. This constructs the right subtree. Finally, return the bst which represents the constructed minimum height BST. The BST struct represents a node in the binary search tree. It has a Value field to store the node's value and Left and Right fields to point to the left and right child nodes, respectively. The Insert method is used to insert a new value into the BST. It recursively finds the appropriate position to insert the value based on the comparison with the current node's value and updates the left or right child accordingly. Overall, this code efficiently constructs a minimum height BST from a sorted array by recursively dividing the array and inserting the mid-value into the BST, ensuring a balanced structure. Time Complexity: The MinHeightBST function calls the constructMinHeightBST helper function, which performs a binary search-like operation to divide the array and construct the BST. This process is recursive and takes O(log n) time, where n is the number of elements in the array. The bst.Insert(value) operation in constructMinHeightBST has a time complexity of O(log n) in the worst case, as it involves traversing the height of the BST to find the appropriate position to insert the value. Since each element in the array is inserted into the BST once, the overall time complexity is O(n log n), where n is the number of elements in the array. Space Complexity: The space complexity is determined by the stack space used during recursive calls and the space required to store the BST. The recursive calls in constructMinHeightBST consume additional stack space proportional to the height of the tree. In the worst case scenario, where the BST is skewed and its height is equal to the number of elements (n), the space complexity becomes O(n). The space required to store the BST is also O(n) in the worst case since each element in the array is inserted into the BST. Therefore, the overall space complexity is O(n) due to the stack space and the BST storage. In summary, the time complexity is O(n log n) and the space complexity is O(n). */ package main func MinHeightBST(array []int) *BST { // call helper method with start index, end index array and nil node return constructMinHeightBST(array, nil, 0, len(array)-1) } func constructMinHeightBST(array []int, bst *BST, start int, end int) *BST { // base case if end < start { return nil } mid := (start + end) / 2 value := array[mid] // there are no value in bst if bst == nil { bst = &BST{Value: value} } else { bst.Insert(value) } constructMinHeightBST(array, bst, start, mid-1) constructMinHeightBST(array, bst, mid+1, end) return bst } type BST struct { Value int Left *BST Right *BST } func (tree *BST) Insert(value int) *BST { if value < tree.Value { if tree.Left == nil { tree.Left = &BST{Value: value} } else { tree.Left.Insert(value) } } else { if tree.Right == nil { tree.Right = &BST{Value: value} } else { tree.Right.Insert(value) } } return tree } ================================================ FILE: Trees/Binary Search Trees/min_height_BST.java ================================================ /* Write a function that takes in a non-empty sorted array of distinct integers, constructs a BST from the integers, and returns the root of the BST. The function should minimize the height of the BST. Smple Input : [1, 2, 5, 7, 10, 13, 14, 15, 22] Output: 10 / \ 2 14 / \ / \ 1 5 13 15 \ \ 7 22 Explanation: The given code is used to construct a minimum height binary search tree (BST) from a sorted array. The goal is to create a balanced BST where the difference in height between the left and right subtrees is minimized. The MinHeightBST function serves as a wrapper function that initializes the construction process by calling the constructMinHeightBST helper function. The constructMinHeightBST function recursively constructs the minimum height BST. It takes the sorted array, a partially constructed bst, and the start and end indices that define the range of elements from the array currently being considered. The function follows these steps: Base Case: If end < start, it means there are no elements to consider in the current range, so it returns nil indicating an empty subtree. Calculate the mid index as the midpoint of the current range (start and end). Get the value from the array at the mid index. If the bst is nil, indicating that there are no values in the BST yet, create a new BST node with the value. Otherwise, insert the value into the existing bst using the Insert method. Recursively call constructMinHeightBST for the left half of the array by passing start and mid-1 as the new range. This constructs the left subtree. Recursively call constructMinHeightBST for the right half of the array by passing mid+1 and end as the new range. This constructs the right subtree. Finally, return the bst which represents the constructed minimum height BST. The BST struct represents a node in the binary search tree. It has a Value field to store the node's value and Left and Right fields to point to the left and right child nodes, respectively. The Insert method is used to insert a new value into the BST. It recursively finds the appropriate position to insert the value based on the comparison with the current node's value and updates the left or right child accordingly. Overall, this code efficiently constructs a minimum height BST from a sorted array by recursively dividing the array and inserting the mid-value into the BST, ensuring a balanced structure. Time Complexity: The MinHeightBST function calls the constructMinHeightBST helper function, which performs a binary search-like operation to divide the array and construct the BST. This process is recursive and takes O(log n) time, where n is the number of elements in the array. The bst.Insert(value) operation in constructMinHeightBST has a time complexity of O(log n) in the worst case, as it involves traversing the height of the BST to find the appropriate position to insert the value. Since each element in the array is inserted into the BST once, the overall time complexity is O(n log n), where n is the number of elements in the array. Space Complexity: The space complexity is determined by the stack space used during recursive calls and the space required to store the BST. The recursive calls in constructMinHeightBST consume additional stack space proportional to the height of the tree. In the worst case scenario, where the BST is skewed and its height is equal to the number of elements (n), the space complexity becomes O(n). The space required to store the BST is also O(n) in the worst case since each element in the array is inserted into the BST. Therefore, the overall space complexity is O(n) due to the stack space and the BST storage. In summary, the time complexity is O(n log n) and the space complexity is O(n). */ public class JuiceBottling { public static int[] juiceBottling(int[] prices) { int numSizes = prices.length; int[] maxProfit = new int[numSizes]; // Array to store the maximum profit for each bottle size int[] dividingPoints = new int[numSizes]; // Array to store the dividing points that maximize profit // Loop through each bottle size for (int size = 0; size < numSizes; size++) { // Loop through possible dividing points for the current size for (int dividingPoint = 0; dividingPoint < size + 1; dividingPoint++) { // Calculate the possible profit by combining the previous maximum profit // with the price at the current dividing point int possibleProfit = maxProfit[size - dividingPoint] + prices[dividingPoint]; // Update maxProfit and dividingPoints if the new possible profit is greater if (possibleProfit > maxProfit[size]) { maxProfit[size] = possibleProfit; dividingPoints[size] = dividingPoint; } } } int[] solution = new int[numSizes]; int currentDividingPoint = numSizes - 1; // Reconstruct the solution by tracing back from the end // using the dividing points information while (currentDividingPoint > 0) { solution[currentDividingPoint] = dividingPoints[currentDividingPoint]; currentDividingPoint -= dividingPoints[currentDividingPoint]; } return solution; } public static void main(String[] args) { int[] prices = {3, 5, 8, 9, 10, 17, 17, 20}; int[] result = juiceBottling(prices); System.out.print("Dividing Points for Maximum Profit:"); for (int point : result) { System.out.print(" " + point); } System.out.println(); } } ================================================ FILE: Trees/Binary Search Trees/min_height_BST.js ================================================ /* Write a function that takes in a non-empty sorted array of distinct integers, constructs a BST from the integers, and returns the root of the BST. The function should minimize the height of the BST. Smple Input : [1, 2, 5, 7, 10, 13, 14, 15, 22] Output: 10 / \ 2 14 / \ / \ 1 5 13 15 \ \ 7 22 Explanation: The given code is used to construct a minimum height binary search tree (BST) from a sorted array. The goal is to create a balanced BST where the difference in height between the left and right subtrees is minimized. The MinHeightBST function serves as a wrapper function that initializes the construction process by calling the constructMinHeightBST helper function. The constructMinHeightBST function recursively constructs the minimum height BST. It takes the sorted array, a partially constructed bst, and the start and end indices that define the range of elements from the array currently being considered. The function follows these steps: Base Case: If end < start, it means there are no elements to consider in the current range, so it returns nil indicating an empty subtree. Calculate the mid index as the midpoint of the current range (start and end). Get the value from the array at the mid index. If the bst is nil, indicating that there are no values in the BST yet, create a new BST node with the value. Otherwise, insert the value into the existing bst using the Insert method. Recursively call constructMinHeightBST for the left half of the array by passing start and mid-1 as the new range. This constructs the left subtree. Recursively call constructMinHeightBST for the right half of the array by passing mid+1 and end as the new range. This constructs the right subtree. Finally, return the bst which represents the constructed minimum height BST. The BST struct represents a node in the binary search tree. It has a Value field to store the node's value and Left and Right fields to point to the left and right child nodes, respectively. The Insert method is used to insert a new value into the BST. It recursively finds the appropriate position to insert the value based on the comparison with the current node's value and updates the left or right child accordingly. Overall, this code efficiently constructs a minimum height BST from a sorted array by recursively dividing the array and inserting the mid-value into the BST, ensuring a balanced structure. Time Complexity: The MinHeightBST function calls the constructMinHeightBST helper function, which performs a binary search-like operation to divide the array and construct the BST. This process is recursive and takes O(log n) time, where n is the number of elements in the array. The bst.Insert(value) operation in constructMinHeightBST has a time complexity of O(log n) in the worst case, as it involves traversing the height of the BST to find the appropriate position to insert the value. Since each element in the array is inserted into the BST once, the overall time complexity is O(n log n), where n is the number of elements in the array. Space Complexity: The space complexity is determined by the stack space used during recursive calls and the space required to store the BST. The recursive calls in constructMinHeightBST consume additional stack space proportional to the height of the tree. In the worst case scenario, where the BST is skewed and its height is equal to the number of elements (n), the space complexity becomes O(n). The space required to store the BST is also O(n) in the worst case since each element in the array is inserted into the BST. Therefore, the overall space complexity is O(n) due to the stack space and the BST storage. In summary, the time complexity is O(n log n) and the space complexity is O(n). */ class TreeNode { constructor(value) { this.value = value; this.left = null; this.right = null; } } // Function to construct a minimum-height BST function minHeightBST(array) { // Call the helper method with start index, end index, and a null root node return constructMinHeightBST(array, null, 0, array.length - 1); } // Recursive helper function to construct a minimum-height BST function constructMinHeightBST(array, bst, start, end) { // Base case if (end < start) { return null; } const mid = Math.floor((start + end) / 2); const value = array[mid]; // If the BST is empty, create a new root node if (bst === null) { bst = new TreeNode(value); } else { // Insert the value into the existing BST bst.insert(value); } // Recursively construct the left and right subtrees bst.left = constructMinHeightBST(array, bst.left, start, mid - 1); bst.right = constructMinHeightBST(array, bst.right, mid + 1, end); return bst; } // Class for Binary Search Tree (BST) class BST { constructor(value) { this.value = value; this.left = null; this.right = null; } insert(value) { if (value < this.value) { if (this.left === null) { this.left = new BST(value); } else { this.left.insert(value); } } else { if (this.right === null) { this.right = new BST(value); } else { this.right.insert(value); } } } } // Example usage: const array = [1, 2, 3, 4, 5, 6, 7]; const root = minHeightBST(array); // Printing the BST is left as an exercise ================================================ FILE: Trees/Binary Search Trees/min_height_BST.py ================================================ ''' Write a function that takes in a non-empty sorted array of distinct integers, constructs a BST from the integers, and returns the root of the BST. The function should minimize the height of the BST. Smple Input : [1, 2, 5, 7, 10, 13, 14, 15, 22] Output: 10 / \ 2 14 / \ / \ 1 5 13 15 \ \ 7 22 Explanation: The given code is used to construct a minimum height binary search tree (BST) from a sorted array. The goal is to create a balanced BST where the difference in height between the left and right subtrees is minimized. The MinHeightBST function serves as a wrapper function that initializes the construction process by calling the constructMinHeightBST helper function. The constructMinHeightBST function recursively constructs the minimum height BST. It takes the sorted array, a partially constructed bst, and the start and end indices that define the range of elements from the array currently being considered. The function follows these steps: Base Case: If end < start, it means there are no elements to consider in the current range, so it returns nil indicating an empty subtree. Calculate the mid index as the midpoint of the current range (start and end). Get the value from the array at the mid index. If the bst is nil, indicating that there are no values in the BST yet, create a new BST node with the value. Otherwise, insert the value into the existing bst using the Insert method. Recursively call constructMinHeightBST for the left half of the array by passing start and mid-1 as the new range. This constructs the left subtree. Recursively call constructMinHeightBST for the right half of the array by passing mid+1 and end as the new range. This constructs the right subtree. Finally, return the bst which represents the constructed minimum height BST. The BST struct represents a node in the binary search tree. It has a Value field to store the node's value and Left and Right fields to point to the left and right child nodes, respectively. The Insert method is used to insert a new value into the BST. It recursively finds the appropriate position to insert the value based on the comparison with the current node's value and updates the left or right child accordingly. Overall, this code efficiently constructs a minimum height BST from a sorted array by recursively dividing the array and inserting the mid-value into the BST, ensuring a balanced structure. Time Complexity: The MinHeightBST function calls the constructMinHeightBST helper function, which performs a binary search-like operation to divide the array and construct the BST. This process is recursive and takes O(log n) time, where n is the number of elements in the array. The bst.Insert(value) operation in constructMinHeightBST has a time complexity of O(log n) in the worst case, as it involves traversing the height of the BST to find the appropriate position to insert the value. Since each element in the array is inserted into the BST once, the overall time complexity is O(n log n), where n is the number of elements in the array. Space Complexity: The space complexity is determined by the stack space used during recursive calls and the space required to store the BST. The recursive calls in constructMinHeightBST consume additional stack space proportional to the height of the tree. In the worst case scenario, where the BST is skewed and its height is equal to the number of elements (n), the space complexity becomes O(n). The space required to store the BST is also O(n) in the worst case since each element in the array is inserted into the BST. Therefore, the overall space complexity is O(n) due to the stack space and the BST storage. In summary, the time complexity is O(n log n) and the space complexity is O(n). ''' class TreeNode: def __init__(self, value): self.value = value self.left = None self.right = None def min_height_bst(array): # Call helper method with start index, end index, and a None root node return construct_min_height_bst(array, None, 0, len(array) - 1) def construct_min_height_bst(array, bst, start, end): # Base case if end < start: return None mid = (start + end) // 2 value = array[mid] # If the BST is empty, create a new root node if bst is None: bst = TreeNode(value) else: # Insert the value into the existing BST bst.insert(value) # Recursively construct the left and right subtrees bst.left = construct_min_height_bst(array, bst.left, start, mid - 1) bst.right = construct_min_height_bst(array, bst.right, mid + 1, end) return bst class BST: def __init__(self, value): self.value = value self.left = None self.right = None def insert(self, value): if value < self.value: if self.left is None: self.left = BST(value) else: self.left.insert(value) else: if self.right is None: self.right = BST(value) else: self.right.insert(value) # Example usage: array = [1, 2, 3, 4, 5, 6, 7] root = min_height_bst(array) # Printing the BST is left as an exercise ================================================ FILE: Trees/Binary Search Trees/reconstruct_bst.cpp ================================================ /* Reconstruct BST The pre-order traversal of a Binary Tree is a traversal technique that starts at the tree's root node and visits nodes in the following order: Current Node Left Subtree Right Subtree Given a non-empty array of integers representing the pre-order traversal of a Binary Search Tree (BST), write a function that creates the relevant BST and returns its root node. The input array will contain the values of BST nodes in the order in which these nodes would be visited with a pre-order traversal. Sample Input: [10, 4, 2, 1, 5, 17, 19, 18] Sample Output: 10 / \ 4 17 / \ \ 2 5 19 / / 1 18 The ReconstructBst function takes a slice preOrderTraversalValues which represents the pre-order traversal of a binary search tree. It reconstructs the BST using a range-based approach. Here's how the algorithm works: The ReconstructBst function initializes a treeInfo struct to keep track of the current root index. The ReconstructBst function calls the reconstructBSTFromRange helper function, passing the minimum and maximum integer values as the initial range, the pre-order traversal values, and the treeInfo struct. The reconstructBSTFromRange function first checks if the current root index has reached the end of the pre-order traversal values. If so, it returns nil indicating an empty subtree. It retrieves the value of the current root from the pre-order traversal values. It checks if the root value is outside the valid range defined by the lower and upper bounds. If so, it returns The time complexity of the ReconstructBst function is O(n), where n is the number of nodes in the reconstructed BST. This is because the function processes each node exactly once. The space complexity of the ReconstructBst function is O(n), where n is the number of nodes in the reconstructed BST. This is because the function creates BST nodes and recursively calls itself to construct the left and right subtrees. The space complexity is determined by the height of the BST, which can be at most n in the worst case for a skewed BST. */ #include #include #include // Definition for a binary tree node. struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} }; // A helper class to keep track of the current root index during reconstruction. class TreeInfo { public: int rootIdx; TreeInfo(int idx) : rootIdx(idx) {} }; // Function to reconstruct the BST from a pre-order traversal. TreeNode* reconstructBst(std::vector& preOrderTraversalValues) { // Create a TreeInfo object to keep track of the current root index. TreeInfo treeInfo(0); // Call the helper function to reconstruct the BST from the given range and return the result. return reconstructBstFromRange(INT_MIN, INT_MAX, preOrderTraversalValues, treeInfo); } // Recursive helper function to reconstruct the BST within a given range. TreeNode* reconstructBstFromRange(int lowerBound, int upperBound, std::vector& preOrderTraversalValues, TreeInfo& currentSubtreeInfo) { // Check if the root index has reached the end of the pre-order traversal values. If so, return nullptr indicating an empty subtree. if (currentSubtreeInfo.rootIdx == preOrderTraversalValues.size()) { return nullptr; } // Get the value of the current root from the pre-order traversal values. int rootValue = preOrderTraversalValues[currentSubtreeInfo.rootIdx]; // Check if the root value is out of the valid range defined by the lower and upper bounds. If so, return nullptr indicating an invalid subtree. if (rootValue < lowerBound || rootValue >= upperBound) { return nullptr; } // Increment the root index to move to the next element in the pre-order traversal values. currentSubtreeInfo.rootIdx++; // Recursively reconstruct the left subtree within the range (lowerBound, rootValue) using the updated root index. TreeNode* leftSubtree = reconstructBstFromRange(lowerBound, rootValue, preOrderTraversalValues, currentSubtreeInfo); // Recursively reconstruct the right subtree within the range (rootValue, upperBound) using the updated root index. TreeNode* rightSubtree = reconstructBstFromRange(rootValue, upperBound, preOrderTraversalValues, currentSubtreeInfo); // Create a new TreeNode with the current root value and the reconstructed left and right subtrees. TreeNode* rootNode = new TreeNode(rootValue); rootNode->left = leftSubtree; rootNode->right = rightSubtree; return rootNode; } // Function to delete the BST and free memory. void deleteBst(TreeNode* root) { if (root == nullptr) { return; } deleteBst(root->left); deleteBst(root->right); delete root; } int main() { std::vector preOrderTraversalValues = {10, 5, 2, 7, 15, 12, 20}; TreeInfo treeInfo(0); // Initialize treeInfo with root index 0 TreeNode* root = reconstructBstFromRange(INT_MIN, INT_MAX, preOrderTraversalValues, treeInfo); // Printing the reconstructed BST is left as an exercise // Clean up memory deleteBst(root); return 0; } ================================================ FILE: Trees/Binary Search Trees/reconstruct_bst.go ================================================ /* Reconstruct BST The pre-order traversal of a Binary Tree is a traversal technique that starts at the tree's root node and visits nodes in the following order: Current Node Left Subtree Right Subtree Given a non-empty array of integers representing the pre-order traversal of a Binary Search Tree (BST), write a function that creates the relevant BST and returns its root node. The input array will contain the values of BST nodes in the order in which these nodes would be visited with a pre-order traversal. Sample Input: [10, 4, 2, 1, 5, 17, 19, 18] Sample Output: 10 / \ 4 17 / \ \ 2 5 19 / / 1 18 Explanation: Approach 1: The ReconstructBst function takes a slice preOrderTraversalValues which represents the pre-order traversal of a binary search tree. It reconstructs the BST using a recursive approach. Here's how the algorithm works: The base case is defined when the preOrderTraversalValues slice is empty, in which case it returns nil indicating an empty tree. The first element in the preOrderTraversalValues slice represents the current node value of the BST. The algorithm finds the index (rightSubTreeRootIdx) where the right subtree starts by iterating over the remaining elements in the preOrderTraversalValues slice and finding the first value greater than or equal to the current value. It recursively calls ReconstructBst on the sub-array representing the left subtree (preOrderTraversalValues[1:rightSubTreeRootIdx]) to reconstruct the left subtree. It recursively calls ReconstructBst on the sub-array representing the right subtree (preOrderTraversalValues[rightSubTreeRootIdx:]) to reconstruct the right subtree. Finally, it creates a new BST node with the current value, the reconstructed left subtree, and the reconstructed right subtree, and returns the node. The algorithm builds the BST in a top-down manner by dividing the pre-order traversal values into left and right subtrees. It constructs the left subtree first and then the right subtree. The time complexity of the algorithm is O(n^2) in the worst case, where n is the number of nodes in the BST. ****************************************************************************************** Approach 2: The ReconstructBst function takes a slice preOrderTraversalValues which represents the pre-order traversal of a binary search tree. It reconstructs the BST using a range-based approach. Here's how the algorithm works: The ReconstructBst function initializes a treeInfo struct to keep track of the current root index. The ReconstructBst function calls the reconstructBSTFromRange helper function, passing the minimum and maximum integer values as the initial range, the pre-order traversal values, and the treeInfo struct. The reconstructBSTFromRange function first checks if the current root index has reached the end of the pre-order traversal values. If so, it returns nil indicating an empty subtree. It retrieves the value of the current root from the pre-order traversal values. It checks if the root value is outside the valid range defined by the lower and upper bounds. If so, it returns The time complexity of the ReconstructBst function is O(n), where n is the number of nodes in the reconstructed BST. This is because the function processes each node exactly once. The space complexity of the ReconstructBst function is O(n), where n is the number of nodes in the reconstructed BST. This is because the function creates BST nodes and recursively calls itself to construct the left and right subtrees. The space complexity is determined by the height of the BST, which can be at most n in the worst case for a skewed BST. */ package main import "math" // BST represents a binary search tree node. type BST struct { Value int Left *BST Right *BST } // Approach 1: Time complexity O(n^2) Space O(n), where n is length of input array // ReconstructBst takes a slice of integers representing the pre-order traversal of a BST and returns the reconstructed BST. func ReconstructBst(preOrderTraversalValues []int) *BST { // Base case: If the pre-order traversal is empty, return nil indicating an empty tree. if len(preOrderTraversalValues) == 0 { return nil } // Get the current value from the pre-order traversal values. currentVal := preOrderTraversalValues[0] // Find the index where the right subtree starts by searching for the first value greater than or equal to the current value. rightSubTreeRootIdx := len(preOrderTraversalValues) for i := 1; i < len(preOrderTraversalValues); i++ { value := preOrderTraversalValues[i] if value >= currentVal { rightSubTreeRootIdx = i break } } // Recursively reconstruct the left and right subtrees by calling the ReconstructBst function on the appropriate sub-arrays. leftSubTree := ReconstructBst(preOrderTraversalValues[1:rightSubTreeRootIdx]) rightSubTree := ReconstructBst(preOrderTraversalValues[rightSubTreeRootIdx:]) // Create a new BST node with the current value and the reconstructed left and right subtrees. return &BST{Value: currentVal, Left: leftSubTree, Right: rightSubTree} } // Approach 2: Time complexity O(n) Space O(n), where n is length of input array // treeInfo is a helper struct to keep track of the current root index during the reconstruction process. type treeInfo struct { rootIdx int } // ReconstructBst takes a slice of integers representing the pre-order traversal of a BST and returns the reconstructed BST. func ReconstructBst2(preOrderTraversalValues []int) *BST { // Create a treeInfo struct to keep track of the current root index. treeInfo := &treeInfo{rootIdx: 0} // Call the helper function to reconstruct the BST from the given range and return the result. return reconstructBSTFromRange(math.MinInt32, math.MaxInt32, preOrderTraversalValues, treeInfo) } // reconstructBSTFromRange reconstructs the BST recursively within the given range using the pre-order traversal values. func reconstructBSTFromRange(lowerBound, upperBound int, preOrderTraversalValues []int, currentSubtreeInfo *treeInfo) *BST { // Check if the root index has reached the end of the pre-order traversal values. If so, return nil indicating an empty subtree. if currentSubtreeInfo.rootIdx == len(preOrderTraversalValues) { return nil } // Get the value of the current root from the pre-order traversal values. rootValue := preOrderTraversalValues[currentSubtreeInfo.rootIdx] // Check if the root value is out of the valid range defined by the lower and upper bounds. If so, return nil indicating an invalid subtree. if rootValue < lowerBound || rootValue >= upperBound { return nil } // Increment the root index to move to the next element in the pre-order traversal values. currentSubtreeInfo.rootIdx++ // Recursively reconstruct the left subtree within the range (lowerBound, rootValue) using the updated root index. leftSubtree := reconstructBSTFromRange(lowerBound, rootValue, preOrderTraversalValues, currentSubtreeInfo) // Recursively reconstruct the right subtree within the range (rootValue, upperBound) using the updated root index. rightSubtree := reconstructBSTFromRange(rootValue, upperBound, preOrderTraversalValues, currentSubtreeInfo) // Create a new BST node with the current root value and the reconstructed left and right subtrees. return &BST{Value: rootValue, Left: leftSubtree, Right: rightSubtree} } ================================================ FILE: Trees/Binary Search Trees/reconstruct_bst.java ================================================ /* Reconstruct BST The pre-order traversal of a Binary Tree is a traversal technique that starts at the tree's root node and visits nodes in the following order: Current Node Left Subtree Right Subtree Given a non-empty array of integers representing the pre-order traversal of a Binary Search Tree (BST), write a function that creates the relevant BST and returns its root node. The input array will contain the values of BST nodes in the order in which these nodes would be visited with a pre-order traversal. Sample Input: [10, 4, 2, 1, 5, 17, 19, 18] Sample Output: 10 / \ 4 17 / \ \ 2 5 19 / / 1 18 The ReconstructBst function takes a slice preOrderTraversalValues which represents the pre-order traversal of a binary search tree. It reconstructs the BST using a range-based approach. Here's how the algorithm works: The ReconstructBst function initializes a treeInfo struct to keep track of the current root index. The ReconstructBst function calls the reconstructBSTFromRange helper function, passing the minimum and maximum integer values as the initial range, the pre-order traversal values, and the treeInfo struct. The reconstructBSTFromRange function first checks if the current root index has reached the end of the pre-order traversal values. If so, it returns nil indicating an empty subtree. It retrieves the value of the current root from the pre-order traversal values. It checks if the root value is outside the valid range defined by the lower and upper bounds. If so, it returns The time complexity of the ReconstructBst function is O(n), where n is the number of nodes in the reconstructed BST. This is because the function processes each node exactly once. The space complexity of the ReconstructBst function is O(n), where n is the number of nodes in the reconstructed BST. This is because the function creates BST nodes and recursively calls itself to construct the left and right subtrees. The space complexity is determined by the height of the BST, which can be at most n in the worst case for a skewed BST. */ class TreeNode { int value; TreeNode left; TreeNode right; TreeNode(int x) { value = x; left = null; right = null; } } class TreeInfo { int rootIdx; TreeInfo(int idx) { rootIdx = idx; } } public class ReconstructBST { public static TreeNode reconstructBst(int[] preOrderTraversalValues) { // Create a TreeInfo object to keep track of the current root index. TreeInfo treeInfo = new TreeInfo(0); // Call the helper function to reconstruct the BST from the given range and return the result. return reconstructBstFromRange(Integer.MIN_VALUE, Integer.MAX_VALUE, preOrderTraversalValues, treeInfo); } private static TreeNode reconstructBstFromRange(int lowerBound, int upperBound, int[] preOrderTraversalValues, TreeInfo currentSubtreeInfo) { // Check if the root index has reached the end of the pre-order traversal values. If so, return null indicating an empty subtree. if (currentSubtreeInfo.rootIdx == preOrderTraversalValues.length) { return null; } // Get the value of the current root from the pre-order traversal values. int rootValue = preOrderTraversalValues[currentSubtreeInfo.rootIdx]; // Check if the root value is out of the valid range defined by the lower and upper bounds. If so, return null indicating an invalid subtree. if (rootValue < lowerBound || rootValue >= upperBound) { return null; } // Increment the root index to move to the next element in the pre-order traversal values. currentSubtreeInfo.rootIdx++; // Recursively reconstruct the left subtree within the range (lowerBound, rootValue) using the updated root index. TreeNode leftSubtree = reconstructBstFromRange(lowerBound, rootValue, preOrderTraversalValues, currentSubtreeInfo); // Recursively reconstruct the right subtree within the range (rootValue, upperBound) using the updated root index. TreeNode rightSubtree = reconstructBstFromRange(rootValue, upperBound, preOrderTraversalValues, currentSubtreeInfo); // Create a new TreeNode with the current root value and the reconstructed left and right subtrees. TreeNode rootNode = new TreeNode(rootValue); rootNode.left = leftSubtree; rootNode.right = rightSubtree; return rootNode; } public static void main(String[] args) { int[] preOrderTraversalValues = {10, 5, 2, 7, 15, 12, 20}; TreeNode root = reconstructBst(preOrderTraversalValues); // Printing the reconstructed BST is left as an exercise } } ================================================ FILE: Trees/Binary Search Trees/reconstruct_bst.js ================================================ /** Reconstruct BST The pre-order traversal of a Binary Tree is a traversal technique that starts at the tree's root node and visits nodes in the following order: Current Node Left Subtree Right Subtree Given a non-empty array of integers representing the pre-order traversal of a Binary Search Tree (BST), write a function that creates the relevant BST and returns its root node. The input array will contain the values of BST nodes in the order in which these nodes would be visited with a pre-order traversal. Sample Input: [10, 4, 2, 1, 5, 17, 19, 18] Sample Output: 10 / \ 4 17 / \ \ 2 5 19 / / 1 18 */ /** * Class representing a node in the Binary Search Tree (BST). */ class TreeNode { /** * Create a new TreeNode. * @param {*} value - The value to be stored in the node. */ constructor(value) { this.val = value; this.left = null; this.right = null; } } /** * Reconstructs a Binary Search Tree (BST) from its pre-order traversal. * @param {number[]} preorder - The pre-order traversal array of the BST. * @return {TreeNode} - The root node of the reconstructed BST. */ function constructBST(preorder) { // Base case: if the array is empty, return null if (preorder.length === 0) { return null; } // The first element in the pre-order array is the root value const rootValue = preorder[0]; const rootNode = new TreeNode(rootValue); // Find the index where the elements are greater than the root value let i = 1; while (i < preorder.length && preorder[i] < rootValue) { i++; } // Split the remaining elements into left and right subtrees const leftSubtree = preorder.slice(1, i); const rightSubtree = preorder.slice(i); // Recursively construct the left and right subtrees rootNode.left = constructBST(leftSubtree); rootNode.right = constructBST(rightSubtree); return rootNode; } // Example usage const preorder = [10, 4, 2, 1, 5, 17, 19, 18]; const root = constructBST(preorder); /** * Prints the values of the BST in ascending order using an in-order traversal. * @param {TreeNode} node - The root node of the BST. */ function printInOrder(node) { if (node === null) { return; } printInOrder(node.left); console.log(node.val); printInOrder(node.right); } // Print the values of the reconstructed BST in ascending order printInOrder(root); /** * The time complexity of the constructBST function can be analyzed as follows: In each recursive call, we split the pre-order traversal array into two parts based on the root value. This operation takes O(N) time, where N is the number of elements in the array. Since the function is called recursively for the left and right subtrees, the total time complexity can be expressed as a summation of the work done in each recursive call. In the worst case, the pre-order traversal array is completely unbalanced, resulting in a linear chain of nodes. In this case, the function will make N recursive calls, each taking O(N) time. Therefore, the overall time complexity of the constructBST function is O(N^2) in the worst case. However, in the average case, when the BST is balanced, the time complexity can be approximated as O(NlogN). */ /** * In each recursive call, the function creates new arrays for the left and right subtrees using the slice method. The space required for these arrays is proportional to the size of the pre-order traversal array. In the worst case, when the BST is completely unbalanced, the size of the arrays will be O(N), where N is the number of elements in the pre-order traversal array. Therefore, the overall space complexity of the `constructBST function is O(N) in the worst case. */ ================================================ FILE: Trees/Binary Search Trees/reconstruct_bst.py ================================================ ''' Reconstruct BST The pre-order traversal of a Binary Tree is a traversal technique that starts at the tree's root node and visits nodes in the following order: Current Node Left Subtree Right Subtree Given a non-empty array of integers representing the pre-order traversal of a Binary Search Tree (BST), write a function that creates the relevant BST and returns its root node. The input array will contain the values of BST nodes in the order in which these nodes would be visited with a pre-order traversal. Sample Input: [10, 4, 2, 1, 5, 17, 19, 18] Sample Output: 10 / \ 4 17 / \ \ 2 5 19 / / 1 18 The ReconstructBst function takes a slice preOrderTraversalValues which represents the pre-order traversal of a binary search tree. It reconstructs the BST using a range-based approach. Here's how the algorithm works: The ReconstructBst function initializes a treeInfo struct to keep track of the current root index. The ReconstructBst function calls the reconstructBSTFromRange helper function, passing the minimum and maximum integer values as the initial range, the pre-order traversal values, and the treeInfo struct. The reconstructBSTFromRange function first checks if the current root index has reached the end of the pre-order traversal values. If so, it returns nil indicating an empty subtree. It retrieves the value of the current root from the pre-order traversal values. It checks if the root value is outside the valid range defined by the lower and upper bounds. If so, it returns The time complexity of the ReconstructBst function is O(n), where n is the number of nodes in the reconstructed BST. This is because the function processes each node exactly once. The space complexity of the ReconstructBst function is O(n), where n is the number of nodes in the reconstructed BST. This is because the function creates BST nodes and recursively calls itself to construct the left and right subtrees. The space complexity is determined by the height of the BST, which can be at most n in the worst case for a skewed BST. ''' class TreeNode: def __init__(self, val): self.val = val self.left = None self.right = None class TreeInfo: def __init__(self, root_idx): self.root_idx = root_idx def reconstruct_bst(pre_order_traversal_values): # Create a TreeInfo object to keep track of the current root index. tree_info = TreeInfo(0) # Call the helper function to reconstruct the BST from the given range and return the result. return reconstruct_bst_from_range(float('-inf'), float('inf'), pre_order_traversal_values, tree_info) def reconstruct_bst_from_range(lower_bound, upper_bound, pre_order_traversal_values, current_subtree_info): # Check if the root index has reached the end of the pre-order traversal values. If so, return None indicating an empty subtree. if current_subtree_info.root_idx == len(pre_order_traversal_values): return None # Get the value of the current root from the pre-order traversal values. root_value = pre_order_traversal_values[current_subtree_info.root_idx] # Check if the root value is out of the valid range defined by the lower and upper bounds. If so, return None indicating an invalid subtree. if root_value < lower_bound or root_value >= upper_bound: return None # Increment the root index to move to the next element in the pre-order traversal values. current_subtree_info.root_idx += 1 # Recursively reconstruct the left subtree within the range (lower_bound, root_value) using the updated root index. left_subtree = reconstruct_bst_from_range(lower_bound, root_value, pre_order_traversal_values, current_subtree_info) # Recursively reconstruct the right subtree within the range (root_value, upper_bound) using the updated root index. right_subtree = reconstruct_bst_from_range(root_value, upper_bound, pre_order_traversal_values, current_subtree_info) # Create a new TreeNode with the current root value and the reconstructed left and right subtrees. root_node = TreeNode(root_value) root_node.left = left_subtree root_node.right = right_subtree return root_node # Example usage: pre_order_traversal_values = [10, 5, 2, 7, 15, 12, 20] tree_info = TreeInfo(0) # Initialize tree_info with root index 0 root = reconstruct_bst_from_range(float('-inf'), float('inf'), pre_order_traversal_values, tree_info) # Printing the reconstructed BST is left as an exercise ================================================ FILE: Trees/Binary Search Trees/search.cpp ================================================ // Binary Search Tree : Searching in Binary Search Tree // Program Author : Abhisek Kumar Gupta /* Input : 7 3 9 2 4 8 10 -1 Value to be searched : 99 Output : 7 / \ 3 9 / \ / \ 2 4 8 10 Not present in BST Value to be searched : 8 Present in BST */ #include using namespace std; class Node{ public: int data; Node* left; Node* right; Node(int x){ data = x; left = NULL; right = NULL; } }; void bfs(Node* root){ queue q; q.push(root); q.push(NULL); while(!q.empty()){ Node* element = q.front(); if(element == NULL){ cout << "\n"; q.pop(); if(!q.empty()){ q.push(NULL); } } else{ cout << element->data << "->"; q.pop(); if(element->left != NULL){ q.push(element->left); } if(element->right != NULL){ q.push(element->right); } } } return; } Node* insert_into_binary_search_tree(Node* root, int data){ if(root == NULL){ return new Node(data); } if(data <= root->data){ root->left = insert_into_binary_search_tree(root->left, data); } else{ root->right = insert_into_binary_search_tree(root->right, data); } return root; } Node* build_binary_search_tree(){ int data; cin >> data; Node* root = NULL; while(data != -1){ root = insert_into_binary_search_tree(root, data); cin >> data; } return root; } bool search_in_BST(Node* root, int value_to_be_searched){ if(root == NULL) return false; if(value_to_be_searched == root->data) return true; if(value_to_be_searched <= root->data){ return search_in_BST(root->left, value_to_be_searched); } else{ return search_in_BST(root->right, value_to_be_searched); } } int main(){ Node* root = build_binary_search_tree(); bfs(root); int value_to_be_searched; cin >> value_to_be_searched; if(search_in_BST(root, value_to_be_searched)){ cout << "Present in BST\n"; } else{ cout << "Not present in BST\n"; } return 0; } ================================================ FILE: Trees/Binary Search Trees/validate_bst.go ================================================ /* Write a function that takes in a potentially invalid Binary Search Tree (BST) and returns a boolean representing whether the BST is valid. Sample Input : 10 / \ 5 15 / \ / \ 2 5 13 22 / \ 1 14 Output : True Explanation: This code defines a Binary Search Tree (BST) struct with an integer value and left and right nodes that can point to other BST nodes. The struct also has a method called ValidateBst() that returns a boolean indicating whether the tree is a valid BST or not. The BST struct has another method called validateBST() that is used by ValidateBst() to check whether the tree is a valid BST or not. The validateBST() method takes in two arguments, min and max, which represent the minimum and maximum values that the current node's value can take in order to be a valid BST. The validateBST() method first checks whether the current node's value is within the valid range determined by the min and max arguments. If not, the method returns false, indicating that the tree is not a valid BST. If the current node's value is within the valid range, the method then recursively calls itself on the left and right child nodes to check whether their values are within their valid ranges. The valid range for the left child node is defined by the minimum value and the parent node's value, while the valid range for the right child node is defined by the parent node's value and the maximum value. If all of the nodes in the tree satisfy the BST property, the method returns true, indicating that the tree is a valid BST. O(n) time | O(d) space - where n is the number of nodes in the BST and d is the depth (height) of the BST */ package main import "math" type BST struct { Value int Left *BST Right *BST } // ValidateBst is a method of BST that checks if the binary search tree is valid func (tree *BST) ValidateBst() bool { return tree.validateBST(math.MinInt32, math.MaxInt32) } // validateBST is a recursive helper function that checks if the binary search tree is valid // min is the minimum value that a node in the subtree rooted at this node can have // max is the maximum value that a node in the subtree rooted at this node can have func (tree *BST) validateBST(min, max int) bool { // if the current node's value is outside the allowed range, then the tree is invalid if tree.Value < min || tree.Value >= max { return false } // recursively check the left subtree, making sure all values are less than the current node's value if tree.Left != nil && !tree.Left.validateBST(min, tree.Value) { return false } // recursively check the right subtree, making sure all values are greater than or equal to the current node's value if tree.Right != nil && !tree.Right.validateBST(tree.Value, max) { return false } // if we reach this point, then the tree is valid return true } ================================================ FILE: Trees/Binary Trees/Trie.js ================================================ /* Description A trie (pronounced as "try") or prefix tree is a tree data structure used to efficiently store and retrieve keys in a dataset of strings. There are various applications of this data structure, such as autocomplete and spellchecker. Implement the Trie class: Trie() Initializes the trie object. void insert(String word) Inserts the string word into the trie. boolean search(String word) Returns true if the string word is in the trie (i.e., was inserted before), and false otherwise. boolean startsWith(String prefix) Returns true if there is a previously inserted string word that has the prefix prefix, and false otherwise. Input ["Trie", "insert", "search", "search", "startsWith", "insert", "search"] [[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]] Output [null, null, true, false, true, null, true] Explanation Trie trie = new Trie(); trie.insert("apple"); trie.search("apple"); // return True trie.search("app"); // return False trie.startsWith("app"); // return True trie.insert("app"); trie.search("app"); // return True Time Complexity for each operation is O(n) Space Complexity O(n*m) where n is the number of words inserted and m is the average length of the words. Explanation: insert() -> traverse through each character of the input word and initializes it if necessary. If the end of the word is reached set isEnd to true. search() -> Search In each child node until the end of the word is reached, then if end of the node is also reached return true else false. startsWith() -> Similar to search method but we only check if end of the prefix is reached and we don't need to check if it is the end of the node. */ class Trie { constructor() { this.root = new Node(); } insert(word) { this.root.insert(word, 0); } search(word) { return this.root.search(word, 0); } startsWith(prefix) { return this.root.startsWith(prefix, 0); } } class Node { constructor() { this.nodes = new Array(26); this.isEnd = false; } // Function to insert the word in the tree insert(word, idx) { if (idx >= word.length) return; // handle edge case const i = word.charCodeAt(idx) - 'a'.charCodeAt(0); if (!this.nodes[i]) { this.nodes[i] = new Node(); // initialize the node[i] if the letter was not found before } if (idx === word.length - 1) this.nodes[i].isEnd = true; // signifies that this is the end of the word this.nodes[i].insert(word, idx + 1); // recursive call to populate the child node } // Function to search the word in the tree search(word, idx) { if (idx >= word.length) return false; const node = this.nodes[word.charCodeAt(idx) - 'a'.charCodeAt(0)]; if (!node) return false; // if the node is null it means that it was not initialised hence the character was never found. if (idx === word.length - 1 && node.isEnd) return true; //if it is the last character and the end of the node then return true return node.search(word, idx + 1); // recursive call search in the child node } //Function to search the prefix in tree startsWith(prefix, idx) { if (idx >= prefix.length) return false; const node = this.nodes[prefix.charCodeAt(idx) - 'a'.charCodeAt(0)]; if (!node) return false; if (idx === prefix.length - 1) return true; // Very similar to above method but here we don't need to check if it is the end of the node. return node.startsWith(prefix, idx + 1); } } ================================================ FILE: Trees/Binary Trees/bfs.cpp ================================================ // Binary Tree : Breadth First Search TC : O(n) // Program Author : Abhisek Kumar Gupta /* 40 / \ 10 30 / \ / \ 5 -1 -1 28 / \ / \ 1 -1 15 20 / \ /\ /\ 1 -1 -1 -1 -1 -1 /\ -1 -1 Input : 40 10 5 1 1 -1 -1 -1 -1 -1 30 -1 28 15 -1 -1 20 -1 -1 Output : 40 10 30 5 28 1 15 20 1 */ #include using namespace std; class Node{ public: int data; Node* left; Node* right; Node(int x){ data = x; } }; Node* build_binary_tree(){ int data; cin >> data; if(data == -1) return NULL; Node* root = new Node(data); root->left = build_binary_tree(); root->right = build_binary_tree(); return root; } void bfs(Node* root){ queue q; q.push(root); while(!q.empty()){ Node* element = q.front(); cout << element->data << "->"; q.pop(); if(element->left != NULL){ q.push(element->left); } if(element->right != NULL){ q.push(element->right); } } return; } int main(){ Node* root = build_binary_tree(); bfs(root); return 0; } ================================================ FILE: Trees/Binary Trees/bfs.go ================================================ /* Breadth First Search: Sample Input: A / | \ B C D / \ / \ E F G H / \ \ I J K Output: ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K"] Explanation: This code defines a struct `Node` with two properties: `Name`, a string representing the name of the node, and `Children`, a slice of pointers to other `Node` objects representing the children of the node. The method `BreadthFirstSearch` is defined on `Node`. This method takes an empty string slice `array` as an argument and performs a breadth-first search traversal of the tree rooted at the current node. It returns a slice of strings representing the names of the nodes visited in breadth-first order. The breadth-first search algorithm starts with a queue containing only the current node. The algorithm then proceeds as follows: 1. Dequeue the first node from the queue. 2. Add the name of the dequeued node to the `array`. 3. Enqueue all the children of the dequeued node to the end of the queue. 4. Repeat steps 1-3 until the queue is empty. At the end of the algorithm, the `array` contains the names of all the nodes visited in breadth-first order. The method returns this `array`. Time complexity: O(v + e) Space complexity: O(v) where v is the number of vertices of the input graph and e is the number of edges of the input graph */ package main // Define a Node struct that has a name and a slice of child nodes. type Node struct { Name string Children []*Node } // Define a method BreadthFirstSearch on the Node struct that takes an array of strings as an argument. // This method performs a breadth-first search on the tree starting from the node the method is called on. // It appends the names of all nodes visited to the input array and returns the modified array. func (n *Node) BreadthFirstSearch(array []string) []string { // Create a queue of nodes starting with the current node. queue := []*Node{n} // While there are still nodes in the queue to be visited: for len(queue) > 0 { // Dequeue the first node in the queue and add its name to the array. current := queue[0] queue = queue[1:] array = append(array, current.Name) // Add all of the node's children to the end of the queue. for _, child := range current.Children { queue = append(queue, child) } } // Return the modified array. return array } ================================================ FILE: Trees/Binary Trees/binary_tree.go ================================================ /* A tree is called binary tree if each node has zero child, one child or two children. Empty tree is also a valid binary tree. We can visualize a binary tree as consisting of a root and two disjoint binary trees, called the left and right subtrees of the root. Types of Binary Trees 1) Strict Binary Tree: A binary tree is called strict binary tree if each node has exactly two children or no children. 2) Full Binary Tree: A binary tree is called full binary tree if each node has exactly two children and all leaf nodes are at the same level. 3) Complete Binary Tree: Before defining the complete binary tree, let us assume that the height of the binary tree is ℎ. In complete binary trees, if we give numbering for the nodes by starting at the root (let us say the root node has 1) then we get a complete sequence from 1 to the number of nodes in the tree. While traversing we should give numbering for nil pointers also. A binary tree is called complete binary tree if all leaf nodes are at height ℎ or ℎ − 1 and also without any missing number in the sequence. 1 / \ 2 3 / \ / \ 4 5 6 7 */ package main import ( "fmt" "math/rand" ) type BinaryTreeNode struct { left *BinaryTreeNode data int right *BinaryTreeNode } // NewBinaryTree returns a new, random binary tree func NewBinaryTree(n, k int) *BinaryTreeNode { var root * BinaryTreeNode for _, v := range rand.Perm(n) { root = Insert(root, (1 + v) * k) } return root } // Insert, inserts an element in binary tree func Insert(root *BinaryTreeNode, v int) *BinaryTreeNode { if root == nil { // fmt.Printf("%d root", v) return &BinaryTreeNode{nil, v, nil} } // data less than root of data the insert in left subtree if v < root.data { // fmt.Printf("%d left\n", v) root.left = Insert(root.left, v) return root } // data greater than or equal to root of data the insert in right subtree // fmt.Printf("%d right\n", v) root.right = Insert(root.right, v) return root } // Pre-order traversal // Preorder traversal is defined as follows: // 1 Visit the root. // 2 Traverse the left subtree in Preorder. // 3 Traverse the right subtree in Preorder. // Time Complexity: O(n). Space Complexity: O(n). // The nodes of tree would be visited in the order: 1 2 4 5 3 6 7 func PreOrder(root *BinaryTreeNode) { if root == nil { return } fmt.Printf("%d ", root.data) PreOrder(root.left) PreOrder(root.right) } // Inorder traversal is defined as follows: // 1 Traverse the left subtree in Inorder. // 2 Visit the root. // 3 Traverse the right subtree in Inorder. // Time Complexity: O(n). Space Complexity: O(n). // The nodes of tree would be visited in the order: 4 2 5 1 6 3 7 func InOrder(root *BinaryTreeNode) { if root == nil { return } InOrder(root.left) fmt.Printf("%d", root.data) InOrder(root.right) } // PostOrder traversal is defined as follows: // 1 Traverse the left subtree in PostOrder. // 2 Traverse the right subtree in PostOrder. // 3 Visit the root. // The nodes of the tree would be visited in the order: 4 5 2 6 7 3 1 func PostOrder(root *BinaryTreeNode) { if root == nil { return } PostOrder(root.left) PostOrder(root.right) fmt.Printf("%d", root.data) } func main() { t1 := NewBinaryTree(10, 1) PreOrder(t1) fmt.Println() InOrder(t1) fmt.Println() PostOrder(t1) fmt.Println() } ================================================ FILE: Trees/Binary Trees/branch_sum.go ================================================ /* Write a function that takes in a Binary Tree and returns a list of its branch sums ordered from leftmost branch sum to rightmost branch sum. A branch sum is the sum of all values in a Binary Tree branch. A Binary Tree branch is a path of nodes in a tree that starts at the root node and ends at any leaf node. Sample INput: 1 / \ 2 3 / \ / \ 4 5 6 7 / \ / 8 9 10 Output: [15, 16, 18, 10, 11] length of output will be always total number of leaves Time and Space complexity : O(n) time | O(n) space - where n is the number of nodes in the Binary Tree */ package main type BinaryTree struct { Value int Left *BinaryTree Right *BinaryTree } func BranchSums(root *BinaryTree) []int { sums := []int{} calculateBranchSums(root, 0, &sums) return sums } /* As you recursively traverse the tree, if you reach a leaf node (a node with no "left" or "right" Binary Tree nodes), add the relevant running sum that you've calculated to a list of sums (which you'll also have to pass to the recursive function). If you reach a node that isn't a leaf node, keep recursively traversing its children nodes, passing the correctly updated running sum to them. */ func calculateBranchSums(node *BinaryTree, runningSum int, sums *[]int) { if node == nil { return } runningSum += node.Value if node.Left == nil && node.Right == nil { *sums = append(*sums, runningSum) } calculateBranchSums(node.Left, runningSum, sums) calculateBranchSums(node.Right, runningSum, sums) } ================================================ FILE: Trees/Binary Trees/build_tree_preorder.cpp ================================================ // Pre-Order Traversal of a Binary-Tree // Program Author : Abhisek Kumar Gupta #include using namespace std; class Node{ public: int data; Node *left; Node *right; Node(int x){ data = x; left = NULL; right = NULL; } }; Node* build_binary_tree(){ int data; cin >> data; if(data == -1) return NULL; Node* root = new Node(data); root->left = build_binary_tree(); root->right = build_binary_tree(); return root; } void print_binary_tree(Node* root){ if(root == NULL) return; cout << root->data << "->"; print_binary_tree(root->left); print_binary_tree(root->right); } int main(){ Node* root = build_binary_tree(); print_binary_tree(root); } ================================================ FILE: Trees/Binary Trees/calculate_size.go ================================================ // Size of binary tree package main type BinaryTreeNode struct { left *BinaryTreeNode data int right *BinaryTreeNode } // Time Complexity: O(n). Space Complexity: O(n). // Approach: calculate the size of left and right subtree recursively // add 1 (curr node) and return to its parent func Size(root *BinaryTreeNode) int { if root == nil { return 0 } else { return Size(root.left) + 1 + Size(root.right) } } // Time Complexity: O(n). Space Complexity: O(n). // Approach: use level order traversal and count nodes func SizeWithoutUsingRecursion(root *BinaryTreeNode) int { if root == nil { return 0 } var result int queue := []*BinaryTreeNode{root} for len(queue) > 0 { qlen := len(queue) //var level []int for i := 0; i < qlen; i++ { node := queue[0] result++ //level = append(level, node.data) queue = queue[1:] if node.left != nil { queue = append(queue, node.left) } if node.right != nil { queue = append(queue, node.right) } } } return result } ================================================ FILE: Trees/Binary Trees/count_nodes.cpp ================================================ // Binary Tree : Count Number of Nodes // Program Author : Abhisek Kumar Gupta /* 40 / \ 10 30 / \ / \ 5 -1 -1 28 / \ / \ 1 -1 15 20 / \ /\ /\ 1 -1 -1 -1 -1 -1 /\ -1 -1 Input : 40 10 5 1 1 -1 -1 -1 -1 -1 30 -1 28 15 -1 -1 20 -1 -1 Output : 9 */ #include using namespace std; class Node{ public: int data; Node* left; Node* right; Node(int d){ data = d; } }; Node* build_binary_tree(){ int data; cin >> data; if(data == -1){ return NULL; } Node* root = new Node(data); root->left = build_binary_tree(); root->right = build_binary_tree(); return root; } int count_number_of_nodes(Node* root){ if(root == NULL) return 0; return 1 + count_number_of_nodes(root->left) + count_number_of_nodes(root->right); } int main(){ Node* root = build_binary_tree(); int number_of_nodes = count_number_of_nodes(root); cout << number_of_nodes; return 0; } ================================================ FILE: Trees/Binary Trees/delete.go ================================================ // Delete binary tree package main type BinaryTreeNode struct { left *BinaryTreeNode data int right *BinaryTreeNode } // Time Complexity: O(n). Space Complexity: O(n). // Approach: before deleting parent node, delete all its children nodes // using post order traversal we can solve this problem func DeleteTree(root *BinaryTreeNode) *BinaryTreeNode { if root == nil { return nil } // delete both subtrees root.left = DeleteTree(root.left) root.right = DeleteTree(root.right) // delete current node after deleting subtrees root = nil return root } ================================================ FILE: Trees/Binary Trees/dfs.cpp ================================================ // Implementation of Depth First Search /* This code demonstrates a basic implementation of Depth-First Search (DFS) on a graph represented by nodes. It uses a recursive approach to traverse the graph in a depth-first manner, printing the values of the visited nodes. The algorithm maintains a set of visited nodes to avoid visiting the same node multiple times. The DFS function serves as the entry point to start the DFS traversal, and the dfsHelper function recursively visits each node and its children. Sample Input : // 1 // / \ // 2 3 // / \ / \ // 4 5 6 7 Output : 1 2 4 5 3 6 7 The time complexity of Depth-First Search (DFS) on a graph is O(V + E), where V represents the number of vertices (nodes) in the graph and E represents the number of edges. In the worst case, DFS may visit all vertices and edges of the graph. The space complexity of DFS is determined by the maximum depth of the recursion stack. In the case of a tree-like structure, where each node has only one child, the maximum depth is equal to the height of the tree. Therefore, the space complexity of DFS on such a tree-like structure is O(H), where H represents the height of the tree. In the worst case, where the graph is a linear structure, the height of the tree is equal to the number of vertices, so the space complexity becomes O(V). */ #include #include #include using namespace std; // Node represents a node in a graph. struct Node { int value; vector children; }; // DFS traverses the graph using Depth-First Search starting from the given node. void DFS(Node* node) { // Create a set to keep track of visited nodes. unordered_map visited; // Call the recursive helper function to perform DFS. dfsHelper(node, visited); } // dfsHelper is a recursive function that performs Depth-First Search on the graph. void dfsHelper(Node* node, unordered_map& visited) { // Mark the current node as visited. visited[node] = true; // Process the current node (print its value in this case). cout << node->value << endl; // Traverse the children of the current node. for (Node* child : node->children) { // If the child node has not been visited, recursively call dfsHelper on it. if (!visited[child]) { dfsHelper(child, visited); } } } int main() { // Create a sample graph. // 1 // / \ // 2 3 // / \ / \ // 4 5 6 7 Node node1{1}; Node node2{2}; Node node3{3}; Node node4{4}; Node node5{5}; Node node6{6}; Node node7{7}; node1.children = {&node2, &node3}; node2.children = {&node4, &node5}; node3.children = {&node6, &node7}; // Perform DFS starting from node1. cout << "Depth-First Search:" << endl; DFS(&node1); return 0; } ================================================ FILE: Trees/Binary Trees/dfs.go ================================================ // Implementation of Depth First Search /* This code demonstrates a basic implementation of Depth-First Search (DFS) on a graph represented by nodes. It uses a recursive approach to traverse the graph in a depth-first manner, printing the values of the visited nodes. The algorithm maintains a set of visited nodes to avoid visiting the same node multiple times. The DFS function serves as the entry point to start the DFS traversal, and the dfsHelper function recursively visits each node and its children. Sample Input : // 1 // / \ // 2 3 // / \ / \ // 4 5 6 7 Output : 1 2 4 5 3 6 7 The time complexity of Depth-First Search (DFS) on a graph is O(V + E), where V represents the number of vertices (nodes) in the graph and E represents the number of edges. In the worst case, DFS may visit all vertices and edges of the graph. The space complexity of DFS is determined by the maximum depth of the recursion stack. In the case of a tree-like structure, where each node has only one child, the maximum depth is equal to the height of the tree. Therefore, the space complexity of DFS on such a tree-like structure is O(H), where H represents the height of the tree. In the worst case, where the graph is a linear structure, the height of the tree is equal to the number of vertices, so the space complexity becomes O(V). */ package main import "fmt" // Node represents a node in a graph. type Node struct { Value int Children []*Node } // DFS traverses the graph using Depth-First Search starting from the given node. func DFS(node *Node) { // Create a set to keep track of visited nodes. visited := make(map[*Node]bool) // Call the recursive helper function to perform DFS. dfsHelper(node, visited) } // dfsHelper is a recursive function that performs Depth-First Search on the graph. func dfsHelper(node *Node, visited map[*Node]bool) { // Mark the current node as visited. visited[node] = true // Process the current node (print its value in this case). fmt.Println(node.Value) // Traverse the children of the current node. for _, child := range node.Children { // If the child node has not been visited, recursively call dfsHelper on it. if !visited[child] { dfsHelper(child, visited) } } } func main() { // Create a sample graph. // 1 // / \ // 2 3 // / \ / \ // 4 5 6 7 node1 := &Node{Value: 1} node2 := &Node{Value: 2} node3 := &Node{Value: 3} node4 := &Node{Value: 4} node5 := &Node{Value: 5} node6 := &Node{Value: 6} node7 := &Node{Value: 7} node1.Children = []*Node{node2, node3} node2.Children = []*Node{node4, node5} node3.Children = []*Node{node6, node7} // Perform DFS starting from node1. fmt.Println("Depth-First Search:") DFS(node1) } ================================================ FILE: Trees/Binary Trees/dfs.java ================================================ // Implementation of Depth First Search /* This code demonstrates a basic implementation of Depth-First Search (DFS) on a graph represented by nodes. It uses a recursive approach to traverse the graph in a depth-first manner, printing the values of the visited nodes. The algorithm maintains a set of visited nodes to avoid visiting the same node multiple times. The DFS function serves as the entry point to start the DFS traversal, and the dfsHelper function recursively visits each node and its children. Sample Input : // 1 // / \ // 2 3 // / \ / \ // 4 5 6 7 Output : 1 2 4 5 3 6 7 The time complexity of Depth-First Search (DFS) on a graph is O(V + E), where V represents the number of vertices (nodes) in the graph and E represents the number of edges. In the worst case, DFS may visit all vertices and edges of the graph. The space complexity of DFS is determined by the maximum depth of the recursion stack. In the case of a tree-like structure, where each node has only one child, the maximum depth is equal to the height of the tree. Therefore, the space complexity of DFS on such a tree-like structure is O(H), where H represents the height of the tree. In the worst case, where the graph is a linear structure, the height of the tree is equal to the number of vertices, so the space complexity becomes O(V). */ import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; // Node represents a node in a graph. class Node { int value; List children; public Node(int value) { this.value = value; this.children = new ArrayList<>(); } } // DFS traverses the graph using Depth-First Search starting from the given node. class DepthFirstSearch { public static void DFS(Node node) { // Create a set to keep track of visited nodes. Set visited = new HashSet<>(); // Call the recursive helper function to perform DFS. dfsHelper(node, visited); } // dfsHelper is a recursive function that performs Depth-First Search on the graph. private static void dfsHelper(Node node, Set visited) { // Mark the current node as visited. visited.add(node); // Process the current node (print its value in this case). System.out.println(node.value); // Traverse the children of the current node. for (Node child : node.children) { // If the child node has not been visited, recursively call dfsHelper on it. if (!visited.contains(child)) { dfsHelper(child, visited); } } } } public class Main { public static void main(String[] args) { // Create a sample graph. // 1 // / \ // 2 3 // / \ / \ // 4 5 6 7 Node node1 = new Node(1); Node node2 = new Node(2); Node node3 = new Node(3); Node node4 = new Node(4); Node node5 = new Node(5); Node node6 = new Node(6); Node node7 = new Node(7); node1.children.add(node2); node1.children.add(node3); node2.children.add(node4); node2.children.add(node5); node3.children.add(node6); node3.children.add(node7); // Perform DFS starting from node1. System.out.println("Depth-First Search:"); DepthFirstSearch.DFS(node1); } } ================================================ FILE: Trees/Binary Trees/dfs.js ================================================ // Implementation of Depth First Search /* This code demonstrates a basic implementation of Depth-First Search (DFS) on a graph represented by nodes. It uses a recursive approach to traverse the graph in a depth-first manner, printing the values of the visited nodes. The algorithm maintains a set of visited nodes to avoid visiting the same node multiple times. The DFS function serves as the entry point to start the DFS traversal, and the dfsHelper function recursively visits each node and its children. Sample Input : // 1 // / \ // 2 3 // / \ / \ // 4 5 6 7 Output : 1 2 4 5 3 6 7 The time complexity of Depth-First Search (DFS) on a graph is O(V + E), where V represents the number of vertices (nodes) in the graph and E represents the number of edges. In the worst case, DFS may visit all vertices and edges of the graph. The space complexity of DFS is determined by the maximum depth of the recursion stack. In the case of a tree-like structure, where each node has only one child, the maximum depth is equal to the height of the tree. Therefore, the space complexity of DFS on such a tree-like structure is O(H), where H represents the height of the tree. In the worst case, where the graph is a linear structure, the height of the tree is equal to the number of vertices, so the space complexity becomes O(V). */ // Node represents a node in a graph. class Node { constructor(value) { this.value = value; this.children = []; } } // DFS traverses the graph using Depth-First Search starting from the given node. function DFS(node) { // Create a set to keep track of visited nodes. let visited = new Set(); // Call the recursive helper function to perform DFS. dfsHelper(node, visited); } // dfsHelper is a recursive function that performs Depth-First Search on the graph. function dfsHelper(node, visited) { // Mark the current node as visited. visited.add(node); // Process the current node (print its value in this case). console.log(node.value); // Traverse the children of the current node. for (let child of node.children) { // If the child node has not been visited, recursively call dfsHelper on it. if (!visited.has(child)) { dfsHelper(child, visited); } } } // Create a sample graph. // 1 // / \ // 2 3 // / \ / \ // 4 5 6 7 let node1 = new Node(1); let node2 = new Node(2); let node3 = new Node(3); let node4 = new Node(4); let node5 = new Node(5); let node6 = new Node(6); let node7 = new Node(7); node1.children = [node2, node3]; node2.children = [node4, node5]; node3.children = [node6, node7]; // Perform DFS starting from node1. console.log("Depth-First Search:"); DFS(node1); ================================================ FILE: Trees/Binary Trees/dfs.py ================================================ ''' Implementation of Depth First Search This code demonstrates a basic implementation of Depth-First Search (DFS) on a graph represented by nodes. It uses a recursive approach to traverse the graph in a depth-first manner, printing the values of the visited nodes. The algorithm maintains a set of visited nodes to avoid visiting the same node multiple times. The DFS function serves as the entry point to start the DFS traversal, and the dfsHelper function recursively visits each node and its children. Sample Input : // 1 // / \ // 2 3 // / \ / \ // 4 5 6 7 Output : 1 2 4 5 3 6 7 The time complexity of Depth-First Search (DFS) on a graph is O(V + E), where V represents the number of vertices (nodes) in the graph and E represents the number of edges. In the worst case, DFS may visit all vertices and edges of the graph. The space complexity of DFS is determined by the maximum depth of the recursion stack. In the case of a tree-like structure, where each node has only one child, the maximum depth is equal to the height of the tree. Therefore, the space complexity of DFS on such a tree-like structure is O(H), where H represents the height of the tree. In the worst case, where the graph is a linear structure, the height of the tree is equal to the number of vertices, so the space complexity becomes O(V). ''' # Node represents a node in a graph. class Node: def __init__(self, value): self.value = value self.children = [] # DFS traverses the graph using Depth-First Search starting from the given node. def DFS(node): # Create a set to keep track of visited nodes. visited = set() # Call the recursive helper function to perform DFS. dfsHelper(node, visited) # dfsHelper is a recursive function that performs Depth-First Search on the graph. def dfsHelper(node, visited): # Mark the current node as visited. visited.add(node) # Process the current node (print its value in this case). print(node.value) # Traverse the children of the current node. for child in node.children: # If the child node has not been visited, recursively call dfsHelper on it. if child not in visited: dfsHelper(child, visited) # Create a sample graph. # 1 # / \ # 2 3 # / \ / \ # 4 5 6 7 node1 = Node(1) node2 = Node(2) node3 = Node(3) node4 = Node(4) node5 = Node(5) node6 = Node(6) node7 = Node(7) node1.children = [node2, node3] node2.children = [node4, node5] node3.children = [node6, node7] # Perform DFS starting from node1. print("Depth-First Search:") DFS(node1) ================================================ FILE: Trees/Binary Trees/diameter.cpp ================================================ /* Write a function that takes in a Binary Tree and returns its diameter. The diameter of a binary tree is defined as the length of its longest path, even if that path doesn't pass through the root of the tree. Sample Input : 1 / \ 3 2 / \ 7 4 / \ 8 5 / \ 9 6 Output: 6 Diameter being 9 -> 8 -> 7 -> 3 -> 4 -> 5 -> 6 Explanation: This code calculates the diameter of a binary tree, which is defined as the length of the longest path between any two nodes in the tree. The BinaryTreeDiameter function takes the root of the binary tree as input and returns the diameter. The getTreeInfo function is a helper function that recursively calculates the height and diameter of the binary tree. It takes a node of the binary tree as input and returns a TreeInfo struct containing the height and diameter of the tree. In the getTreeInfo function: The base case is when the tree is nil, indicating an empty tree. In this case, it returns a TreeInfo with height 0 and diameter 0. The height and diameter of the left and right subtrees are calculated recursively by calling getTreeInfo on the left and right child nodes. The longest path passing through the root node is determined by adding the heights of the left and right subtrees. The maximum diameter seen so far is calculated by taking the maximum of the diameters of the left and right subtrees. The current diameter is determined by taking the maximum among the longest path through the root and the maximum diameter seen so far. The current height is calculated by taking the maximum height among the left and right subtrees and adding 1. Finally, the function returns a TreeInfo struct with the current height and diameter. The max function is a helper function that returns the maximum of two integers. Overall, the code effectively calculates the diameter of a binary tree by recursively calculating the height and diameter of the tree and considering the longest path passing through the root. Average case: Time Complexity O(n) when the tree is balanced Space complexity: O(h) where n is the number of nodes in the Binary Tree and h is the height of the Binary Tree */ #include struct BinaryTree { int value; BinaryTree* left; BinaryTree* right; }; struct TreeInfo { int height; int diameter; }; // Calculates the diameter of a binary tree. int BinaryTreeDiameter(BinaryTree* tree) { return getTreeInfo(tree).diameter; } // Recursively calculates the height and diameter of the binary tree. TreeInfo getTreeInfo(BinaryTree* tree) { // Base case: If the tree is nullptr, return height 0 and diameter 0. if (tree == nullptr) { return {0, 0}; } // Recursively calculate the height and diameter of the left and right subtrees. TreeInfo leftTreeInfo = getTreeInfo(tree->left); TreeInfo rightTreeInfo = getTreeInfo(tree->right); // Calculate the longest path passing through the root node. int longestPathThroughRoot = leftTreeInfo.height + rightTreeInfo.height; // Calculate the maximum diameter seen so far. int maxDiameterSoFar = std::max(leftTreeInfo.diameter, rightTreeInfo.diameter); // Calculate the current diameter, which is the maximum among the longest path through root and max diameter so far. int currentDiameter = std::max(longestPathThroughRoot, maxDiameterSoFar); // Calculate the current height, which is the maximum height among the left and right subtrees plus 1. int currentHeight = 1 + std::max(leftTreeInfo.height, rightTreeInfo.height); // Return the current height and diameter as the tree information. return {currentHeight, currentDiameter}; } // Returns the maximum of two integers. int max(int a, int b) { return (a > b) ? a : b; } int main() { // Test the BinaryTreeDiameter function with a sample binary tree BinaryTree* tree = new BinaryTree{1, new BinaryTree{2, new BinaryTree{4, nullptr, nullptr}, new BinaryTree{5, nullptr, nullptr}}, new BinaryTree{3, nullptr, new BinaryTree{6, new BinaryTree{7, nullptr, nullptr}, nullptr}}}; int diameter = BinaryTreeDiameter(tree); std::cout << "Diameter of the binary tree: " << diameter << std::endl; delete tree; return 0; } ================================================ FILE: Trees/Binary Trees/diameter.go ================================================ /* Write a function that takes in a Binary Tree and returns its diameter. The diameter of a binary tree is defined as the length of its longest path, even if that path doesn't pass through the root of the tree. Sample Input : 1 / \ 3 2 / \ 7 4 / \ 8 5 / \ 9 6 Output: 6 Diameter being 9 -> 8 -> 7 -> 3 -> 4 -> 5 -> 6 Explanation: This code calculates the diameter of a binary tree, which is defined as the length of the longest path between any two nodes in the tree. The BinaryTreeDiameter function takes the root of the binary tree as input and returns the diameter. The getTreeInfo function is a helper function that recursively calculates the height and diameter of the binary tree. It takes a node of the binary tree as input and returns a TreeInfo struct containing the height and diameter of the tree. In the getTreeInfo function: The base case is when the tree is nil, indicating an empty tree. In this case, it returns a TreeInfo with height 0 and diameter 0. The height and diameter of the left and right subtrees are calculated recursively by calling getTreeInfo on the left and right child nodes. The longest path passing through the root node is determined by adding the heights of the left and right subtrees. The maximum diameter seen so far is calculated by taking the maximum of the diameters of the left and right subtrees. The current diameter is determined by taking the maximum among the longest path through the root and the maximum diameter seen so far. The current height is calculated by taking the maximum height among the left and right subtrees and adding 1. Finally, the function returns a TreeInfo struct with the current height and diameter. The max function is a helper function that returns the maximum of two integers. Overall, the code effectively calculates the diameter of a binary tree by recursively calculating the height and diameter of the tree and considering the longest path passing through the root. Average case: Time Complexity O(n) when the tree is balanced Space complexity: O(h) where n is the number of nodes in the Binary Tree and h is the height of the Binary Tree */ package main // This is an input class. Do not edit. type BinaryTree struct { Value int Left *BinaryTree Right *BinaryTree } type TreeInfo struct { height int diameter int } // Calculates the diameter of a binary tree. func BinaryTreeDiameter(tree *BinaryTree) int { return getTreeInfo(tree).diameter } // Recursively calculates the height and diameter of the binary tree. func getTreeInfo(tree *BinaryTree) TreeInfo { // Base case: If the tree is nil, return height 0 and diameter 0. if tree == nil { return TreeInfo{0, 0} } // Recursively calculate the height and diameter of the left and right subtrees. leftTreeInfo := getTreeInfo(tree.Left) rightTreeInfo := getTreeInfo(tree.Right) // Calculate the longest path passing through the root node. longestPathThroughRoot := leftTreeInfo.height + rightTreeInfo.height // Calculate the maximum diameter seen so far. maxDiameterSoFar := max(leftTreeInfo.diameter, rightTreeInfo.diameter) // Calculate the current diameter, which is the maximum among the longest path through root and max diameter so far. currentDiameter := max(longestPathThroughRoot, maxDiameterSoFar) // Calculate the current height, which is the maximum height among the left and right subtrees plus 1. currentHeight := 1 + max(leftTreeInfo.height, rightTreeInfo.height) // Return the current height and diameter as the tree information. return TreeInfo{currentHeight, currentDiameter} } // Returns the maximum of two integers. func max(a, b int) int { if a > b { return a } return b } ================================================ FILE: Trees/Binary Trees/diameter.java ================================================ /* Write a function that takes in a Binary Tree and returns its diameter. The diameter of a binary tree is defined as the length of its longest path, even if that path doesn't pass through the root of the tree. Sample Input : 1 / \ 3 2 / \ 7 4 / \ 8 5 / \ 9 6 Output: 6 Diameter being 9 -> 8 -> 7 -> 3 -> 4 -> 5 -> 6 Explanation: This code calculates the diameter of a binary tree, which is defined as the length of the longest path between any two nodes in the tree. The BinaryTreeDiameter function takes the root of the binary tree as input and returns the diameter. The getTreeInfo function is a helper function that recursively calculates the height and diameter of the binary tree. It takes a node of the binary tree as input and returns a TreeInfo struct containing the height and diameter of the tree. In the getTreeInfo function: The base case is when the tree is nil, indicating an empty tree. In this case, it returns a TreeInfo with height 0 and diameter 0. The height and diameter of the left and right subtrees are calculated recursively by calling getTreeInfo on the left and right child nodes. The longest path passing through the root node is determined by adding the heights of the left and right subtrees. The maximum diameter seen so far is calculated by taking the maximum of the diameters of the left and right subtrees. The current diameter is determined by taking the maximum among the longest path through the root and the maximum diameter seen so far. The current height is calculated by taking the maximum height among the left and right subtrees and adding 1. Finally, the function returns a TreeInfo struct with the current height and diameter. The max function is a helper function that returns the maximum of two integers. Overall, the code effectively calculates the diameter of a binary tree by recursively calculating the height and diameter of the tree and considering the longest path passing through the root. Average case: Time Complexity O(n) when the tree is balanced Space complexity: O(h) where n is the number of nodes in the Binary Tree and h is the height of the Binary Tree */ public class BinaryTree { int value; BinaryTree left; BinaryTree right; public BinaryTree(int value) { this.value = value; this.left = null; this.right = null; } } class TreeInfo { int height; int diameter; public TreeInfo(int height, int diameter) { this.height = height; this.diameter = diameter; } } public class Main { // Calculates the diameter of a binary tree. public static int binaryTreeDiameter(BinaryTree tree) { return getTreeInfo(tree).diameter; } // Recursively calculates the height and diameter of the binary tree. private static TreeInfo getTreeInfo(BinaryTree tree) { // Base case: If the tree is null, return height 0 and diameter 0. if (tree == null) { return new TreeInfo(0, 0); } // Recursively calculate the height and diameter of the left and right subtrees. TreeInfo leftTreeInfo = getTreeInfo(tree.left); TreeInfo rightTreeInfo = getTreeInfo(tree.right); // Calculate the longest path passing through the root node. int longestPathThroughRoot = leftTreeInfo.height + rightTreeInfo.height; // Calculate the maximum diameter seen so far. int maxDiameterSoFar = Math.max(leftTreeInfo.diameter, rightTreeInfo.diameter); // Calculate the current diameter, which is the maximum among the longest path through root and max diameter so far. int currentDiameter = Math.max(longestPathThroughRoot, maxDiameterSoFar); // Calculate the current height, which is the maximum height among the left and right subtrees plus 1. int currentHeight = Math.max(leftTreeInfo.height, rightTreeInfo.height) + 1; // Return the current height and diameter as the tree information. return new TreeInfo(currentHeight, currentDiameter); } public static void main(String[] args) { // Example usage BinaryTree tree = new BinaryTree(1); tree.left = new BinaryTree(2); tree.right = new BinaryTree(3); tree.left.left = new BinaryTree(4); tree.left.right = new BinaryTree(5); int diameter = binaryTreeDiameter(tree); System.out.println("Diameter of the binary tree: " + diameter); } } ================================================ FILE: Trees/Binary Trees/diameter.js ================================================ /* Write a function that takes in a Binary Tree and returns its diameter. The diameter of a binary tree is defined as the length of its longest path, even if that path doesn't pass through the root of the tree. Sample Input : 1 / \ 3 2 / \ 7 4 / \ 8 5 / \ 9 6 Output: 6 Diameter being 9 -> 8 -> 7 -> 3 -> 4 -> 5 -> 6 Explanation: This code calculates the diameter of a binary tree, which is defined as the length of the longest path between any two nodes in the tree. The BinaryTreeDiameter function takes the root of the binary tree as input and returns the diameter. The getTreeInfo function is a helper function that recursively calculates the height and diameter of the binary tree. It takes a node of the binary tree as input and returns a TreeInfo struct containing the height and diameter of the tree. In the getTreeInfo function: The base case is when the tree is nil, indicating an empty tree. In this case, it returns a TreeInfo with height 0 and diameter 0. The height and diameter of the left and right subtrees are calculated recursively by calling getTreeInfo on the left and right child nodes. The longest path passing through the root node is determined by adding the heights of the left and right subtrees. The maximum diameter seen so far is calculated by taking the maximum of the diameters of the left and right subtrees. The current diameter is determined by taking the maximum among the longest path through the root and the maximum diameter seen so far. The current height is calculated by taking the maximum height among the left and right subtrees and adding 1. Finally, the function returns a TreeInfo struct with the current height and diameter. The max function is a helper function that returns the maximum of two integers. Overall, the code effectively calculates the diameter of a binary tree by recursively calculating the height and diameter of the tree and considering the longest path passing through the root. Average case: Time Complexity O(n) when the tree is balanced Space complexity: O(h) where n is the number of nodes in the Binary Tree and h is the height of the Binary Tree */ class BinaryTree { constructor(value) { this.value = value; this.left = null; this.right = null; } } class TreeInfo { constructor(height, diameter) { this.height = height; this.diameter = diameter; } } function binaryTreeDiameter(tree) { return getTreeInfo(tree).diameter; } function getTreeInfo(tree) { // Base case: If the tree is null, return height 0 and diameter 0. if (tree === null) { return new TreeInfo(0, 0); } // Recursively calculate the height and diameter of the left and right subtrees. const leftTreeInfo = getTreeInfo(tree.left); const rightTreeInfo = getTreeInfo(tree.right); // Calculate the longest path passing through the root node. const longestPathThroughRoot = leftTreeInfo.height + rightTreeInfo.height; // Calculate the maximum diameter seen so far. const maxDiameterSoFar = Math.max( leftTreeInfo.diameter, rightTreeInfo.diameter ); // Calculate the current diameter, which is the maximum among the longest path through root and max diameter so far. const currentDiameter = Math.max(longestPathThroughRoot, maxDiameterSoFar); // Calculate the current height, which is the maximum height among the left and right subtrees plus 1. const currentHeight = Math.max(leftTreeInfo.height, rightTreeInfo.height) + 1; // Return the current height and diameter as the tree information. return new TreeInfo(currentHeight, currentDiameter); } // Example usage const tree = new BinaryTree(1); tree.left = new BinaryTree(2); tree.right = new BinaryTree(3); tree.left.left = new BinaryTree(4); tree.left.right = new BinaryTree(5); const diameter = binaryTreeDiameter(tree); console.log("Diameter of the binary tree:", diameter); ================================================ FILE: Trees/Binary Trees/diameter.py ================================================ ''' Write a function that takes in a Binary Tree and returns its diameter. The diameter of a binary tree is defined as the length of its longest path, even if that path doesn't pass through the root of the tree. Sample Input : 1 / \ 3 2 / \ 7 4 / \ 8 5 / \ 9 6 Output: 6 Diameter being 9 -> 8 -> 7 -> 3 -> 4 -> 5 -> 6 Explanation: This code calculates the diameter of a binary tree, which is defined as the length of the longest path between any two nodes in the tree. The BinaryTreeDiameter function takes the root of the binary tree as input and returns the diameter. The getTreeInfo function is a helper function that recursively calculates the height and diameter of the binary tree. It takes a node of the binary tree as input and returns a TreeInfo struct containing the height and diameter of the tree. In the getTreeInfo function: The base case is when the tree is nil, indicating an empty tree. In this case, it returns a TreeInfo with height 0 and diameter 0. The height and diameter of the left and right subtrees are calculated recursively by calling getTreeInfo on the left and right child nodes. The longest path passing through the root node is determined by adding the heights of the left and right subtrees. The maximum diameter seen so far is calculated by taking the maximum of the diameters of the left and right subtrees. The current diameter is determined by taking the maximum among the longest path through the root and the maximum diameter seen so far. The current height is calculated by taking the maximum height among the left and right subtrees and adding 1. Finally, the function returns a TreeInfo struct with the current height and diameter. The max function is a helper function that returns the maximum of two integers. Overall, the code effectively calculates the diameter of a binary tree by recursively calculating the height and diameter of the tree and considering the longest path passing through the root. Average case: Time Complexity O(n) when the tree is balanced Space complexity: O(h) where n is the number of nodes in the Binary Tree and h is the height of the Binary Tree ''' class BinaryTree: def __init__(self, value): self.value = value self.left = None self.right = None class TreeInfo: def __init__(self, height, diameter): self.height = height self.diameter = diameter def binary_tree_diameter(tree): return get_tree_info(tree).diameter def get_tree_info(tree): # Base case: If the tree is None, return height 0 and diameter 0. if tree is None: return TreeInfo(0, 0) # Recursively calculate the height and diameter of the left and right subtrees. left_tree_info = get_tree_info(tree.left) right_tree_info = get_tree_info(tree.right) # Calculate the longest path passing through the root node. longest_path_through_root = left_tree_info.height + right_tree_info.height # Calculate the maximum diameter seen so far. max_diameter_so_far = max(left_tree_info.diameter, right_tree_info.diameter) # Calculate the current diameter, which is the maximum among the longest path through root and max diameter so far. current_diameter = max(longest_path_through_root, max_diameter_so_far) # Calculate the current height, which is the maximum height among the left and right subtrees plus 1. current_height = max(left_tree_info.height, right_tree_info.height) + 1 # Return the current height and diameter as the tree information. return TreeInfo(current_height, current_diameter) # Example usage tree = BinaryTree(1) tree.left = BinaryTree(2) tree.right = BinaryTree(3) tree.left.left = BinaryTree(4) tree.left.right = BinaryTree(5) diameter = binary_tree_diameter(tree) print("Diameter of the binary tree:", diameter) ================================================ FILE: Trees/Binary Trees/find_branch_sum.go ================================================ /* Write a function that takes in a Binary Tree and returns a list of its branch sums ordered from leftmost branch sum to rightmost branch sum. A branch sum is the sum of all values in a Binary Tree branch. A Binary Tree branch is a path of nodes in a tree that starts at the root node and ends at any leaf node. Sample Input 1 / \ 2 3 / \ / \ 4 5 6 7 / \ / 8 9 10 Output:[15, 16, 18, 10, 11] */ /* As you recursively traverse the tree, if you reach a leaf node (a node with no "left" or "right" Binary Tree nodes), add the relevant running sum that you've calculated to a list of sums (which you'll also have to pass to the recursive function). If you reach a node that isn't a leaf node, keep recursively traversing its children nodes, passing the correctly updated running sum to them. Time and Space complexity : O(n) time | O(n) space - where n is the number of nodes in the Binary Tree */ package main type BinaryTree struct { Value int Left *BinaryTree Right *BinaryTree } func BranchSums(root *BinaryTree) []int { sums := []int{} calculateBranchSums(root, 0, &sums) return sums } func calculateBranchSums(node *BinaryTree, runningSum int, sums *[]int) { if node == nil { return } runningSum += node.Value if node.Left == nil && node.Right == nil { *sums = append(*sums, runningSum) } calculateBranchSums(node.Left, runningSum, sums) calculateBranchSums(node.Right, runningSum, sums) } ================================================ FILE: Trees/Binary Trees/find_max.go ================================================ // Find max in Binary tree package main import "math" type BinaryTreeNode struct { left *BinaryTreeNode data int right *BinaryTreeNode } // Time Complexity: O(n). Space Complexity: O(n). // Approach: find maximum in left sub tree, find maximum in right subtree // compare them with root data and select the one which is giving the max value // recursive appraoch func FindMax(root *BinaryTreeNode) int { max := math.MinInt32 if root != nil { rootVal := root.data left := FindMax(root.left) right := FindMax(root.right) if left > max { max = left } else { max = right } if rootVal > max { max = rootVal } } return max } // Time Complexity: O(n). Space Complexity: O(n). // Approach: Using level order traversal observe the elements data func FindMaxWithoutRecursion(root *BinaryTreeNode) int { max := math.MinInt32 if root == nil { return max } queue := []*BinaryTreeNode{root} for len(queue) > 0 { qlen := len(queue) for i := 0; i < qlen; i++ { node := queue[0] if node.data > max { max = node.data } queue = queue[1:] if node.left != nil { queue = append(queue, node.left) } if node.right != nil { queue = append(queue, node.right) } } } return max } ================================================ FILE: Trees/Binary Trees/height.cpp ================================================ // Compute Height of a Binary-Tree // Program Author : Abhisek Kumar Gupta /* 40 / \ 10 30 / \ / \ 5 -1 -1 28 / \ / \ 1 -1 15 20 / \ /\ /\ 1 -1 -1 -1 -1 -1 /\ -1 -1 Input : 40 10 5 1 1 -1 -1 -1 -1 -1 30 -1 28 15 -1 -1 20 -1 -1 Output : 5 */ #include using namespace std; class Node{ public: int data; Node* left; Node* right; Node(int x){ data = x; } }; Node* build_binary_tree(){ int data; cin >> data; if(data == -1) return NULL; Node* root = new Node(data); root->left = build_binary_tree(); root->right = build_binary_tree(); return root; } int compute_height_of_binary_tree(Node* root){ if(root == NULL){ return 0; } int left_height = compute_height_of_binary_tree(root->left); int right_height = compute_height_of_binary_tree(root->right); return max(left_height, right_height) + 1; } int main(){ Node* root = build_binary_tree(); int height = compute_height_of_binary_tree(root); cout << "Height of Tree is " << height; return 0; } ================================================ FILE: Trees/Binary Trees/height.go ================================================ // Binary tree Find height package main type BinaryTreeNode struct { left *BinaryTreeNode data int right *BinaryTreeNode } // Time Complexity: O(n). Space Complexity: O(n). // Approach: Recursively calculate height of left and right subtrees of a node // and assign height to the node as max of heights of two children + 1 func Height(root *BinaryTreeNode) int { if root == nil { return 0 } else { // compute depth of each subtree leftHeight := Height(root.left) rightHeight := Height(root.right) if leftHeight > rightHeight { return leftHeight + 1 } else { return rightHeight + 1 } } } ================================================ FILE: Trees/Binary Trees/height_balanced_binary_tree.cpp ================================================ /* You're given the root node of a Binary Tree. Write a function that returns true if this Binary Tree is height balanced and false if it isn't. Explanation: The provided code is for checking whether a binary tree is height-balanced or not. Here's how it works: - The code defines a `BinaryTree` struct representing a node in a binary tree. Each node has a value and pointers to its left and right child nodes. - The code also defines a `TreeeInfo` struct to store information about a binary tree. It includes a boolean field `isBalanced` indicating whether the tree is balanced or not, and an integer field `height` representing the height of the tree. - The `HeightBalancedBinaryTree` function is the main function that checks if a binary tree is height-balanced. It takes the root of the tree as input and returns a boolean value indicating the balance status. - The `getTreeInfo` function is a helper function that recursively calculates the information of a binary tree. It takes a binary tree node as input and returns the `TreeeInfo` struct containing the balance status and height of the tree. - In the `getTreeInfo` function, there are two base cases: - If the current tree node is `nil`, it is considered balanced with height -1. - If the current tree node is not `nil`, the function recursively calculates the tree information of its left and right subtrees. - After getting the information of the left and right subtrees, the code checks if both subtrees are balanced (`isBalanced` field is `true`) and their height difference is at most 1. If so, the current tree is considered balanced. - The height of the current tree is calculated by taking the maximum height of the left and right subtrees and adding 1. - Finally, the `max` function is used to get the maximum of two integers, and the `abs` function is used to get the absolute value of an integer. To determine whether a binary tree is height-balanced, you can call the `HeightBalancedBinaryTree` function with the root of the tree. It will return `true` if the tree is balanced and `false` otherwise. The time complexity of the `HeightBalancedBinaryTree` function is O(N), where N is the number of nodes in the binary tree. This is because the function needs to traverse each node of the tree once to calculate the tree information. The space complexity of the `HeightBalancedBinaryTree` function is O(H), where H is the height of the binary tree. This is because the recursive calls to the `getTreeInfo` function will utilize the call stack, and the maximum depth of the recursive calls is equal to the height of the tree. Additionally, the space complexity of the `getTreeInfo` function itself is O(1) as it uses a constant amount of space for the `TreeeInfo` struct. Overall, the space complexity is determined by the height of the binary tree, and the time complexity is determined by the number of nodes in the binary tree. */ #include #include // Node class represents a node in a binary tree class BinaryTree { public: int value; BinaryTree* left; BinaryTree* right; BinaryTree(int value) { this->value = value; this->left = nullptr; this->right = nullptr; } }; // TreeInfo class represents the information of a binary tree, including its balance status and height class TreeInfo { public: bool isBalanced; int height; TreeInfo(bool isBalanced, int height) { this->isBalanced = isBalanced; this->height = height; } }; // heightBalancedBinaryTree checks if a binary tree is height-balanced bool heightBalancedBinaryTree(BinaryTree* tree) { // Retrieve the tree information using the helper function TreeInfo treeInfo = getTreeInfo(tree); // Return the balance status of the tree return treeInfo.isBalanced; } // getTreeInfo retrieves the information of a binary tree, including its balance status and height TreeInfo getTreeInfo(BinaryTree* tree) { // Base case: If the tree is nullptr, it is considered balanced with height -1 if (tree == nullptr) { return TreeInfo(true, -1); } // Recursively calculate the tree information of the left and right subtrees TreeInfo leftSubtreeInfo = getTreeInfo(tree->left); TreeInfo rightSubtreeInfo = getTreeInfo(tree->right); // Check if both left and right subtrees are balanced and their height difference is at most 1 bool isBalanced = leftSubtreeInfo.isBalanced && rightSubtreeInfo.isBalanced && std::abs(leftSubtreeInfo.height - rightSubtreeInfo.height) <= 1; // Calculate the height of the current tree by taking the maximum height of the left and right subtrees plus 1 int height = std::max(leftSubtreeInfo.height, rightSubtreeInfo.height) + 1; // Create and return the tree information return TreeInfo(isBalanced, height); } // Helper function to create a binary tree BinaryTree* createBinaryTree(int value) { return new BinaryTree(value); } // Main function int main() { // Create a sample binary tree // 1 // / \ // 2 3 // / \ / // 4 5 6 BinaryTree* node1 = createBinaryTree(1); BinaryTree* node2 = createBinaryTree(2); BinaryTree* node3 = createBinaryTree(3); BinaryTree* node4 = createBinaryTree(4); BinaryTree* node5 = createBinaryTree(5); BinaryTree* node6 = createBinaryTree(6); node1->left = node2; node1->right = node3; node2->left = node4; node2->right = node5; node3->left = node6; // Check if the binary tree is height-balanced bool isBalanced = heightBalancedBinaryTree(node1); // Print the result if (isBalanced) { std::cout << "The binary tree is height-balanced." << std::endl; } else { std::cout << "The binary tree is not height-balanced." << std::endl; } return 0; } ================================================ FILE: Trees/Binary Trees/height_balanced_binary_tree.go ================================================ /* You're given the root node of a Binary Tree. Write a function that returns true if this Binary Tree is height balanced and false if it isn't. Explanation: The provided code is for checking whether a binary tree is height-balanced or not. Here's how it works: - The code defines a `BinaryTree` struct representing a node in a binary tree. Each node has a value and pointers to its left and right child nodes. - The code also defines a `TreeeInfo` struct to store information about a binary tree. It includes a boolean field `isBalanced` indicating whether the tree is balanced or not, and an integer field `height` representing the height of the tree. - The `HeightBalancedBinaryTree` function is the main function that checks if a binary tree is height-balanced. It takes the root of the tree as input and returns a boolean value indicating the balance status. - The `getTreeInfo` function is a helper function that recursively calculates the information of a binary tree. It takes a binary tree node as input and returns the `TreeeInfo` struct containing the balance status and height of the tree. - In the `getTreeInfo` function, there are two base cases: - If the current tree node is `nil`, it is considered balanced with height -1. - If the current tree node is not `nil`, the function recursively calculates the tree information of its left and right subtrees. - After getting the information of the left and right subtrees, the code checks if both subtrees are balanced (`isBalanced` field is `true`) and their height difference is at most 1. If so, the current tree is considered balanced. - The height of the current tree is calculated by taking the maximum height of the left and right subtrees and adding 1. - Finally, the `max` function is used to get the maximum of two integers, and the `abs` function is used to get the absolute value of an integer. To determine whether a binary tree is height-balanced, you can call the `HeightBalancedBinaryTree` function with the root of the tree. It will return `true` if the tree is balanced and `false` otherwise. The time complexity of the `HeightBalancedBinaryTree` function is O(N), where N is the number of nodes in the binary tree. This is because the function needs to traverse each node of the tree once to calculate the tree information. The space complexity of the `HeightBalancedBinaryTree` function is O(H), where H is the height of the binary tree. This is because the recursive calls to the `getTreeInfo` function will utilize the call stack, and the maximum depth of the recursive calls is equal to the height of the tree. Additionally, the space complexity of the `getTreeInfo` function itself is O(1) as it uses a constant amount of space for the `TreeeInfo` struct. Overall, the space complexity is determined by the height of the binary tree, and the time complexity is determined by the number of nodes in the binary tree. */ package main import "fmt" // This is an input class. Do not edit. type BinaryTree struct { Value int Left *BinaryTree Right *BinaryTree } type TreeeInfo struct { isBalanced bool height int } // HeightBalancedBinaryTree checks if a binary tree is height-balanced. func HeightBalancedBinaryTree(tree *BinaryTree) bool { // Retrieve the tree information using the helper function. treeInfo := getTreeInfo(tree) // Return the balance status of the tree. return treeInfo.isBalanced } // getTreeInfo retrieves the information of a binary tree, including its balance status and height. func getTreeInfo(tree *BinaryTree) TreeeInfo { // Base case: If the tree is nil, it is considered balanced with height -1. if tree == nil { return TreeeInfo{isBalanced: true, height: -1} } // Recursively calculate the tree information of the left and right subtrees. leftSubtreeInfo := getTreeInfo(tree.Left) rightSubtreeInfo := getTreeInfo(tree.Right) // Check if both left and right subtrees are balanced and their height difference is at most 1. isBalanced := leftSubtreeInfo.isBalanced && rightSubtreeInfo.isBalanced && abs(leftSubtreeInfo.height - rightSubtreeInfo.height) <= 1 // Calculate the height of the current tree by taking the maximum height of the left and right subtrees plus 1. height := max(leftSubtreeInfo.height, rightSubtreeInfo.height) + 1 // Create and return the tree information. return TreeeInfo{ isBalanced: isBalanced, height: height, } } // max returns the maximum of two integers. func max(a, b int) int { if a > b { return a } return b } // abs returns the absolute value of an integer. func abs(a int) int { if a < 0 { return -a } return a } func main() { // Create a binary tree tree := &BinaryTree{ Value: 1, Left: &BinaryTree{ Value: 2, Left: &BinaryTree{ Value: 4, }, Right: &BinaryTree{ Value: 5, }, }, Right: &BinaryTree{ Value: 3, Right: &BinaryTree{ Value: 6, Left: &BinaryTree{ Value: 7, }, }, }, } // Check if the binary tree is height-balanced isBalanced := HeightBalancedBinaryTree(tree) // Output the result if isBalanced { fmt.Println("The binary tree is height-balanced.") } else { fmt.Println("The binary tree is not height-balanced.") } } ================================================ FILE: Trees/Binary Trees/height_balanced_binary_tree.java ================================================ /* You're given the root node of a Binary Tree. Write a function that returns true if this Binary Tree is height balanced and false if it isn't. Explanation: The provided code is for checking whether a binary tree is height-balanced or not. Here's how it works: - The code defines a `BinaryTree` struct representing a node in a binary tree. Each node has a value and pointers to its left and right child nodes. - The code also defines a `TreeeInfo` struct to store information about a binary tree. It includes a boolean field `isBalanced` indicating whether the tree is balanced or not, and an integer field `height` representing the height of the tree. - The `HeightBalancedBinaryTree` function is the main function that checks if a binary tree is height-balanced. It takes the root of the tree as input and returns a boolean value indicating the balance status. - The `getTreeInfo` function is a helper function that recursively calculates the information of a binary tree. It takes a binary tree node as input and returns the `TreeeInfo` struct containing the balance status and height of the tree. - In the `getTreeInfo` function, there are two base cases: - If the current tree node is `nil`, it is considered balanced with height -1. - If the current tree node is not `nil`, the function recursively calculates the tree information of its left and right subtrees. - After getting the information of the left and right subtrees, the code checks if both subtrees are balanced (`isBalanced` field is `true`) and their height difference is at most 1. If so, the current tree is considered balanced. - The height of the current tree is calculated by taking the maximum height of the left and right subtrees and adding 1. - Finally, the `max` function is used to get the maximum of two integers, and the `abs` function is used to get the absolute value of an integer. To determine whether a binary tree is height-balanced, you can call the `HeightBalancedBinaryTree` function with the root of the tree. It will return `true` if the tree is balanced and `false` otherwise. The time complexity of the `HeightBalancedBinaryTree` function is O(N), where N is the number of nodes in the binary tree. This is because the function needs to traverse each node of the tree once to calculate the tree information. The space complexity of the `HeightBalancedBinaryTree` function is O(H), where H is the height of the binary tree. This is because the recursive calls to the `getTreeInfo` function will utilize the call stack, and the maximum depth of the recursive calls is equal to the height of the tree. Additionally, the space complexity of the `getTreeInfo` function itself is O(1) as it uses a constant amount of space for the `TreeeInfo` struct. Overall, the space complexity is determined by the height of the binary tree, and the time complexity is determined by the number of nodes in the binary tree. */ // Node class represents a node in a binary tree class BinaryTree { int value; BinaryTree left; BinaryTree right; BinaryTree(int value) { this.value = value; this.left = null; this.right = null; } } // TreeInfo class represents the information of a binary tree, including its balance status and height class TreeInfo { boolean isBalanced; int height; TreeInfo(boolean isBalanced, int height) { this.isBalanced = isBalanced; this.height = height; } } // heightBalancedBinaryTree checks if a binary tree is height-balanced class HeightBalancedBinaryTree { public static boolean heightBalancedBinaryTree(BinaryTree tree) { // Retrieve the tree information using the helper function TreeInfo treeInfo = getTreeInfo(tree); // Return the balance status of the tree return treeInfo.isBalanced; } // getTreeInfo retrieves the information of a binary tree, including its balance status and height private static TreeInfo getTreeInfo(BinaryTree tree) { // Base case: If the tree is null, it is considered balanced with height -1 if (tree == null) { return new TreeInfo(true, -1); } // Recursively calculate the tree information of the left and right subtrees TreeInfo leftSubtreeInfo = getTreeInfo(tree.left); TreeInfo rightSubtreeInfo = getTreeInfo(tree.right); // Check if both left and right subtrees are balanced and their height difference is at most 1 boolean isBalanced = leftSubtreeInfo.isBalanced && rightSubtreeInfo.isBalanced && Math.abs(leftSubtreeInfo.height - rightSubtreeInfo.height) <= 1; // Calculate the height of the current tree by taking the maximum height of the left and right subtrees plus 1 int height = Math.max(leftSubtreeInfo.height, rightSubtreeInfo.height) + 1; // Create and return the tree information return new TreeInfo(isBalanced, height); } // Helper function to create a binary tree private static BinaryTree createBinaryTree(int value) { return new BinaryTree(value); } // Main function public static void main(String[] args) { // Create a sample binary tree // 1 // / \ // 2 3 // / \ / // 4 5 6 BinaryTree node1 = createBinaryTree(1); BinaryTree node2 = createBinaryTree(2); BinaryTree node3 = createBinaryTree(3); BinaryTree node4 = createBinaryTree(4); BinaryTree node5 = createBinaryTree(5); BinaryTree node6 = createBinaryTree(6); node1.left = node2; node1.right = node3; node2.left = node4; node2.right = node5; node3.left = node6; // Check if the binary tree is height-balanced boolean isBalanced = heightBalancedBinaryTree(node1); // Print the result if (isBalanced) { System.out.println("The binary tree is height-balanced."); } else { System.out.println("The binary tree is not height-balanced."); } } } ================================================ FILE: Trees/Binary Trees/height_balanced_binary_tree.js ================================================ /* You're given the root node of a Binary Tree. Write a function that returns true if this Binary Tree is height balanced and false if it isn't. Explanation: The provided code is for checking whether a binary tree is height-balanced or not. Here's how it works: - The code defines a `BinaryTree` struct representing a node in a binary tree. Each node has a value and pointers to its left and right child nodes. - The code also defines a `TreeeInfo` struct to store information about a binary tree. It includes a boolean field `isBalanced` indicating whether the tree is balanced or not, and an integer field `height` representing the height of the tree. - The `HeightBalancedBinaryTree` function is the main function that checks if a binary tree is height-balanced. It takes the root of the tree as input and returns a boolean value indicating the balance status. - The `getTreeInfo` function is a helper function that recursively calculates the information of a binary tree. It takes a binary tree node as input and returns the `TreeeInfo` struct containing the balance status and height of the tree. - In the `getTreeInfo` function, there are two base cases: - If the current tree node is `nil`, it is considered balanced with height -1. - If the current tree node is not `nil`, the function recursively calculates the tree information of its left and right subtrees. - After getting the information of the left and right subtrees, the code checks if both subtrees are balanced (`isBalanced` field is `true`) and their height difference is at most 1. If so, the current tree is considered balanced. - The height of the current tree is calculated by taking the maximum height of the left and right subtrees and adding 1. - Finally, the `max` function is used to get the maximum of two integers, and the `abs` function is used to get the absolute value of an integer. To determine whether a binary tree is height-balanced, you can call the `HeightBalancedBinaryTree` function with the root of the tree. It will return `true` if the tree is balanced and `false` otherwise. The time complexity of the `HeightBalancedBinaryTree` function is O(N), where N is the number of nodes in the binary tree. This is because the function needs to traverse each node of the tree once to calculate the tree information. The space complexity of the `HeightBalancedBinaryTree` function is O(H), where H is the height of the binary tree. This is because the recursive calls to the `getTreeInfo` function will utilize the call stack, and the maximum depth of the recursive calls is equal to the height of the tree. Additionally, the space complexity of the `getTreeInfo` function itself is O(1) as it uses a constant amount of space for the `TreeeInfo` struct. Overall, the space complexity is determined by the height of the binary tree, and the time complexity is determined by the number of nodes in the binary tree. */ // Node class represents a node in a binary tree class BinaryTree { constructor(value) { this.value = value; this.left = null; this.right = null; } } // TreeInfo class represents the information of a binary tree, including its balance status and height class TreeInfo { constructor(isBalanced, height) { this.isBalanced = isBalanced; this.height = height; } } // heightBalancedBinaryTree checks if a binary tree is height-balanced function heightBalancedBinaryTree(tree) { // Retrieve the tree information using the helper function const treeInfo = getTreeInfo(tree); // Return the balance status of the tree return treeInfo.isBalanced; } // getTreeInfo retrieves the information of a binary tree, including its balance status and height function getTreeInfo(tree) { // Base case: If the tree is null, it is considered balanced with height -1 if (tree === null) { return new TreeInfo(true, -1); } // Recursively calculate the tree information of the left and right subtrees const leftSubtreeInfo = getTreeInfo(tree.left); const rightSubtreeInfo = getTreeInfo(tree.right); // Check if both left and right subtrees are balanced and their height difference is at most 1 const isBalanced = leftSubtreeInfo.isBalanced && rightSubtreeInfo.isBalanced && Math.abs(leftSubtreeInfo.height - rightSubtreeInfo.height) <= 1; // Calculate the height of the current tree by taking the maximum height of the left and right subtrees plus 1 const height = Math.max(leftSubtreeInfo.height, rightSubtreeInfo.height) + 1; // Create and return the tree information return new TreeInfo(isBalanced, height); } // Helper function to create a binary tree function createBinaryTree(value) { return new BinaryTree(value); } // Main function function main() { // Create a sample binary tree // 1 // / \ // 2 3 // / \ / // 4 5 6 const node1 = createBinaryTree(1); const node2 = createBinaryTree(2); const node3 = createBinaryTree(3); const node4 = createBinaryTree(4); const node5 = createBinaryTree(5); const node6 = createBinaryTree(6); node1.left = node2; node1.right = node3; node2.left = node4; node2.right = node5; node3.left = node6; // Check if the binary tree is height-balanced const isBalanced = heightBalancedBinaryTree(node1); // Print the result if (isBalanced) { console.log("The binary tree is height-balanced."); } else { console.log("The binary tree is not height-balanced."); } } // Call the main function main(); ================================================ FILE: Trees/Binary Trees/height_balanced_binary_tree.py ================================================ ''' You're given the root node of a Binary Tree. Write a function that returns true if this Binary Tree is height balanced and false if it isn't. Explanation: The provided code is for checking whether a binary tree is height-balanced or not. Here's how it works: - The code defines a `BinaryTree` struct representing a node in a binary tree. Each node has a value and pointers to its left and right child nodes. - The code also defines a `TreeeInfo` struct to store information about a binary tree. It includes a boolean field `isBalanced` indicating whether the tree is balanced or not, and an integer field `height` representing the height of the tree. - The `HeightBalancedBinaryTree` function is the main function that checks if a binary tree is height-balanced. It takes the root of the tree as input and returns a boolean value indicating the balance status. - The `getTreeInfo` function is a helper function that recursively calculates the information of a binary tree. It takes a binary tree node as input and returns the `TreeeInfo` struct containing the balance status and height of the tree. - In the `getTreeInfo` function, there are two base cases: - If the current tree node is `nil`, it is considered balanced with height -1. - If the current tree node is not `nil`, the function recursively calculates the tree information of its left and right subtrees. - After getting the information of the left and right subtrees, the code checks if both subtrees are balanced (`isBalanced` field is `true`) and their height difference is at most 1. If so, the current tree is considered balanced. - The height of the current tree is calculated by taking the maximum height of the left and right subtrees and adding 1. - Finally, the `max` function is used to get the maximum of two integers, and the `abs` function is used to get the absolute value of an integer. To determine whether a binary tree is height-balanced, you can call the `HeightBalancedBinaryTree` function with the root of the tree. It will return `true` if the tree is balanced and `false` otherwise. The time complexity of the `HeightBalancedBinaryTree` function is O(N), where N is the number of nodes in the binary tree. This is because the function needs to traverse each node of the tree once to calculate the tree information. The space complexity of the `HeightBalancedBinaryTree` function is O(H), where H is the height of the binary tree. This is because the recursive calls to the `getTreeInfo` function will utilize the call stack, and the maximum depth of the recursive calls is equal to the height of the tree. Additionally, the space complexity of the `getTreeInfo` function itself is O(1) as it uses a constant amount of space for the `TreeeInfo` struct. Overall, the space complexity is determined by the height of the binary tree, and the time complexity is determined by the number of nodes in the binary tree. ''' # This is an input class. Do not edit. class BinaryTree: def __init__(self, value): self.value = value self.left = None self.right = None class TreeInfo: def __init__(self, is_balanced, height): self.is_balanced = is_balanced self.height = height # Checks if a binary tree is height-balanced. def height_balanced_binary_tree(tree): # Retrieve the tree information using the helper function. tree_info = get_tree_info(tree) # Return the balance status of the tree. return tree_info.is_balanced # Retrieves the information of a binary tree, including its balance status and height. def get_tree_info(tree): # Base case: If the tree is None, it is considered balanced with height -1. if tree is None: return TreeInfo(True, -1) # Recursively calculate the tree information of the left and right subtrees. left_subtree_info = get_tree_info(tree.left) right_subtree_info = get_tree_info(tree.right) # Check if both left and right subtrees are balanced and their height difference is at most 1. is_balanced = left_subtree_info.is_balanced and right_subtree_info.is_balanced and \ abs(left_subtree_info.height - right_subtree_info.height) <= 1 # Calculate the height of the current tree by taking the maximum height of the left and right subtrees plus 1. height = max(left_subtree_info.height, right_subtree_info.height) + 1 # Create and return the tree information. return TreeInfo(is_balanced, height) # Returns the maximum of two integers. def max(a, b): return a if a > b else b # Returns the absolute value of an integer. def abs(a): return -a if a < 0 else a # Main function def main(): # Create a sample binary tree # 1 # / \ # 2 3 # / \ / # 4 5 6 node1 = BinaryTree(1) node2 = BinaryTree(2) node3 = BinaryTree(3) node4 = BinaryTree(4) node5 = BinaryTree(5) node6 = BinaryTree(6) node1.left = node2 node1.right = node3 node2.left = node4 node2.right = node5 node3.left = node6 # Check if the binary tree is height-balanced is_balanced = height_balanced_binary_tree(node1) # Print the result if is_balanced: print("The binary tree is height-balanced.") else: print("The binary tree is not height-balanced.") # Call the main function if __name__ == "__main__": main() ================================================ FILE: Trees/Binary Trees/inorder_traversal.cpp ================================================ // In-Order Traversal of a Binary-Tree // Program Author : Abhisek Kumar Gupta /* 40 / \ 10 30 / \ / \ 5 -1 -1 28 / \ / \ 1 -1 15 20 / \ /\ /\ -1 -1 -1 -1 -1 -1 Input : 40 10 5 1 -1 -1 -1 -1 30 -1 28 15 -1 -1 20 -1 -1 Output : 1->5->10->40->30->15->28->20 */ #include #include #include // Definition for a binary tree node. struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} }; class Solution { public: std::vector inorderTraversal(TreeNode* root) { // Vector to store the in-order traversal result std::vector result; // Stack to simulate the recursive call stack std::stack stack; // Current node starts from the root TreeNode* current = root; // Continue traversal until the current node is null and the stack is empty while (current != nullptr || !stack.empty()) { // Traverse all the way to the leftmost node, pushing each node onto the stack while (current != nullptr) { stack.push(current); current = current->left; } // Pop the top node from the stack (current leftmost node) current = stack.top(); stack.pop(); // Add the value of the current node to the result vector result.push_back(current->val); // Move to the right subtree of the current node current = current->right; } // Return the final in-order traversal result return result; } }; // Example usage int main() { // Create a sample binary tree TreeNode* root = new TreeNode(1); root->right = new TreeNode(2); root->right->left = new TreeNode(3); // Perform in-order traversal Solution solution; std::vector result = solution.inorderTraversal(root); // Print the result for (int val : result) { std::cout << val << " "; } // Output: 1 3 2 return 0; } ================================================ FILE: Trees/Binary Trees/inorder_traversal.go ================================================ package main import "fmt" // TreeNode definition type TreeNode struct { Val int Left *TreeNode Right *TreeNode } func inorderTraversal(root *TreeNode) []int { var result []int var stack []*TreeNode current := root for current != nil || len(stack) > 0 { // Traverse all the way to the leftmost node, pushing each node onto the stack for current != nil { stack = append(stack, current) current = current.Left } // Pop the top node from the stack (current leftmost node) current, stack = stack[len(stack)-1], stack[:len(stack)-1] // Add the value of the current node to the result slice result = append(result, current.Val) // Move to the right subtree of the current node current = current.Right } return result } func main() { // Create a sample binary tree root := &TreeNode{Val: 1, Right: &TreeNode{Val: 2, Left: &TreeNode{Val: 3}}} // Perform in-order traversal result := inorderTraversal(root) // Print the result fmt.Println(result) // Output: [1 3 2] } ================================================ FILE: Trees/Binary Trees/inorder_traversal.java ================================================ /** * Given the root of a binary tree, return the inorder traversal of its nodes' values. * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val = val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val = val; * this.left = left; * this.right = right; * } * } */ /** */ class Solution { public List inorderTraversal(TreeNode root) { // List to store the in-order traversal result List result = new ArrayList<>(); // Stack to simulate the recursive call stack Stack stack = new Stack<>(); // Current node starts from the root TreeNode current = root; // Continue traversal until current node is null and stack is empty while (current != null || !stack.isEmpty()) { // Traverse all the way to the leftmost node, pushing each node onto the stack while (current != null) { stack.push(current); current = current.left; } // Pop the top node from the stack (current leftmost node) current = stack.pop(); // Add the value of the current node to the result list result.add(current.val); // Move to the right subtree of the current node current = current.right; } // Return the final in-order traversal result return result; } } ================================================ FILE: Trees/Binary Trees/inorder_traversal.js ================================================ class TreeNode { constructor(val) { this.val = val; this.left = this.right = null; } } class Solution { inorderTraversal(root) { const result = []; const stack = []; let current = root; while (current || stack.length > 0) { // Traverse all the way to the leftmost node, pushing each node onto the stack while (current) { stack.push(current); current = current.left; } // Pop the top node from the stack (current leftmost node) current = stack.pop(); // Add the value of the current node to the result array result.push(current.val); // Move to the right subtree of the current node current = current.right; } return result; } } // Example usage const root = new TreeNode(1); root.right = new TreeNode(2); root.right.left = new TreeNode(3); const solution = new Solution(); const result = solution.inorderTraversal(root); console.log(result); // Output: [1, 3, 2] ================================================ FILE: Trees/Binary Trees/inorder_traversal.py ================================================ class TreeNode: def __init__(self, val): self.val = val self.left = self.right = None class Solution: def inorderTraversal(self, root): result = [] stack = [] current = root while current or stack: # Traverse all the way to the leftmost node, pushing each node onto the stack while current: stack.append(current) current = current.left # Pop the top node from the stack (current leftmost node) current = stack.pop() # Add the value of the current node to the result list result.append(current.val) # Move to the right subtree of the current node current = current.right return result # Example usage root = TreeNode(1) root.right = TreeNode(2) root.right.left = TreeNode(3) solution = Solution() result = solution.inorderTraversal(root) print(result) # Output: [1, 3, 2] ================================================ FILE: Trees/Binary Trees/invert.cpp ================================================ // Invert Binary tree #include class BinaryTreeNode { public: int data; BinaryTreeNode* left; BinaryTreeNode* right; BinaryTreeNode(int val) : data(val), left(nullptr), right(nullptr) {} }; BinaryTreeNode* invertTree(BinaryTreeNode* root) { if (root != nullptr) { root->left = invertTree(root->right); root->right = invertTree(root->left); } return root; } BinaryTreeNode* invertTree2(BinaryTreeNode* root) { if (root != nullptr) { // swap the pointers in this node BinaryTreeNode* temp = root->left; root->left = root->right; root->right = temp; invertTree2(root->left); invertTree2(root->right); } return root; } int main() { // Example usage: // Construct a binary tree BinaryTreeNode* root = new BinaryTreeNode(1); root->left = new BinaryTreeNode(2); root->right = new BinaryTreeNode(3); root->left->left = new BinaryTreeNode(4); root->left->right = new BinaryTreeNode(5); // Invert the binary tree using the first approach BinaryTreeNode* invertedRoot = invertTree(root); // Invert the binary tree using the second approach BinaryTreeNode* invertedRoot2 = invertTree2(root); // Additional code for printing or further usage... return 0; } ================================================ FILE: Trees/Binary Trees/invert.go ================================================ // Invert Binary tree package main type BinaryTreeNode struct { left *BinaryTreeNode data int right *BinaryTreeNode } // Time Complexity: O(n). Space Complexity: O(n). // Approach: The inverse of an empty tree is an empty tree // The inverse of a tree with root r, and subtrees right and left is a tree with // root, whose right subtree is the inverse of left and whoose left subtree // is the inverse of right func InvertTree(root *BinaryTreeNode) *BinaryTreeNode { if root != nil { root.left, root.right = InvertTree(root.right), InvertTree(root.left) } return root } // Time Complexity: O(n). Space Complexity: O(n). // Method2 : swap pointers func InvertTree2(root *BinaryTreeNode) *BinaryTreeNode { if root == nil { return root } // swap the pointers in this node root.left, root.right = root.right, root.left InvertTree(root.left) InvertTree(root.right) return root } ================================================ FILE: Trees/Binary Trees/invert.java ================================================ class BinaryTreeNode { int data; BinaryTreeNode left, right; public BinaryTreeNode(int data) { this.data = data; this.left = this.right = null; } } public class BinaryTreeInverter { // Time Complexity: O(n). Space Complexity: O(n). // Approach: The inverse of an empty tree is an empty tree // The inverse of a tree with root r, and subtrees right and left is a tree with // root, whose right subtree is the inverse of left and whose left subtree // is the inverse of right public BinaryTreeNode invertTree(BinaryTreeNode root) { if (root != null) { root.left = invertTree(root.right); root.right = invertTree(root.left); } return root; } // Time Complexity: O(n). Space Complexity: O(1). // Method2: swap pointers public BinaryTreeNode invertTree2(BinaryTreeNode root) { if (root != null) { // swap the pointers in this node BinaryTreeNode temp = root.left; root.left = root.right; root.right = temp; invertTree2(root.left); invertTree2(root.right); } return root; } // Additional methods or code as needed... public static void main(String[] args) { // Example usage: // Construct a binary tree BinaryTreeNode root = new BinaryTreeNode(1); root.left = new BinaryTreeNode(2); root.right = new BinaryTreeNode(3); root.left.left = new BinaryTreeNode(4); root.left.right = new BinaryTreeNode(5); BinaryTreeInverter inverter = new BinaryTreeInverter(); // Invert the binary tree using the first approach BinaryTreeNode invertedRoot = inverter.invertTree(root); // Invert the binary tree using the second approach BinaryTreeNode invertedRoot2 = inverter.invertTree2(root); // Additional code for printing or further usage... } } ================================================ FILE: Trees/Binary Trees/invert.js ================================================ class BinaryTreeNode { constructor(data) { this.data = data; this.left = this.right = null; } } class BinaryTreeInverter { // Time Complexity: O(n). Space Complexity: O(n). // Approach: The inverse of an empty tree is an empty tree // The inverse of a tree with root r, and subtrees right and left is a tree with // root, whose right subtree is the inverse of left and whoose left subtree // is the inverse of right invertTree(root) { if (root !== null) { root.left = this.invertTree(root.right); root.right = this.invertTree(root.left); } return root; } // Time Complexity: O(n). Space Complexity: O(1). // Method2: swap pointers invertTree2(root) { if (root !== null) { // swap the pointers in this node let temp = root.left; root.left = root.right; root.right = temp; this.invertTree2(root.left); this.invertTree2(root.right); } return root; } // Additional methods or code as needed... // Example usage: static main() { // Construct a binary tree let root = new BinaryTreeNode(1); root.left = new BinaryTreeNode(2); root.right = new BinaryTreeNode(3); root.left.left = new BinaryTreeNode(4); root.left.right = new BinaryTreeNode(5); let inverter = new BinaryTreeInverter(); // Invert the binary tree using the first approach let invertedRoot = inverter.invertTree(root); // Invert the binary tree using the second approach let invertedRoot2 = inverter.invertTree2(root); // Additional code for printing or further usage... } } // Example usage: BinaryTreeInverter.main(); ================================================ FILE: Trees/Binary Trees/invert.py ================================================ # Invert Binary tree class BinaryTreeNode: def __init__(self, data): self.left = None self.data = data self.right = None def invert_tree(root): if root: root.left, root.right = invert_tree(root.right), invert_tree(root.left) return root def invert_tree2(root): if root is not None: # swap the pointers in this node root.left, root.right = root.right, root.left invert_tree2(root.left) invert_tree2(root.right) return root # Example usage: # Assuming you have a binary tree root = BinaryTreeNode(1) root.left = BinaryTreeNode(2) root.right = BinaryTreeNode(3) root.left.left = BinaryTreeNode(4) root.left.right = BinaryTreeNode(5) # Invert the binary tree using the first approach inverted_root = invert_tree(root) # Invert the binary tree using the second approach inverted_root2 = invert_tree2(root) ================================================ FILE: Trees/Binary Trees/is_symmetric.cpp ================================================ /* Write a function that takes in a Binary Tree and returns if that tree is symmetrical. A tree is symmetrical if the left and right subtrees are mirror images of each other. Explanation: 1. The code defines a class `BinaryTree` representing a binary tree node. It has an `int` value and pointers to its left and right children. 2. The `SymmetricalTree` function is the main entry point. It calls the helper function `treesAreMirrored` to check if the left and right subtrees are mirrored. 3. The `treesAreMirrored` function checks if two binary trees are mirrored. It uses recursion to compare corresponding nodes in the left and right subtrees. 4. In the `treesAreMirrored` function, the base case checks if both the left and right trees are non-null and have the same value. If so, it recursively checks if their subtrees are mirrored. 5. If either the left or right tree is null or their values are not equal, they are not mirrored. If both the left and right trees are null, they are considered mirrored. 6. In the `main` function, a binary tree is created for testing purposes. 7. The `SymmetricalTree` function is called to check if the binary tree is symmetrical. 8. The result is printed to the console. 9. Memory cleanup is performed by deleting the dynamically allocated nodes. The time and space complexity of the given code snippet can be analyzed as follows: 1. Time Complexity: - The `SymmetricalTree` function calls the `treesAreMirrored` function, which performs a recursive traversal of the binary tree. - In the worst case, the recursion visits each node once, so the time complexity is O(N), where N is the number of nodes in the tree. 2. Space Complexity: - The space complexity is determined by the maximum depth of the recursion stack. - In the worst case, the binary tree is linear, resulting in a recursion depth of N, where N is the number of nodes in the tree. - Therefore, the space complexity is O(N) due to the recursion stack usage. It's important to note that the space complexity can be optimized by using an iterative approach instead of recursion. By using an iterative algorithm that leverages a stack or queue to perform a level-order traversal, we can achieve a space complexity of O(W), where W is the maximum width (number of nodes at the same level) of the binary tree. */ #include using namespace std; // This is an input class. Do not edit. class BinaryTree { public: int Value; BinaryTree* Left; BinaryTree* Right; }; // SymmetricalTree checks if a binary tree is symmetrical. bool SymmetricalTreeRecursive(BinaryTree* tree) { // Call the helper function to check if the left and right subtrees are mirrored. return treesAreMirrored(tree->Left, tree->Right); } // treesAreMirrored checks if two binary trees are mirrored. bool treesAreMirrored(BinaryTree* left, BinaryTree* right) { // Base case: If both left and right trees are non-null and have the same value, // recursively check if their subtrees are mirrored. if (left != nullptr && right != nullptr && left->Value == right->Value) { return treesAreMirrored(left->Left, right->Right) && treesAreMirrored(left->Right, right->Left); } // If either left or right tree is null or their values are not equal, they are not mirrored. // Also, if both left and right trees are null, they are considered mirrored. return left == right; } // Approach 2: Iterative Approach using Stack /* In this iterative approach, we use two stacks (stackLeft and stackRight) to perform a mirror traversal of the left and right subtrees. The process is similar to the original code snippet but implemented iteratively using a while loop and stacks. The stacks are initialized with the left and right children of the root node, and in each iteration, we compare the corresponding nodes from both stacks and check for asymmetry. The children of the left and right nodes are pushed onto their respective stacks in reverse order to maintain the mirror traversal. The loop continues until both stacks are empty or an asymmetry is detected. Finally, the function returns whether the tree is symmetric or not. The time complexity of this algorithm is O(n), where n is the number of nodes in the binary tree, as it traverses each node once. The space complexity is O(max(d, h)), where d is the maximum width of the tree (number of nodes at the widest level) and h is the height of the tree. The space complexity depends on the maximum number of nodes stored in the stacks during the traversal. */ struct BinaryTree { int value; BinaryTree* left; BinaryTree* right; }; bool SymmetricalTreeIterative(BinaryTree* tree) { std::stack stackLeft; std::stack stackRight; stackLeft.push(tree->left); // Initialize stackLeft with the left child of the root node stackRight.push(tree->right); // Initialize stackRight with the right child of the root node // Perform mirror traversal of the left and right subtrees while (!stackLeft.empty()) { BinaryTree* left = stackLeft.top(); BinaryTree* right = stackRight.top(); stackLeft.pop(); stackRight.pop(); if (left == nullptr && right == nullptr) { continue; // Both left and right subtrees are symmetric, continue to the next iteration } if (left == nullptr || right == nullptr || left->value != right->value) { return false; // Asymmetry detected, tree is not symmetric } // Push the children of left and right onto the respective stacks in reverse order stackLeft.push(left->left); stackLeft.push(left->right); stackRight.push(right->right); stackRight.push(right->left); } return true; // Tree is symmetric } int main() { // Create a binary tree for testing BinaryTree* tree = new BinaryTree(); tree->Value = 1; tree->Left = new BinaryTree(); tree->Left->Value = 2; tree->Right = new BinaryTree(); tree->Right->Value = 2; tree->Left->Left = new BinaryTree(); tree->Left->Left->Value = 3; tree->Right->Right = new BinaryTree(); tree->Right->Right->Value = 3; // Check if the tree is symmetrical bool isSymmetrical = SymmetricalTree(tree); // Output the result if (isSymmetrical) { cout << "The binary tree is symmetrical." << endl; } else { cout << "The binary tree is not symmetrical." << endl; } // Clean up the allocated memory delete tree->Left->Left; delete tree->Right->Right; delete tree->Left; delete tree->Right; delete tree; return 0; } ================================================ FILE: Trees/Binary Trees/is_symmetric.go ================================================ /* Write a function that takes in a Binary Tree and returns if that tree is symmetrical. A tree is symmetrical if the left and right subtrees are mirror images of each other. */ package main // This is an input class. Do not edit. type BinaryTree struct { Value int Left *BinaryTree Right *BinaryTree } /* The code consists of two functions. The SymmetricalTree function serves as an entry point and checks if the binary tree is symmetrical by calling the treesAreMirrored helper function with the left and right subtrees of the root node. The treesAreMirrored function is a recursive helper function that checks if two binary trees are mirrored. It uses a bottom-up approach to compare corresponding nodes of the left and right subtrees. The function performs the following steps: 1. Base case: If both the left and right trees are non-nil and have the same value, recursively check if their respective subtrees are mirrored by calling treesAreMirrored with the left subtree's left and the right subtree's right children, as well as the left subtree's right and the right subtree's left children. 2. If either the left or right tree is nil or their values are not equal, they are not mirrored, and the function returns false. 3. If both the left and right trees are nil, they are considered mirrored, and the function returns true. The recursive nature of the treesAreMirrored function allows it to traverse and compare corresponding nodes in a symmetrical manner. If the function successfully reaches the base case for all nodes, it indicates that the binary tree is symmetrical. Overall, the code leverages recursion and the concept of mirror images to determine if a binary tree is symmetrical or not. The time and space complexity of the code snippet can be analyzed as follows: Time Complexity: The time complexity of the SymmetricalTree function primarily depends on the size of the binary tree. In the worst case, where the tree is symmetric, the function needs to traverse all the nodes of the tree once. Let's assume there are 'n' nodes in the tree. In the worst case, the function will visit each node exactly once. Therefore, the time complexity is O(n), where 'n' is the number of nodes in the binary tree. Space Complexity: The space complexity is determined by the recursive calls and the stack space used during the recursion. In the worst case, when the binary tree is highly unbalanced and resembles a linked list, the depth of the recursion can be 'n' (the number of nodes in the tree). This means that the space required on the function call stack will be O(n). Additionally, the space complexity also includes the space used to store the function arguments and local variables. However, as these are constant-sized values (pointers), they don't contribute significantly to the overall space complexity. Therefore, the overall space complexity is O(n), where 'n' is the number of nodes in the binary tree, considering the worst-case scenario. In summary, the time complexity is O(n), and the space complexity is O(n), where 'n' is the number of nodes in the binary tree. */ // Approach 1: Recursive Approach // SymmetricalTree checks if a binary tree is symmetrical. func SymmetricalTreerecursive(tree *BinaryTree) bool { // Call the helper function to check if the left and right subtrees are mirrored. return treesAreMirrored(tree.Left, tree.Right) } // treesAreMirrored checks if two binary trees are mirrored. func treesAreMirrored(left, right *BinaryTree) bool { // Base case: If both left and right trees are non-nil and have the same value, // recursively check if their subtrees are mirrored. if left != nil && right != nil && left.Value == right.Value { return treesAreMirrored(left.Left, right.Right) && treesAreMirrored(left.Right, right.Left) } // If either left or right tree is nil or their values are not equal, they are not mirrored. // Also, if both left and right trees are nil, they are considered mirrored. return left == right } /* Explanation: The code snippet defines a function SymmetricalTree that checks if a binary tree is symmetrical. The binary tree is represented by the BinaryTree struct, which has a Value field and pointers to its left and right child nodes. The function uses two stacks, stackLeft and stackRight, to perform a mirror traversal of the left and right subtrees of the input tree. It starts by pushing the left child of the root node onto stackLeft and the right child of the root node onto stackRight. The function then enters a loop that continues until the stackLeft is empty. In each iteration of the loop, it pops the top nodes from stackLeft and stackRight, assigning them to variables left and right, respectively. If both left and right are nil, it means the corresponding subtrees are symmetric, so the loop continues to the next iteration. If either left or right is nil, or their values are not equal, it means the tree is not symmetric, and the function returns false. Otherwise, it pushes the left child of left and the right child of right onto stackLeft and stackRight, respectively, in reverse order. This ensures that the subsequent nodes popped from the stacks are compared as mirror images. Once the loop is completed, and no asymmetry has been detected, the function returns true, indicating that the binary tree is symmetric. The time complexity of this algorithm is O(n), where n is the number of nodes in the binary tree, as it traverses each node once. The space complexity is O(max(d, h)), where d is the maximum width of the tree (number of nodes at the widest level) and h is the height of the tree. The space complexity depends on the maximum number of nodes stored in the stacks during the traversal. */ // Approach 2: Iterative Approach using Stack func SymmetricalTreeIterative(tree *BinaryTree) bool { stackLeft := []*BinaryTree{tree.Left} // Initialize stackLeft with the left child of the root node stackRight := []*BinaryTree{tree.Right} // Initialize stackRight with the right child of the root node // Perform mirror traversal of the left and right subtrees for len(stackLeft) > 0 { var left, right *BinaryTree left, stackLeft = stackLeft[len(stackLeft)-1], stackLeft[:len(stackLeft)-1] // Pop the top node from stackLeft right, stackRight = stackRight[len(stackRight)-1], stackRight[:len(stackRight)-1] // Pop the top node from stackRight if left == nil && right == nil { continue // Both left and right subtrees are symmetric, continue to the next iteration } if left == nil || right == nil || left.Value != right.Value { return false // Asymmetry detected, tree is not symmetric } // Push the children of left and right onto the respective stacks in reverse order stackLeft = append(stackLeft, left.Left, left.Right) stackRight = append(stackRight, right.Right, right.Left) } return true // Tree is symmetric } ================================================ FILE: Trees/Binary Trees/is_symmetric.java ================================================ /* Write a function that takes in a Binary Tree and returns if that tree is symmetrical. A tree is symmetrical if the left and right subtrees are mirror images of each other. */ import java.util.Stack; public class BinaryTree { // Assume that BinaryTree has properties: left, right, and value. public boolean isSymmetricalTreeIterative(BinaryTree tree) { Stack stackLeft = new Stack<>(); Stack stackRight = new Stack<>(); // Initialize stackLeft with the left child of the root node // Initialize stackRight with the right child of the root node stackLeft.push(tree.getLeft()); stackRight.push(tree.getRight()); // Perform mirror traversal of the left and right subtrees while (!stackLeft.isEmpty()) { BinaryTree left = stackLeft.pop(); BinaryTree right = stackRight.pop(); if (left == null && right == null) { continue; // Both left and right subtrees are symmetric, continue to the next iteration } if (left == null || right == null || left.getValue() != right.getValue()) { return false; // Asymmetry detected, tree is not symmetric } // Push the children of left and right onto the respective stacks in reverse order stackLeft.push(left.getLeft()); stackLeft.push(left.getRight()); stackRight.push(right.getRight()); stackRight.push(right.getLeft()); } return true; // Tree is symmetric } // Other methods and properties for BinaryTree class } ================================================ FILE: Trees/Binary Trees/is_symmetric.js ================================================ /* Write a function that takes in a Binary Tree and returns if that tree is symmetrical. A tree is symmetrical if the left and right subtrees are mirror images of each other. */ class BinaryTree { constructor(value, left = null, right = null) { this.value = value; this.left = left; this.right = right; } } function isSymmetricalTreeIterative(tree) { const stackLeft = (tree && [tree.left]) || []; // Initialize stackLeft with the left child of the root node const stackRight = (tree && [tree.right]) || []; // Initialize stackRight with the right child of the root node // Perform mirror traversal of the left and right subtrees while (stackLeft.length > 0) { const left = stackLeft.pop() || null; // Pop the top node from stackLeft const right = stackRight.pop() || null; // Pop the top node from stackRight if (!left && !right) { continue; // Both left and right subtrees are symmetric, continue to the next iteration } if (!left || !right || left.value !== right.value) { return false; // Asymmetry detected, tree is not symmetric } // Push the children of left and right onto the respective stacks in reverse order if (left) { stackLeft.push(left.left, left.right); } if (right) { stackRight.push(right.right, right.left); } } return true; // Tree is symmetric } // Example usage: // Construct a symmetric tree const symmetricTree = new BinaryTree( 1, new BinaryTree(2, new BinaryTree(3), new BinaryTree(4)), new BinaryTree(2, new BinaryTree(4), new BinaryTree(3)) ); console.log(isSymmetricalTreeIterative(symmetricTree)); // Output: true ================================================ FILE: Trees/Binary Trees/is_symmetric.py ================================================ ''' Write a function that takes in a Binary Tree and returns if that tree is symmetrical. A tree is symmetrical if the left and right subtrees are mirror images of each other. ''' class BinaryTree: def __init__(self, value, left=None, right=None): self.value = value self.left = left self.right = right def is_symmetrical_tree_iterative(tree): stack_left = [tree.left] if tree else [] # Initialize stackLeft with the left child of the root node stack_right = [tree.right] if tree else [] # Initialize stackRight with the right child of the root node # Perform mirror traversal of the left and right subtrees while stack_left: left = stack_left.pop() if stack_left else None right = stack_right.pop() if stack_right else None if not left and not right: continue # Both left and right subtrees are symmetric, continue to the next iteration if not left or not right or left.value != right.value: return False # Asymmetry detected, tree is not symmetric # Push the children of left and right onto the respective stacks in reverse order stack_left.extend([left.left, left.right] if left else []) stack_right.extend([right.right, right.left] if right else []) return True # Tree is symmetric # Example usage: # Construct a symmetric tree symmetric_tree = BinaryTree(1, BinaryTree(2, BinaryTree(3), BinaryTree(4)), BinaryTree(2, BinaryTree(4), BinaryTree(3))) print(is_symmetrical_tree_iterative(symmetric_tree)) # Output: True ================================================ FILE: Trees/Binary Trees/level_by_level.cpp ================================================ // Binary Tree : Breadth First Search Print level by level TC : O(n) // Program Author : Abhisek Kumar Gupta /* 40 / \ 10 30 / \ / \ 5 -1 -1 28 / \ / \ 1 -1 15 20 / \ /\ /\ 1 -1 -1 -1 -1 -1 /\ -1 -1 Input : 40 10 5 1 1 -1 -1 -1 -1 -1 30 -1 28 15 -1 -1 20 -1 -1 Output : 40-> 10-> 30-> 5-> 28-> 1-> 15-> 20-> 1-> */ #include using namespace std; class Node{ public: int data; Node* left; Node* right; Node(int x){ data = x; } }; Node* build_binary_tree(){ int data; cin >> data; if(data == -1){ return NULL; } Node* root = new Node(data); root->left = build_binary_tree(); root->right = build_binary_tree(); return root; } void bfs(Node* root){ queue q; q.push(root); q.push(NULL); while(!q.empty()){ Node* element = q.front(); if(element == NULL){ q.pop(); cout << "\n"; if(!q.empty()){ q.push(NULL); } } else{ cout << element->data << "->"; q.pop(); if(element->left !=NULL){ q.push(element->left); } if(element->right != NULL){ q.push(element->right); } } } return; } int main(){ Node* root = build_binary_tree(); bfs(root); return 0; } ================================================ FILE: Trees/Binary Trees/level_order_traversal.cpp ================================================ // Level order Traversal of a Binary Tree TC O(n^2) // Program Author : Abhisek Kumar Gupta /* 40 / \ 10 30 / \ / \ 5 -1 -1 28 / \ / \ 1 -1 15 20 / \ /\ /\ 1 -1 -1 -1 -1 -1 /\ -1 -1 Input : 40 10 5 1 1 -1 -1 -1 -1 -1 30 -1 28 15 -1 -1 20 -1 -1 Output : 40 10 30 5 28 1 15 20 1 */ #include using namespace std; class Node{ public: int data; Node* left; Node* right; Node(int x){ data = x; } }; Node* build_binary_tree(){ int data; cin >> data; if(data == -1){ return NULL; } Node* root = new Node(data); root->left = build_binary_tree(); root->right = build_binary_tree(); return root; } int compute_height_of_binary_tree(Node* root){ if(root == NULL) return 0; int left_height = compute_height_of_binary_tree(root->left); int right_height = compute_height_of_binary_tree(root->right); return max(left_height, right_height) + 1; } void print_kth_level(Node* root, int k){ if(root == NULL) return; if(k == 1){ cout << root->data << " "; return; } print_kth_level(root->left, k - 1); print_kth_level(root->right, k- 1); return; } void level_order_traversal(Node* root){ int height = compute_height_of_binary_tree(root); for(int i = 1; i <= height; i++){ print_kth_level(root, i); cout << "\n"; } } int main(){ Node* root = build_binary_tree(); level_order_traversal(root); return 0; } ================================================ FILE: Trees/Binary Trees/level_order_traversal.go ================================================ package main type BinaryTreeNode struct { left *BinaryTreeNode data int right *BinaryTreeNode } // Level order traversal is defined as follows: // 1 Visit the root. // 2 While traversing level 􀝈, keep all the elements at level 􀝈 + 1 in queue. // 3 Go to the next level and visit all the nodes at that level. // 4 Repeat this until all levels are completed. // The nodes of the tree are visited in the order: [1] [2 3] [ 4 5 6 7] // Time Complexity: O(n), Space Complexity: O(n) In the worst case, all the nodes on the entire last level could be in the queue. func LevelOrder(root *BinaryTreeNode) [][]int { if root == nil { return [][]int{} } // Data from each level is being returned as a separate list var result [][]int queue := []*BinaryTreeNode{root} for len(queue) > 0 { qlen := len(queue) var level []int for i:= 0; i < qlen; i++ { node := queue[0] level = append(level, node.data) queue = queue[1:] // if there are left children then append them in queue if node.left != nil { queue = append(queue, node.left) } // if there are right children then append them in queue if node.right != nil { queue = append(queue, node.right) } } } return result } ================================================ FILE: Trees/Binary Trees/node_depth.go ================================================ /* The distance between a node in a Binary Tree and the tree's root is called the node's depth. Write a function that takes in a Binary Tree and returns the sum of its nodes' depths. Sample Input: 1 / \ 2 3 / \ / \ 4 5 6 7 / \ 8 9 Output: 16 */ /* The depth of any node in the tree is equal to the depth of its parent node plus 1. By starting at the root node whose depth is 0, you can pass down to every node in the tree its respective depth, and you can implement the algorithm that does this and that sums up all of the depths either recursively or iteratively. Time and Space complexity Average case: when the tree is balanced O(n) time | O(h) space - where n is the number of nodes in the Binary Tree and h is the height of the Binary Tree */ package main type BinaryTree struct { Value int Left, Right *BinaryTree } func NodeDepths(root *BinaryTree) int { return nodeDepthHelper(root, 0) } func nodeDepthHelper(root *BinaryTree, depth int) int { if root == nil { return 0 } return depth + nodeDepthHelper(root.Left, depth + 1) + nodeDepthHelper(root.Right, depth + 1) } ================================================ FILE: Trees/Binary Trees/postorder_traversal.cpp ================================================ // Post-Order Traversal of a Binary-Tree // Program Author : Abhisek Kumar Gupta /* 40 / \ 10 30 / \ / \ 5 -1 -1 28 / \ / \ 1 -1 15 20 / \ /\ /\ -1 -1 -1 -1 -1 -1 Input : 40 10 5 1 -1 -1 -1 -1 30 -1 28 15 -1 -1 20 -1 -1 Output : 1->5->10->15->20->28->30->40 */ #include using namespace std; class Node{ public: int data; Node* left; Node* right; Node(int x){ data = x; left = NULL; right = NULL; } }; Node* build_binary_tree(){ int data; cin >> data; if(data == -1){ return NULL; } Node* root = new Node(data); root->left = build_binary_tree(); root->right = build_binary_tree(); return root; } void print_binary_tree(Node* root){ if(root == NULL) return; print_binary_tree(root->left); print_binary_tree(root->right); cout << root->data << "->"; } int main(){ Node* root = build_binary_tree(); print_binary_tree(root); return 0; } //40 10 5 1 -1 -1 -1 -1 30 -1 28 15 -1 -1 20 -1 -1 ================================================ FILE: Trees/Binary Trees/preorder_traversal.cpp ================================================ // Pre-Order Traversal of a Binary-Tree // Program Author : Abhisek Kumar Gupta /* 40 / \ 10 30 / \ / \ 5 -1 -1 28 / \ / \ 1 -1 15 20 / \ /\ /\ -1 -1 -1 -1 -1 -1 Input : 40 10 5 1 -1 -1 -1 -1 30 -1 28 15 -1 -1 20 -1 -1 Output : 40->10->5->1->30->28->15->20 */ #include using namespace std; class Node{ public: int data; Node *left; Node *right; Node(int x){ data = x; left = NULL; right = NULL; } }; Node* build_binary_tree(){ int data; cin >> data; if(data == -1) return NULL; Node* root = new Node(data); root->left = build_binary_tree(); root->right = build_binary_tree(); return root; } void print_binary_tree(Node* root){ if(root == NULL) return; cout << root->data << "->"; print_binary_tree(root->left); print_binary_tree(root->right); } int main(){ Node* root = build_binary_tree(); print_binary_tree(root); } ================================================ FILE: Trees/Binary Trees/remove_leaf_nodes.go ================================================ // Remove leaf nodes package main type BinaryTreeNode struct { left *BinaryTreeNode data int right *BinaryTreeNode } // Time Complexity: O(n). Space Complexity: O(n). // Approach: recurse both left and right subtree and check if the node doesn't have // left and right children func RemoveLeafNodes(root *BinaryTreeNode) *BinaryTreeNode { if root == nil { return root } // if it doesnt have left and right children then delete it if root.left == nil && root.right == nil { root = nil return root } else { // recurse to left and right subtree root.left = RemoveLeafNodes(root.left) root.right = RemoveLeafNodes(root.right) } return root } ================================================ FILE: Trees/Binary Trees/search_an_element.go ================================================ // Search an element in Binary tree package main type BinaryTreeNode struct { left *BinaryTreeNode data int right *BinaryTreeNode } // Time Complexity: O(n). Space Complexity: O(n). // Approach: recurse down the tree choose left or right branch by comparing data with each node's data func SearchAnElement(root *BinaryTreeNode, data int) *BinaryTreeNode { // base case empty tree if root == nil { return root } else { // if found return root if data == root.data { return root } else { // recurse down correct subtree temp := SearchAnElement(root.left, data) if temp != nil { return temp } else { return SearchAnElement(root.right, data) } } } } // Time Complexity: O(n). Space Complexity: O(n). // Approach: using level order traversal we can solve this problem, check whether // the root data is equal to the element we want to search func SearchAnElementWithoutRecursion(root *BinaryTreeNode, data int) *BinaryTreeNode { if root == nil { return root } queue := []*BinaryTreeNode{root} for len(queue) > 0 { qlen := len(queue) for i := 0; i < qlen; i++ { node := queue[0] if data == node.data { return node } queue = queue[1:] if node.left != nil { queue = append(queue, node.left) } if node.right != nil { queue = append(queue, node.right) } } } return nil } ================================================ FILE: Trees/Binary Trees/sum_of_all_nodes.cpp ================================================ // Binary Tree : Sum of all Nodes // Program Author : Abhisek Kumar Gupta /* 40 / \ 10 30 / \ / \ 5 -1 -1 28 / \ / \ 1 -1 15 20 / \ /\ /\ 1 -1 -1 -1 -1 -1 /\ -1 -1 Input : 40 10 5 1 1 -1 -1 -1 -1 -1 30 -1 28 15 -1 -1 20 -1 -1 Output : 150 */ #include using namespace std; class Node{ public: int data; Node* left; Node* right; Node(int d){ data = d; } }; Node* build_binary_tree(){ int data; cin >> data; if(data == -1){ return NULL; } Node* root = new Node(data); root->left = build_binary_tree(); root->right = build_binary_tree(); return root; } int sum_of_all_nodes(Node* root){ if(root == NULL){ return 0; } return root->data + sum_of_all_nodes(root->right) + sum_of_all_nodes(root->left); } int main(){ Node* root = build_binary_tree(); int sum_of_nodes = sum_of_all_nodes(root); cout << sum_of_nodes; return 0; } ================================================ FILE: Trees/Implement_Trie.cpp ================================================ /* Approach: - We define a Trie node structure that contains an array of child nodes and a flag to indicate the end of a word. - The Trie class provides methods to insert words into the Trie, search for words, and check if any word starts with a given prefix. - The insert operation involves traversing the Trie for each character of the word and creating a new node if the path doesn't exist. At the end, we mark the last node as the end of a word. - The search operation is similar to the insert operation, but it returns true only if the last node is marked as the end of a word. - The startsWith operation also involves traversing the Trie for each character of the prefix. It returns true regardless of whether the last node is marked as the end of a word or not. Time Complexity: - The time complexity of inserting a word into the Trie is O(m), where m is the length of the word. - The time complexity of searching for a word or checking for a prefix is O(m), where m is the length of the word or prefix. Space Complexity: - The space complexity of the Trie is O(N * M), where N is the number of words and M is the average length of the words. Sample Input / Output: int main() { Trie trie; trie.insert("apple"); trie.insert("banana"); cout << trie.search("apple") << endl; // Output: 1 (true) cout << trie.search("banana") << endl; // Output: 1 (true) cout << trie.search("car") << endl; // Output: 0 (false) cout << trie.startsWith("app") << endl; // Output: 1 (true) cout << trie.startsWith("ban") << endl; // Output: 1 (true) cout << trie.startsWith("cab") << endl; // Output: 0 (false) return 0; } */ //Here's the code for implementing a Trie (Prefix Tree) in C++: // Trie node structure struct TrieNode { struct TrieNode* children[26]; // Array to store child nodes bool isEndOfWord; // Flag to indicate end of word TrieNode() { isEndOfWord = false; for (int i = 0; i < 26; i++) children[i] = nullptr; } }; // Trie class class Trie { private: TrieNode* root; // Pointer to the root node public: Trie() { root = new TrieNode(); } // Insert a word into the Trie void insert(string word) { TrieNode* current = root; for (char ch : word) { int index = ch - 'a'; // Create a new node if the path doesn't exist if (!current->children[index]) { current->children[index] = new TrieNode(); } current = current->children[index]; } // Mark the end of a word current->isEndOfWord = true; } // Search for a word in the Trie bool search(string word) { TrieNode* current = root; for (char ch : word) { int index = ch - 'a'; // Return false if the path doesn't exist if (!current->children[index]) { return false; } current = current->children[index]; } // Return true only if the current node is the end of a word return current->isEndOfWord; } // Check if any word in the Trie starts with the given prefix bool startsWith(string prefix) { TrieNode* current = root; for (char ch : prefix) { int index = ch - 'a'; // Return false if the path doesn't exist if (!current->children[index]) { return false; } current = current->children[index]; } // Return true regardless of whether the current node is the end of a word or not return true; } }; ================================================ FILE: Trees/Implement_Trie.py ================================================ ''' Approach: 1. We implement the TrieNode class with a dictionary of children nodes and a flag to indicate the end of a word. 2. The Trie class is implemented with a root TrieNode as its member variable. 3. The insert function traverses through each character of the input word and creates new TrieNode objects if necessary. 4. The search function traverses through each character of the input word and returns True only if the last node is marked as the end of a word. 5. The startsWith function is similar to the search function but returns True as long as the prefix exists in the Trie. Time Complexity: - Insertion: O(m), where m is the length of the word being inserted. - Search: O(m), where m is the length of the word being searched. - Starts With: O(m), where m is the length of the prefix being checked. Space Complexity: - O(n*m), where n is the number of words inserted and m is the average length of the words. Sample input and output: trie = Trie() trie.insert("apple") print(trie.search("apple")) # Output: True print(trie.search("app")) # Output: False print(trie.startsWith("app")) # Output: True trie.insert("app") print(trie.search("app")) # Output: True Here's an implementation of the Trie data structure, also known as the Prefix Tree, in Python: ''' # Trie node class class TrieNode: def __init__(self): self.children = {} self.is_end_of_word = False # Trie class class Trie: def __init__(self): self.root = TrieNode() # Function to insert a word into the Trie def insert(self, word: str) -> None: node = self.root for char in word: # If the current character doesn't exist in the Trie, create a new node if char not in node.children: node.children[char] = TrieNode() # Move to next node node = node.children[char] # Mark the end of a word node.is_end_of_word = True # Function to search for a word in the Trie def search(self, word: str) -> bool: node = self.root for char in word: # If the current character doesn't exist, the word doesn't exist if char not in node.children: return False # Move to next node node = node.children[char] # Return True only if the last node is marked as the end of a word return node.is_end_of_word # Function to check if a word is a prefix of any existing word in the Trie def startsWith(self, prefix: str) -> bool: node = self.root for char in prefix: # If the current character doesn't exist, the prefix doesn't exist if char not in node.children: return False # Move to next node node = node.children[char] # The prefix exists if we reach this point return True ================================================ FILE: Trees/MaxpathBinaryTree.cpp ================================================ #include #include // For max function using namespace std; // Definition for a binary tree node. struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} }; class Solution { public: int maxPathSum(TreeNode* root) { int maxSum = INT_MIN; // Initialize with the minimum possible value findMaxPathSum(root, maxSum); return maxSum; } private: int findMaxPathSum(TreeNode* node, int& maxSum) { if (!node) { return 0; } // Calculate the maximum path sum for the left and right subtrees int leftMax = max(0, findMaxPathSum(node->left, maxSum)); int rightMax = max(0, findMaxPathSum(node->right, maxSum)); // Calculate the maximum path sum that includes the current node int currentMax = node->val + leftMax + rightMax; // Update the overall maximum path sum maxSum = max(maxSum, currentMax); // Return the maximum path sum that can be extended from this node return node->val + max(leftMax, rightMax); } }; int main() { // Create a sample binary tree TreeNode* root = new TreeNode(10); root->left = new TreeNode(2); root->right = new TreeNode(10); root->left->left = new TreeNode(20); root->left->right = new TreeNode(1); root->right->right = new TreeNode(-25); root->right->right->left = new TreeNode(3); root->right->right->right = new TreeNode(4); Solution solution; int maxSum = solution.maxPathSum(root); cout << "Maximum Path Sum: " << maxSum << endl; return 0; } ================================================ FILE: Trees/tree.go ================================================ // Represent simple HTML document using Tree Data Structure package main import "strings" /*

This is a H1

This is a H1

This is paragraph. some logo

This is the footer of the page. 2022 © AKGMAGE
*/ type Node struct { tag string text string src string alt string class string id string children []*Node } func FindByIDBFS(root *Node, id string) *Node { // make queue of type Node which will contain all the nodes queue := make([]*Node, 0) queue = append(queue, root) for len(queue) > 0 { curr := queue[0] // set curr as first node queue = queue[1:] // remove first node from queue // return if match is found if curr.id == id { return curr } // if it has any children then queue it up if len(curr.children) > 0 { // dont care about the index, only care about the value for _, child := range curr.children { // add children in queue queue = append(queue, child) } } } return nil } func FindByIDDFS(node *Node, id string) *Node { // return if match is found if node.id == id { return node } // keep exploring children if it has any if len(node.children) > 0 { for _, child := range node.children { // recursively call FindByIDBFS FindByIDBFS(child, id) } } return nil } // hasClass returns true if given className is found in node class func (n *Node) hasClass(className string) bool { // Fields splits the string s around each instance of one or more consecutive white space characters classes := strings.Fields(n.class) // range through slice of classes for _, class := range classes { // return trupe if classname matches if class == className { return true } } // No match found return false } func findAllByClassName(root *Node, className string) []*Node { // make result of type Node which will contain all the resulting className nodes result := make([]*Node, 0) // make queue of type Node which will contain all the nodes queue := make([]*Node, 0) queue = append(queue, root) for len(queue) > 0 { curr := queue[0] // set curr as first node queue = queue[1:] // remove first node from queue // if match is found append it to result's slice if curr.hasClass(className) { result = append(result, curr) } // keep exploring children if it has any if len(curr.children) > 0 { for _, child := range curr.children { // add children to queue queue = append(queue, child) } } } return result } func main() { span := Node { tag: "span", id: "spanId", text: "2022 © AKGMAGE", } div := Node { tag: "div", class: "someClass", children: []*Node{&span}, } image := Node{ tag: "image", src: "https://image.com/image.png", alt: "some logo", } p := Node { tag: "p", text: "This is para.", children: []*Node{&image}, } h1:= Node { tag: "h1", text: "This is h1", } h2:= Node { tag: "h2", text: "This is h2", } body := Node { tag: "body", children: []*Node{&div, &p, &h2, &h1}, } html := Node { tag: "html", children: []*Node{&body}, } } ================================================ FILE: Trees/trie.cpp ================================================ /* https://leetcode.com/problems/implement-trie-prefix-tree/ Implement Trie (also called 'prefix tree') which supports lowercase letters only, a search tree with multiple childrens (26 in this case). methods: Insert - push new string into the tree. Delete - remove string from the tree. search - check if a string is already pushed into the tree. StartWith - check if sum prefix is appear in the tree. Example: int main(){ Trie trie; trie.Insert("abcd"); trie.Search("abcd") // return true; trie.Search("ab") // return false; trie.startWith("ab") // return true; trie.Insert("ab"); trie.Search("ab") // return true; trie.Delete("ab") trie.Search("ab") // return false; trie.Search("abcd") // return true; return 0; } Time Complexity: Insert() / Search() / startWith() | Average & Worst-case == O(m) | m == number of chars in string. Space Complexity: Space complexity for a trie: O(k * N) | k == size of the node, N == number of the different nodes. */ #include #include enum{CHARS_IN_ALPHABET = 26}; class TrieNode{ public: TrieNode() : vec(CHARS_IN_ALPHABET, nullptr), end_of_str(false) {}; // every node has a vector for his childrens and a flag to mark - end of word. std::vector vec; bool end_of_str; }; /****************************************************************************/ class Trie { public: Trie(); ~Trie(); // non-copyable class Trie(const Trie& other) = delete; Trie& operator= (const Trie& other) = delete; void Insert(std::string word); bool Delete(std::string word); bool Search(std::string word); bool startWith(std::string prefix); void Destroy(TrieNode* root); private: enum RecDeleteStatus{NOT_FOUND = -1, DELETE = 0 , COMPLITE = 1}; RecDeleteStatus RecDelete(TrieNode* curr_node, const char* string_ptr); bool IsLeaf(TrieNode* node); TrieNode* CreatNodes(TrieNode* node_runner, const char* rest_letters); TrieNode* FindLastChar(const char* first_char_string); TrieNode* m_root; }; /****************************************************************************/ Trie::Trie() // Ctor { m_root = new TrieNode(); } /****************************************************************************/ Trie::~Trie() // Dtor { Destroy(m_root); delete m_root; } /****************************************************************************/ // function for push a word(string) inside the trie void Trie::Insert(std::string word) { const char* string_ptr = word.data(); TrieNode* node_runner = m_root; while(*string_ptr) { // if this node does not have this latter as a child yet. if(!node_runner->vec[*string_ptr - 'a']) { node_runner = CreatNodes(node_runner, string_ptr); break; } node_runner = node_runner->vec[*string_ptr - 'a']; ++string_ptr; } // mark node as end of word. node_runner->end_of_str = true; } /****************************************************************************/ // function for remove a word from the trie, if the word or part of her suffix // stand alone, the whole node will be removed. bool Trie::Delete(std::string word) { if(RecDelete(m_root, word.data()) != NOT_FOUND) { return true; } return false; } /****************************************************************************/ // function for check if a string is already pushed into the tree. bool Trie::Search(std::string word) { TrieNode* node = FindLastChar(word.data()); // if FindLastChar return nullptr the word isnt in the trie, // if the word is found but it just a prefix of another word, return false if(node == nullptr || node->end_of_str == false){ return false; } return true; } /****************************************************************************/ // function for check if sum prefix is appear in the tree. bool Trie::startWith(std::string prefix) { TrieNode* node = FindLastChar(prefix.data()); // if FindLastChar return nullptr the word isnt in the trie, if(node == nullptr){ return false; } return true; } /****************************************************************************/ // Recursive function for delete a word and the whole node if the word or part // of her suffix stand alone. if the word is prefix of another word, no nodes // will be removed Trie::RecDeleteStatus Trie::RecDelete(TrieNode* curr_node, const char* string_ptr) { if(curr_node == nullptr) { return NOT_FOUND; } if(*string_ptr == '\0') { return DELETE; } int char_idx = *string_ptr - 'a'; RecDeleteStatus status = RecDelete(curr_node->vec[char_idx], string_ptr + 1); if(status == DELETE) { if(IsLeaf(curr_node->vec[char_idx])) { delete curr_node->vec[char_idx]; curr_node->vec[char_idx] = nullptr; return DELETE; } else if(*(string_ptr + 1) == '\0') { if(curr_node->vec[char_idx]->end_of_str) { curr_node->vec[char_idx]->end_of_str = false; return DELETE; } else { return NOT_FOUND; } } } if(status == NOT_FOUND) { return NOT_FOUND; } return COMPLITE; } /****************************************************************************/ // function to check if node does not have childrens bool Trie::IsLeaf(TrieNode* curr_node) { for(auto it : curr_node->vec) { if(it != nullptr) { return false; } } return true; } /****************************************************************************/ // function for create and push new nodes ,from the input node(node_runner) // according to the string(string_ptr) that must have a null termination sign. TrieNode* Trie::CreatNodes(TrieNode* node_runner, const char* string_ptr) { while(*string_ptr) { TrieNode* new_node = new TrieNode(); node_runner->vec[*string_ptr - 'a'] = new_node; node_runner = new_node; ++string_ptr; } return node_runner; } /****************************************************************************/ // function for search and return the node of the last character in a string // return nullptr if the word is not found. TrieNode* Trie::FindLastChar(const char* string_ptr) { TrieNode* node_runner = m_root; while(*string_ptr) { TrieNode* next_node = node_runner->vec[*string_ptr - 'a']; if(nullptr == next_node) { return nullptr; } node_runner = next_node; ++string_ptr; } return node_runner; } /****************************************************************************/ // destroy all nodes inside the trie except the root void Trie::Destroy(TrieNode* node) { if(node == nullptr) { return; } for(auto it : node->vec) { Destroy(it); delete it; } } ================================================ FILE: Trees/trie.go ================================================ /* Approach: In this implementation, we define two struct types: `TrieNode` and `Trie`. `TrieNode` represents a single node in the Trie. It has two fields: `children` (a map of rune to `TrieNode`) to store the child nodes and `isWord` (a boolean) to indicate the end of a word. `Trie` represents the Trie data structure and has a single field `root` (a pointer to `TrieNode`), which is initialized with an empty `TrieNode` in the `NewTrie` function. The Trie supports three operations: - `Insert` function is used to add a word to the Trie. It starts from the root node and traverses through each character of the word, creating new nodes as needed. - `Search` function is used to check if a given word exists in the Trie. It follows a similar approach to the `Insert` function but returns `true` if the final node marks the end of a word. - `StartsWith` function is used to check if there is any word in the Trie that starts with a given prefix. It works similar to the `Search` function but does not require the final node to mark the end of a word. Sample Input: In the `main` function demonstrates the usage of the Trie data structure. We insert words "apple", "app", "application", "book", and "dog" into the Trie. Then, we search for words "app", "apple", and "banana". Finally, we check if there are any words in the Trie that start with prefixes "ap", "do", and "cat". Time complexity of the `Insert`, `Search`, and `StartsWith` operations: O(m), where m is the length of the word or prefix being processed. Space complexity: O(n), where n is the total number of characters in all the words inserted in the Trie. */ //Here's an implementation of the Trie data structure in Go: package main import "fmt" // TrieNode represents a single node in the Trie type TrieNode struct { children map[rune]*TrieNode isWord bool } // Trie represents the Trie data structure type Trie struct { root *TrieNode } // NewTrie initializes a new Trie func NewTrie() *Trie { return &Trie{ root: &TrieNode{ children: make(map[rune]*TrieNode), isWord: false, }, } } // Insert adds a word to the Trie func (t *Trie) Insert(word string) { node := t.root // Traverse through each character of the word for _, ch := range word { if node.children[ch] == nil { node.children[ch] = &TrieNode{ children: make(map[rune]*TrieNode), isWord: false, } } node = node.children[ch] } // Mark the end of the word node.isWord = true } // Search returns true if the word exists in the Trie, otherwise false func (t *Trie) Search(word string) bool { node := t.root // Traverse through each character of the word for _, ch := range word { if node.children[ch] == nil { return false } node = node.children[ch] } // Check if the node marks the end of a word return node.isWord } // StartsWith returns true if there is any word in the Trie that starts with the given prefix, otherwise false func (t *Trie) StartsWith(prefix string) bool { node := t.root // Traverse through each character of the prefix for _, ch := range prefix { if node.children[ch] == nil { return false } node = node.children[ch] } return true } func main() { trie := NewTrie() // Inserting words into the Trie trie.Insert("apple") trie.Insert("app") trie.Insert("application") trie.Insert("book") trie.Insert("dog") // Searching words in the Trie fmt.Println(trie.Search("app")) // Output: true fmt.Println(trie.Search("apple")) // Output: true fmt.Println(trie.Search("banana")) // Output: false // Checking prefixes in the Trie fmt.Println(trie.StartsWith("ap")) // Output: true fmt.Println(trie.StartsWith("do")) // Output: true fmt.Println(trie.StartsWith("cat")) // Output: false } ================================================ FILE: Trees/trie.java ================================================ /* Description A trie (pronounced as "try") or prefix tree is a tree data structure used to efficiently store and retrieve keys in a dataset of strings. There are various applications of this data structure, such as autocomplete and spellchecker. Implement the Trie class: Trie() Initializes the trie object. void insert(String word) Inserts the string word into the trie. boolean search(String word) Returns true if the string word is in the trie (i.e., was inserted before), and false otherwise. boolean startsWith(String prefix) Returns true if there is a previously inserted string word that has the prefix prefix, and false otherwise. Input ["Trie", "insert", "search", "search", "startsWith", "insert", "search"] [[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]] Output [null, null, true, false, true, null, true] Explanation Trie trie = new Trie(); trie.insert("apple"); trie.search("apple"); // return True trie.search("app"); // return False trie.startsWith("app"); // return True trie.insert("app"); trie.search("app"); // return True Time Complexity for each operation is O(n) Space Complexity O(n*m) where n is the number of words inserted and m is the average length of the words. Explanation: insert() -> traverse through each character of the input word and initializes it if necessary. If the end of the word is reached set isEnd to true. search() -> Search In each child node until the end of the word is reached, then if end of the node is also reached return true else false. startsWith() -> Similar to search method but we only check if end of the prefix is reached and we don't need to check if it is the end of the node. */ class Trie { Node root; public Trie() { root = new Node(); } public void insert(String word) { root.insert(word, 0); } public boolean search(String word) { return root.search(word, 0); } public boolean startsWith(String prefix) { return root.startsWith(prefix, 0); } class Node { Node[] nodes; boolean isEnd; Node() { nodes = new Node[26]; } // Function to insert the word in the tree private void insert(String word, int idx) { if (idx >= word.length()) return; // handle edge case int i = word.charAt(idx) - 'a'; if (nodes[i] == null) { nodes[i] = new Node(); // initialize the node[i] if the letter was not found before } if (idx == word.length()-1) nodes[i].isEnd = true; // signifies that this is the end of the word nodes[i].insert(word, idx+1); // recursive call to populate the child node } // Function to search the word in the tree private boolean search(String word, int idx) { if (idx >= word.length()) return false; Node node = nodes[word.charAt(idx) - 'a']; if (node == null) return false; // if the node is null it means that it was not initialised hence the character was never found. if (idx == word.length() - 1 && node.isEnd) return true; //if it is the last character and the end of the node then return true return node.search(word, idx+1); // recursive call search in the child node } //Function to search the prefix in tree private boolean startsWith(String prefix, int idx) { if (idx >= prefix.length()) return false; Node node = nodes[prefix.charAt(idx) - 'a']; if (node == null) return false; if (idx == prefix.length() - 1) return true; // Very similar to above method but here we don't need to check if it is the end of the node. return node.startsWith(prefix, idx+1); } } } ================================================ FILE: Tries/pattern_matching.cpp ================================================ /* Given a list of n words and a pattern p that we want to search. Check if the pattern p is present the given words or not. Return true if the pattern is present and false otherwise. Input Format : The first line of input contains an integer, that denotes the value of n. The following line contains n space separated words. The following line contains a string, that denotes the value of the pattern p. Output Format : The first and only line of output contains true if the pattern is present and false otherwise. Constraints: Time Limit: 1 sec Sample Input 1 : 4 abc def ghi cba de Sample Output 2 : true Sample Input 2 : 4 abc def ghi hg hi Sample Output 2 : true Sample Input 3 : 4 abc def ghi hg hif Sample Output 3 : false Explaination : The TrieNode class represents a single node in the Trie. Each node contains a character data, an array of pointers to its child nodes children, and a boolean flag isTerminal to indicate if a word ends at that node. The Trie class serves as the main data structure and contains a pointer to the root node of the Trie, along with a count variable to keep track of the number of words inserted. The insertWord function is used to insert a word into the Trie. It takes a root parameter representing the current node and a word parameter representing the word to be inserted. The function recursively traverses the Trie, creating new nodes as needed and marking the last node as terminal if the word does not already exist. The search function is used to search for a word in the Trie. It takes a root parameter and a word parameter. The function recursively checks if each character in the word exists as a child node starting from the given root. The patternMatching function takes a vector of strings vect and a pattern string. It iterates over each word in the vector and inserts all possible substrings of that word into the Trie. Finally, it performs a search operation on the pattern string in the Trie and returns the result. */ #include #include #include using namespace std; class TrieNode { public: char data; TrieNode **children; bool isTerminal; TrieNode(char data) { this->data = data; children = new TrieNode *[26]; for (int i = 0; i < 26; i++) { children[i] = NULL; } isTerminal = false; } }; class Trie { TrieNode *root; public: int count; Trie() { this->count = 0; root = new TrieNode('\0'); } bool insertWord(TrieNode *root, string word) { // Base case if (word.size() == 0) { if (!root->isTerminal) { root->isTerminal = true; return true; } else { return false; } } // Small calculation int index = word[0] - 'a'; TrieNode *child; if (root->children[index] != NULL) { child = root->children[index]; } else { child = new TrieNode(word[0]); root->children[index] = child; } // Recursive call return insertWord(child, word.substr(1)); } void insertWord(string word) { if (insertWord(root, word)) { this->count++; } } bool search(TrieNode *root, string word) { if (word.length() == 0) { return true; } if (root->children[word[0] - 'a'] == NULL) { return false; } bool ans = search(root->children[word[0] - 'a'], word.substr(1)); return ans; } bool search(string word) { return search(root, word); } bool patternMatching(vector vect, string pattern) { for (int i = 0; i < vect.size(); i++) { string word = vect[i]; for (int j = 0; j < word.size(); j++) { insertWord(word.substr(j)); } } return search(pattern); } }; int main() { Trie t; int n; cin >> n; string pattern; vector vect; for (int i = 0; i < n; ++i) { string word; cin >> word; vect.push_back(word); } cin >> pattern; cout << (t.patternMatching(vect, pattern) ? "true" : "false"); } ================================================ FILE: Tries/search_in_tries.cpp ================================================ /* Implement the function SearchWord for the Trie class. For a Trie, write the function for searching a word. Return true if the word is found successfully, otherwise return false. Note : main function is given for your reference which we are using internally to test the code. Explaination : The given code implements a Trie data structure and focuses on implementing the search functionality. The TrieNode class represents a single node in the Trie, and the Trie class serves as the main data structure. The insertWord function is used to insert a word into the Trie. It takes a root parameter representing the current node and a word parameter representing the word to be inserted. The function recursively traverses the Trie, creating new nodes as needed and marking the last node as terminal. The search function is used to search for a word in the Trie. It takes a root parameter and a word parameter. The function recursively checks if each character in the word exists as a child node starting from the given root. It returns true if the word is found and the last node is marked as terminal, otherwise it returns false. In the main function, an instance of the Trie class is created. The program then enters a loop where the user can input commands. The user is prompted to enter a choice: 1 for inserting a word or 2 for searching a word. If the choice is 1, the user can enter a word to be inserted into the Trie. If the choice is 2, the user can enter a word to search for in the Trie. The result of the search operation is printed as "true" or "false" accordingly. */ #include #include using namespace std; class TrieNode { public: char data; TrieNode **children; bool isTerminal; TrieNode(char data) { this->data = data; children = new TrieNode *[26]; for (int i = 0; i < 26; i++) { children[i] = NULL; } isTerminal = false; } }; class Trie { TrieNode *root; public: Trie() { root = new TrieNode('\0'); } void insertWord(TrieNode *root, string word) { // Base case if (word.size() == 0) { root->isTerminal = true; return; } // Small Calculation int index = word[0] - 'a'; TrieNode *child; if (root->children[index] != NULL) { child = root->children[index]; } else { child = new TrieNode(word[0]); root->children[index] = child; } // Recursive call insertWord(child, word.substr(1)); } void insertWord(string word) { insertWord(root, word); } bool search(TrieNode *root, string word) { if (word.length() == 0) { return root->isTerminal && true; } int index = word[0] - 'a'; TrieNode *child; if (root->children[index] != NULL) { child = root->children[index]; } else { return root->isTerminal && false; } bool ans = search(child, word.substr(1)); return ans; } bool search(string word) { return search(root, word); } }; int main() { int choice; cin >> choice; Trie t; while (choice != -1) { string word; bool ans; switch (choice) { case 1: // insert cin >> word; t.insertWord(word); break; case 2: // search cin >> word; cout << (t.search(word) ? "true\n" : "false\n"); break; default: return 0; } cin >> choice; } return 0; } ================================================ FILE: Tries/trie_node_class.cpp ================================================ /* Explaination : The TrieNode class represents a single node in the Trie. Each node contains a character data, an array of pointers to its child nodes children, and a boolean flag isTerminal to indicate if a word ends at that node. The Trie class serves as the main data structure and contains a pointer to the root node of the Trie. It provides public functions to insert a word into the Trie, search for a word in the Trie, and remove a word from the Trie. The insertWord function is a private member function of the Trie class and is responsible for inserting a word into the Trie. It takes a root parameter representing the current node and a word parameter representing the word to be inserted. The function recursively traverses the Trie, creating new nodes as needed and marking the last node as terminal. The search function is also a private member function of the Trie class and is used to search for a word in the Trie. It takes a root parameter and a word parameter. The function recursively checks if each character in the word exists as a child node starting from the given root and returns true if the entire word is found and marked as terminal. The removeWord function is another private member function of the Trie class and is used to remove a word from the Trie. It takes a root parameter and a word parameter. The function recursively searches for the word in the Trie, marks the last node of the word as non-terminal, and removes any unnecessary child nodes that are not part of other words. In the main function, an instance of the Trie class is created. Words "and", "dot", and "double" are inserted into the Trie using the insertWord function. The search function is then used to check if the word "and" exists in the Trie, and the result is printed. The removeWord function is called to remove the word "and" from the Trie, and again the search function is used to check if the word "and" exists in the Trie after removal, and the result is printed. */ #include #include using namespace std; class TrieNode { public: char data; TrieNode **children; bool isTerminal; TrieNode(char data) { this->data = data; children = new TrieNode *[26]; for (int i = 0; i < 26; i++) { children[i] = NULL; } isTerminal = false; } }; class Trie { TrieNode *root; public: Trie() { root = new TrieNode('\0'); } private: void insertWord(TrieNode *root, string word) { // Base Case if (word.length() == 0) { root->isTerminal = true; return; } // Small Calculation int index = word[0] - 'a'; TrieNode *child; if (root->children[index] != NULL) { child = root->children[index]; } else { child = new TrieNode(word[0]); root->children[index] = child; } // Recursive Call insertWord(child, word.substr(1)); } bool search(TrieNode *root, string word) { if (word.length() == 0) { return root->isTerminal && true; } int index = word[0] - 'a'; TrieNode *child; if (root->children[index] != NULL) { child = root->children[index]; } else { return root->isTerminal && false; } bool ans = search(child, word.substr(1)); return ans; } void removeWord(TrieNode *root, string word) { if (word.size() == 0) { root->isTerminal = false; return; } int index = word[0] - 'a'; TrieNode *child; if (root->children[index] != NULL) { child = root->children[index]; } else { // Word not found return; } removeWord(child, word.substr(1)); // Remove Child Node if it is useless if (child->isTerminal == false) { for (int i = 0; i < 26; i++) { if (child->children[i] != NULL) { return; } } delete child; root->children[index] = NULL; } } public: void insertWord(string word) { insertWord(root, word); } bool search(string word) { return search(root, word); } void removeWord(string word) { removeWord(root, word); } }; int main() { Trie t; t.insertWord("and"); t.insertWord("dot"); t.insertWord("double"); cout << t.search("and") << endl; t.removeWord("and"); cout << t.search("and") << endl; } ================================================ FILE: sorting/Cyclic_Sort.java ================================================ /* What is Cyclic Sort? The basic idea behind cycle sort is to divide the input array into cycles, where each cycle consists of elements that belong to the same position in the sorted output array. The algorithm then performs a series of swaps to place each element in its correct position within its cycle, until all cycles are complete and the array is sorted. It is usually used where elements are in the range of (1,n) Note : This Algorithm solution is for elements from 1-N , where N is the number of elements in the array. Time Complexity Analysis: Worst Case: O(n) Average Case: O(n) Best Case: O(n) Auxiliary Space: O(1) */ import java.util.Arrays; public class Cyclic_Sort { public static void main(String[] args) { int[] arr={3, 5, 2, 1, 4}; //Sample Input sort(arr); System.out.println(Arrays.toString(arr)); //Printing the original array } //Cyclic Sort Program static void sort(int[] arr){ int i=0; //Variable to iterate over each element of array while(i #include using namespace std; void bubbleSort(vector& arr) { int n = arr.size(); // Traverse through all array elements for (int i = 0; i < n - 1; i++) { // Last i elements are already sorted for (int j = 0; j < n - i - 1; j++) { // Swap adjacent elements if they are in the wrong order if (arr[j] > arr[j + 1]) { swap(arr[j], arr[j + 1]); } } } } int main() { vector arr = {64, 25, 12, 22, 11}; bubbleSort(arr); cout << "Sorted array: "; for (auto i : arr) { cout << i << " "; } return 0; } ================================================ FILE: sorting/bubble_sort.go ================================================ /* Here's how the Bubble Sort algorithm works: 1. We start by comparing the first two elements of the array. If the first element is greater than the second element, we swap them. 2. We then compare the second and third elements. If the second element is greater than the third element, we swap them. 3. We continue this process until we reach the end of the array. At this point, the largest element will be at the end of the array. 4. We then repeat steps 1-3 for the remaining unsorted portion of the array until the entire array is sorted. The time complexity of Bubble Sort is O(n^2) in the worst and average case, and O(n) in the best case when the input array is already sorted. The space complexity is O(1) as Bubble Sort operates on the input array in-place. Bubble sort is O(n) on a list that is already sorted i.e. Best case Sample Input : [2, 1, 9, 3, 5, 4, 0] Output : [0 1 2 3 4 5 9] */ package main import "fmt" func bubbleSort(arr []int) { n := len(arr) flag := true // Traverse through all array elements for i := 0; i < n-1; i++ { // Last i elements are already sorted for j := 0; j < n-i-1; j++ { // Swap adjacent elements if they are in the wrong order if arr[j] > arr[j+1] { arr[j], arr[j+1] = arr[j+1], arr[j] flag = false } } if flag { fmt.Println("Already sorted so no further redundant passes best case O(n)") break } } } func main() { arr := []int{64, 25, 12, 22, 11} bubbleSort(arr) fmt.Println("Sorted array:", arr) } ================================================ FILE: sorting/bubble_sort.java ================================================ /* Here's how the Bubble Sort algorithm works: 1. We start by comparing the first two elements of the array. If the first element is greater than the second element, we swap them. 2. We then compare the second and third elements. If the second element is greater than the third element, we swap them. 3. We continue this process until we reach the end of the array. At this point, the largest element will be at the end of the array. 4. We then repeat steps 1-3 for the remaining unsorted portion of the array until the entire array is sorted. The time complexity of Bubble Sort is O(n^2) in the worst and average case, and O(n) in the best case when the input array is already sorted. The space complexity is O(1) as Bubble Sort operates on the input array in-place. Bubble sort is O(n) on a list that is already sorted i.e. Best case Sample Input : [2, 1, 9, 3, 5, 4, 0] Output : [0 1 2 3 4 5 9] */ import java.util.Arrays; public class bubble_sort { public static void bubbleSort(int[] arr) { int n = arr.length; // Traverse through all array elements for (int i = 0; i < n - 1; i++) { // Last i elements are already in place for (int j = 0; j < n - i - 1; j++) { // Swap adjacent elements if they are in the wrong order if (arr[j] > arr[j + 1]) { int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } public static void main(String[] args) { int[] arr = {64, 34, 25, 12, 22, 11, 90}; bubbleSort(arr); System.out.println(Arrays.toString(arr)); } } ================================================ FILE: sorting/bubble_sort.js ================================================ /* Here's how the Bubble Sort algorithm works: 1. We start by comparing the first two elements of the array. If the first element is greater than the second element, we swap them. 2. We then compare the second and third elements. If the second element is greater than the third element, we swap them. 3. We continue this process until we reach the end of the array. At this point, the largest element will be at the end of the array. 4. We then repeat steps 1-3 for the remaining unsorted portion of the array until the entire array is sorted. The time complexity of Bubble Sort is O(n^2) in the worst and average case, and O(n) in the best case when the input array is already sorted. The space complexity is O(1) as Bubble Sort operates on the input array in-place. Bubble sort is O(n) on a list that is already sorted i.e. Best case Sample Input : [2, 1, 9, 3, 5, 4, 0] Output : [0 1 2 3 4 5 9] */ function bubbleSort(arr) { var n = arr.length; // Traverse through all array elements for (var i = 0; i < n - 1; i++) { // Last i elements are already sorted for (var j = 0; j < n - i - 1; j++) { // Swap adjacent elements if they are in the wrong order if (arr[j] > arr[j + 1]) { var temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } var arr = [64, 25, 12, 22, 11]; bubbleSort(arr); console.log("Sorted array: " + arr); ================================================ FILE: sorting/bubble_sort.py ================================================ ''' Here's how the Bubble Sort algorithm works: 1. We start by comparing the first two elements of the array. If the first element is greater than the second element, we swap them. 2. We then compare the second and third elements. If the second element is greater than the third element, we swap them. 3. We continue this process until we reach the end of the array. At this point, the largest element will be at the end of the array. 4. We then repeat steps 1-3 for the remaining unsorted portion of the array until the entire array is sorted. The time complexity of Bubble Sort is O(n^2) in the worst and average case, and O(n) in the best case when the input array is already sorted. The space complexity is O(1) as Bubble Sort operates on the input array in-place. Bubble sort is O(n) on a list that is already sorted i.e. Best case Sample Input : [2, 1, 9, 3, 5, 4, 0] Output : [0 1 2 3 4 5 9] ''' def bubbleSort(arr): n = len(arr) # Traverse through all array elements for i in range(n - 1): # Last i elements are already sorted for j in range(0, n - i - 1): # Swap adjacent elements if they are in the wrong order if arr[j] > arr[j + 1]: arr[j], arr[j + 1] = arr[j + 1], arr[j] arr = [64, 25, 12, 22, 11] bubbleSort(arr) print("Sorted array:", arr) ================================================ FILE: sorting/bucket-sort.js ================================================ function bucketSort(arr, bucketSize = 5) { if (arr.length === 0) { return arr; } // Determine minimum and maximum values in the array let minValue = arr[0]; let maxValue = arr[0]; for (let i = 1; i < arr.length; i++) { if (arr[i] < minValue) { minValue = arr[i]; } else if (arr[i] > maxValue) { maxValue = arr[i]; } } // Determine number of buckets needed and initialize empty buckets const bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1; const buckets = new Array(bucketCount); for (let i = 0; i < bucketCount; i++) { buckets[i] = []; } // Assign array elements to buckets based on their value for (let i = 0; i < arr.length; i++) { const bucketIndex = Math.floor((arr[i] - minValue) / bucketSize); buckets[bucketIndex].push(arr[i]); } // Sort each bucket using another sorting algorithm (here, insertion sort) const sortedArray = []; for (let i = 0; i < buckets.length; i++) { const bucket = buckets[i]; insertionSort(bucket); sortedArray.push(...bucket); } return sortedArray; } function insertionSort(arr) { for (let i = 1; i < arr.length; i++) { const currentValue = arr[i]; let j = i - 1; while (j >= 0 && arr[j] > currentValue) { arr[j + 1] = arr[j]; j--; } arr[j + 1] = currentValue; } } ================================================ FILE: sorting/bucket_sort.cpp ================================================ /*The code implements the bucket sort algorithm in C++. It defines a bucketSort function that takes a vector of integers as input and sorts it using the bucket sort technique. The algorithm distributes the elements into separate buckets based on their values, sorts each bucket individually, and then concatenates the sorted buckets to obtain the final sorted array.*/ /*Time Complexity: O(n + k) for best case and average case and O(n^2) for the worst case. The space complexity of bucket sort is O(n + k)*/ #include #include #include using namespace std; void bucketSort(vector& arr) { // Find the maximum value in the array to determine the range of buckets int max_value = *max_element(arr.begin(), arr.end()); // Calculate the number of buckets needed int num_buckets = max_value + 1; // Create empty buckets vector> buckets(num_buckets); // Distribute the elements into buckets for (int num : arr) { buckets[num].push_back(num); } // Sort each bucket individually for (auto& bucket : buckets) { sort(bucket.begin(), bucket.end()); } // Concatenate the sorted buckets to get the sorted array int index = 0; for (const auto& bucket : buckets) { for (int num : bucket) { arr[index++] = num; } } } int main() { // Create an array of integers vector array = {5, 3, 9, 1, 8, 2, 7, 4, 6}; // Sort the array using bucket sort bucketSort(array); // Print the sorted array for (int num : array) { cout << num << " "; } cout << endl; return 0; } ================================================ FILE: sorting/bucket_sort.go ================================================ /* Bucket sort, or bin sort, is a sorting algorithm that works by distributing the elements of an array into a number of buckets. Each bucket is then sorted individually, either using a different sorting algorithm, or by recursively applying the bucket sorting algorithm. It is a distribution sort, a generalization of pigeonhole sort that allows multiple keys per bucket, and is a cousin of radix sort in the most-to-least significant digit flavor. Bucket sort can be implemented with comparisons and therefore can also be considered a comparison sort algorithm. The computational complexity depends on the algorithm used to sort each bucket, the number of buckets to use, and whether the input is uniformly distributed. Bucket sort works as follows: Set up an array of initially empty "buckets". Scatter: Go over the original array, putting each object in its bucket. Sort each non-empty bucket. Gather: Visit the buckets in order and put all elements back into the original array. Source(https://en.wikipedia.org/wiki/Bucket_sort) */ // The average time complexity for Bucket Sort is O(n + k). // The worst time complexity is O(n²). // The space complexity for Bucket Sort is O(n+k). package main import ( "fmt" ) func InsertionSort(Array []int) { for i := 0; i < len(Array); i++ { temp := Array[i] j := i - 1 for ; j >= 0 && Array[j] > temp; j-- { Array[j+1] = Array[j] } Array[j+1] = temp } } func BucketSort(Array []int, bucketSize int) []int { var max, min int for _, n := range Array { if n < min { min = n } if n > max { max = n } } nBuckets := int(max-min)/bucketSize + 1 buckets := make([][]int, nBuckets) for i := 0; i < nBuckets; i++ { buckets[i] = make([]int, 0) } for _, n := range Array { idx := int(n-min) / bucketSize buckets[idx] = append(buckets[idx], n) } sorted := make([]int, 0) for _, bucket := range buckets { if len(bucket) > 0 { InsertionSort(bucket) sorted = append(sorted, bucket...) } } return sorted } func main() { Array := []int{3, 4, 5, 2, 1} Array = BucketSort(Array, 2) for _, val := range Array { fmt.Println(val) } } ================================================ FILE: sorting/bucket_sort.java ================================================ /* Here is a step by step process : Set up an array of initially empty "buckets". Scatter: Go over the original array, putting each object in its bucket. Sort each non-empty bucket. Gather: Visit the buckets in order and put all elements back into the original array. Bucket Sort time complexity Best Case Time Complexity: O(n+k) Average Case Time Complexity: O(n) Worst Case Time Complexity: O(n^2^) Best Case Time Complexity: If the array elements are uniformly distributed, bucket size will almost be the same for all the buckets. Hence, this will be the best case which will take up the least amount of time. Sorting time complexity will reduce even further if all the elements inside each bucket are already sorted. To create n buckets and scatter each element from the array, time complexity = O(n). If we use Insertion sort to sort each bucket, time complexity = O(k). Hence, best case time complexity for bucket sort = O(n+k), where n = number of elements, and k = number of buckets Worst Case Time Complexity If the array elements are not uniformly distributed, i.e., elements are concentrated within specific ranges. This will result in one or more buckets having more elements than other buckets, making bucket sort like any other sorting technique, where every element is compared to the other. Time complexity increases even further if the elements in the array are present in the reverse order. If insertion sort is used, the worst-case time complexity can go up to O(n^2^). Bucket Sort Space Complexity Space Complexity : O(n+k) Space Complexity for bucket sort is O(n+k), where n = number of elements in the array, and k = number of buckets formed Space taken by each bucket is O(k), and inside each bucket, we have n elements scattered. Hence, the space complexity becomes O(n+k). */ package util; import java.util.Collections; import java.util.LinkedList; import java.util.List; public class BucketSort { static void bucketSort(int[] arr, int noOfBuckets) { boolean isNegativePresent = false; int offset = Integer.MAX_VALUE; for (int i : arr) { if (i < offset) offset = i; if (i < 0) isNegativePresent = true; } int globalMax = Integer.MIN_VALUE; int globalMin = Integer.MAX_VALUE; for (int i = 0; i < arr.length; i++) { arr[i] -= offset; globalMin = Math.min(arr[i], globalMin); globalMax = Math.max(arr[i], globalMax); } int range = globalMax - globalMin; int bucketRange = (int) Math.ceil((double) range / noOfBuckets); // Create bucket array List[] buckets = new List[noOfBuckets]; // Associate a list with each index in the bucket array for (int i = 0; i < noOfBuckets; i++) { buckets[i] = new LinkedList<>(); } // Assign numbers from array to the proper bucket // by using hashing function for (int num : arr) { buckets[hash(num, bucketRange, noOfBuckets)].add(num); } // sort buckets for (List bucket : buckets) Collections.sort(bucket); int idx = 0; // Merge buckets to get sorted array for (List bucket : buckets) { for (int num : bucket) { arr[idx++] = num; } } if (isNegativePresent) { for (int i = 0; i < arr.length; i++) { arr[i] += offset; } } } private static int hash(int num, int hashValue, int numberOfBuckets) { int bucketNumber = num / hashValue; if (bucketNumber == numberOfBuckets) bucketNumber--; return bucketNumber; } } ================================================ FILE: sorting/bucket_sort.js ================================================ Here's an implementation of bucket sort in JavaScript: function bucketSort(arr, numBuckets = 10) { const n = arr.length; const buckets = new Array(numBuckets); // Initialize the buckets for (let i = 0; i < numBuckets; i++) { buckets[i] = []; } // Distribute the elements into the buckets for (let i = 0; i < n; i++) { const bucketIndex = Math.floor(arr[i] * numBuckets); buckets[bucketIndex].push(arr[i]); } // Sort the elements within each bucket for (let i = 0; i < numBuckets; i++) { buckets[i].sort((a, b) => a - b); } // Concatenate the sorted buckets let sortedArr = []; for (let i = 0; i < numBuckets; i++) { sortedArr = sortedArr.concat(buckets[i]); } return sortedArr; } The bucketSort function takes an array arr of numbers and an optional parameter numBuckets that specifies the number of buckets to use (default is 10). The algorithm works by distributing the elements of arr into numBuckets buckets based on their value. Each bucket contains elements within a range of values, and since the buckets are sorted, the elements within each bucket are sorted as well. Finally, the sorted buckets are concatenated to obtain the final sorted array. Note that the performance of bucket sort depends on the distribution of the elements in the input array. If the elements are evenly distributed, bucket sort can achieve a time complexity of O(n), which is faster than many other sorting algorithms. However, if the elements are concentrated in a small range of values, the buckets may become unbalanced and the performance may degrade. Therefore, bucket sort is often used as a sub-routine in other sorting algorithms, or when the distribution of the input is known to be relatively even. ================================================ FILE: sorting/count_sort.cpp ================================================ /* Count Sort The Counting Sort algorithm is a non-comparative sorting algorithm that works by counting the occurrences of each distinct element in the input list. It then uses this information to determine the correct position of each element in the sorted output. Here are the steps involved in the Counting Sort algorithm: 1. Find the range: Determine the range of values in the input list. This range is necessary to create an array with the appropriate size to store the counts. 2. Count the occurrences: Create a count array of size equal to the range determined in the previous step. Iterate through the input list and count the occurrences of each element by incrementing the corresponding count in the count array. 3. Calculate cumulative counts: Modify the count array such that each element represents the cumulative count of elements up to that index. This step ensures that the count array contains the correct positions for each element in the sorted order. 4. Generate the sorted output: Create an output array of the same size as the input list. Iterate through the input list and use the count array to determine the correct position of each element in the output array. Place each element in its corresponding position and decrement the count in the count array. 5. Return the sorted list: The output array now contains the elements in sorted order. Return this sorted list as the result of the Counting Sort algorithm. Here's an example to illustrate the process: Sample Input: [4, 2, 9, 4, 6, 1] 1. Find the range: The range of values in the input list is from 1 to 9. 2. Count the occurrences: Create the count array [0, 1, 1, 0, 2, 0, 1, 0, 0, 1], where the index represents the element and the value represents the count. 3. Calculate cumulative counts: Modify the count array to [0, 1, 2, 2, 4, 4, 5, 5, 5, 6]. Each element represents the cumulative count of elements up to that index. 4. Generate the sorted output: Create the output array [1, 2, 4, 4, 6, 9]. Iterate through the input list, use the count array to determine the correct position of each element, place it in the output array, and decrement the count in the count array. 5. Return the sorted list: The sorted list is [1, 2, 4, 4, 6, 9]. Counting Sort is an efficient algorithm when the range of values in the input list is relatively small. It has a time complexity of O(n + k), where n is the number of elements in the input list and k is the range of values. */ #include using namespace std; int getMax(int a[], int n) { int max = a[0]; for (int i = 1; i < n; i++) { if (a[i] > max) max = a[i]; } return max; // maximum element from the array } void countSort(int a[], int n) // function to perform counting sort { int output[n + 1]; int max = getMax(a, n); int count[max + 1]; // create count array with size [max+1] for (int i = 0; i <= max; ++i) { count[i] = 0; // Initialize count array with all zeros } for (int i = 0; i < n; i++) // Store the count of each element { count[a[i]]++; } for (int i = 1; i <= max; i++) count[i] += count[i - 1]; // find cumulative frequency /* This loop will find the index of each element of the original array in count array, and place the elements in output array*/ for (int i = n - 1; i >= 0; i--) { output[count[a[i]] - 1] = a[i]; count[a[i]]--; // decrease count for same numbers } for (int i = 0; i < n; i++) { a[i] = output[i]; // store the sorted elements into main array } } void printArr(int a[], int n) /* function to print the array */ { int i; for (i = 0; i < n; i++) cout << a[i] << " "; } int main() { int a[] = {31, 11, 42, 7, 30, 11}; int n = sizeof(a) / sizeof(a[0]); cout << "Before sorting array elements are - \n"; printArr(a, n); countSort(a, n); cout << "\nAfter sorting array elements are - \n"; printArr(a, n); return 0; } ================================================ FILE: sorting/count_sort.java ================================================ /* Count Sort The Counting Sort algorithm is a non-comparative sorting algorithm that works by counting the occurrences of each distinct element in the input list. It then uses this information to determine the correct position of each element in the sorted output. Here are the steps involved in the Counting Sort algorithm: 1. Find the range: Determine the range of values in the input list. This range is necessary to create an array with the appropriate size to store the counts. 2. Count the occurrences: Create a count array of size equal to the range determined in the previous step. Iterate through the input list and count the occurrences of each element by incrementing the corresponding count in the count array. 3. Calculate cumulative counts: Modify the count array such that each element represents the cumulative count of elements up to that index. This step ensures that the count array contains the correct positions for each element in the sorted order. 4. Generate the sorted output: Create an output array of the same size as the input list. Iterate through the input list and use the count array to determine the correct position of each element in the output array. Place each element in its corresponding position and decrement the count in the count array. 5. Return the sorted list: The output array now contains the elements in sorted order. Return this sorted list as the result of the Counting Sort algorithm. Here's an example to illustrate the process: Sample Input: [4, 2, 9, 4, 6, 1] 1. Find the range: The range of values in the input list is from 1 to 9. 2. Count the occurrences: Create the count array [0, 1, 1, 0, 2, 0, 1, 0, 0, 1], where the index represents the element and the value represents the count. 3. Calculate cumulative counts: Modify the count array to [0, 1, 2, 2, 4, 4, 5, 5, 5, 6]. Each element represents the cumulative count of elements up to that index. 4. Generate the sorted output: Create the output array [1, 2, 4, 4, 6, 9]. Iterate through the input list, use the count array to determine the correct position of each element, place it in the output array, and decrement the count in the count array. 5. Return the sorted list: The sorted list is [1, 2, 4, 4, 6, 9]. Counting Sort is an efficient algorithm when the range of values in the input list is relatively small. It has a time complexity of O(n + k), where n is the number of elements in the input list and k is the range of values. */ class CountingSort { int getMax(int[] a, int n) { int max = a[0]; for(int i = 1; i max) max = a[i]; } return max; //maximum element from the array } void countSort(int[] a, int n) // function to perform counting sort { int[] output = new int [n+1]; int max = getMax(a, n); //int max = 42; int[] count = new int [max+1]; //create count array with size [max+1] for (int i = 0; i <= max; ++i) { count[i] = 0; // Initialize count array with all zeros } for (int i = 0; i < n; i++) // Store the count of each element { count[a[i]]++; } for(int i = 1; i<=max; i++) count[i] += count[i-1]; //find cumulative frequency /* This loop will find the index of each element of the original array in count array, and place the elements in output array*/ for (int i = n - 1; i >= 0; i--) { output[count[a[i]] - 1] = a[i]; count[a[i]]--; // decrease count for same numbers } for(int i = 0; i 0); // Create a count array to store count of individual // characters and initialize count array as 0 var count = Array.from({length: 256}, (_, i) => 0); // store count of each character for (var i = 0; i < n; ++i) ++count[arr[i].charCodeAt(0)]; // Change count[i] so that count[i] now contains actual // position of this character in output array for (var i = 1; i <= 255; ++i) count[i] += count[i - 1]; // Build the output character array // To make it stable we are operating in reverse order. for (var i = n - 1; i >= 0; i--) { output[count[arr[i].charCodeAt(0)] - 1] = arr[i]; --count[arr[i].charCodeAt(0)]; } // Copy the output array to arr, so that arr now // contains sorted characters for (var i = 0; i < n; ++i) arr[i] = output[i]; return arr; } // Driver method var arr = [ 'g', 'e', 'e', 'k', 's', 'f', 'o', 'r', 'g', 'e', 'e', 'k', 's' ]; arr = sort(arr); document.write("Sorted character array is "); for (var i = 0; i < arr.length; ++i) document.write(arr[i]); ================================================ FILE: sorting/count_sort.py ================================================ # Count Sort # The Counting Sort algorithm is a non-comparative sorting algorithm that works by counting the occurrences of each distinct element in the input # list. It then uses this information to determine the correct position of each element in the sorted output. # Here are the steps involved in the Counting Sort algorithm: # 1. Find the range: Determine the range of values in the input list. This range is necessary to create an array with the appropriate size to store the counts. # 2. Count the occurrences: Create a count array of size equal to the range determined in the previous step. Iterate through the input list and count # the occurrences of each element by incrementing the corresponding count in the count array. # 3. Calculate cumulative counts: Modify the count array such that each element represents the cumulative count of elements up to that index. This step # ensures that the count array contains the correct positions for each element in the sorted order. # 4. Generate the sorted output: Create an output array of the same size as the input list. Iterate through the input list and use the count array to # determine the correct position of each element in the output array. Place each element in its corresponding position and decrement the count # in the count array. # 5. Return the sorted list: The output array now contains the elements in sorted order. Return this sorted list as the result of the Counting Sort algorithm. # Here's an example to illustrate the process: # Sample Input: [4, 2, 9, 4, 6, 1] # 1. Find the range: The range of values in the input list is from 1 to 9. # 2. Count the occurrences: Create the count array [0, 1, 1, 0, 2, 0, 1, 0, 0, 1], where the index represents the element and the value represents the count. # 3. Calculate cumulative counts: Modify the count array to [0, 1, 2, 2, 4, 4, 5, 5, 5, 6]. Each element represents the cumulative count of elements up to that index. # 4. Generate the sorted output: Create the output array [1, 2, 4, 4, 6, 9]. Iterate through the input list, use the count array to determine the correct # position of each element, place it in the output array, and decrement the count in the count array. # 5. Return the sorted list: The sorted list is [1, 2, 4, 4, 6, 9]. # Counting Sort is an efficient algorithm when the range of values in the input list is relatively small. It has a time complexity of O(n + k), where n is # the number of elements in the input list and k is the range of values. # The main function that sort the given string arr[] inalphabetical order def countSort(arr): # The output character array that will have sorted arr output = [0 for i in range(len(arr))] # Create a count array to store count of individual # characters and initialize count array as 0 count = [0 for i in range(256)] # For storing the resulting answer since the # string is immutable ans = ["" for _ in arr] # Store count of each character for i in arr: count[ord(i)] += 1 # Change count[i] so that count[i] now contains actual # position of this character in output array for i in range(256): count[i] += count[i-1] # Build the output character array for i in range(len(arr)): output[count[ord(arr[i])]-1] = arr[i] count[ord(arr[i])] -= 1 # Copy the output array to arr, so that arr now # contains sorted characters for i in range(len(arr)): ans[i] = output[i] return ans # Driver code if __name__ == '__main__': arr = "geeksforgeeks" ans = countSort(arr) print("Sorted character array is % s" % ("".join(ans))) ================================================ FILE: sorting/dnf.cpp ================================================ /* The Dutch National Flag algorithm is used to sort an array containing elements with values of 0, 1, and 2. The goal is to rearrange the elements in-place so that all the 0s are grouped at the beginning, followed by all the 1s, and finally all the 2s. The algorithm uses three pointers: low, mid, and high. The low pointer represents the boundary of the 0s section, the mid pointer scans the array, and the high pointer represents the boundary of the 2s section. The algorithm iterates through the array and performs the following operations: 1. If the element at the mid pointer is 0, it is swapped with the element at the low pointer, and both pointers are incremented. 2. If the element at the mid pointer is 1, it is already in the correct section, so the mid pointer is simply incremented. 3. If the element at the mid pointer is 2, it is swapped with the element at the high pointer, and the high pointer is decremented. The iteration continues until the mid pointer crosses the high pointer, indicating that all elements have been processed. After the algorithm finishes, the array will be sorted according to the Dutch National Flag problem requirements, with all 0s at the beginning, followed by 1s, and finally 2s. The sorting is done in-place, meaning it does not require any additional space. The time complexity of the Dutch National Flag algorithm is O(n), where n is the length of the array, as we only need to iterate through the array once. The space complexity is O(1) since no extra space is used apart from the input array. Consider an array: [1, 2, 0, 2, 1, 0]. The algorithm uses three pointers: low, mid, and high. Initially, low = 0, mid = 0, and high = 5. Iterate while mid <= high: If the element at mid is 0, swap it with the element at low, increment both low and mid. If the element at mid is 1, increment mid. If the element at mid is 2, swap it with the element at high, decrement high. After applying the algorithm, the sorted array will be: [0, 0, 1, 1, 2, 2]. In this example, the algorithm moves all the 0s to the beginning, followed by the 1s, and finally the 2s, achieving the desired sorting according to the Dutch National Flag problem requirements. */ #include #include std::vector DutchNationalFlag(std::vector& array) { int low = 0; // Initialize the low pointer to the beginning of the array int mid = 0; // Initialize the mid pointer to the beginning of the array int high = array.size() - 1; // Initialize the high pointer to the end of the array while (mid <= high) { switch (array[mid]) { case 0: // If the value at mid is 0, swap it with the value at low std::swap(array[low], array[mid]); low++; // Increment low to move forward mid++; // Increment mid to move forward break; case 1: // If the value at mid is 1, no swapping needed, just move mid forward mid++; break; case 2: // If the value at mid is 2, swap it with the value at high std::swap(array[mid], array[high]); high--; // Decrement high to move backward break; } } return array; } int main() { std::vector array = {2, 0, 1, 1, 0, 2, 2, 1, 0}; std::vector sortedArray = DutchNationalFlag(array); std::cout << "Sorted Array: "; for (int64_t num : sortedArray) { std::cout << num << " "; } std::cout << std::endl; return 0; } ================================================ FILE: sorting/dnf.go ================================================ /* The Dutch National Flag algorithm is used to sort an array containing elements with values of 0, 1, and 2. The goal is to rearrange the elements in-place so that all the 0s are grouped at the beginning, followed by all the 1s, and finally all the 2s. The algorithm uses three pointers: low, mid, and high. The low pointer represents the boundary of the 0s section, the mid pointer scans the array, and the high pointer represents the boundary of the 2s section. The algorithm iterates through the array and performs the following operations: 1. If the element at the mid pointer is 0, it is swapped with the element at the low pointer, and both pointers are incremented. 2. If the element at the mid pointer is 1, it is already in the correct section, so the mid pointer is simply incremented. 3. If the element at the mid pointer is 2, it is swapped with the element at the high pointer, and the high pointer is decremented. The iteration continues until the mid pointer crosses the high pointer, indicating that all elements have been processed. After the algorithm finishes, the array will be sorted according to the Dutch National Flag problem requirements, with all 0s at the beginning, followed by 1s, and finally 2s. The sorting is done in-place, meaning it does not require any additional space. The time complexity of the Dutch National Flag algorithm is O(n), where n is the length of the array, as we only need to iterate through the array once. The space complexity is O(1) since no extra space is used apart from the input array. Consider an array: [1, 2, 0, 2, 1, 0]. The algorithm uses three pointers: low, mid, and high. Initially, low = 0, mid = 0, and high = 5. Iterate while mid <= high: If the element at mid is 0, swap it with the element at low, increment both low and mid. If the element at mid is 1, increment mid. If the element at mid is 2, swap it with the element at high, decrement high. After applying the algorithm, the sorted array will be: [0, 0, 1, 1, 2, 2]. In this example, the algorithm moves all the 0s to the beginning, followed by the 1s, and finally the 2s, achieving the desired sorting according to the Dutch National Flag problem requirements. */ package main import "fmt" func DutchNationalFlag(array []int64) []int64 { // Initialize Low, Mid, and High pointers var ( Low = 0 Mid = 0 High = len(array) - 1 ) // Iterate while Mid pointer is less than or equal to High pointer for Mid <= High { // Check the value at Mid pointer switch array[Mid] { // Case 0: Value is 0, so swap it with the value at Low pointer // Increment both Low and Mid pointers to move forward case 0: array[Low], array[Mid] = array[Mid], array[Low] Low++ Mid++ // Case 1: Value is 1, no swapping needed // Increment Mid pointer to move forward case 1: Mid++ // Case 2: Value is 2, so swap it with the value at High pointer // Decrement High pointer to move backward case 2: array[Mid], array[High] = array[High], array[Mid] High-- } } // Return the sorted array return array } ================================================ FILE: sorting/dnf.java ================================================ package sorting; /* *Explanation: The code above solves the Dutch National Flag problem. It takes an array of integers and a pivot element as input, and sorts the array in the Dutch National Flag order. The Dutch National Flag problem involves partitioning the array into three sections based on the pivot element. The dutchNationalFlagSort method uses three pointers: low, mid, and high. The low pointer represents the boundary for elements less than the pivot, the mid pointer represents the boundary for elements equal to the pivot, and the high pointer represents the boundary for elements greater than the pivot. The method uses a while loop that continues until the mid pointer surpasses the high pointer. Inside the loop, it compares the element at the mid index with the pivot. If the element is less than the pivot, it swaps it with the element at the low index and increments both low and mid. If the element is greater than the pivot, it swaps it with the element at the high index and decrements high. If the element is equal to the pivot, it increments mid. The swap method is a helper function that swaps two elements in the array. In the main method, an example array and pivot value are provided. The dutchNationalFlagSort method is called with these values, and the sorted array is printed. Time Complexity: The time complexity of the Dutch National Flag algorithm is O(n), where n is the length of the input array. Space Complexity: The space complexity is O(1) as the algorithm sorts the array in-place without using any additional data structures. **/ import java.util.Arrays; public class DutchNationalFlag { /** * Sorts an array of integers in the Dutch National Flag order. * The Dutch National Flag problem partitions the array into three sections: * - All elements less than the pivot are placed before it. * - All elements greater than the pivot are placed after it. * - All elements equal to the pivot are placed in the middle. * * @param array the array of integers to be sorted * @param pivot the pivot element */ public static void main(String[] args) { int[] array = {2, 2, 1, 1, 0, 0, 2, 1, 0}; int pivot = 1; dutchNationalFlagSort(array, pivot); System.out.println("Sorted Array: " + Arrays.toString(array)); } public static void dutchNationalFlagSort(int[] array, int pivot) { int low = 0; // Pointer for elements less than the pivot int mid = 0; // Pointer for elements equal to the pivot int high = array.length - 1; // Pointer for elements greater than the pivot while (mid <= high) { if (array[mid] < pivot) { // Current element is less than the pivot swap(array, low, mid); // Swap current element with element at the low index low++; // Increment low pointer mid++; // Increment mid pointer } else if (array[mid] > pivot) { // Current element is greater than the pivot swap(array, mid, high); // Swap current element with element at the high index high--; // Decrement high pointer } else { // Current element is equal to the pivot mid++; // Increment mid pointer } } } /** * Swaps two elements in an array. * * @param array the array * @param i the index of the first element * @param j the index of the second element */ private static void swap(int[] array, int i, int j) { int temp = array[i]; array[i] = array[j]; array[j] = temp; } } ================================================ FILE: sorting/dnf.js ================================================ /* Dutch National Flag Problem Given an array containing only 0s, 1s, and 2s, sort the array in a single traversal. Sample Input: [0, 2, 1, 2, 0] Sample Output: [0, 0, 1, 2, 2] Approach: - We can use three pointers, low, mid, and high, to divide the array into three regions: 1. 0s region: elements before the low pointer (excluding low) are 0s 2. 1s region: elements between the low and mid pointers (excluding mid) are 1s 3. 2s region: elements after the high pointer (excluding high) are 2s - Initialize low and mid pointers to the start of the array (0 index) and high pointer to the end of the array (array.length - 1). - Iterate while the mid pointer is less than or equal to the high pointer: - If the current element at mid is 0, swap it with the element at low and increment both low and mid pointers. - If the current element at mid is 1, it is already in the correct region, so we just increment the mid pointer. - If the current element at mid is 2, swap it with the element at high and decrement the high pointer. - Repeat the above steps until the mid pointer crosses the high pointer. - At the end, the array will be sorted in place. Time Complexity: O(n), where n is the length of the array. Space Complexity: O(1), no additional space is used. Further Reading: https://en.wikipedia.org/wiki/Dutch_national_flag_problem */ function dutchNationalFlagProblem(arr) { let low = 0; let mid = 0; let high = arr.length - 1; while (mid <= high) { if (arr[mid] === 0) { // Swap current element at mid with element at low [arr[mid], arr[low]] = [arr[low], arr[mid]]; // Increment both low and mid pointers low++; mid++; } else if (arr[mid] === 1) { // Move to the next element in the 1s region mid++; } else { // Swap current element at mid with element at high [arr[mid], arr[high]] = [arr[high], arr[mid]]; // Decrement the high pointer high--; } } return arr; } // Test the function const input = [0, 2, 1, 2, 0]; const output = dutchNationalFlagProblem(input); console.log(output); ================================================ FILE: sorting/dnf.py ================================================ # Approach Explanation: # The code implements the Dutch National Flag problem, which is a sorting problem where we need to sort an array consisting of 0s, 1s, and 2s in ascending order. The approach used is called the "Three Pointers" approach. # We initialize three pointers, low, mid, and high, which represent the boundaries of three sections in the array: # All elements before low are 0s (left section). # All elements between low and mid are 1s (middle section). # All elements after high are 2s (right section). # We iterate through the array using the mid pointer: # If the element at mid is 0, we swap it with the element at low and increment both low and mid pointers. # If the element at mid is 1, we increment the mid pointer. # If the element at mid is 2, we swap it with the element at high and decrement the high pointer. # By doing this, we move all the 0s to the left section, 1s to the middle section, and 2s to the right section, effectively sorting the array. # Time Complexity: O(n), where n is the length of the input array. # Space Complexity: O(1) (constant space), as we are sorting the array in-place # Sample Input: # arr = [2, 0, 1, 2, 1, 0] # Sample Output: # Input Array: [2, 0, 1, 2, 1, 0] # Output Array: [0, 0, 1, 1, 2, 2] def dutch_national_flag_problem(arr): # Initialize variables for the three pointers low = 0 mid = 0 high = len(arr) - 1 # Loop through the array until mid and high pointers meet while mid <= high: if arr[mid] == 0: # Swap the element at mid with the element at low arr[mid], arr[low] = arr[low], arr[mid] # Move the low and mid pointers to the right low += 1 mid += 1 elif arr[mid] == 1: # Increment the mid pointer mid += 1 else: # Swap the element at mid with the element at high arr[mid], arr[high] = arr[high], arr[mid] # Move the high pointer to the left high -= 1 return arr # Test the function arr = [2, 0, 1, 2, 1, 0] result = dutch_national_flag_problem(arr) print("Input Array:", arr) print("Output Array:", result) ================================================ FILE: sorting/heap_sort.cpp ================================================ /* Heap sort is a sorting technique based on comparison based on binary heap data. Similar to sorting, it finds the largest number first and then puts the largest number last. This sorting algorithm uses a tree structure called the stack, where the stack is a kind of binary tree. A binary decision tree in which the value of the root of a tree is less than or equal to the value of one of its roots is called a min-heap. A decision binary tree is called maximum heap when the value of the root of a tree is greater than or equal to the value of one of its trees. In this post, we'll learn more about C++ Stack Sorting. Working of heap sort in C++ To sort any list into a logical order following steps are followed:- Convert the list into a heap. Now convert this heap into a max heap. As the heap is converted to max heap largest element in the list is stored in the root of the heap, replace it with the last item of the heap. Now delete this node and reduce the size of the heap by 1. Follow these steps until the list is sorted. */ #include using namespace std; void heapify(int arr[], int n, int i){ int largest = i; int l = 2*i + 1; int r = 2*i + 2; //If left child is larger than root if (l < n && arr[l] > arr[largest]) largest = l; //If right child largest if (r < n && arr[r] > arr[largest]) largest = r; //If root is nor largest if (largest != i){ swap(arr[i], arr[largest]); //Recursively heapifying the sub-tree heapify(arr, n, largest); } } void heapSort(int arr[], int n){ for (int i = n / 2 - 1; i >= 0; i--) heapify(arr, n, i); //One by one extract an element from heap for (int i=n-1; i>=0; i--){ //Moving current root to end swap(arr[0], arr[i]); //Calling max heapify on the reduced heap heapify(arr, i, 0); } } //Function to print array void display(int arr[], int n){ for (int i = 0; i < n; i++){ cout << arr[i] << "\t"; } cout << "\n"; } int main(){ int arr[] = {1, 14, 3, 7, 0}; int n = sizeof(arr)/sizeof(arr[0]); cout << "Unsorted array \n"; display(arr, n); heapSort(arr, n); cout << "Sorted array \n"; display(arr, n); } /*Time Complexcity Best O(nlog n) Average O(nlog n) Worst O(nlog n) */ ================================================ FILE: sorting/heap_sort.java ================================================ /*Since the tree satisfies Max-Heap property, then the largest item is stored at the root node. Swap: Remove the root element and put at the end of the array (nth position) Put the last item of the tree (heap) at the vacant place. Remove: Reduce the size of the heap by 1. Heapify: Heapify the root element again so that we have the highest element at root. The process is repeated until all the items of the list are sorted.*/ // Heap Sort in Java language public class heap_sort { public void sort(int arr[]) { int n = arr.length; // Build max heap for (int i = n / 2 - 1; i >= 0; i--) { heapify(arr, n, i); } // Heap sort for (int i = n - 1; i >= 0; i--) { int temp = arr[0]; arr[0] = arr[i]; arr[i] = temp; // Heapify root element heapify(arr, i, 0); } } void heapify(int arr[], int n, int i) { // Find largest among root, left child and right child int largest = i; int l = 2 * i + 1; int r = 2 * i + 2; if (l < n && arr[l] > arr[largest]) largest = l; if (r < n && arr[r] > arr[largest]) largest = r; // Swap and continue heapifying if root is not largest if (largest != i) { int swap = arr[i]; arr[i] = arr[largest]; arr[largest] = swap; heapify(arr, n, largest); } } // Function to print an array static void printArray(int arr[]) { int n = arr.length; for (int i = 0; i < n; ++i) System.out.print(arr[i] + " "); System.out.println(); } // Driver code public static void main(String args[]) { int arr[] = { 1, 12, 9, 5, 6, 10 }; heap_sort hs = new heap_sort(); hs.sort(arr); System.out.println("Sorted array is"); printArray(arr); } } ================================================ FILE: sorting/heap_sort.js ================================================ /** Heap Sort is a sorting algorithm that works by building a binary heap out of the input array and then repeatedly extracting the maximum element and restoring the heap property. The time complexity of Heap Sort is O(n log n) in the worst, average, and best cases, making it an efficient algorithm for sorting large data sets. The space complexity of the implementation is O(1) because the algorithm sorts the input array in place and does not use any additional data structures other than a few variables used for index tracking and swapping. Therefore, the space used by the algorithm does not depend on the size of the input array. */ /** * The heapSort function takes an array as input and returns a sorted array using the Heap Sort algorithm. The function first builds a max heap from the input array using the buildMaxHeap function. It then repeatedly extracts the maximum element from the heap and restores the heap property using the siftDown function * @param {Array} array The array to be sorted. * @returns {Array} The sorted array. * time complexity is O(n log n) because it calls buildMaxHeap function once and then the siftDown function n times in the worst case, where n is the length of the input array. */ function heapSort(array) { // Build a max heap from the input array buildMaxHeap(array); // Extract the max element from the heap and restore the heap property for (let i = array.length - 1; i > 0; i--) { // Move the root element to the end of the array swap(array, 0, i); // Restore the heap property by sifting down the root element siftDown(array, 0, i); } return array; } /** * The buildMaxHeap function takes an array as input and transforms it into a max heap by calling siftDown on each non-leaf node in the tree. * @param {Array} array The array to be transformed into a max heap. * time complexity is O(n) because it calls the siftDown function on each non-leaf node in the tree */ function buildMaxHeap(array) { const startIndex = Math.floor((array.length - 1) / 2); for (let i = startIndex; i >= 0; i--) { siftDown(array, i, array.length); } } /** * The siftDown function takes an array, an index, and an endIndex as input. It sifts down the element at the given index in the heap until it reaches its correct position. * @param {Array} array The array representing the heap. * @param {number} index The index of the element to be sifted down. * @param {number} endIndex The index at which to stop sifting down. * time complexity is O(log n) because it repeatedly swaps the element at the given index with its child elements until it reaches its correct position in the heap. */ function siftDown(array, index, endIndex) { let childIndex1 = index * 2 + 1; while (childIndex1 < endIndex) { const childIndex2 = index * 2 + 2 < endIndex ? index * 2 + 2 : null; const swapIndex = childIndex2 !== null && array[childIndex2] > array[childIndex1] ? childIndex2 : childIndex1; if (array[swapIndex] > array[index]) { swap(array, index, swapIndex); index = swapIndex; childIndex1 = index * 2 + 1; } else { return; } } } /** * The swap function takes an array and two indices as input and swaps the elements at those indices. It is used in the heapSort function to move the root element of the heap to the end of the array during each iteration of the main loop. This function is also used within the siftDown function to swap the parent element with its child element when restoring the heap property. * @param {Array} array The array containing the elements to be swapped. * @param {number} i The index of the first element. * @param {number} j The index of the second element. */ function swap(array, i, j) { const temp = array[i]; array[i] = array[j]; array[j] = temp; } ================================================ FILE: sorting/heap_sort.py ================================================ ''' Heap sort is a sorting algorithm that works by transforming an unsorted list into a heap data structure, which is a binary tree where each parent node is greater than or equal to its children if any. then it repeatedly extracts the maximum element from the heap and puts it into its correct sorted position until the whole list is sorted. Sample_input : [5,16,8,14,20,1,26] Sample_output : [1,5,8,14,16,20,26] Here's how the algorithm works: The heap_sort function takes an unsorted list arr as input. It starts by building the initial heap by calling the heapify function on each parent node in the tree. It does this by iterating over the parent nodes in reverse order starting from the last parent node, and calling heapify on each of them. This builds a heap where each parent node is greater than or equal to its children. It then repeatedly extracts the maximum element from the heap and puts it into its correct sorted position. It does this by swapping the maximum element which is always at the root of the heap with the last element in the heap, and then calling heapify on the root node to restore the heap property. Finally, it returns the sorted list. ''' def heap_sort(arr): # Build the initial heap n = len(arr) for i in range(n // 2 - 1, -1, -1): heapify(arr, n, i) # Extract the maximum element repeatedly for i in range(n - 1, 0, -1): arr[0], arr[i] = arr[i], arr[0] # Swap heapify(arr, i, 0) return arr def heapify(arr, n, i): largest = i left = 2 * i + 1 right = 2 * i + 2 # Check if left child is larger than root if left < n and arr[left] > arr[largest]: largest = left # Check if right child is larger than root if right < n and arr[right] > arr[largest]: largest = right # Swap if necessary and heapify the affected subtree if largest != i: arr[i], arr[largest] = arr[largest], arr[i] heapify(arr, n, largest) ================================================ FILE: sorting/insertion_sort.cpp ================================================ /* Insertion sort is a simple sorting algorithm that builds the final sorted array one item at a time. It starts with the second element of the array, compares it with the first element, and swaps them if necessary. It then continues to the third element, compares it with the first and second elements, and swaps it into the correct position. This process continues until the last element is compared and sorted into its correct position in the sorted array. At each iteration, the algorithm assumes that the subarray to the left of the current element is already sorted, and it searches for the correct position of the current element within that subarray by comparing it to each element from right to left until it finds the correct position. Once it finds the correct position, it shifts all the larger elements to the right to make space for the current element and inserts it in its correct position. Insertion sort has an average-case time complexity of O(n^2) but can perform better than other O(n^2) algorithms, such as bubble sort or selection sort, in certain cases. It is also an efficient algorithm for small data sets or partially sorted data sets. In this implementation, we define a function called InsertionSort that takes an array of integers and sorts it in ascending order using the Insertion Sort algorithm. The algorithm works by iterating over the array from the second element to the end. For each element, it compares it with the previous elements in the array and inserts it in the correct position. The current variable holds the value of the current element being compared. The j variable holds the index of the previous element being compared. The loop compares the current value with the previous values in the array and shifts the values to the right to make space for the current value. Once the correct position is found, the current value is inserted into the array. Finally, the sorted array is returned. In the main function, we define an array of integers, sort it using the InsertionSort function, and print the sorted array. Sample input: [0, 2, 1,-1, 10, 3, 4] Output: [-1 0 1 2 3 4 10] */ #include #include using namespace std; // InsertionSort is a function that takes an array of integers and sorts it in // ascending order using the Insertion Sort algorithm. void insertionSort(vector& arr) { int n = arr.size(); for(int i=1; i=0 && arr[j]>key) { arr[j+1] = arr[j]; j--; } arr[j+1] = key; // insert the key at its sorted position } } int main() { vector arr = {3, 5, 1, 4, 2}; insertionSort(arr); cout << "Sorted Array: "; for(int num: arr) { cout << num << " "; } cout << endl; return 0; } ================================================ FILE: sorting/insertion_sort.go ================================================ /* Insertion sort is a simple sorting algorithm that builds the final sorted array one item at a time. It starts with the second element of the array, compares it with the first element, and swaps them if necessary. It then continues to the third element, compares it with the first and second elements, and swaps it into the correct position. This process continues until the last element is compared and sorted into its correct position in the sorted array. At each iteration, the algorithm assumes that the subarray to the left of the current element is already sorted, and it searches for the correct position of the current element within that subarray by comparing it to each element from right to left until it finds the correct position. Once it finds the correct position, it shifts all the larger elements to the right to make space for the current element and inserts it in its correct position. Insertion sort has an average-case time complexity of O(n^2) but can perform better than other O(n^2) algorithms, such as bubble sort or selection sort, in certain cases. It is also an efficient algorithm for small data sets or partially sorted data sets. In this implementation, we define a function called InsertionSort that takes an array of integers and sorts it in ascending order using the Insertion Sort algorithm. The algorithm works by iterating over the array from the second element to the end. For each element, it compares it with the previous elements in the array and inserts it in the correct position. The current variable holds the value of the current element being compared. The j variable holds the index of the previous element being compared. The loop compares the current value with the previous values in the array and shifts the values to the right to make space for the current value. Once the correct position is found, the current value is inserted into the array. Finally, the sorted array is returned. In the main function, we define an array of integers, sort it using the InsertionSort function, and print the sorted array. Sample input: [0, 2, 1,-1, 10, 3, 4] Output: [-1 0 1 2 3 4 10] */ package main import "fmt" // InsertionSort is a function that takes an array of integers and sorts it in // ascending order using the Insertion Sort algorithm. func InsertionSort(arr []int) []int { // Iterate over the array from the second element to the end for i := 1; i < len(arr); i++ { // Set the current value and the previous index current := arr[i] j := i - 1 // Compare the current value with the previous values in the array for j >= 0 && arr[j] > current { // Shift the values to the right to make space for the current value arr[j+1] = arr[j] j-- } // Insert the current value in the correct position arr[j+1] = current } // Return the sorted array return arr } func main() { arr := []int{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5} sortedArr := InsertionSort(arr) fmt.Println(sortedArr) } ================================================ FILE: sorting/insertion_sort.java ================================================ /* Insertion sort is a simple sorting algorithm that builds the final sorted array one item at a time. It starts with the second element of the array, compares it with the first element, and swaps them if necessary. It then continues to the third element, compares it with the first and second elements, and swaps it into the correct position. This process continues until the last element is compared and sorted into its correct position in the sorted array. At each iteration, the algorithm assumes that the subarray to the left of the current element is already sorted, and it searches for the correct position of the current element within that subarray by comparing it to each element from right to left until it finds the correct position. Once it finds the correct position, it shifts all the larger elements to the right to make space for the current element and inserts it in its correct position. Insertion sort has an average-case time complexity of O(n^2) but can perform better than other O(n^2) algorithms, such as bubble sort or selection sort, in certain cases. It is also an efficient algorithm for small data sets or partially sorted data sets. In this implementation, we define a function called InsertionSort that takes an array of integers and sorts it in ascending order using the Insertion Sort algorithm. The algorithm works by iterating over the array from the second element to the end. For each element, it compares it with the previous elements in the array and inserts it in the correct position. The current variable holds the value of the current element being compared. The j variable holds the index of the previous element being compared. The loop compares the current value with the previous values in the array and shifts the values to the right to make space for the current value. Once the correct position is found, the current value is inserted into the array. Finally, the sorted array is returned. In the main function, we define an array of integers, sort it using the InsertionSort function, and print the sorted array. Sample input: [0, 2, 1,-1, 10, 3, 4] Output: [-1 0 1 2 3 4 10] */ // InsertionSort is a function that takes an array of integers and sorts it in // ascending order using the Insertion Sort algorithm. public class InsertionSort { public static void insertionSort(int[] arr) { int n = arr.length; for (int i = 1; i < n; ++i) { int key = arr[i]; int j = i - 1; /* Move elements of arr[0..i-1], that are greater than key, to one position ahead of their current position */ while (j >= 0 && arr[j] > key) { arr[j + 1] = arr[j]; j = j - 1; } arr[j + 1] = key; } } public static void main(String[] args) { int[] arr = { 12, 11, 13, 5, 6 }; insertionSort(arr); System.out.println(Arrays.toString(arr)); } } ================================================ FILE: sorting/insertion_sort.js ================================================ /* Insertion sort is a simple sorting algorithm that builds the final sorted array one item at a time. It starts with the second element of the array, compares it with the first element, and swaps them if necessary. It then continues to the third element, compares it with the first and second elements, and swaps it into the correct position. This process continues until the last element is compared and sorted into its correct position in the sorted array. At each iteration, the algorithm assumes that the subarray to the left of the current element is already sorted, and it searches for the correct position of the current element within that subarray by comparing it to each element from right to left until it finds the correct position. Once it finds the correct position, it shifts all the larger elements to the right to make space for the current element and inserts it in its correct position. Insertion sort has an average-case time complexity of O(n^2) but can perform better than other O(n^2) algorithms, such as bubble sort or selection sort, in certain cases. It is also an efficient algorithm for small data sets or partially sorted data sets. In this implementation, we define a function called InsertionSort that takes an array of integers and sorts it in ascending order using the Insertion Sort algorithm. The algorithm works by iterating over the array from the second element to the end. For each element, it compares it with the previous elements in the array and inserts it in the correct position. The current variable holds the value of the current element being compared. The j variable holds the index of the previous element being compared. The loop compares the current value with the previous values in the array and shifts the values to the right to make space for the current value. Once the correct position is found, the current value is inserted into the array. Finally, the sorted array is returned. In the main function, we define an array of integers, sort it using the InsertionSort function, and print the sorted array. Sample input: [0, 2, 1,-1, 10, 3, 4] Output: [-1 0 1 2 3 4 10] */ /** * Perform insertion sort on an array of integers in non-decreasing order. * @param {number[]} arr - The input array to sort. * @returns {number[]} The sorted array in non-decreasing order. */ function insertionSort(arr) { // Loop through each element of the array, starting with the second. for (let i = 1; i < arr.length; i++) { // Save the current element to be inserted later. let current = arr[i]; // Loop through the sorted portion of the array backwards. for (let j = i - 1; j >= 0 && arr[j] > current; j--) { // Shift each element that is greater than the current element up one position. arr[j + 1] = arr[j]; } // Insert the current element in its proper place. arr[j + 1] = current; } // Return the sorted array. return arr; } const inputArr = [4, 5, 67, 56, 3, 35, 45]; console.log(insertionSort(inputArr)); ================================================ FILE: sorting/insertion_sort.py ================================================ """ Insertion sort is a simple sorting algorithm that builds the final sorted array one item at a time. It starts with the second element of the array, compares it with the first element, and swaps them if necessary. It then continues to the third element, compares it with the first and second elements, and swaps it into the correct position. This process continues until the last element is compared and sorted into its correct position in the sorted array. At each iteration, the algorithm assumes that the subarray to the left of the current element is already sorted, and it searches for the correct position of the current element within that subarray by comparing it to each element from right to left until it finds the correct position. Once it finds the correct position, it shifts all the larger elements to the right to make space for the current element and inserts it in its correct position. Insertion sort has an average-case time complexity of O(n^2) but can perform better than other O(n^2) algorithms, such as bubble sort or selection sort, in certain cases. It is also an efficient algorithm for small data sets or partially sorted data sets. In this implementation, we define a function called InsertionSort that takes an array of integers and sorts it in ascending order using the Insertion Sort algorithm. The algorithm works by iterating over the array from the second element to the end. For each element, it compares it with the previous elements in the array and inserts it in the correct position. The current variable holds the value of the current element being compared. The j variable holds the index of the previous element being compared. The loop compares the current value with the previous values in the array and shifts the values to the right to make space for the current value. Once the correct position is found, the current value is inserted into the array. Finally, the sorted array is returned. In the main function, we define an array of integers, sort it using the InsertionSort function, and print the sorted array. Sample input: [0, 2, 1,-1, 10, 3, 4] Output: [-1 0 1 2 3 4 10] """ def insertion_sort(arr): """ Sorts an array in ascending order using the insertion sort algorithm. @param arr: list of integers to be sorted @return: sorted list of integers """ # iterate through every element of the array for i in range(1, len(arr)): # store the current element and its index current = arr[i] j = i - 1 # move all elements greater than the current element to the right while j >= 0 and arr[j] > current: arr[j + 1] = arr[j] j -= 1 # insert the current element in its correct position arr[j + 1] = current # return the sorted array return arr ================================================ FILE: sorting/merge_sort.cpp ================================================ // Merge Sort /* Here's how the merge sort algorithm works: 1. It divides the input array into two halves, recursively sorts them, and then merges the sorted halves. 2. To merge two sorted sub-arrays, we need to create a temporary array and then compare the elements of the two sub-arrays, one by one, and add the smaller element to the temporary array. 3. After we have exhausted one of the sub-arrays, we simply copy the remaining elements of the other sub-array to the temporary array. 4. Finally, we copy the elements of the temporary array back to the original array. The time complexity of merge sort is O(n log n), where n is the number of elements in the array. The space complexity is O(n), because we create a temporary array of size n during the merging process. */ #include #include using namespace std; void merge(vector& arr, int start, int mid, int end) { vector temp(end - start + 1); int i = start, j = mid + 1, k = 0; while (i <= mid && j <= end) { if (arr[i] <= arr[j]) { temp[k] = arr[i]; ++i; } else { temp[k] = arr[j]; ++j; } ++k; } while (i <= mid) { temp[k] = arr[i]; ++i; ++k; } while (j <= end) { temp[k] = arr[j]; ++j; ++k; } for (int l = start; l <= end; ++l) { arr[l] = temp[l - start]; } } void merge_sort(vector& arr, int start, int end) { if (start < end) { int mid = (start + end) / 2; merge_sort(arr, start, mid); merge_sort(arr, mid + 1, end); merge(arr, start, mid, end); } } int main() { vector arr = {5, 2, 8, 1, 9, 4}; merge_sort(arr, 0, arr.size() - 1); for (int num : arr) { cout << num << " "; } cout << endl; return 0; } ================================================ FILE: sorting/merge_sort.go ================================================ // Merge Sort /* The MergeSort function is the main function that takes an integer array as input and sorts it using the Merge Sort algorithm. In the MergeSort function, if the length of the input array is only one element, it is already sorted, so we return it as is. Otherwise, we find the middle point of the array and split it into two halves. We then recursively call the MergeSort function on the left half and the right half of the array. Finally, we merge the two sorted halves using the merge function. The merge function takes two sorted arrays as input and merges them into one sorted array. We initialize a new array to hold the merged result and three index variables for the left, right, and result arrays. We then iterate over the left and right arrays and compare their elements. We add the smaller element to the result array and move the corresponding index variable. Finally, we append the remaining elements of the left or right array to the result array and return it. The time complexity of Merge Sort is O(n*log n), where n is the number of elements in the array, and the space complexity is O(n) due to the use of the temporary arrays during the merging phase. */ package main import "fmt" // MergeSort is the main function that takes an integer array as input // and sorts it using the Merge Sort algorithm. func MergeSort(arr []int) []int { // If the array has only one element, return it. if len(arr) == 1 { return arr } // Find the middle point to divide the array into two halves. mid := len(arr) / 2 // Split the array into two halves. left := arr[:mid] right := arr[mid:] // Recursively sort the left half of the array. left = MergeSort(left) // Recursively sort the right half of the array. right = MergeSort(right) // Merge the two sorted halves. return merge(left, right) } // Merge is a helper function that takes two sorted arrays and merges them into one sorted array. func merge(left, right []int) []int { // Initialize a new array to hold the merged result. result := make([]int, len(left)+len(right)) // Initialize the index variables for the left, right, and result arrays. i := 0 // Index for left array j := 0 // Index for right array k := 0 // Index for result array // Iterate over the left and right arrays and compare their elements. // Add the smaller element to the result array and move the corresponding index variable. for i < len(left) && j < len(right) { if left[i] < right[j] { result[k] = left[i] i++ } else { result[k] = right[j] j++ } k++ } // Append the remaining elements of the left or right array to the result array. for i < len(left) { result[k] = left[i] i++ k++ } for j < len(right) { result[k] = right[j] j++ k++ } // Return the merged and sorted result array. return result } func main() { // Example usage arr := []int{3, 2, 1, 5, 4} fmt.Println("Unsorted array:", arr) arr = MergeSort(arr) fmt.Println("Sorted array:", arr) } ================================================ FILE: sorting/merge_sort.java ================================================ // In computer science, merge sort (also commonly spelled as mergesort) is an efficient, general-purpose, // and comparison-based sorting algorithm. Most implementations produce a stable sort, // which means that the order of equal elements is the same in the input and output. // Merge sort is a divide-and-conquer algorithm that was invented by John von Neumann in 1945. // A detailed description and analysis of bottom-up merge sort appeared in a report by Goldstine and von Neumann as early as 1948. // Conceptually, a merge sort works as follows: // Divide the unsorted list into n sublists, each containing one element (a list of one element is considered sorted). // Repeatedly merge sublists to produce new sorted sublists until there is only one sublist remaining. This will be the sorted list // Source(https://en.wikipedia.org/wiki/Merge_sort) // Approach: Divide by finding the number mid of the position midway between left and right. Do this step the same // way we found the midpoint in binary search // Conquer by recursively sorting the subarrays in each of the two subproblems created by the divide step. // That is, recursively sort the subarray Arr[left. . mid] and recursively sort the subarray Arr[mid + 1. . right]. // Combine by merging the two sorted subarrays back into the single sorted subarray Arr[left. . right]. class MergeSort { void mergeSort(int ar[], int p,int r){ if(p None: if start < n: mid: int = (start + n) // 2 merge_sort(a_lst, start, mid) merge_sort(a_lst, mid + 1, n) merge(a_lst, start, mid, n) def merge(a_lst: list[int], start: int, mid: int, n: int): n_left: int = mid - start + 1 # length of left array n_right: int = n - mid # length of right array left: list[int] = [0] * n_left # Initialize left and right arrays right: list[int] = [0] * n_right for idx in range(n_left): # Fill left and right arrays left[idx] = a_lst[start + idx] for idx in range(n_right): right[idx] = a_lst[mid + idx + 1] # Fill the orignal array with the smallest element from either # left or right until left or right is empty l_idx: int = 0 r_idx: int = 0 idx: int = start while l_idx < len(left) and r_idx < len(right): if left[l_idx] < right[r_idx]: a_lst[idx] = left[l_idx] l_idx += 1 else: a_lst[idx] = right[r_idx] r_idx += 1 idx += 1 # Fill the original array with the rest of the elements # from the half array that is nonempty while l_idx < len(left): a_lst[idx] = left[l_idx] l_idx += 1 idx += 1 while r_idx < len(right): a_lst[idx] = right[r_idx] r_idx += 1 idx += 1 if __name__ == "__main__": main() ================================================ FILE: sorting/quick_sort.cpp ================================================ #include using namespace std; int Hoare_partition(int arr[],int l,int h) { // Here p is the Index of pivot element // Here We consider first element as pivot, // But if we Want to consider any element as pivot then just swap that index with the first element int pivot=arr[l]; int i=l-1,j=h+1; while(true) { do{ i++; }while(arr[i]pivot); if(i>=j) return j; swap(arr[i],arr[j]); } } void Quick_Sort(int arr[],int low,int high) { if(low pivot { j-- } // Swap elements at i and j if they are in the wrong subarray if i <= j { arr[i], arr[j] = arr[j], arr[i] i++ j-- } } // Recursively sort left and right subarrays if left < j { quicksort(arr, left, j) } if i < right { quicksort(arr, i, right) } } func main() { // Example usage arr := []int{3, 7, 1, 8, 4, 2, 9, 5, 6} quicksort(arr, 0, len(arr)-1) fmt.Println(arr) // Output: [1 2 3 4 5 6 7 8 9] } ================================================ FILE: sorting/quick_sort.java ================================================ /* Quick sort: Quicksort picks an element as pivot, and then it partitions the given array around the picked pivot element. In quick sort, a large array is divided into two arrays in which one holds values that are smaller than the specified value (Pivot), and another array holds the values that are greater than the pivot. After that, left and right sub-arrays are also partitioned using the same approach. It will continue until the single element remains in the sub-array. For more information: --> https://www.geeksforgeeks.org/java-program-for-quicksort/ --> https://www.javatpoint.com/quick-sort */ package sorting; public class quick_sort { public static void main(String[] args) { int[] a = {15,4,3,7,13,2,6,7,3,2,1,5,6,3,2}; int n = a.length; sort(a, 0, n-1); System.out.println("sorted array"); for (int j : a) System.out.print(j + " "); System.out.println(); } /** * * @param a to be sorted * @param start starting index * @param end ending index */ private static void sort(int[] a, int start, int end){ if (start < end) { int p = partition(a, start, end); sort(a, start, p-1); sort(a, p+1, end); } } /** * smaller elements to the left of the pivot, greater elements to the right * @param a to be sorted * @param start starting index * @param end ending index * @return index */ private static int partition(int[] a, int start, int end) { int pivot = a[end]; int i = (start-1); // index of smaller element for (int j=start; j using namespace std; // Function to get maximum value in arr[] int getMax(int arr[], int n) { int mx = arr[0]; for (int i = 1; i < n; i++) if (arr[i] > mx) mx = arr[i]; return mx; } // A function to do counting sort of arr[] according to // the digit represented by exp. void countSort(int arr[], int n, int exp) { int output[n]; // output array int i, count[10] = { 0 }; // Store count of occurrences in count[] for (i = 0; i < n; i++) count[(arr[i] / exp) % 10]++; // Change count[i] so that count[i] now contains actual // position of this digit in output[] for (i = 1; i < 10; i++) count[i] += count[i - 1]; // Build the output array for (i = n - 1; i >= 0; i--) { output[count[(arr[i] / exp) % 10] - 1] = arr[i]; count[(arr[i] / exp) % 10]--; } // Copy the output array to arr[], so that arr[] now // contains sorted numbers according to current digit for (i = 0; i < n; i++) arr[i] = output[i]; } // The main function to that sorts arr[] of size n using // Radix Sort void radixsort(int arr[], int n) { // Find the maximum number to know number of digits int m = getMax(arr, n); // Do counting sort for every digit. Note that instead // of passing digit number, exp is passed. exp is 10^i // where i is current digit number for (int exp = 1; m / exp > 0; exp *= 10) countSort(arr, n, exp); } // Function to print an array void print(int arr[], int n) { for (int i = 0; i < n; i++) cout << arr[i] << " "; } int main() { int arr[] = { 170, 45, 75, 90, 802, 24, 2, 66 }; int n = sizeof(arr) / sizeof(arr[0]); // Function Call radixsort(arr, n); print(arr, n); return 0; } // Time Compexity -> O((n+k)*d) // Space Complexity -> O(n+k) ================================================ FILE: sorting/radix_sort.go ================================================ /* Radix sort is a sorting Algorithm that sorts elements by grouping them based on their individual digits or by the position of their digits. It is a linear time sorting Algorithm that has a time complexity of O(nk). Using the Least Significant Bit In this method, we will write a go language program to implement the radix sort by using the least significant bit. LSD sorts the elements from right to left by comparing their individual digits. Algorithm Step 1 − First, we need to import the fmt package. Then create a function named radixSortMSD() to sort the array. Step 2 − Inside the function create a new array. Use the for loop to iterate over the array and store the maximum element of the array in a variable. Step 3 − Now, initialize a count array to keep track of the number of elements that have a particular digit. Step 4 − Traverse the array and increment the count for the corresponding digit for each element. Step 5 − Modify the count array to store the actual position of each element in the Output array. Copy the elements from the input array to the Output array in the order specified by the count array. Step 6 − Update the input array to the sorted Output array and return the sorted array. Step 7 − Start the main() function. Inside the main() initialize an array and store value to it. further, call the radixSort() function and pass the array as an argument to the function. Step 8 − Store the result obtained by the function in a variable and print It on the screen. The code implements Radix Sort using the LSD (Least Significant Digit) approach to sort an array of integers. It iterates through each digit from the least significant to the most significant, performing counting sort for each digit. Counting sort is used to determine the correct positions of elements based on the current digit. The sorted elements are then copied back to the original array. This process is repeated until all digits have been processed, resulting in a sorted array. */ package main import "fmt" // radixSortLSD performs Radix Sort using the LSD (Least Significant Digit) algorithm func radixSortLSD(arr []int) []int { // Find the maximum element in the array max := arr[0] for i := 1; i < len(arr); i++ { if arr[i] > max { max = arr[i] } } exp := 1 // Perform counting sort for each digit from LSD to MSD for max/exp > 0 { // Create a count array to store the frequency of each digit (0-9) count := make([]int, 10) // Count the frequency of each digit at the current exponent for i := 0; i < len(arr); i++ { count[(arr[i]/exp)%10]++ } // Calculate the cumulative sum of count array to determine the correct positions of each digit for i := 1; i < 10; i++ { count[i] += count[i-1] } // Create an output array to store the sorted elements based on the current digit output := make([]int, len(arr)) // Build the output array by placing each element in its correct position based on the current digit for i := len(arr) - 1; i >= 0; i-- { output[count[(arr[i]/exp)%10]-1] = arr[i] count[(arr[i]/exp)%10]-- } // Copy the sorted elements from the output array back to the original array for i := 0; i < len(arr); i++ { arr[i] = output[i] } // Move to the next digit by multiplying the exponent by 10 exp *= 10 } // Return the sorted array return arr } func main() { // Test the radixSortLSD function arr := []int{15, 31, 42, 20, 9} fmt.Println("The unsorted array is:", arr) result := radixSortLSD(arr) fmt.Println("The sorted array is:", result) } // Output // The unsorted array is: [15 31 42 20 9] // The sorted array is: [9 15 20 31 42] // Time Compexity -> O((n+k)*d) // Space Complexity -> O(n+k) ================================================ FILE: sorting/radix_sort.java ================================================ /* Radix Sort 1. Identify the maximum number: Find the maximum number in the given list. This is necessary to determine the number of digits we need to consider during the sorting process. 2. Perform counting sort for each digit position: Starting from the least significant digit (rightmost digit), perform the following steps for each digit position, moving towards the most significant digit (leftmost digit): a. Create a count array: Create a count array of size 10 (to represent digits 0-9) and initialize all elements to 0. This count array will be used to store the frequency of each digit at the current position. b. Count the frequencies: Iterate through the list of numbers and count the frequency of each digit at the current position. For example, if the current digit position is the units place, count the frequency of each digit from 0 to 9. c. Update the count array: Modify the count array such that each element represents the cumulative count of digits up to that index. This step ensures that the count array contains the correct positions for each digit in the sorted order. d. Distribute the numbers: Iterate through the list of numbers again, and for each number, find its digit at the current position. Use the count array to determine the correct position of the number in the output array and place it there. After placing the number, decrement the count for that digit in the count array. e. Collect the numbers: After distributing all the numbers, collect them back into the original array. The array will now be partially sorted based on the current digit position. 3. Repeat the counting sort for the next digit position: After collecting the numbers based on the least significant digit, move to the next digit position (towards the left) and repeat steps 2a to 2e for that digit. Continue this process until all the digits have been processed, from the least significant digit to the most significant digit. 4. Final sorted list: After completing the counting sort process for all digit positions, you will have a fully sorted list of numbers. Here's an example to illustrate the process: Sample Input: [170, 45, 75, 90, 802, 24, 2, 66] Maximum number: 802 # 1. First iteration (Least significant digit - rightmost digit): # Create the count array: [0, 2, 1, 1, 0, 1, 0, 0, 0, 1] # Update the count array: [0, 2, 3, 4, 4, 5, 5, 5, 5, 6] # Distribute the numbers: [802, 2, 24, 45, 75, 170, 90, 66] # Collect the numbers: [802, 2, 24, 45, 75, 170, 90, 66] # 2. Second iteration (Next least significant digit): # Create the count array: [1, 2, 1, 1, 1, 1, 0, 0, 0, 1] # Update the count array: [1, 3, 4, 5, 6, 7, 7, 7, 7, 8] # Distribute the numbers: [802, 2, 24, 45, 66, 75, 90, 170] # Collect the numbers: [802, 2, 24, 45, 66, 75, 90, 170] # 3. Third iteration (Most significant digit): # Create the count array: [1, 1, 1, 1, 2, 2, 1, 0, 0, 0] # Update the count array: [1, 2, 3, 4, 6, 8, 9, 9, 9, 9] # Distribute the numbers: [2, 24, 45, 66, 75, 90, 170, 802] # Collect the numbers: [2, 24, 45, 66, 75, 90, 170, 802] # The final sorted list is [2, 24, 45, 66, 75, 90, 170, 802]. # Radix sort using counting sort works by sorting the numbers digit by digit, from the least significant digit to the most significant digit. # The counting sort process distributes and collects the numbers based on each digit position, ensuring that the numbers are correctly ordered at # each iteration. By repeating this process for each digit, the algorithm achieves a fully sorted list without the need for explicit element comparisons. */ import java.io.*; import java.util.*; class Radix { // A utility function to get maximum value in arr[] static int getMax(int arr[], int n) { int mx = arr[0]; for (int i = 1; i < n; i++) if (arr[i] > mx) mx = arr[i]; return mx; } // A function to do counting sort of arr[] according to // the digit represented by exp. static void countSort(int arr[], int n, int exp) { int output[] = new int[n]; // output array int i; int count[] = new int[10]; Arrays.fill(count, 0); // Store count of occurrences in count[] for (i = 0; i < n; i++) count[(arr[i] / exp) % 10]++; // Change count[i] so that count[i] now contains // actual position of this digit in output[] for (i = 1; i < 10; i++) count[i] += count[i - 1]; // Build the output array for (i = n - 1; i >= 0; i--) { output[count[(arr[i] / exp) % 10] - 1] = arr[i]; count[(arr[i] / exp) % 10]--; } // Copy the output array to arr[], so that arr[] now // contains sorted numbers according to current // digit for (i = 0; i < n; i++) arr[i] = output[i]; } // The main function to that sorts arr[] of // size n using Radix Sort static void radixsort(int arr[], int n) { // Find the maximum number to know number of digits int m = getMax(arr, n); // Do counting sort for every digit. Note that // instead of passing digit number, exp is passed. // exp is 10^i where i is current digit number for (int exp = 1; m / exp > 0; exp *= 10) countSort(arr, n, exp); } // A utility function to print an array static void print(int arr[], int n) { for (int i = 0; i < n; i++) System.out.print(arr[i] + " "); } // Main driver method public static void main(String[] args) { int arr[] = { 170, 45, 75, 90, 802, 24, 2, 66 }; int n = arr.length; // Function Call radixsort(arr, n); print(arr, n); } } // Time Compexity -> O((n+k)*d) // Space Complexity -> O(n+k) ================================================ FILE: sorting/radix_sort.js ================================================ /* Radix Sort 1. Identify the maximum number: Find the maximum number in the given list. This is necessary to determine the number of digits we need to consider during the sorting process. 2. Perform counting sort for each digit position: Starting from the least significant digit (rightmost digit), perform the following steps for each digit position, moving towards the most significant digit (leftmost digit): a. Create a count array: Create a count array of size 10 (to represent digits 0-9) and initialize all elements to 0. This count array will be used to store the frequency of each digit at the current position. b. Count the frequencies: Iterate through the list of numbers and count the frequency of each digit at the current position. For example, if the current digit position is the units place, count the frequency of each digit from 0 to 9. c. Update the count array: Modify the count array such that each element represents the cumulative count of digits up to that index. This step ensures that the count array contains the correct positions for each digit in the sorted order. d. Distribute the numbers: Iterate through the list of numbers again, and for each number, find its digit at the current position. Use the count array to determine the correct position of the number in the output array and place it there. After placing the number, decrement the count for that digit in the count array. e. Collect the numbers: After distributing all the numbers, collect them back into the original array. The array will now be partially sorted based on the current digit position. 3. Repeat the counting sort for the next digit position: After collecting the numbers based on the least significant digit, move to the next digit position (towards the left) and repeat steps 2a to 2e for that digit. Continue this process until all the digits have been processed, from the least significant digit to the most significant digit. 4. Final sorted list: After completing the counting sort process for all digit positions, you will have a fully sorted list of numbers. Here's an example to illustrate the process: Sample Input: [170, 45, 75, 90, 802, 24, 2, 66] Maximum number: 802 # 1. First iteration (Least significant digit - rightmost digit): # Create the count array: [0, 2, 1, 1, 0, 1, 0, 0, 0, 1] # Update the count array: [0, 2, 3, 4, 4, 5, 5, 5, 5, 6] # Distribute the numbers: [802, 2, 24, 45, 75, 170, 90, 66] # Collect the numbers: [802, 2, 24, 45, 75, 170, 90, 66] # 2. Second iteration (Next least significant digit): # Create the count array: [1, 2, 1, 1, 1, 1, 0, 0, 0, 1] # Update the count array: [1, 3, 4, 5, 6, 7, 7, 7, 7, 8] # Distribute the numbers: [802, 2, 24, 45, 66, 75, 90, 170] # Collect the numbers: [802, 2, 24, 45, 66, 75, 90, 170] # 3. Third iteration (Most significant digit): # Create the count array: [1, 1, 1, 1, 2, 2, 1, 0, 0, 0] # Update the count array: [1, 2, 3, 4, 6, 8, 9, 9, 9, 9] # Distribute the numbers: [2, 24, 45, 66, 75, 90, 170, 802] # Collect the numbers: [2, 24, 45, 66, 75, 90, 170, 802] # The final sorted list is [2, 24, 45, 66, 75, 90, 170, 802]. # Radix sort using counting sort works by sorting the numbers digit by digit, from the least significant digit to the most significant digit. # The counting sort process distributes and collects the numbers based on each digit position, ensuring that the numbers are correctly ordered at # each iteration. By repeating this process for each digit, the algorithm achieves a fully sorted list without the need for explicit element comparisons. */ // A utility function to get maximum value in arr[] function getMax(arr,n) { let mx = arr[0]; for (let i = 1; i < n; i++) if (arr[i] > mx) mx = arr[i]; return mx; } // A function to do counting sort of arr[] according to // the digit represented by exp. function countSort(arr,n,exp) { let output = new Array(n); // output array let i; let count = new Array(10); for(let i=0;i<10;i++) count[i]=0; // Store count of occurrences in count[] for (i = 0; i < n; i++) { let x = Math.floor(arr[i] / exp) % 10; count[x]++; } // Change count[i] so that count[i] now contains // actual position of this digit in output[] for (i = 1; i < 10; i++) count[i] += count[i - 1]; // Build the output array for (i = n - 1; i >= 0; i--) { output[count[x] - 1] = arr[i]; count[x]--; } // Copy the output array to arr[], so that arr[] now // contains sorted numbers according to current digit for (i = 0; i < n; i++) arr[i] = output[i]; } // The main function to that sorts arr[] of size n using // Radix Sort function radixsort(arr,n) { // Find the maximum number to know number of digits let m = getMax(arr, n); // Do counting sort for every digit. Note that // instead of passing digit number, exp is passed. // exp is 10^i where i is current digit number for (let exp = 1; Math.floor(m / exp) > 0; exp *= 10) countSort(arr, n, exp); } // A utility function to print an array function print(arr,n) { for (let i = 0; i < n; i++) document.write(arr[i] + " "); } /*Driver Code*/ let arr=[170, 45, 75, 90, 802, 24, 2, 66]; let n = arr.length; // Function Call radixsort(arr, n); print(arr, n); // Time Compexity -> O((n+k)*d) // Space Complexity -> O(n+k) ================================================ FILE: sorting/radix_sort.py ================================================ # Radix Sort # 1. Identify the maximum number: Find the maximum number in the given list. This is necessary to determine the number of digits we need to consider # during the sorting process. # 2. Perform counting sort for each digit position: Starting from the least significant digit (rightmost digit), perform the following steps for each # digit position, moving towards the most significant digit (leftmost digit): # a. Create a count array: Create a count array of size 10 (to represent digits 0-9) and initialize all elements to 0. This count array will be used # to store the frequency of each digit at the current position. # b. Count the frequencies: Iterate through the list of numbers and count the frequency of each digit at the current position. For example, if the # current digit position is the units place, count the frequency of each digit from 0 to 9. # c. Update the count array: Modify the count array such that each element represents the cumulative count of digits up to that index. This step # ensures that the count array contains the correct positions for each digit in the sorted order. # d. Distribute the numbers: Iterate through the list of numbers again, and for each number, find its digit at the current position. Use the count # array to determine the correct position of the number in the output array and place it there. After placing the number, decrement the count for # that digit in the count array. # e. Collect the numbers: After distributing all the numbers, collect them back into the original array. The array will now be partially sorted # based on the current digit position. # 3. Repeat the counting sort for the next digit position: After collecting the numbers based on the least significant digit, move to the next digit # position (towards the left) and repeat steps 2a to 2e for that digit. Continue this process until all the digits have been processed, from the least # significant digit to the most significant digit. # 4. Final sorted list: After completing the counting sort process for all digit positions, you will have a fully sorted list of numbers. # Here's an example to illustrate the process: # Sample Input: [170, 45, 75, 90, 802, 24, 2, 66] # Maximum number: 802 # 1. First iteration (Least significant digit - rightmost digit): # Create the count array: [0, 2, 1, 1, 0, 1, 0, 0, 0, 1] # Update the count array: [0, 2, 3, 4, 4, 5, 5, 5, 5, 6] # Distribute the numbers: [802, 2, 24, 45, 75, 170, 90, 66] # Collect the numbers: [802, 2, 24, 45, 75, 170, 90, 66] # 2. Second iteration (Next least significant digit): # Create the count array: [1, 2, 1, 1, 1, 1, 0, 0, 0, 1] # Update the count array: [1, 3, 4, 5, 6, 7, 7, 7, 7, 8] # Distribute the numbers: [802, 2, 24, 45, 66, 75, 90, 170] # Collect the numbers: [802, 2, 24, 45, 66, 75, 90, 170] # 3. Third iteration (Most significant digit): # Create the count array: [1, 1, 1, 1, 2, 2, 1, 0, 0, 0] # Update the count array: [1, 2, 3, 4, 6, 8, 9, 9, 9, 9] # Distribute the numbers: [2, 24, 45, 66, 75, 90, 170, 802] # Collect the numbers: [2, 24, 45, 66, 75, 90, 170, 802] # The final sorted list is [2, 24, 45, 66, 75, 90, 170, 802]. # Radix sort using counting sort works by sorting the numbers digit by digit, from the least significant digit to the most significant digit. # The counting sort process distributes and collects the numbers based on each digit position, ensuring that the numbers are correctly ordered at # each iteration. By repeating this process for each digit, the algorithm achieves a fully sorted list without the need for explicit element comparisons. # Method to do Radix Sort def countingSort(arr, exp1): n = len(arr) # The output array elements that will have sorted arr output = [0] * (n) # initialize count array as 0 count = [0] * (10) # Store count of occurrences in count[] for i in range(0, n): index = arr[i] // exp1 count[index % 10] += 1 # Change count[i] so that count[i] now contains actual # position of this digit in output array for i in range(1, 10): count[i] += count[i - 1] # Build the output array i = n - 1 while i >= 0: index = arr[i] // exp1 output[count[index % 10] - 1] = arr[i] count[index % 10] -= 1 i -= 1 # Copying the output array to arr[], # so that arr now contains sorted numbers i = 0 for i in range(0, len(arr)): arr[i] = output[i] # Method to do Radix Sort def radixSort(arr): # Find the maximum number to know number of digits max1 = max(arr) # Do counting sort for every digit. Note that instead # of passing digit number, exp is passed. exp is 10^i # where i is current digit number exp = 1 while max1 / exp >= 1: countingSort(arr, exp) exp *= 10 # Driver code arr = [170, 45, 75, 90, 802, 24, 2, 66] # Function Call radixSort(arr) for i in range(len(arr)): print(arr[i],end=" ") # Time Compexity -> O((n+k)*d) # Space Complexity -> O(n+k) ================================================ FILE: sorting/selection_sort.cpp ================================================ /* Selection sort is a simple sorting algorithm that works by repeatedly finding the minimum element from an unsorted part of the array and putting it at the beginning of the array. In each iteration, the minimum element is found by comparing each element of the unsorted part of the array with the current minimum element, and if a smaller element is found, it becomes the new minimum element. Once the minimum element is found, it is swapped with the first element of the unsorted part of the array. This process is repeated until the entire array is sorted. The main idea behind selection sort is to divide the array into two parts: a sorted part and an unsorted part. Initially, the sorted part is empty, and the unsorted part is the entire array. In each iteration, the minimum element is selected from the unsorted part and added to the end of the sorted part. The time complexity of selection sort is O(n^2) in both the best and worst cases, where n is the number of elements in the array. This is because for each element in the array, it needs to compare it with every other element to find the smallest one, which takes O(n) time. Since this process needs to be repeated for each element, the overall time complexity becomes O(n^2). The space complexity of selection sort is O(1) because it does not require any extra space other than the input array itself. It performs the sorting in place by swapping the elements within the array. */ // Sample Input : [2, 1, 9, 3, 5, 4, 0] // Output : [0 1 2 3 4 5 9] #include #include using namespace std; // Function to perform selection sort on a vector void selectionSort(vector& arr) { int n = arr.size(); // Iterate through the array for (int i = 0; i < n - 1; i++) { int minIdx = i; // Find the index of the minimum element in the unsorted part of the array for (int j = i + 1; j < n; j++) { if (arr[j] < arr[minIdx]) { minIdx = j; } } // Swap the minimum element with the first element of the unsorted part of the array swap(arr[i], arr[minIdx]); } } int main() { // Example usage vector arr = {64, 25, 12, 22, 11}; selectionSort(arr); for (int i = 0; i < arr.size(); i++) { cout << arr[i] << " "; } return 0; } ================================================ FILE: sorting/selection_sort.go ================================================ /* Selection sort is a simple sorting algorithm that works by repeatedly finding the minimum element from an unsorted part of the array and putting it at the beginning of the array. In each iteration, the minimum element is found by comparing each element of the unsorted part of the array with the current minimum element, and if a smaller element is found, it becomes the new minimum element. Once the minimum element is found, it is swapped with the first element of the unsorted part of the array. This process is repeated until the entire array is sorted. The main idea behind selection sort is to divide the array into two parts: a sorted part and an unsorted part. Initially, the sorted part is empty, and the unsorted part is the entire array. In each iteration, the minimum element is selected from the unsorted part and added to the end of the sorted part. The time complexity of selection sort is O(n^2) in both the best and worst cases, where n is the number of elements in the array. This is because for each element in the array, it needs to compare it with every other element to find the smallest one, which takes O(n) time. Since this process needs to be repeated for each element, the overall time complexity becomes O(n^2). The space complexity of selection sort is O(1) because it does not require any extra space other than the input array itself. It performs the sorting in place by swapping the elements within the array. */ // Sample Input : [2, 1, 9, 3, 5, 4, 0] // Output : [0 1 2 3 4 5 9] package main import "fmt" // selectionSort sorts an array of integers in ascending order // using the selection sort algorithm. func selectionSort(arr []int) { // Loop over the array from start to end for i := 0; i < len(arr); i++ { // Assume the current index contains the minimum value minIdx := i // Loop over the rest of the array to find the minimum value for j := i + 1; j < len(arr); j++ { // If a value is found that is smaller than the current minimum, // update the minimum index if arr[j] < arr[minIdx] { minIdx = j } } // Swap the minimum value with the current index arr[i], arr[minIdx] = arr[minIdx], arr[i] } } func main() { // Example usage arr := []int{5, 3, 6, 2, 10} fmt.Println("Before sorting:", arr) selectionSort(arr) fmt.Println("After sorting:", arr) } ================================================ FILE: sorting/selection_sort.java ================================================ /* Selection sort is a simple sorting algorithm that works by repeatedly finding the minimum element from an unsorted part of the array and putting it at the beginning of the array. In each iteration, the minimum element is found by comparing each element of the unsorted part of the array with the current minimum element, and if a smaller element is found, it becomes the new minimum element. Once the minimum element is found, it is swapped with the first element of the unsorted part of the array. This process is repeated until the entire array is sorted. The main idea behind selection sort is to divide the array into two parts: a sorted part and an unsorted part. Initially, the sorted part is empty, and the unsorted part is the entire array. In each iteration, the minimum element is selected from the unsorted part and added to the end of the sorted part. The time complexity of selection sort is O(n^2) in both the best and worst cases, where n is the number of elements in the array. This is because for each element in the array, it needs to compare it with every other element to find the smallest one, which takes O(n) time. Since this process needs to be repeated for each element, the overall time complexity becomes O(n^2). The space complexity of selection sort is O(1) because it does not require any extra space other than the input array itself. It performs the sorting in place by swapping the elements within the array. */ // Sample Input : [2, 1, 9, 3, 5, 4, 0] // Output : [0 1 2 3 4 5 9] /** * Sorts an array of integers in ascending order using the Selection Sort algorithm. * * @param arr the array to be sorted * @return the sorted array */ public static int[] selectionSort(int[] arr) { // iterate through the array for (int i = 0; i < arr.length - 1; i++) { int minIdx = i; // find the index of the smallest element in the unsorted portion of the array for (int j = i + 1; j < arr.length; j++) { if (arr[j] < arr[minIdx]) { minIdx = j; } } // swap the smallest element with the first unsorted element int temp = arr[minIdx]; arr[minIdx] = arr[i]; arr[i] = temp; } return arr; } ================================================ FILE: sorting/selection_sort.js ================================================ /* Selection sort is a simple sorting algorithm that works by repeatedly finding the minimum element from an unsorted part of the array and putting it at the beginning of the array. In each iteration, the minimum element is found by comparing each element of the unsorted part of the array with the current minimum element, and if a smaller element is found, it becomes the new minimum element. Once the minimum element is found, it is swapped with the first element of the unsorted part of the array. This process is repeated until the entire array is sorted. The main idea behind selection sort is to divide the array into two parts: a sorted part and an unsorted part. Initially, the sorted part is empty, and the unsorted part is the entire array. In each iteration, the minimum element is selected from the unsorted part and added to the end of the sorted part. The time complexity of selection sort is O(n^2) in both the best and worst cases, where n is the number of elements in the array. This is because for each element in the array, it needs to compare it with every other element to find the smallest one, which takes O(n) time. Since this process needs to be repeated for each element, the overall time complexity becomes O(n^2). The space complexity of selection sort is O(1) because it does not require any extra space other than the input array itself. It performs the sorting in place by swapping the elements within the array. */ // Sample Input : [2, 1, 9, 3, 5, 4, 0] // Output : [0 1 2 3 4 5 9] const inputArr = [4, 5, 67, 56, 3, 35, 45]; /** * Sorts an array using selection sort algorithm. * @param {number[]} array - The array to be sorted. * @returns {number[]} - The sorted array. */ function selectionSort(array) { // Loop through the array from the start to the second last element for (let i = 0; i < array.length - 1; i++) { // Assume that the current element is the minimum let minIndex = i; // Loop through the rest of the array to find the minimum element for (let j = i + 1; j < array.length; j++) { if (array[j] < array[minIndex]) { // If we find a smaller element, update the minimum index minIndex = j; } } // Swap the current element with the minimum element let temp = array[i]; array[i] = array[minIndex]; array[minIndex] = temp; } // Return the sorted array return array; } console.log(selectionSort(inputArr)); ================================================ FILE: sorting/selection_sort.py ================================================ ''' Selection sort is a simple sorting algorithm that works by repeatedly finding the minimum element from an unsorted part of the array and putting it at the beginning of the array. In each iteration, the minimum element is found by comparing each element of the unsorted part of the array with the current minimum element, and if a smaller element is found, it becomes the new minimum element. Once the minimum element is found, it is swapped with the first element of the unsorted part of the array. This process is repeated until the entire array is sorted. The main idea behind selection sort is to divide the array into two parts: a sorted part and an unsorted part. Initially, the sorted part is empty, and the unsorted part is the entire array. In each iteration, the minimum element is selected from the unsorted part and added to the end of the sorted part. The time complexity of selection sort is O(n^2) in both the best and worst cases, where n is the number of elements in the array. This is because for each element in the array, it needs to compare it with every other element to find the smallest one, which takes O(n) time. Since this process needs to be repeated for each element, the overall time complexity becomes O(n^2). The space complexity of selection sort is O(1) because it does not require any extra space other than the input array itself. It performs the sorting in place by swapping the elements within the array. ''' # Sample Input : [2, 1, 9, 3, 5, 4, 0] # Output : [0 1 2 3 4 5 9] def selection_sort(arr): """ Sorts an array in ascending order using the selection sort algorithm. Args: arr (list): An array of integers. Returns: list: The sorted array in ascending order. """ # Loop through the array for i in range(len(arr)): # Find the minimum element in the unsorted portion of the array min_idx = i for j in range(i+1, len(arr)): if arr[j] < arr[min_idx]: min_idx = j # Swap the minimum element with the first element in the unsorted portion of the array arr[i], arr[min_idx] = arr[min_idx], arr[i] return arr data = [-2, 45, 0, 11, -9] size = len(data) selection_sort(data, size) print('Sorted Array in Ascending Order:') print(data) ================================================ FILE: sorting/tim_sort.cpp ================================================ //Implementation of Tim Sort //Tim Sort makes use of insertionSort and Merge function of MergeSort //The basic idea is to divide the input array into blocks called as runs //The size of runs varies from 32 to 64, and perform insertion sort on the runs //Then, The runs(blocks) are merged to form the final sorted array // //Time Complexity of this algorithm in Best case is O(n),Average case is O(n*log(n)) //Time Complexity in Worst case is O(n*log(n)). //This is a stable sorting algorithm //This algorithm uses Space of O(n) //This algorithm is used in languages like Java and Python for sorting. #include using namespace std; const int run=32; void insertionSort(vector&array,int l,int r) { for(int i = l + 1; i <= r; i++){ int temp = array[i]; int j = i - 1; while(j >= l and array[j] > temp) { array[j + 1] = array[j]; j--; } array[j + 1] = temp; } } void merge(vector&array,int l,int m,int r) { int len1 = m - l + 1, len2 = r - m; vectorleft(len1), right(len2); for(int i = 0; i < len1; i++){ left[i] = array[l + i]; } for(int i = 0; i < len2; i++){ right[i] = array[m + 1 + i]; } int i = 0, j = 0, k = l; while(i < len1 and j < len2) { if(left[i] <= right[j]) array[k] = left[i++]; else array[k] = right[j++]; k++; } while(i&array, int n) { // calling insertion sort on blocks of size equal to run=32 // if the size of array is less than defined run size, then the insertion sort would have sorted the array and there is no need of merging. for(int i = 0; i < n; i += run) { insertionSort(array, i, min((i + run - 1), n - 1)); } //we merge from size run,the array merges to form sizes 64,128,... for(int size = run; size < n; size = 2 * size) { //we merge from array[left,left+size-1] //and array[left+size,left+2*size] //after every merge,we increase left by 2*size for(int left = 0; left < n; left += 2 * size) { int mid = left + size - 1;//ending point of left subarray int right = min((left + 2 * size - 1), n - 1);//ending point of right subarray if(mid < right) merge(array, left, mid, right); //calling the merge function } } for(int i: array)//printing the sorted array cout << i <<" "; //sample output 1,2,3,4,5,6,7,8,9 } int main() { vector array = {9,8,7,6,5,4,3,2,1}; int n = array.size(); timSort(array, n);//call to TimSort function return 0; } ================================================ FILE: sorting/tim_sort.go ================================================ // File: tim_sort.go //Implementation of Tim Sort //Tim Sort makes use of insertionSort and Merge function of MergeSort //The basic idea is to divide the input array into blocks called as runs //The size of runs varies from 32 to 64, and perform insertion sort on the runs //Then, The runs(blocks) are merged to form the final sorted array // //Time Complexity of this algorithm in Best case is O(n),Average case is O(n*log(n)) //Time Complexity in Worst case is O(n*log(n)). //This is a stable sorting algorithm //This algorithm uses Space of O(n) //This algorithm is used in languages like Java and Python for sorting. package main import ( "fmt" "math" ) func main() { arr := []int{43, 56, 2, 99, 1, 64, 23, 78, 34, 11, 90, 45, 32, 67, 88, 12, 9, 10, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} n := len(arr) fmt.Printf("Before Sorting\n") printArray(arr, n) timSort(arr, n) fmt.Printf("Sorted Array is\n") printArray(arr, n) } func timSort(arr []int, n int) { run := 32 // you can take any value between 32 to 64, it actually is some empirical result from insertion s for i := 0; i < n; i += run { insertionSort(arr, i, int(math.Min(float64(i + 31), float64(n - 1)))) } for size := run; size < n; size = 2 * size { for left := 0; left < n; left += 2 * size { mid := left + size - 1 right := int(math.Min(float64(left + 2 * size - 1), float64(n - 1))) merge(arr, left, mid, right) } } } func insertionSort(arr []int, left int, right int) { for i := left + 1; i <= right; i++ { j := i for j > left && arr[j] < arr[j - 1] { arr[j], arr[j - 1] = arr[j - 1], arr[j] j-- } } } func merge(arr []int, left int, mid int, right int) { // function to merge the two sorted arrays len1 := mid - left + 1 len2 := right - mid leftArr := make([]int, len1) rightArr := make([]int, len2) for i := 0; i < len1; i++ { leftArr[i] = arr[left + i] } for j := 0; j < len2; j++ { rightArr[j] = arr[mid + 1 + j] } i := 0 j := 0 k := left for i < len1 && j < len2 { if leftArr[i] <= rightArr[j] { arr[k] = leftArr[i] i++ } else { arr[k] = rightArr[j] j++ } k++ } for i < len1 { arr[k] = leftArr[i] i++ k++ } for j < len2 { arr[k] = rightArr[j] j++ k++ } } // Function to print the array func printArray(arr []int, n int) { for i := 0; i < n; i++ { fmt.Printf("%d ", arr[i]) } fmt.Printf("\n") } // to run in terminal // $ go run tim_sort.go // Output // Before Sorting // 43 56 2 99 1 64 23 78 34 11 90 45 32 67 88 12 9 10 3 4 5 6 7 8 9 10 11 12 13 14 15 // Sorted Array is // 1 2 3 4 5 6 7 8 9 9 10 10 11 11 12 12 13 14 15 23 32 34 43 45 56 64 67 78 88 90 99 ================================================ FILE: sorting/tim_sort.java ================================================ //Implementation of Tim Sort //Tim Sort makes use of insertionSort and Merge function of MergeSort //The basic idea is to divide the input array into blocks called as runs //The size of runs varies from 32 to 64, and perform insertion sort on the runs //Then, The runs(blocks) are merged to form the final sorted array // //Time Complexity of this algorithm in Best case is O(n),Average case is O(n*log(n)) //Time Complexity in Worst case is O(n*log(n)). //This is a stable sorting algorithm //This algorithm uses Space of O(n) //This algorithm is used in languages like Java and Python for sorting. import java.util.Arrays; class tim_sort { static int run=32; // you can take any value between 32 to 64, it actually is some empirical result from insertion sort public static void main(String[] args) { int[] arr={43,56,2,99,1,64,23,78,34,11,90,45,32,67,88,12,9,10,3,4,5,6,7,8,9,10,11,12,13,14,15}; System.out.println("Before Sorting: "+Arrays.toString(arr)); timSort(arr,arr.length); System.out.println("After Sorting: "+Arrays.toString(arr)); } public static void timSort(int[] arr,int n) { for(int i=0;i=left && arr[j]>temp) { arr[j+1]=arr[j]; j--; } arr[j+1]=temp; } } public static void merge(int[] arr,int left,int mid,int right) { int len1=mid-left+1; int len2=right-mid; int[] leftArr=new int[len1]; int[] rightArr=new int[len2]; for(int i=0;i= MINIMUM: r |= n & 1 n >>= 1 return n + r # Sorts an array from left to right index (Insertion Sort) def insertion_sort(array, left, right): for i in range(left + 1, right + 1): j = i while j > left and array[j] < array[j - 1]: array[j], array[j - 1] = array[j - 1], array[j] j -= 1 # Merges the sorted runs def merge(array, l, m, r): # original array is broken in two parts: left - right array_len1 = m - l + 1 array_len2 = r - m left = [] right = [] for i in range(0, array_len1): left.append(array[l + i]) for i in range(0, array_len2): right.append(array[m + 1 + i]) i = 0 j = 0 k = l # merge the two arrays in a larger sub array while i < array_len1 and j < array_len2: if left[i] <= right[j]: array[k] = left[i] i += 1 else: array[k] = right[j] j += 1 k += 1 # Copy remaining elements of the left part, if any while i < array_len1: array[k] = left[i] k += 1 i += 1 # Copy remaining element of the right part, if any while j /* Wave sort, also known as "odd-even sort," is a simple sorting algorithm that works by repeatedly comparing and swapping adjacent elements in an array until the array is sorted. The algorithm gets its name from the pattern of element swaps, which resembles a wave-like motion. In this sorting method we arrange array in Arr[0] >=arr[1]<=arr[2]>=arr[3]<=arr[4]………. Like a wave */ using namespace std; void wavesort(int arr[], int n) { for (int i = 1; i < n; i += 2) { if (arr[i] > arr[i - 1]) { swap(arr[i], arr[i - 1]); } if (arr[i] > arr[i + 1] && i <= n - 2) { swap(arr[i], arr[i + 1]); } } } int main() { int arr[9] = {1, 3, 4, 7, 5, 6, 2}; wavesort(arr, 7); for (int i = 0; i < 7; i++) { cout << arr[i] << " "; } return 0; } /* Time Complexity: O(N) Auxiliary Space: O(1) */ /* The idea is based on the fact that if we make sure that all even positioned (at index 0, 2, 4, ..) elements are greater than their adjacent odd elements, we don’t need to worry about oddly positioned elements. - Traverse all even positioned elements of the input array, and do the following. - If the current element is smaller than the previous odd element, swap the previous and current. - If the current element is smaller than the next odd element, swap next and current. */ /* Dry-Run Consider the array: [7, 2, 5, 1, 8, 3] Step 1: Start with the initial array: [7, 2, 5, 1, 8, 3] In the first iteration, we compare and swap adjacent elements at even indices (0, 2, 4). Compare 7 and 2: 7 > 2, so we swap them. Array becomes: [2, 7, 5, 1, 8, 3] Compare 7 and 8: 7 < 8, so no swap is needed. After the first phase, the array becomes: [2, 7, 5, 1, 8, 3] Step 2: In the second iteration, we compare and swap adjacent elements at odd indices (1, 3, 5). Compare 7 and 5: 7 > 5, so we swap them. Array becomes: [2, 5, 7, 1, 8, 3] Compare 7 and 1: 7 > 1, so we swap them. Array becomes: [2, 5, 1, 7, 8, 3] Compare 7 and 3: 7 > 3, so we swap them. Array becomes: [2, 5, 1, 3, 8, 7] After the second phase, the array becomes: [2, 5, 1, 3, 8, 7] Step 3: In the third iteration, we again compare and swap adjacent elements at even indices (0, 2, 4). Compare 2 and 5: 2 < 5, so no swap is needed. Compare 5 and 1: 5 > 1, so we swap them. Array becomes: [1, 5, 2, 3, 8, 7] Compare 5 and 3: 5 > 3, so we swap them. Array becomes: [1, 3, 2, 5, 8, 7] After the third phase, the array becomes: [1, 3, 2, 5, 8, 7] Step 4: In the fourth iteration, we compare and swap adjacent elements at odd indices (1, 3, 5). Compare 3 and 2: 3 > 2, so we swap them. Array becomes: [1, 2, 3, 5, 8, 7] Compare 3 and 5: 3 < 5, so no swap is needed. Compare 5 and 8: 5 < 8, so no swap is needed. After the fourth phase, the array remains the same: [1, 2, 3, 5, 8, 7] Step 5: In the fifth iteration, we again compare and swap adjacent elements at even indices (0, 2, 4). Compare 1 and 2: 1 < 2, so no swap is needed. Compare 2 and 3: 2 < 3, so no swap is needed. Compare 3 and 5: 3 < 5, so no swap is needed. After the fifth phase, the array remains the same: [1, 2, 3, 5, 8, 7] Step 6: In the sixth iteration, we compare and swap adjacent elements at odd indices (1, 3, 5). Compare 2 and 3: 2 < 3, so no swap is needed. Compare 3 and 5: 3 < 5, so no swap is needed. Compare 5 and 7: 5 < 7, so no swap is needed. After the sixth phase, the array remains the same: [1, 2, 3, 5, 8, 7] Since no more swaps occur in the array, the algorithm terminates, and the array is considered sorted. Final sorted array: [1, 2, 3, 5, 7, 8] */