From 502a953e23443c8b3fee07e3d2c37fa6e07017b4 Mon Sep 17 00:00:00 2001 From: Yancey <135342560+Yancey2023@users.noreply.github.com> Date: Sun, 1 Sep 2024 12:17:49 +0800 Subject: [PATCH 1/5] feat: let KMP algorithm return index (#2713) * fix: KMP algorithm (#2712) * feat: let the KMP algorithm return index and add more tests. * feat: format code * Update strings/knuth_morris_pratt.cpp * update knuth_morris_pratt.cpp --------- Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> --- strings/knuth_morris_pratt.cpp | 119 +++++++++++++++++---------------- 1 file changed, 61 insertions(+), 58 deletions(-) diff --git a/strings/knuth_morris_pratt.cpp b/strings/knuth_morris_pratt.cpp index 41be1561a..e266d2c17 100644 --- a/strings/knuth_morris_pratt.cpp +++ b/strings/knuth_morris_pratt.cpp @@ -1,95 +1,98 @@ /** - * \file - * \brief The [Knuth-Morris-Pratt + * @file + * @brief The [Knuth-Morris-Pratt * Algorithm](https://en.wikipedia.org/wiki/Knuth–Morris–Pratt_algorithm) for * finding a pattern within a piece of text with complexity O(n + m) - * + * @details * 1. Preprocess pattern to identify any suffixes that are identical to * prefixes. This tells us where to continue from if we get a mismatch between a * character in our pattern and the text. * 2. Step through the text one character at a time and compare it to a * character in the pattern updating our location within the pattern if * necessary + * @author [Yancey](https://github.com/Yancey2023) */ -#include -#ifdef _MSC_VER -#include // use this for MS Visual C++ -#else -#include -#endif -#include +#include /// for assert +#include /// for IO operations +#include /// for std::string +#include /// for std::vector -/** \namespace string_search - * \brief String search algorithms +/** + * @namespace string_search + * @brief String search algorithms */ namespace string_search { /** - * Generate the partial match table aka failure function for a pattern to + * @brief Generate the partial match table aka failure function for a pattern to * search. - * \param[in] pattern text for which to create the partial match table - * \returns the partial match table as a vector array + * @param pattern text for which to create the partial match table + * @returns the partial match table as a vector array */ -std::vector getFailureArray(const std::string &pattern) { - int pattern_length = pattern.size(); - std::vector failure(pattern_length + 1); - failure[0] = -1; - int j = -1; - +std::vector getFailureArray(const std::string &pattern) { + size_t pattern_length = pattern.size(); + std::vector failure(pattern_length + 1); + failure[0] = std::string::npos; + size_t j = std::string::npos; for (int i = 0; i < pattern_length; i++) { - while (j != -1 && pattern[j] != pattern[i]) { + while (j != std::string::npos && pattern[j] != pattern[i]) { j = failure[j]; } - j++; - failure[i + 1] = j; + failure[i + 1] = ++j; } return failure; } /** - * KMP algorithm to find a pattern in a text - * \param[in] pattern string pattern to search - * \param[in] text text in which to search - * \returns `true` if pattern was found - * \returns `false` if pattern was not found + * @brief KMP algorithm to find a pattern in a text + * @param pattern string pattern to search + * @param text text in which to search + * @returns the starting index of the pattern if found + * @returns `std::string::npos` if not found */ -bool kmp(const std::string &pattern, const std::string &text) { - int text_length = text.size(), pattern_length = pattern.size(); - std::vector failure = getFailureArray(pattern); - - int k = 0; - for (int j = 0; j < text_length; j++) { - while (k != -1 && pattern[k] != text[j]) { +size_t kmp(const std::string &pattern, const std::string &text) { + if (pattern.empty()) { + return 0; + } + std::vector failure = getFailureArray(pattern); + size_t text_length = text.size(); + size_t pattern_length = pattern.size(); + size_t k = 0; + for (size_t j = 0; j < text_length; j++) { + while (k != std::string::npos && pattern[k] != text[j]) { k = failure[k]; } - k++; - if (k == pattern_length) - return true; + if (++k == pattern_length) { + return j - k + 1; + } } - return false; + return std::string::npos; } } // namespace string_search using string_search::kmp; -/** Main function */ +/** + * @brief self-test implementations + * @returns void + */ +static void tests() { + assert(kmp("abc1abc12l", "alskfjaldsabc1abc1abc12k2") == std::string::npos); + assert(kmp("bca", "abcabc") == 1); + assert(kmp("World", "helloWorld") == 5); + assert(kmp("c++", "his_is_c++") == 7); + assert(kmp("happy", "happy_coding") == 0); + assert(kmp("", "pattern is empty") == 0); + + // this lets the user know that the tests have passed + std::cout << "All KMP algorithm tests have successfully passed!\n"; +} + +/* + * @brief Main function + * @returns 0 on exit + */ int main() { - std::string text = "alskfjaldsabc1abc1abc12k23adsfabcabc"; - std::string pattern = "abc1abc12l"; - - if (kmp(pattern, text) == true) { - std::cout << "Found" << std::endl; - } else { - std::cout << "Not Found" << std::endl; - } - - text = "abcabc"; - pattern = "bca"; - if (kmp(pattern, text) == true) { - std::cout << "Found" << std::endl; - } else { - std::cout << "Not Found" << std::endl; - } - + tests(); return 0; } From fe41cf4d1131580e9b45fe1c8e8e4ced88a567e9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 09:54:27 +0530 Subject: [PATCH 2/5] docs: update DIRECTORY.md (#2718) Co-authored-by: github-actions[bot] Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> --- DIRECTORY.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index bda68918d..b78662887 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -119,6 +119,7 @@ * [Searching Of Element In Dynamic Array](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/dynamic_programming/searching_of_element_in_dynamic_array.cpp) * [Shortest Common Supersequence](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/dynamic_programming/shortest_common_supersequence.cpp) * [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) * [Word Break](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/dynamic_programming/word_break.cpp) @@ -161,7 +162,7 @@ * [Boruvkas Minimum Spanning Tree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/greedy_algorithms/boruvkas_minimum_spanning_tree.cpp) * [Dijkstra](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/greedy_algorithms/dijkstra.cpp) * [Huffman](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/greedy_algorithms/huffman.cpp) - * [Jumpgame](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/greedy_algorithms/jumpgame.cpp) + * [Jump Game](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/greedy_algorithms/jump_game.cpp) * [Knapsack](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/greedy_algorithms/knapsack.cpp) * [Kruskals Minimum Spanning Tree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/greedy_algorithms/kruskals_minimum_spanning_tree.cpp) * [Prims Minimum Spanning Tree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/greedy_algorithms/prims_minimum_spanning_tree.cpp) From 54a20b447efbdf56da7f331de7469e86d50f7dd5 Mon Sep 17 00:00:00 2001 From: SOZEL <80200848+TruongNhanNguyen@users.noreply.github.com> Date: Sun, 1 Sep 2024 11:37:27 +0700 Subject: [PATCH 3/5] Rework on Stack Array Data Structure (#2683) * ref: rework on stack array data structure - The `Stack` class is placed within the `data_structures` namespace - The destructor is added to the Stack class to ensure memory deallocation - Comments are added to the member functions to describe their purpose - The self-test function is provided, which demonstrates the usage of the Stack class * chore(docs): add `namespace` docstring * chore: add `std::out_of_range` exception and test cases when stack is empty * ref: add `full` and `empty` methods * ref: improve stack array implementation - Use TOS instead of stack index - Add tests for overflow and underflow * fix: remove comparision to true from asserts * chore: remove `stack.hpp` * fix: revert * Update data_structures/stack_using_array.cpp * docs: add namespace comment * chore: remove redundant line in docstring of `empty` method --------- Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> --- data_structures/stack_using_array.cpp | 221 ++++++++++++++++++++------ 1 file changed, 169 insertions(+), 52 deletions(-) diff --git a/data_structures/stack_using_array.cpp b/data_structures/stack_using_array.cpp index 8483d6ad7..73feaf445 100644 --- a/data_structures/stack_using_array.cpp +++ b/data_structures/stack_using_array.cpp @@ -1,62 +1,179 @@ -#include +#include /// For std::assert +#include /// For std::cout +#include /// For std::unique_ptr +#include /// For std::out_of_range -int *stack; -int stack_idx = 0, stack_size; +/** + * @namespace + * @brief data_structures + */ +namespace data_structures { +/** + * @brief Class representation of a stack + * @tparam T The type of the elements in the stack + */ +template +class Stack { + private: + std::unique_ptr stack; ///< Smart pointer to the stack array + int stackSize; ///< Maximum size of the stack + int stackIndex; ///< Index pointing to the top element of the stack -void push(int x) { - if (stack_idx == stack_size) { - std::cout << "\nOverflow"; - } else { - stack[stack_idx++] = x; - } -} + public: + /** + * @brief Constructs a new Stack object + * + * @param size Maximum size of the stack + */ + Stack(int size) : stackSize(size), stackIndex(-1), stack(new T[size]) {} -void pop() { - if (stack_idx == 0) { - std::cout << "\nUnderflow"; - } else { - std::cout << "\n" << stack[--stack_idx] << " deleted"; - } -} + /** + * @brief Checks if the stack is full + * + * @return true if the stack is full, false otherwise + */ + bool full() const { return stackIndex == stackSize - 1; } -void show() { - for (int i = 0; i < stack_idx; i++) { - std::cout << stack[i] << "\n"; - } -} + /** + * @brief Checks if the stack is empty + * @return true if the stack is empty, false otherwise + */ + bool empty() const { return stackIndex == -1; } -void topmost() { std::cout << "\nTopmost element: " << stack[stack_idx - 1]; } -void bottom() { std::cout << "\nBottom element: " << stack[0]; } // If we need access to first element without using pop command -int main() { - std::cout << "\nEnter stack_size of stack : "; - std::cin >> stack_size; - stack = new int[stack_size]; - int ch, x; - do { - std::cout << "\n0. Exit"; - std::cout << "\n1. Push"; - std::cout << "\n2. Pop"; - std::cout << "\n3. Print"; - std::cout << "\n4. Print topmost element:"; - std::cout << "\n5. Print Bottom element:"; - std::cout << "\nEnter Your Choice : "; - std::cin >> ch; - if (ch == 1) { - std::cout << "\nInsert : "; - std::cin >> x; - push(x); - } else if (ch == 2) { - pop(); - } else if (ch == 3) { - show(); - } else if (ch == 4) { - topmost(); - } else if(ch == 5) { - bottom(); + /** + * @brief Pushes an element onto the stack + * + * @param element Element to push onto the stack + */ + void push(T element) { + if (full()) { + throw std::out_of_range("Stack overflow"); + } else { + stack[++stackIndex] = element; } - } while (ch != 0); + } - delete[] stack; + /** + * @brief Pops an element from the stack + * + * @return The popped element + * @throws std::out_of_range if the stack is empty + */ + T pop() { + if (empty()) { + throw std::out_of_range("Stack underflow"); + } + return stack[stackIndex--]; + } + /** + * @brief Displays all elements in the stack + */ + void show() const { + for (int i = 0; i <= stackIndex; i++) { + std::cout << stack[i] << "\n"; + } + } + + /** + * @brief Displays the topmost element of the stack + * + * @return The topmost element of the stack + * @throws std::out_of_range if the stack is empty + */ + T topmost() const { + if (empty()) { + throw std::out_of_range("Stack underflow"); + } + return stack[stackIndex]; + } + + /** + * @brief Displays the bottom element of the stack + * + * @return The bottom element of the stack + * @throws std::out_of_range if the stack is empty + */ + T bottom() const { + if (empty()) { + throw std::out_of_range("Stack underflow"); + } + return stack[0]; + } +}; +} // namespace data_structures + +/** + * @brief Self-test implementations + * @returns void + */ +static void test() { + data_structures::Stack stack(5); + + // Test empty and full operations + assert(stack.empty()); + assert(!stack.full()); + + // Test pushing elements and checking topmost + stack.push(10); + assert(stack.topmost() == 10); + + stack.push(20); + assert(stack.topmost() == 20); + + stack.push(30); + stack.push(40); + stack.push(50); + assert(stack.full()); + + // Test stack overflow + try { + stack.push(60); + } catch (const std::out_of_range& e) { + assert(std::string(e.what()) == "Stack overflow"); + } + + // Test popping elements + assert(stack.pop() == 50); + assert(stack.pop() == 40); + assert(stack.pop() == 30); + + // Check topmost and bottom elements + assert(stack.topmost() == 20); + assert(stack.bottom() == 10); + + assert(stack.pop() == 20); + assert(stack.pop() == 10); + + assert(stack.empty()); + assert(!stack.full()); + + // Test stack underflow + try { + stack.pop(); + } catch (const std::out_of_range& e) { + assert(std::string(e.what()) == "Stack underflow"); + } + + try { + stack.topmost(); + } catch (const std::out_of_range& e) { + assert(std::string(e.what()) == "Stack underflow"); + } + + try { + stack.bottom(); + } catch (const std::out_of_range& e) { + assert(std::string(e.what()) == "Stack underflow"); + } +} + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + test(); // run self-test implementations + std::cout << "All tests passed!" << std::endl; return 0; } From 52db277fb686a3232abdff9dd4e76ca999328418 Mon Sep 17 00:00:00 2001 From: Piotr Idzik <65706193+vil02@users.noreply.github.com> Date: Mon, 2 Sep 2024 01:03:19 +0200 Subject: [PATCH 4/5] fix: remove memory leak from `iterative_tree_traversals.cpp` (#2720) --- others/iterative_tree_traversals.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/others/iterative_tree_traversals.cpp b/others/iterative_tree_traversals.cpp index 0f9713b04..11fa18146 100644 --- a/others/iterative_tree_traversals.cpp +++ b/others/iterative_tree_traversals.cpp @@ -180,6 +180,25 @@ std::vector BinaryTree::inOrderIterative(Node *root) { } return result; } +void deleteAll(Node *root) { + if (root) { + std::stack stack; + stack.push(root); + + while (!stack.empty()) { + const Node *current = stack.top(); + stack.pop(); + + if (current->right) { + stack.push(current->right); + } + if (current->left) { + stack.push(current->left); + } + delete current; + } + } +} } // namespace iterative_tree_traversals } // namespace others @@ -396,5 +415,7 @@ int main() { test6(binaryTree, root); // run inorder-iterative test on negative values std::cout << "\nIn-order test on-negative value Passed!" << std::endl; + deleteAll(root); + return 0; } From db182d55217384912d7e837917d1dbc3ede45637 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Mon, 2 Sep 2024 02:12:33 +0300 Subject: [PATCH 5/5] fix: fix bug in timSort (#2692) * fix: fix bug in timSort * Apply suggestions from code review Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> * Fix * Update sorting/tim_sort.cpp * Add const --------- Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> --- sorting/tim_sort.cpp | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/sorting/tim_sort.cpp b/sorting/tim_sort.cpp index 94f5aa230..a9ef0459c 100644 --- a/sorting/tim_sort.cpp +++ b/sorting/tim_sort.cpp @@ -1,6 +1,8 @@ // C++ program to perform TimSort. #include +#include #include +#include const int RUN = 32; @@ -8,7 +10,7 @@ const int RUN = 32; // atmost RUN void insertionSort(int arr[], int left, int right) { for (int i = left + 1; i <= right; i++) { - int temp = arr[i]; + const int temp = arr[i]; int j = i - 1; while (arr[j] > temp && j >= left) { arr[j + 1] = arr[j]; @@ -21,7 +23,7 @@ void insertionSort(int arr[], int left, int right) { // merge function merges the sorted runs void merge(int arr[], int l, int m, int r) { // original array is broken in two parts, left and right array - int len1 = m - l + 1, len2 = r - m; + const int len1 = m - l + 1, len2 = r - m; int *left = new int[len1], *right = new int[len2]; for (int i = 0; i < len1; i++) left[i] = arr[l + i]; for (int i = 0; i < len2; i++) right[i] = arr[m + 1 + i]; @@ -74,8 +76,8 @@ void timSort(int arr[], int n) { for (int left = 0; left < n; left += 2 * size) { // find ending point of left sub array // mid+1 is starting point of right sub array - int mid = left + size - 1; - int right = std::min((left + 2 * size - 1), (n - 1)); + const int mid = std::min((left + size - 1), (n - 1)); + const int right = std::min((left + 2 * size - 1), (n - 1)); // merge sub array arr[left.....mid] & arr[mid+1....right] merge(arr, left, mid, right); @@ -89,10 +91,29 @@ void printArray(int arr[], int n) { std::cout << std::endl; } +/** + * @brief self-test implementation + * @returns void + */ +void tests() { + // Case: array of length 65 + constexpr int N = 65; + int arr[N]; + + std::iota(arr, arr + N, 0); + std::reverse(arr, arr + N); + assert(!std::is_sorted(arr, arr + N)); + + timSort(arr, N); + assert(std::is_sorted(arr, arr + N)); +} + // Driver program to test above function int main() { + tests(); // run self test implementations + int arr[] = {5, 21, 7, 23, 19}; - int n = sizeof(arr) / sizeof(arr[0]); + const int n = sizeof(arr) / sizeof(arr[0]); printf("Given Array is\n"); printArray(arr, n);