From f34f93e77a25a987d19d9b582236b822d54e3452 Mon Sep 17 00:00:00 2001 From: Swastika Gupta <64654203+Swastyy@users.noreply.github.com> Date: Thu, 22 Jul 2021 00:52:16 +0530 Subject: [PATCH] feat: Add the Subarray Sum implementation (#1527) * Create subarray_sum.cpp * updating DIRECTORY.md * clang-format and clang-tidy fixes for 0a293ece * Update backtracking/subarray_sum.cpp Co-authored-by: David Leal * clang-format and clang-tidy fixes for f37f7b7c * Update backtracking/subarray_sum.cpp Co-authored-by: David Leal * Update backtracking/subarray_sum.cpp Co-authored-by: David Leal * Update subarray_sum.cpp * clang-format and clang-tidy fixes for 9b0b5f87 * Update backtracking/subarray_sum.cpp * Update backtracking/subarray_sum.cpp Co-authored-by: David Leal * Update backtracking/subarray_sum.cpp Co-authored-by: David Leal * Update backtracking/subarray_sum.cpp Co-authored-by: David Leal * clang-format and clang-tidy fixes for 047366a8 * Update subarray_sum.cpp * clang-format and clang-tidy fixes for 512b1887 * Update backtracking/subarray_sum.cpp Co-authored-by: David Leal * Update backtracking/subarray_sum.cpp Co-authored-by: David Leal * Update backtracking/subarray_sum.cpp Co-authored-by: David Leal * Update backtracking/subarray_sum.cpp Co-authored-by: David Leal * fix: Apply suggestions from code review * docs: Apply suggestions from code review * clang-format and clang-tidy fixes for e6979047 Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: David Leal --- DIRECTORY.md | 1 + backtracking/graph_coloring.cpp | 151 +++++------ backtracking/subarray_sum.cpp | 117 +++++++++ others/iterative_tree_traversals.cpp | 266 +++++++++++-------- sorting/random_pivot_quick_sort.cpp | 376 +++++++++++++++------------ 5 files changed, 549 insertions(+), 362 deletions(-) create mode 100644 backtracking/subarray_sum.cpp diff --git a/DIRECTORY.md b/DIRECTORY.md index 7f2468bed..6f25fa66b 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -8,6 +8,7 @@ * [N Queens All Solution Optimised](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/backtracking/n_queens_all_solution_optimised.cpp) * [Nqueen Print All Solutions](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/backtracking/nqueen_print_all_solutions.cpp) * [Rat Maze](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/backtracking/rat_maze.cpp) + * [Subarray Sum](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/backtracking/subarray_sum.cpp) * [Subset Sum](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/backtracking/subset_sum.cpp) * [Sudoku Solve](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/backtracking/sudoku_solve.cpp) diff --git a/backtracking/graph_coloring.cpp b/backtracking/graph_coloring.cpp index 0f82a77bf..baa10ab46 100644 --- a/backtracking/graph_coloring.cpp +++ b/backtracking/graph_coloring.cpp @@ -1,22 +1,24 @@ /** * @file * @brief prints the assigned colors - * using [Graph Coloring](https://en.wikipedia.org/wiki/Graph_coloring) algorithm + * using [Graph Coloring](https://en.wikipedia.org/wiki/Graph_coloring) + * algorithm * * @details - * In graph theory, graph coloring is a special case of graph labeling; - * it is an assignment of labels traditionally called "colors" to elements of a graph subject to certain constraints. - * In its simplest form, it is a way of coloring the vertices of a graph such that no two adjacent vertices are of the same color; - * this is called a vertex coloring. Similarly, an edge coloring assigns - * a color to each edge so that no two adjacent edges are of the same color, - * and a face coloring of a planar graph assigns a color to each face or + * In graph theory, graph coloring is a special case of graph labeling; + * it is an assignment of labels traditionally called "colors" to elements of a + * graph subject to certain constraints. In its simplest form, it is a way of + * coloring the vertices of a graph such that no two adjacent vertices are of + * the same color; this is called a vertex coloring. Similarly, an edge coloring + * assigns a color to each edge so that no two adjacent edges are of the same + * color, and a face coloring of a planar graph assigns a color to each face or * region so that no two faces that share a boundary have the same color. * * @author [Anup Kumar Panwar](https://github.com/AnupKumarPanwar) * @author [David Leal](https://github.com/Panquesito7) */ -#include #include +#include #include /** @@ -24,70 +26,72 @@ * @brief Backtracking algorithms */ namespace backtracking { - /** A utility function to print solution - * @tparam V number of vertices in the graph - * @param color array of colors assigned to the nodes - */ - template - void printSolution(const std::array & color) { - std::cout << "Following are the assigned colors" << std::endl; - for (auto &col : color) { - std::cout << col; - } - std::cout << std::endl; +/** A utility function to print solution + * @tparam V number of vertices in the graph + * @param color array of colors assigned to the nodes + */ +template +void printSolution(const std::array& color) { + std::cout << "Following are the assigned colors" << std::endl; + for (auto& col : color) { + std::cout << col; } + std::cout << std::endl; +} - /** A utility function to check if the current color assignment is safe for - * vertex v - * @tparam V number of vertices in the graph - * @param v index of graph vertex to check - * @param graph matrix of graph nonnectivity - * @param color vector of colors assigned to the graph nodes/vertices - * @param c color value to check for the node `v` - * @returns `true` if the color is safe to be assigned to the node - * @returns `false` if the color is not safe to be assigned to the node - */ - template - bool isSafe(int v, const std::array, V>& graph, const std::array & color, int c) { - for (int i = 0; i < V; i++) { - if (graph[v][i] && c == color[i]) { - return false; - } - } - return true; - } - - /** A recursive utility function to solve m coloring problem - * @tparam V number of vertices in the graph - * @param graph matrix of graph nonnectivity - * @param m number of colors - * @param [in,out] color description // used in,out to notify in documentation - * that this parameter gets modified by the function - * @param v index of graph vertex to check - */ - template - void graphColoring(const std::array, V>& graph, int m, std::array color, int v) { - // base case: - // If all vertices are assigned a color then return true - if (v == V) { - backtracking::printSolution(color); - return; - } - - // Consider this vertex v and try different colors - for (int c = 1; c <= m; c++) { - // Check if assignment of color c to v is fine - if (backtracking::isSafe(v, graph, color, c)) { - color[v] = c; - - // recur to assign colors to rest of the vertices - backtracking::graphColoring(graph, m, color, v + 1); - - // If assigning color c doesn't lead to a solution then remove it - color[v] = 0; - } +/** A utility function to check if the current color assignment is safe for + * vertex v + * @tparam V number of vertices in the graph + * @param v index of graph vertex to check + * @param graph matrix of graph nonnectivity + * @param color vector of colors assigned to the graph nodes/vertices + * @param c color value to check for the node `v` + * @returns `true` if the color is safe to be assigned to the node + * @returns `false` if the color is not safe to be assigned to the node + */ +template +bool isSafe(int v, const std::array, V>& graph, + const std::array& color, int c) { + for (int i = 0; i < V; i++) { + if (graph[v][i] && c == color[i]) { + return false; } } + return true; +} + +/** A recursive utility function to solve m coloring problem + * @tparam V number of vertices in the graph + * @param graph matrix of graph nonnectivity + * @param m number of colors + * @param [in,out] color description // used in,out to notify in documentation + * that this parameter gets modified by the function + * @param v index of graph vertex to check + */ +template +void graphColoring(const std::array, V>& graph, int m, + std::array color, int v) { + // base case: + // If all vertices are assigned a color then return true + if (v == V) { + backtracking::printSolution(color); + return; + } + + // Consider this vertex v and try different colors + for (int c = 1; c <= m; c++) { + // Check if assignment of color c to v is fine + if (backtracking::isSafe(v, graph, color, c)) { + color[v] = c; + + // recur to assign colors to rest of the vertices + backtracking::graphColoring(graph, m, color, v + 1); + + // If assigning color c doesn't lead to a solution then remove it + color[v] = 0; + } + } +} } // namespace backtracking /** @@ -102,15 +106,12 @@ int main() { // (0)---(1) const int V = 4; // number of vertices in the graph - std::array , V> graph = { - std::array ({0, 1, 1, 1}), - std::array ({1, 0, 1, 0}), - std::array ({1, 1, 0, 1}), - std::array ({1, 0, 1, 0}) - }; + std::array, V> graph = { + std::array({0, 1, 1, 1}), std::array({1, 0, 1, 0}), + std::array({1, 1, 0, 1}), std::array({1, 0, 1, 0})}; int m = 3; // Number of colors - std::array color{}; + std::array color{}; backtracking::graphColoring(graph, m, color, 0); return 0; diff --git a/backtracking/subarray_sum.cpp b/backtracking/subarray_sum.cpp new file mode 100644 index 000000000..8001d74cc --- /dev/null +++ b/backtracking/subarray_sum.cpp @@ -0,0 +1,117 @@ +/** + * @file + * @brief [Subset-sum](https://en.wikipedia.org/wiki/Subset_sum_problem) (only + * continuous subsets) problem + * @details We are given an array and a sum value. The algorithms find all + * the subarrays of that array with sum equal to the given sum and return such + * subarrays count. This approach will have \f$O(n)\f$ time complexity and + * \f$O(n)\f$ space complexity. NOTE: In this problem, we are only referring to + * the continuous subsets as subarrays everywhere. Subarrays can be created + * using deletion operation at the end of the front of an array only. The parent + * array is also counted in subarrays having 0 number of deletion operations. + * + * @author [Swastika Gupta](https://github.com/Swastyy) + */ + +#include /// for assert +#include /// for IO operations +#include /// for unordered_map +#include /// for std::vector + +/** + * @namespace backtracking + * @brief Backtracking algorithms + */ +namespace backtracking { +/** + * @namespace subarray_sum + * @brief Functions for the [Subset + * sum](https://en.wikipedia.org/wiki/Subset_sum_problem) implementation + */ +namespace subarray_sum { +/** + * @brief The main function that implements the count of the subarrays + * @param sum is the required sum of any subarrays + * @param in_arr is the input array + * @returns count of the number of subsets with required sum + */ +uint64_t subarray_sum(int64_t sum, const std::vector &in_arr) { + int64_t nelement = in_arr.size(); + int64_t count_of_subset = 0; + int64_t current_sum = 0; + std::unordered_map + sumarray; // to store the subarrays count + // frequency having some sum value + + for (int64_t i = 0; i < nelement; i++) { + current_sum += in_arr[i]; + + if (current_sum == sum) { + count_of_subset++; + } + // If in case current_sum is greater than the required sum + if (sumarray.find(current_sum - sum) != sumarray.end()) { + count_of_subset += (sumarray[current_sum - sum]); + } + sumarray[current_sum]++; + } + return count_of_subset; +} +} // namespace subarray_sum +} // namespace backtracking + +/** + * @brief Self-test implementations + * @returns void + */ +static void test() { + // 1st test + std::cout << "1st test "; + std::vector array1 = {-7, -3, -2, 5, 8}; // input array + assert( + backtracking::subarray_sum::subarray_sum(0, array1) == + 1); // first argument in subarray_sum function is the required sum and + // second is the input array, answer is the subarray {(-3,-2,5)} + std::cout << "passed" << std::endl; + + // 2nd test + std::cout << "2nd test "; + std::vector array2 = {1, 2, 3, 3}; + assert(backtracking::subarray_sum::subarray_sum(6, array2) == + 2); // here we are expecting 2 subsets which sum up to 6 i.e. + // {(1,2,3),(3,3)} + std::cout << "passed" << std::endl; + + // 3rd test + std::cout << "3rd test "; + std::vector array3 = {1, 1, 1, 1}; + assert(backtracking::subarray_sum::subarray_sum(1, array3) == + 4); // here we are expecting 4 subsets which sum up to 1 i.e. + // {(1),(1),(1),(1)} + std::cout << "passed" << std::endl; + + // 4rd test + std::cout << "4th test "; + std::vector array4 = {3, 3, 3, 3}; + assert(backtracking::subarray_sum::subarray_sum(6, array4) == + 3); // here we are expecting 3 subsets which sum up to 6 i.e. + // {(3,3),(3,3),(3,3)} + std::cout << "passed" << std::endl; + + // 5th test + std::cout << "5th test "; + std::vector array5 = {}; + assert(backtracking::subarray_sum::subarray_sum(6, array5) == + 0); // here we are expecting 0 subsets which sum up to 6 i.e. we + // cannot select anything from an empty array + std::cout << "passed" << std::endl; +} + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + test(); // run self-test implementations + return 0; +} diff --git a/others/iterative_tree_traversals.cpp b/others/iterative_tree_traversals.cpp index 6d0e7dc2e..0f9713b04 100644 --- a/others/iterative_tree_traversals.cpp +++ b/others/iterative_tree_traversals.cpp @@ -1,43 +1,45 @@ /** * @file - * @brief Iterative version of Preorder, Postorder, and preorder [Traversal of the Tree] - * (https://en.wikipedia.org/wiki/Tree_traversal) + * @brief Iterative version of Preorder, Postorder, and preorder [Traversal of + * the Tree] (https://en.wikipedia.org/wiki/Tree_traversal) * @author [Motasim](https://github.com/motasimmakki) * @details - * + * * ### Iterative Preorder Traversal of a tree * Create a Stack that will store the Node of Tree. * Push the root node into the stack. - * Save the root into the variabe named as current, and pop and elemnt from the stack. - * Store the data of current into the result array, and start traversing from it. - * Push both the child node of the current node into the stack, first right child then left child. - * Repeat the same set of steps untill the Stack becomes empty. - * And return the result array as the preorder traversal of a tree. - * + * Save the root into the variabe named as current, and pop and elemnt from the + * stack. Store the data of current into the result array, and start traversing + * from it. Push both the child node of the current node into the stack, first + * right child then left child. Repeat the same set of steps untill the Stack + * becomes empty. And return the result array as the preorder traversal of a + * tree. + * * ### Iterative Postorder Traversal of a tree * Create a Stack that will store the Node of Tree. * Push the root node into the stack. - * Save the root into the variabe named as current, and pop and elemnt from the stack. - * Store the data of current into the result array, and start traversing from it. - * Push both the child node of the current node into the stack, first left child then right child. - * Repeat the same set of steps untill the Stack becomes empty. - * Now reverse the result array and then return it to the calling function as a postorder traversal of a tree. - * + * Save the root into the variabe named as current, and pop and elemnt from the + * stack. Store the data of current into the result array, and start traversing + * from it. Push both the child node of the current node into the stack, first + * left child then right child. Repeat the same set of steps untill the Stack + * becomes empty. Now reverse the result array and then return it to the calling + * function as a postorder traversal of a tree. + * * ### Iterative Inorder Traversal of a tree * Create a Stack that will store the Node of Tree. * Push the root node into the stack. * Save the root into the variabe named as current. - * Now iterate and take the current to the extreme left of the tree by traversing only to its left. - * Pop the elemnt from the stack and assign it to the current. - * Store the data of current into the result array. - * Repeat the same set of steps until the Stack becomes empty or the current becomes NULL. - * And return the result array as the inorder traversal of a tree. + * Now iterate and take the current to the extreme left of the tree by + * traversing only to its left. Pop the elemnt from the stack and assign it to + * the current. Store the data of current into the result array. Repeat the same + * set of steps until the Stack becomes empty or the current becomes NULL. And + * return the result array as the inorder traversal of a tree. */ +#include /// for `reverse` +#include /// for `assert` #include /// for I/O operations #include /// for `stack` #include /// for `vector` -#include /// for `reverse` -#include /// for `assert` /** * @namespace others @@ -46,35 +48,44 @@ namespace others { /** * @namespace iterative_tree_traversals - * @brief Functions for the [Traversal of the Tree](https://en.wikipedia.org/wiki/Tree_traversal) algorithm + * @brief Functions for the [Traversal of the + * Tree](https://en.wikipedia.org/wiki/Tree_traversal) algorithm */ namespace iterative_tree_traversals { /** * @brief defines the structure of a node of the tree */ struct Node { - int64_t data = 0; ///< The value/key of the node. - struct Node *left; ///< struct pointer to left subtree. - struct Node *right; ///< struct pointer to right subtree. + int64_t data = 0; ///< The value/key of the node. + struct Node *left{}; ///< struct pointer to left subtree. + struct Node *right{}; ///< struct pointer to right subtree. }; /** * @brief defines the functions associated with the binary tree */ class BinaryTree { - public: - Node *createNewNode(int64_t); ///< function that will create new node for insertion. - std::vector preOrderIterative(Node *); ///< function that takes root of the tree as an argument, and returns its preorder traversal. - std::vector postOrderIterative(Node *); ///< function that takes root of the tree as an argument, and returns its postorder traversal. - std::vector inOrderIterative(Node *); ///< function that takes root of the tree as an argument, and returns its inorder traversal. + public: + Node *createNewNode( + int64_t); ///< function that will create new node for insertion. + std::vector preOrderIterative( + Node *); ///< function that takes root of the tree as an argument, and + ///< returns its preorder traversal. + std::vector postOrderIterative( + Node *); ///< function that takes root of the tree as an argument, and + ///< returns its postorder traversal. + std::vector inOrderIterative( + Node *); ///< function that takes root of the tree as an argument, and + ///< returns its inorder traversal. }; /** - * @brief will allocate the memory for a node and, along the data and return the node. + * @brief will allocate the memory for a node and, along the data and return the + * node. * @param data value that a particular node will contain. - * @return pointer to the newly created node with assigned data. + * @return pointer to the newly created node with assigned data. */ -Node * BinaryTree::createNewNode(int64_t data) { +Node *BinaryTree::createNewNode(int64_t data) { Node *node = new Node(); node->data = data; node->left = node->right = nullptr; @@ -82,85 +93,91 @@ Node * BinaryTree::createNewNode(int64_t data) { } /** - * @brief preOrderIterative() function that will perform the preorder traversal iteratively, - * and return the result array that contain the preorder traversal of a tree. + * @brief preOrderIterative() function that will perform the preorder traversal + * iteratively, and return the result array that contain the preorder traversal + * of a tree. * @param root head/root node of a tree * @return result that is containing the preorder traversal of a tree */ -std::vector BinaryTree::preOrderIterative(Node *root) { - std::stack stack; ///< is used to find and traverse the child nodes. +std::vector BinaryTree::preOrderIterative(Node *root) { + std::stack + stack; ///< is used to find and traverse the child nodes. std::vector result; ///< list of values, sorted in pre-order. - + stack.push(root); - - while(!stack.empty()) { + + while (!stack.empty()) { result.push_back(stack.top()->data); Node *current = stack.top(); stack.pop(); - - if(current->right) { + + if (current->right) { stack.push(current->right); } - if(current->left) { + if (current->left) { stack.push(current->left); } } - + return result; } /** - * @brief postOrderIterative() function that will perform the postorder traversal iteratively, - * and return the result array that contain the postorder traversal of a tree. + * @brief postOrderIterative() function that will perform the postorder + * traversal iteratively, and return the result array that contain the postorder + * traversal of a tree. * @param root head/root node of a tree * @return result that is containing the postorder traversal of a tree */ -std::vector BinaryTree::postOrderIterative(Node *root) { - std::stack stack; ///< is used to find and traverse the child nodes. +std::vector BinaryTree::postOrderIterative(Node *root) { + std::stack + stack; ///< is used to find and traverse the child nodes. std::vector result; ///< List of values, sorted in post-order. - + stack.push(root); - - while(!stack.empty()) { + + while (!stack.empty()) { result.push_back(stack.top()->data); Node *current = stack.top(); stack.pop(); - - if(current->left) { + + if (current->left) { stack.push(current->left); } - if(current->right) { + if (current->right) { stack.push(current->right); } } reverse(result.begin(), result.end()); - + return result; } /** - * @brief inOrderIterative() function that will perform the inorder traversal iteratively, - * and return the result array that contain the inorder traversal of a tree. + * @brief inOrderIterative() function that will perform the inorder traversal + * iteratively, and return the result array that contain the inorder traversal + * of a tree. * @param root head/root node of a tree * @return result that is containing the inorder traversal of a tree */ std::vector BinaryTree::inOrderIterative(Node *root) { - std::stack stack; ///< is used to find and traverse the child nodes. + std::stack + stack; ///< is used to find and traverse the child nodes. std::vector result; ///< List of values, sorted in in-order. Node *current = root; - - while(!stack.empty() || current) { - while(current) { + + while (!stack.empty() || current) { + while (current) { stack.push(current); current = current->left; } current = stack.top(); - stack.pop(); - result.push_back(current->data); + stack.pop(); + result.push_back(current->data); current = current->right; - } + } return result; } } // namespace iterative_tree_traversals @@ -171,22 +188,25 @@ std::vector BinaryTree::inOrderIterative(Node *root) { * @param binaryTree instance of the BinaryTree class * @param root head/root node of a tree */ -static void test1(others::iterative_tree_traversals::BinaryTree binaryTree, others::iterative_tree_traversals::Node *root){ +static void test1(others::iterative_tree_traversals::BinaryTree binaryTree, + others::iterative_tree_traversals::Node *root) { std::vector actual_result{1, 2, 4, 5, 3}; - std::vector result; ///< result stores the preorder traversal of the binary tree + std::vector + result; ///< result stores the preorder traversal of the binary tree // Calling preOrderIterative() function by passing a root node, // and storing the preorder traversal in result. - result = binaryTree.preOrderIterative(root); + result = binaryTree.preOrderIterative(root); // Self-testing the result using `assert` - for(int i = 0; i < result.size(); i++) + for (int i = 0; i < result.size(); i++) { assert(actual_result[i] == result[i]); + } // Printing the result storing preorder. - std::cout<< "\nPreOrder Traversal Is : "<< std::endl; - for(auto i: result) { - std::cout<< i<< " "; + std::cout << "\nPreOrder Traversal Is : " << std::endl; + for (auto i : result) { + std::cout << i << " "; } } @@ -195,22 +215,25 @@ static void test1(others::iterative_tree_traversals::BinaryTree binaryTree, othe * @param binaryTree instance of BinaryTree class * @param root head/root node of a tree */ -static void test2(others::iterative_tree_traversals::BinaryTree binaryTree, others::iterative_tree_traversals::Node *root){ +static void test2(others::iterative_tree_traversals::BinaryTree binaryTree, + others::iterative_tree_traversals::Node *root) { std::vector actual_result{4, 5, 2, 3, 1}; - std::vector result; ///< result stores the postorder traversal of the binary tree. + std::vector + result; ///< result stores the postorder traversal of the binary tree. // Calling postOrderIterative() function by passing a root node, // and storing the postorder traversal in result. result = binaryTree.postOrderIterative(root); // Self-testing the result using `assert` - for(int i = 0; i < result.size(); i++) + for (int i = 0; i < result.size(); i++) { assert(actual_result[i] == result[i]); + } // Printing the result storing postorder. - std::cout<< "\nPostOrder Traversal Is : "<< std::endl; - for(auto i: result) { - std::cout<< i<< " "; + std::cout << "\nPostOrder Traversal Is : " << std::endl; + for (auto i : result) { + std::cout << i << " "; } } @@ -219,22 +242,25 @@ static void test2(others::iterative_tree_traversals::BinaryTree binaryTree, othe * @param binaryTree instance of BinaryTree class * @param root head/root node of a tree */ -static void test3(others::iterative_tree_traversals::BinaryTree binaryTree, others::iterative_tree_traversals::Node *root){ +static void test3(others::iterative_tree_traversals::BinaryTree binaryTree, + others::iterative_tree_traversals::Node *root) { std::vector actual_result{4, 2, 5, 1, 3}; - std::vector result; ///< result stores the inorder traversal of the binary tree. + std::vector + result; ///< result stores the inorder traversal of the binary tree. // Calling inOrderIterative() function by passing a root node, // and storing the inorder traversal in result. result = binaryTree.inOrderIterative(root); // Self-testing the result using `assert` - for(int i = 0; i < result.size(); i++) + for (int i = 0; i < result.size(); i++) { assert(actual_result[i] == result[i]); + } // Printing the result storing inorder. - std::cout<< "\nInOrder Traversal Is : "<< std::endl; - for(auto i: result) { - std::cout<< i<< " "; + std::cout << "\nInOrder Traversal Is : " << std::endl; + for (auto i : result) { + std::cout << i << " "; } } @@ -243,46 +269,53 @@ static void test3(others::iterative_tree_traversals::BinaryTree binaryTree, othe * @param binaryTree instance of BinaryTree class * @param root head/root node of a tree */ -static void test4(others::iterative_tree_traversals::BinaryTree binaryTree, others::iterative_tree_traversals::Node *root){ +static void test4(others::iterative_tree_traversals::BinaryTree binaryTree, + others::iterative_tree_traversals::Node *root) { std::vector actual_result{-1, -2, -4, -5, -3}; - std::vector result; ///< result stores the preorder traversal of the binary tree + std::vector + result; ///< result stores the preorder traversal of the binary tree // Calling preOrderIterative() function by passing a root node, // and storing the preorder traversal in result. - result = binaryTree.preOrderIterative(root); + result = binaryTree.preOrderIterative(root); // Self-testing the result using `assert` - for(int i = 0; i < result.size(); i++) + for (int i = 0; i < result.size(); i++) { assert(actual_result[i] == result[i]); + } // Printing the result storing preorder. - std::cout<< "\nPreOrder Traversal Is : "<< std::endl; - for(auto i: result) { - std::cout<< i<< " "; + std::cout << "\nPreOrder Traversal Is : " << std::endl; + for (auto i : result) { + std::cout << i << " "; } } /** - * @brief Test the computed postorder with the actual postorder on negative value. + * @brief Test the computed postorder with the actual postorder on negative + * value. * @param binaryTree instance of BinaryTree class * @param root head/root node of a tree */ -static void test5(others::iterative_tree_traversals::BinaryTree binaryTree, others::iterative_tree_traversals::Node *root){ +static void test5(others::iterative_tree_traversals::BinaryTree binaryTree, + others::iterative_tree_traversals::Node *root) { std::vector actual_result{-4, -5, -2, -3, -1}; - std::vector result; ///< result stores the postorder traversal of the binary tree. + std::vector + result; ///< result stores the postorder traversal of the binary tree. // Calling postOrderIterative() function by passing a root node, // and storing the postorder traversal in result. result = binaryTree.postOrderIterative(root); // Self-testing the result using `assert` - for(int i = 0; i < result.size(); i++) + for (int i = 0; i < result.size(); i++) { assert(actual_result[i] == result[i]); + } // Printing the result storing postorder. - std::cout<< "\nPostOrder Traversal Is : "<< std::endl; - for(auto i: result) { - std::cout<< i<< " "; + std::cout << "\nPostOrder Traversal Is : " << std::endl; + for (auto i : result) { + std::cout << i << " "; } } @@ -291,22 +324,25 @@ static void test5(others::iterative_tree_traversals::BinaryTree binaryTree, othe * @param binaryTree instance of BinaryTree class * @param root head/root node of a tree */ -static void test6(others::iterative_tree_traversals::BinaryTree binaryTree, others::iterative_tree_traversals::Node *root){ +static void test6(others::iterative_tree_traversals::BinaryTree binaryTree, + others::iterative_tree_traversals::Node *root) { std::vector actual_result{-4, -2, -5, -1, -3}; - std::vector result; ///< result stores the inorder traversal of the binary tree. + std::vector + result; ///< result stores the inorder traversal of the binary tree. // Calling inOrderIterative() function by passing a root node, // and storing the inorder traversal in result. result = binaryTree.inOrderIterative(root); // Self-testing the result using `assert` - for(int i = 0; i < result.size(); i++) + for (int i = 0; i < result.size(); i++) { assert(actual_result[i] == result[i]); + } // Printing the result storing inorder. - std::cout<< "\nInOrder Traversal Is : "<< std::endl; - for(auto i: result) { - std::cout<< i<< " "; + std::cout << "\nInOrder Traversal Is : " << std::endl; + for (auto i : result) { + std::cout << i << " "; } } @@ -316,7 +352,7 @@ static void test6(others::iterative_tree_traversals::BinaryTree binaryTree, othe */ int main() { // Creating a tree with the following structure, - /* + /* 1 / \ 2 3 @@ -324,22 +360,24 @@ int main() { 4 5 */ - others::iterative_tree_traversals::BinaryTree binaryTree; ///< instace of BinaryTree, used to access its members functions. + others::iterative_tree_traversals::BinaryTree + binaryTree; ///< instace of BinaryTree, used to access its members + ///< functions. others::iterative_tree_traversals::Node *root = binaryTree.createNewNode(1); root->left = binaryTree.createNewNode(2); root->right = binaryTree.createNewNode(3); root->left->left = binaryTree.createNewNode(4); root->left->right = binaryTree.createNewNode(5); - std::cout<< "\n| Tests for positive data value |"<< std::endl; + std::cout << "\n| Tests for positive data value |" << std::endl; test1(binaryTree, root); // run preorder-iterative test - std::cout<< "\nPre-order test Passed!"<< std::endl; + std::cout << "\nPre-order test Passed!" << std::endl; test2(binaryTree, root); // run postorder-iterative test - std::cout<< "\nPost-order test Passed!"<< std::endl; + std::cout << "\nPost-order test Passed!" << std::endl; test3(binaryTree, root); // run inorder-iterative test - std::cout<< "\nIn-order test Passed!"<< std::endl; + std::cout << "\nIn-order test Passed!" << std::endl; // Modifying tree for negative values. root->data = -1; @@ -348,15 +386,15 @@ int main() { root->left->left->data = -4; root->left->right->data = -5; - std::cout<< "\n| Tests for negative data values |"<< std::endl; + std::cout << "\n| Tests for negative data values |" << std::endl; test4(binaryTree, root); // run preorder-iterative test on negative values - std::cout<< "\nPre-order test on-negative value Passed!"<< std::endl; + std::cout << "\nPre-order test on-negative value Passed!" << std::endl; test5(binaryTree, root); // run postorder-iterative test on negative values - std::cout<< "\nPost-order test on-negative value Passed!"<< std::endl; + std::cout << "\nPost-order test on-negative value Passed!" << std::endl; test6(binaryTree, root); // run inorder-iterative test on negative values - std::cout<< "\nIn-order test on-negative value Passed!"<< std::endl; + std::cout << "\nIn-order test on-negative value Passed!" << std::endl; return 0; } diff --git a/sorting/random_pivot_quick_sort.cpp b/sorting/random_pivot_quick_sort.cpp index 97892368f..a6ed19f12 100644 --- a/sorting/random_pivot_quick_sort.cpp +++ b/sorting/random_pivot_quick_sort.cpp @@ -1,44 +1,55 @@ /** * @file - * @brief Implementation of the [Random Pivot Quick Sort](https://www.sanfoundry.com/cpp-program-implement-quick-sort-using-randomisation) algorithm. + * @brief Implementation of the [Random Pivot Quick + * Sort](https://www.sanfoundry.com/cpp-program-implement-quick-sort-using-randomisation) + * algorithm. * @details - * * A random pivot quick sort algorithm is pretty much same as quick sort with a difference of having a logic of - * selecting next pivot element from the input array. - * * Where in quick sort is fast, but still can give you the time complexity of O(n^2) in worst case. - * * To avoid hitting the time complexity of O(n^2), we use the logic of randomize the selection process of pivot - * element. + * * A random pivot quick sort algorithm is pretty much same as quick + * sort with a difference of having a logic of selecting next pivot element from + * the input array. + * * Where in quick sort is fast, but still can give you the time + * complexity of O(n^2) in worst case. + * * To avoid hitting the time complexity of O(n^2), we use the logic + * of randomize the selection process of pivot element. * * ### Logic - * * The logic is pretty simple, the only change is in the partitioning algorithm, which is selecting the - * pivot element. - * * Instead of selecting the last or the first element from array for pivot we use a random index to select - * pivot element. - * * This avoids hitting the O(n^2) time complexity in practical use cases. + * * The logic is pretty simple, the only change is in the + * partitioning algorithm, which is selecting the pivot element. + * * Instead of selecting the last or the first element from array + * for pivot we use a random index to select pivot element. + * * This avoids hitting the O(n^2) time complexity in practical + * use cases. * * ### Partition Logic - * * Partitions are done such as numbers lower than the "pivot" element is arranged on the left side of the "pivot", - * and number larger than the "pivot" element are arranged on the right part of the array. + * * Partitions are done such as numbers lower than the "pivot" + * element is arranged on the left side of the "pivot", and number larger than + * the "pivot" element are arranged on the right part of the array. * * ### Algorithm - * * Select the pivot element randomly using getRandomIndex() function from this namespace. - * * Initialize the pInd (partition index) from the start of the array. - * * Loop through the array from start to less than end. (from start to < end). - * (Inside the loop) :- - * * Check if the current element (arr[i]) is less than the pivot element in each iteration. - * * If current element in the iteration is less than the pivot element, - * then swap the elements at current index (i) and partition index (pInd) and increment the partition index by one. - * * At the end of the loop, swap the pivot element with partition index element. + * * Select the pivot element randomly using getRandomIndex() function + * from this namespace. + * * Initialize the pInd (partition index) from the start of the + * array. + * * Loop through the array from start to less than end. (from start + * to < end). (Inside the loop) :- + * * Check if the current element (arr[i]) is less than the + * pivot element in each iteration. + * * If current element in the iteration is less than the + * pivot element, then swap the elements at current index (i) and partition + * index (pInd) and increment the partition index by one. + * * At the end of the loop, swap the pivot element with partition + * index element. * * Return the partition index from the function. * * @author [Nitin Sharma](https://github.com/foo290) */ -#include /// for IO operations -#include /// for initializing random number generator -#include /// for assert -#include /// for std::is_sorted(), std::swap() +#include /// for std::is_sorted(), std::swap() #include /// for std::array -#include /// for returning multiple values form a function at once +#include /// for assert +#include /// for initializing random number generator +#include /// for IO operations +#include /// for returning multiple values form a function at once /** * @namespace sorting @@ -46,111 +57,120 @@ */ namespace sorting { /** - * @brief Functions for the [Random Pivot Quick Sort](https://www.sanfoundry.com/cpp-program-implement-quick-sort-using-randomisation) implementation + * @brief Functions for the [Random Pivot Quick + * Sort](https://www.sanfoundry.com/cpp-program-implement-quick-sort-using-randomisation) + * implementation * @namespace random_pivot_quick_sort */ - namespace random_pivot_quick_sort { - /** - * @brief Utility function to print the array - * @tparam T size of the array - * @param arr array used to print its content - * @returns void - * */ - template - void showArray(std::array arr) { - for (int64_t i = 0; i < arr.size(); i++) { - std::cout << arr[i] << " "; - } - std::cout << std::endl; +namespace random_pivot_quick_sort { +/** + * @brief Utility function to print the array + * @tparam T size of the array + * @param arr array used to print its content + * @returns void + * */ +template +void showArray(std::array arr) { + for (int64_t i = 0; i < arr.size(); i++) { + std::cout << arr[i] << " "; + } + std::cout << std::endl; +} + +/** + * @brief Takes the start and end indices of an array and returns a random + * int64_teger between the range of those two for selecting pivot element. + * + * @param start The starting index. + * @param end The ending index. + * @returns int64_t A random number between start and end index. + * */ +int64_t getRandomIndex(int64_t start, int64_t end) { + srand(time(nullptr)); // Initialize random number generator. + int64_t randomPivotIndex = start + rand() % (end - start + 1); + return randomPivotIndex; +} + +/** + * @brief A partition function which handles the partition logic of quick sort. + * @tparam size size of the array to be passed as argument. + * @param start The start index of the passed array + * @param end The ending index of the passed array + * @returns std::tuple> A tuple of pivot + * index and pivot sorted array. + */ +template +std::tuple> partition( + std::array arr, int64_t start, int64_t end) { + int64_t pivot = arr[end]; // Randomly selected element will be here from + // caller function (quickSortRP()). + int64_t pInd = start; + + for (int64_t i = start; i < end; i++) { + if (arr[i] <= pivot) { + std::swap(arr[i], arr[pInd]); // swapping the elements from current + // index to pInd. + pInd++; } + } + std::swap(arr[pInd], + arr[end]); // swapping the pivot element to its sorted position + return std::make_tuple(pInd, arr); +} - /** - * @brief Takes the start and end indices of an array and returns a random int64_teger between the range of those two - * for selecting pivot element. - * - * @param start The starting index. - * @param end The ending index. - * @returns int64_t A random number between start and end index. - * */ - int64_t getRandomIndex(int64_t start, int64_t end) { - srand(time(nullptr)); // Initialize random number generator. - int64_t randomPivotIndex = start + rand() % (end - start + 1); - return randomPivotIndex; - } - - /** - * @brief A partition function which handles the partition logic of quick sort. - * @tparam size size of the array to be passed as argument. - * @param start The start index of the passed array - * @param end The ending index of the passed array - * @returns std::tuple> A tuple of pivot index and pivot sorted array. - */ - template - std::tuple> partition(std::array arr, int64_t start, int64_t end) { - - int64_t pivot = arr[end]; // Randomly selected element will be here from caller function (quickSortRP()). - int64_t pInd = start; - - for (int64_t i = start; i < end; i++) { - if (arr[i] <= pivot) { - std::swap(arr[i], arr[pInd]); // swapping the elements from current index to pInd. - pInd++; - } - } - std::swap(arr[pInd], arr[end]); // swapping the pivot element to its sorted position - return std::make_tuple(pInd, arr); - } - - /** - * @brief Random pivot quick sort function. This function is the starting point of the algorithm. - * @tparam size size of the array to be passed as argument. - * @param start The start index of the passed array - * @param end The ending index of the passed array - * @returns std::array A fully sorted array in ascending order. - */ - template - std::array quickSortRP(std::array arr, int64_t start, int64_t end) { - if (start < end) { - - int64_t randomIndex = getRandomIndex(start, end); - - // switching the pivot with right most bound. - std::swap(arr[end], arr[randomIndex]); - - int64_t pivotIndex = 0; - // getting pivot index and pivot sorted array. - std::tie(pivotIndex, arr) = partition(arr, start, end); - - // Recursively calling - std::array rightSortingLeft = quickSortRP(arr, start, pivotIndex - 1); - std::array full_sorted = quickSortRP(rightSortingLeft, pivotIndex + 1, end); - arr = full_sorted; - } - return arr; - } - - /** - * @brief A function utility to generate unsorted array of given size and range. - * @tparam size Size of the output array. - * @param from Stating of the range. - * @param to Ending of the range. - * @returns std::array Unsorted array of specified size. - * */ - template - std::array generateUnsortedArray(int64_t from, int64_t to) { - srand(time(nullptr)); - std::array unsortedArray{}; - assert(from < to); - int64_t i = 0; - while (i < size) { - int64_t randomNum = from + rand() % (to - from + 1); - if (randomNum) { - unsortedArray[i] = randomNum; - i++; - } - } - return unsortedArray; +/** + * @brief Random pivot quick sort function. This function is the starting point + * of the algorithm. + * @tparam size size of the array to be passed as argument. + * @param start The start index of the passed array + * @param end The ending index of the passed array + * @returns std::array A fully sorted array in ascending order. + */ +template +std::array quickSortRP(std::array arr, + int64_t start, int64_t end) { + if (start < end) { + int64_t randomIndex = getRandomIndex(start, end); + + // switching the pivot with right most bound. + std::swap(arr[end], arr[randomIndex]); + + int64_t pivotIndex = 0; + // getting pivot index and pivot sorted array. + std::tie(pivotIndex, arr) = partition(arr, start, end); + + // Recursively calling + std::array rightSortingLeft = + quickSortRP(arr, start, pivotIndex - 1); + std::array full_sorted = + quickSortRP(rightSortingLeft, pivotIndex + 1, end); + arr = full_sorted; + } + return arr; +} + +/** + * @brief A function utility to generate unsorted array of given size and range. + * @tparam size Size of the output array. + * @param from Stating of the range. + * @param to Ending of the range. + * @returns std::array Unsorted array of specified size. + * */ +template +std::array generateUnsortedArray(int64_t from, int64_t to) { + srand(time(nullptr)); + std::array unsortedArray{}; + assert(from < to); + int64_t i = 0; + while (i < size) { + int64_t randomNum = from + rand() % (to - from + 1); + if (randomNum) { + unsortedArray[i] = randomNum; + i++; } + } + return unsortedArray; +} } // namespace random_pivot_quick_sort } // namespace sorting @@ -159,23 +179,23 @@ namespace sorting { * @brief a class containing the necessary test cases */ class TestCases { -private: + private: /** - * @brief A function to print64_t given message on console. - * @tparam T Type of the given message. - * @returns void - * */ - template + * @brief A function to print64_t given message on console. + * @tparam T Type of the given message. + * @returns void + * */ + template void log(T msg) { // It's just to avoid writing cout and endl std::cout << "[TESTS] : ---> " << msg << std::endl; } -public: + public: /** - * @brief Executes test cases - * @returns void - * */ + * @brief Executes test cases + * @returns void + * */ void runTests() { log("Running Tests..."); @@ -188,24 +208,25 @@ public: } /** - * @brief A test case with single input - * @returns void - * */ + * @brief A test case with single input + * @returns void + * */ void testCase_1() { const int64_t inputSize = 1; - log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); + log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + "~"); log("This is test case 1 for Random Pivot Quick Sort Algorithm : "); log("Description:"); log(" EDGE CASE : Only contains one element"); - std::array unsorted_arr{2}; + std::array unsorted_arr{2}; int64_t start = 0; - int64_t end = unsorted_arr.size() - 1; // length - 1 + int64_t end = unsorted_arr.size() - 1; // length - 1 log("Running algorithm of data of length 50 ..."); - std::array sorted_arr = sorting::random_pivot_quick_sort::quickSortRP( - unsorted_arr, start, end - ); + std::array sorted_arr = + sorting::random_pivot_quick_sort::quickSortRP(unsorted_arr, start, + end); log("Algorithm finished!"); log("Checking assert expression..."); @@ -213,28 +234,32 @@ public: log("Assertion check passed!"); log("[PASS] : TEST CASE 1 PASS!"); - log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); + log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + "~"); } /** - * @brief A test case with input array of length 500 - * @returns void - * */ + * @brief A test case with input array of length 500 + * @returns void + * */ void testCase_2() { const int64_t inputSize = 500; - log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); + log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + "~"); log("Description:"); log(" BIG INPUT : Contains 500 elements and repeated elements"); log("This is test case 2 for Random Pivot Quick Sort Algorithm : "); - std::array unsorted_arr = sorting::random_pivot_quick_sort::generateUnsortedArray(1, 10000); + std::array unsorted_arr = + sorting::random_pivot_quick_sort::generateUnsortedArray( + 1, 10000); int64_t start = 0; - int64_t end = unsorted_arr.size() - 1; // length - 1 + int64_t end = unsorted_arr.size() - 1; // length - 1 log("Running algorithm of data of length 500 ..."); - std::array sorted_arr = sorting::random_pivot_quick_sort::quickSortRP( - unsorted_arr, start, end - ); + std::array sorted_arr = + sorting::random_pivot_quick_sort::quickSortRP(unsorted_arr, start, + end); log("Algorithm finished!"); log("Checking assert expression..."); @@ -242,28 +267,32 @@ public: log("Assertion check passed!"); log("[PASS] : TEST CASE 2 PASS!"); - log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); + log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + "~"); } /** - * @brief A test case with array of length 1000. - * @returns void - * */ + * @brief A test case with array of length 1000. + * @returns void + * */ void testCase_3() { const int64_t inputSize = 1000; - log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); + log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + "~"); log("This is test case 3 for Random Pivot Quick Sort Algorithm : "); log("Description:"); log(" LARGE INPUT : Contains 1000 elements and repeated elements"); - std::array unsorted_arr = sorting::random_pivot_quick_sort::generateUnsortedArray(1, 10000); + std::array unsorted_arr = + sorting::random_pivot_quick_sort::generateUnsortedArray( + 1, 10000); int64_t start = 0; - int64_t end = unsorted_arr.size() - 1; // length - 1 + int64_t end = unsorted_arr.size() - 1; // length - 1 log("Running algorithm..."); - std::array sorted_arr = sorting::random_pivot_quick_sort::quickSortRP( - unsorted_arr, start, end - ); + std::array sorted_arr = + sorting::random_pivot_quick_sort::quickSortRP(unsorted_arr, start, + end); log("Algorithm finished!"); log("Checking assert expression..."); @@ -271,11 +300,11 @@ public: log("Assertion check passed!"); log("[PASS] : TEST CASE 3 PASS!"); - log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); + log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + "~"); } }; - /** * @brief Self-test implementations * @returns void @@ -292,17 +321,18 @@ static void test() { * @returns 0 on exit */ int main(int argc, char *argv[]) { - test(); // Executes various test cases. + test(); // Executes various test cases. const int64_t inputSize = 10; - std::array unsorted_array = sorting::random_pivot_quick_sort::generateUnsortedArray(50, 1000); + std::array unsorted_array = + sorting::random_pivot_quick_sort::generateUnsortedArray( + 50, 1000); std::cout << "Unsorted array is : " << std::endl; sorting::random_pivot_quick_sort::showArray(unsorted_array); - std::array sorted_array = sorting::random_pivot_quick_sort::quickSortRP( - unsorted_array, 0, - unsorted_array.size() - 1 - ); + std::array sorted_array = + sorting::random_pivot_quick_sort::quickSortRP( + unsorted_array, 0, unsorted_array.size() - 1); std::cout << "Sorted array is : " << std::endl; sorting::random_pivot_quick_sort::showArray(sorted_array); return 0;