From 8a738b07f3b7c1bda97bee8f6a6e64d35dc9fea2 Mon Sep 17 00:00:00 2001 From: Ayaan Khan Date: Wed, 1 Jul 2020 03:28:12 +0530 Subject: [PATCH 01/30] resolve merge conflicts --- machine_learning/ordinary_least_squares_regressor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/machine_learning/ordinary_least_squares_regressor.cpp b/machine_learning/ordinary_least_squares_regressor.cpp index 0c865761b..a693fdd6c 100644 --- a/machine_learning/ordinary_least_squares_regressor.cpp +++ b/machine_learning/ordinary_least_squares_regressor.cpp @@ -436,7 +436,7 @@ int main() { std::vector Y(N); std::cout - << "Enter training data. Per sample, provide features and one output." + << "Enter training data. Per sample, provide features ad one output." << std::endl; for (size_t rows = 0; rows < N; rows++) { From 4a5310c7ad25cd263559bf285c042e8e4fe609ee Mon Sep 17 00:00:00 2001 From: fedom Date: Tue, 1 Dec 2020 13:46:49 +0800 Subject: [PATCH 02/30] feat: add 2-3-4-tree implment (#1366) * feat: add 2-3-4 tree implment * updating DIRECTORY.md * docs: fix format issue of tab&space * fix: fix code format issues * fix: convert printf() to std::cout * fix: fix some clang-tidy warnings * fix: fix clang-tidy warnings of memory owning * fix: remove use of std::make_unique which is not support by c++11 * docs: improve documents * fix: replace fprint with ofstream, and improve docs * docs: improve docs for including header file * docs: improve file doces * fix: convert item type to int64_t, convert node item count type to int8_t * refactor: Apply suggestions from code review Add namespaces Co-authored-by: David Leal * docs: remove obsolete comments Co-authored-by: liuhuan Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: David Leal --- DIRECTORY.md | 1 + data_structures/tree_234.cpp | 1306 ++++++++++++++++++++++++++++++++++ 2 files changed, 1307 insertions(+) create mode 100644 data_structures/tree_234.cpp diff --git a/DIRECTORY.md b/DIRECTORY.md index a53ccf97e..3b0654975 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -46,6 +46,7 @@ * [Test Stack](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/test_stack.cpp) * [Test Stack Students](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/test_stack_students.cpp) * [Tree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/tree.cpp) + * [Tree 234](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/tree_234.cpp) * [Trie Modern](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/trie_modern.cpp) * [Trie Tree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/trie_tree.cpp) diff --git a/data_structures/tree_234.cpp b/data_structures/tree_234.cpp new file mode 100644 index 000000000..46de03bfa --- /dev/null +++ b/data_structures/tree_234.cpp @@ -0,0 +1,1306 @@ +/** + * @file + * @brief A demo 2-3-4 tree implementation + * @details + * 2–3–4 tree is a self-balancing data structure that is an isometry of + * red–black trees. Though we seldom use them in practice, we study them + * to understand the theory behind Red-Black tree. Please read following + * links for more infomation. + * [2–3–4 tree](https://en.wikipedia.org/wiki/2%E2%80%933%E2%80%934_tree) + * [2-3-4 Trees: A Visual +Introduction](https://www.educative.io/page/5689413791121408/80001) + * We Only implement some basic and complicated operations in this demo. + * Other operations should be easy to be added. + * @author [liuhuan](https://github.com/fedom) + */ +#include /// for std::array +#include /// for assert +#include /// for std::ofstream +#include /// for std::cout +#include /// for std::unique_ptr +#include /// for std::queue +#include /// for std::to_string + +/** + * @namespace data_structures + * @brief Algorithms with data structures + */ +namespace data_structures { +/** + * @namespace tree_234 + * @brief Functions for [2–3–4 tree](https://en.wikipedia.org/wiki/2%E2%80%933%E2%80%934_tree) + */ +namespace tree_234 { +/** @brief 2-3-4 tree node class */ +class Node { + public: + /** + * @brief Node constructor + * @param item the first value we insert to the node + */ + explicit Node(int64_t item) + : count(1), + items({{item, 0, 0}}), + children({{nullptr, nullptr, nullptr, nullptr}}) {} + + /** + * @brief Get the item count that current saved in the node + * @return item count + */ + int8_t GetCount() { return count; } + + /** + * @brief Set the item count of the node + * + * This is only used when we spliting and merging node where we need to do + * some raw operation manually. In common inserting and removing operation + * the count is maintained automatically. + * + * @param c the count to set + */ + void SetCount(int8_t c) { count = c; } + + /** + * @brief Check if node is a leaf + * @return true if node is leaf, false otherwise + */ + bool IsLeaf() { return children[0] == nullptr; } + + /** + * @brief Check if node is a full (4-node) + * @return true if node is full (4-node), false otherwise + */ + bool IsFull() { return count == 3; } + + /** + * @brief Check if node is a 2-node + * @return true if node is 2-node, otherwise false + */ + bool Is2Node() { return count == 1; } + + /** @brief Check if node is a 3-node or 4-node, this is useful when we + * delete item from 2-3-4 tree + * @return true if node is 3-node or 4-node, false otherwise + */ + bool Is34Node() { return count == 2 || count == 3; } + + /** + * @brief Check if item is in the node + * @param item item to check + * @return true if item in the node, otherwise false + */ + bool Contains(int64_t item) { + for (int8_t i = 0; i < count; i++) { + if (item == items[i]) { + return true; + } + } + return false; + } + + /** + * @brief Get the index of the item in the node, 0-based + * @param item item to check + * @return 0-based index of the item in the node, if not in the node, -1 is + * returned + */ + int8_t GetItemIndex(int64_t item) { + for (int8_t i = 0; i < count; i++) { + if (items[i] == item) { + return i; + } + } + return -1; + } + + /** + * @brief Get max item (rightmost) in the current node + * @return max item + */ + int64_t GetMaxItem() { return items[count - 1]; } + + /** + * @brief get min item (leftmost) in the current node + * @return min item + */ + int64_t GetMinItem() { return items[0]; } + + /** + * @brief Get item of the \index index + * @param index the item index to get + * @return the item + */ + int64_t GetItem(int8_t index) { return items[index]; } + + /** + * @brief Set item value at position of index + * @param index the index of the item to set + * @param new_item item value + */ + void SetItem(int8_t index, int64_t new_item) { + assert(index >= 0 && index <= 2); + + items[index] = new_item; + } + + /** + * @brief Insert item to the proper position of the node and return the + * position index. + * + * This is a helper function we use during insertion. Please mind that when + * insert a item, we aslo need to take care of two child pointers. One is + * the original child pointer at the insertion position. It can be placed as + * new item's either left child or right child. And the other is the new + * child that should be added. For our dedicated situation here, we choose + * to use the original child as the new item's left child, and add a null + * pointer to its right child. So after use the function, please update + * these two children pointer manually. + * + * @param item value to be inserted to the node + * @return index where item is inserted, caller can use this + * index to update its left and right child + */ + int InsertItem(int item) { + assert(!IsFull()); + + if (Contains(item)) { + return -1; + } + + int8_t i = 0; + for (i = 0; i < count; i++) { + if (items[i] > item) { + break; + } + } + + InsertItemByIndex(i, item, nullptr, true); + return i; + } + + /** + * @brief Insert a value to the index position + * @param index index where to insert item + * @param item value to insert + * @param with_child new added child pointer + * @param to_left true indicate adding with_child to new item's left child, + * otherwise to right child + */ + void InsertItemByIndex(int8_t index, int64_t item, Node *with_child, + bool to_left = true) { + assert(count < 3 && index >= 0 && index < 3); + + for (int8_t i = count - 1; i >= index; i--) { + items[i + 1] = items[i]; + } + + items[index] = item; + + int8_t start_index = to_left ? index : index + 1; + + for (int8_t i = count; i >= start_index; i--) { + children[i + 1] = children[i]; + } + + children[start_index] = with_child; + + count++; + } + + /** + * @brief Insert a value to the index position + * @param index index of the item to remove + * @param keep_left which child of the item to keep, true keep the left + * child, false keep the right child + * @return the removed child pointer + */ + Node *RemoveItemByIndex(int8_t index, bool keep_left) { + assert(index >= 0 && index < count); + Node *removed_child = keep_left ? children[index + 1] : children[index]; + for (int8_t i = index; i < count - 1; i++) { + items[i] = items[i + 1]; + } + + for (int8_t i = keep_left ? index + 1 : index; i < count; i++) { + children[i] = children[i + 1]; + } + + count--; + return removed_child; + } + + /** + * @brief Get the child's index of the children array + * @param child child pointer of which to get the index + * @return the index of child + */ + int8_t GetChildIndex(Node *child) { + for (int8_t i = 0; i < count + 1; i++) { + if (children[i] == child) { + return i; + } + } + + return -1; + } + + /** + * @brief Get the child pointer at position of index + * @param index index of child to get + * @return the child pointer + */ + Node *GetChild(int8_t index) { return children[index]; } + + /** + * @brief Set child pointer to the position of index + * @param index children index + * @param child pointer to set + */ + void SetChild(int8_t index, Node *child) { children[index] = child; } + + /** + * @brief Get rightmose child of the current node + * @return the rightmost child + */ + Node *GetRightmostChild() { return children[count]; } + + /** + * @brief Get leftmose child of the current node + * @return the leftmost child + */ + Node *GetLeftmostChild() { return children[0]; } + + /** + * @brief Get left child of item at item_index + * @param item_index index of the item whose left child to be get + * @return left child of items[index]'s + */ + Node *GetItemLeftChild(int8_t item_index) { + if (item_index < 0 || item_index > count - 1) { + return nullptr; + } + + return children[item_index]; + } + + /** + * @brief Get right child of item at item_index + * @param item_index index of the item whose right child to be get + * @return right child of items[index]'s + */ + Node *GetItemRightChild(int8_t item_index) { + if (item_index < 0 || item_index > count - 1) { + return nullptr; + } + + return children[item_index + 1]; + } + + /** + * @brief Get next node which is possibly contains item + * @param item item to search + * @return the next node that possibly contains item + */ + Node *GetNextPossibleChild(int64_t item) { + int i = 0; + for (i = 0; i < count; i++) { + if (items[i] > item) { + break; + } + } + return children[i]; + } + + private: + std::array items; ///< store items + + std::array children; ///< store the children pointers + + int8_t count = 0; ///< track the current item count +}; + +/** @brief 2-3-4 tree class */ +class Tree234 { + public: + Tree234() = default; + Tree234(const Tree234 &) = delete; + Tree234(const Tree234 &&) = delete; + Tree234 &operator=(const Tree234 &) = delete; + Tree234 &operator=(const Tree234 &&) = delete; + + ~Tree234(); + + /** + * @brief Insert item to tree + * @param item item to insert + */ + void Insert(int64_t item); + + /** + * @brief Remove item from tree + * @param item item to remove + * @return true if item found and removed, false otherwise + */ + bool Remove(int64_t item); + + /** @brief In-order traverse */ + void Traverse(); + + /** + * @brief Print tree into a dot file + * @param file_name output file name, if nullptr then use "out.dot" as + * default + */ + void Print(const char *file_name = nullptr); + + private: + /** + * @brief A insert implementation of pre-split + * @param item item to insert + */ + void InsertPreSplit(int64_t item); + + /** + * @brief A insert implementation of post-merge + * @param item item to insert + */ + void InsertPostMerge(int64_t item); + + /** + * @brief A helper function used by post-merge insert + * @param tree tree where to insert item + * @param item item to insert + * @return the node that split as the parent when overflow happen + */ + Node *Insert(Node *tree, int64_t item); + + /** + * @brief A helper function used during post-merge insert + * + * When the inserting leads to overflow, it will split the node to 1 parent + * and 2 children. The parent will be merged to its origin parent after + * that. This is the function to complete this task. So the param node is + * always a 2-node. + * + * @param dst_node the target node we will merge node to, can be type of + * 2-node, 3-node or 4-node + * @param node the source node we will merge from, type must be 2-node + * @return overflow node of this level + */ + Node *MergeNode(Node *dst_node, Node *node); + + /** + * @brief Merge node to a not-full target node + * + * Since the target node is not-full, no overflow will happen. So we have + * nothing to return. + * + * @param dst_node the target not-full node, that is the type is either + * 2-node or 3-node, but not 4-node + * @param node the source node we will merge from, type must be 2-node + */ + void MergeNodeNotFull(Node *dst_node, Node *node); + + /** + * @brief Split a 4-node to 1 parent and 2 children, and return the parent + * node + * @param node the node to split, it must be a 4-node + * @return split parent node + */ + Node *SplitNode(Node *node); + + /** + * @brief Get the max item of the tree + * @param tree the tree we will get item from + * @return max item of the tree + */ + int64_t GetTreeMaxItem(Node *tree); + + /** + * @brief Get the min item of the tree + * @param tree the tree we will get item from + * @return min item of the tree + */ + int64_t GetTreeMinItem(Node *tree); + + /** + * @brief A handy function to try if we can do a left rotate to the target + * node + * + * Given two node, the parent and the target child, the left rotate + * operation is uniquely identified. The source node must be the right + * sibling of the target child. The operation can be successfully done if + * the to_child has a right sibling and its right sibling is not 2-node. + * + * @param parent the parent node in this left rotate operation + * @param to_child the target child of this left rotate operation. In our + * case, this node is always 2-node + * @return true if we successfully do the rotate. false if the + * requirements are not fulfilled. + */ + bool TryLeftRotate(Node *parent, Node *to_child); + + /** + * @brief A handy function to try if we can do a right rotate to the target + * node + * + * Given two node, the parent and the target child, the right rotate + * operation is uniquely identified. The source node must be the left + * sibling of the target child. The operation can be successfully done if + * the to_child has a left sibling and its left sibling is not 2-node. + * + * @param parent the parent node in this right rotate operation + * @param to_child the target child of this right rotate operation. In our + * case, it is always 2-node + * @return true if we successfully do the rotate. false if the + * requirements are not fulfilled. + */ + bool TryRightRotate(Node *parent, Node *to_child); + + /** + * @brief Do the actual right rotate operation + * + * Given parent node, and the pivot item index, the right rotate operation + * is uniquely identified. The function assume the requirements are + * fulfilled and won't do any extra check. This function is call by + * TryRightRotate(), and the condition checking should be done before call + * it. + * + * @param parent the parent node in this right rotate operation + * @param index the pivot item index of this right rotate operation. + */ + void RightRotate(Node *parent, int8_t index); + + /** + * @brief Do the actual left rotate operation + * + * Given parent node, and the pivot item index, the left rotate operation is + * uniquely identified. The function assume the requirements are fulfilled + * and won't do any extra check. This function is call by TryLeftRotate(), + * and the condition checking should be done before call it. + * + * @param parent the parent node in this right rotate operation + * @param index the pivot item index of this right rotate operation. + */ + void LeftRotate(Node *parent, int8_t index); + + /** + * @brief Main function implement the pre-merge remove operation + * @param node the tree to remove item from + * @param item item to remove + * @return true if remove success, false otherwise + * */ + bool RemovePreMerge(Node *node, int64_t item); + + /** + * @brief Merge the item at index of the parent node, and its left and right + * child + * + * the left and right child node must be 2-node. The 3 items will be merged + * into a 4-node. In our case the parent can be a 2-node iff it is the root. + * Otherwise, it must be 3-node or 4-node. + * + * @param parent the parent node in the merging operation + * @param index the item index of the parent node that involved in the + * merging + * @return the merged 4-node + */ + Node *Merge(Node *parent, int8_t index); + + /** + * @brief Recursive release the tree + * @param tree root node of the tree to delete + */ + void DeleteNode(Node *tree); + + /** + * @brief In-order traverse the tree, print items + * @param tree tree to traverse + */ + void Traverse(Node *tree); + + /** + * @brief Print the tree to a dot file. You can convert it to picture with + * graphviz + * @param ofs output file stream to print to + * @param node current node to print + * @param parent_index current node's parent node index, this is used to + * draw the link from parent to current node + * @param index current node's index of level order which is used to name + * the node in dot file + * @param parent_child_index the index that current node in parent's + * children array, range in [0,4), help to locate the start position of the + * link between nodes + */ + void PrintNode(std::ofstream &ofs, Node *node, int64_t parent_index, + int64_t index, int8_t parent_child_index); + + Node *root_{nullptr}; ///< root node of the tree +}; + +Tree234::~Tree234() { DeleteNode(root_); } + +/** + * @brief Recursive release the tree + * @param tree root node of the tree to delete + */ +void Tree234::DeleteNode(Node *tree) { + if (!tree) { + return; + } + for (int8_t i = 0; i <= tree->GetCount(); i++) { + DeleteNode(tree->GetChild(i)); + } + + delete tree; +} + +/** + * @brief In-order traverse the tree, print items + * @param tree tree to traverse + */ +void Tree234::Traverse() { + Traverse(root_); + std::cout << std::endl; +} + +void Tree234::Traverse(Node *node) { + if (!node) { + return; + } + + int8_t i = 0; + for (i = 0; i < node->GetCount(); i++) { + Traverse(node->GetChild(i)); + std::cout << node->GetItem(i) << ", "; + } + + Traverse(node->GetChild(i)); +} + +/** + * @brief A insert implementation of pre-split + * @param item item to insert + */ +void Tree234::InsertPreSplit(int64_t item) { + if (!root_) { + root_ = new Node(item); + return; + } + + Node *parent = nullptr; + Node *node = root_; + + while (true) { + if (!node) { + std::unique_ptr tmp(new Node(item)); + MergeNodeNotFull(parent, tmp.get()); + return; + } + + if (node->Contains(item)) { + return; + } + + if (node->IsFull()) { + node = SplitNode(node); + + Node *cur_node = nullptr; + + if (item < node->GetItem(0)) { + cur_node = node->GetChild(0); + } else { + cur_node = node->GetChild(1); + } + + if (!parent) { + // for the root node parent is nullptr, we simply assign the + // split parent to root_ + root_ = node; + } else { + // merge the split parent to its origin parent + MergeNodeNotFull(parent, node); + } + + node = cur_node; + } + + parent = node; + node = parent->GetNextPossibleChild(item); + } +} + +/** + * @brief A insert implementation of post-merge + * @param item item to insert + */ +void Tree234::InsertPostMerge(int64_t item) { + if (!root_) { + root_ = new Node(item); + return; + } + + Node *split_node = Insert(root_, item); + + // if root has split, then update root_ + if (split_node) { + root_ = split_node; + } +} + +/** + * @brief Insert item to tree + * @param item item to insert + */ +void Tree234::Insert(int64_t item) { InsertPreSplit(item); } + +/** + * @brief A helper function used by post-merge insert + * @param tree tree where to insert item + * @param item item to insert + * @return the node that split as the parent when overflow happen + */ +Node *Tree234::Insert(Node *tree, int64_t item) { + assert(tree != nullptr); + + std::unique_ptr split_node; + + if (tree->Contains(item)) { + // return nullptr indicate current node not overflow + return nullptr; + } + + Node *next_node = tree->GetNextPossibleChild(item); + if (next_node) { + split_node.reset(Insert(next_node, item)); + } else { + split_node.reset(new Node(item)); + } + + if (split_node) { + return MergeNode(tree, split_node.get()); + } + + return nullptr; +} + +/** + * @brief A helper function used during post-merge insert + * + * When the inserting leads to overflow, it will split the node to 1 parent + * and 2 children. The parent will be merged to its origin parent after + * that. This is the function to complete this task. So the param node is + * always a 2-node. + * + * @param dst_node the target node we will merge node to, can be type of + * 2-node, 3-node or 4-node + * @param node the source node we will merge from, type must be 2-node + * @return overflow node of this level + */ +Node *Tree234::MergeNode(Node *dst_node, Node *node) { + assert(dst_node != nullptr && node != nullptr); + + if (!dst_node->IsFull()) { + MergeNodeNotFull(dst_node, node); + return nullptr; + } + + dst_node = SplitNode(dst_node); + + if (node->GetItem(0) < dst_node->GetItem(0)) { + MergeNodeNotFull(dst_node->GetChild(0), node); + + } else { + MergeNodeNotFull(dst_node->GetChild(1), node); + } + + return dst_node; +} + +/** + * @brief Merge node to a not-full target node + * + * Since the target node is not-full, no overflow will happen. So we have + * nothing to return. + * + * @param dst_node the target not-full node, that is the type is either + * 2-node or 3-node, but not 4-node + * @param node the source node we will merge from, type must be 2-node + */ +void Tree234::MergeNodeNotFull(Node *dst_node, Node *node) { + assert(dst_node && node && !dst_node->IsFull() && node->Is2Node()); + + int8_t i = dst_node->InsertItem(node->GetItem(0)); + + dst_node->SetChild(i, node->GetChild(0)); + dst_node->SetChild(i + 1, node->GetChild(1)); +} + +/** + * @brief Split a 4-node to 1 parent and 2 children, and return the parent + * node + * @param node the node to split, it must be a 4-node + * @return split parent node + */ +Node *Tree234::SplitNode(Node *node) { + assert(node->GetCount() == 3); + + Node *left = node; + + Node *right = new Node(node->GetItem(2)); + right->SetChild(0, node->GetChild(2)); + right->SetChild(1, node->GetChild(3)); + + Node *parent = new Node(node->GetItem(1)); + parent->SetChild(0, left); + parent->SetChild(1, right); + + left->SetCount(1); + + return parent; +} + +/** + * @brief A handy function to try if we can do a left rotate to the target + * node + * + * Given two node, the parent and the target child, the left rotate + * operation is uniquely identified. The source node must be the right + * sibling of the target child. The operation can be successfully done if + * the to_child has a right sibling and its right sibling is not 2-node. + * + * @param parent the parent node in this left rotate operation + * @param to_child the target child of this left rotate operation. In our + * case, this node is always 2-node + * @return true if we successfully do the rotate. false if the + * requirements are not fulfilled. + */ +bool Tree234::TryLeftRotate(Node *parent, Node *to_child) { + int to_child_index = parent->GetChildIndex(to_child); + + // child is right most, can not do left rotate to it + if (to_child_index >= parent->GetCount()) { + return false; + } + + Node *right_sibling = parent->GetChild(to_child_index + 1); + + // right sibling is 2-node. can not do left rotate. + if (right_sibling->Is2Node()) { + return false; + } + + LeftRotate(parent, to_child_index); + + return true; +} + +/** + * @brief A handy function to try if we can do a right rotate to the target + * node + * + * Given two node, the parent and the target child, the right rotate + * operation is uniquely identified. The source node must be the left + * sibling of the target child. The operation can be successfully done if + * the to_child has a left sibling and its left sibling is not 2-node. + * + * @param parent the parent node in this right rotate operation + * @param to_child the target child of this right rotate operation. In our + * case, it is always 2-node + * @return true if we successfully do the rotate. false if the + * requirements are not fulfilled. + */ +bool Tree234::TryRightRotate(Node *parent, Node *to_child) { + int8_t to_child_index = parent->GetChildIndex(to_child); + + // child is left most, can not do right rotate to it + if (to_child_index <= 0) { + return false; + } + + Node *left_sibling = parent->GetChild(to_child_index - 1); + + // right sibling is 2-node. can not do left rotate. + if (left_sibling->Is2Node()) { + return false; + } + + RightRotate(parent, to_child_index - 1); + + return true; +} + +/** + * @brief Do the actual right rotate operation + * + * Given parent node, and the pivot item index, the right rotate operation + * is uniquely identified. The function assume the requirements are + * fulfilled and won't do any extra check. This function is call by + * TryRightRotate(), and the condition checking should be done before call + * it. + * + * @param parent the parent node in this right rotate operation + * @param index the pivot item index of this right rotate operation. + */ +void Tree234::RightRotate(Node *parent, int8_t index) { + Node *left = parent->GetItemLeftChild(index); + Node *right = parent->GetItemRightChild(index); + + assert(left && left->Is34Node()); + assert(right && right->Is2Node()); + + right->InsertItemByIndex(0, parent->GetItem(index), + left->GetRightmostChild(), true); + parent->SetItem(index, left->GetMaxItem()); + left->RemoveItemByIndex(left->GetCount() - 1, true); +} + +/** + * @brief Do the actual left rotate operation + * + * Given parent node, and the pivot item index, the left rotate operation is + * uniquely identified. The function assume the requirements are fulfilled + * and won't do any extra check. This function is call by TryLeftRotate(), + * and the condition checking should be done before call it. + * + * @param parent the parent node in this right rotate operation + * @param index the pivot item index of this right rotate operation. + */ +void Tree234::LeftRotate(Node *parent, int8_t index) { + Node *left = parent->GetItemLeftChild(index); + Node *right = parent->GetItemRightChild(index); + + assert(right && right->Is34Node()); + assert(left && left->Is2Node()); + + left->InsertItemByIndex(left->GetCount(), parent->GetItem(index), + right->GetLeftmostChild(), false); + parent->SetItem(index, right->GetMinItem()); + right->RemoveItemByIndex(0, false); +} + +/** + * @brief Merge the item at index of the parent node, and its left and right + * child + * + * the left and right child node must be 2-node. The 3 items will be merged + * into a 4-node. In our case the parent can be a 2-node iff it is the root. + * Otherwise, it must be 3-node or 4-node. + * + * @param parent the parent node in the merging operation + * @param index the item index of the parent node that involved in the + * merging + * @return the merged 4-node + */ +Node *Tree234::Merge(Node *parent, int8_t index) { + assert(parent); + + // bool is_parent_2node = parent->Is2Node(); + + Node *left_child = parent->GetItemLeftChild(index); + Node *right_child = parent->GetItemRightChild(index); + + assert(left_child->Is2Node() && right_child->Is2Node()); + + int64_t item = parent->GetItem(index); + + // 1. merge parent's item and right child to left child + left_child->SetItem(1, item); + left_child->SetItem(2, right_child->GetItem(0)); + left_child->SetChild(2, right_child->GetChild(0)); + left_child->SetChild(3, right_child->GetChild(1)); + + left_child->SetCount(3); + + // 2. remove the parent's item + parent->RemoveItemByIndex(index, true); + + // 3. delete the unused right child + delete right_child; + + return left_child; +} + +/** + * @brief Remove item from tree + * @param item item to remove + * @return true if item found and removed, false otherwise + */ +bool Tree234::Remove(int64_t item) { return RemovePreMerge(root_, item); } + +/** + * @brief Main function implement the pre-merge remove operation + * @param node the tree to remove item from + * @param item item to remove + * @return true if remove success, false otherwise + */ +bool Tree234::RemovePreMerge(Node *node, int64_t item) { + while (node) { + if (node->IsLeaf()) { + if (node->Contains(item)) { + if (node->Is2Node()) { + // node must be root + delete node; + root_ = nullptr; + } else { + node->RemoveItemByIndex(node->GetItemIndex(item), true); + } + return true; + } + return false; + } + + // node is internal + if (node->Contains(item)) { + int8_t index = node->GetItemIndex(item); + + // Here is important!!! What we do next depend on its children's + // state. Why? + Node *left_child = node->GetItemLeftChild(index); + Node *right_child = node->GetItemRightChild(index); + assert(left_child && right_child); + + if (left_child->Is2Node() && right_child->Is2Node()) { + // both left and right child are 2-node,we should not modify + // current node in this situation. Because we are going to do + // merge with its children which will move target item to next + // layer. so if we replace the item with successor or + // predecessor now, when we do the recursive remove with + // successor or predecessor, we will result in removing the just + // replaced one in the merged node. That's not what we want. + + // we need to convert the child 2-node to 3-node or 4-node + // first. First we try to see if any of them can convert to + // 3-node by rotate. By using rotate we keep the empty house for + // the future insertion which will be more efficient than merge. + // + // | ? | node | ? | + // / | | \ + // / | | \ + // / | | \ + // / | | \ + // / | | \ + // / | | \ + // ? left_child right_child ? + // + + // node must be the root + if (node->Is2Node()) { + // this means we can't avoid merging the target item into + // next layer, and this will cause us do different process + // compared with other cases + Node *new_root = Merge(node, index); + delete root_; + root_ = new_root; + node = root_; + + // now node point to the + continue; + } + + // here means we can avoid merging the target item into next + // layer. So we convert one of its left or right child to 3-node + // and then do the successor or predecessor swap and recursive + // remove the next layer will successor or predecessor. + do { + if (index > 0) { + // left_child has left-sibling, we check if we can do a + // rotate + Node *left_sibling = node->GetItemLeftChild(index - 1); + if (left_sibling->Is34Node()) { + RightRotate(node, index - 1); + break; + } + } + + if (index < node->GetCount() - 1) { + // right_child has right-sibling, we check if we can do + // a rotate + Node *right_sibling = + node->GetItemRightChild(index + 1); + if (right_sibling->Is34Node()) { + LeftRotate(node, index + 1); + break; + } + } + + // we do a merge. We avoid merging the target item, which + // may trigger another merge in the recursion process. + if (index > 0) { + Merge(node, index - 1); + break; + } + + Merge(node, index + 1); + + } while (false); + } + + // refresh the left_child and right_child since they may be invalid + // because of merge + left_child = node->GetItemLeftChild(index); + right_child = node->GetItemRightChild(index); + + if (left_child->Is34Node()) { + int64_t predecessor_item = GetTreeMaxItem(left_child); + node->SetItem(node->GetItemIndex(item), predecessor_item); + + node = left_child; + item = predecessor_item; + continue; + } + + if (right_child->Is34Node()) { + int64_t successor_item = GetTreeMinItem(right_child); + node->SetItem(node->GetItemIndex(item), successor_item); + node = right_child; + item = successor_item; + continue; + } + } + + Node *next_node = node->GetNextPossibleChild(item); + + if (next_node->Is34Node()) { + node = next_node; + continue; + } + + if (TryRightRotate(node, next_node)) { + node = next_node; + continue; + } + + if (TryLeftRotate(node, next_node)) { + node = next_node; + continue; + } + + // get here means both left sibling and right sibling of next_node is + // 2-node, so we do merge + int8_t child_index = node->GetChildIndex(next_node); + if (child_index > 0) { + node = Merge(node, child_index - 1); + } else { + node = Merge(node, child_index); + } + + } // while + + return false; +} + +/** + * @brief Get the max item of the tree + * @param tree the tree we will get item from + * @return max item of the tree + */ +int64_t Tree234::GetTreeMaxItem(Node *tree) { + assert(tree); + int64_t max = 0; + + while (tree) { + max = tree->GetMaxItem(); + tree = tree->GetRightmostChild(); + } + + return max; +} + +/** + * @brief Get the min item of the tree + * @param tree the tree we will get item from + * @return min item of the tree + */ +int64_t Tree234::GetTreeMinItem(Node *tree) { + assert(tree); + int64_t min = 0; + + while (tree) { + min = tree->GetMinItem(); + tree = tree->GetLeftmostChild(); + } + + return min; +} + +/** + * @brief Print tree into a dot file + * @param file_name output file name, if nullptr then use "out.dot" as default + */ +void Tree234::Print(const char *file_name) { + if (!file_name) { + file_name = "out.dot"; + } + + std::ofstream ofs; + + ofs.open(file_name); + if (!ofs) { + std::cout << "create tree dot file failed, " << file_name << std::endl; + return; + } + + ofs << "digraph G {\n"; + ofs << "node [shape=record]\n"; + + int64_t index = 0; + + /** @brief This is a helper structure to do a level order traversal to print + * the tree. */ + struct NodeInfo { + Node *node; ///< tree node + int64_t index; ///< node index of level order that used when draw the + ///< link between child and parent + }; + + std::queue q; + + if (root_) { + // print root node + PrintNode(ofs, root_, -1, index, 0); + + NodeInfo ni{}; + ni.node = root_; + ni.index = index; + + q.push(ni); + + while (!q.empty()) { + NodeInfo node_info = q.front(); + q.pop(); + + assert(node_info.node->GetCount() > 0); + + if (!node_info.node->IsLeaf()) { + if (node_info.node->GetCount() > 0) { + PrintNode(ofs, node_info.node->GetChild(0), node_info.index, + ++index, 0); + ni.node = node_info.node->GetChild(0); + ni.index = index; + q.push(ni); + + PrintNode(ofs, node_info.node->GetChild(1), node_info.index, + ++index, 1); + ni.node = node_info.node->GetChild(1); + ni.index = index; + q.push(ni); + } + + if (node_info.node->GetCount() > 1) { + PrintNode(ofs, node_info.node->GetChild(2), node_info.index, + ++index, 2); + ni.node = node_info.node->GetChild(2); + ni.index = index; + q.push(ni); + } + + if (node_info.node->GetCount() > 2) { + PrintNode(ofs, node_info.node->GetChild(3), node_info.index, + ++index, 3); + ni.node = node_info.node->GetChild(3); + ni.index = index; + q.push(ni); + } + } + } + } + + ofs << "}\n"; + ofs.close(); +} + +/** + * @brief Print the tree to a dot file. You can convert it to picture with + * graphviz + * @param ofs output file stream to print to + * @param node current node to print + * @param parent_index current node's parent node index, this is used to draw + * the link from parent to current node + * @param index current node's index of level order which is used to name the + * node in dot file + * @param parent_child_index the index that current node in parent's children + * array, range in [0,4), help to locate the start position of the link between + * nodes + */ +void Tree234::PrintNode(std::ofstream &ofs, Node *node, int64_t parent_index, + int64_t index, int8_t parent_child_index) { + assert(node); + + switch (node->GetCount()) { + case 1: + ofs << "node_" << index << " [label=\" " << node->GetItem(0) + << "\"]\n"; + break; + case 2: + ofs << "node_" << index << " [label=\" " << node->GetItem(0) + << " | " << node->GetItem(1) << "\"]\n"; + break; + case 3: + ofs << "node_" << index << " [label=\" " << node->GetItem(0) + << " | " << node->GetItem(1) << "| " + << node->GetItem(2) << "\"]\n"; + break; + + default: + break; + } + + // draw the edge + if (parent_index >= 0) { + ofs << "node_" << parent_index << ":f" + << (parent_child_index == 0 ? 0 : parent_child_index - 1) << ":" + << (parent_child_index == 0 ? "sw" : "se") << " -> node_" << index + << "\n"; + } +} +} // namespace tree_234 +} // namespace data_structures + + +/** @brief simple test to insert a given array and delete some item, and print + * the tree*/ +static void test1() { + std::array arr = {3, 1, 5, 4, 2, 9, 10, 8, 7, 6, 16, 13, 14}; + data_structures::tree_234::Tree234 tree; + + for (auto i : arr) { + tree.Insert(i); + } + + // tree.Remove(10); + tree.Remove(5); + tree.Print(); +} + +/** + * @brief simple test to insert continuous number of range [0, n), and print + * the tree + * @param n upper bound of the range number to insert + */ +static void test2(int64_t n) { + data_structures::tree_234::Tree234 tree; + + for (int64_t i = 0; i < n; i++) { + tree.Insert(i); + } + + tree.Traverse(); + tree.Print((std::to_string(n) + ".dot").c_str()); +} + +/** + * @brief Main function + * @param argc commandline argument count (ignored) + * @param argv commandline array of arguments (ignored) + * @returns 0 on exit + */ +int main(int argc, char *argv[]) { + if (argc < 2) { + test1(); // execute 1st test + } else { + test2(std::stoi(argv[1])); // execute 2nd test + } + + return 0; +} From 40eaea4bcc61403ce9fb82db9526323659766686 Mon Sep 17 00:00:00 2001 From: Hashir Hameed Niazi <56411583+HashirGJ8842@users.noreply.github.com> Date: Wed, 2 Dec 2020 18:27:53 +0530 Subject: [PATCH 03/30] feat: Add Saddleback search (#1432) * feat: add saddleback search algorithm * docs: add comments and explaination for saddleback algorithm * test: add test for saddleback algorithm * refactor: use size_t instead of int * refactor: made suggested changes * updating DIRECTORY.md * refactor: made suggested changes * refactor: made suggested changes * refactor: made suggested changes Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 1 + search/saddleback_search.cpp | 106 +++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 search/saddleback_search.cpp diff --git a/DIRECTORY.md b/DIRECTORY.md index 3b0654975..cf1621932 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -243,6 +243,7 @@ * [Jump Search](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/search/jump_search.cpp) * [Linear Search](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/search/linear_search.cpp) * [Median Search](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/search/median_search.cpp) + * [Saddleback Search](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/search/saddleback_search.cpp) * [Ternary Search](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/search/ternary_search.cpp) * [Text Search](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/search/text_search.cpp) diff --git a/search/saddleback_search.cpp b/search/saddleback_search.cpp new file mode 100644 index 000000000..dab5adcf0 --- /dev/null +++ b/search/saddleback_search.cpp @@ -0,0 +1,106 @@ +/** + * @file + * @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. + * + * @author [Hashir Niazi](https://github.com/HashirGJ8842) + */ +#include /// for assert +#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). + */ +namespace saddleback { +/** + * 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 + * @return An std::pair of with row and column populated within it, if the + * 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) { + 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. + 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); + } else if (element > + matrix[left_index] + [right_index]) { // Else if value on this position of + // matrix is less than the element, + // move left. + ++left_index; + } else if (element < + matrix[left_index] + [right_index]) { // Else if value on this position of + // matrix is greater than the + // element, move down. + if(!right_index) + break; + 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. +} +} // namespace saddleback +} // namespace search + +/** + * @brief Test implementations + * @returns void + */ +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}}; + + std::pair not_found = std::make_pair(0, 0); + std::pair test_answer; + // Test 1 + std::pair answer1 = search::saddleback::saddleback(matrix, 123); + assert(not_found == answer1); + // Test 2 + answer1 = search::saddleback::saddleback(matrix, 0); + assert(not_found == answer1); + // Test 3 + answer1 = search::saddleback::saddleback(matrix, 1); + test_answer = std::make_pair(1, 1); + assert(test_answer == answer1); + // Test 4 + answer1 = search::saddleback::saddleback(matrix, 50000); + test_answer = std::make_pair(5, 5); + assert(test_answer == answer1); + // Test 5 + answer1 = search::saddleback::saddleback(matrix, 300); + test_answer = std::make_pair(3, 3); + assert(test_answer == answer1); +} + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + test(); // execute the tests + return 0; +} From 2c861b2dd25b96fdb5a6718de0899df24074e865 Mon Sep 17 00:00:00 2001 From: Rakshit Raj Date: Wed, 16 Dec 2020 08:03:04 +0530 Subject: [PATCH 04/30] feat: Add sorting/count_inversions.cpp (#1425) * Create count_inversions.cpp Counts the number of inversions in a list using merge sort. The number of Inversions in a list is the measure of the list's proximity to being sorted in ascending/increasing order. * Update count_inversions.cpp - fixed template error on line 156 - an added test case for character array - an added test case for list type double * Update sorting/count_inversions.cpp Co-authored-by: Taj * Update sorting/count_inversions.cpp Co-authored-by: Taj * Update sorting/count_inversions.cpp Co-authored-by: David Leal * updating DIRECTORY.md * clang-format and clang-tidy fixes for b51af5e8 * using `uint64_t` from cstdint header and doxygen formatiing * clang-format and clang-tidy fixes for 047578bb * Update count_inversions.cpp * added comments on imports * clang-format and clang-tidy fixes for 2f65017d * Update sorting/count_inversions.cpp Co-authored-by: David Leal * Update sorting/count_inversions.cpp Co-authored-by: David Leal * Update sorting/count_inversions.cpp Co-authored-by: David Leal * Update sorting/count_inversions.cpp Co-authored-by: David Leal * Update count_inversions.cpp * clang-format and clang-tidy fixes for 5d5cc53e * Update sorting/count_inversions.cpp Co-authored-by: David Leal * Update sorting/count_inversions.cpp Co-authored-by: David Leal * Update sorting/count_inversions.cpp Co-authored-by: David Leal * Update sorting/count_inversions.cpp Co-authored-by: David Leal * Update sorting/count_inversions.cpp Co-authored-by: David Leal * Update sorting/count_inversions.cpp Co-authored-by: David Leal * Update sorting/count_inversions.cpp Co-authored-by: David Leal * Update sorting/count_inversions.cpp Co-authored-by: David Leal * Update sorting/count_inversions.cpp Co-authored-by: David Leal * Update sorting/count_inversions.cpp Co-authored-by: David Leal * Update sorting/count_inversions.cpp Co-authored-by: David Leal * Update sorting/count_inversions.cpp Co-authored-by: David Leal * Update count_inversions.cpp * Update sorting/count_inversions.cpp Co-authored-by: David Leal * Update sorting/count_inversions.cpp Co-authored-by: David Leal * Update sorting/count_inversions.cpp Co-authored-by: David Leal * Update count_inversions.cpp * clang-format and clang-tidy fixes for 121ce330 * Update sorting/count_inversions.cpp Co-authored-by: John Law Co-authored-by: Taj Co-authored-by: David Leal Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: John Law --- DIRECTORY.md | 1 + sorting/count_inversions.cpp | 275 +++++++++++++++++++++++++++++++++++ 2 files changed, 276 insertions(+) create mode 100644 sorting/count_inversions.cpp diff --git a/DIRECTORY.md b/DIRECTORY.md index cf1621932..ebe776444 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -255,6 +255,7 @@ * [Bucket Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/bucket_sort.cpp) * [Cocktail Selection Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/cocktail_selection_sort.cpp) * [Comb Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/comb_sort.cpp) + * [Count Inversions](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/count_inversions.cpp) * [Counting Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/counting_sort.cpp) * [Counting Sort String](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/counting_sort_string.cpp) * [Cycle Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/cycle_sort.cpp) diff --git a/sorting/count_inversions.cpp b/sorting/count_inversions.cpp new file mode 100644 index 000000000..2619bdab7 --- /dev/null +++ b/sorting/count_inversions.cpp @@ -0,0 +1,275 @@ +/** + * @file + * @brief Counting Inversions using [Merge + Sort](https://en.wikipedia.org/wiki/Merge_sort) + * + * @details + * Program to count the number of inversions in an array + * using merge-sort. + * + * The count of inversions help to determine how close the array + * is to be sorted in ASCENDING order. + * + * two elements a[i] and a[j] form an inversion if `a[i]` > `a[j]` and i < j + * + * Time Complexity --> `O(n.log n)` + + * Space Complexity --> `O(n)` ; additional array `temp[1..n]` + * ### Algorithm + + * 1. The idea is similar to merge sort, divide the array into two equal or + almost + * equal halves in each step until the base case is reached. + * 2. Create a function merge that counts the number of inversions when two + halves of + * the array are merged, create two indices i and j, i is the index for + first half + * and j is an index of the second half. if `a[i]` is greater than `a[j]`, + then there are (mid – i) + * inversions, Because left and right subarrays are sorted, so all the + remaining elements + * in left-subarray (a[i+1], a[i+2] … a[mid]) will be greater than a[j]. + * 3. Create a recursive function to divide the array into halves and find the + answer by summing + * the number of inversions is the first half, number of inversion in the + second half and + * the number of inversions by merging the two. + * 4. The base case of recursion is when there is only one element in the + given half. + * 5. Print the answer + * + * @author [Rakshit Raj](https://github.com/rakshitraj) + */ +#include /// for assert +#include /// for typedef datatype uint64_t +#include /// for IO operations +#include /// for std::vector + +/** + * @namespace sorting + * @brief Sorting algorithms + */ +namespace sorting { +/** + * @namespace inversion + * @brief Functions for counting inversions using Merge Sort algorithm + */ +namespace inversion { + +// Functions used ---> +// int mergeSort(int* arr, int* temp, int left, int right); +// int merge(int* arr, int* temp, int left, int mid, int right); +// int countInversion(int* arr, const int size); +// void show(int* arr, const int size); + +/** + * @brief Function to merge two sub-arrays. + * + * @details + * merge() function is called from mergeSort() + * to merge the array after it split for sorting + * by the mergeSort() funtion. + * + * In this case the merge fuction will also count and return + * inversions detected when merging the sub arrays. + * + * @param arr input array, data-menber of vector + * @param temp stores the resultant merged array + * @param left lower bound of `arr[]` and left-sub-array + * @param mid midpoint, upper bound of left sub-array, + * `(mid+1)` gives the lower bound of right-sub-array + * @param right upper bound of `arr[]` and right-sub-array + * @returns number of inversions found in merge step + */ +template +uint32_t merge(T* arr, T* temp, uint32_t left, uint32_t mid, uint32_t right) { + uint32_t i = left; /* i --> index of left sub-array */ + uint32_t j = mid + 1; /* j --> index for right sub-array */ + uint32_t k = left; /* k --> index for resultant array temp */ + uint32_t inv_count = 0; // inversion count + + while ((i <= mid) && (j <= right)) { + if (arr[i] <= arr[j]) { + temp[k++] = arr[i++]; + } else { + temp[k++] = arr[j++]; + inv_count += + (mid - i + + 1); // tricky; may vary depending on selection of sub-array + } + } + // Add remaining elements from the larger subarray to the end of temp + while (i <= mid) { + temp[k++] = arr[i++]; + } + while (j <= right) { + temp[k++] = arr[j++]; + } + // Copy temp[] to arr[] + for (k = left; k <= right; k++) { + arr[k] = temp[k]; + } + return inv_count; +} + +/** + * @brief Implement merge Sort and count inverions while merging + * + * @details + * The mergeSort() function implements Merge Sort, a + * Divide and conquer algorithm, it divides the input + * array into two halves and calls itself for each + * sub-array and then calls the merge() function to + * merge the two halves. + * + * @param arr - array to be sorted + * @param temp - merged resultant array + * @param left - lower bound of array + * @param right - upper bound of array + * @returns number of inversions in array + */ +template +uint32_t mergeSort(T* arr, T* temp, uint32_t left, uint32_t right) { + uint32_t mid = 0, inv_count = 0; + if (right > left) { + // midpoint to split the array + mid = (right + left) / 2; + // Add inversions in left and right sub-arrays + inv_count += mergeSort(arr, temp, left, mid); // left sub-array + inv_count += mergeSort(arr, temp, mid + 1, right); + + // inversions in the merge step + inv_count += merge(arr, temp, left, mid, right); + } + return inv_count; +} + +/** + * @brief Function countInversion() returns the number of inversion + * present in the input array. Inversions are an estimate of + * how close or far off the array is to being sorted. + * + * @details + * Number of inversions in a sorted array is 0. + * Number of inversion in an array[1...n] sorted in + * non-ascending order is n(n-1)/2, since each pair of elements + * contitute an inversion. + * + * @param arr - array, data member of std::vector, input for counting + * inversions + * @param array_size - number of elementa in the array + * @returns number of inversions in input array, sorts the array + */ +template +uint32_t countInversion(T* arr, const uint32_t size) { + std::vector temp; + temp.reserve(size); + temp.assign(size, 0); + return mergeSort(arr, temp.data(), 0, size - 1); +} + +/** + * @brief UTILITY function to print array. + * @param arr[] array to print + * @param array_size size of input array arr[] + * @returns void + * + */ +template +void show(T* arr, const uint32_t array_size) { + std::cout << "Printing array: \n"; + for (uint32_t i = 0; i < array_size; i++) { + std::cout << " " << arr[i]; + } + std::cout << "\n"; +} + +} // namespace inversion +} // namespace sorting + +/** + * @brief Test implementations + * @returns void + */ +static void test() { + // Test 1 + std::vector arr1 = { + 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, + 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, + 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, + 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, + 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, + 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; + uint32_t size1 = arr1.size(); + uint32_t inv_count1 = 4950; + uint32_t result1 = sorting::inversion::countInversion(arr1.data(), size1); + assert(inv_count1 == result1); + // Test 2 + std::vector arr2 = {22, 66, 75, 23, 11, 87, 2, 44, 98, 43}; + uint32_t size2 = arr2.size(); + uint32_t inv_count2 = 20; + uint32_t result2 = sorting::inversion::countInversion(arr2.data(), size2); + assert(inv_count2 == result2); + // Test 3 + std::vector arr3 = {33.1, 45.2, 65.4, 76.5, 1.0, + 2.9, 5.4, 7.7, 88.9, 12.4}; + uint32_t size3 = arr3.size(); + uint32_t inv_count3 = 21; + uint32_t result3 = sorting::inversion::countInversion(arr3.data(), size3); + assert(inv_count3 == result3); + // Test 4 + std::vector arr4 = {'a', 'b', 'c', 'd', 'e'}; + uint32_t size4 = arr4.size(); + uint32_t inv_count4 = 0; + uint32_t result4 = sorting::inversion::countInversion(arr4.data(), size4); + assert(inv_count4 == result4); +} + +// /** +// * @brief Program Body contains all main funtionality +// * @returns void +// */ +// template +// static void body() { +// // Input your own sequence +// uint_t size; +// T input; +// std::cout << "Enter number of elements:"; +// std::cin >> size; +// +// std::vector arr; +// arr.reserve(size); +// +// std::cout << "Enter elements -->\n"; +// for (uint64_t i=1; i<=size; i++) { +// std::cout << "Element "<< i <<" :"; +// std::cin >> input; +// arr.push_back(input); +// } +// +// if (size != arr.size()) { +// size = arr.size(); +// } +// +// std::cout << "\n"; +// sorting::inversion::show(arr.data(), size); +// std::cout << "\n"; +// +// // Counting inversions +// std::cout << "\nThe number of inversions: "<< +// sorting::inversion::countInversion(arr.data(), size) << "\n"; +// +// // Output sorted array +// std::cout << "\nSorted array --> \n"; +// sorting::inversion::show(arr.data(), size); +// } + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + test(); // Run test implementations + // body(); // test your own array + return 0; +} From 00972e55e3c0d816644131a2110e442360532ca9 Mon Sep 17 00:00:00 2001 From: Francesco Urbani Date: Tue, 5 Jan 2021 10:40:27 +0100 Subject: [PATCH 05/30] feat: added highlighting differences in Sudoku Solver (#1438) * printing out the solved grid with the differences highlighted. * added documentation to the functions. * removed #define because code formatter check failed. --- backtracking/sudoku_solve.cpp | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/backtracking/sudoku_solve.cpp b/backtracking/sudoku_solve.cpp index ccde36383..d631d5dfd 100644 --- a/backtracking/sudoku_solve.cpp +++ b/backtracking/sudoku_solve.cpp @@ -59,14 +59,19 @@ namespace backtracking { * Utility function to print matrix * @tparam V number of vertices in array * @param mat matrix where numbers are saved + * @param starting_mat copy of mat, required by printMat for highlighting the differences * @param n number of times loop will run * @return void */ template - void printMat(const std::array , V> &mat, int n) { + void printMat(const std::array , V> &mat, const std::array , V> &starting_mat, int n) { for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { - std::cout << mat[i][j] << " "; + if (starting_mat[i][j] != mat[i][j]) { + std::cout << "\033[93m" << mat[i][j] << "\033[0m" << " "; + } else { + std::cout << mat[i][j] << " "; + } if ((j + 1) % 3 == 0) { std::cout << '\t'; } @@ -82,28 +87,29 @@ namespace backtracking { * Sudoku algorithm * @tparam V number of vertices in array * @param mat matrix where numbers are saved + * @param starting_mat copy of mat, required by printMat for highlighting the differences * @param i current index in rows * @param j current index in columns * @returns `true` if 'no' was placed * @returns `false` if 'no' was not placed */ template - bool solveSudoku(std::array , V> &mat, int i, int j) { + bool solveSudoku(std::array , V> &mat, const std::array , V> &starting_mat, int i, int j) { /// Base Case if (i == 9) { /// Solved for 9 rows already - backtracking::printMat(mat, 9); + backtracking::printMat(mat, starting_mat, 9); return true; } /// Crossed the last Cell in the row if (j == 9) { - return backtracking::solveSudoku(mat, i + 1, 0); + return backtracking::solveSudoku(mat, starting_mat, i + 1, 0); } /// Blue Cell - Skip if (mat[i][j] != 0) { - return backtracking::solveSudoku(mat, i, j + 1); + return backtracking::solveSudoku(mat, starting_mat, i, j + 1); } /// White Cell /// Try to place every possible no @@ -111,7 +117,7 @@ namespace backtracking { if (backtracking::isPossible(mat, i, j, no, 9)) { /// Place the 'no' - assuming a solution will exist mat[i][j] = no; - bool solution_found = backtracking::solveSudoku(mat, i, j + 1); + bool solution_found = backtracking::solveSudoku(mat, starting_mat, i, j + 1); if (solution_found) { return true; } @@ -142,9 +148,10 @@ int main() { std::array {0, 0, 0, 0, 8, 0, 0, 7, 9} }; - backtracking::printMat(mat, 9); + backtracking::printMat(mat, mat, 9); std::cout << "Solution " << std::endl; - backtracking::solveSudoku(mat, 0, 0); + std::array , V> starting_mat = mat; + backtracking::solveSudoku(mat, starting_mat, 0, 0); return 0; } From ecd14b44fda4bd42f61b22e98d477ca6f62bd7a8 Mon Sep 17 00:00:00 2001 From: David Leal Date: Thu, 7 Jan 2021 22:18:38 -0600 Subject: [PATCH 06/30] fix: Update copyright notice to 2021 (#1440) --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 1f8bce116..30820cbe0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 TheAlgorithms +Copyright (c) 2016-2021 TheAlgorithms and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From a69051ac53b24635086a9ad792e2e132d53d887c Mon Sep 17 00:00:00 2001 From: Sagar Pandya Date: Fri, 8 Jan 2021 15:11:34 +0530 Subject: [PATCH 07/30] namespaces added datatype changed for big number documentation improved --- graph/connected_components_with_dsu.cpp | 122 +++++++++++++++++------- 1 file changed, 89 insertions(+), 33 deletions(-) diff --git a/graph/connected_components_with_dsu.cpp b/graph/connected_components_with_dsu.cpp index 50d0c4242..5317361b2 100644 --- a/graph/connected_components_with_dsu.cpp +++ b/graph/connected_components_with_dsu.cpp @@ -1,56 +1,112 @@ -#include -#include -#include +/** + * @file + * @brief [Disjoint union](https://en.wikipedia.org/wiki/Disjoint_union) + * + * @details + * 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 + * 1. Depth first search + * 2. Disjoint union + * 1st option is inefficient, Disjoint union is the most optimal way to find this. + */ +#include /// for io operations +#include /// for std::set +#include /// for std::vector -int number_of_nodes; // denotes number of nodes; -std::vector parent; -std::vector connected_set_size; -void make_set() { // function the initialize every node as it's own parent - for (int i = 1; i <= number_of_nodes; i++) { +/** + * @namespace graph + * @brief Graph Algorithms + */ +namespace graph { + +/** + * @namespace disjoint_union + * @brief Function for [Disjoint union] (https://en.wikipedia.org/wiki/Disjoint_union) implementation + */ +namespace disjoint_union { + +int64_t number_of_nodes; // denotes number of nodes +std::vector parent; // parent of each node +std::vector connected_set_size; // size of each set +/** + * @brief function the initialize every node as it's own parent + * @returns void + */ +void make_set() { + for (int64_t i = 1; i <= number_of_nodes; i++) { parent[i] = i; connected_set_size[i] = 1; } } -// To find the component where following node belongs to -int find_set(int v) { - if (v == parent[v]) { - return v; +/** + * @brief To find the component where following node belongs to + * @param val parent of val should be found + * @return parent of val + */ +int64_t find_set(int64_t val) { + while (parent[val] != val) { + parent[val] = parent[parent[val]]; + val = parent[val]; } - return parent[v] = find_set(parent[v]); + return val; } +/** + * @brief To join 2 components to belong to one + * @param node1 1st component + * @param node2 2nd component + * @returns void + */ +void union_sets(int64_t node1, int64_t node2) { + node1 = find_set(node1); // find the parent of node1 + node2 = find_set(node2); // find the parent of node2 -void union_sets(int a, int b) { // To join 2 components to belong to one - a = find_set(a); - b = find_set(b); - if (a != b) { - if (connected_set_size[a] < connected_set_size[b]) { - std::swap(a, b); + // If parents of both nodes are not same, combine them + if (node1 != node2) { + if (connected_set_size[node1] < connected_set_size[node2]) { + std::swap(node1, node2); // swap both components } - parent[b] = a; - connected_set_size[a] += connected_set_size[b]; + parent[node2] = node1; // make node1 as parent of node2. + connected_set_size[node1] += + connected_set_size[node2]; // sum the size of both as they combined } } - -int no_of_connected_components() { // To find total no of connected components - std::set temp; // temp set to count number of connected components - for (int i = 1; i <= number_of_nodes; i++) temp.insert(find_set(i)); - return temp.size(); +/** + * @brief To find total no of connected components + * @return Number of connected components + */ +int64_t no_of_connected_components() { + std::set temp; // temp set to count number of connected components + for (int64_t i = 1; i <= number_of_nodes; i++) temp.insert(find_set(i)); + return temp.size(); // return the size of temp set } - -// All critical/corner cases have been taken care of. -// Input your required values: (not hardcoded) -int main() { +/** + * @brief Test Implementations + * @returns void + */ +static void test() { std::cin >> number_of_nodes; parent.resize(number_of_nodes + 1); connected_set_size.resize(number_of_nodes + 1); make_set(); - int edges = 0; + int64_t edges = 0; std::cin >> edges; // no of edges in the graph while (edges--) { - int node_a = 0, node_b = 0; + int64_t node_a = 0, node_b = 0; std::cin >> node_a >> node_b; union_sets(node_a, node_b); } std::cout << no_of_connected_components() << std::endl; - return 0; } +} // namespace disjoint_union +} // namespace graph + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + graph::disjoint_union::test(); // Execute the tests + return 0; +} \ No newline at end of file From bc6cb2f91f53df16f5f321c2397ed186f00f6c64 Mon Sep 17 00:00:00 2001 From: shubhamamsa Date: Sun, 17 Jan 2021 23:09:01 +0530 Subject: [PATCH 08/30] feat: added modular division algorithm --- math/modular_division.cpp | 109 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 math/modular_division.cpp diff --git a/math/modular_division.cpp b/math/modular_division.cpp new file mode 100644 index 000000000..c243c8424 --- /dev/null +++ b/math/modular_division.cpp @@ -0,0 +1,109 @@ +/** + * @file + * @brief Division of two numbers under modulo + * + * @details To calculate division of two numbers under modulo p + * Modulo operator is not distributive under division, therefore + * we first have to calculate the inverse of divisor using + * [Fermat's little theorem](https://en.wikipedia.org/wiki/Fermat%27s_little_theorem) + * Now, we can multiply the dividend with the inverse of divisor + * and modulo is distributive over multiplication operation. + * Let, + * We have 3 numbers a, b, p + * To compute (a/b)%p + * (a/b)%p ≡ (a*(inverse(b)))%p ≡ ((a%p)*inverse(b)%p)%p + * NOTE: For the existence of inverse of 'b', 'b' and 'p' must be coprime + * For simplicity we take p as prime + * Time Complexity: O(log(b)) + * + * Example: ( 24 / 3 ) % 5 => 8 % 5 = 3 --- (i) + Now the inverse of 3 is 2 + (24 * 2) % 5 = (24 % 5) * (2 % 5) = (4 * 2) % 5 = 3 --- (ii) + (i) and (ii) are equal hence the answer is correct. + + * + * @see modular_inverse_fermat_little_theorem.cpp, modular_exponentiation.cpp + * + * @author [Shubham Yadav](https://github.com/shubhamamsa) + */ + +#include +#include + +/** + * @namespace math + */ +namespace math { + /** + * @brief This function calculates a raised to exponent b under modulo c using + * modular exponentiation. + * @param a integer base + * @param b unsigned integer exponent + * @param c integer modulo + * @return a raised to power b modulo c + */ + int power(int a, int b, int c) { + int ans = 1; /// Initialize the answer to be returned + a = a % c; /// Update a if it is more than or equal to c + if (a == 0) { + return 0; /// In case a is divisible by c; + } + while (b > 0) { + /// If b is odd, multiply a with answer + if (b & 1) { + ans = ((ans % c) * (a % c)) % c; + } + /// b must be even now + b = b >> 1; /// b = b/2 + a = ((a % c) * (a % c)) % c; + } + return ans; + } + + /** + * @brief This function calculates modular division + * @param a integer dividend + * @param b integer divisor + * @param p integer modulo + * @return a/b modulo c + */ + int mod_division(int a, int b, int p) { + int inverse = power(b, p-2, p)%p; /// Calculate the inverse of b + int result = ((a%p)*(inverse%p))%p; /// Calculate the final result + return result; + } +} + +/** + * Function for testing power function. + * test cases and assert statement. + * @returns `void` + */ +static void test() { + int test_case_1 = math::mod_division(8, 2, 2); + assert(test_case_1 == 0); + std::cout << "Test 1 Passed!" << std::endl; + int test_case_2 = math::mod_division(15, 3, 7); + assert(test_case_2 == 5); + std::cout << "Test 2 Passed!" << std::endl; + int test_case_3 = math::mod_division(10, 5, 2); + assert(test_case_3 == 0); + std::cout << "Test 3 Passed!" << std::endl; + int test_case_4 = math::mod_division(81, 3, 5); + assert(test_case_4 == 2); + std::cout << "Test 4 Passed!" << std::endl; + int test_case_5 = math::mod_division(12848, 73, 29); + assert(test_case_5 == 2); + std::cout << "Test 5 Passed!" << std::endl; +} + +/** + * @brief Main function + * @param argc commandline argument count (ignored) + * @param argv commandline array of arguments (ignored) + * @returns 0 on exit + */ +int main(int argc, char *argv[]) { + test(); // execute the tests + return 0; +} From f752084bcaa3571dc23c3c71c12a50dfbcf419e5 Mon Sep 17 00:00:00 2001 From: shubhamamsa Date: Mon, 18 Jan 2021 02:13:32 +0530 Subject: [PATCH 09/30] feat: Addressed comments for adding modular division algorithm --- math/modular_division.cpp | 101 ++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 49 deletions(-) diff --git a/math/modular_division.cpp b/math/modular_division.cpp index c243c8424..205b6c49a 100644 --- a/math/modular_division.cpp +++ b/math/modular_division.cpp @@ -1,7 +1,7 @@ /** * @file - * @brief Division of two numbers under modulo - * + * @brief An algorithm to divide two numbers under modulo p [Modular + * Division](https://www.geeksforgeeks.org/modular-division) * @details To calculate division of two numbers under modulo p * Modulo operator is not distributive under division, therefore * we first have to calculate the inverse of divisor using @@ -15,64 +15,67 @@ * NOTE: For the existence of inverse of 'b', 'b' and 'p' must be coprime * For simplicity we take p as prime * Time Complexity: O(log(b)) - * * Example: ( 24 / 3 ) % 5 => 8 % 5 = 3 --- (i) Now the inverse of 3 is 2 (24 * 2) % 5 = (24 % 5) * (2 % 5) = (4 * 2) % 5 = 3 --- (ii) (i) and (ii) are equal hence the answer is correct. - - * * @see modular_inverse_fermat_little_theorem.cpp, modular_exponentiation.cpp - * * @author [Shubham Yadav](https://github.com/shubhamamsa) */ -#include -#include +#include /// for assert +#include /// for IO operations /** * @namespace math + * @brief Mathematical algorithms */ namespace math { /** - * @brief This function calculates a raised to exponent b under modulo c using - * modular exponentiation. - * @param a integer base - * @param b unsigned integer exponent - * @param c integer modulo - * @return a raised to power b modulo c + * @namespace modular_division + * @brief Functions for Modular Division implementation */ - int power(int a, int b, int c) { - int ans = 1; /// Initialize the answer to be returned - a = a % c; /// Update a if it is more than or equal to c - if (a == 0) { - return 0; /// In case a is divisible by c; - } - while (b > 0) { - /// If b is odd, multiply a with answer - if (b & 1) { - ans = ((ans % c) * (a % c)) % c; - } - /// b must be even now - b = b >> 1; /// b = b/2 - a = ((a % c) * (a % c)) % c; - } - return ans; - } + namespace modular_division { + /** + * @brief This function calculates a raised to exponent b under modulo c using + * modular exponentiation. + * @param a integer base + * @param b unsigned integer exponent + * @param c integer modulo + * @return a raised to power b modulo c + */ + uint64_t power(uint64_t a, uint64_t b, uint64_t c) { + uint64_t ans = 1; /// Initialize the answer to be returned + a = a % c; /// Update a if it is more than or equal to c + if (a == 0) { + return 0; /// In case a is divisible by c; + } + while (b > 0) { + /// If b is odd, multiply a with answer + if (b & 1) { + ans = ((ans % c) * (a % c)) % c; + } + /// b must be even now + b = b >> 1; /// b = b/2 + a = ((a % c) * (a % c)) % c; + } + return ans; + } - /** - * @brief This function calculates modular division - * @param a integer dividend - * @param b integer divisor - * @param p integer modulo - * @return a/b modulo c - */ - int mod_division(int a, int b, int p) { - int inverse = power(b, p-2, p)%p; /// Calculate the inverse of b - int result = ((a%p)*(inverse%p))%p; /// Calculate the final result - return result; - } -} + /** + * @brief This function calculates modular division + * @param a integer dividend + * @param b integer divisor + * @param p integer modulo + * @return a/b modulo c + */ + uint64_t mod_division(uint64_t a, uint64_t b, uint64_t p) { + uint64_t inverse = power(b, p-2, p)%p; /// Calculate the inverse of b + uint64_t result = ((a%p)*(inverse%p))%p; /// Calculate the final result + return result; + } + } // namespace modular_division +} // namespace math /** * Function for testing power function. @@ -80,19 +83,19 @@ namespace math { * @returns `void` */ static void test() { - int test_case_1 = math::mod_division(8, 2, 2); + uint64_t test_case_1 = math::modular_division::mod_division(8, 2, 2); assert(test_case_1 == 0); std::cout << "Test 1 Passed!" << std::endl; - int test_case_2 = math::mod_division(15, 3, 7); + uint64_t test_case_2 = math::modular_division::mod_division(15, 3, 7); assert(test_case_2 == 5); std::cout << "Test 2 Passed!" << std::endl; - int test_case_3 = math::mod_division(10, 5, 2); + uint64_t test_case_3 = math::modular_division::mod_division(10, 5, 2); assert(test_case_3 == 0); std::cout << "Test 3 Passed!" << std::endl; - int test_case_4 = math::mod_division(81, 3, 5); + uint64_t test_case_4 = math::modular_division::mod_division(81, 3, 5); assert(test_case_4 == 2); std::cout << "Test 4 Passed!" << std::endl; - int test_case_5 = math::mod_division(12848, 73, 29); + uint64_t test_case_5 = math::modular_division::mod_division(12848, 73, 29); assert(test_case_5 == 2); std::cout << "Test 5 Passed!" << std::endl; } From ea6c7933a614a23da0af57021f3b1dca1b19b0b4 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Sun, 17 Jan 2021 20:44:52 +0000 Subject: [PATCH 10/30] updating DIRECTORY.md --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index ebe776444..09e589c21 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -154,6 +154,7 @@ * [Least Common Multiple](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/least_common_multiple.cpp) * [Magic Number](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/magic_number.cpp) * [Miller Rabin](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/miller_rabin.cpp) + * [Modular Division](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/modular_division.cpp) * [Modular Exponentiation](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/modular_exponentiation.cpp) * [Modular Inverse Fermat Little Theorem](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/modular_inverse_fermat_little_theorem.cpp) * [N Choose R](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/n_choose_r.cpp) From bbb1b984262cf2f77c8456106c59b327879a3fb4 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Sun, 17 Jan 2021 20:44:59 +0000 Subject: [PATCH 11/30] clang-format and clang-tidy fixes for 2ad5420a --- math/modular_division.cpp | 94 ++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 46 deletions(-) diff --git a/math/modular_division.cpp b/math/modular_division.cpp index 205b6c49a..67001b8cf 100644 --- a/math/modular_division.cpp +++ b/math/modular_division.cpp @@ -1,11 +1,12 @@ /** * @file - * @brief An algorithm to divide two numbers under modulo p [Modular + * @brief An algorithm to divide two numbers under modulo p [Modular * Division](https://www.geeksforgeeks.org/modular-division) * @details To calculate division of two numbers under modulo p * Modulo operator is not distributive under division, therefore * we first have to calculate the inverse of divisor using - * [Fermat's little theorem](https://en.wikipedia.org/wiki/Fermat%27s_little_theorem) + * [Fermat's little + theorem](https://en.wikipedia.org/wiki/Fermat%27s_little_theorem) * Now, we can multiply the dividend with the inverse of divisor * and modulo is distributive over multiplication operation. * Let, @@ -31,51 +32,52 @@ * @brief Mathematical algorithms */ namespace math { - /** - * @namespace modular_division - * @brief Functions for Modular Division implementation - */ - namespace modular_division { - /** - * @brief This function calculates a raised to exponent b under modulo c using - * modular exponentiation. - * @param a integer base - * @param b unsigned integer exponent - * @param c integer modulo - * @return a raised to power b modulo c - */ - uint64_t power(uint64_t a, uint64_t b, uint64_t c) { - uint64_t ans = 1; /// Initialize the answer to be returned - a = a % c; /// Update a if it is more than or equal to c - if (a == 0) { - return 0; /// In case a is divisible by c; - } - while (b > 0) { - /// If b is odd, multiply a with answer - if (b & 1) { - ans = ((ans % c) * (a % c)) % c; - } - /// b must be even now - b = b >> 1; /// b = b/2 - a = ((a % c) * (a % c)) % c; - } - return ans; +/** + * @namespace modular_division + * @brief Functions for Modular Division implementation + */ +namespace modular_division { +/** + * @brief This function calculates a raised to exponent b under modulo c using + * modular exponentiation. + * @param a integer base + * @param b unsigned integer exponent + * @param c integer modulo + * @return a raised to power b modulo c + */ +uint64_t power(uint64_t a, uint64_t b, uint64_t c) { + uint64_t ans = 1; /// Initialize the answer to be returned + a = a % c; /// Update a if it is more than or equal to c + if (a == 0) { + return 0; /// In case a is divisible by c; } + while (b > 0) { + /// If b is odd, multiply a with answer + if (b & 1) { + ans = ((ans % c) * (a % c)) % c; + } + /// b must be even now + b = b >> 1; /// b = b/2 + a = ((a % c) * (a % c)) % c; + } + return ans; +} - /** - * @brief This function calculates modular division - * @param a integer dividend - * @param b integer divisor - * @param p integer modulo - * @return a/b modulo c - */ - uint64_t mod_division(uint64_t a, uint64_t b, uint64_t p) { - uint64_t inverse = power(b, p-2, p)%p; /// Calculate the inverse of b - uint64_t result = ((a%p)*(inverse%p))%p; /// Calculate the final result - return result; - } - } // namespace modular_division -} // namespace math +/** + * @brief This function calculates modular division + * @param a integer dividend + * @param b integer divisor + * @param p integer modulo + * @return a/b modulo c + */ +uint64_t mod_division(uint64_t a, uint64_t b, uint64_t p) { + uint64_t inverse = power(b, p - 2, p) % p; /// Calculate the inverse of b + uint64_t result = + ((a % p) * (inverse % p)) % p; /// Calculate the final result + return result; +} +} // namespace modular_division +} // namespace math /** * Function for testing power function. @@ -107,6 +109,6 @@ static void test() { * @returns 0 on exit */ int main(int argc, char *argv[]) { - test(); // execute the tests + test(); // execute the tests return 0; } From f6eb8e72ed4037857d883e8da486f9ce9417c130 Mon Sep 17 00:00:00 2001 From: shubhamamsa Date: Mon, 18 Jan 2021 16:14:35 +0530 Subject: [PATCH 12/30] feat: addressed comments for math/modular_division.cpp --- math/modular_division.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/math/modular_division.cpp b/math/modular_division.cpp index 67001b8cf..89bbdf9bb 100644 --- a/math/modular_division.cpp +++ b/math/modular_division.cpp @@ -34,7 +34,8 @@ namespace math { /** * @namespace modular_division - * @brief Functions for Modular Division implementation + * @brief Functions for [Modular + * Division](https://www.geeksforgeeks.org/modular-division) implementation */ namespace modular_division { /** From e1986f77cd8bf60f7eb204cef4903daa210a8f7b Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Mon, 18 Jan 2021 10:45:57 +0000 Subject: [PATCH 13/30] clang-format and clang-tidy fixes for 7293e15a --- math/modular_division.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/math/modular_division.cpp b/math/modular_division.cpp index 89bbdf9bb..6fd984f2b 100644 --- a/math/modular_division.cpp +++ b/math/modular_division.cpp @@ -34,7 +34,7 @@ namespace math { /** * @namespace modular_division - * @brief Functions for [Modular + * @brief Functions for [Modular * Division](https://www.geeksforgeeks.org/modular-division) implementation */ namespace modular_division { From f74af2943b581843187166830aa241f8e637d3dd Mon Sep 17 00:00:00 2001 From: ABHISHEK-821005 <69668943+ABHISHEK-821005@users.noreply.github.com> Date: Sun, 25 Oct 2020 20:55:58 +0530 Subject: [PATCH 14/30] improved time complexity if all the price are same then we can do it in o(n). improved time complexity. --- dynamic_programming/cut_rod.cpp | 56 ++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/dynamic_programming/cut_rod.cpp b/dynamic_programming/cut_rod.cpp index 9513e487e..e67c3eb4a 100644 --- a/dynamic_programming/cut_rod.cpp +++ b/dynamic_programming/cut_rod.cpp @@ -68,17 +68,71 @@ int maxProfitByCuttingRod(const std::array &price, const int n) { * @brief Function to test above algorithm * @returns void */ + + + +template +bool WhatIfAllPricesAreSame(const std::array &price, const int n){ + + /* + + Note that if all the prices of the different lengths of rod are same the answer will be always n*price; + where price=price of 1 length rod + Reason:: + cR() ---> cutRod() + + cR(4) + / / + / / + cR(3) cR(2) cR(1) cR(0) + / | / | + / | / | + cR(2) cR(1) cR(0) cR(1) cR(0) cR(0) + / | | + / | | + cR(1) cR(0) cR(0) cR(0) + / + / +CR(0) + + + if every length has same price , you would definitely want the rod of length 1, with n quantities. + which will give us maximum profits and maximum cuts. + + */ + + const int temp=price[0]; + for (size_t i = 1; i price1 = {1, 5, 8, 9, 10, 17, 17, 20}; // price array + std::array price1 = {1, 1,1,1,1,1,1,1}; // price array const int max_profit1 = dynamic_programming::cut_rod::maxProfitByCuttingRod(price1, n1); const int expected_max_profit1 = 22; + + if( WhatIfAllPricesAreSame(price1,n1)){ + std::cout << "Maximum profit with " << n1 << " inch road is " <<(n1)*price1[0] + << std::endl; + } + else{ + assert(max_profit1 == expected_max_profit1); std::cout << "Maximum profit with " << n1 << " inch road is " << max_profit1 << std::endl; + } + + + // Test 2 const int n2 = 30; // size of rod std::array price2 = { From a363964e058274390777f4b2e97cd5079226e844 Mon Sep 17 00:00:00 2001 From: ABHISHEK-821005 <69668943+ABHISHEK-821005@users.noreply.github.com> Date: Mon, 26 Oct 2020 12:51:34 +0530 Subject: [PATCH 15/30] updated time complexity if all the prices are same case(o(n)). --- dynamic_programming/cut_rod.cpp | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/dynamic_programming/cut_rod.cpp b/dynamic_programming/cut_rod.cpp index e67c3eb4a..5aeb801f5 100644 --- a/dynamic_programming/cut_rod.cpp +++ b/dynamic_programming/cut_rod.cpp @@ -61,18 +61,11 @@ int maxProfitByCuttingRod(const std::array &price, const int n) { delete[] profit; return ans; // returning maximum profit } -} // namespace cut_rod -} // namespace dynamic_programming - -/** - * @brief Function to test above algorithm - * @returns void - */ template -bool WhatIfAllPricesAreSame(const std::array &price, const int n){ +bool WhatIfAllPricesAreSame(const std::array &price, const uint64_t &n){ /* @@ -108,7 +101,18 @@ CR(0) } return true; -} +}// checks whether all the prices are same or not + + +} // namespace cut_rod +} // namespace dynamic_programming + +/** + * @brief Function to test above algorithm + * @returns void + */ + + static void test() { @@ -119,7 +123,7 @@ static void test() { dynamic_programming::cut_rod::maxProfitByCuttingRod(price1, n1); const int expected_max_profit1 = 22; - if( WhatIfAllPricesAreSame(price1,n1)){ + if(dynamic_programming::cut_rod::WhatIfAllPricesAreSame(price1,n1)){ std::cout << "Maximum profit with " << n1 << " inch road is " <<(n1)*price1[0] << std::endl; } From 98eb5ce6602eb12c7bab8a2c5348d95d537b9a60 Mon Sep 17 00:00:00 2001 From: ABHISHEK-821005 <69668943+ABHISHEK-821005@users.noreply.github.com> Date: Wed, 28 Oct 2020 17:53:40 +0530 Subject: [PATCH 16/30] added a new function to improve time complexity WhatIfAllPricesAreSame(price1,n1) added this function. --- dynamic_programming/cut_rod.cpp | 49 ++++++--------------------------- 1 file changed, 9 insertions(+), 40 deletions(-) diff --git a/dynamic_programming/cut_rod.cpp b/dynamic_programming/cut_rod.cpp index 5aeb801f5..cbfd68298 100644 --- a/dynamic_programming/cut_rod.cpp +++ b/dynamic_programming/cut_rod.cpp @@ -41,7 +41,7 @@ namespace cut_rod { * @return maximum profit obtainable for @param n inch rod. */ template -int maxProfitByCuttingRod(const std::array &price, const int n) { +int maxProfitByCuttingRod(const std::array &price, const uint64_t &n) { int *profit = new int[n + 1]; // profit[i] will hold maximum profit for i inch rod @@ -62,48 +62,23 @@ int maxProfitByCuttingRod(const std::array &price, const int n) { return ans; // returning maximum profit } - - template bool WhatIfAllPricesAreSame(const std::array &price, const uint64_t &n){ /* - Note that if all the prices of the different lengths of rod are same the answer will be always n*price; where price=price of 1 length rod - Reason:: - cR() ---> cutRod() - - cR(4) - / / - / / - cR(3) cR(2) cR(1) cR(0) - / | / | - / | / | - cR(2) cR(1) cR(0) cR(1) cR(0) cR(0) - / | | - / | | - cR(1) cR(0) cR(0) cR(0) - / - / -CR(0) - - + Reason::--> if every length has same price , you would definitely want the rod of length 1, with n quantities. which will give us maximum profits and maximum cuts. - */ - - const int temp=price[0]; + const int16_t temp=price[0]; for (size_t i = 1; i price1 = {1, 1,1,1,1,1,1,1}; // price array - const int max_profit1 = + const int16_t n1 = 8; // size of rod + std::array price1 = {1,1,1,1,1,1,1,1}; // price array + const int64_t max_profit1 = dynamic_programming::cut_rod::maxProfitByCuttingRod(price1, n1); - const int expected_max_profit1 = 22; + const int32_t expected_max_profit1 = 22; - if(dynamic_programming::cut_rod::WhatIfAllPricesAreSame(price1,n1)){ + if (dynamic_programming::cut_rod::WhatIfAllPricesAreSame(price1,n1)) { std::cout << "Maximum profit with " << n1 << " inch road is " <<(n1)*price1[0] << std::endl; } - else{ + else { assert(max_profit1 == expected_max_profit1); std::cout << "Maximum profit with " << n1 << " inch road is " << max_profit1 << std::endl; } - - - // Test 2 const int n2 = 30; // size of rod std::array price2 = { From 8c4d4c0dba0a0bc2eade47e0e3d0070d30dffa7b Mon Sep 17 00:00:00 2001 From: ABHISHEK-821005 <69668943+ABHISHEK-821005@users.noreply.github.com> Date: Fri, 30 Oct 2020 19:06:04 +0530 Subject: [PATCH 17/30] updated references --- dynamic_programming/cut_rod.cpp | 48 ++++++++------------------------- 1 file changed, 11 insertions(+), 37 deletions(-) diff --git a/dynamic_programming/cut_rod.cpp b/dynamic_programming/cut_rod.cpp index cbfd68298..c45c8b2be 100644 --- a/dynamic_programming/cut_rod.cpp +++ b/dynamic_programming/cut_rod.cpp @@ -57,28 +57,10 @@ int maxProfitByCuttingRod(const std::array &price, const uint64_t &n) { } profit[i] = q; } - int ans = profit[n]; + const int16_t ans = profit[n]; delete[] profit; return ans; // returning maximum profit } - -template -bool WhatIfAllPricesAreSame(const std::array &price, const uint64_t &n){ - - /* - Note that if all the prices of the different lengths of rod are same the answer will be always n*price; - where price=price of 1 length rod - Reason::--> - if every length has same price , you would definitely want the rod of length 1, with n quantities. - which will give us maximum profits and maximum cuts. - */ - const int16_t temp=price[0]; - for (size_t i = 1; i &price, const uint64_t &n){ * @brief Function to test above algorithm * @returns void */ - static void test() { // Test 1 const int16_t n1 = 8; // size of rod - std::array price1 = {1,1,1,1,1,1,1,1}; // price array - const int64_t max_profit1 = + std::array price1 = {1,2,4,6,8,45,21,9}; // price array + const int64_t max_profit1 = dynamic_programming::cut_rod::maxProfitByCuttingRod(price1, n1); - const int32_t expected_max_profit1 = 22; - - if (dynamic_programming::cut_rod::WhatIfAllPricesAreSame(price1,n1)) { - std::cout << "Maximum profit with " << n1 << " inch road is " <<(n1)*price1[0] - << std::endl; - } - else { - - assert(max_profit1 == expected_max_profit1); + const int64_t expected_max_profit1 = 47; + assert(max_profit1 == expected_max_profit1); std::cout << "Maximum profit with " << n1 << " inch road is " << max_profit1 << std::endl; - } // Test 2 - const int n2 = 30; // size of rod - std::array price2 = { + const int16_t n2 = 30; // size of rod + std::array price2 = { 1, 5, 8, 9, 10, 17, 17, 20, 24, 30, // price array 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50}; - const int max_profit2 = + + const int64_t max_profit2= dynamic_programming::cut_rod::maxProfitByCuttingRod(price2, n2); - const int expected_max_profit2 = 90; - assert(max_profit2 == expected_max_profit2); + const int32_t expected_max_profit2 = 90; + assert(max_profit2 == expected_max_profit2); std::cout << "Maximum profit with " << n2 << " inch road is " << max_profit2 << std::endl; } From b2583bb9dfdbe6ad2d7f284d35f47f4b3cbfa6a6 Mon Sep 17 00:00:00 2001 From: ABHISHEK-821005 <69668943+ABHISHEK-821005@users.noreply.github.com> Date: Sat, 31 Oct 2020 11:46:30 +0530 Subject: [PATCH 18/30] Update cut_rod.cpp --- dynamic_programming/cut_rod.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dynamic_programming/cut_rod.cpp b/dynamic_programming/cut_rod.cpp index c45c8b2be..80e4391d2 100644 --- a/dynamic_programming/cut_rod.cpp +++ b/dynamic_programming/cut_rod.cpp @@ -71,11 +71,11 @@ int maxProfitByCuttingRod(const std::array &price, const uint64_t &n) { static void test() { // Test 1 const int16_t n1 = 8; // size of rod - std::array price1 = {1,2,4,6,8,45,21,9}; // price array - const int64_t max_profit1 = + std::array price1 = {1,2,4,6,8,45,21,9}; // price array + const int64_t max_profit1 = dynamic_programming::cut_rod::maxProfitByCuttingRod(price1, n1); - const int64_t expected_max_profit1 = 47; - assert(max_profit1 == expected_max_profit1); + const int64_t expected_max_profit1 = 47; + assert(max_profit1 == expected_max_profit1); std::cout << "Maximum profit with " << n1 << " inch road is " << max_profit1 << std::endl; @@ -86,10 +86,10 @@ static void test() { 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50}; - const int64_t max_profit2= + const int64_t max_profit2= dynamic_programming::cut_rod::maxProfitByCuttingRod(price2, n2); - const int32_t expected_max_profit2 = 90; - assert(max_profit2 == expected_max_profit2); + const int32_t expected_max_profit2 = 90; + assert(max_profit2 == expected_max_profit2); std::cout << "Maximum profit with " << n2 << " inch road is " << max_profit2 << std::endl; } From e371fc259d9cc3135c80d2a4a0531489057dfaf5 Mon Sep 17 00:00:00 2001 From: ABHISHEK-821005 <69668943+ABHISHEK-821005@users.noreply.github.com> Date: Sat, 31 Oct 2020 11:53:40 +0530 Subject: [PATCH 19/30] added a new example to check correctness of the code --- dynamic_programming/cut_rod.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dynamic_programming/cut_rod.cpp b/dynamic_programming/cut_rod.cpp index 80e4391d2..c365be4fc 100644 --- a/dynamic_programming/cut_rod.cpp +++ b/dynamic_programming/cut_rod.cpp @@ -92,6 +92,15 @@ static void test() { assert(max_profit2 == expected_max_profit2); std::cout << "Maximum profit with " << n2 << " inch road is " << max_profit2 << std::endl; + // Test 3 + const int16_t n3 = 5; // size of rod + std::array price3 = {2,9,17,23,45}; // price array + const int64_t max_profit3 = + dynamic_programming::cut_rod::maxProfitByCuttingRod(price3, n3); + const int64_t expected_max_profit3 = 45; + assert(max_profit3 == expected_max_profit3); + std::cout << "Maximum profit with " << n3 << " inch road is " << max_profit3 + << std::endl; } /** From 2903d4861f195821f6401ff953ed30100b56ccca Mon Sep 17 00:00:00 2001 From: villayatali123 <70214280+villayatali123@users.noreply.github.com> Date: Fri, 5 Feb 2021 15:12:13 +0530 Subject: [PATCH 20/30] Nth fibonacci number using matrix exponentiation (#1215) * Nth fibonacci number using matrix exponentiation Co-authored-by: unknown --- math/fibonacci_matrix_exponentiation.cpp | 113 +++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 math/fibonacci_matrix_exponentiation.cpp diff --git a/math/fibonacci_matrix_exponentiation.cpp b/math/fibonacci_matrix_exponentiation.cpp new file mode 100644 index 000000000..1a119d210 --- /dev/null +++ b/math/fibonacci_matrix_exponentiation.cpp @@ -0,0 +1,113 @@ +/** + * @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 + */ + +#include +#include +#include + +/** + * This function finds nth fibonacci number in a given modulus + * @param n nth fibonacci 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; +} + +/** + * Function to test above algorithm + */ +void test() +{ + assert(fibo(6, 1000000007 ) == 8); + std::cout << "test case:1 passed\n"; + assert(fibo(5, 1000000007 ) == 5); + std::cout << "test case:2 passed\n"; + assert(fibo(10 , 1000000007) == 55); + std::cout << "test case:3 passed\n"; + assert(fibo(500 , 100) == 25); + std::cout << "test case:3 passed\n"; + assert(fibo(500 , 10000) == 4125); + std::cout << "test case:3 passed\n"; + std::cout << "--All tests passed--\n"; +} + +/** + * 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< Date: Fri, 5 Feb 2021 09:43:02 +0000 Subject: [PATCH 21/30] updating DIRECTORY.md --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 09e589c21..4d6ab73fc 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -143,6 +143,7 @@ * [Fibonacci](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/fibonacci.cpp) * [Fibonacci Fast](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/fibonacci_fast.cpp) * [Fibonacci Large](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/fibonacci_large.cpp) + * [Fibonacci Matrix Exponentiation](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/fibonacci_matrix_exponentiation.cpp) * [Fibonacci Sum](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/fibonacci_sum.cpp) * [Gcd Iterative Euclidean](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/gcd_iterative_euclidean.cpp) * [Gcd Of N Numbers](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/gcd_of_n_numbers.cpp) From 6520ffcdc59c65b2843af93b9d8120f069bf95e5 Mon Sep 17 00:00:00 2001 From: Shreyas Sable <72278840+Shreyas-OwO@users.noreply.github.com> Date: Fri, 5 Feb 2021 15:13:45 +0530 Subject: [PATCH 22/30] cross product of two vectors (#1292) * cross product of two vectors * cross product of two mathematical vectors (fixed) * cross product of two mathematical vectors (fixed) * updating DIRECTORY.md * cross product of two mathematical vectors (fixed) * cross product of two mathematical vectors (fixed) * cross product of two mathematical vectors (fixed) * cross product of two vectors (with tests) * cross product of two mathematical vectors (fixed) * cross product of two vectors (with example fixed) * cross product of two vectors * cross product of two mathematical vectors Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 1 + math/vector_cross_product.cpp | 133 ++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 math/vector_cross_product.cpp diff --git a/DIRECTORY.md b/DIRECTORY.md index 4d6ab73fc..63305a5a8 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -171,6 +171,7 @@ * [String Fibonacci](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/string_fibonacci.cpp) * [Sum Of Binomial Coefficient](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/sum_of_binomial_coefficient.cpp) * [Sum Of Digits](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/sum_of_digits.cpp) + * [Vector Cross Product](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/vector_cross_product.cpp) ## Numerical Methods * [Bisection Method](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/numerical_methods/bisection_method.cpp) diff --git a/math/vector_cross_product.cpp b/math/vector_cross_product.cpp new file mode 100644 index 000000000..391faceb8 --- /dev/null +++ b/math/vector_cross_product.cpp @@ -0,0 +1,133 @@ +/** + * @file + * + * @brief Calculates the [Cross Product](https://en.wikipedia.org/wiki/Cross_product) and the magnitude of two mathematical 3D vectors. + * + * + * @details Cross Product of two vectors gives a vector. + * Direction Ratios of a vector are the numeric parts of the given vector. They are the tree parts of the + * vector which determine the magnitude (value) of the vector. + * The method of finding a cross product is the same as finding the determinant of an order 3 matrix consisting + * of the first row with unit vectors of magnitude 1, the second row with the direction ratios of the + * first vector and the third row with the direction ratios of the second vector. + * The magnitude of a vector is it's value expressed as a number. + * Let the direction ratios of the first vector, P be: a, b, c + * Let the direction ratios of the second vector, Q be: x, y, z + * Therefore the calculation for the cross product can be arranged as: + * + * ``` + * P x Q: + * 1 1 1 + * a b c + * x y z + * ``` + * + * The direction ratios (DR) are calculated as follows: + * 1st DR, J: (b * z) - (c * y) + * 2nd DR, A: -((a * z) - (c * x)) + * 3rd DR, N: (a * y) - (b * x) + * + * Therefore, the direction ratios of the cross product are: J, A, N + * The following C++ Program calculates the direction ratios of the cross products of two vector. + * The program uses a function, cross() for doing so. + * The direction ratios for the first and the second vector has to be passed one by one seperated by a space character. + * + * Magnitude of a vector is the square root of the sum of the squares of the direction ratios. + * + * ### Example: + * An example of a running instance of the executable program: + * + * Pass the first Vector: 1 2 3 + * Pass the second Vector: 4 5 6 + * The cross product is: -3 6 -3 + * Magnitude: 7.34847 + * + * @author [Shreyas Sable](https://github.com/Shreyas-OwO) + */ + +#include +#include +#include +#include + +/** + * @namespace math + * @brief Math algorithms + */ +namespace math { + /** + * @namespace vector_cross + * @brief Functions for Vector Cross Product algorithms + */ + namespace vector_cross { + /** + * @brief Function to calculate the cross product of the passed arrays containing the direction ratios of the two mathematical vectors. + * @param A contains the direction ratios of the first mathematical vector. + * @param B contains the direction ration of the second mathematical vector. + * @returns the direction ratios of the cross product. + */ + std::array cross(const std::array &A, const std::array &B) { + std::array product; + /// Performs the cross product as shown in @algorithm. + product[0] = (A[1] * B[2]) - (A[2] * B[1]); + product[1] = -((A[0] * B[2]) - (A[2] * B[0])); + product[2] = (A[0] * B[1]) - (A[1] * B[0]); + return product; + } + + /** + * @brief Calculates the magnitude of the mathematical vector from it's direction ratios. + * @param vec an array containing the direction ratios of a mathematical vector. + * @returns type: double description: the magnitude of the mathematical vector from the given direction ratios. + */ + double mag(const std::array &vec) { + double magnitude = sqrt((vec[0] * vec[0]) + (vec[1] * vec[1]) + (vec[2] * vec[2])); + return magnitude; + } + } /// namespace vector_cross +} /// namespace math + +/** + * @brief test function. + * @details test the cross() and the mag() functions. + */ +static void test() { + /// Tests the cross() function. + std::array t_vec = math::vector_cross::cross({1, 2, 3}, {4, 5, 6}); + assert(t_vec[0] == -3 && t_vec[1] == 6 && t_vec[2] == -3); + + /// Tests the mag() function. + double t_mag = math::vector_cross::mag({6, 8, 0}); + assert(t_mag == 10); +} + +/** + * @brief Main Function + * @details Asks the user to enter the direction ratios for each of the two mathematical vectors using std::cin + * @returns 0 on exit + */ +int main() { + + /// Tests the functions with sample input before asking for user input. + test(); + + std::array vec1; + std::array vec2; + + /// Gets the values for the first vector. + std::cout << "\nPass the first Vector: "; + std::cin >> vec1[0] >> vec1[1] >> vec1[2]; + + /// Gets the values for the second vector. + std::cout << "\nPass the second Vector: "; + std::cin >> vec2[0] >> vec2[1] >> vec2[2]; + + /// Displays the output out. + std::array product = math::vector_cross::cross(vec1, vec2); + std::cout << "\nThe cross product is: " << product[0] << " " << product[1] << " " << product[2] << std::endl; + + /// Displays the magnitude of the cross product. + std::cout << "Magnitude: " << math::vector_cross::mag(product) << "\n" << std::endl; + + return 0; +} From 6920e7db6f6cd7c8a2bad7381d1e1e16545437c6 Mon Sep 17 00:00:00 2001 From: anjali1903 Date: Fri, 5 Feb 2021 15:16:06 +0530 Subject: [PATCH 23/30] feat: add longest palindromic subsequence algorithm (#1185) * feat: add longest palindromic subsequence algorithm * feat: add longest palindromic subsequence algorithm * feat: add longest palindromic subsequence algorithm * feat: add longest palindromic subsequence algorithm --- .../longest_palindromic_subsequence.cpp | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 dynamic_programming/longest_palindromic_subsequence.cpp diff --git a/dynamic_programming/longest_palindromic_subsequence.cpp b/dynamic_programming/longest_palindromic_subsequence.cpp new file mode 100644 index 000000000..9505dcc10 --- /dev/null +++ b/dynamic_programming/longest_palindromic_subsequence.cpp @@ -0,0 +1,90 @@ +/** + * @file + * @brief Program to find the Longest Palindormic + * Subsequence of a string + * + * @details + * [Palindrome](https://en.wikipedia.org/wiki/Palindrome) string sequence of + * characters which reads the same backward as forward + * [Subsequence](https://en.wikipedia.org/wiki/Subsequence) is a sequence that + * can be derived from another sequence by deleting some or no elements without + * changing the order of the remaining elements. + + * @author [Anjali Jha](https://github.com/anjali1903) + */ + +#include +#include +#include +#include + +/** + * Function that returns the longest palindromic + * subsequence of a string + */ +std::string lps(std::string a) { + std::string b = a; + reverse(b.begin(), b.end()); + int m = a.length(); + std::vector > res(m + 1); + + // Finding the length of the longest + // palindromic subsequence and storing + // in a 2D array in bottoms-up manner + for (int i = 0; i <= m; i++) { + for (int j = 0; j <= m; j++) { + if (i == 0 || j == 0) { + res[i][j] = 0; + } else if (a[i - 1] == b[j - 1]) { + res[i][j] = res[i - 1][j - 1] + 1; + } else { + res[i][j] = std::max(res[i - 1][j], res[i][j - 1]); + } + } + } + // Length of longest palindromic subsequence + int idx = res[m][m]; + // Creating string of index+1 length + std::string ans(idx + 1, '\0'); + int i = m, j = m; + + // starting from right-most bottom-most corner + // and storing them one by one in ans + while (i > 0 && j > 0) { + // if current characters in a and b are same + // then it is a part of the ans + if (a[i - 1] == b[j - 1]) { + ans[idx - 1] = a[i - 1]; + i--; + j--; + idx--; + } + // If they are not same, find the larger of the + // two and move in that direction + else if (res[i - 1][j] > res[i][j - 1]) { + i--; + } else { + j--; + } + } + + return ans; +} + +/** Test function */ +void test() { + // lps("radar") return "radar" + assert(lps("radar") == "radar"); + // lps("abbcbaa") return "abcba" + assert(lps("abbcbaa") == "abcba"); + // lps("bbbab") return "bbbb" + assert(lps("bbbab") == "bbbb"); +} + +/** + * Main Function + */ +int main() { + test(); // execute the tests + return 0; +} From bbd876f2ec9ea4264d029c5c526f43dd9a7d7d1a Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Fri, 5 Feb 2021 09:46:52 +0000 Subject: [PATCH 24/30] updating DIRECTORY.md --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 63305a5a8..bb140b7c5 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -68,6 +68,7 @@ * [Longest Common Subsequence](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/longest_common_subsequence.cpp) * [Longest Increasing Subsequence](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/longest_increasing_subsequence.cpp) * [Longest Increasing Subsequence (Nlogn)](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/longest_increasing_subsequence_(nlogn).cpp) + * [Longest Palindromic Subsequence](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/longest_palindromic_subsequence.cpp) * [Matrix Chain Multiplication](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/matrix_chain_multiplication.cpp) * [Palindrome Partitioning](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/palindrome_partitioning.cpp) * [Searching Of Element In Dynamic Array](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/searching_of_element_in_dynamic_array.cpp) From 0462bef8b21c40025b4fbc305aadcf30351c21fa Mon Sep 17 00:00:00 2001 From: Sagar Pandya <31953933+sagarpandyansit@users.noreply.github.com> Date: Fri, 5 Feb 2021 15:35:44 +0530 Subject: [PATCH 25/30] [docs/test]: Documentation and test in the list_array.cpp (#1375) * Added introductory documentation in list_array * documentation for search completed, minor bug fixed in the list_array * bug fixed in the insert method * edge case check added in the remove method * Documentation completed in list_array * Testing completed in the list_array.cpp * Bug fixed in the sort method * test cases improved and minor bug fixed in list_array * minor allignment changed * changes in documentation and improvement in the code in list_array * resolve clang-tidy warning * link added datatype changed * namespaces added * minor documentation changed in list_array.cpp Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> --- data_structures/list_array.cpp | 363 ++++++++++++++++++++------------- 1 file changed, 224 insertions(+), 139 deletions(-) diff --git a/data_structures/list_array.cpp b/data_structures/list_array.cpp index 9dd142a5a..39e74c66c 100644 --- a/data_structures/list_array.cpp +++ b/data_structures/list_array.cpp @@ -1,165 +1,250 @@ /** - * @file list_array.cpp - * @todo Add documentation - * @warning The sorting algorithm is erroneous + * @file + * @brief [Dynamic Array](https://en.wikipedia.org/wiki/Dynamic_array) + * + * @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. + * + * ### 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. */ -#include -#include -struct list { - std::array data{}; - int top = 0; - bool isSorted = false; +#include /// for io operations +#include /// for std::array +#include /// for assert - int BinarySearch(const std::array& dataArr, int first, int last, - int x) { - if (last < first) { +/** + * @namespace data_structures + * @brief Algorithms with data structures + */ +namespace data_structures { +/** + * @namespace list_array + * @brief Functions for [Dynamic Array](https://en.wikipedia.org/wiki/Dynamic_array) algorithm + */ +namespace list_array { + /** + * @brief Structure of List with supporting methods. + */ + 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"; return -1; } - int mid = (first + last) / 2; - if (dataArr[mid] == x) { - return mid; - } else if (x < dataArr[mid]) { - return (BinarySearch(dataArr, first, mid - 1, x)); - } else if (x > dataArr[mid]) { - return (BinarySearch(dataArr, mid + 1, last, x)); - } - std::cerr << __func__ << ":" << __LINE__ << ": Undefined condition\n"; - return -1; - } - - int LinearSearch(const std::array& dataArr, int x) const { - for (int i = 0; i < top; i++) { - if (dataArr[i] == x) { - return i; - } - } - - return -1; - } - - int Search(int x) { - int pos = 0; - - if (isSorted) { - pos = BinarySearch(data, 0, top - 1, x); - } else { - pos = LinearSearch(data, x); - } - - if (pos != -1) { - std::cout << "\nElement found at position : " << pos; - } else { - std::cout << "\nElement not found"; - } - return pos; - } - - void Sort() { - int i = 0, j = 0, pos = 0; - for (i = 0; i < top; i++) { - int min = data[i]; - for (j = i + 1; j < top; j++) { - if (data[j] < min) { - pos = j; - min = data[pos]; + /** + * @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 } } - - int temp = data[i]; - data[i] = data[pos]; - data[pos] = temp; + // element is not present in the list + return -1; } - isSorted = true; - } - void insert(int x) { - if (!isSorted) { + /* + * @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 + } + } + // 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 == 49) { std::cout << "\nOverflow"; - } else { - data[top] = x; + return; + } + // if list is not sorted, insert at the last + // otherwise place it to correct position + if (!isSorted) { + data[top] = val; top++; - } - } else { - int pos = 0; - - for (int i = 0; i < top - 1; i++) { - if (data[i] <= x && x <= data[i + 1]) { - pos = i + 1; - break; + } 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]; + } + top++; // Increment the value of top. + data[pos] = val; // Assign the value to the correct index in the array } - if (pos == 0) { - pos = top - 1; + } + + /** + * @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; } - - for (int i = top; i > pos; i--) { - data[i] = data[i - 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]; } - top++; - data[pos] = x; + top--; // decrement the top variable to maintain last index } - } - void Remove(int x) { - int pos = Search(x); - std::cout << "\n" << data[pos] << " deleted"; - for (int i = pos; i < top; i++) { - data[i] = data[i + 1]; + /** + * @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 + } } - top--; - } + }; // structure list +} // namespace list_array +} // namespace data_structures - void Show() { - for (int i = 0; i < top; i++) { - std::cout << data[i] << "\t"; - } - } -}; +/** + * @brief Test implementations + * @returns void + */ +static void test() { + data_structures::list_array::list L; + // Insert testing + L.insert(11); + L.insert(12); + assert(L.top == 2); + L.insert(15); + L.insert(10); + L.insert(12); + L.insert(20); + L.insert(18); + assert(L.top == 7); + 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 + assert(L.top == 5); + 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(12) == 2); + assert(L.search(50) == -1); // search for the non-existing element + + // Sort testing + L.sort(); + assert(L.isSorted == true); + L.show(); + + // BinarySearch testing + 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 +} + +/** + * @brief Main function + * @returns 0 on exit + */ int main() { - list L; - int choice = 0; - int x = 0; - do { - // Choices for operations on the list_array. - std::cout << "\n0.Exit"; - std::cout << "\n1.Insert"; - std::cout << "\n2.Delete"; - std::cout << "\n3.Search"; - std::cout << "\n4.Sort"; - std::cout << "\n5.Print"; - std::cout << "\n\nEnter Your Choice : "; - std::cin >> choice; - switch (choice) { - case 0: - break; - case 1: - std::cout << "\nEnter the element to be inserted : "; - std::cin >> x; - L.insert(x); - break; - case 2: - std::cout << "\nEnter the element to be removed : "; - std::cin >> x; - L.Remove(x); - break; - case 3: - std::cout << "\nEnter the element to be searched : "; - std::cin >> x; - L.Search(x); - break; - case 4: - L.Sort(); - break; - case 5: - L.Show(); - break; - default: - std::cout << "\nplease enter valid option."; - } - } while (choice != 0); + test(); // Execute the tests return 0; } From a4f583958ae55f09693e45a4f88c90e14190a8f9 Mon Sep 17 00:00:00 2001 From: Walt <32001362+Walt280@users.noreply.github.com> Date: Fri, 5 Feb 2021 05:07:16 -0500 Subject: [PATCH 26/30] feat: add new implementation of binary search tree (#1208) * fix: revamp binary_search_tree.cpp to use generics, oop, and modern c++ features * fix: remove make_unique from code * fix: fix some clang-tidy errors in binary_search_tree.cpp * Move new implementation into new file, restore old file. * Reset binary_search_tree.cpp to original implementation. * Add link to new implementation. * fix: add suggested fixes (see PR) * Implement comment suggestions. * Update data_structures/binary_search_tree2.cpp Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> * Fix additional comments. Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com> --- DIRECTORY.md | 1 + data_structures/binary_search_tree2.cpp | 564 ++++++++++++++++++++++++ 2 files changed, 565 insertions(+) create mode 100644 data_structures/binary_search_tree2.cpp diff --git a/DIRECTORY.md b/DIRECTORY.md index bb140b7c5..f164b9ba5 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -20,6 +20,7 @@ ## Data Structures * [Avltree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/avltree.cpp) * [Binary Search Tree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/binary_search_tree.cpp) + * [Binary Search Tree2](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/binary_search_tree2.cpp) * [Binaryheap](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/binaryheap.cpp) * [Circular Queue Using Linked List](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/circular_queue_using_linked_list.cpp) * Cll diff --git a/data_structures/binary_search_tree2.cpp b/data_structures/binary_search_tree2.cpp new file mode 100644 index 000000000..d506de151 --- /dev/null +++ b/data_structures/binary_search_tree2.cpp @@ -0,0 +1,564 @@ +/** + * @file + * @brief A generic [binary search tree](https://en.wikipedia.org/wiki/Binary_search_tree) implementation. + * @see binary_search_tree.cpp + */ + +#include +#include +#include +#include +#include + +/** + * @brief The Binary Search Tree class. + * + * @tparam T The type of the binary search tree key. + */ +template +class binary_search_tree { + private: + /** + * @brief A struct to represent a node in the Binary Search Tree. + */ + struct bst_node { + T value; /**< The value/key of the node. */ + std::unique_ptr left; /**< Pointer to left subtree. */ + std::unique_ptr right; /**< Pointer to right subtree. */ + + /** + * Constructor for bst_node, used to simplify node construction and + * smart pointer construction. + * @param _value The value of the constructed node. + */ + explicit bst_node(T _value) { + value = _value; + left = nullptr; + right = nullptr; + } + }; + + std::unique_ptr root_; /**< Pointer to the root of the BST. */ + std::size_t size_ = 0; /**< Number of elements/nodes in the BST. */ + + /** + * @brief Recursive function to find the maximum value in the BST. + * + * @param node The node to search from. + * @param ret_value Variable to hold the maximum value. + * @return true If the maximum value was successfully found. + * @return false Otherwise. + */ + bool find_max(std::unique_ptr& node, T& ret_value) { + if (!node) { + return false; + } else if (!node->right) { + ret_value = node->value; + return true; + } + return find_max(node->right, ret_value); + } + + /** + * @brief Recursive function to find the minimum value in the BST. + * + * @param node The node to search from. + * @param ret_value Variable to hold the minimum value. + * @return true If the minimum value was successfully found. + * @return false Otherwise. + */ + bool find_min(std::unique_ptr& node, T& ret_value) { + if (!node) { + return false; + } else if (!node->left) { + ret_value = node->value; + return true; + } + + return find_min(node->left, ret_value); + } + + /** + * @brief Recursive function to insert a value into the BST. + * + * @param node The node to search from. + * @param new_value The value to insert. + * @return true If the insert operation was successful. + * @return false Otherwise. + */ + bool insert(std::unique_ptr& node, T new_value) { + if (root_ == node && !root_) { + root_ = std::unique_ptr(new bst_node(new_value)); + return true; + } + + if (new_value < node->value) { + if (!node->left) { + node->left = std::unique_ptr(new bst_node(new_value)); + return true; + } else { + return insert(node->left, new_value); + } + } else if (new_value > node->value) { + if (!node->right) { + node->right = + std::unique_ptr(new bst_node(new_value)); + return true; + } else { + return insert(node->right, new_value); + } + } else { + return false; + } + } + + /** + * @brief Recursive function to remove a value from the BST. + * + * @param parent The parent node of node. + * @param node The node to search from. + * @param rm_value The value to remove. + * @return true If the removal operation was successful. + * @return false Otherwise. + */ + bool remove(std::unique_ptr& parent, + std::unique_ptr& node, T rm_value) { + if (!node) { + return false; + } + + if (node->value == rm_value) { + if (node->left && node->right) { + T successor_node_value{}; + find_max(node->left, successor_node_value); + remove(root_, root_, successor_node_value); + node->value = successor_node_value; + return true; + } else if (node->left || node->right) { + std::unique_ptr& non_null = + (node->left ? node->left : node->right); + + if (node == root_) { + root_ = std::move(non_null); + } else if (rm_value < parent->value) { + parent->left = std::move(non_null); + } else { + parent->right = std::move(non_null); + } + + return true; + } else { + if (node == root_) { + root_.reset(nullptr); + } else if (rm_value < parent->value) { + parent->left.reset(nullptr); + } else { + parent->right.reset(nullptr); + } + + return true; + } + } else if (rm_value < node->value) { + return remove(node, node->left, rm_value); + } else { + return remove(node, node->right, rm_value); + } + } + + /** + * @brief Recursive function to check if a value is in the BST. + * + * @param node The node to search from. + * @param value The value to find. + * @return true If the value was found in the BST. + * @return false Otherwise. + */ + bool contains(std::unique_ptr& node, T value) { + if (!node) { + return false; + } + + if (value < node->value) { + return contains(node->left, value); + } else if (value > node->value) { + return contains(node->right, value); + } else { + return true; + } + } + + /** + * @brief Recursive function to traverse the tree in in-order order. + * + * @param callback Function that is called when a value needs to processed. + * @param node The node to traverse from. + */ + void traverse_inorder(std::function callback, + std::unique_ptr& node) { + if (!node) { + return; + } + + traverse_inorder(callback, node->left); + callback(node->value); + traverse_inorder(callback, node->right); + } + + /** + * @brief Recursive function to traverse the tree in pre-order order. + * + * @param callback Function that is called when a value needs to processed. + * @param node The node to traverse from. + */ + void traverse_preorder(std::function callback, + std::unique_ptr& node) { + if (!node) { + return; + } + + callback(node->value); + traverse_preorder(callback, node->left); + traverse_preorder(callback, node->right); + } + + /** + * @brief Recursive function to traverse the tree in post-order order. + * + * @param callback Function that is called when a value needs to processed. + * @param node The node to traverse from. + */ + void traverse_postorder(std::function callback, + std::unique_ptr& node) { + if (!node) { + return; + } + + traverse_postorder(callback, node->left); + traverse_postorder(callback, node->right); + callback(node->value); + } + + public: + /** + * @brief Construct a new Binary Search Tree object. + * + */ + binary_search_tree() { + root_ = nullptr; + size_ = 0; + } + + /** + * @brief Insert a new value into the BST. + * + * @param new_value The value to insert into the BST. + * @return true If the insertion was successful. + * @return false Otherwise. + */ + bool insert(T new_value) { + bool result = insert(root_, new_value); + if (result) { + size_++; + } + return result; + } + + /** + * @brief Remove a specified value from the BST. + * + * @param rm_value The value to remove. + * @return true If the removal was successful. + * @return false Otherwise. + */ + bool remove(T rm_value) { + bool result = remove(root_, root_, rm_value); + if (result) { + size_--; + } + return result; + } + + /** + * @brief Check if a value is in the BST. + * + * @param value The value to find. + * @return true If value is in the BST. + * @return false Otherwise. + */ + bool contains(T value) { return contains(root_, value); } + + /** + * @brief Find the smallest value in the BST. + * + * @param ret_value Variable to hold the minimum value. + * @return true If minimum value was successfully found. + * @return false Otherwise. + */ + bool find_min(T& ret_value) { return find_min(root_, ret_value); } + + /** + * @brief Find the largest value in the BST. + * + * @param ret_value Variable to hold the maximum value. + * @return true If maximum value was successfully found. + * @return false Otherwise. + */ + bool find_max(T& ret_value) { return find_max(root_, ret_value); } + + /** + * @brief Get the number of values in the BST. + * + * @return std::size_t Number of values in the BST. + */ + std::size_t size() { return size_; } + + /** + * @brief Get all values of the BST in in-order order. + * + * @return std::vector List of values, sorted in in-order order. + */ + std::vector get_elements_inorder() { + std::vector result; + traverse_inorder([&](T node_value) { result.push_back(node_value); }, + root_); + return result; + } + + /** + * @brief Get all values of the BST in pre-order order. + * + * @return std::vector List of values, sorted in pre-order order. + */ + std::vector get_elements_preorder() { + std::vector result; + traverse_preorder([&](T node_value) { result.push_back(node_value); }, + root_); + return result; + } + + /** + * @brief Get all values of the BST in post-order order. + * + * @return std::vector List of values, sorted in post-order order. + */ + std::vector get_elements_postorder() { + std::vector result; + traverse_postorder([&](T node_value) { result.push_back(node_value); }, + root_); + return result; + } +}; + +/** + * @brief Function for testing insert(). + * + * @returns `void` + */ +static void test_insert() { + std::cout << "Testing BST insert..."; + + binary_search_tree tree; + bool res = tree.insert(5); + int min = -1, max = -1; + assert(res); + assert(tree.find_max(max)); + assert(tree.find_min(min)); + assert(max == 5); + assert(min == 5); + assert(tree.size() == 1); + + tree.insert(4); + tree.insert(3); + tree.insert(6); + assert(tree.find_max(max)); + assert(tree.find_min(min)); + assert(max == 6); + assert(min == 3); + assert(tree.size() == 4); + + bool fail_res = tree.insert(4); + assert(!fail_res); + assert(tree.size() == 4); + + std::cout << "ok" << std::endl; +} + +/** + * @brief Function for testing remove(). + * + * @returns `void` + */ +static void test_remove() { + std::cout << "Testing BST remove..."; + + binary_search_tree tree; + tree.insert(5); + tree.insert(4); + tree.insert(3); + tree.insert(6); + + bool res = tree.remove(5); + int min = -1, max = -1; + assert(res); + assert(tree.find_max(max)); + assert(tree.find_min(min)); + assert(max == 6); + assert(min == 3); + assert(tree.size() == 3); + assert(tree.contains(5) == false); + + tree.remove(4); + tree.remove(3); + tree.remove(6); + assert(tree.size() == 0); + assert(tree.contains(6) == false); + + bool fail_res = tree.remove(5); + assert(!fail_res); + assert(tree.size() == 0); + + std::cout << "ok" << std::endl; +} + +/** + * @brief Function for testing contains(). + * + * @returns `void` + */ +static void test_contains() { + std::cout << "Testing BST contains..."; + + binary_search_tree tree; + tree.insert(5); + tree.insert(4); + tree.insert(3); + tree.insert(6); + + assert(tree.contains(5)); + assert(tree.contains(4)); + assert(tree.contains(3)); + assert(tree.contains(6)); + assert(!tree.contains(999)); + + std::cout << "ok" << std::endl; +} + +/** + * @brief Function for testing find_min(). + * + * @returns `void` + */ +static void test_find_min() { + std::cout << "Testing BST find_min..."; + + int min = 0; + binary_search_tree tree; + assert(!tree.find_min(min)); + + tree.insert(5); + tree.insert(4); + tree.insert(3); + tree.insert(6); + + assert(tree.find_min(min)); + assert(min == 3); + + std::cout << "ok" << std::endl; +} + +/** + * @brief Function for testing find_max(). + * + * @returns `void` + */ +static void test_find_max() { + std::cout << "Testing BST find_max..."; + + int max = 0; + binary_search_tree tree; + assert(!tree.find_max(max)); + + tree.insert(5); + tree.insert(4); + tree.insert(3); + tree.insert(6); + + assert(tree.find_max(max)); + assert(max == 6); + + std::cout << "ok" << std::endl; +} + +/** + * @brief Function for testing get_elements_inorder(). + * + * @returns `void` + */ +static void test_get_elements_inorder() { + std::cout << "Testing BST get_elements_inorder..."; + + binary_search_tree tree; + tree.insert(5); + tree.insert(4); + tree.insert(3); + tree.insert(6); + + std::vector expected = {3, 4, 5, 6}; + std::vector actual = tree.get_elements_inorder(); + assert(actual == expected); + + std::cout << "ok" << std::endl; +} + +/** + * @brief Function for testing get_elements_preorder(). + * + * @returns `void` + */ +static void test_get_elements_preorder() { + std::cout << "Testing BST get_elements_preorder..."; + + binary_search_tree tree; + tree.insert(5); + tree.insert(4); + tree.insert(3); + tree.insert(6); + + std::vector expected = {5, 4, 3, 6}; + std::vector actual = tree.get_elements_preorder(); + assert(actual == expected); + + std::cout << "ok" << std::endl; +} + +/** + * @brief Function for testing get_elements_postorder(). + * + * @returns `void` + */ +static void test_get_elements_postorder() { + std::cout << "Testing BST get_elements_postorder..."; + + binary_search_tree tree; + tree.insert(5); + tree.insert(4); + tree.insert(3); + tree.insert(6); + + std::vector expected = {3, 4, 6, 5}; + std::vector actual = tree.get_elements_postorder(); + assert(actual == expected); + + std::cout << "ok" << std::endl; +} + +int main() { + test_insert(); + test_remove(); + test_contains(); + test_find_max(); + test_find_min(); + test_get_elements_inorder(); + test_get_elements_preorder(); + test_get_elements_postorder(); +} From 301f55b5711616458f67f366702e3eabe5aaf7da Mon Sep 17 00:00:00 2001 From: Saurav Uppoor <41644947+sauravUppoor@users.noreply.github.com> Date: Fri, 5 Feb 2021 15:37:38 +0530 Subject: [PATCH 27/30] fix/test/docs: update variable sizes, add namespace in graph/depth_first_search_with_stack.cpp (#1335) * fix: add namespace graph and other improvements * test: add tests * docs: add documentation thorughout the code * Apply suggestions from code review Co-authored-by: David Leal * Add documentation for header files * docs: improve formatting Co-authored-by: David Leal * Update depth_first_search_with_stack.cpp Co-authored-by: saurav Co-authored-by: David Leal --- graph/depth_first_search_with_stack.cpp | 205 +++++++++++++++++++++--- 1 file changed, 183 insertions(+), 22 deletions(-) diff --git a/graph/depth_first_search_with_stack.cpp b/graph/depth_first_search_with_stack.cpp index 9810edeb1..5376ea4c4 100644 --- a/graph/depth_first_search_with_stack.cpp +++ b/graph/depth_first_search_with_stack.cpp @@ -1,46 +1,207 @@ -#include -#include -#include -#include +/** + * + * @file + * @brief [Depth First Search Algorithm using Stack + * (Depth First Search Algorithm)](https://en.wikipedia.org/wiki/Depth-first_search) + * + * @author [Ayaan Khan](http://github.com/ayaankhan98) + * @author [Saurav Uppoor](https://github.com/sauravUppoor) + * + * @details + * Depth First Search also quoted as DFS is a Graph Traversal Algorithm. + * Time Complexity O(|V| + |E|) where V is number of vertices and E + * is number of edges in graph. + * + * Application of Depth First Search are + * + * 1. Finding connected components + * 2. Finding 2-(edge or vertex)-connected components. + * 3. Finding 3-(edge or vertex)-connected components. + * 4. Finding the bridges of a graph. + * 5. Generating words in order to plot the limit set of a group. + * 6. Finding strongly connected components. + * + *

Working

+ * 1. Mark all vertices as unvisited (colour it WHITE). + * 2. Push starting vertex into the stack and colour it GREY. + * 3. Once a node is popped out of the stack and is coloured GREY, we colour it BLACK. + * 4. Push all its neighbours which are not coloured BLACK. + * 5. Repeat steps 4 and 5 until the stack is empty. + */ -constexpr int WHITE = 0; -constexpr int GREY = 1; -constexpr int BLACK = 2; -constexpr int INF = 99999; +#include /// for IO operations +#include /// header for std::stack +#include /// header for std::vector +#include /// header for preprocessor macro assert() +#include /// header for limits of integral types + +constexpr int WHITE = 0; /// indicates the node hasn't been explored +constexpr int GREY = 1; /// indicates node is in stack waiting to be explored +constexpr int BLACK = 2; /// indicates node has already been explored +constexpr int64_t INF = std::numeric_limits::max(); + + +/** + * @namespace graph + * @brief Graph algorithms + */ +namespace graph { +/** + * @namespace depth_first_search + * @brief Functions for [Depth First Search](https://en.wikipedia.org/wiki/Depth-first_search) algorithm + */ +namespace depth_first_search { +/** + * @brief + * Adds and edge between two vertices of graph say u and v in this + * case. + * + * @param adj Adjacency list representation of graph + * @param u first vertex + * @param v second vertex + * + */ +void addEdge(std::vector> *adj, size_t u, size_t v) { + /* + * + * Here we are considering undirected graph that's the + * reason we are adding v to the adjacency list representation of u + * and also adding u to the adjacency list representation of v + * + */ + (*adj)[u - 1].push_back(v - 1); +} + +/** + * + * @brief + * Explores the given vertex, exploring a vertex means traversing + * over all the vertices which are connected to the vertex that is + * currently being explored and push it onto the stack. + * + * @param adj graph + * @param start starting vertex for DFS + * @return vector with nodes stored in the order of DFS traversal + * + */ +std::vector dfs(const std::vector > &graph, size_t start) { + /// checked[i] stores the status of each node + std::vector checked(graph.size(), WHITE), traversed_path; -void dfs(const std::vector > &graph, int start) { - std::vector checked(graph.size(), WHITE); checked[start] = GREY; - std::stack stack; + std::stack stack; stack.push(start); + + /// while stack is not empty we keep exploring the node on top of stack while (!stack.empty()) { int act = stack.top(); stack.pop(); if (checked[act] == GREY) { - std::cout << act << ' '; + /// push the node to the final result vector + traversed_path.push_back(act + 1); + + /// exploring the neighbours of the current node for (auto it : graph[act]) { stack.push(it); if (checked[it] != BLACK) { checked[it] = GREY; } } - checked[act] = BLACK; // nodo controllato + checked[act] = BLACK; /// Node has been explored } } + return traversed_path; +} +} // namespace depth_first_search +} // namespace graph + +/** + * Self-test implementations + * @returns none + */ +static void tests() { + size_t start_pos; + + /// Test 1 + std::cout << "Case 1: " << std::endl; + start_pos = 1; + std::vector > g1(3, std::vector()); + + graph::depth_first_search::addEdge(&g1, 1, 2); + graph::depth_first_search::addEdge(&g1, 2, 3); + graph::depth_first_search::addEdge(&g1, 3, 1); + + std::vector expected1 {1, 2, 3}; /// for the above sample data, this is the expected output + assert(graph::depth_first_search::dfs(g1, start_pos - 1) == expected1); + std::cout << "Passed" << std::endl; + + /// Test 2 + std::cout << "Case 2: " << std::endl; + start_pos = 1; + std::vector > g2(4, std::vector()); + + graph::depth_first_search::addEdge(&g2, 1, 2); + graph::depth_first_search::addEdge(&g2, 1, 3); + graph::depth_first_search::addEdge(&g2, 2, 4); + graph::depth_first_search::addEdge(&g2, 4, 1); + + std::vector expected2 {1, 3, 2, 4}; /// for the above sample data, this is the expected output + assert(graph::depth_first_search::dfs(g2, start_pos - 1) == expected2); + std::cout << "Passed" << std::endl; + + /// Test 3 + std::cout << "Case 3: " << std::endl; + start_pos = 2; + std::vector > g3(4, std::vector()); + + graph::depth_first_search::addEdge(&g3, 1, 2); + graph::depth_first_search::addEdge(&g3, 1, 3); + graph::depth_first_search::addEdge(&g3, 2, 4); + graph::depth_first_search::addEdge(&g3, 4, 1); + + std::vector expected3 {2, 4, 1, 3}; /// for the above sample data, this is the expected output + assert(graph::depth_first_search::dfs(g3, start_pos - 1) == expected3); + std::cout << "Passed" << std::endl; + } +/** + * @brief Main function + * @returns 0 on exit + */ int main() { - int n = 0; - std::cin >> n; - std::vector > graph(INF); - for (int i = 0; i < n; ++i) { - int u = 0, w = 0; - std::cin >> u >> w; - graph[u].push_back(w); - } + tests(); // execute the tests - dfs(graph, 0); + size_t vertices = 0, edges = 0, start_pos = 1; + std::vector traversal; + + std::cout << "Enter the Vertices : "; + std::cin >> vertices; + std::cout << "Enter the Edges : "; + std::cin >> edges; + + /// creating a graph + std::vector > adj(vertices, std::vector()); + + /// taking input for the edges + std::cout << "Enter the vertices which have edges between them : " << std::endl; + while (edges--) { + size_t u = 0, v = 0; + std::cin >> u >> v; + graph::depth_first_search::addEdge(&adj, u, v); + } + + /// taking input for the starting position + std::cout << "Enter the starting vertex [1,n]: " << std::endl; + std::cin >> start_pos; + start_pos -= 1; + traversal = graph::depth_first_search::dfs(adj, start_pos); + + /// Printing the order of traversal + for (auto x : traversal) { + std::cout << x << ' '; + } return 0; } From 59be3fa660f64ae0acb199a538efc838bc7e6a19 Mon Sep 17 00:00:00 2001 From: Magdy Sedra <62825437+MSedra@users.noreply.github.com> Date: Mon, 8 Feb 2021 19:19:26 +0200 Subject: [PATCH 28/30] feat: Added persistent segment tree with lazy propagation (#1445) * Added persistent segment tree with lazy propagation * Updated * Updated * Updated * Updated * Updated * Updated * Added a documentation * Updated * updating DIRECTORY.md * clang-format and clang-tidy fixes for 4b6566d2 * Updated * clang-format and clang-tidy fixes for 6197ba4c * Update persistent_segment_tree_with_lazy_propagation.cpp * Updated * Delete persistent_segment_tree_with_lazy_propagation.cpp * Updated * updating DIRECTORY.md * clang-format and clang-tidy fixes for 81621d83 * Update persistent_seg_tree_lazy_prop.cpp Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: David Leal --- DIRECTORY.md | 1 + .../persistent_seg_tree_lazy_prop.cpp | 321 ++++++++++++++++++ 2 files changed, 322 insertions(+) create mode 100644 range_queries/persistent_seg_tree_lazy_prop.cpp diff --git a/DIRECTORY.md b/DIRECTORY.md index f164b9ba5..c68a02db0 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -235,6 +235,7 @@ * [Fenwick Tree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/range_queries/fenwick_tree.cpp) * [Heavy Light Decomposition](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/range_queries/heavy_light_decomposition.cpp) * [Mo](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/range_queries/mo.cpp) + * [Persistent Seg Tree Lazy Prop](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/range_queries/persistent_seg_tree_lazy_prop.cpp) * [Segtree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/range_queries/segtree.cpp) * [Sparse Table](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/range_queries/sparse_table.cpp) diff --git a/range_queries/persistent_seg_tree_lazy_prop.cpp b/range_queries/persistent_seg_tree_lazy_prop.cpp new file mode 100644 index 000000000..9c84e2c4d --- /dev/null +++ b/range_queries/persistent_seg_tree_lazy_prop.cpp @@ -0,0 +1,321 @@ +/** + * @file + * @brief [Persistent segment tree with range updates (lazy + * propagation)](https://en.wikipedia.org/wiki/Persistent_data_structure) + * + * @details + * A normal segment tree facilitates making point updates and range queries in + * logarithmic time. Lazy propagation preserves the logarithmic time with range + * updates. So, a segment tree with lazy propagation enables doing range updates + * and range queries in logarithmic time, but it doesn't save any information + * about itself before the last update. A persistent data structure always + * preserves the previous version of itself when it is modified. That is, a new + * version of the segment tree is generated after every update. It saves all + * previous versions of itself (before every update) to facilitate doing range + * queries in any version. More memory is used ,but the logarithmic time is + * preserved because the new version points to the same nodes, that the previous + * version points to, that are not affected by the update. That is, only the + * nodes that are affected by the update and their ancestors are copied. The + * rest is copied using lazy propagation in the next queries. Thus preserving + * the logarithmic time because the number of nodes copied after any update is + * logarithmic. + * + * @author [Magdy Sedra](https://github.com/MSedra) + */ +#include /// for IO operations +#include /// to manage dynamic memory +#include /// for std::vector + +/** + * @namespace range_queries + * @brief Range queries algorithms + */ +namespace range_queries { + +/** + * @brief Range query here is range sum, but the code can be modified to make + * different queries like range max or min. + */ +class perSegTree { + private: + class Node { + public: + std::shared_ptr left = nullptr; /// pointer to the left node + std::shared_ptr right = nullptr; /// pointer to the right node + int64_t val = 0, + prop = 0; /// val is the value of the node (here equals to the + /// sum of the leaf nodes children of that node), + /// prop is the value to be propagated/added to all + /// the leaf nodes children of that node + }; + + uint32_t n = 0; /// number of elements/leaf nodes in the segment tree + std::vector> + ptrs{}; /// ptrs[i] holds a root pointer to the segment tree after the + /// ith update. ptrs[0] holds a root pointer to the segment + /// tree before any updates + std::vector vec{}; /// values of the leaf nodes that the segment + /// tree will be constructed with + + /** + * @brief Creating a new node with the same values of curr node + * @param curr node that would be copied + * @returns the new node + */ + std::shared_ptr newKid(std::shared_ptr const &curr) { + auto newNode = std::make_shared(Node()); + newNode->left = curr->left; + newNode->right = curr->right; + newNode->prop = curr->prop; + newNode->val = curr->val; + return newNode; + } + + /** + * @brief If there is some value to be propagated to the passed node, value + * is added to the node and the children of the node, if exist, are copied + * and the propagated value is also added to them + * @param i the left index of the range that the passed node holds its sum + * @param j the right index of the range that the passed node holds its sum + * @param curr pointer to the node to be propagated + * @returns void + */ + void lazy(const uint32_t &i, const uint32_t &j, + std::shared_ptr const &curr) { + if (!curr->prop) { + return; + } + curr->val += (j - i + 1) * curr->prop; + if (i != j) { + curr->left = newKid(curr->left); + curr->right = newKid(curr->right); + curr->left->prop += curr->prop; + curr->right->prop += curr->prop; + } + curr->prop = 0; + } + + /** + * @brief Constructing the segment tree with the early passed vector. Every + * call creates a node to hold the sum of the given range, set its pointers + * to the children, and set its value to the sum of the children's values + * @param i the left index of the range that the created node holds its sum + * @param j the right index of the range that the created node holds its sum + * @returns pointer to the newly created node + */ + std::shared_ptr construct(const uint32_t &i, const uint32_t &j) { + auto newNode = std::make_shared(Node()); + if (i == j) { + newNode->val = vec[i]; + } else { + uint32_t mid = i + (j - i) / 2; + auto leftt = construct(i, mid); + auto right = construct(mid + 1, j); + newNode->val = leftt->val + right->val; + newNode->left = leftt; + newNode->right = right; + } + return newNode; + } + + /** + * @brief Doing range update, checking at every node if it has some value to + * be propagated. All nodes affected by the update are copied and + * propagation value is added to the leaf of them + * @param i the left index of the range that the passed node holds its sum + * @param j the right index of the range that the passed node holds its sum + * @param l the left index of the range to be updated + * @param r the right index of the range to be updated + * @param value the value to be added to every element whose index x + * satisfies l<=x<=r + * @param curr pointer to the current node, which has value = the sum of + * elements whose index x satisfies i<=x<=j + * @returns pointer to the current newly created node + */ + std::shared_ptr update(const uint32_t &i, const uint32_t &j, + const uint32_t &l, const uint32_t &r, + const int64_t &value, + std::shared_ptr const &curr) { + lazy(i, j, curr); + if (i >= l && j <= r) { + std::shared_ptr newNode = newKid(curr); + newNode->prop += value; + lazy(i, j, newNode); + return newNode; + } + if (i > r || j < l) { + return curr; + } + auto newNode = std::make_shared(Node()); + uint32_t mid = i + (j - i) / 2; + newNode->left = update(i, mid, l, r, value, curr->left); + newNode->right = update(mid + 1, j, l, r, value, curr->right); + newNode->val = newNode->left->val + newNode->right->val; + return newNode; + } + + /** + * @brief Querying the range from index l to index r, checking at every node + * if it has some value to be propagated. Current node's value is returned + * if its range is completely inside the wanted range, else 0 is returned + * @param i the left index of the range that the passed node holds its sum + * @param j the right index of the range that the passed node holds its sum + * @param l the left index of the range whose sum should be returned as a + * result + * @param r the right index of the range whose sum should be returned as a + * result + * @param curr pointer to the current node, which has value = the sum of + * elements whose index x satisfies i<=x<=j + * @returns sum of elements whose index x satisfies l<=x<=r + */ + int64_t query(const uint32_t &i, const uint32_t &j, const uint32_t &l, + const uint32_t &r, std::shared_ptr const &curr) { + lazy(i, j, curr); + if (j < l || r < i) { + return 0; + } + if (i >= l && j <= r) { + return curr->val; + } + uint32_t mid = i + (j - i) / 2; + return query(i, mid, l, r, curr->left) + + query(mid + 1, j, l, r, curr->right); + } + + /** + * public methods that can be used directly from outside the class. They + * call the private functions that do all the work + */ + public: + /** + * @brief Constructing the segment tree with the values in the passed + * vector. Returned root pointer is pushed in the pointers vector to have + * access to the original version if the segment tree is updated + * @param vec vector whose values will be used to build the segment tree + * @returns void + */ + void construct(const std::vector + &vec) // the segment tree will be built from the values + // in "vec", "vec" is 0 indexed + { + if (vec.empty()) { + return; + } + n = vec.size(); + this->vec = vec; + auto root = construct(0, n - 1); + ptrs.push_back(root); + } + + /** + * @brief Doing range update by passing the left and right indexes of the + * range as well as the value to be added. + * @param l the left index of the range to be updated + * @param r the right index of the range to be updated + * @param value the value to be added to every element whose index x + * satisfies l<=x<=r + * @returns void + */ + void update(const uint32_t &l, const uint32_t &r, + const int64_t + &value) // all elements from index "l" to index "r" would + // by updated by "value", "l" and "r" are 0 indexed + { + ptrs.push_back(update( + 0, n - 1, l, r, value, + ptrs[ptrs.size() - + 1])); // saving the root pointer to the new segment tree + } + + /** + * @brief Querying the range from index l to index r, getting the sum of the + * elements whose index x satisfies l<=x<=r + * @param l the left index of the range whose sum should be returned as a + * result + * @param r the right index of the range whose sum should be returned as a + * result + * @param version the version to query on. If equals to 0, the original + * segment tree will be queried + * @returns sum of elements whose index x satisfies l<=x<=r + */ + int64_t query( + const uint32_t &l, const uint32_t &r, + const uint32_t + &version) // querying the range from "l" to "r" in a segment tree + // after "version" updates, "l" and "r" are 0 indexed + { + return query(0, n - 1, l, r, ptrs[version]); + } + + /** + * @brief Getting the number of versions after updates so far which is equal + * to the size of the pointers vector + * @returns the number of versions + */ + uint32_t size() // returns the number of segment trees (versions) , the + // number of updates done so far = returned value - 1 + // ,because one of the trees is the original segment tree + { + return ptrs.size(); + } +}; +} // namespace range_queries + +/** + * @brief Test implementations + * @returns void + */ +static void test() { + std::vector arr = {-5, 2, 3, 11, -2, 7, 0, 1}; + range_queries::perSegTree tree; + std::cout << "Elements before any updates are {"; + for (uint32_t i = 0; i < arr.size(); ++i) { + std::cout << arr[i]; + if (i != arr.size() - 1) { + std::cout << ","; + } + } + std::cout << "}\n"; + tree.construct( + arr); // constructing the original segment tree (version = 0) + std::cout << "Querying range sum on version 0 from index 2 to 4 = 3+11-2 = " + << tree.query(2, 4, 0) << '\n'; + std::cout + << "Subtract 7 from all elements from index 1 to index 5 inclusive\n"; + tree.update(1, 5, -7); // subtracting 7 from index 1 to index 5 + std::cout << "Elements of the segment tree whose version = 1 (after 1 " + "update) are {"; + for (uint32_t i = 0; i < arr.size(); ++i) { + std::cout << tree.query(i, i, 1); + if (i != arr.size() - 1) { + std::cout << ","; + } + } + std::cout << "}\n"; + std::cout << "Add 10 to all elements from index 0 to index 7 inclusive\n"; + tree.update(0, 7, 10); // adding 10 to all elements + std::cout << "Elements of the segment tree whose version = 2 (after 2 " + "updates) are {"; + for (uint32_t i = 0; i < arr.size(); ++i) { + std::cout << tree.query(i, i, 2); + if (i != arr.size() - 1) { + std::cout << ","; + } + } + std::cout << "}\n"; + std::cout << "Number of segment trees (versions) now = " << tree.size() + << '\n'; + std::cout << "Querying range sum on version 0 from index 3 to 5 = 11-2+7 = " + << tree.query(3, 5, 0) << '\n'; + std::cout << "Querying range sum on version 1 from index 3 to 5 = 4-9+0 = " + << tree.query(3, 5, 1) << '\n'; +} + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + test(); // run self-test implementations + return 0; +} From 4ef3069f6cf895bde9d01ad76d48667babeb46a6 Mon Sep 17 00:00:00 2001 From: David Leal Date: Tue, 9 Feb 2021 22:09:38 -0600 Subject: [PATCH 29/30] feat: Setup Probot Stale app --- .github/stale.yml | 62 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 .github/stale.yml diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 000000000..0bbe23e5d --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,62 @@ +# Configuration for probot-stale - https://github.com/probot/stale + +# Number of days of inactivity before an Issue or Pull Request becomes stale +daysUntilStale: 30 + +# Number of days of inactivity before an Issue or Pull Request with the stale label is closed. +# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. +daysUntilClose: 7 + +# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) +onlyLabels: [] + +# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable +exemptLabels: + - "approved" + +# Set to true to ignore issues in a project (defaults to false) +exemptProjects: false + +# Set to true to ignore issues in a milestone (defaults to false) +exemptMilestones: false + +# Set to true to ignore issues with an assignee (defaults to false) +exemptAssignees: false + +# Label to use when marking as stale +staleLabel: abandoned + +# Limit the number of actions per hour, from 1-30. Default is 30 +limitPerRun: 5 + +# Comment to post when removing the stale label. +# unmarkComment: > +# Your comment here. + +# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': +pulls: + # Comment to post when marking as stale. Set to `false` to disable + markComment: > + This pull request has been automatically marked as abandoned because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. + + # Comment to post when closing a stale Pull Request. + closeComment: > + Please ping one of the maintainers once you commit the changes requested + or make improvements on the code. If this is not the case and you need + some help, feel free to ask for help in our [Gitter](https://gitter.im/TheAlgorithms) + channel. Thank you for your contributions! + +issues: + # Comment to post when marking as stale. Set to `false` to disable + markComment: > + This issue has been automatically marked as abandoned because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. + + # Comment to post when closing a stale Issue. + closeComment: > + Please ping one of the maintainers once you add more information and updates here. + If this is not the case and you need some help, feel free to ask for help + in our [Gitter](https://gitter.im/TheAlgorithms) channel. Thank you for your contributions! From 0665fbdf64701026b2b5b268ee444f4e62aeb0c3 Mon Sep 17 00:00:00 2001 From: ayaankhan98 Date: Thu, 11 Feb 2021 11:23:42 +0530 Subject: [PATCH 30/30] fix: merge conflict --- graph/connected_components_with_dsu.cpp | 49 +++++++++++++------------ 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/graph/connected_components_with_dsu.cpp b/graph/connected_components_with_dsu.cpp index 5317361b2..670aa4496 100644 --- a/graph/connected_components_with_dsu.cpp +++ b/graph/connected_components_with_dsu.cpp @@ -10,8 +10,11 @@ * 1. Depth first search * 2. Disjoint union * 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 IO operations #include /// for std::set #include /// for std::vector @@ -20,28 +23,26 @@ * @brief Graph Algorithms */ namespace graph { - /** * @namespace disjoint_union - * @brief Function 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 { - -int64_t number_of_nodes; // denotes number of nodes -std::vector parent; // parent of each node -std::vector connected_set_size; // size of each set +uint32_t number_of_nodes = 0; // denotes number of nodes +std::vector parent{}; // parent of each node +std::vector connected_set_size{}; // size of each set /** * @brief function the initialize every node as it's own parent * @returns void */ void make_set() { - for (int64_t i = 1; i <= number_of_nodes; i++) { + for (uint32_t i = 1; i <= number_of_nodes; i++) { parent[i] = i; connected_set_size[i] = 1; } } /** - * @brief To find the component where following node belongs to + * @brief Find the component where following node belongs to * @param val parent of val should be found * @return parent of val */ @@ -53,7 +54,7 @@ int64_t find_set(int64_t val) { return val; } /** - * @brief To join 2 components to belong to one + * @brief Merge 2 components to become one * @param node1 1st component * @param node2 2nd component * @returns void @@ -73,40 +74,42 @@ void union_sets(int64_t node1, int64_t node2) { } } /** - * @brief To find total no of connected components + * @brief Find total no. of connected components * @return Number of connected components */ -int64_t no_of_connected_components() { +uint32_t no_of_connected_components() { std::set temp; // temp set to count number of connected components - for (int64_t i = 1; i <= number_of_nodes; i++) temp.insert(find_set(i)); + for (uint32_t i = 1; i <= number_of_nodes; i++) temp.insert(find_set(i)); return temp.size(); // return the size of temp set } +} // namespace disjoint_union +} // namespace graph + /** * @brief Test Implementations * @returns void */ static void test() { - std::cin >> number_of_nodes; - parent.resize(number_of_nodes + 1); - connected_set_size.resize(number_of_nodes + 1); - make_set(); - int64_t edges = 0; + namespace dsu = graph::disjoint_union; + std::cin >> dsu::number_of_nodes; + dsu::parent.resize(dsu::number_of_nodes + 1); + dsu::connected_set_size.resize(dsu::number_of_nodes + 1); + dsu::make_set(); + uint32_t edges = 0; std::cin >> edges; // no of edges in the graph while (edges--) { int64_t node_a = 0, node_b = 0; std::cin >> node_a >> node_b; - union_sets(node_a, node_b); + dsu::union_sets(node_a, node_b); } - std::cout << no_of_connected_components() << std::endl; + std::cout << dsu::no_of_connected_components() << std::endl; } -} // namespace disjoint_union -} // namespace graph /** * @brief Main function * @returns 0 on exit */ int main() { - graph::disjoint_union::test(); // Execute the tests + test(); // Execute the tests return 0; } \ No newline at end of file