diff --git a/DIRECTORY.md b/DIRECTORY.md index 79eb258c16f..05fe99c0c11 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -122,6 +122,7 @@ * [Subset Sum](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/dynamic_programming/subset_sum.cpp) * [Trapped Rainwater](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/dynamic_programming/trapped_rainwater.cpp) * [Tree Height](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/dynamic_programming/tree_height.cpp) + * [Unbounded 0 1 Knapsack](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/dynamic_programming/unbounded_0_1_knapsack.cpp) * [Word Break](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/dynamic_programming/word_break.cpp) ## Games @@ -160,7 +161,9 @@ * [Spirograph](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/graphics/spirograph.cpp) ## Greedy Algorithms + * [Binary Addition](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/greedy_algorithms/binary_addition.cpp) * [Boruvkas Minimum Spanning Tree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/greedy_algorithms/boruvkas_minimum_spanning_tree.cpp) + * [Digit Separation](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/greedy_algorithms/digit_separation.cpp) * [Dijkstra](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/greedy_algorithms/dijkstra.cpp) * [Gale Shapley](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/greedy_algorithms/gale_shapley.cpp) * [Huffman](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/greedy_algorithms/huffman.cpp) @@ -300,7 +303,10 @@ * [Iterative Tree Traversals](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/others/iterative_tree_traversals.cpp) * [Kadanes3](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/others/kadanes3.cpp) * [Kelvin To Celsius](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/others/kelvin_to_celsius.cpp) + * [Lfu Cache](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/others/lfu_cache.cpp) + * [Longest Substring Without Repeating Characters](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/others/longest_substring_without_repeating_characters.cpp) * [Lru Cache](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/others/lru_cache.cpp) + * [Lru Cache2](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/others/lru_cache2.cpp) * [Matrix Exponentiation](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/others/matrix_exponentiation.cpp) * [Palindrome Of Number](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/others/palindrome_of_number.cpp) * [Paranthesis Matching](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/others/paranthesis_matching.cpp) @@ -322,6 +328,7 @@ * [Addition Rule](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/probability/addition_rule.cpp) * [Bayes Theorem](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/probability/bayes_theorem.cpp) * [Binomial Dist](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/probability/binomial_dist.cpp) + * [Exponential Dist](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/probability/exponential_dist.cpp) * [Geometric Dist](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/probability/geometric_dist.cpp) * [Poisson Dist](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/probability/poisson_dist.cpp) * [Windowed Median](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/probability/windowed_median.cpp) @@ -345,6 +352,7 @@ * [Interpolation Search2](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/search/interpolation_search2.cpp) * [Jump Search](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/search/jump_search.cpp) * [Linear Search](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/search/linear_search.cpp) + * [Longest Increasing Subsequence Using Binary Search](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/search/longest_increasing_subsequence_using_binary_search.cpp) * [Median Search](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/search/median_search.cpp) * [Median Search2](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/search/median_search2.cpp) * [Saddleback Search](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/search/saddleback_search.cpp) @@ -369,6 +377,7 @@ * [Gnome Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/sorting/gnome_sort.cpp) * [Heap Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/sorting/heap_sort.cpp) * [Insertion Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/sorting/insertion_sort.cpp) + * [Insertion Sort Recursive](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/sorting/insertion_sort_recursive.cpp) * [Library Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/sorting/library_sort.cpp) * [Merge Insertion Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/sorting/merge_insertion_sort.cpp) * [Merge Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/sorting/merge_sort.cpp) @@ -399,6 +408,7 @@ ## Strings * [Boyer Moore](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/strings/boyer_moore.cpp) * [Brute Force String Searching](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/strings/brute_force_string_searching.cpp) + * [Duval](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/strings/duval.cpp) * [Horspool](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/strings/horspool.cpp) * [Knuth Morris Pratt](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/strings/knuth_morris_pratt.cpp) * [Manacher Algorithm](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/strings/manacher_algorithm.cpp) diff --git a/backtracking/subarray_sum.cpp b/backtracking/subarray_sum.cpp index 8001d74cc0d..ba9834e87a6 100644 --- a/backtracking/subarray_sum.cpp +++ b/backtracking/subarray_sum.cpp @@ -14,6 +14,7 @@ */ #include /// for assert +#include #include /// for IO operations #include /// for unordered_map #include /// for std::vector diff --git a/backtracking/subset_sum.cpp b/backtracking/subset_sum.cpp index fad820920a5..8551a66e0ca 100644 --- a/backtracking/subset_sum.cpp +++ b/backtracking/subset_sum.cpp @@ -10,6 +10,7 @@ */ #include /// for assert +#include #include /// for IO operations #include /// for std::vector diff --git a/backtracking/wildcard_matching.cpp b/backtracking/wildcard_matching.cpp index d9163a41b48..cd0bd72ad8c 100644 --- a/backtracking/wildcard_matching.cpp +++ b/backtracking/wildcard_matching.cpp @@ -12,6 +12,7 @@ */ #include /// for assert +#include #include /// for IO operations #include /// for std::vector diff --git a/bit_manipulation/count_bits_flip.cpp b/bit_manipulation/count_bits_flip.cpp index 5a6584b7241..2ab2ce31c15 100644 --- a/bit_manipulation/count_bits_flip.cpp +++ b/bit_manipulation/count_bits_flip.cpp @@ -20,6 +20,7 @@ * @author [Yash Raj Singh](https://github.com/yashrajyash) */ #include /// for assert +#include #include /// for IO operations /** * @namespace bit_manipulation diff --git a/bit_manipulation/count_of_set_bits.cpp b/bit_manipulation/count_of_set_bits.cpp index f2d802061cc..dd29fd8bf19 100644 --- a/bit_manipulation/count_of_set_bits.cpp +++ b/bit_manipulation/count_of_set_bits.cpp @@ -16,6 +16,7 @@ * @author [Prashant Thakur](https://github.com/prashant-th18) */ #include /// for assert +#include #include /// for IO operations /** * @namespace bit_manipulation diff --git a/bit_manipulation/count_of_trailing_ciphers_in_factorial_n.cpp b/bit_manipulation/count_of_trailing_ciphers_in_factorial_n.cpp index aa2ceb3dd8a..c1c57d0a617 100644 --- a/bit_manipulation/count_of_trailing_ciphers_in_factorial_n.cpp +++ b/bit_manipulation/count_of_trailing_ciphers_in_factorial_n.cpp @@ -18,6 +18,7 @@ */ #include /// for assert +#include #include /// for IO operations /** diff --git a/bit_manipulation/hamming_distance.cpp b/bit_manipulation/hamming_distance.cpp index 62879cf4883..ca4e9fc5b36 100644 --- a/bit_manipulation/hamming_distance.cpp +++ b/bit_manipulation/hamming_distance.cpp @@ -13,6 +13,7 @@ */ #include /// for assert +#include #include /// for io operations /** diff --git a/bit_manipulation/next_higher_number_with_same_number_of_set_bits.cpp b/bit_manipulation/next_higher_number_with_same_number_of_set_bits.cpp index d93884d4e11..f6d840f91bd 100644 --- a/bit_manipulation/next_higher_number_with_same_number_of_set_bits.cpp +++ b/bit_manipulation/next_higher_number_with_same_number_of_set_bits.cpp @@ -16,6 +16,7 @@ */ #include /// for assert +#include #include /// for IO operations /** diff --git a/bit_manipulation/power_of_2.cpp b/bit_manipulation/power_of_2.cpp index c62bfdaa3f5..f35361c8a9d 100644 --- a/bit_manipulation/power_of_2.cpp +++ b/bit_manipulation/power_of_2.cpp @@ -16,6 +16,7 @@ */ #include /// for assert +#include #include /// for IO operations /** diff --git a/bit_manipulation/set_kth_bit.cpp b/bit_manipulation/set_kth_bit.cpp index a58e663b163..d3d50e6cb28 100644 --- a/bit_manipulation/set_kth_bit.cpp +++ b/bit_manipulation/set_kth_bit.cpp @@ -19,6 +19,7 @@ */ #include /// for assert +#include #include /// for IO operations /** diff --git a/bit_manipulation/travelling_salesman_using_bit_manipulation.cpp b/bit_manipulation/travelling_salesman_using_bit_manipulation.cpp index 31243ed2921..9858b8107a3 100644 --- a/bit_manipulation/travelling_salesman_using_bit_manipulation.cpp +++ b/bit_manipulation/travelling_salesman_using_bit_manipulation.cpp @@ -22,6 +22,7 @@ */ #include /// for std::min #include /// for assert +#include #include /// for IO operations #include /// for limits of integral types #include /// for std::vector diff --git a/ciphers/base64_encoding.cpp b/ciphers/base64_encoding.cpp index a592226a5e7..81459408a8e 100644 --- a/ciphers/base64_encoding.cpp +++ b/ciphers/base64_encoding.cpp @@ -13,6 +13,7 @@ */ #include /// for `std::array` #include /// for `assert` operations +#include #include /// for IO operations /** diff --git a/ciphers/hill_cipher.cpp b/ciphers/hill_cipher.cpp index 1fef0f32e2d..d77a51c22b3 100644 --- a/ciphers/hill_cipher.cpp +++ b/ciphers/hill_cipher.cpp @@ -35,6 +35,7 @@ #include #include +#include #include #include #include diff --git a/ciphers/uint128_t.hpp b/ciphers/uint128_t.hpp index d50cb4ffb2d..7abffb08a61 100644 --- a/ciphers/uint128_t.hpp +++ b/ciphers/uint128_t.hpp @@ -8,6 +8,7 @@ */ #include /// for `std::reverse` and other operations +#include #include /// for `std::cout` overload #include /// for `std::string` #include /// for `std::pair` library diff --git a/cpu_scheduling_algorithms/fcfs_scheduling.cpp b/cpu_scheduling_algorithms/fcfs_scheduling.cpp index 4fc61af6bce..5c5e2fa3c58 100644 --- a/cpu_scheduling_algorithms/fcfs_scheduling.cpp +++ b/cpu_scheduling_algorithms/fcfs_scheduling.cpp @@ -11,6 +11,7 @@ #include /// for sorting #include /// for assert +#include #include /// random number generation #include /// for time #include /// for formatting the output diff --git a/data_structures/dsu_path_compression.cpp b/data_structures/dsu_path_compression.cpp index 022e632a784..c08040570dd 100644 --- a/data_structures/dsu_path_compression.cpp +++ b/data_structures/dsu_path_compression.cpp @@ -19,6 +19,7 @@ */ #include /// for assert +#include #include /// for IO operations #include /// for std::vector diff --git a/data_structures/dsu_union_rank.cpp b/data_structures/dsu_union_rank.cpp index 8936d6d6964..16e1d3275bf 100644 --- a/data_structures/dsu_union_rank.cpp +++ b/data_structures/dsu_union_rank.cpp @@ -20,6 +20,7 @@ */ #include /// for assert +#include #include /// for IO operations #include /// for std::vector diff --git a/data_structures/list_array.cpp b/data_structures/list_array.cpp index 05c66ed84fa..0c8099f5788 100644 --- a/data_structures/list_array.cpp +++ b/data_structures/list_array.cpp @@ -4,17 +4,20 @@ * * @details * The list_array is the implementation of list represented using array. - * We can perform basic CRUD operations as well as other operations like sorting etc. + * We can perform basic CRUD operations as well as other operations like sorting + * etc. * * ### Algorithm * It implements various method like insert, sort, search etc. efficiently. * You can select the operation and methods will do the rest work for you. - * You can insert element, sort them in order, search efficiently, delete values and print the list. + * You can insert element, sort them in order, search efficiently, delete values + * and print the list. */ -#include /// for io operations -#include /// for std::array -#include /// for assert +#include /// for std::array +#include /// for assert +#include +#include /// for io operations /** * @namespace data_structures @@ -23,181 +26,190 @@ namespace data_structures { /** * @namespace list_array - * @brief Functions for [Dynamic Array](https://en.wikipedia.org/wiki/Dynamic_array) algorithm + * @brief Functions for [Dynamic + * Array](https://en.wikipedia.org/wiki/Dynamic_array) algorithm */ namespace list_array { +/** + * @brief Structure of List with supporting methods. + */ +template +struct list { + std::array data{}; // Array that implement list + uint64_t top = 0; // Pointer to the last element + bool isSorted = false; // indicator whether list is sorted or not /** - * @brief Structure of List with supporting methods. + * @brief Search an element in the list using binarySearch. + * @param dataArr list + * @param first pointer to the first element in the remaining list + * @param last pointer to the last element in the remaining list + * @param val element that will be searched + * @return index of element in the list if present else -1 */ - template - struct list { - std::array data{}; // Array that implement list - uint64_t top = 0; // Pointer to the last element - bool isSorted = false; // indicator whether list is sorted or not - /** - * @brief Search an element in the list using binarySearch. - * @param dataArr list - * @param first pointer to the first element in the remaining list - * @param last pointer to the last element in the remaining list - * @param val element that will be searched - * @return index of element in the list if present else -1 - */ - uint64_t BinarySearch(const std::array &dataArr, const uint64_t &first, const uint64_t &last, - const uint64_t &val) { - // If both pointer cross each other means no element present in the list which is equal to the val - if (last < first) { - return -1; - } - uint64_t mid = (first + last) / 2; - // check whether current mid pointer value is equal to element or not - if (dataArr[mid] == val) - return mid; - // if current mid value is greater than element we have to search in first half - else if (val < dataArr[mid]) - return (BinarySearch(dataArr, first, mid - 1, val)); - // if current mid value is greater than element we have to search in second half - else if (val > dataArr[mid]) - return (BinarySearch(dataArr, mid + 1, last, val)); - - std::cerr << __func__ << ":" << __LINE__ << ": Undefined condition\n"; + uint64_t BinarySearch(const std::array &dataArr, + const uint64_t &first, const uint64_t &last, + const uint64_t &val) { + // If both pointer cross each other means no element present in the list + // which is equal to the val + if (last < first) { return -1; } + uint64_t mid = (first + last) / 2; + // check whether current mid pointer value is equal to element or not + if (dataArr[mid] == val) + return mid; + // if current mid value is greater than element we have to search in + // first half + else if (val < dataArr[mid]) + return (BinarySearch(dataArr, first, mid - 1, val)); + // if current mid value is greater than element we have to search in + // second half + else if (val > dataArr[mid]) + return (BinarySearch(dataArr, mid + 1, last, val)); - /** - * @brief Search an element using linear search - * @param dataArr list - * @param val element that will be searched - * @return index of element in the list if present else -1 - */ - uint64_t LinearSearch(const std::array &dataArr, const uint64_t &val) const { - // Going through each element in the list - for (uint64_t i = 0; i < top; i++) { - if (dataArr[i] == val) { - return i; // element found at ith index - } + std::cerr << __func__ << ":" << __LINE__ << ": Undefined condition\n"; + return -1; + } + + /** + * @brief Search an element using linear search + * @param dataArr list + * @param val element that will be searched + * @return index of element in the list if present else -1 + */ + uint64_t LinearSearch(const std::array &dataArr, + const uint64_t &val) const { + // Going through each element in the list + for (uint64_t i = 0; i < top; i++) { + if (dataArr[i] == val) { + return i; // element found at ith index } - // element is not present in the list - return -1; } + // element is not present in the list + return -1; + } - /* - * @brief Parent function of binarySearch and linearSearch methods - * @param val element that will be searched - * @return index of element in the list if present else -1 - */ - uint64_t search(const uint64_t &val) { - uint64_t pos; // pos variable to store index value of element. - // if list is sorted, binary search works efficiently else linear search is the only option - if (isSorted) { - pos = BinarySearch(data, 0, top - 1, val); - } else { - pos = LinearSearch(data, val); - } - // if index is equal to -1 means element does not present - // else print the index of that element - if (pos != -1) { - std::cout << "\nElement found at position : " << pos; - } else { - std::cout << "\nElement not found"; - } - // return the index of element or -1. - return pos; + /* + * @brief Parent function of binarySearch and linearSearch methods + * @param val element that will be searched + * @return index of element in the list if present else -1 + */ + uint64_t search(const uint64_t &val) { + uint64_t pos; // pos variable to store index value of element. + // if list is sorted, binary search works efficiently else linear search + // is the only option + if (isSorted) { + pos = BinarySearch(data, 0, top - 1, val); + } else { + pos = LinearSearch(data, val); + } + // if index is equal to -1 means element does not present + // else print the index of that element + if (pos != -1) { + std::cout << "\nElement found at position : " << pos; + } else { + std::cout << "\nElement not found"; } + // return the index of element or -1. + return pos; + } - /** - * @brief Sort the list - * @returns void - */ - void sort() { - //Going through each element in the list - for (uint64_t i = 0; i < top; i++) { - uint64_t min_idx = i; // Initialize the min variable - for (uint64_t j = i + 1; j < top; j++) { - // check whether any element less than current min value - if (data[j] < data[min_idx]) { - min_idx = j; // update index accordingly - } + /** + * @brief Sort the list + * @returns void + */ + void sort() { + // Going through each element in the list + for (uint64_t i = 0; i < top; i++) { + uint64_t min_idx = i; // Initialize the min variable + for (uint64_t j = i + 1; j < top; j++) { + // check whether any element less than current min value + if (data[j] < data[min_idx]) { + min_idx = j; // update index accordingly } - // swap min value and element at the ith index - std::swap(data[min_idx], data[i]); } - // mark isSorted variable as true - isSorted = true; + // swap min value and element at the ith index + std::swap(data[min_idx], data[i]); } + // mark isSorted variable as true + isSorted = true; + } - /** - * @brief Insert the new element in the list - * @param val element that will be inserted - * @returns void - */ - void insert(const uint64_t &val) { - // overflow check - if (top == N) { - std::cout << "\nOverflow"; - return; - } - // if list is not sorted, insert at the last - // otherwise place it to correct position - if (!isSorted) { - data[top] = val; - top++; - } else { - uint64_t pos = 0; // Initialize the index variable - // Going through each element and find correct position for element - for (uint64_t i = 0; i < top - 1; i++) { - // check for the correct position - if (data[i] <= val && val <= data[i + 1]) { - pos = i + 1; // assign correct pos to the index var - break; // to get out from the loop - } - } - // if all elements are smaller than the element - if (pos == 0) { - pos = top - 1; - } - // shift all element to make a room for new element - for (uint64_t i = top; i > pos; i--) { - data[i] = data[i - 1]; + /** + * @brief Insert the new element in the list + * @param val element that will be inserted + * @returns void + */ + void insert(const uint64_t &val) { + // overflow check + if (top == N) { + std::cout << "\nOverflow"; + return; + } + // if list is not sorted, insert at the last + // otherwise place it to correct position + if (!isSorted) { + data[top] = val; + top++; + } else { + uint64_t pos = 0; // Initialize the index variable + // Going through each element and find correct position for element + for (uint64_t i = 0; i < top - 1; i++) { + // check for the correct position + if (data[i] <= val && val <= data[i + 1]) { + pos = i + 1; // assign correct pos to the index var + break; // to get out from the loop } - top++; // Increment the value of top. - data[pos] = val; // Assign the value to the correct index in the array } - } - - /** - * @brief To remove the element from the list - * @param val element that will be removed - * @returns void - */ - void remove(const uint64_t &val) { - uint64_t pos = search(val); // search the index of the value - // if search returns -1, element does not present in the list - if (pos == -1) { - std::cout << "\n Element does not present in the list "; - return; + // if all elements are smaller than the element + if (pos == 0) { + pos = top - 1; } - std::cout << "\n" << data[pos] << " deleted"; // print the appropriate message - // shift all the element 1 left to fill vacant space - for (uint64_t i = pos; i < top; i++) { - data[i] = data[i + 1]; + // shift all element to make a room for new element + for (uint64_t i = top; i > pos; i--) { + data[i] = data[i - 1]; } - top--; // decrement the top variable to maintain last index + top++; // Increment the value of top. + data[pos] = + val; // Assign the value to the correct index in the array } + } - /** - * @brief Utility function to print array - * @returns void - */ - void show() { - // Going through each element in the list - std::cout << '\n'; - for (uint64_t i = 0; i < top; i++) { - std::cout << data[i] << " "; // print the element - } + /** + * @brief To remove the element from the list + * @param val element that will be removed + * @returns void + */ + void remove(const uint64_t &val) { + uint64_t pos = search(val); // search the index of the value + // if search returns -1, element does not present in the list + if (pos == -1) { + std::cout << "\n Element does not present in the list "; + return; + } + std::cout << "\n" + << data[pos] << " deleted"; // print the appropriate message + // shift all the element 1 left to fill vacant space + for (uint64_t i = pos; i < top; i++) { + data[i] = data[i + 1]; + } + top--; // decrement the top variable to maintain last index + } + + /** + * @brief Utility function to print array + * @returns void + */ + void show() { + // Going through each element in the list + std::cout << '\n'; + for (uint64_t i = 0; i < top; i++) { + std::cout << data[i] << " "; // print the element } - }; // structure list -} // namespace list_array -} // namespace data_structures + } +}; // structure list +} // namespace list_array +} // namespace data_structures /** * @brief Test implementations @@ -216,19 +228,19 @@ static void test() { L.insert(20); L.insert(18); assert(L.top == 7); - L.show(); // To print the array + L.show(); // To print the array // Remove testing - L.remove(12); // Remove Duplicate value in the list - L.remove(15); // Remove the existing value in the list + L.remove(12); // Remove Duplicate value in the list + L.remove(15); // Remove the existing value in the list assert(L.top == 5); - L.remove(50); // Try to remove the non-existing value in the list + L.remove(50); // Try to remove the non-existing value in the list assert(L.top == 5); // LinearSearch testing - assert(L.search(11) == 0); // search for the existing element + assert(L.search(11) == 0); // search for the existing element assert(L.search(12) == 2); - assert(L.search(50) == -1); // search for the non-existing element + assert(L.search(50) == -1); // search for the non-existing element // Sort testing L.sort(); @@ -236,9 +248,9 @@ static void test() { L.show(); // BinarySearch testing - assert(L.search(11) == 1); // search for the existing element + assert(L.search(11) == 1); // search for the existing element assert(L.search(12) == 2); - assert(L.search(50) == -1); // search for the non-existing element + assert(L.search(50) == -1); // search for the non-existing element } /** @@ -246,6 +258,6 @@ static void test() { * @returns 0 on exit */ int main() { - test(); // Execute the tests + test(); // Execute the tests return 0; } diff --git a/data_structures/queue_using_array.cpp b/data_structures/queue_using_array.cpp index 8b9b387c5b8..c2437258328 100644 --- a/data_structures/queue_using_array.cpp +++ b/data_structures/queue_using_array.cpp @@ -18,6 +18,7 @@ * @author [Farbod Ahmadian](https://github.com/farbodahm) */ #include /// for std::array +#include #include /// for io operations constexpr uint16_t max_size{10}; ///< Maximum size of the queue diff --git a/data_structures/sparse_table.cpp b/data_structures/sparse_table.cpp index b99616ae6b0..7b3d74b90c3 100644 --- a/data_structures/sparse_table.cpp +++ b/data_structures/sparse_table.cpp @@ -24,6 +24,7 @@ #include /// for std::array #include /// for assert +#include #include /// for IO operations /** diff --git a/data_structures/stack_using_queue.cpp b/data_structures/stack_using_queue.cpp index b849fcf053a..c5fc55a3e61 100644 --- a/data_structures/stack_using_queue.cpp +++ b/data_structures/stack_using_queue.cpp @@ -9,6 +9,7 @@ * @author [tushar2407](https://github.com/tushar2407) */ #include /// for assert +#include #include /// for IO operations #include /// for queue data structure diff --git a/data_structures/treap.cpp b/data_structures/treap.cpp index 522876941b1..592f49e7dfb 100644 --- a/data_structures/treap.cpp +++ b/data_structures/treap.cpp @@ -19,6 +19,7 @@ #include /// For array #include /// For assert +#include #include /// For IO operations /** @@ -157,11 +158,9 @@ struct Treap { } if (k == key[x]) { return size[childs[x][0]] + 1; - } - else if (k < key[x]) { + } else if (k < key[x]) { return _get_rank(childs[x][0], k); - } - else { + } else { return size[childs[x][0]] + cnt[x] + _get_rank(childs[x][1], k); } } diff --git a/dynamic_programming/trapped_rainwater.cpp b/dynamic_programming/trapped_rainwater.cpp index 8ac80835180..d9e16682754 100644 --- a/dynamic_programming/trapped_rainwater.cpp +++ b/dynamic_programming/trapped_rainwater.cpp @@ -11,8 +11,8 @@ #include /// For std::min and std::max #include /// For assert #include /// For std::size_t -#include /// For integral typedefs -#include /// For std::vector +#include +#include /// For std::vector /* * @namespace diff --git a/dynamic_programming/unbounded_0_1_knapsack.cpp b/dynamic_programming/unbounded_0_1_knapsack.cpp new file mode 100644 index 00000000000..96588fe3936 --- /dev/null +++ b/dynamic_programming/unbounded_0_1_knapsack.cpp @@ -0,0 +1,151 @@ +/** + * @file + * @brief Implementation of the Unbounded 0/1 Knapsack Problem + * + * @details + * The Unbounded 0/1 Knapsack problem allows taking unlimited quantities of each item. + * The goal is to maximize the total value without exceeding the given knapsack capacity. + * Unlike the 0/1 knapsack, where each item can be taken only once, in this variation, + * any item can be picked any number of times as long as the total weight stays within + * the knapsack's capacity. + * + * Given a set of N items, each with a weight and a value, represented by the arrays + * `wt` and `val` respectively, and a knapsack with a weight limit W, the task is to + * fill the knapsack to maximize the total value. + * + * @note weight and value of items is greater than zero + * + * ### Algorithm + * The approach uses dynamic programming to build a solution iteratively. + * A 2D array is used for memoization to store intermediate results, allowing + * the function to avoid redundant calculations. + * + * @author [Sanskruti Yeole](https://github.com/yeolesanskruti) + * @see dynamic_programming/0_1_knapsack.cpp + */ + +#include // Standard input-output stream +#include // Standard library for using dynamic arrays (vectors) +#include // For using assert function to validate test cases +#include // For fixed-width integer types like std::uint16_t + +/** + * @namespace dynamic_programming + * @brief Namespace for dynamic programming algorithms + */ +namespace dynamic_programming { + +/** + * @namespace Knapsack + * @brief Implementation of unbounded 0-1 knapsack problem + */ +namespace unbounded_knapsack { + +/** + * @brief Recursive function to calculate the maximum value obtainable using + * an unbounded knapsack approach. + * + * @param i Current index in the value and weight vectors. + * @param W Remaining capacity of the knapsack. + * @param val Vector of values corresponding to the items. + * @note "val" data type can be changed according to the size of the input. + * @param wt Vector of weights corresponding to the items. + * @note "wt" data type can be changed according to the size of the input. + * @param dp 2D vector for memoization to avoid redundant calculations. + * @return The maximum value that can be obtained for the given index and capacity. + */ +std::uint16_t KnapSackFilling(std::uint16_t i, std::uint16_t W, + const std::vector& val, + const std::vector& wt, + std::vector>& dp) { + if (i == 0) { + if (wt[0] <= W) { + return (W / wt[0]) * val[0]; // Take as many of the first item as possible + } else { + return 0; // Can't take the first item + } + } + if (dp[i][W] != -1) return dp[i][W]; // Return result if available + + int nottake = KnapSackFilling(i - 1, W, val, wt, dp); // Value without taking item i + int take = 0; + if (W >= wt[i]) { + take = val[i] + KnapSackFilling(i, W - wt[i], val, wt, dp); // Value taking item i + } + return dp[i][W] = std::max(take, nottake); // Store and return the maximum value +} + +/** + * @brief Wrapper function to initiate the unbounded knapsack calculation. + * + * @param N Number of items. + * @param W Maximum weight capacity of the knapsack. + * @param val Vector of values corresponding to the items. + * @param wt Vector of weights corresponding to the items. + * @return The maximum value that can be obtained for the given capacity. + */ +std::uint16_t unboundedKnapsack(std::uint16_t N, std::uint16_t W, + const std::vector& val, + const std::vector& wt) { + if(N==0)return 0; // Expect 0 since no items + std::vector> dp(N, std::vector(W + 1, -1)); // Initialize memoization table + return KnapSackFilling(N - 1, W, val, wt, dp); // Start the calculation +} + +} // unbounded_knapsack + +} // dynamic_programming + +/** + * @brief self test implementation + * @return void + */ +static void tests() { + // Test Case 1 + std::uint16_t N1 = 4; // Number of items + std::vector wt1 = {1, 3, 4, 5}; // Weights of the items + std::vector val1 = {6, 1, 7, 7}; // Values of the items + std::uint16_t W1 = 8; // Maximum capacity of the knapsack + // Test the function and assert the expected output + assert(unboundedKnapsack(N1, W1, val1, wt1) == 48); + std::cout << "Maximum Knapsack value " << unboundedKnapsack(N1, W1, val1, wt1) << std::endl; + + // Test Case 2 + std::uint16_t N2 = 3; // Number of items + std::vector wt2 = {10, 20, 30}; // Weights of the items + std::vector val2 = {60, 100, 120}; // Values of the items + std::uint16_t W2 = 5; // Maximum capacity of the knapsack + // Test the function and assert the expected output + assert(unboundedKnapsack(N2, W2, val2, wt2) == 0); + std::cout << "Maximum Knapsack value " << unboundedKnapsack(N2, W2, val2, wt2) << std::endl; + + // Test Case 3 + std::uint16_t N3 = 3; // Number of items + std::vector wt3 = {2, 4, 6}; // Weights of the items + std::vector val3 = {5, 11, 13};// Values of the items + std::uint16_t W3 = 27;// Maximum capacity of the knapsack + // Test the function and assert the expected output + assert(unboundedKnapsack(N3, W3, val3, wt3) == 27); + std::cout << "Maximum Knapsack value " << unboundedKnapsack(N3, W3, val3, wt3) << std::endl; + + // Test Case 4 + std::uint16_t N4 = 0; // Number of items + std::vector wt4 = {}; // Weights of the items + std::vector val4 = {}; // Values of the items + std::uint16_t W4 = 10; // Maximum capacity of the knapsack + assert(unboundedKnapsack(N4, W4, val4, wt4) == 0); + std::cout << "Maximum Knapsack value for empty arrays: " << unboundedKnapsack(N4, W4, val4, wt4) << std::endl; + + std::cout << "All test cases passed!" << std::endl; + +} + +/** + * @brief main function + * @return 0 on successful exit + */ +int main() { + tests(); // Run self test implementation + return 0; +} + diff --git a/geometry/graham_scan_functions.hpp b/geometry/graham_scan_functions.hpp index f6e05095e74..58ac066e498 100644 --- a/geometry/graham_scan_functions.hpp +++ b/geometry/graham_scan_functions.hpp @@ -39,6 +39,7 @@ * *******************************************************************************/ #include /// for std::swap +#include #include /// for mathematics and datatype conversion #include /// for IO operations #include /// for std::stack diff --git a/graph/bidirectional_dijkstra.cpp b/graph/bidirectional_dijkstra.cpp index deef6622758..b4f58d22621 100644 --- a/graph/bidirectional_dijkstra.cpp +++ b/graph/bidirectional_dijkstra.cpp @@ -14,6 +14,7 @@ */ #include /// for assert +#include #include /// for io operations #include /// for variable INF #include /// for the priority_queue of distances diff --git a/graph/connected_components_with_dsu.cpp b/graph/connected_components_with_dsu.cpp index c0194e85163..59dc2fda3cd 100644 --- a/graph/connected_components_with_dsu.cpp +++ b/graph/connected_components_with_dsu.cpp @@ -3,20 +3,24 @@ * @brief [Disjoint union](https://en.wikipedia.org/wiki/Disjoint_union) * * @details - * The Disjoint union is the technique to find connected component in graph efficiently. + * The Disjoint union is the technique to find connected component in graph + * efficiently. * * ### Algorithm - * In Graph, if you have to find out the number of connected components, there are 2 options + * In Graph, if you have to find out the number of connected components, there + * are 2 options * 1. Depth first search * 2. Disjoint union - * 1st option is inefficient, Disjoint union is the most optimal way to find this. + * 1st option is inefficient, Disjoint union is the most optimal way to find + * this. * * @author Unknown author * @author [Sagar Pandya](https://github.com/sagarpandyansit) */ -#include /// for IO operations -#include /// for std::set -#include /// for std::vector +#include +#include /// for IO operations +#include /// for std::set +#include /// for std::vector /** * @namespace graph @@ -25,7 +29,8 @@ namespace graph { /** * @namespace disjoint_union - * @brief Functions for [Disjoint union](https://en.wikipedia.org/wiki/Disjoint_union) implementation + * @brief Functions for [Disjoint + * union](https://en.wikipedia.org/wiki/Disjoint_union) implementation */ namespace disjoint_union { uint32_t number_of_nodes = 0; // denotes number of nodes diff --git a/graph/cycle_check_directed_graph.cpp b/graph/cycle_check_directed_graph.cpp index 8a651cfc5f6..15b92c4d6d2 100644 --- a/graph/cycle_check_directed_graph.cpp +++ b/graph/cycle_check_directed_graph.cpp @@ -7,6 +7,7 @@ * */ +#include #include // for std::cout #include // for std::map #include // for std::queue diff --git a/graph/is_graph_bipartite2.cpp b/graph/is_graph_bipartite2.cpp index f1b04d0707b..0711dfb9296 100644 --- a/graph/is_graph_bipartite2.cpp +++ b/graph/is_graph_bipartite2.cpp @@ -15,6 +15,7 @@ * @author [tushar2407](https://github.com/tushar2407) */ #include /// for assert +#include #include /// for IO operations #include /// for queue data structure #include /// for vector data structure diff --git a/graph/topological_sort.cpp b/graph/topological_sort.cpp index 5de8ed69e7f..6ff81b473f1 100644 --- a/graph/topological_sort.cpp +++ b/graph/topological_sort.cpp @@ -1,50 +1,189 @@ -#include -#include -#include - -int number_of_vertices, - number_of_edges; // For number of Vertices (V) and number of edges (E) -std::vector> graph; -std::vector visited; -std::vector topological_order; - -void dfs(int v) { - visited[v] = true; - for (int u : graph[v]) { - if (!visited[u]) { - dfs(u); +/** + * @file + * @brief [Topological Sort + * Algorithm](https://en.wikipedia.org/wiki/Topological_sorting) + * @details + * Topological sorting of a directed graph is a linear ordering or its vertices + * such that for every directed edge (u,v) from vertex u to vertex v, u comes + * before v in the oredering. + * + * A topological sort is possible only in a directed acyclic graph (DAG). + * This file contains code of finding topological sort using Kahn's Algorithm + * which involves using Depth First Search technique + */ + +#include // For std::reverse +#include // For assert +#include // For IO operations +#include // For std::stack +#include // For std::invalid_argument +#include // For std::vector + +/** + * @namespace graph + * @brief Graph algorithms + */ +namespace graph { + +/** + * @namespace topological_sort + * @brief Topological Sort Algorithm + */ +namespace topological_sort { +/** + * @class Graph + * @brief Class that represents a directed graph and provides methods for + * manipulating the graph + */ +class Graph { + private: + int n; // Number of nodes + std::vector> adj; // Adjacency list representation + + public: + /** + * @brief Constructor for the Graph class + * @param nodes Number of nodes in the graph + */ + Graph(int nodes) : n(nodes), adj(nodes) {} + + /** + * @brief Function that adds an edge between two nodes or vertices of graph + * @param u Start node of the edge + * @param v End node of the edge + */ + void addEdge(int u, int v) { adj[u].push_back(v); } + + /** + * @brief Get the adjacency list of the graph + * @returns A reference to the adjacency list + */ + const std::vector>& getAdjacencyList() const { + return adj; + } + + /** + * @brief Get the number of nodes in the graph + * @returns The number of nodes + */ + int getNumNodes() const { return n; } +}; + +/** + * @brief Function to perform Depth First Search on the graph + * @param v Starting vertex for depth-first search + * @param visited Array representing whether each node has been visited + * @param graph Adjacency list of the graph + * @param s Stack containing the vertices for topological sorting + */ +void dfs(int v, std::vector& visited, + const std::vector>& graph, std::stack& s) { + visited[v] = 1; + for (int neighbour : graph[v]) { + if (!visited[neighbour]) { + dfs(neighbour, visited, graph, s); } } - topological_order.push_back(v); + s.push(v); } -void topological_sort() { - visited.assign(number_of_vertices, false); - topological_order.clear(); - for (int i = 0; i < number_of_vertices; ++i) { +/** + * @brief Function to get the topological sort of the graph + * @param g Graph object + * @returns A vector containing the topological order of nodes + */ +std::vector topologicalSort(const Graph& g) { + int n = g.getNumNodes(); + const auto& adj = g.getAdjacencyList(); + std::vector visited(n, 0); + std::stack s; + + for (int i = 0; i < n; i++) { if (!visited[i]) { - dfs(i); + dfs(i, visited, adj, s); } } - reverse(topological_order.begin(), topological_order.end()); + + std::vector ans; + while (!s.empty()) { + int elem = s.top(); + s.pop(); + ans.push_back(elem); + } + + if (ans.size() < n) { // Cycle detected + throw std::invalid_argument("cycle detected in graph"); + } + return ans; } -int main() { - std::cout - << "Enter the number of vertices and the number of directed edges\n"; - std::cin >> number_of_vertices >> number_of_edges; - int x = 0, y = 0; - graph.resize(number_of_vertices, std::vector()); - for (int i = 0; i < number_of_edges; ++i) { - std::cin >> x >> y; - x--, y--; // to convert 1-indexed to 0-indexed - graph[x].push_back(y); - } - topological_sort(); - std::cout << "Topological Order : \n"; - for (int v : topological_order) { - std::cout << v + 1 - << ' '; // converting zero based indexing back to one based. +} // namespace topological_sort +} // namespace graph + +/** + * @brief Self-test implementation + * @returns void + */ +static void test() { + // Test 1 + std::cout << "Testing for graph 1\n"; + int n_1 = 6; + graph::topological_sort::Graph graph1(n_1); + graph1.addEdge(4, 0); + graph1.addEdge(5, 0); + graph1.addEdge(5, 2); + graph1.addEdge(2, 3); + graph1.addEdge(3, 1); + graph1.addEdge(4, 1); + std::vector ans_1 = graph::topological_sort::topologicalSort(graph1); + std::vector expected_1 = {5, 4, 2, 3, 1, 0}; + std::cout << "Topological Sorting Order: "; + for (int i : ans_1) { + std::cout << i << " "; + } + std::cout << '\n'; + assert(ans_1 == expected_1); + std::cout << "Test Passed\n\n"; + + // Test 2 + std::cout << "Testing for graph 2\n"; + int n_2 = 5; + graph::topological_sort::Graph graph2(n_2); + graph2.addEdge(0, 1); + graph2.addEdge(0, 2); + graph2.addEdge(1, 2); + graph2.addEdge(2, 3); + graph2.addEdge(1, 3); + graph2.addEdge(2, 4); + std::vector ans_2 = graph::topological_sort::topologicalSort(graph2); + std::vector expected_2 = {0, 1, 2, 4, 3}; + std::cout << "Topological Sorting Order: "; + for (int i : ans_2) { + std::cout << i << " "; } std::cout << '\n'; + assert(ans_2 == expected_2); + std::cout << "Test Passed\n\n"; + + // Test 3 - Graph with cycle + std::cout << "Testing for graph 3\n"; + int n_3 = 3; + graph::topological_sort::Graph graph3(n_3); + graph3.addEdge(0, 1); + graph3.addEdge(1, 2); + graph3.addEdge(2, 0); + try { + graph::topological_sort::topologicalSort(graph3); + } catch (std::invalid_argument& err) { + assert(std::string(err.what()) == "cycle detected in graph"); + } + std::cout << "Test Passed\n"; +} + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + test(); // run self test implementations return 0; } diff --git a/graph/travelling_salesman_problem.cpp b/graph/travelling_salesman_problem.cpp index 28df3e93155..1b329d5cb32 100644 --- a/graph/travelling_salesman_problem.cpp +++ b/graph/travelling_salesman_problem.cpp @@ -19,6 +19,7 @@ #include /// for std::min #include /// for assert +#include #include /// for IO operations #include /// for limits of integral types #include /// for std::vector diff --git a/greedy_algorithms/binary_addition.cpp b/greedy_algorithms/binary_addition.cpp new file mode 100644 index 00000000000..4a365323815 --- /dev/null +++ b/greedy_algorithms/binary_addition.cpp @@ -0,0 +1,119 @@ +/** + * @file binary_addition.cpp + * @brief Adds two binary numbers and outputs resulting string + * + * @details The algorithm for adding two binary strings works by processing them + * from right to left, similar to manual addition. It starts by determining the + * longer string's length to ensure both strings are fully traversed. For each + * pair of corresponding bits and any carry from the previous addition, it + * calculates the sum. If the sum exceeds 1, a carry is generated for the next + * bit. The results for each bit are collected in a result string, which is + * reversed at the end to present the final binary sum correctly. Additionally, + * the function validates the input to ensure that only valid binary strings + * (containing only '0' and '1') are processed. If invalid input is detected, + * it returns an empty string. + * @author [Muhammad Junaid Khalid](https://github.com/mjk22071998) + */ + +#include /// for reverse function +#include /// for tests +#include /// for input and outputs +#include /// for string class + +/** + * @namespace + * @brief Greedy Algorithms + */ +namespace greedy_algorithms { +/** + * @brief A class to perform binary addition of two binary strings. + */ +class BinaryAddition { + public: + /** + * @brief Adds two binary strings and returns the result as a binary string. + * @param a The first binary string. + * @param b The second binary string. + * @return The sum of the two binary strings as a binary string, or an empty + * string if either input string contains non-binary characters. + */ + std::string addBinary(const std::string& a, const std::string& b) { + if (!isValidBinaryString(a) || !isValidBinaryString(b)) { + return ""; // Return empty string if input contains non-binary + // characters + } + + std::string result; + int carry = 0; + int maxLength = std::max(a.size(), b.size()); + + // Traverse both strings from the end to the beginning + for (int i = 0; i < maxLength; ++i) { + // Get the current bits from both strings, if available + int bitA = (i < a.size()) ? (a[a.size() - 1 - i] - '0') : 0; + int bitB = (i < b.size()) ? (b[b.size() - 1 - i] - '0') : 0; + + // Calculate the sum of bits and carry + int sum = bitA + bitB + carry; + carry = sum / 2; // Determine the carry for the next bit + result.push_back((sum % 2) + + '0'); // Append the sum's current bit to result + } + if (carry) { + result.push_back('1'); + } + std::reverse(result.begin(), result.end()); + return result; + } + + private: + /** + * @brief Validates whether a string contains only binary characters (0 or 1). + * @param str The string to validate. + * @return true if the string is binary, false otherwise. + */ + bool isValidBinaryString(const std::string& str) const { + return std::all_of(str.begin(), str.end(), + [](char c) { return c == '0' || c == '1'; }); + } +}; +} // namespace greedy_algorithms + +/** + * @brief run self test implementation. + * @returns void + */ +static void tests() { + greedy_algorithms::BinaryAddition binaryAddition; + + // Valid binary string tests + assert(binaryAddition.addBinary("1010", "1101") == "10111"); + assert(binaryAddition.addBinary("1111", "1111") == "11110"); + assert(binaryAddition.addBinary("101", "11") == "1000"); + assert(binaryAddition.addBinary("0", "0") == "0"); + assert(binaryAddition.addBinary("1111", "1111") == "11110"); + assert(binaryAddition.addBinary("0", "10101") == "10101"); + assert(binaryAddition.addBinary("10101", "0") == "10101"); + assert(binaryAddition.addBinary("101010101010101010101010101010", + "110110110110110110110110110110") == + "1100001100001100001100001100000"); + assert(binaryAddition.addBinary("1", "11111111") == "100000000"); + assert(binaryAddition.addBinary("10101010", "01010101") == "11111111"); + + // Invalid binary string tests (should return empty string) + assert(binaryAddition.addBinary("10102", "1101") == ""); + assert(binaryAddition.addBinary("ABC", "1101") == ""); + assert(binaryAddition.addBinary("1010", "1102") == ""); + assert(binaryAddition.addBinary("111", "1x1") == ""); + assert(binaryAddition.addBinary("1x1", "111") == ""); + assert(binaryAddition.addBinary("1234", "1101") == ""); +} + +/** + * @brief main function + * @returns 0 on successful exit + */ +int main() { + tests(); /// To execute tests + return 0; +} diff --git a/greedy_algorithms/digit_separation.cpp b/greedy_algorithms/digit_separation.cpp new file mode 100644 index 00000000000..68735741153 --- /dev/null +++ b/greedy_algorithms/digit_separation.cpp @@ -0,0 +1,142 @@ +/** + * @file digit_separation.cpp + * @brief Separates digits from numbers in forward and reverse order + * @see https://www.log2base2.com/c-examples/loop/split-a-number-into-digits-in-c.html + * @details The DigitSeparation class provides two methods to separate the + * digits of large integers: digitSeparationReverseOrder and + * digitSeparationForwardOrder. The digitSeparationReverseOrder method extracts + * digits by repeatedly applying the modulus operation (% 10) to isolate the + * last digit, then divides the number by 10 to remove it. This process + * continues until the entire number is broken down into its digits, which are + * stored in reverse order. If the number is zero, the method directly returns a + * vector containing {0} to handle this edge case. Negative numbers are handled + * by taking the absolute value, ensuring consistent behavior regardless of the + * sign. + * @author [Muhammad Junaid Khalid](https://github.com/mjk22071998) + */ + +#include /// For reveresing the vector +#include /// For assert() function to check for errors +#include /// For abs() function +#include /// For int64_t data type to handle large numbers +#include /// For input/output operations +#include /// For std::vector to store separated digits + +/** + * @namespace + * @brief Greedy Algorithms + */ +namespace greedy_algorithms { + +/** + * @brief A class that provides methods to separate the digits of a large + * positive number. + */ +class DigitSeparation { + public: + /** + * @brief Default constructor for the DigitSeparation class. + */ + DigitSeparation() {} + + /** + * @brief Implementation of digitSeparationReverseOrder method. + * + * @param largeNumber The large number to separate digits from. + * @return A vector of digits in reverse order. + */ + std::vector digitSeparationReverseOrder( + std::int64_t largeNumber) const { + std::vector result; + if (largeNumber != 0) { + while (largeNumber != 0) { + result.push_back(std::abs(largeNumber % 10)); + largeNumber /= 10; + } + } else { + result.push_back(0); + } + return result; + } + + /** + * @brief Implementation of digitSeparationForwardOrder method. + * + * @param largeNumber The large number to separate digits from. + * @return A vector of digits in forward order. + */ + std::vector digitSeparationForwardOrder( + std::int64_t largeNumber) const { + std::vector result = + digitSeparationReverseOrder(largeNumber); + std::reverse(result.begin(), result.end()); + return result; + } +}; + +} // namespace greedy_algorithms + +/** + * @brief self test implementation + * @return void + */ +static void tests() { + greedy_algorithms::DigitSeparation ds; + + // Test case: Positive number + std::int64_t number = 1234567890; + std::vector expectedReverse = {0, 9, 8, 7, 6, 5, 4, 3, 2, 1}; + std::vector expectedForward = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}; + std::vector reverseOrder = + ds.digitSeparationReverseOrder(number); + assert(reverseOrder == expectedReverse); + std::vector forwardOrder = + ds.digitSeparationForwardOrder(number); + assert(forwardOrder == expectedForward); + + // Test case: Single digit number + number = 5; + expectedReverse = {5}; + expectedForward = {5}; + reverseOrder = ds.digitSeparationReverseOrder(number); + assert(reverseOrder == expectedReverse); + forwardOrder = ds.digitSeparationForwardOrder(number); + assert(forwardOrder == expectedForward); + + // Test case: Zero + number = 0; + expectedReverse = {0}; + expectedForward = {0}; + reverseOrder = ds.digitSeparationReverseOrder(number); + assert(reverseOrder == expectedReverse); + forwardOrder = ds.digitSeparationForwardOrder(number); + assert(forwardOrder == expectedForward); + + // Test case: Large number + number = 987654321012345; + expectedReverse = {5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + expectedForward = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5}; + reverseOrder = ds.digitSeparationReverseOrder(number); + assert(reverseOrder == expectedReverse); + forwardOrder = ds.digitSeparationForwardOrder(number); + assert(forwardOrder == expectedForward); + + // Test case: Negative number + number = -987654321012345; + expectedReverse = {5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + expectedForward = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5}; + reverseOrder = ds.digitSeparationReverseOrder(number); + assert(reverseOrder == expectedReverse); + forwardOrder = ds.digitSeparationForwardOrder(number); + assert(forwardOrder == expectedForward); +} + +/** + * @brief main function + * @return 0 on successful exit + */ +int main() { + tests(); // run self test implementation + + return 0; +} diff --git a/greedy_algorithms/gale_shapley.cpp b/greedy_algorithms/gale_shapley.cpp index ddc6298fa44..6267bf3b160 100644 --- a/greedy_algorithms/gale_shapley.cpp +++ b/greedy_algorithms/gale_shapley.cpp @@ -1,24 +1,26 @@ /** * @file - * @brief [Gale Shapley Algorithm](https://en.wikipedia.org/wiki/Gale%E2%80%93Shapley_algorithm) + * @brief [Gale Shapley + * Algorithm](https://en.wikipedia.org/wiki/Gale%E2%80%93Shapley_algorithm) * @details - * This implementation utilizes the Gale-Shapley algorithm to find stable matches. + * This implementation utilizes the Gale-Shapley algorithm to find stable + * matches. * - * **Gale Shapley Algorithm** aims to find a stable matching between two equally sized - * sets of elements given an ordinal preference for each element. The algorithm was - * introduced by David Gale and Lloyd Shapley in 1962. - * - * Reference: + * **Gale Shapley Algorithm** aims to find a stable matching between two equally + * sized sets of elements given an ordinal preference for each element. The + * algorithm was introduced by David Gale and Lloyd Shapley in 1962. + * + * Reference: * [Wikipedia](https://en.wikipedia.org/wiki/Gale%E2%80%93Shapley_algorithm) * [Wikipedia](https://en.wikipedia.org/wiki/Stable_matching_problem) * * @author [B Karthik](https://github.com/BKarthik7) */ -#include /// for std::u32int_t -#include /// for std::vector #include /// for std::find #include /// for assert +#include /// for std::uint32_t +#include /// for std::vector /** * @namespace @@ -31,19 +33,25 @@ namespace greedy_algorithms { */ namespace stable_matching { /** - * @brief The main function that finds the stable matching between two sets of elements - * using the Gale-Shapley Algorithm. - * @note This doesn't work on negative preferences. the preferences should be continuous integers starting from - * 0 to number of preferences - 1. - * @param primary_preferences the preferences of the primary set should be a 2D vector - * @param secondary_preferences the preferences of the secondary set should be a 2D vector + * @brief The main function that finds the stable matching between two sets of + * elements using the Gale-Shapley Algorithm. + * @note This doesn't work on negative preferences. the preferences should be + * continuous integers starting from 0 to number of preferences - 1. + * @param primary_preferences the preferences of the primary set should be a 2D + * vector + * @param secondary_preferences the preferences of the secondary set should be a + * 2D vector * @returns matches the stable matching between the two sets */ -std::vector gale_shapley(const std::vector>& secondary_preferences, const std::vector>& primary_preferences) { +std::vector gale_shapley( + const std::vector>& secondary_preferences, + const std::vector>& primary_preferences) { std::uint32_t num_elements = secondary_preferences.size(); std::vector matches(num_elements, -1); std::vector is_free_primary(num_elements, true); - std::vector proposal_index(num_elements, 0); // Tracks the next secondary to propose for each primary + std::vector proposal_index( + num_elements, + 0); // Tracks the next secondary to propose for each primary while (true) { int free_primary_index = -1; @@ -57,10 +65,13 @@ std::vector gale_shapley(const std::vector gale_shapley(const std::vector gale_shapley(const std::vector> primary_preferences = {{0, 1, 2, 3}, {2, 1, 3, 0}, {1, 2, 0, 3}, {3, 0, 1, 2}}; - std::vector> secondary_preferences = {{1, 0, 2, 3}, {3, 0, 1, 2}, {0, 2, 1, 3}, {1, 2, 0, 3}}; - assert(greedy_algorithms::stable_matching::gale_shapley(secondary_preferences, primary_preferences) == std::vector({0, 2, 1, 3})); + std::vector> primary_preferences = { + {0, 1, 2, 3}, {2, 1, 3, 0}, {1, 2, 0, 3}, {3, 0, 1, 2}}; + std::vector> secondary_preferences = { + {1, 0, 2, 3}, {3, 0, 1, 2}, {0, 2, 1, 3}, {1, 2, 0, 3}}; + assert(greedy_algorithms::stable_matching::gale_shapley( + secondary_preferences, primary_preferences) == + std::vector({0, 2, 1, 3})); // Test Case 2 - primary_preferences = {{0, 2, 1, 3}, {2, 3, 0, 1}, {3, 1, 2, 0}, {2, 1, 0, 3}}; - secondary_preferences = {{1, 0, 2, 3}, {3, 0, 1, 2}, {0, 2, 1, 3}, {1, 2, 0, 3}}; - assert(greedy_algorithms::stable_matching::gale_shapley(secondary_preferences, primary_preferences) == std::vector({0, 3, 1, 2})); + primary_preferences = { + {0, 2, 1, 3}, {2, 3, 0, 1}, {3, 1, 2, 0}, {2, 1, 0, 3}}; + secondary_preferences = { + {1, 0, 2, 3}, {3, 0, 1, 2}, {0, 2, 1, 3}, {1, 2, 0, 3}}; + assert(greedy_algorithms::stable_matching::gale_shapley( + secondary_preferences, primary_preferences) == + std::vector({0, 3, 1, 2})); // Test Case 3 primary_preferences = {{0, 1, 2}, {2, 1, 0}, {1, 2, 0}}; secondary_preferences = {{1, 0, 2}, {2, 0, 1}, {0, 2, 1}}; - assert(greedy_algorithms::stable_matching::gale_shapley(secondary_preferences, primary_preferences) == std::vector({0, 2, 1})); + assert(greedy_algorithms::stable_matching::gale_shapley( + secondary_preferences, primary_preferences) == + std::vector({0, 2, 1})); // Test Case 4 primary_preferences = {}; secondary_preferences = {}; - assert(greedy_algorithms::stable_matching::gale_shapley(secondary_preferences, primary_preferences) == std::vector({})); + assert(greedy_algorithms::stable_matching::gale_shapley( + secondary_preferences, primary_preferences) == + std::vector({})); } /** @@ -124,6 +150,6 @@ static void tests() { * @returns 0 on exit */ int main() { - tests(); // Run self-test implementations + tests(); // Run self-test implementations return 0; } diff --git a/hashing/md5.cpp b/hashing/md5.cpp index aad75731007..52bc898f7d4 100644 --- a/hashing/md5.cpp +++ b/hashing/md5.cpp @@ -41,6 +41,7 @@ #include /// Used for std::copy #include /// Used for std::array #include /// Used for assert +#include #include /// Used for std::memcopy #include /// Used for IO operations #include /// Used for strings diff --git a/hashing/sha1.cpp b/hashing/sha1.cpp index 106da827296..2d551b99316 100644 --- a/hashing/sha1.cpp +++ b/hashing/sha1.cpp @@ -32,6 +32,7 @@ #include /// For std::copy #include /// For std::array #include /// For assert +#include #include /// For std::memcopy #include /// For IO operations #include /// For strings diff --git a/math/aliquot_sum.cpp b/math/aliquot_sum.cpp index 6d94d53a1be..0b8afdf8920 100644 --- a/math/aliquot_sum.cpp +++ b/math/aliquot_sum.cpp @@ -20,6 +20,7 @@ */ #include /// for assert +#include #include /// for IO operations /** diff --git a/math/area.cpp b/math/area.cpp index 691fe91f0fc..a787e2e3a45 100644 --- a/math/area.cpp +++ b/math/area.cpp @@ -109,6 +109,18 @@ template T cylinder_surface_area(T radius, T height) { return 2 * M_PI * radius * height + 2 * M_PI * pow(radius, 2); } + +/** + * @brief surface area of a [hemi-sphere](https://en.wikipedia.org/wiki/Surface_area) ( 3 * + * pi * r^2) + * @param radius is the radius of the hemi-sphere + * @tparam T datatype of radius + * @returns surface area of the hemi-sphere + */ +template +T hemi_sphere_surface_area(T radius) { + return 3 * M_PI * pow(radius, 2); +} } // namespace math /** @@ -267,6 +279,18 @@ static void test() { std::cout << "Output: " << double_area << std::endl; assert(double_area == double_expected); std::cout << "TEST PASSED" << std::endl << std::endl; + + // 11th test + double_radius = 10.0; + double_expected = 942.4777960769379; + double_area = math::hemi_sphere_surface_area(double_radius); + + std::cout << "SURFACE AREA OF A HEMI-SPHERE" << std::endl; + std::cout << "Input Radius: " << double_radius << std::endl; + std::cout << "Expected Output: " << double_expected << std::endl; + std::cout << "Output: " << double_area << std::endl; + assert(double_area == double_expected); + std::cout << "TEST PASSED" << std::endl << std::endl; } /** diff --git a/math/check_factorial.cpp b/math/check_factorial.cpp index 0be45b89509..0b63433ebe4 100644 --- a/math/check_factorial.cpp +++ b/math/check_factorial.cpp @@ -10,6 +10,7 @@ * @author [ewd00010](https://github.com/ewd00010) */ #include /// for assert +#include #include /// for cout /** diff --git a/math/double_factorial.cpp b/math/double_factorial.cpp index 72feda60c4e..5832ce440d6 100644 --- a/math/double_factorial.cpp +++ b/math/double_factorial.cpp @@ -10,6 +10,7 @@ */ #include +#include #include /** Compute double factorial using iterative method diff --git a/math/eulers_totient_function.cpp b/math/eulers_totient_function.cpp index f1752e9e92a..768034327da 100644 --- a/math/eulers_totient_function.cpp +++ b/math/eulers_totient_function.cpp @@ -1,6 +1,7 @@ /** * @file - * @brief Implementation of [Euler's Totient](https://en.wikipedia.org/wiki/Euler%27s_totient_function) + * @brief Implementation of [Euler's + * Totient](https://en.wikipedia.org/wiki/Euler%27s_totient_function) * @description * Euler Totient Function is also known as phi function. * \f[\phi(n) = @@ -24,8 +25,9 @@ * @author [Mann Mehta](https://github.com/mann2108) */ -#include /// for IO operations -#include /// for assert +#include /// for assert +#include +#include /// for IO operations /** * @brief Mathematical algorithms @@ -39,12 +41,14 @@ namespace math { uint64_t phiFunction(uint64_t n) { uint64_t result = n; for (uint64_t i = 2; i * i <= n; i++) { - if (n % i != 0) continue; + if (n % i != 0) + continue; while (n % i == 0) n /= i; result -= result / i; } - if (n > 1) result -= result / n; + if (n > 1) + result -= result / n; return result; } diff --git a/math/factorial.cpp b/math/factorial.cpp index acfa053d89a..aca1886c783 100644 --- a/math/factorial.cpp +++ b/math/factorial.cpp @@ -12,8 +12,8 @@ */ #include /// for assert +#include #include /// for I/O operations - /** * @namespace * @brief Mathematical algorithms diff --git a/math/fibonacci.cpp b/math/fibonacci.cpp index 4e3c15de86e..79048040ac4 100644 --- a/math/fibonacci.cpp +++ b/math/fibonacci.cpp @@ -1,68 +1,67 @@ /** * @file - * @brief Generate fibonacci sequence + * @brief n-th [Fibonacci + * number](https://en.wikipedia.org/wiki/Fibonacci_sequence). * - * Calculate the the value on Fibonacci's sequence given an - * integer as input. + * @details + * Naive recursive implementation to calculate the n-th Fibonacci number. * \f[\text{fib}(n) = \text{fib}(n-1) + \text{fib}(n-2)\f] * * @see fibonacci_large.cpp, fibonacci_fast.cpp, string_fibonacci.cpp */ -#include -#include +#include +#include /// for assert +#include /// for IO operations + +/** + * @namespace math + * @brief Math algorithms + */ +namespace math { /** - * Recursively compute sequences - * @param n input - * @returns n-th element of the Fbinacci's sequence + * @namespace fibonacci + * @brief Functions for Fibonacci sequence + */ +namespace fibonacci { +/** + * @brief Function to compute the n-th Fibonacci number + * @param n the index of the Fibonacci number + * @returns n-th element of the Fibonacci's sequence */ uint64_t fibonacci(uint64_t n) { - /* If the input is 0 or 1 just return the same - This will set the first 2 values of the sequence */ + // If the input is 0 or 1 just return the same (Base Case) + // This will set the first 2 values of the sequence if (n <= 1) { return n; } - /* Add the last 2 values of the sequence to get next */ + // Add the preceding 2 values of the sequence to get next return fibonacci(n - 1) + fibonacci(n - 2); } +} // namespace fibonacci +} // namespace math /** - * Function for testing the fibonacci() function with a few - * test cases and assert statement. + * @brief Self-test implementation * @returns `void` -*/ + */ static void test() { - uint64_t test_case_1 = fibonacci(0); - assert(test_case_1 == 0); - std::cout << "Passed Test 1!" << std::endl; - - uint64_t test_case_2 = fibonacci(1); - assert(test_case_2 == 1); - std::cout << "Passed Test 2!" << std::endl; - - uint64_t test_case_3 = fibonacci(2); - assert(test_case_3 == 1); - std::cout << "Passed Test 3!" << std::endl; - - uint64_t test_case_4 = fibonacci(3); - assert(test_case_4 == 2); - std::cout << "Passed Test 4!" << std::endl; - - uint64_t test_case_5 = fibonacci(4); - assert(test_case_5 == 3); - std::cout << "Passed Test 5!" << std::endl; - - uint64_t test_case_6 = fibonacci(15); - assert(test_case_6 == 610); - std::cout << "Passed Test 6!" << std::endl << std::endl; + assert(math::fibonacci::fibonacci(0) == 0); + assert(math::fibonacci::fibonacci(1) == 1); + assert(math::fibonacci::fibonacci(2) == 1); + assert(math::fibonacci::fibonacci(3) == 2); + assert(math::fibonacci::fibonacci(4) == 3); + assert(math::fibonacci::fibonacci(15) == 610); + assert(math::fibonacci::fibonacci(20) == 6765); + std::cout << "All tests have passed successfully!\n"; } -/// Main function +/** + * @brief Main function + * @returns 0 on exit + */ int main() { - test(); - int n = 0; - std::cin >> n; - assert(n >= 0); - std::cout << "F(" << n << ")= " << fibonacci(n) << std::endl; + test(); // run self-test implementations + return 0; } diff --git a/math/fibonacci_matrix_exponentiation.cpp b/math/fibonacci_matrix_exponentiation.cpp index 1a119d210aa..915cf62b580 100644 --- a/math/fibonacci_matrix_exponentiation.cpp +++ b/math/fibonacci_matrix_exponentiation.cpp @@ -1,101 +1,103 @@ /** - * @file + * @file * @brief This program computes the N^th Fibonacci number in modulo mod * input argument . * * Takes O(logn) time to compute nth Fibonacci number - * + * * * \author [villayatali123](https://github.com/villayatali123) * \author [unknown author]() - * @see fibonacci.cpp, fibonacci_fast.cpp, string_fibonacci.cpp, fibonacci_large.cpp + * @see fibonacci.cpp, fibonacci_fast.cpp, string_fibonacci.cpp, + * fibonacci_large.cpp */ -#include -#include #include +#include +#include +#include /** * This function finds nth fibonacci number in a given modulus * @param n nth fibonacci number - * @param mod modulo number + * @param mod modulo number */ -uint64_t fibo(uint64_t n , uint64_t mod ) -{ - std::vector result(2,0); - std::vector> transition(2,std::vector(2,0)); - std::vector> Identity(2,std::vector(2,0)); - n--; - result[0]=1, result[1]=1; - Identity[0][0]=1; Identity[0][1]=0; - Identity[1][0]=0; Identity[1][1]=1; - - transition[0][0]=0; - transition[1][0]=transition[1][1]=transition[0][1]=1; - - while(n) - { - if(n%2) - { - std::vector> res(2, std::vector(2,0)); - for(int i=0;i<2;i++) - { - for(int j=0;j<2;j++) - { - for(int k=0;k<2;k++) - { - res[i][j]=(res[i][j]%mod+((Identity[i][k]%mod*transition[k][j]%mod))%mod)%mod; - } - } - } - for(int i=0;i<2;i++) - { - for(int j=0;j<2;j++) - { - Identity[i][j]=res[i][j]; - } - } - n--; - } - else{ - std::vector> res1(2, std::vector(2,0)); - for(int i=0;i<2;i++) - { - for(int j=0;j<2;j++) - { - for(int k=0;k<2;k++) - { - res1[i][j]=(res1[i][j]%mod+((transition[i][k]%mod*transition[k][j]%mod))%mod)%mod; - } - } - } - for(int i=0;i<2;i++) - { - for(int j=0;j<2;j++) - { - transition[i][j]=res1[i][j]; - } - } - n=n/2; - } - } - return ((result[0]%mod*Identity[0][0]%mod)%mod+(result[1]%mod*Identity[1][0]%mod)%mod)%mod; +uint64_t fibo(uint64_t n, uint64_t mod) { + std::vector result(2, 0); + std::vector> transition(2, + std::vector(2, 0)); + std::vector> Identity(2, std::vector(2, 0)); + n--; + result[0] = 1, result[1] = 1; + Identity[0][0] = 1; + Identity[0][1] = 0; + Identity[1][0] = 0; + Identity[1][1] = 1; + + transition[0][0] = 0; + transition[1][0] = transition[1][1] = transition[0][1] = 1; + + while (n) { + if (n % 2) { + std::vector> res(2, + std::vector(2, 0)); + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + for (int k = 0; k < 2; k++) { + res[i][j] = + (res[i][j] % mod + + ((Identity[i][k] % mod * transition[k][j] % mod)) % + mod) % + mod; + } + } + } + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + Identity[i][j] = res[i][j]; + } + } + n--; + } else { + std::vector> res1( + 2, std::vector(2, 0)); + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + for (int k = 0; k < 2; k++) { + res1[i][j] = + (res1[i][j] % mod + ((transition[i][k] % mod * + transition[k][j] % mod)) % + mod) % + mod; + } + } + } + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + transition[i][j] = res1[i][j]; + } + } + n = n / 2; + } + } + return ((result[0] % mod * Identity[0][0] % mod) % mod + + (result[1] % mod * Identity[1][0] % mod) % mod) % + mod; } /** * Function to test above algorithm */ -void test() -{ - assert(fibo(6, 1000000007 ) == 8); +static void test() { + assert(fibo(6, 1000000007) == 8); std::cout << "test case:1 passed\n"; - assert(fibo(5, 1000000007 ) == 5); + assert(fibo(5, 1000000007) == 5); std::cout << "test case:2 passed\n"; - assert(fibo(10 , 1000000007) == 55); + assert(fibo(10, 1000000007) == 55); std::cout << "test case:3 passed\n"; - assert(fibo(500 , 100) == 25); + assert(fibo(500, 100) == 25); std::cout << "test case:3 passed\n"; - assert(fibo(500 , 10000) == 4125); + assert(fibo(500, 10000) == 4125); std::cout << "test case:3 passed\n"; std::cout << "--All tests passed--\n"; } @@ -103,11 +105,12 @@ void test() /** * Main function */ -int main() -{ - test(); - uint64_t mod=1000000007; - std::cout<<"Enter the value of N: "; - uint64_t n=0; std::cin>>n; - std::cout<> n; + std::cout << n << "th Fibonacci number in modulo " << mod << ": " + << fibo(n, mod) << std::endl; } diff --git a/math/fibonacci_sum.cpp b/math/fibonacci_sum.cpp index 5107578888e..9373e21cb87 100644 --- a/math/fibonacci_sum.cpp +++ b/math/fibonacci_sum.cpp @@ -13,6 +13,7 @@ */ #include /// for assert +#include #include /// for std::cin and std::cout #include /// for std::vector diff --git a/math/finding_number_of_digits_in_a_number.cpp b/math/finding_number_of_digits_in_a_number.cpp index 32ddd649ec3..b0edb746d65 100644 --- a/math/finding_number_of_digits_in_a_number.cpp +++ b/math/finding_number_of_digits_in_a_number.cpp @@ -18,6 +18,7 @@ #include /// for assert #include /// for log calculation +#include #include /// for IO operations /** diff --git a/math/gcd_of_n_numbers.cpp b/math/gcd_of_n_numbers.cpp index 92968ff1265..45ba0b074ef 100644 --- a/math/gcd_of_n_numbers.cpp +++ b/math/gcd_of_n_numbers.cpp @@ -1,41 +1,114 @@ /** * @file - * @brief This program aims at calculating the GCD of n numbers by division - * method + * @brief This program aims at calculating the GCD of n numbers + * + * @details + * The GCD of n numbers can be calculated by + * repeatedly calculating the GCDs of pairs of numbers + * i.e. \f$\gcd(a, b, c)\f$ = \f$\gcd(\gcd(a, b), c)\f$ + * Euclidean algorithm helps calculate the GCD of each pair of numbers + * efficiently * * @see gcd_iterative_euclidean.cpp, gcd_recursive_euclidean.cpp */ -#include +#include /// for std::abs +#include /// for std::array +#include /// for assert +#include /// for IO operations -/** Compute GCD using division algorithm - * - * @param[in] a array of integers to compute GCD for - * @param[in] n number of integers in array `a` - */ -int gcd(int *a, int n) { - int j = 1; // to access all elements of the array starting from 1 - int gcd = a[0]; - while (j < n) { - if (a[j] % gcd == 0) // value of gcd is as needed so far - j++; // so we check for next element - else - gcd = a[j] % gcd; // calculating GCD by division method +/** + * @namespace math + * @brief Maths algorithms + */ +namespace math { +/** + * @namespace gcd_of_n_numbers + * @brief Compute GCD of numbers in an array + */ +namespace gcd_of_n_numbers { +/** + * @brief Function to compute GCD of 2 numbers x and y + * @param x First number + * @param y Second number + * @return GCD of x and y via recursion + */ +int gcd_two(int x, int y) { + // base cases + if (y == 0) { + return x; + } + if (x == 0) { + return y; + } + return gcd_two(y, x % y); // Euclidean method +} + +/** + * @brief Function to check if all elements in the array are 0 + * @param a Array of numbers + * @return 'True' if all elements are 0 + * @return 'False' if not all elements are 0 + */ +template +bool check_all_zeros(const std::array &a) { + // Use std::all_of to simplify zero-checking + return std::all_of(a.begin(), a.end(), [](int x) { return x == 0; }); +} + +/** + * @brief Main program to compute GCD using the Euclidean algorithm + * @param a Array of integers to compute GCD for + * @return GCD of the numbers in the array or std::nullopt if undefined + */ +template +int gcd(const std::array &a) { + // GCD is undefined if all elements in the array are 0 + if (check_all_zeros(a)) { + return -1; // Use std::optional to represent undefined GCD + } + + // divisors can be negative, we only want the positive value + int result = std::abs(a[0]); + for (std::size_t i = 1; i < n; ++i) { + result = gcd_two(result, std::abs(a[i])); + if (result == 1) { + break; // Further computations still result in gcd of 1 } - return gcd; + } + return result; } +} // namespace gcd_of_n_numbers +} // namespace math -/** Main function */ -int main() { - int n; - std::cout << "Enter value of n:" << std::endl; - std::cin >> n; - int *a = new int[n]; - int i; - std::cout << "Enter the n numbers:" << std::endl; - for (i = 0; i < n; i++) std::cin >> a[i]; +/** + * @brief Self-test implementation + * @return void + */ +static void test() { + std::array array_1 = {0}; + std::array array_2 = {1}; + std::array array_3 = {0, 2}; + std::array array_4 = {-60, 24, 18}; + std::array array_5 = {100, -100, -100, 200}; + std::array array_6 = {0, 0, 0, 0, 0}; + std::array array_7 = {10350, -24150, 0, 17250, 37950, -127650, 51750}; + std::array array_8 = {9500000, -12121200, 0, 4444, 0, 0, 123456789}; - std::cout << "GCD of entered n numbers:" << gcd(a, n) << std::endl; + assert(math::gcd_of_n_numbers::gcd(array_1) == -1); + assert(math::gcd_of_n_numbers::gcd(array_2) == 1); + assert(math::gcd_of_n_numbers::gcd(array_3) == 2); + assert(math::gcd_of_n_numbers::gcd(array_4) == 6); + assert(math::gcd_of_n_numbers::gcd(array_5) == 100); + assert(math::gcd_of_n_numbers::gcd(array_6) == -1); + assert(math::gcd_of_n_numbers::gcd(array_7) == 3450); + assert(math::gcd_of_n_numbers::gcd(array_8) == 1); +} - delete[] a; - return 0; +/** + * @brief Main function + * @return 0 on exit + */ +int main() { + test(); // run self-test implementation + return 0; } diff --git a/math/integral_approximation.cpp b/math/integral_approximation.cpp index 7e72ef4042b..2d1d42f916d 100644 --- a/math/integral_approximation.cpp +++ b/math/integral_approximation.cpp @@ -1,18 +1,29 @@ /** * @file - * @brief Compute integral approximation of the function using [Riemann sum](https://en.wikipedia.org/wiki/Riemann_sum) - * @details In mathematics, a Riemann sum is a certain kind of approximation of an integral by a finite sum. It is named after nineteenth-century German mathematician Bernhard Riemann. - * One very common application is approximating the area of functions or lines on a graph and the length of curves and other approximations. - * The sum is calculated by partitioning the region into shapes (rectangles, trapezoids, parabolas, or cubics) that form a region similar to the region being measured, then calculating the area for each of these shapes, and finally adding all of these small areas together. - * This approach can be used to find a numerical approximation for a definite integral even if the fundamental theorem of calculus does not make it easy to find a closed-form solution. - * Because the region filled by the small shapes is usually not the same shape as the region being measured, the Riemann sum will differ from the area being measured. - * This error can be reduced by dividing up the region more finely, using smaller and smaller shapes. As the shapes get smaller and smaller, the sum approaches the Riemann integral. - * \author [Benjamin Walton](https://github.com/bwalton24) - * \author [Shiqi Sheng](https://github.com/shiqisheng00) + * @brief Compute integral approximation of the function using [Riemann + * sum](https://en.wikipedia.org/wiki/Riemann_sum) + * @details In mathematics, a Riemann sum is a certain kind of approximation of + * an integral by a finite sum. It is named after nineteenth-century German + * mathematician Bernhard Riemann. One very common application is approximating + * the area of functions or lines on a graph and the length of curves and other + * approximations. The sum is calculated by partitioning the region into shapes + * (rectangles, trapezoids, parabolas, or cubics) that form a region similar to + * the region being measured, then calculating the area for each of these + * shapes, and finally adding all of these small areas together. This approach + * can be used to find a numerical approximation for a definite integral even if + * the fundamental theorem of calculus does not make it easy to find a + * closed-form solution. Because the region filled by the small shapes is + * usually not the same shape as the region being measured, the Riemann sum will + * differ from the area being measured. This error can be reduced by dividing up + * the region more finely, using smaller and smaller shapes. As the shapes get + * smaller and smaller, the sum approaches the Riemann integral. \author + * [Benjamin Walton](https://github.com/bwalton24) \author [Shiqi + * Sheng](https://github.com/shiqisheng00) */ -#include /// for assert -#include /// for mathematical functions -#include /// for passing in functions +#include /// for assert +#include /// for mathematical functions +#include +#include /// for passing in functions #include /// for IO operations /** diff --git a/math/inv_sqrt.cpp b/math/inv_sqrt.cpp index ef490ddf918..96a13fc08a1 100644 --- a/math/inv_sqrt.cpp +++ b/math/inv_sqrt.cpp @@ -10,9 +10,9 @@ #include /// for assert #include /// for `std::sqrt` +#include #include /// for IO operations #include /// for numeric_limits - /** * @brief This is the function that calculates the fast inverse square root. * The following code is the fast inverse square root implementation from diff --git a/math/largest_power.cpp b/math/largest_power.cpp index 8fffbbd078b..bc1ba484754 100644 --- a/math/largest_power.cpp +++ b/math/largest_power.cpp @@ -1,41 +1,42 @@ /** * @file - * @brief Algorithm to find largest x such that p^x divides n! (factorial) using Legendre's Formula. - * @details Given an integer n and a prime number p, the task is to find the largest x such that - * p^x (p raised to power x) divides n! (factorial). This will be done using Legendre's formula: - * x = [n/(p^1)] + [n/(p^2)] + [n/(p^3)] + \ldots + 1 - * @see more on https://math.stackexchange.com/questions/141196/highest-power-of-a-prime-p-dividing-n + * @brief Algorithm to find largest x such that p^x divides n! (factorial) using + * Legendre's Formula. + * @details Given an integer n and a prime number p, the task is to find the + * largest x such that p^x (p raised to power x) divides n! (factorial). This + * will be done using Legendre's formula: x = [n/(p^1)] + [n/(p^2)] + [n/(p^3)] + * + \ldots + 1 + * @see more on + * https://math.stackexchange.com/questions/141196/highest-power-of-a-prime-p-dividing-n * @author [uday6670](https://github.com/uday6670) */ -#include /// for std::cin and std::cout -#include /// for assert - +#include /// for assert +#include +#include /// for std::cin and std::cout /** * @namespace math * @brief Mathematical algorithms */ namespace math { - /** - * @brief Function to calculate largest power - * @param n number - * @param p prime number - * @returns largest power - */ - uint64_t largestPower(uint32_t n, const uint16_t& p) - { - // Initialize result - int x = 0; - - // Calculate result - while (n) - { - n /= p; - x += n; - } - return x; +/** + * @brief Function to calculate largest power + * @param n number + * @param p prime number + * @returns largest power + */ +uint64_t largestPower(uint32_t n, const uint16_t& p) { + // Initialize result + int x = 0; + + // Calculate result + while (n) { + n /= p; + x += n; } + return x; +} } // namespace math @@ -43,36 +44,34 @@ namespace math { * @brief Function for testing largestPower function. * test cases and assert statement. * @returns `void` -*/ -static void test() -{ - uint8_t test_case_1 = math::largestPower(5,2); - assert(test_case_1==3); - std::cout<<"Test 1 Passed!"< /// for assert +#include #include /// for std::cin and std::cout #include /// for std::vector diff --git a/math/linear_recurrence_matrix.cpp b/math/linear_recurrence_matrix.cpp index 98c90e5f8fa..c54d1272bf7 100644 --- a/math/linear_recurrence_matrix.cpp +++ b/math/linear_recurrence_matrix.cpp @@ -18,6 +18,7 @@ * @author [Ashish Daulatabad](https://github.com/AshishYUO) */ #include /// for assert +#include #include /// for IO operations #include /// for std::vector STL diff --git a/math/magic_number.cpp b/math/magic_number.cpp index 51232601b2b..c07869e3dfe 100644 --- a/math/magic_number.cpp +++ b/math/magic_number.cpp @@ -17,6 +17,7 @@ * @author [Neha Hasija](https://github.com/neha-hasija17) */ #include /// for assert +#include #include /// for io operations /** diff --git a/math/modular_division.cpp b/math/modular_division.cpp index 6fd984f2bf0..d37341b4a47 100644 --- a/math/modular_division.cpp +++ b/math/modular_division.cpp @@ -25,6 +25,7 @@ */ #include /// for assert +#include #include /// for IO operations /** diff --git a/math/modular_exponentiation.cpp b/math/modular_exponentiation.cpp index 01410860fda..d4a9dd84d07 100644 --- a/math/modular_exponentiation.cpp +++ b/math/modular_exponentiation.cpp @@ -17,8 +17,8 @@ * @author [Shri2206](https://github.com/Shri2206) */ #include /// for assert +#include #include /// for io operations - /** * @namespace math * @brief Mathematical algorithms diff --git a/math/modular_inverse_fermat_little_theorem.cpp b/math/modular_inverse_fermat_little_theorem.cpp index 7550e14bf23..d870c4da978 100644 --- a/math/modular_inverse_fermat_little_theorem.cpp +++ b/math/modular_inverse_fermat_little_theorem.cpp @@ -30,8 +30,8 @@ * a^{m-2} &≡& a^{-1} \;\text{mod}\; m * \f} * - * We will find the exponent using binary exponentiation. Such that the - * algorithm works in \f$O(\log m)\f$ time. + * We will find the exponent using binary exponentiation such that the + * algorithm works in \f$O(\log n)\f$ time. * * Examples: - * * a = 3 and m = 7 @@ -43,56 +43,98 @@ * (as \f$a\times a^{-1} = 1\f$) */ -#include -#include +#include /// for assert +#include /// for std::int64_t +#include /// for IO implementations -/** Recursive function to calculate exponent in \f$O(\log n)\f$ using binary - * exponent. +/** + * @namespace math + * @brief Maths algorithms. + */ +namespace math { +/** + * @namespace modular_inverse_fermat + * @brief Calculate modular inverse using Fermat's Little Theorem. + */ +namespace modular_inverse_fermat { +/** + * @brief Calculate exponent with modulo using binary exponentiation in \f$O(\log b)\f$ time. + * @param a The base + * @param b The exponent + * @param m The modulo + * @return The result of \f$a^{b} % m\f$ */ -int64_t binExpo(int64_t a, int64_t b, int64_t m) { - a %= m; - int64_t res = 1; - while (b > 0) { - if (b % 2) { - res = res * a % m; - } - a = a * a % m; - // Dividing b by 2 is similar to right shift. - b >>= 1; +std::int64_t binExpo(std::int64_t a, std::int64_t b, std::int64_t m) { + a %= m; + std::int64_t res = 1; + while (b > 0) { + if (b % 2 != 0) { + res = res * a % m; } - return res; + a = a * a % m; + // Dividing b by 2 is similar to right shift by 1 bit + b >>= 1; + } + return res; } - -/** Prime check in \f$O(\sqrt{m})\f$ time. +/** + * @brief Check if an integer is a prime number in \f$O(\sqrt{m})\f$ time. + * @param m An intger to check for primality + * @return true if the number is prime + * @return false if the number is not prime */ -bool isPrime(int64_t m) { - if (m <= 1) { - return false; - } else { - for (int64_t i = 2; i * i <= m; i++) { - if (m % i == 0) { - return false; - } - } +bool isPrime(std::int64_t m) { + if (m <= 1) { + return false; + } + for (std::int64_t i = 2; i * i <= m; i++) { + if (m % i == 0) { + return false; } - return true; + } + return true; +} +/** + * @brief calculates the modular inverse. + * @param a Integer value for the base + * @param m Integer value for modulo + * @return The result that is the modular inverse of a modulo m + */ +std::int64_t modular_inverse(std::int64_t a, std::int64_t m) { + while (a < 0) { + a += m; + } + + // Check for invalid cases + if (!isPrime(m) || a == 0) { + return -1; // Invalid input + } + + return binExpo(a, m - 2, m); // Fermat's Little Theorem +} +} // namespace modular_inverse_fermat +} // namespace math + +/** + * @brief Self-test implementation + * @return void + */ +static void test() { + assert(math::modular_inverse_fermat::modular_inverse(0, 97) == -1); + assert(math::modular_inverse_fermat::modular_inverse(15, -2) == -1); + assert(math::modular_inverse_fermat::modular_inverse(3, 10) == -1); + assert(math::modular_inverse_fermat::modular_inverse(3, 7) == 5); + assert(math::modular_inverse_fermat::modular_inverse(1, 101) == 1); + assert(math::modular_inverse_fermat::modular_inverse(-1337, 285179) == 165519); + assert(math::modular_inverse_fermat::modular_inverse(123456789, 998244353) == 25170271); + assert(math::modular_inverse_fermat::modular_inverse(-9876543210, 1000000007) == 784794281); } /** - * Main function + * @brief Main function + * @return 0 on exit */ int main() { - int64_t a, m; - // Take input of a and m. - std::cout << "Computing ((a^(-1))%(m)) using Fermat's Little Theorem"; - std::cout << std::endl << std::endl; - std::cout << "Give input 'a' and 'm' space separated : "; - std::cin >> a >> m; - if (isPrime(m)) { - std::cout << "The modular inverse of a with mod m is (a^(m-2)) : "; - std::cout << binExpo(a, m - 2, m) << std::endl; - } else { - std::cout << "m must be a prime number."; - std::cout << std::endl; - } + test(); // run self-test implementation + return 0; } diff --git a/math/modular_inverse_simple.cpp b/math/modular_inverse_simple.cpp index 813f0e0b4a5..f8a36d4eae9 100644 --- a/math/modular_inverse_simple.cpp +++ b/math/modular_inverse_simple.cpp @@ -8,6 +8,7 @@ */ #include /// for assert +#include #include /// for IO operations /** diff --git a/math/n_bonacci.cpp b/math/n_bonacci.cpp index c34dab7a19d..bb2e85d13e4 100644 --- a/math/n_bonacci.cpp +++ b/math/n_bonacci.cpp @@ -16,9 +16,9 @@ */ #include /// for assert +#include #include /// for std::cout #include /// for std::vector - /** * @namespace math * @brief Mathematical algorithms diff --git a/math/n_choose_r.cpp b/math/n_choose_r.cpp index 8bb3bbcdc2d..5e151f39a84 100644 --- a/math/n_choose_r.cpp +++ b/math/n_choose_r.cpp @@ -11,8 +11,8 @@ */ #include /// for assert +#include #include /// for io operations - /** * @namespace math * @brief Mathematical algorithms diff --git a/math/sieve_of_eratosthenes.cpp b/math/sieve_of_eratosthenes.cpp index e011b6c0018..29115d306d5 100644 --- a/math/sieve_of_eratosthenes.cpp +++ b/math/sieve_of_eratosthenes.cpp @@ -1,6 +1,7 @@ /** * @file - * @brief Get list of prime numbers using Sieve of Eratosthenes + * @brief Prime Numbers using [Sieve of + * Eratosthenes](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes) * @details * Sieve of Eratosthenes is an algorithm that finds all the primes * between 2 and N. @@ -11,21 +12,40 @@ * @see primes_up_to_billion.cpp prime_numbers.cpp */ -#include -#include -#include +#include +#include /// for assert +#include /// for IO operations +#include /// for std::vector /** - * This is the function that finds the primes and eliminates the multiples. + * @namespace math + * @brief Mathematical algorithms + */ +namespace math { +/** + * @namespace sieve_of_eratosthenes + * @brief Functions for finding Prime Numbers using Sieve of Eratosthenes + */ +namespace sieve_of_eratosthenes { +/** + * @brief Function to sieve out the primes + * @details + * This function finds all the primes between 2 and N using the Sieve of + * Eratosthenes algorithm. It starts by assuming all numbers (except zero and + * one) are prime and then iteratively marks the multiples of each prime as + * non-prime. + * * Contains a common optimization to start eliminating multiples of * a prime p starting from p * p since all of the lower multiples * have been already eliminated. - * @param N number of primes to check - * @return is_prime a vector of `N + 1` booleans identifying if `i`^th number is a prime or not + * @param N number till which primes are to be found + * @return is_prime a vector of `N + 1` booleans identifying if `i`^th number is + * a prime or not */ std::vector sieve(uint32_t N) { - std::vector is_prime(N + 1, true); - is_prime[0] = is_prime[1] = false; + std::vector is_prime(N + 1, true); // Initialize all as prime numbers + is_prime[0] = is_prime[1] = false; // 0 and 1 are not prime numbers + for (uint32_t i = 2; i * i <= N; i++) { if (is_prime[i]) { for (uint32_t j = i * i; j <= N; j += i) { @@ -37,9 +57,10 @@ std::vector sieve(uint32_t N) { } /** - * This function prints out the primes to STDOUT - * @param N number of primes to check - * @param is_prime a vector of `N + 1` booleans identifying if `i`^th number is a prime or not + * @brief Function to print the prime numbers + * @param N number till which primes are to be found + * @param is_prime a vector of `N + 1` booleans identifying if `i`^th number is + * a prime or not */ void print(uint32_t N, const std::vector &is_prime) { for (uint32_t i = 2; i <= N; i++) { @@ -50,23 +71,52 @@ void print(uint32_t N, const std::vector &is_prime) { std::cout << std::endl; } +} // namespace sieve_of_eratosthenes +} // namespace math + /** - * Test implementations + * @brief Self-test implementations + * @return void */ -void tests() { - // 0 1 2 3 4 5 6 7 8 9 10 - std::vector ans{false, false, true, true, false, true, false, true, false, false, false}; - assert(sieve(10) == ans); +static void tests() { + std::vector is_prime_1 = + math::sieve_of_eratosthenes::sieve(static_cast(10)); + std::vector is_prime_2 = + math::sieve_of_eratosthenes::sieve(static_cast(20)); + std::vector is_prime_3 = + math::sieve_of_eratosthenes::sieve(static_cast(100)); + + std::vector expected_1{false, false, true, true, false, true, + false, true, false, false, false}; + assert(is_prime_1 == expected_1); + + std::vector expected_2{false, false, true, true, false, true, + false, true, false, false, false, true, + false, true, false, false, false, true, + false, true, false}; + assert(is_prime_2 == expected_2); + + std::vector expected_3{ + false, false, true, true, false, true, false, true, false, false, + false, true, false, true, false, false, false, true, false, true, + false, false, false, true, false, false, false, false, false, true, + false, true, false, false, false, false, false, true, false, false, + false, true, false, true, false, false, false, true, false, false, + false, false, false, true, false, false, false, false, false, true, + false, true, false, false, false, false, false, true, false, false, + false, true, false, true, false, false, false, false, false, true, + false, false, false, true, false, false, false, false, false, true, + false, false, false, false, false, false, false, true, false, false, + false}; + assert(is_prime_3 == expected_3); + } /** - * Main function + * @brief Main function + * @returns 0 on exit */ int main() { tests(); - - uint32_t N = 100; - std::vector is_prime = sieve(N); - print(N, is_prime); return 0; } diff --git a/math/string_fibonacci.cpp b/math/string_fibonacci.cpp index eb9b6d7e1a7..1aef80f7a3c 100644 --- a/math/string_fibonacci.cpp +++ b/math/string_fibonacci.cpp @@ -8,6 +8,7 @@ * @see fibonacci_large.cpp, fibonacci_fast.cpp, fibonacci.cpp */ +#include #include #ifdef _MSC_VER #include // use this for MS Visual C diff --git a/math/sum_of_binomial_coefficient.cpp b/math/sum_of_binomial_coefficient.cpp index 1942df527dd..c5b1fbeb681 100644 --- a/math/sum_of_binomial_coefficient.cpp +++ b/math/sum_of_binomial_coefficient.cpp @@ -10,6 +10,7 @@ * @author [muskan0719](https://github.com/muskan0719) */ #include /// for assert +#include #include /// for std::cin and std::cout /** diff --git a/numerical_methods/brent_method_extrema.cpp b/numerical_methods/brent_method_extrema.cpp index 654a694514a..ea3c783bc53 100644 --- a/numerical_methods/brent_method_extrema.cpp +++ b/numerical_methods/brent_method_extrema.cpp @@ -16,6 +16,7 @@ #define _USE_MATH_DEFINES ///< required for MS Visual C++ #include #include +#include #include #include #include diff --git a/numerical_methods/durand_kerner_roots.cpp b/numerical_methods/durand_kerner_roots.cpp index 9bf0619b829..a5afc6a0046 100644 --- a/numerical_methods/durand_kerner_roots.cpp +++ b/numerical_methods/durand_kerner_roots.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include diff --git a/numerical_methods/fast_fourier_transform.cpp b/numerical_methods/fast_fourier_transform.cpp index 23a6c8a1f54..392789ea710 100644 --- a/numerical_methods/fast_fourier_transform.cpp +++ b/numerical_methods/fast_fourier_transform.cpp @@ -22,6 +22,7 @@ #include /// for assert #include /// for mathematical-related functions #include /// for storing points and coefficents +#include #include /// for IO operations #include /// for std::vector diff --git a/numerical_methods/golden_search_extrema.cpp b/numerical_methods/golden_search_extrema.cpp index 1ca47949fee..3f72db06f56 100644 --- a/numerical_methods/golden_search_extrema.cpp +++ b/numerical_methods/golden_search_extrema.cpp @@ -10,6 +10,7 @@ #define _USE_MATH_DEFINES //< required for MS Visual C++ #include #include +#include #include #include #include diff --git a/numerical_methods/inverse_fast_fourier_transform.cpp b/numerical_methods/inverse_fast_fourier_transform.cpp index 3837c21f966..928a20da802 100644 --- a/numerical_methods/inverse_fast_fourier_transform.cpp +++ b/numerical_methods/inverse_fast_fourier_transform.cpp @@ -14,6 +14,7 @@ #include /// for assert #include /// for mathematical-related functions #include /// for storing points and coefficents +#include #include /// for IO operations #include /// for std::vector diff --git a/numerical_methods/newton_raphson_method.cpp b/numerical_methods/newton_raphson_method.cpp index 17147e0bedd..1f5c9849f74 100644 --- a/numerical_methods/newton_raphson_method.cpp +++ b/numerical_methods/newton_raphson_method.cpp @@ -13,6 +13,7 @@ * \see bisection_method.cpp, false_position.cpp */ #include +#include #include #include #include diff --git a/numerical_methods/rungekutta.cpp b/numerical_methods/rungekutta.cpp index 01ca3a0745a..8522bd0ebc2 100644 --- a/numerical_methods/rungekutta.cpp +++ b/numerical_methods/rungekutta.cpp @@ -19,9 +19,9 @@ * There can be many such equations */ #include /// asserting the test functions +#include #include /// for io operations #include /// for using the vector container - /** * @brief The change() function is used * to return the updated iterative value corresponding diff --git a/others/easter.cpp b/others/easter.cpp index 36ac48028fc..6ae357deba7 100644 --- a/others/easter.cpp +++ b/others/easter.cpp @@ -18,6 +18,7 @@ */ #include /// for assert +#include #include /// for IO operations /* diff --git a/others/kadanes3.cpp b/others/kadanes3.cpp index 9cc6604e9c5..3d7ef442cfd 100644 --- a/others/kadanes3.cpp +++ b/others/kadanes3.cpp @@ -17,8 +17,8 @@ #include /// for std::array #include /// for assert #include /// for INT_MIN value +#include #include /// for IO operations - /** * @brief Utility function to check the current maximum number * \param arr input array diff --git a/others/lfu_cache.cpp b/others/lfu_cache.cpp new file mode 100644 index 00000000000..d893a96599d --- /dev/null +++ b/others/lfu_cache.cpp @@ -0,0 +1,304 @@ +/** + * @file + * @brief Implementation for [LFU Cache] + * (https://en.wikipedia.org/wiki/Least_frequently_used) + * + * @details + * LFU discards the least frequently used value. if there are multiple items + * with the same minimum frequency then, the least recently used among them is + * discarded. Data structures used - doubly linked list and unordered_map(hash + * map). + * + * Hashmap maps the key to the address of the node of the linked list and its + * current usage frequency. If the element is accessed the element is removed + * from the linked list of the current frequency and added to the linked list of + * incremented frequency. + * + * When the cache is full, the last element in the minimum frequency linked list + * is popped. + * + * @author [Karan Sharma](https://github.com/deDSeC00720) + */ + +#include // for assert +#include // for std::cout +#include // for std::unordered_map + +/** + * @namespace + * @brief Other algorithms + */ +namespace others { + +/** + * @namespace + * @brief Cache algorithm + */ +namespace Cache { + +/** + * @class + * @brief Node for a doubly linked list with data, prev and next pointers + * @tparam T type of the data of the node + */ +template +class D_Node { + public: + T data; ///< data of the node + D_Node *prev; ///< previous node in the doubly linked list + D_Node *next; ///< next node in the doubly linked list + + explicit D_Node(T data) : data(data), prev(nullptr), next(nullptr) {} +}; + +template +using CacheNode = D_Node>; + +/** + * @class + * @brief LFUCache + * @tparam K type of key in the LFU + * @tparam V type of value in the LFU + */ +template +class LFUCache { + std::unordered_map *, int>> + node_map; ///< maps the key to the node address and frequency + std::unordered_map *, CacheNode *>> + freq_map; ///< maps the frequency to doubly linked list + + int minFreq; ///< minimum frequency in the cache + int _capacity; ///< maximum capacity of the cache + + public: + /** + * @brief Constructor, Initialize with minFreq and _capacity. + * @param _capacity Total capacity of the cache. + */ + explicit LFUCache(int _capacity) : minFreq(0), _capacity(_capacity) {} + + private: + /** + * @brief push the node at first position in the linked list of given + * frequency + * @param freq the frequency mapping to the linked list where node should be + * pushed. + * @param node node to be pushed to the linked list. + */ + void push(int freq, CacheNode *node) { + // if freq is not present, then make a new list with node as the head as + // well as tail. + if (!freq_map.count(freq)) { + freq_map[freq] = {node, node}; + return; + } + + std::pair *, CacheNode *> &p = freq_map[freq]; + + // insert the node at the beginning of the linked list and update the + // head. + p.first->prev = node; + node->next = p.first; + p.first = node; + } + + /** + * @brief increase the frequency of node and push it in the respective list. + * @param p_node the node to be updated + */ + void increase_frequency(std::pair *, int> &p_node) { + CacheNode *node = p_node.first; + int freq = p_node.second; + + std::pair *, CacheNode *> &p = freq_map[freq]; + + // if the given node is the only node in the list, + // then erase the frequency from map + // and increase minFreq by 1. + if (p.first == node && p.second == node) { + freq_map.erase(freq); + if (minFreq == freq) { + minFreq = freq + 1; + } + } else { + // remove the given node from current freq linked list + CacheNode *prev = node->prev; + CacheNode *next = node->next; + node->prev = nullptr; + node->next = nullptr; + + if (prev) { + prev->next = next; + } else { + p.first = next; + } + + if (next) { + next->prev = prev; + } else { + p.second = prev; + } + } + push(freq + 1, node); + ++p_node.second; + } + + /** + * @brief pop the last node in the least frequently used linked list + */ + void pop() { + std::pair *, CacheNode *> &p = freq_map[minFreq]; + + // if there is only one node + // remove the node and erase + // the frequency from freq_map + if (p.first == p.second) { + delete p.first; + freq_map.erase(minFreq); + return; + } + + // remove the last node in the linked list + CacheNode *temp = p.second; + p.second = temp->prev; + p.second->next = nullptr; + delete temp; + } + + public: + /** + * @brief upsert a key-value pair + * @param key key of the key-value pair + * @param value value of the key-value pair + */ + void put(K key, V value) { + // update the value if key already exists + if (node_map.count(key)) { + node_map[key].first->data.second = value; + increase_frequency(node_map[key]); + return; + } + + // if the cache is full + // remove the least frequently used item + if (node_map.size() == _capacity) { + node_map.erase(freq_map[minFreq].second->data.first); + pop(); + } + + // insert the new node and set minFreq to 1 + CacheNode *node = new CacheNode({key, value}); + node_map[key] = {node, 1}; + minFreq = 1; + push(1, node); + } + + /** + * @brief get the value of the key-value pair if exists + * @param key key of the key-value pair + * @return the value mapped to the given key + * @exception exception is thrown if the key is not present in the cache + */ + V get(K key) { + if (!node_map.count(key)) { + throw std::runtime_error("key is not present in the cache"); + } + + // increase the frequency and return the value + V value = node_map[key].first->data.second; + increase_frequency(node_map[key]); + return value; + } + + /** + * @brief Returns the number of items present in the cache. + * @return number of items in the cache + */ + int size() const { return node_map.size(); } + + /** + * @brief Returns the total capacity of the cache + * @return Total capacity of the cache + */ + int capacity() const { return _capacity; } + + /** + * @brief returns true if the cache is empty, false otherwise. + * @return true if the cache is empty, false otherwise. + */ + bool empty() const { return node_map.empty(); } + + /** + * @brief destructs the cache, iterates on the map and deletes every node + * present in the cache. + */ + ~LFUCache() { + auto it = node_map.begin(); + while (it != node_map.end()) { + delete it->second.first; + ++it; + } + } +}; +} // namespace Cache +} // namespace others + +/** + * @brief self test implementation + * @return void + */ +static void test() { + others::Cache::LFUCache cache(5); + + // test the initial state of the cache + assert(cache.size() == 0); + assert(cache.capacity() == 5); + assert(cache.empty()); + + // test insertion in the cache + cache.put(1, 10); + cache.put(-2, 20); + + // test the state of cache after inserting some items + assert(cache.size() == 2); + assert(cache.capacity() == 5); + assert(!cache.empty()); + + // test getting items from the cache + assert(cache.get(1) == 10); + assert(cache.get(-2) == 20); + + cache.put(-3, -30); + cache.put(4, 40); + cache.put(5, -50); + cache.put(6, 60); + + // test the state after inserting more items than the capacity + assert(cache.size() == 5); + assert(cache.capacity() == 5); + assert(!cache.empty()); + + // test retrieval of all items in the cache + assert(cache.get(1) == 10); + assert(cache.get(-2) == 20); + + // fetching -3 throws runtime_error + // as -3 was evicted being the least frequently used + // when 6 was added + // assert(cache.get(-3) == -30); + + assert(cache.get(4) == 40); + assert(cache.get(5) == -50); + assert(cache.get(6) == 60); + + std::cout << "test - passed\n"; +} + +/** + * @brief main function + * @return 0 on exit + */ +int main() { + test(); // run the self test implementation + return 0; +} diff --git a/others/longest_substring_without_repeating_characters.cpp b/others/longest_substring_without_repeating_characters.cpp new file mode 100644 index 00000000000..3664ad71e7e --- /dev/null +++ b/others/longest_substring_without_repeating_characters.cpp @@ -0,0 +1,110 @@ +/** + * @file + * @brief Solution for Longest Substring Without Repeating Characters problem. + * @details + * Problem link: https://leetcode.com/problems/longest-substring-without-repeating-characters/description/ + * + * Intuition: + * 1) The intuition is straightforward and simple. We track the frequency of characters. + * 2) Since we can't use a string to track the longest substring without repeating characters efficiently (as removing a character from the front of a string isn't O(1)), we optimize the solution using a deque approach. + * + * Approach: + * 1) Initialize an unordered_map to track the frequency of characters. + * 2) Use a deque for pushing characters, and update the result deque (`res`) with the current deque (`temp`) + * whenever we find a longer substring. + * 3) Use a while loop to reduce the frequency from the front, incrementing `i`, + * and removing characters from the `temp` deque as we no longer need them. + * 4) Return `res.size()` as we are interested in the length of the longest substring. + * + * Time Complexity: O(N) + * Space Complexity: O(N) + * + * I hope this helps to understand. + * Thank you! + * @author [Ashish Kumar Sahoo](github.com/ashish5kmax) + **/ + +#include // for IO Operations +#include // for std::unordered_map +#include // for std::deque +#include // for string class/string datatype which is taken as input +#include // for assert + +/** + * @class Longest_Substring + * @brief Class that solves the Longest Substring Without Repeating Characters problem. + */ +class Longest_Substring { +public: + /** + * @brief Function to find the length of the longest substring without repeating characters. + * @param s Input string. + * @return Length of the longest substring. + */ + int lengthOfLongestSubstring(std::string s) { + // If the size of string is 1, then it will be the answer. + if (s.size() == 1) return 1; + + // Map used to store the character frequency. + std::unordered_map m; + int n = s.length(); + + // Deque to remove from back if repeating characters are present. + std::deque temp; + std::deque res; + int i, j; + + // Sliding window approach using two pointers. + for (i = 0, j = 0; i < n && j < n;) { + m[s[j]]++; + + // If repeating character found, update result and remove from the front. + if (m[s[j]] > 1) { + if (temp.size() > res.size()) { + res = temp; + } + + while (m[s[j]] > 1) { + temp.pop_front(); + m[s[i]]--; + i++; + } + } + + // Add the current character to the deque. + temp.push_back(s[j]); + j++; + } + + // Final check to update result. + if (temp.size() > res.size()) { + res = temp; + } + + return res.size(); // Return the length of the longest substring. + } +}; + +/** + * @brief Self-test implementations + * @returns void + */ +static void tests() { + Longest_Substring soln; + assert(soln.lengthOfLongestSubstring("abcabcbb") == 3); + assert(soln.lengthOfLongestSubstring("bbbbb") == 1); + assert(soln.lengthOfLongestSubstring("pwwkew") == 3); + assert(soln.lengthOfLongestSubstring("") == 0); // Test case for empty string + assert(soln.lengthOfLongestSubstring("abcdef") == 6); // Test case for all unique characters + assert(soln.lengthOfLongestSubstring("a") == 1); // Single character + std::cout << "All test cases passed!" << std::endl; +} + +/** + * @brief Main function. + * @return 0 on successful execution. + */ +int main() { + tests(); // run self-test implementations + return 0; +} diff --git a/others/lru_cache.cpp b/others/lru_cache.cpp index f9cd3caec7d..29f8bd0ed16 100644 --- a/others/lru_cache.cpp +++ b/others/lru_cache.cpp @@ -46,6 +46,7 @@ * */ #include /// for assert +#include #include /// for IO Operations #include /// for std::list #include /// for std::unordered_map diff --git a/others/lru_cache2.cpp b/others/lru_cache2.cpp new file mode 100644 index 00000000000..aaa6c943fbf --- /dev/null +++ b/others/lru_cache2.cpp @@ -0,0 +1,277 @@ +/** + * @file + * @brief Implementation for [LRU Cache] + * (https://en.wikipedia.org/wiki/Cache_replacement_policies#:~:text=Least%20Recently%20Used%20(LRU)) + * + * @details + * LRU discards the least recently used value. + * Data structures used - doubly linked list and unordered_map + * + * unordered_map maps the key to the address of the node of the linked list. + * If the element is accessed, the element is moved to the beginning of the + * linked list. + * + * When the cache is full, the last element in the linked list is popped. + * + * @author [Karan Sharma](https://github.com/deDSeC00720) + */ + +#include // for assert +#include // for std::uint32_t +#include // for std::cout +#include // for std::unordered_map + +/** + * @namespace + * @brief Other algorithms + */ +namespace others { + +/** + * @namespace + * @brief Cache algorithm + */ +namespace Cache { + +/** + * @class + * @brief Node for a doubly linked list with data, prev and next pointers + * @tparam T type of the data of the node + */ +template +class D_Node { + public: + T data; ///< data of the node + D_Node *prev; ///< previous node in the doubly linked list + D_Node *next; ///< next node in the doubly linked list + + explicit D_Node(T data) : data(data), prev(nullptr), next(nullptr) {} +}; + +template +using CacheNode = D_Node>; + +/** + * @class + * @brief LRUCache + * @tparam K type of key in the LRU + * @tparam V type of value in the LRU + */ +template +class LRUCache { + CacheNode *head; ///< head of the doubly linked list + CacheNode *tail; ///< tail of the doubly linked list + std::uint32_t _capacity; ///< maximum capacity of the cache + + std::unordered_map *> + node_map; ///< maps the key to the node address + + public: + /** + * @brief Constructor, Initialize the head and tail pointers to nullptr and + * initialize the _capacity of the cache + * @param _capacity Total capacity of the cache + */ + explicit LRUCache(int _capacity) + : head(nullptr), tail(nullptr), _capacity(_capacity) {} + + private: + /** + * @brief push the node to the front of the linked list. + * @param node_ptr the node to be pushed + */ + void push_front(CacheNode *node_ptr) { + if (!head) { + head = node_ptr; + tail = node_ptr; + return; + } + + node_ptr->next = head; + head->prev = node_ptr; + head = node_ptr; + } + + /** + * @brief move the existing node in the list to the beginning of the list. + * @param node_ptr node to be moved to the beginning. + */ + void make_recent(CacheNode *node_ptr) { + if (head == node_ptr) { + return; + } + + CacheNode *prev = node_ptr->prev; + CacheNode *next = node_ptr->next; + + prev->next = next; + if (next) { + next->prev = prev; + } else { + tail = prev; + } + + node_ptr->prev = nullptr; + node_ptr->next = nullptr; + push_front(node_ptr); + } + + /** + * @brief pop the last node in the linked list. + */ + void pop_back() { + if (!head) { + return; + } + if (head == tail) { + delete head; + head = nullptr; + tail = nullptr; + return; + } + + CacheNode *temp = tail; + tail = tail->prev; + tail->next = nullptr; + delete temp; + } + + public: + /** + * @brief upsert a key-value pair + * @param key key of the key-value pair + * @param value value of the key-value pair + */ + void put(K key, V value) { + // update the value if key already exists + if (node_map.count(key)) { + node_map[key]->data.second = value; + make_recent(node_map[key]); + return; + } + + // if the cache is full + // remove the least recently used item + if (node_map.size() == _capacity) { + node_map.erase(tail->data.first); + pop_back(); + } + + CacheNode *newNode = new CacheNode({key, value}); + + node_map[key] = newNode; + push_front(newNode); + } + + /** + * @brief get the value of the key-value pair if exists + * @param key key of the key-value pair + * @return the value mapped to the given key + * @exception exception is thrown if the key is not present in the cache + */ + V get(K key) { + if (!node_map.count(key)) { + throw std::runtime_error("key is not present in the cache"); + } + + // move node to the beginning of the list + V value = node_map[key]->data.second; + make_recent(node_map[key]); + return value; + } + + /** + * @brief Returns the number of items present in the cache. + * @return number of items in the cache + */ + int size() const { return node_map.size(); } + + /** + * @brief Returns the total capacity of the cache + * @return Total capacity of the cache + */ + int capacity() const { return _capacity; } + + /** + * @brief returns whether the cache is empty or not + * @return true if the cache is empty, false otherwise. + */ + bool empty() const { return node_map.empty(); } + + /** + * @brief destructs the cache, iterates on the map and deletes every node + * present in the cache. + */ + ~LRUCache() { + auto it = node_map.begin(); + while (it != node_map.end()) { + delete it->second; + ++it; + } + } +}; +} // namespace Cache +} // namespace others + +/** + * @brief self test implementations + * @return void + */ +static void test() { + others::Cache::LRUCache cache(5); + + // test the initial state of the cache + assert(cache.size() == 0); + assert(cache.capacity() == 5); + assert(cache.empty()); + + // test insertion in the cache + cache.put(1, 10); + cache.put(-2, 20); + + // test the state of cache after inserting some items + assert(cache.size() == 2); + assert(cache.capacity() == 5); + assert(!cache.empty()); + + // test getting items from the cache + assert(cache.get(1) == 10); + assert(cache.get(-2) == 20); + + cache.put(-3, -30); + cache.put(4, 40); + cache.put(5, -50); + cache.put(6, 60); + + // test the state after inserting more items than the capacity + assert(cache.size() == 5); + assert(cache.capacity() == 5); + assert(!cache.empty()); + + // fetching 1 throws runtime_error + // as 1 was evicted being the least recently used + // when 6 was added + try { + cache.get(1); + } catch (const std::runtime_error &e) { + assert(std::string(e.what()) == "key is not present in the cache"); + } + + // test retrieval of all items in the cache + assert(cache.get(-2) == 20); + assert(cache.get(-3) == -30); + assert(cache.get(4) == 40); + assert(cache.get(5) == -50); + assert(cache.get(6) == 60); + + std::cout << "test - passed\n"; +} + +/** + * @brief main function + * @return 0 on exit + */ +int main() { + test(); // run the self test implementation + return 0; +} diff --git a/probability/exponential_dist.cpp b/probability/exponential_dist.cpp new file mode 100644 index 00000000000..ef6d396d58c --- /dev/null +++ b/probability/exponential_dist.cpp @@ -0,0 +1,151 @@ +/** + * @file + * @brief [Exponential + * Distribution](https://en.wikipedia.org/wiki/Exponential_distribution) + * + * The exponential distribution is used to model + * events occuring between a Poisson process like radioactive decay. + * + * \f[P(x, \lambda) = \lambda e^{-\lambda x}\f] + * + * Summary of variables used: + * \f$\lambda\f$ : rate parameter + */ + +#include // For assert +#include // For std::pow +#include // For I/O operation +#include // For std::invalid_argument +#include // For std::string + +/** + * @namespace probability + * @brief Probability algorithms + */ +namespace probability { +/** + * @namespace exponential_dist + * @brief Functions for the [Exponential + * Distribution](https://en.wikipedia.org/wiki/Exponential_distribution) + * algorithm implementation + */ +namespace geometric_dist { +/** + * @brief the expected value of the exponential distribution + * @returns \f[\mu = \frac{1}{\lambda}\f] + */ +double exponential_expected(double lambda) { + if (lambda <= 0) { + throw std::invalid_argument("lambda must be greater than 0"); + } + return 1 / lambda; +} + +/** + * @brief the variance of the exponential distribution + * @returns \f[\sigma^2 = \frac{1}{\lambda^2}\f] + */ +double exponential_var(double lambda) { + if (lambda <= 0) { + throw std::invalid_argument("lambda must be greater than 0"); + } + return 1 / pow(lambda, 2); +} + +/** + * @brief the standard deviation of the exponential distribution + * @returns \f[\sigma = \frac{1}{\lambda}\f] + */ +double exponential_std(double lambda) { + if (lambda <= 0) { + throw std::invalid_argument("lambda must be greater than 0"); + } + return 1 / lambda; +} +} // namespace geometric_dist +} // namespace probability + +/** + * @brief Self-test implementations + * @returns void + */ +static void test() { + double lambda_1 = 1; + double expected_1 = 1; + double var_1 = 1; + double std_1 = 1; + + double lambda_2 = 2; + double expected_2 = 0.5; + double var_2 = 0.25; + double std_2 = 0.5; + + double lambda_3 = 3; + double expected_3 = 0.333333; + double var_3 = 0.111111; + double std_3 = 0.333333; + + double lambda_4 = 0; // Test 0 + double lambda_5 = -2.3; // Test negative value + + const float threshold = 1e-3f; + + std::cout << "Test for lambda = 1 \n"; + assert( + std::abs(expected_1 - probability::geometric_dist::exponential_expected( + lambda_1)) < threshold); + assert(std::abs(var_1 - probability::geometric_dist::exponential_var( + lambda_1)) < threshold); + assert(std::abs(std_1 - probability::geometric_dist::exponential_std( + lambda_1)) < threshold); + std::cout << "ALL TEST PASSED\n\n"; + + std::cout << "Test for lambda = 2 \n"; + assert( + std::abs(expected_2 - probability::geometric_dist::exponential_expected( + lambda_2)) < threshold); + assert(std::abs(var_2 - probability::geometric_dist::exponential_var( + lambda_2)) < threshold); + assert(std::abs(std_2 - probability::geometric_dist::exponential_std( + lambda_2)) < threshold); + std::cout << "ALL TEST PASSED\n\n"; + + std::cout << "Test for lambda = 3 \n"; + assert( + std::abs(expected_3 - probability::geometric_dist::exponential_expected( + lambda_3)) < threshold); + assert(std::abs(var_3 - probability::geometric_dist::exponential_var( + lambda_3)) < threshold); + assert(std::abs(std_3 - probability::geometric_dist::exponential_std( + lambda_3)) < threshold); + std::cout << "ALL TEST PASSED\n\n"; + + std::cout << "Test for lambda = 0 \n"; + try { + probability::geometric_dist::exponential_expected(lambda_4); + probability::geometric_dist::exponential_var(lambda_4); + probability::geometric_dist::exponential_std(lambda_4); + } catch (std::invalid_argument& err) { + assert(std::string(err.what()) == "lambda must be greater than 0"); + } + std::cout << "ALL TEST PASSED\n\n"; + + std::cout << "Test for lambda = -2.3 \n"; + try { + probability::geometric_dist::exponential_expected(lambda_5); + probability::geometric_dist::exponential_var(lambda_5); + probability::geometric_dist::exponential_std(lambda_5); + } catch (std::invalid_argument& err) { + assert(std::string(err.what()) == "lambda must be greater than 0"); + } + std::cout << "ALL TEST PASSED\n\n"; +} + +/** + * @brief Main function + * @return 0 on exit + */ +int main() { + test(); // Self test implementation + return 0; +} diff --git a/search/binary_search.cpp b/search/binary_search.cpp index bed938b290c..ebe488e5658 100644 --- a/search/binary_search.cpp +++ b/search/binary_search.cpp @@ -37,6 +37,7 @@ #include /// for std::sort function #include /// for std::assert +#include #include /// for IO operations #include /// for std::vector /****************************************************************************** diff --git a/search/exponential_search.cpp b/search/exponential_search.cpp index f57cbf96b51..6443084dc25 100644 --- a/search/exponential_search.cpp +++ b/search/exponential_search.cpp @@ -14,7 +14,7 @@ */ #include #include -#include +#include #ifdef _MSC_VER #include // use for MS Visual C++ #else diff --git a/search/floyd_cycle_detection_algo.cpp b/search/floyd_cycle_detection_algo.cpp index c7dd95aea8c..58c994dccf3 100644 --- a/search/floyd_cycle_detection_algo.cpp +++ b/search/floyd_cycle_detection_algo.cpp @@ -12,9 +12,9 @@ */ #include /// for assert +#include #include /// for IO operations #include /// for std::vector - /** * @namespace search * @brief Search algorithms diff --git a/search/interpolation_search.cpp b/search/interpolation_search.cpp index 5ed75774959..234d90ff698 100644 --- a/search/interpolation_search.cpp +++ b/search/interpolation_search.cpp @@ -31,6 +31,7 @@ #include /// for std::sort function #include /// for std::assert +#include #include /// for IO operations #include /// for std::vector diff --git a/search/longest_increasing_subsequence_using_binary_search.cpp b/search/longest_increasing_subsequence_using_binary_search.cpp new file mode 100644 index 00000000000..2f83de72ecf --- /dev/null +++ b/search/longest_increasing_subsequence_using_binary_search.cpp @@ -0,0 +1,117 @@ +/** + * @file + * @brief find the length of the Longest Increasing Subsequence (LIS) + * using [Binary Search](https://en.wikipedia.org/wiki/Longest_increasing_subsequence) + * @details + * Given an integer array nums, return the length of the longest strictly + * increasing subsequence. + * The longest increasing subsequence is described as a subsequence of an array + * where: All elements of the subsequence are in increasing order. This subsequence + * itself is of the longest length possible. + + * For solving this problem we have Three Approaches :- + + * Approach 1 :- Using Brute Force + * The first approach that came to your mind is the Brute Force approach where we + * generate all subsequences and then manually filter the subsequences whose + * elements come in increasing order and then return the longest such subsequence. + * Time Complexity :- O(2^n) + * It's time complexity is exponential. Therefore we will try some other + * approaches. + + * Approach 2 :- Using Dynamic Programming + * To generate all subsequences we will use recursion and in the recursive logic we + * will figure out a way to solve this problem. Recursive Logic to solve this + * problem:- + * 1. We only consider the element in the subsequence if the element is grater then + * the last element present in the subsequence + * 2. When we consider the element we will increase the length of subsequence by 1 + * Time Complexity: O(N*N) + * Space Complexity: O(N*N) + O(N) + + * This approach is better then the previous Brute Force approach so, we can + * consider this approach. + + * But when the Constraints for the problem is very larger then this approach fails + + * Approach 3 :- Using Binary Search + * Other approaches use additional space to create a new subsequence Array. + * Instead, this solution uses the existing nums Array to build the subsequence + * array. We can do this because the length of the subsequence array will never be + * longer than the current index. + + * Time complexity: O(n∗log(n)) + * Space complexity: O(1) + + * This approach consider Most optimal Approach for solving this problem + + * @author [Naman Jain](https://github.com/namanmodi65) + */ + +#include /// for std::assert +#include /// for IO operations +#include /// for std::vector +#include /// for std::lower_bound +#include /// for std::uint32_t + +/** + * @brief Function to find the length of the Longest Increasing Subsequence (LIS) + * using Binary Search + * @tparam T The type of the elements in the input vector + * @param nums The input vector of elements of type T + * @return The length of the longest increasing subsequence + */ +template +std::uint32_t longest_increasing_subsequence_using_binary_search(std::vector& nums) { + if (nums.empty()) return 0; + + std::vector ans; + ans.push_back(nums[0]); + for (std::size_t i = 1; i < nums.size(); i++) { + if (nums[i] > ans.back()) { + ans.push_back(nums[i]); + } else { + auto idx = std::lower_bound(ans.begin(), ans.end(), nums[i]) - ans.begin(); + ans[idx] = nums[i]; + } + } + return static_cast(ans.size()); +} + +/** + * @brief Test cases for Longest Increasing Subsequence function + * @returns void + */ +static void tests() { + std::vector arr = {10, 9, 2, 5, 3, 7, 101, 18}; + assert(longest_increasing_subsequence_using_binary_search(arr) == 4); + + std::vector arr2 = {0, 1, 0, 3, 2, 3}; + assert(longest_increasing_subsequence_using_binary_search(arr2) == 4); + + std::vector arr3 = {7, 7, 7, 7, 7, 7, 7}; + assert(longest_increasing_subsequence_using_binary_search(arr3) == 1); + + std::vector arr4 = {-10, -1, -5, 0, 5, 1, 2}; + assert(longest_increasing_subsequence_using_binary_search(arr4) == 5); + + std::vector arr5 = {3.5, 1.2, 2.8, 3.1, 4.0}; + assert(longest_increasing_subsequence_using_binary_search(arr5) == 4); + + std::vector arr6 = {'a', 'b', 'c', 'a', 'd'}; + assert(longest_increasing_subsequence_using_binary_search(arr6) == 4); + + std::vector arr7 = {}; + assert(longest_increasing_subsequence_using_binary_search(arr7) == 0); + + std::cout << "All tests have successfully passed!\n"; +} + +/** + * @brief Main function to run tests + * @returns 0 on exit + */ +int main() { + tests(); // run self test implementation + return 0; +} diff --git a/search/saddleback_search.cpp b/search/saddleback_search.cpp index dab5adcf059..d75c65e3ea6 100644 --- a/search/saddleback_search.cpp +++ b/search/saddleback_search.cpp @@ -1,28 +1,34 @@ /** * @file - * @brief Implementation of [Saddleback Algorithm](https://www.geeksforgeeks.org/saddleback-search-algorithm-in-a-2d-array) for 2D arrays. + * @brief Implementation of [Saddleback + * Algorithm](https://www.geeksforgeeks.org/saddleback-search-algorithm-in-a-2d-array) + * for 2D arrays. * * @details * Saddleback Algorithm is an algorithm that searches 2D array in linear time, - * i.e, O(m + n), where m is number of rows and n is number of columns of 2D array. Also, each row and - * column of the matrix should be sorted beforehand for this algorithm to work. + * i.e, O(m + n), where m is number of rows and n is number of columns of 2D + * array. Also, each row and column of the matrix should be sorted beforehand + * for this algorithm to work. * * @author [Hashir Niazi](https://github.com/HashirGJ8842) */ -#include /// for assert -#include /// for io operations, and std::pair -#include /// for std::vector +#include /// for assert +#include +#include /// for io operations, and std::pair +#include /// for std::vector /** \namespace search * \brief Algorithms for searching */ namespace search { /** \namespace saddleback - * \brief Function for implementing [Saddleback Algorithm](https://www.geeksforgeeks.org/saddleback-search-algorithm-in-a-2d-array). + * \brief Function for implementing [Saddleback + * Algorithm](https://www.geeksforgeeks.org/saddleback-search-algorithm-in-a-2d-array). */ namespace saddleback { /** - * This function implements [Saddleback Algorithm](https://www.geeksforgeeks.org/saddleback-search-algorithm-in-a-2d-array), + * This function implements [Saddleback + * Algorithm](https://www.geeksforgeeks.org/saddleback-search-algorithm-in-a-2d-array), * on a sorted 2D array, and finds the location of the element needed to search * @param matrix 2D matrix which is sorted on the basis of rows and columns * @param element element to be searched @@ -30,16 +36,17 @@ namespace saddleback { * element is present. * @return An std::pair with (0, 0), if the element is not present. */ -std::pair saddleback(std::vector> matrix, - int32_t element) { +std::pair saddleback( + std::vector> matrix, int32_t element) { uint32_t left_index = 0; uint32_t right_index = matrix[0].size() - 1; // Start from top right corner - while (left_index < matrix.size()) { // Exit once the value of indexes get out of range. + while (left_index < + matrix.size()) { // Exit once the value of indexes get out of range. if (element == matrix[left_index] [right_index]) { // If value on this position of matrix is // equal to element, return (row, column). - return std::make_pair(left_index+1, right_index+1); + return std::make_pair(left_index + 1, right_index + 1); } else if (element > matrix[left_index] [right_index]) { // Else if value on this position of @@ -51,14 +58,15 @@ std::pair saddleback(std::vector> matri [right_index]) { // Else if value on this position of // matrix is greater than the // element, move down. - if(!right_index) + if (!right_index) break; - else --right_index; + else + --right_index; } } return std::make_pair( 0, 0); // If the program reaches here, that means one of the index - // went out of index, hence no element present. + // went out of index, hence no element present. } } // namespace saddleback } // namespace search @@ -69,15 +77,16 @@ std::pair saddleback(std::vector> matri */ static void test() { std::vector> matrix = {{1, 10, 100, 1000, 10000}, - {2, 20, 200, 2000, 20000}, - {3, 30, 300, 3000, 30000}, - {4, 40, 400, 4000, 40000}, - {5, 50, 500, 5000, 50000}}; + {2, 20, 200, 2000, 20000}, + {3, 30, 300, 3000, 30000}, + {4, 40, 400, 4000, 40000}, + {5, 50, 500, 5000, 50000}}; std::pair not_found = std::make_pair(0, 0); std::pair test_answer; // Test 1 - std::pair answer1 = search::saddleback::saddleback(matrix, 123); + std::pair answer1 = + search::saddleback::saddleback(matrix, 123); assert(not_found == answer1); // Test 2 answer1 = search::saddleback::saddleback(matrix, 0); @@ -101,6 +110,6 @@ static void test() { * @returns 0 on exit */ int main() { - test(); // execute the tests + test(); // execute the tests return 0; } diff --git a/search/sublist_search.cpp b/search/sublist_search.cpp index 0954173d20c..bb63cac0fa5 100644 --- a/search/sublist_search.cpp +++ b/search/sublist_search.cpp @@ -26,6 +26,7 @@ */ #include /// for assert +#include #include /// for IO operations #include /// for std::vector diff --git a/sorting/bubble_sort.cpp b/sorting/bubble_sort.cpp index c43e425fcd9..9209f8664e7 100644 --- a/sorting/bubble_sort.cpp +++ b/sorting/bubble_sort.cpp @@ -2,82 +2,133 @@ * @file * @brief Bubble sort algorithm * - * The working principle of the Bubble sort algorithm: + * @details + * Bubble sort algorithm is the bubble sorting algorithm. The most important reason + * for calling the bubble is that the largest number is thrown at the end of this + * algorithm. This is all about the logic. In each iteration, the largest number is + * expired and when iterations are completed, the sorting takes place. + * + * What is Swap? + * + * Swap in the software means that two variables are displaced. + * An additional variable is required for this operation. x = 5, y = 10. + * We want x = 10, y = 5. Here we create the most variable to do it. + * + * ```cpp + * int z; + * z = x; + * x = y; + * y = z; + * ``` + * + * The above process is a typical displacement process. + * When x assigns the value to x, the old value of x is lost. + * That's why we created a variable z to create the first value of the value of x, + * and finally, we have assigned to y. + * + * ## Bubble Sort Algorithm Analysis (Best Case - Worst Case - Average Case) + * + * ### Best Case + * Bubble Sort Best Case Performance. \f$O(n)\f$. However, you + * can't get the best status in the code we shared above. This happens on the + * optimized bubble sort algorithm. It's right down there. + * + * ### Worst Case + * Bubble Sort Worst Case Performance is \f$O(n^{2})\f$. Why is that? Because if you + * remember Big O Notation, we were calculating the complexity of the algorithms in + * the nested loops. The \f$n * (n - 1)\f$ product gives us \f$O(n^{2})\f$ performance. In the + * worst case all the steps of the cycle will occur. + * + * ### Average Case + * Bubble Sort is not an optimal algorithm. In average, \f$O(n^{2})\f$ performance is taken. + * + * @author [Deepak](https://github.com/Deepak-j-p) + * @author [Nguyen Phuc Chuong](https://github.com/hollowcrust) + */ -Bubble sort algorithm is the bubble sorting algorithm. The most important reason -for calling the bubble is that the largest number is thrown at the end of this -algorithm. This is all about the logic. In each iteration, the largest number is -expired and when iterations are completed, the sorting takes place. +#include /// for std::is_sorted +#include /// for assert +#include /// for IO implementations +#include /// for std::string +#include /// for std::pair, std::swap +#include /// for std::vector, std::vector::push_back, std::vector::size -What is Swap? +/** + * @namespace sorting + * @brief Sorting algorithms + */ +namespace sorting { +/** + * @namespace bubble_sort + * @brief Bubble sort algorithm + */ +namespace bubble_sort { +/** + * @brief Bubble sort algorithm + * @param array An array to be sorted + * @return The array sorted in ascending order + */ +template +std::vector bubble_sort(std::vector& array) { + // swap_check flag to terminate the function early + // if there is no swap occurs in one iteration. + bool swap_check = true; + int size = array.size(); + for (int i = 0; (i < size) && (swap_check); i++) { + swap_check = false; + for (int j = 0; j < size - 1 - i; j++) { + if (array[j] > array[j + 1]) { + swap_check = true; + std::swap(array[j], array[j + 1]); + } + } + } -Swap in the software means that two variables are displaced. -An additional variable is required for this operation. x = 5, y = 10. -We want x = 10, y = 5. Here we create the most variable to do it. + return array; +} +} // namespace bubble_sort +} // namespace sorting -int z; -z = x; -x = y; -y = z; +/** + * @brief Self-test implementation + * @return void + */ +static void test() { + std::vector vec_1 = {3, 1, -9, 0}; + std::vector sorted_1 = sorting::bubble_sort::bubble_sort(vec_1); -The above process is a typical displacement process. -When x assigns the value to x, the old value of x is lost. -That's why we created a variable z to create the first value of the value of x, -and finally, we have assigned to y. + std::vector vec_2 = {3}; + std::vector sorted_2 = sorting::bubble_sort::bubble_sort(vec_2); -Bubble Sort Algorithm Analysis (Best Case - Worst Case - Average Case) + std::vector vec_3 = {10, 10, 10, 10, 10}; + std::vector sorted_3 = sorting::bubble_sort::bubble_sort(vec_3); -Bubble Sort Worst Case Performance is O (n²). Why is that? Because if you -remember Big O Notation, we were calculating the complexity of the algorithms in -the nested loops. The n * (n - 1) product gives us O (n²) performance. In the -worst case all the steps of the cycle will occur. Bubble Sort (Avarage Case) -Performance. Bubble Sort is not an optimal algorithm. in average, O (n²) -performance is taken. Bubble Sort Best Case Performance. O (n). However, you -can't get the best status in the code we shared above. This happens on the -optimized bubble sort algorithm. It's right down there. -*/ + std::vector vec_4 = {1234, -273.1, 23, 150, 1234, 1555.55, -2000}; + std::vector sorted_4 = sorting::bubble_sort::bubble_sort(vec_4); -#include -#include + std::vector vec_5 = {'z', 'Z', 'a', 'B', ' ', 'c', 'a'}; + std::vector sorted_5 = sorting::bubble_sort::bubble_sort(vec_5); -int main() { - int n; - bool swap_check = true; - std::cout << "Enter the amount of numbers to sort: "; - std::cin >> n; - std::vector numbers; - std::cout << "Enter " << n << " numbers: "; - int num; + std::vector vec_6 = {"Hello", "hello", "Helo", "Hi", "hehe"}; + std::vector sorted_6 = sorting::bubble_sort::bubble_sort(vec_6); - // Input - for (int i = 0; i < n; i++) { - std::cin >> num; - numbers.push_back(num); - } + std::vector> vec_7 = {{10, 'c'}, {2, 'z'}, {10, 'a'}, {0, 'b'}, {-1, 'z'}}; + std::vector> sorted_7 = sorting::bubble_sort::bubble_sort(vec_7); - // Bubble Sorting - for (int i = 0; (i < n) && (swap_check); i++) { - swap_check = false; - for (int j = 0; j < n - 1 - i; j++) { - if (numbers[j] > numbers[j + 1]) { - swap_check = true; - std::swap(numbers[j], - numbers[j + 1]); // by changing swap location. - // I mean, j. If the number is - // greater than j + 1, then it - // means the location. - } - } - } + assert(std::is_sorted(sorted_1.begin(), sorted_1.end())); + assert(std::is_sorted(sorted_2.begin(), sorted_2.end())); + assert(std::is_sorted(sorted_3.begin(), sorted_3.end())); + assert(std::is_sorted(sorted_4.begin(), sorted_4.end())); + assert(std::is_sorted(sorted_5.begin(), sorted_5.end())); + assert(std::is_sorted(sorted_6.begin(), sorted_6.end())); + assert(std::is_sorted(sorted_7.begin(), sorted_7.end())); +} - // Output - std::cout << "\nSorted Array : "; - for (int i = 0; i < numbers.size(); i++) { - if (i != numbers.size() - 1) { - std::cout << numbers[i] << ", "; - } else { - std::cout << numbers[i] << std::endl; - } - } - return 0; +/** + * @brief Main function + * @return 0 on exit + */ +int main() { + test(); + return 0; } diff --git a/sorting/cycle_sort.cpp b/sorting/cycle_sort.cpp index 8acf462af78..88364bf12a0 100644 --- a/sorting/cycle_sort.cpp +++ b/sorting/cycle_sort.cpp @@ -13,6 +13,7 @@ #include /// for std::is_sorted, std::swap #include /// for assert +#include #include /// for io operations #include /// for std::vector diff --git a/sorting/dnf_sort.cpp b/sorting/dnf_sort.cpp index 8b7c2279e0e..4fe0129cc81 100644 --- a/sorting/dnf_sort.cpp +++ b/sorting/dnf_sort.cpp @@ -12,6 +12,7 @@ #include /// for std::is_sorted #include /// for assert +#include #include /// for std::swap and io operations #include /// for std::vector diff --git a/sorting/insertion_sort_recursive.cpp b/sorting/insertion_sort_recursive.cpp new file mode 100644 index 00000000000..256a27e1ce3 --- /dev/null +++ b/sorting/insertion_sort_recursive.cpp @@ -0,0 +1,152 @@ +/** + * @file + * @brief Insertion Sort Algorithm + * @author [Dhanush S](https://github.com/Fandroid745) + * + * @details + * Insertion sort is a simple sorting algorithm that builds the final + * sorted array one element at a time. It is much less efficient compared + * to other sorting algorithms like heap sort, merge sort, or quick sort. + * + * However, it has several advantages: + * - Easy to implement. + * - Efficient for small data sets. + * - More efficient than other O(n²) algorithms like selection sort or bubble sort. + * - Stable: it does not change the relative order of elements with equal keys. + * + * Insertion sort works similarly to how people sort playing cards in their hands. + * The algorithm iterates through the list and inserts each element into its correct + * position in the sorted portion of the array. + * + * The time complexity of the algorithm is \f$O(n^2)\f$, and in some cases, it + * can be \f$O(n)\f$. + * + * Example execution: + * 1. Start with the array [4, 3, 2, 5, 1]. + * 2. Insert 3 in its correct position: [3, 4, 2, 5, 1]. + * 3. Insert 2: [2, 3, 4, 5, 1]. + * 4. Continue this until the array is sorted: [1, 2, 3, 4, 5]. + */ + + +#include /// for std::is_sorted +#include /// for assert function in testing +#include /// for std::cout and std::endl +#include /// for using std::vector + +/** + * @namespace sorting + * @brief Contains sorting algorithms + */ +namespace sorting { + +/** + * @brief Insertion Sort Function + * + * @tparam T Type of the array elements + * @param[in,out] arr Array to be sorted + * @param n Size of the array + */ +template +void insertionSort(T *arr, int n) { + for (int i = 1; i < n; i++) { + T temp = arr[i]; + int j = i - 1; + while (j >= 0 && temp < arr[j]) { + arr[j + 1] = arr[j]; + j--; + } + arr[j + 1] = temp; + } +} + +/** + * @brief Insertion Sort for a vector + * + * @tparam T Type of the vector elements + * @param [in,out] arr Pointer to the vector to be sorted + */ +template +void insertionSort(std::vector *arr) { + size_t n = arr->size(); + + for (size_t i = 1; i < n; i++) { + T temp = arr->at(i); + int32_t j = i - 1; + while (j >= 0 && temp < arr->at(j)) { + arr->at(j + 1) = arr->at(j); + j--; + } + arr->at(j + 1) = temp; + } +} + +} // namespace sorting + +/** + * @brief Helper function to create a random array + * + * @tparam T Type of the array elements + * @param arr Array to fill (must be pre-allocated) + * @param N Number of elements in the array + */ +template +static void create_random_array(T *arr, int N) { + while (N--) { + double r = (std::rand() % 10000 - 5000) / 100.f; + arr[N] = static_cast(r); + } +} + +/** + * @brief self test implementation + * @return void + */ +static void tests() { + int arr1[10] = {78, 34, 35, 6, 34, 56, 3, 56, 2, 4}; + std::cout << "Test 1... "; + sorting::insertionSort(arr1, 10); + assert(std::is_sorted(arr1, arr1 + 10)); + std::cout << "passed" << std::endl; + + int arr2[5] = {5, -3, 7, -2, 1}; + std::cout << "Test 2... "; + sorting::insertionSort(arr2, 5); + assert(std::is_sorted(arr2, arr2 + 5)); + std::cout << "passed" << std::endl; + + float arr3[5] = {5.6, -3.1, -3.0, -2.1, 1.8}; + std::cout << "Test 3... "; + sorting::insertionSort(arr3, 5); + assert(std::is_sorted(arr3, arr3 + 5)); + std::cout << "passed" << std::endl; + + std::vector arr4({5.6, -3.1, -3.0, -2.1, 1.8}); + std::cout << "Test 4... "; + sorting::insertionSort(&arr4); + assert(std::is_sorted(std::begin(arr4), std::end(arr4))); + std::cout << "passed" << std::endl; + + int arr5[50]; + std::cout << "Test 5... "; + create_random_array(arr5, 50); + sorting::insertionSort(arr5, 50); + assert(std::is_sorted(arr5, arr5 + 50)); + std::cout << "passed" << std::endl; + + float arr6[50]; + std::cout << "Test 6... "; + create_random_array(arr6, 50); + sorting::insertionSort(arr6, 50); + assert(std::is_sorted(arr6, arr6 + 50)); + std::cout << "passed" << std::endl; +} + +/** + * @brief Main function + * @return 0 on successful exit. + */ +int main() { + tests(); /// run self test implementations + return 0; +} diff --git a/sorting/quick_sort.cpp b/sorting/quick_sort.cpp index 514361444e4..8a582790817 100644 --- a/sorting/quick_sort.cpp +++ b/sorting/quick_sort.cpp @@ -26,6 +26,7 @@ #include /// for std::is_sorted #include /// for std::assert +#include #include /// for std::time #include /// for IO operations #include /// for std::vector diff --git a/sorting/radix_sort2.cpp b/sorting/radix_sort2.cpp index 3d6916e1617..d20e81bccae 100644 --- a/sorting/radix_sort2.cpp +++ b/sorting/radix_sort2.cpp @@ -23,8 +23,10 @@ */ /// header files + #include /// for collection of functions #include /// for a macro called assert which can be used to verify assumptions +#include #include /// for io operations #include /// for std::vector diff --git a/sorting/recursive_bubble_sort.cpp b/sorting/recursive_bubble_sort.cpp index d76a72d8633..f73cc0aeac5 100644 --- a/sorting/recursive_bubble_sort.cpp +++ b/sorting/recursive_bubble_sort.cpp @@ -1,20 +1,23 @@ /** * @file * @author [Aditya Prakash](https://adityaprakash.tech) - * @brief This is an implementation of a recursive version of the [Bubble sort algorithm](https://www.geeksforgeeks.org/recursive-bubble-sort/) + * @brief This is an implementation of a recursive version of the [Bubble sort + algorithm](https://www.geeksforgeeks.org/recursive-bubble-sort/) * * @details * The working principle of the Bubble sort algorithm. - * Bubble sort is a simple sorting algorithm used to rearrange a set of ascending or descending order elements. - * Bubble sort gets its name from the fact that data "bubbles" to the top of the dataset. - + * Bubble sort is a simple sorting algorithm used to rearrange a set of + ascending or descending order elements. + * Bubble sort gets its name from the fact that data "bubbles" to the top of the + dataset. + * ### Algorithm * What is Swap? * Swapping two numbers means that we interchange their values. - * Often, an additional variable is required for this operation. + * Often, an additional variable is required for this operation. * This is further illustrated in the following: * void swap(int x, int y){ @@ -26,15 +29,19 @@ * The above process is a typical displacement process. * When we assign a value to x, the old value of x is lost. * That's why we create a temporary variable z to store the initial value of x. - * z is further used to assign the initial value of x to y, to complete swapping. + * z is further used to assign the initial value of x to y, to complete + swapping. * Recursion - * While the recursive method does not necessarily have advantages over iterative + * While the recursive method does not necessarily have advantages over + iterative * versions, but it is useful to enhance the understanding of the algorithm and * recursion itself. In Recursive Bubble sort algorithm, we firstly call the - * function on the entire array, and for every subsequent function call, we exclude - * the last element. This fixes the last element for that sub-array.Formally, for + * function on the entire array, and for every subsequent function call, we + exclude + * the last element. This fixes the last element for that sub-array.Formally, + for * `ith` iteration, we consider elements up to n-i, where n is the number of * elements in the array. Exit condition: n==1; i.e. the sub-array contains only * one element. @@ -43,16 +50,19 @@ * Time complexity: O(n) best case; O(n²) average case; O(n²) worst case * Space complexity: O(n) - * We need to traverse the array `n * (n-1)` times. However, if the entire array is - * already sorted, then we need to traverse it only once. Hence, O(n) is the best case + * We need to traverse the array `n * (n-1)` times. However, if the entire array + is + * already sorted, then we need to traverse it only once. Hence, O(n) is the + best case * complexity */ -#include /// for assert -#include /// for IO operations -#include /// for std::vector -#include /// for std::array -#include /// for std::is_sorted +#include /// for std::is_sorted +#include /// for std::array +#include /// for assert +#include +#include /// for IO operations +#include /// for std::vector /** * @namespace sorting @@ -61,11 +71,11 @@ namespace sorting { /** - * @brief This is an implementation of the recursive_bubble_sort. A vector is passed - * to the function which is then dereferenced, so that the changes are + * @brief This is an implementation of the recursive_bubble_sort. A vector is + * passed to the function which is then dereferenced, so that the changes are * reflected in the original vector. It also accepts a second parameter of * type `int` and name `n`, which is the size of the array. - * + * * @tparam T type of data variables in the array * @param nums our array of elements. * @param n size of the array @@ -136,14 +146,13 @@ static void test() { std::cout << double_arr[i] << ", "; } std::cout << std::endl; - } /** * @brief Main function * @returns 0 on exit */ -int main() { +int main() { test(); // run self-test implementations return 0; } diff --git a/sorting/selection_sort_iterative.cpp b/sorting/selection_sort_iterative.cpp index a9adac0891b..db293b88d03 100644 --- a/sorting/selection_sort_iterative.cpp +++ b/sorting/selection_sort_iterative.cpp @@ -29,6 +29,7 @@ *******************************************************************************/ #include /// for std::is_sorted #include /// for std::assert +#include #include /// for IO operations #include /// for std::vector diff --git a/sorting/selection_sort_recursive.cpp b/sorting/selection_sort_recursive.cpp index 57eadfbd0fe..2ef6219b8dc 100644 --- a/sorting/selection_sort_recursive.cpp +++ b/sorting/selection_sort_recursive.cpp @@ -28,9 +28,10 @@ */ #include /// for std::is_sorted -#include /// for assert -#include /// for std::swap and io operations -#include /// for std::vector +#include /// for assert +#include +#include /// for std::swap and io operations +#include /// for std::vector /** * @namespace sorting diff --git a/sorting/wiggle_sort.cpp b/sorting/wiggle_sort.cpp index bf8574c98c5..45ba788a86e 100644 --- a/sorting/wiggle_sort.cpp +++ b/sorting/wiggle_sort.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include /// for io operations #include @@ -50,7 +51,7 @@ namespace wiggle_sort { */ template // this allows to have vectors of ints, double, float, // etc - std::vector wiggleSort(const std::vector &arr) { +std::vector wiggleSort(const std::vector &arr) { uint32_t size = arr.size(); std::vector out( diff --git a/strings/duval.cpp b/strings/duval.cpp new file mode 100644 index 00000000000..9304da0c06f --- /dev/null +++ b/strings/duval.cpp @@ -0,0 +1,118 @@ +/** + * @file duval.cpp + * @brief Implementation of [Duval's algorithm](https://en.wikipedia.org/wiki/Lyndon_word). + * + * @details + * Duval's algorithm is an algorithm to find the lexicographically smallest + * rotation of a string. It is based on the concept of Lyndon words. + * Lyndon words are defined as the lexicographically smallest string in a + * rotation equivalence class. A rotation equivalence class is a set of strings + * that can be obtained by rotating a string. For example, the rotation + * equivalence class of "abc" is {"abc", "bca", "cab"}. The lexicographically + * smallest string in this class is "abc". + * + * Duval's algorithm works by iterating over the string and finding the + * smallest rotation of the string that is a Lyndon word. This is done by + * comparing the string with its suffixes and finding the smallest suffix that + * is lexicographically smaller than the string. This suffix is then added to + * the result and the process is repeated with the remaining string. + * The algorithm has a time complexity of O(n) where n is the length of the + * string. + * + * @note While Lyndon words are described in the context of strings, + * Duval's algorithm can be used to find the lexicographically smallest cyclic + * shift of any sequence of comparable elements. + * + * @author [Amine Ghoussaini](https://github.com/aminegh20) +*/ + +#include /// for std::array +#include /// for assert +#include /// for std::size_t +#include /// for std::deque +#include /// for std::cout and std::endl +#include /// for std::string +#include /// for std::vector + +/** + * @brief string manipulation algorithms + * @namespace + */ +namespace string { +/** + * @brief Find the lexicographically smallest cyclic shift of a sequence. + * @tparam T type of the sequence + * @param s the sequence + * @returns the 0-indexed position of the least cyclic shift of the sequence + */ +template +size_t duval(const T& s) { + size_t n = s.size(); + size_t i = 0, ans = 0; + while (i < n) { + ans = i; + size_t j = i + 1, k = i; + while (j < (n + n) && s[j % n] >= s[k % n]) { + if (s[k % n] < s[j % n]) { + k = i; + } else { + k++; + } + j++; + } + while (i <= k) { + i += j - k; + } + } + return ans; + // returns 0-indexed position of the least cyclic shift +} + +} // namespace string + +/** + * @brief self test implementation + * returns void + */ +static void test() { + using namespace string; + + // Test 1 + std::string s1 = "abcab"; + assert(duval(s1) == 3); + + // Test 2 + std::string s2 = "011100"; + assert(duval(s2) == 4); + + // Test 3 + std::vector v = {5, 2, 1, 3, 4}; + assert(duval(v) == 2); + + // Test 4 + std::array a = {1, 2, 3, 4, 5}; + assert(duval(a) == 0); + + // Test 5 + std::deque d = {'a', 'z', 'c', 'a', 'b'}; + assert(duval(d) == 3); + + // Test 6 + std::string s3; + assert(duval(s3) == 0); + + // Test 7 + std::vector v2 = {5, 2, 1, 3, -4}; + assert(duval(v2) == 4); + + std::cout << "All tests passed!" << std::endl; +} + +/** + * @brief main function + * @returns 0 on exit + */ +int main() { + test(); // run self test implementations + return 0; +} diff --git a/strings/manacher_algorithm.cpp b/strings/manacher_algorithm.cpp index 94314c3b466..e544d818d8c 100644 --- a/strings/manacher_algorithm.cpp +++ b/strings/manacher_algorithm.cpp @@ -11,6 +11,7 @@ */ #include /// for assert +#include #include /// for IO operations #include /// for std::vector STL #ifdef _MSC_VER diff --git a/strings/z_function.cpp b/strings/z_function.cpp index 6ce98c491d1..d32f19b9f4a 100644 --- a/strings/z_function.cpp +++ b/strings/z_function.cpp @@ -11,6 +11,7 @@ * @author [Ritika Gupta](https://github.com/RitikaGupta8734) */ +#include #include /// for IO operations #ifdef _MSC_VER #include /// for string (use this for MS Visual C++)